def __init__(self): """Initialization abstract method.""" self._bench = None self._target = None self._context = Context() self._reset_state = False self._direct_init_dict = None
def context(self): """ """ context = Context() context.set_code_segment(self._init_code_address) context.set_data_segment(self._init_data_address) context.set_symbolic(False) context.set_absolute(True) return context
def context(self): """ """ context = Context() context.set_code_segment(0x100000) context.set_data_segment(0x200000) context.set_symbolic(False) return context
def elf_abi(self, stack_size, start_symbol, **kwargs): """ """ stack = VariableArray(kwargs.get("stack_name", "microprobe_stack"), "uint8_t", stack_size, align=kwargs.get("stack_alignment", 16), address=kwargs.get("stack_address", None)) instructions = [] instructions += self.target.set_register_to_address( self.stack_pointer, Address(base_address=kwargs.get("stack_name", "microprobe_stack")), Context()) if self.stack_direction == "decrease": instructions += self.target.add_to_register( self.stack_pointer, stack_size) if start_symbol is not None: instructions += self.target.function_call(start_symbol) instructions += self.target.function_call("ELF_ABI_EXIT") instructions[0].set_label("ELF_ABI_START") return [stack], instructions
def get_context(self, variable=None, tmpl_path=None): """ """ if variable is None: variable = self.context_var if variable.size < self.context_var.size: raise MicroprobeCodeGenerationError( "Variable '%s' is too small to save the target context") asm = open(os.path.join(tmpl_path, "getcontext.S")).readlines() if len(asm) == 0: return [] reg = self._scratch_registers[0] instrs = self.set_register_to_address(reg, variable.address, Context()) return instrs + \ microprobe.code.ins.instructions_from_asm(asm, self.target)
def function_call(self, target, return_address_reg=None, long_jump=False): if return_address_reg is None: return_address_reg = self.target.isa.registers["X1"] if isinstance(target, str): target = InstructionAddress(base_address=target) if long_jump: assert isinstance(target, int) instrs = self.target.set_register(return_address_reg, target, Context()) jalr_ins = self.target.new_instruction("JALR_V0") jalr_ins.set_operands([0, return_address_reg, return_address_reg]) jalr_ins.add_comment("Long jump to address 0X%016X" % target) instrs.append(jalr_ins) return instrs else: jal_ins = self.target.new_instruction("JAL_V0") jal_ins.set_operands([target, return_address_reg]) return [jal_ins]
def _compute_reset_code(target, test_def, args): instructions = interpret_asm( test_def.code, target, [var.name for var in test_def.variables], show_progress=True, ) # TODO: This can be done in parallel or look for speed up the process instructions = [ instruction_from_definition(instr) for instr in instructions ] instruction_dict = {} address = test_def.default_code_address progress = Progress( len(test_def.roi_memory_access_trace), msg="Building instruction dictionary", ) for instr in instructions: progress() if instr.address is not None: if instr.address.base_address == "code": address = test_def.default_code_address + \ instr.address.displacement instr.set_address(address) else: address = address + instr.architecture_type.format.length instr.set_address(address) instruction_dict[instr.address] = instr free_regs = [] written_after_read_regs = [] read_regs = [] level = 0 dynamic_count = 0 progress = Progress( len(test_def.roi_memory_access_trace), msg="Evaluating register usage", ) reset_regs = set() for access in test_def.roi_memory_access_trace: progress() if access.data_type == "D": continue dynamic_count += 1 try: instr = instruction_dict[access.address] uses = instruction_dict[access.address].uses() sets = instruction_dict[access.address].sets() except KeyError: print_error( "Access to from instruction at address " "0x%016X registered but such instruction is not" " present in the definition." % access.address, ) exit(1) # Calls if instr.mnemonic == "BL": level += 1 elif instr.mnemonic == "BCL": level += 1 elif instr.mnemonic == "BCCTRL": if instr.operands()[2].value in [0, 3]: level += 1 # Returns if instr.mnemonic == "BCLR": if (((instr.operands()[0].value & 0b10100) == 20) and (instr.operands()[2].value == 0)): level -= 1 # TODO: this should include Z and RISCV instructions for call # and return, but currently we do not have memory access traces # for such platforms for reg in uses: if reg not in read_regs: read_regs.append(reg) for reg in sets: if reg in free_regs: continue elif reg not in read_regs: free_regs.append(reg) elif reg not in written_after_read_regs: written_after_read_regs.append(reg) reset_regs = set(read_regs).intersection( set(written_after_read_regs), ) reset_regs = sorted(reset_regs) assert len(free_regs) == len(set(free_regs)) assert len(set(free_regs).intersection(set(reset_regs))) == 0 if len(test_def.roi_memory_access_trace) == 0: # We do not have memory access trace, assume calling conventions reset_regs = target.volatile_registers reset_regs = [ reg for reg in reset_regs if reg in target.volatile_registers] if len(reset_regs) == 0 and len(test_def.roi_memory_access_trace) == 0: print_info( "No memory access trace found. Resetting volatile registers." ) reset_regs = target.volatile_registers unused_regs = sorted( (reg for reg in target.registers.values() if reg not in read_regs), ) # # Make sure scratch registers are reset last # for reg in target.scratch_registers: if reg in reset_regs: reset_regs.remove(reg) reset_regs.append(reg) free_regs = unused_regs + free_regs # Know which ones are not used (or written) and which ones are used # Use them as base / temporal registers for addresses # Check addresses conflict_addresses = {} new_ins = [] progress = Progress( len(test_def.roi_memory_access_trace), msg="Evaluating memory usage", ) for access in test_def.roi_memory_access_trace: progress() if access.data_type == "I": continue val = conflict_addresses.get( access.address, [access.length, access.access_type], ) if access.access_type not in val[1]: val[1] += access.access_type val[0] = max(val[0], access.length) conflict_addresses[access.address] = val fix_addresses = [] for address in conflict_addresses: value = conflict_addresses[address] if value[1] == "RW": wvalue = None for var in test_def.variables: if var.var_type.upper() in ["CHAR", "UINT8_T"]: elem_size = 1 else: raise NotImplementedError end_address = var.address + var.num_elements * elem_size if var.address <= address <= end_address: offset = int((address - var.address) / elem_size) svalue = var.init_value[ offset:offset + int(value[0] / elem_size) ] svalue = "".join(["%02X" % tval for tval in svalue]) wvalue = int(svalue, 16) break if wvalue is None: print_error( "Unable to restore original value for address 0x%X" % address, ) exit(1) if value[0] <= 8: fix_addresses.append((address, value[0], wvalue)) else: for selem in range(0, value[0]//8): sfmt = "%%0%dX" % (2*value[0]) nvalue = sfmt % wvalue nvalue = int(nvalue[selem*16:(selem+1)*16], 16) fix_addresses.append( (address + selem * 8, 8, nvalue) ) reset_steps = [] context = Context() context.set_symbolic(True) if len(fix_addresses) > 0: # TODO: This can be optimized. Reduce the number of instructions to # be added by sorting the reset code (shared values or similar # addresses) # TODO: This can be optimized for use vector registers when # needed # print_info("Adding instructions to reset memory state") reset_register = [ reg for reg in free_regs if reg.type.used_for_address_arithmetic and reg.name != "GPR0" ][0] for address, length, value in fix_addresses: address_obj = Address(base_address="data", displacement=address) new_instructions = target.set_register( reset_register, value, context, opt=False, ) for ins in new_instructions: ins.add_comment( "Reset code. Setting %s to 0X%016X" % (reset_register.name, value), ) reset_steps.append([new_instructions[:], reset_register, value]) context.set_register_value(reset_register, value) try: store_ins = target.store_integer( reset_register, address_obj, length * 8, context, ) new_instructions += store_ins reset_steps.append( [store_ins, reset_register, address_obj, length], ) except MicroprobeCodeGenerationError: areg = [ reg for reg in free_regs if reg.type.used_for_address_arithmetic and reg.name != "GPR0" ][1] set_ins = target.set_register( areg, address, context, opt=False, ) new_instructions += set_ins reset_steps.append([set_ins, areg, address_obj]) context.set_register_value(areg, address_obj) store_ins = target.store_integer( reset_register, address_obj, length * 8, context, ) new_instructions += store_ins reset_steps.append( [store_ins, reset_register, address_obj, length], ) for ins in set_ins: ins.add_comment( "Reset code. Setting %s to 0X%016X" % (areg.name, address), ) for ins in store_ins: ins.add_comment( "Reset code. Setting mem content in 0X%016X" % (address), ) new_ins.extend(new_instructions) # Reset contents of used registers for reset_register in reset_regs: try: value = [ reg for reg in test_def.registers if reg.name == reset_register.name ][0].value except IndexError: continue new_instructions = target.set_register( reset_register, value, context, opt=False, ) reset_steps.append([new_instructions, reset_register, value]) context.set_register_value(reset_register, value) for ins in new_instructions: ins.add_comment( "Reset code. Setting %s to 0X%016X" % (reset_register.name, value), ) new_ins.extend(new_instructions) try: overhead = (((len(new_ins) * 1.0) / dynamic_count) * 100) except ZeroDivisionError: print_warning("Unable to compute overhead. Zero dynamic instruction " "count") overhead = 0 print_info( "%03.2f%% overhead added by resetting code" % overhead, ) if overhead > args['wrap_endless_threshold']: print_error( "Instructions added: %d" % len(new_ins), ) print_error( "Total instructions: %d" % dynamic_count, ) print_error( "Reset code above --wrap-endless-threshold. Stopping generation.", ) exit(1) return new_ins, overhead, reset_steps
class Wrapper(six.with_metaclass(abc.ABCMeta, object)): """ Abstract class to represent a language wrapper. """ @abc.abstractmethod def __init__(self): """Initialization abstract method.""" self._bench = None self._target = None self._context = Context() self._reset_state = False @abc.abstractmethod def outputname(self, name): """ :param name: """ raise NotImplementedError @abc.abstractmethod def headers(self): """ """ raise NotImplementedError # @abc.abstractmethod # def declare_option(self, option_flag, var,): # raise NotImplementedError @abc.abstractmethod def declare_global_var(self, var): """ :param var: """ raise NotImplementedError @abc.abstractmethod def init_global_var(self, var, value): """ :param var: :param value: """ raise NotImplementedError @abc.abstractmethod def required_global_vars(self): """ """ raise NotImplementedError @abc.abstractmethod def start_main(self): """ """ raise NotImplementedError @abc.abstractmethod def post_var(self): """ """ raise NotImplementedError @abc.abstractmethod def start_loop(self, instr, instr_reset, aligned=True): """ :param instr: :param instr_reset: :param aligned: (Default value = True) """ raise NotImplementedError @abc.abstractmethod def wrap_ins(self, instr): """ :param instr: """ raise NotImplementedError @abc.abstractmethod def end_loop(self, instr): """ :param instr: """ raise NotImplementedError @abc.abstractmethod def end_main(self): """ """ raise NotImplementedError @abc.abstractmethod def footer(self): """ """ raise NotImplementedError @abc.abstractmethod def infinite(self): """Returns a :class:`~.bool` indicating if the loop is infinite. """ raise NotImplementedError @abc.abstractmethod def reserved_registers(self, registers, target): """ :param registers: :param target: """ raise NotImplementedError def set_benchmark(self, bench): """ :param bench: """ self._bench = bench @property def benchmark(self): """ """ return self._bench @property def reset(self): """ """ return self._reset_state def set_target(self, target): """ :param target: """ self._target = target @property def target(self): """ """ return self._target def context(self): """ """ return self._context.copy() def init_loop_pad(self): """ """ return 0 def init_main_pad(self): """ """ return 0 @property def direct_initialization_support(self): """ Boolean indicating if the wrapper supports direct initialization. Direct initialization refers to the capability of initializing values without requiring the execution of instructions. For instance, simulation-based format usually allow the specification of the initial values of the memory and the registers. """ return False def register_direct_init(self, dummy_key, dummy_value): """ Initialize *key* with the value *value* """ if self.direct_initialization_support: raise NotImplementedError else: raise MicroprobeCodeGenerationError( "Direct intialization function called but not supported")
def _shift_and_fix_code( target, code, offset, addresses, reset_steps, registers, ): """Shift code and fix reset code.""" scode = [] for instruction in code: instruction = instruction.copy() # Fix and shift decorators (not need to modify code) for key, values in instruction.decorators.items(): if not isinstance(values, list): values = [values] if key in ['MA', 'BT']: for idx in range(0, len(values)): if addresses[0] <= values[idx] <= addresses[1]: values[idx] = values[idx] + offset scode.append(instruction) cidx = 0 context = Context() context.set_symbolic(False) for reset_step in reset_steps: rins = reset_step[0] cins = scode[cidx:cidx+len(rins)] rreg = reset_step[1] try: rval = [reg for reg in registers if reg.name == rreg.name][0].value except IndexError: # This was a support register to compute an address, # it should be fixed rval = 0 for rin, cin in zip(rins, cins): if rin.name != cin.instruction_type.name: print_error("Unable to fix the reset code") exit(1) if len(reset_step) == 3: rins, reset_register, value = reset_step if not isinstance(value, six.integer_types): # All addresses should be offset value += offset nins = target.set_register( reset_register, value.displacement, context, opt=False, ) print_info( "Fixing reset code for reg: %s. " "New value: 0x%016X" % (rreg.name, value.displacement), ) else: if abs(value-rval) == offset: # This has been shifted, force offset value += offset print_info( "Fixing reset code for reg: %s. " "New value: 0x%016X" % (rreg.name, value), ) nins = target.set_register( reset_register, value, context, opt=False, ) context.set_register_value(reset_register, value) elif len(reset_step) == 4: rins, reset_register, address_obj, length = reset_step # All addresses should be offsetted address_obj += offset nins = target.store_integer( reset_register, address_obj, length * 8, context, ) print_info( "Fixing reset code for reg: %s. " "New value: 0x%016X" % (rreg.name, address_obj.displacement), ) else: raise NotImplementedError( "Unable to shift and fix code" ) if len(rins) != len(nins): print_error("Original resetting code:") for ins in rins: print_error(ins.assembly()) print_error("New resetting code:") for ins in nins: print_error(ins.assembly()) print_error("New resetting code differs from original in length") exit(1) for ins in nins: if len(reset_step) == 3: if not isinstance(value, six.integer_types): value = value.displacement ins.add_comment( "Reset code. Setting %s to 0X%016X" % (reset_register.name, value), ) else: ins.add_comment( "Reset code. Setting mem content in 0X%016X" % (address_obj.displacement), ) for idx, (nin, cin) in enumerate(zip(nins, cins)): if nin.name != cin.instruction_type.name: print_warning("New code differs from original in opcodes") scode[cidx+idx] = instruction_to_definition(nin) scode[cidx+idx].comments = scode[cidx+idx].comments[1:] cidx += len(rins) return scode