Пример #1
0
    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
Пример #3
0
    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)
Пример #4
0
    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
Пример #5
0
    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)
Пример #8
0
    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)