def get_build_output_path(chipdata): """Returns the path to the release build. Reads the build ID from the chip and tries to search for the released build based on the ID. Args: chipdata: Access to the chip. """ # If the build_output_path was not supplied, try to work it out from the # build ID. This obviously doesn't work for unreleased builds. build_id = chipdata.get_firmware_id() build_output_path = None if build_id == 0xFFFF: # Unreleased build! raise UsageError("ERROR: Path to build output not supplied, " "and build is unreleased!") else: try: from ACAT.Core.BuildFinder import BuildFinder build_finder = BuildFinder(build_id) build_output_path = os.path.join(build_finder.rom_build_path, 'debugbin') except ImportError: raise UsageError("ERROR: Path to build output not supplied.") return build_output_path
def poll_debug_log(self, wait_for_keypress=None): """Polls the debug_log buffer. If the contents of the buffer changes then it prints out the new extra contents. This function spawns a new thread to perform the polling operation. Note: This is only available on a live chip. Raises: UsageError: Invoked on something other than a live chip. UsageError: Debug Log is being polled. AnalysisError: The firmware does not have Debug Log symbols. """ if wait_for_keypress is None: if os.name == 'nt': wait_for_keypress = False else: wait_for_keypress = True if not self.chipdata.is_volatile(): raise UsageError( "ERROR: This functionality is only available on a live chip.\n" "Use get_cu.debug_log()") if not self._debug_logging.debuginfo_present: raise AnalysisError( "This firmware does not have symbols for the Debug log.\n" "Most likely it's not compiled in.") # If we're already polling don't do it again if self._polling.is_set(): raise UsageError("Debug log is being polled; " "you must stop polling to make this call!") # Use 'alert' to differentiate command responses from the debug log # contents self.formatter.alert("Polling debug log...") # start polling in s separate thread if not self._debug_logging.is_alive(): self._debug_logging.start() self._polling.set() if wait_for_keypress: self.formatter.alert("Press any key to stop.") # wait until a key is pressed. sys.stdin.read(1) self.stop_polling()
def get_debug_log(self): """Gets the (decoded) contents of the debug_log buffer. This could raise a number of different exception types, including AnalysisError. Returns: A list of debug strings, ordered from oldest to newest. Raises: AnalysisError: The firmware does not have Debug Log symbols. UsageError: Debug Log is being polled. """ if not self._debug_logging.debuginfo_present: raise AnalysisError( "This firmware does not have symbols for the Debug Log.\n" "Most likely it's not compiled in.") # Don't perform this action if polling is occurring if self._polling.is_set(): raise UsageError("Debug Log is being polled; " "you must stop polling to make this call!") return self._debug_logging.decode_log()
def set_debug_log_level(self, set_level): """Sets the debug log level to use. Note: This is only available on a live chip. Args: set_level (int) """ if not self.chipdata.is_volatile(): raise UsageError( "The debug log level can only be set on a live chip.") # Force to int, just in case the user supplied a string set_level = int(set_level) if set_level > 5 or set_level < 0: raise UsageError("Level must be in the range 0-5\n") else: # for the 32-bit architecture, the memory location where log_level # is might contain other values, therefore set_data() cannot be # used without any algorithm to preserve the other values since it # overwrites the whole word the offset of log_level in the word offset = self.log_level.address % Arch.addr_per_word # the mask to be applied to get the value of log_level from the # word mask = (int(math.pow(2, 8 * self.log_level.size) - 1)) << (8 * offset) value = self.chipdata.get_data(self.log_level.address) # set the log_level to 0, while preserving the other bits in the # word value &= (~mask) # add the set_level variable in the location of log_level in the # word value |= (set_level << (8 * offset)) self.chipdata.set_data(self.log_level.address, [value]) self.formatter.alert("Debug log level set to " + str(set_level) + "\n")
def __init__(self, coredump_lines, build_output_path, processor, formatter): # Initialise the specific information for a core: chipdata, # debuginfo and the functions used in Interactive mode that use # the specific information. self.processor = processor self._kalaccess = None self.available_analyses = {} if cu.global_options.live: if self.processor == 0: self._kalaccess = cu.global_options.kal else: self._kalaccess = cu.global_options.kal2 if cu.global_options.kalcmd_object is not None: from ACAT.Core.LiveKalcmd import LiveKalcmd self.chipdata = LiveKalcmd(cu.global_options.kalcmd_object, self.processor) else: from ACAT.Core.LiveSpi import LiveSpi if self.processor == 0: self.chipdata = LiveSpi( cu.global_options.kal, cu.global_options.spi_trans, self.processor, wait_for_proc_to_start=cu.global_options. wait_for_proc_to_start) else: self.chipdata = LiveSpi(cu.global_options.kal2, cu.global_options.spi_trans, self.processor, wait_for_proc_to_start=False) else: from ACAT.Core.Coredump import Coredump self.chipdata = Coredump(coredump_lines, processor) if build_output_path == "": # Try to get the build path automatically. If this fails it will # throw an exception and we'll bail. build_output_path = get_build_output_path(self.chipdata) # search for the elf file name import glob elf_files = glob.glob(os.path.join(build_output_path, '*.elf')) # Filter out the "_external.elf" files generated by some _release # builds -- we want the corresponding internal one with full # symbols (which we assume must exist). elf_files = [ elf_file for elf_file in elf_files if not elf_file.endswith("_external.elf") ] if len(elf_files) > 1: raise UsageError( "ERROR: Multiple elf files in the build output, " "don't know which to use.") # remove the .elf extension build_output_path = elf_files[0].replace(".elf", "") cu.global_options.build_output_path_p0 = build_output_path cu.global_options.build_output_path_p1 = build_output_path if processor == 0: if cu.global_options.kymera_p0 is None: self.debuginfo = DebugInformation(cu.global_options.ker) self.debuginfo.read_kymera_debuginfo(build_output_path) cu.global_options.kymera_p0 = self.debuginfo # If the two build are the same set the main build for the # other processor if cu.global_options.build_output_path_p0 == \ cu.global_options.build_output_path_p1: cu.global_options.kymera_p1 = \ cu.global_options.kymera_p0 else: self.debuginfo = cu.global_options.kymera_p0 else: if cu.global_options.kymera_p1 is None: self.debuginfo = DebugInformation(cu.global_options.ker) self.debuginfo.read_kymera_debuginfo(build_output_path) cu.global_options.kymera_p1 = self.debuginfo # If the two build are the same set the main build for the # other processor if cu.global_options.build_output_path_p0 == \ cu.global_options.build_output_path_p1: cu.global_options.kymera_p0 = \ cu.global_options.kymera_p1 else: self.debuginfo = cu.global_options.kymera_p1 # check if there are any bundles that needs reading if cu.global_options.bundle_paths is not None: # check if they were already read if cu.global_options.bundles is None: # if not than read all of them bundles_dictionary = {} for bundle_path in cu.global_options.bundle_paths: bundles_dictionary.update(load_bundle(bundle_path)) # and save it in global_options to avoid reading them multiple # times. cu.global_options.bundles = bundles_dictionary # set the bundle dictionary. self.debuginfo.update_bundles(cu.global_options.bundles) self.debuginfo.set_table(MappedTable(self.chipdata, self.debuginfo)) # Set the debug info for the chipdata self.chipdata.set_debuginfo(self.debuginfo) super(Processor, self).__init__(self.chipdata, self.debuginfo, formatter) # Patch the processor ProcessorPatcher(self, patch_path=cu.global_options.patch).apply() self.formatter = formatter