def get_data_pm(self, address, length=0): """Get data from PM. This method works in the exactly the same way as method, but it returns data from PM region instead of DM. Args: address length (int, optional) """ try: address = int(address, 16) except TypeError: pass address = cu.get_correct_addr(address, Arch.addr_per_word) if length == 0: return self.pm[address] # note: Even the PM RAM is saved in self.pm data section. rawdata = [] length = cu.convert_byte_len_word(length, Arch.addr_per_word) for i in range(0, length, Arch.addr_per_word): if address + i in self.pm: rawdata.append(self.pm[address + i]) else: if i == 0: raise KeyError("Key " + hex(address + i) + " is invalid") else: raise OutOfRangeError( "Key " + hex(address + i) + " is invalid", address + i - Arch.addr_per_word) return tuple(rawdata)
def get_data_pm(self, address, length=0): """Get data from PM. This method works in the exactly the same way as get_data(self, address, length = 0) method, but it returns data from PM region instead of DM. Args: address length (int, optional) """ # We helpfully accept address as either a hex string or an integer try: # Try to convert address from a hex string to an integer address = int(address, 16) except TypeError: # Probably means address was already an integer pass address = cu.get_correct_addr(address, Arch.addr_per_word) if length == 0: mem_region = Arch.get_pm_region(address) else: mem_region = Arch.get_pm_region( address + cu.convert_byte_len_word(length, Arch.addr_per_word) - Arch.addr_per_word) try: if ('PMRAM' in mem_region or 'PMCACHE' in mem_region or 'SLT' in mem_region): if length == 0: return self.pm[address:address + Arch.addr_per_word][0] return tuple([ int(x) for x in self. pm[address:address + cu.convert_byte_len_word(length, Arch.addr_per_word)] ]) else: if length == 0: raise KeyError( "Key " + hex(address + Arch.addr_per_word) + " is not a valid PM RAM address", address + Arch.addr_per_word) raise OutOfRangeError( "Key " + hex(address + cu.convert_byte_len_word(length, Arch.addr_per_word) - Arch.addr_per_word) + " is not a valid PM RAM address", address + cu.convert_byte_len_word(length, Arch.addr_per_word) - Arch.addr_per_word) except Exception as exception: if "Transport failure (Unable to read)" in exception: sys.stderr.write(str(exception)) raise ChipNotPoweredError else: raise exception
def get_data(self, address, length=0, ignore_overflow=False): """Returns the contents of one or more addresses in DM. This allows you to grab a chunk of memory, e.g. get_data(0x600, 50). Note: The length supplied is measured in addressable units. get_data(addr) will return a single value; get_data(addr, 1) will return a tuple with a single member. get_data(addr, 10) will return a tuple with ten members or a tuple with three members (when the memory is octet addressed, 32bit words). If the address is out of range, a KeyError exception will be raised. If the address is valid but the length is not (i.e. address+length is not a valid address) an OutOfRangeError exception will be raised. Args: address length (int, optional) ignore_overflow (bool, optional): Ignore if the read goes out from the memory region and append zeros. This is useful if an union is at the end of the memory region. """ # We helpfully accept address as either a hex string or an integer try: # Try to convert address from a hex string to an integer address = int(address, 16) except TypeError: # Probably means address was already an integer pass # if the address does not start at the beginning of a word, correct it address = cu.get_correct_addr(address, Arch.addr_per_word) # By default, return a single value. if length == 0: return self.data[address] # Otherwise, return a list. # This method deliberately copies from self.data, rather than returning # a reference to our 'master copy'. We don't want a caller modifying # that. rawdata = [] # length needs to have a value that can fetch words; # if it does not, adjust it by adding # the missing number of bytes length = cu.convert_byte_len_word(length, Arch.addr_per_word) for i in range(0, length, Arch.addr_per_word): if address + i in self.data: rawdata.append(self.data[address + i]) else: # We want to raise a different exception depending on whether # address is valid or not. Unfortunately this requires some # special treatment for i == 0 if i == 0: raise InvalidDmAddressError( "No DM at " + hex(address) ) if ignore_overflow: # Just append zeros if we running out of the memory. rawdata.append(0) else: raise InvalidDmLengthError(i, address) return tuple(rawdata)
def get_data(self, address, length=0, ignore_overflow=False): """Returns the contents of one or more addresses. This allows you to grab a chunk of memory, e.g. get_data(0x600, 50). Addresses can be from any valid DM region (DM1, PM RAM, mapped NVMEM, memory-mapped registers, etc.). Note: The length supplied is measured in addressable units. get_data(addr) will return a single value; get_data(addr, 1) will return a list with a single member. get_data(addr, 10) will return a list with ten members or a list with three members (when the memory is octet addressed, 32bit words). Note that reading PM RAM via DM is not supported (since not all chips map PM into DM). Use `get_data_pm()` instead. Args: address length ignore_overflow (bool, optional): Ignore if the read goes out from the memory region and append zeros. This is useful if an union is at the end of the memory region. Raises: KeyError: If the address is out of range. OutOfRangeError: If the address is valid but the length is not (i.e. address+length is not a valid address). """ # We helpfully accept address as either a hex string or an integer try: # Try to convert address from a hex string to an integer address = int(address, 16) except TypeError: # Probably means address was already an integer pass address = cu.get_correct_addr(address, Arch.addr_per_word) if length == 0: mem_region = Arch.get_dm_region(address, False) else: # kalaccess will wrap the memory if the read goes out of boundary. # So to ignore overflow just get the memory region from the # beginning. if ignore_overflow: mem_region = Arch.get_dm_region(address) else: mem_region = Arch.get_dm_region( address + cu.convert_byte_len_word(length, Arch.addr_per_word) - Arch.addr_per_word) try: if ('DM' in mem_region or 'SLT' in mem_region or 'MMR' in mem_region or 'NVMEM' in mem_region or 'PMRAM' in mem_region or 'MCU' in mem_region): if length == 0: return self.dm[address:address + Arch.addr_per_word][0] return tuple([ int(x) for x in self. dm[address:address + cu.convert_byte_len_word(length, Arch.addr_per_word)] ]) else: if length == 0: raise KeyError( "Key " + hex(address + Arch.addr_per_word) + " is not a valid DM address", address + Arch.addr_per_word) raise OutOfRangeError( "Key " + hex(address + cu.convert_byte_len_word(length, Arch.addr_per_word) - Arch.addr_per_word) + " is not a valid DM address", address + cu.convert_byte_len_word(length, Arch.addr_per_word) - Arch.addr_per_word) except (SystemExit, KeyboardInterrupt, GeneratorExit): raise except Exception as exception: if "Transport failure (Unable to read)" in exception: sys.stderr.write(str(exception)) raise ChipNotPoweredError # Will also spit exceptions to log file, if set. sys.stderr.write(traceback.format_exc()) raise exception
def set_data(self, address, value): """Sets the contents of one or more addresses. This allows you to write a list of values to a chunk of memory. Addresses can only be from any valid DM RAM or memory mapped register region. e.g. set_data(0x3e5d, [1 2 3]) Note: set_data(address, [val]) writes address with a single value val. set_data(address, [val1 ... valn]) writes the list of values to memory starting from address. This function should only be implemented for live chips. And should not be called if the chip is not live. Args: address value Raises: KeyError: If the address is out of range. OutOfRangeError: If the address is valid but the length is not (i.e. address+length is not a valid address). """ # We helpfully accept address as either a hex string or an integer try: # Try to convert address from a hex string to an integer address = int(address, 16) except TypeError: # Probably means address was already an integer pass address = cu.get_correct_addr(address, Arch.addr_per_word) length = len(value) if length == 1: mem_region = Arch.get_dm_region(address) else: mem_region = Arch.get_dm_region( address + cu.convert_byte_len_word(length, Arch.addr_per_word) - Arch.addr_per_word) try: if 'DM' in mem_region or 'MMR' in mem_region: self.dm[address] = value else: if length == 1: raise KeyError( "Key " + hex(Arch.addr_per_word + address) + " is not in a DM or PM region", address + Arch.addr_per_word) else: raise OutOfRangeError( "Key " + hex(address + cu.convert_byte_len_word( length, Arch.addr_per_word) - Arch.addr_per_word) + " is not in a DM or PM region", address + cu.convert_byte_len_word(length, Arch.addr_per_word) - Arch.addr_per_word) except (SystemExit, KeyboardInterrupt, GeneratorExit): raise except Exception as exception: if "Transport failure (Unable to read)" in exception: sys.stderr.write(str(exception)) raise ChipNotPoweredError else: raise exception
def get_var(self, identifier, elf_id=None, datalen=None): """Get a variable. Like Analysis.get_var_strict(), except it's not strict (!) 'identifier' can be a variable name, or address. If it's a name, we attempt to find the closest match in our list of variables. In this case the user can also provide a data length; if it is set, we return a slice of data, 'datalen' addressable units long starting at the address specified by 'identifier'. Args: identifier: Could be name or address. elf_id (int, optional): The bundle elf id if the variable is in a downloadable capability. datalen: If the identifier is an address the data length is specified by this input. Returns: A Variable. Raises: AmbiguousSymbolError: If more than one match is found. """ # For Crescendo, data can only be fetched as words. Since it is # octet-addressed, the addresses must be divisible with the number of # addresses per word (32 bit words - 4 octets, therefore addresses must # be divisible with 4). if isinstance(identifier, numbers.Integral): identifier = cu.get_correct_addr(identifier, Arch.addr_per_word) # Same as above. The lengths are measured in addressable units. if datalen is not None: datalen = cu.convert_byte_len_word(datalen, Arch.addr_per_word) # The following is necessary since we can't rely on variable # sizes. If a (say) register address was passed in here we will likely # match a variable entry for $flash.data24.__Limit. if isinstance(identifier, numbers.Integral) and \ Arch.get_dm_region(identifier) == "MMR": return None # First, look up the variable in the debug information. # Even if the user supplied an address rather than a name, it's nice # if we can tell them which variable it might be part of. # Might throw an AmbiguousSymbolError exception here; can't get that # with an address but can with a variable name. var = None try: var = self.debuginfo.get_var(identifier, elf_id) except AmbiguousSymbolError as amb: # Filter out matches of struct/array members, where their parent is # also in the list of matches. matches = amb.args[1] quarantine_list = [] for match in matches: try: mvar = self.debuginfo.get_var_strict( match["name"], match["elf_id"]) if mvar.parent is not None and mvar.parent.name in matches: # This is a struct/array member quarantine_list.append(match) else: possible_parent = mvar except ValueError: # Out of memory can be seen for asm memory reagions. Ignore # them. quarantine_list.append(match) # If the number of things in the quarantine list is EXACTLY # ONE MORE than the number of things in the matches list, then # we probably have found a single variable and all its # members. if len(matches) == len(quarantine_list) + 1: var = possible_parent else: # Give up raise AmbiguousSymbolError(amb.args[0], amb.args[1]) if var is None: return None # Don't necessarily want to modify the actual variable entry below*, # so maybe create a copy here. # * Why? Well var is just a reference to the original variable in the # debuginfo class - we ought not to change it frivolously, since it # could break some other analysis. # In this case, we don't want to permanently munge the name # just because we're doing a slice this time. ret_var = var if datalen: if isinstance(identifier, numbers.Integral): if var.address == identifier and var.size <= datalen: ret_var = copy.deepcopy(var) ret_var.name = "User-defined slice, part of: " + \ var.name + " ???" # We want to get a slice of data, not just the variable # entry. ret_var.size = datalen # If the identifier is a variable name, don't include any # members we might have already inspected. ret_var.members = None else: ret_var = ct.Variable("???", identifier, datalen) else: # Mitigation: we can't rely on 'var' actually containing the # supplied address, due to the lack of size information (see # KerDebugInfo.py). So work around it here. if (isinstance(identifier, numbers.Integral) and identifier >= var.address + Arch.addr_per_word * var.size): # Just return the value at the given address. ret_var = ct.Variable(var.name + " ???", identifier, 1) # Now get the data value(s) from chipdata. Look in DM first, only # try const if we run out of options. try: ret_var.value = self.chipdata.get_data(ret_var.address, ret_var.size) except InvalidDmLengthError as oor: # Address was valid, but size was not. # oor.args[1] contains the last valid address in the supplied # range. valid_size = (oor.max_length - ret_var.address) + 1 ret_var.value = self.chipdata.get_data(ret_var.address, valid_size) ret_var.size = valid_size except InvalidDmAddressError: # The address wasn't valid. Could be that this variable is # actually in dm const. try: ret_var.value = self.debuginfo.get_dm_const( ret_var.address, ret_var.size) except InvalidDmConstLengthError as oor: # Address was valid, but size was not. valid_size = oor.max_length - ret_var.address ret_var.value = self.debuginfo.get_dm_const( ret_var.address, valid_size) except InvalidDmConstAddressError: # Ok we really are stuck. Return variable with a value of None. debug_info = self.debuginfo.get_kymera_debuginfo() ret_var.value = debug_info.debug_strings[ret_var.address] return ret_var # Need a way to work out whether we've already inspected this # variable, so we can avoid doing it more than once. # An inspection *should* result in a non-empty type_name string. # Also, don't inspect the slices. It would be bad. ret_var.members = [] var_elf_id = self.debuginfo.table.get_elf_id_from_address( ret_var.address) if not var_elf_id: var_elf_id = self.debuginfo.get_kymera_debuginfo().elf_id self.debuginfo.inspect_var(ret_var, var_elf_id) return ret_var
def get(self, identifier, elf_id=None, datalen=None): """ Get a variable, register or range of addresses. 'identifier' can be an address, or the name of a register or variable (note constant names are not handled). If it's a name, we attempt to find the closest match in our lists of registers and variables. However, please note that there is a 'search' function to conduct searches, i.e. search('*[IDENTIFIER]*'), and it's advised to use that instead of 'get'. If the identifier is ambiguous, an AmbiguousSymbolError exception may be thrown. Like `get_var`, it's also possible to pass in a data length to request a slice of data (this only makes sense for things in DM). Obviously there are things that could go wrong here, especially if the user passes in a random number and we try to guess what it points to. That's why we only provide this function in Interactive mode! Args: identifier: Could be name or address elf_id: The bundle elf id if the variable is in a downloadable capability. datalen: If the identifier is an address the data length is specified by this input. Returns: a DataSym (which may be a Variable) or a SourceInfo (if a code address was supplied). """ pm_addr = None reg = None var = None apology = "" hit_flag = False # For Crescendo, data can only be fetched as words. Since it is # octet-addressed, the addresses must be divisible by the number of # addresses per word (32 bit words - 4 octets, therefore addresses must # be divisible by 4). if isinstance(identifier, numbers.Integral): identifier = cu.get_correct_addr(identifier, Arch.addr_per_word) # Same as above. The lengths are measured in addressable units. if datalen is not None: datalen = cu.convert_byte_len_word(datalen, Arch.addr_per_word) # Look to see whether it's an address in PM. # Since the PM range can be huge (and may overlap with DM), perform a # sanity check to avoid false positives. if (isinstance(identifier, numbers.Integral) and Arch.get_pm_region(identifier, False) in ("PMROM", "PMRAM") and datalen is None): # Is it the address of a valid instruction? try: self.debuginfo.get_instruction(identifier) except KeyError: pass else: pm_addr = self.debuginfo.get_source_info(identifier) # Look to see whether it's the name of a module in PM. This needs to # include all the fluff at the start, (i.e. '$M.foo.bar' rather than # just 'foo.bar' or 'bar') so as to avoid clashes with names in DM # space (which are more likely to be what's wanted). if isinstance(identifier, numbers.Integral): try: pm_addr = self.debuginfo.get_source_info(identifier) hit_flag = True except KeyError: pass except AmbiguousSymbolError as amb_pm: apology += str(amb_pm) + "\n" # Look to see whether it's a register. try: reg = self.get_reg(identifier) hit_flag = True except AmbiguousSymbolError as amb_reg: logger.warning( "Multiple registers found for {}. Use `search('*{}*')` to see " "the results clearer.\n".format(str(identifier), str(identifier))) apology += str(amb_reg) + "\n" # Also look to see whether it's a variable name/address. try: var = self.get_var(identifier, elf_id, datalen) hit_flag = True except AmbiguousSymbolError as amb_var: logger.warning( "Multiple variables found for {}. Use `search('*{}*')` to see " "the results clearer.\n".format(str(identifier), str(identifier))) apology += str(amb_var) + "\n" # We got at least one hit if hit_flag: if not pm_addr and not reg and not var: # We didn't find anything at all logger.warning( "Nothing was found! You can also use `search('*%s*')`.", identifier) return None elif pm_addr and not reg and not var: return pm_addr elif reg and not var and not pm_addr: return reg elif var and not reg and not pm_addr: return var else: # We got unique, valid hits for one or more of the above. apology = ( "Multiple matches found for {}. Use `search('*{}*')` to " "see the results clearer.\n".format( str(identifier), str(identifier))) # If we get here then we couldn't resolve ambiguous symbols in one or # more cases. Note any genuine match of the other types as well. if pm_addr: apology += " code address in module " + pm_addr.module_name + "\n" if var: apology += " variable " + var.name + "\n" if reg: apology += " register " + reg.name + "\n" raise AmbiguousSymbolError(apology)
def get(self, identifier, elf_id=None, datalen=None): """ @brief A 'smart' lookup funtion. 'identifier' can be an address, or the name of a register or variable (note constant names are not handled). If it's a name, we attempt to find the closest match in our lists of registers and variables. If the identifier is ambiguous, an AmbiguousSymbol exception may be thrown. Like get_var, it's also possible to pass in a data length to request a slice of data (this only makes sense for things in DM). Returns a DataSym (which may be a Variable) or a SourceInfo (if a code address was supplied). Obviously there are things that could go wrong here, especially if the user passes in a random number and we try to guess what it points to. That's why we only provide this function in Interactive mode! @param[in] self Pointer to the current object @param[in] identifier Could be name or address @param[in] elf_id The bundle elf id if the variable is in a downloadable capability. @param[in] datalen If the identifier is an address the data length is specified by this input. """ pm_addr = None reg = None var = None apology = "" hit_flag = False # For Crescendo, data can only be fetched as words. Since it is # octet-addressed, the addresses must be divisible with the number of # addresses per word (32 bit words - 4 octets, therefore addresses must # be divisble with 4). if isinstance(identifier, cu.INTEGERS): identifier = cu.get_correct_addr(identifier, Arch.addr_per_word) # Same as above. The lengths are measured in addressable units. if datalen is not None: datalen = cu.convert_byte_len_word(datalen, Arch.addr_per_word) # Look to see whether it's an address in PM. # Since the PM range can be huge (and may overlap with DM), perform a # sanity check to avoid false positives. if isinstance(identifier, cu.INTEGERS) and \ (Arch.get_pm_region(identifier, False) == "PMROM" or \ Arch.get_pm_region(identifier, False) == "PMRAM") and \ datalen is None: # Is it the address of a valid instruction? try: _ = self.debuginfo.get_instruction(identifier) except KeyError: pass else: pm_addr = self.debuginfo.get_source_info(identifier) # Look to see whether it's the name of a module in PM. This needs to # include all the fluff at the start, (i.e. '$M.foo.bar' rather than # just 'foo.bar' or 'bar') so as to avoid clashes with names in DM # space (which are more likely to be what's wanted). if isinstance(identifier, cu.INTEGERS): try: pm_addr = self.debuginfo.get_source_info(identifier) hit_flag = True except KeyError: pass except ct.AmbiguousSymbol as amb_pm: apology += str(amb_pm) + "\n" # Look to see whether it's a register. try: reg = self.get_reg(identifier) hit_flag = True except ct.AmbiguousSymbol as amb_reg: apology += str(amb_reg) + "\n" # Also look to see whether it's a variable name/address. try: var = self.get_var(identifier, elf_id, datalen) hit_flag = True except ct.AmbiguousSymbol as amb_var: apology += str(amb_var) + "\n" # We got at least one hit if hit_flag: if not pm_addr and not reg and not var: # We didn't find anything at all return None elif pm_addr and not reg and not var: return pm_addr elif reg and not var and not pm_addr: return reg elif var and not reg and not pm_addr: return var else: # We got unique, valid hits for one or more of the above. apology = "Multiple potential matches found for identifier '" + \ str(identifier) + "': \n" # If we get here then we couldn't resolve ambiguous symbols in one or more cases. # Note any genuine match of the other types as well. if pm_addr: apology += " code address in module " + pm_addr.module_name + "\n" if var: apology += " variable " + var.name + "\n" if reg: apology += " register " + reg.name + "\n" raise ct.AmbiguousSymbol(apology)