diff options
Diffstat (limited to 'roms/u-boot/tools/binman/image.py')
-rw-r--r-- | roms/u-boot/tools/binman/image.py | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/roms/u-boot/tools/binman/image.py b/roms/u-boot/tools/binman/image.py new file mode 100644 index 000000000..10778f47f --- /dev/null +++ b/roms/u-boot/tools/binman/image.py @@ -0,0 +1,387 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Class for an image, the output of binman +# + +from collections import OrderedDict +import fnmatch +from operator import attrgetter +import os +import re +import sys + +from binman.entry import Entry +from binman.etype import fdtmap +from binman.etype import image_header +from binman.etype import section +from dtoc import fdt +from dtoc import fdt_util +from patman import tools +from patman import tout + +class Image(section.Entry_section): + """A Image, representing an output from binman + + An image is comprised of a collection of entries each containing binary + data. The image size must be large enough to hold all of this data. + + This class implements the various operations needed for images. + + Attributes: + filename: Output filename for image + image_node: Name of node containing the description for this image + fdtmap_dtb: Fdt object for the fdtmap when loading from a file + fdtmap_data: Contents of the fdtmap when loading from a file + allow_repack: True to add properties to allow the image to be safely + repacked later + + Args: + copy_to_orig: Copy offset/size to orig_offset/orig_size after reading + from the device tree + test: True if this is being called from a test of Images. This this case + there is no device tree defining the structure of the section, so + we create a section manually. + ignore_missing: Ignore any missing entry arguments (i.e. don't raise an + exception). This should be used if the Image is being loaded from + a file rather than generated. In that case we obviously don't need + the entry arguments since the contents already exists. + use_expanded: True if we are updating the FDT wth entry offsets, etc. + and should use the expanded versions of the U-Boot entries. + Any entry type that includes a devicetree must put it in a + separate entry so that it will be updated. For example. 'u-boot' + normally just picks up 'u-boot.bin' which includes the + devicetree, but this is not updateable, since it comes into + binman as one piece and binman doesn't know that it is actually + an executable followed by a devicetree. Of course it could be + taught this, but then when reading an image (e.g. 'binman ls') + it may need to be able to split the devicetree out of the image + in order to determine the location of things. Instead we choose + to ignore 'u-boot-bin' in this case, and build it ourselves in + binman with 'u-boot-dtb.bin' and 'u-boot.dtb'. See + Entry_u_boot_expanded and Entry_blob_phase for details. + """ + def __init__(self, name, node, copy_to_orig=True, test=False, + ignore_missing=False, use_expanded=False): + super().__init__(None, 'section', node, test=test) + self.copy_to_orig = copy_to_orig + self.name = 'main-section' + self.image_name = name + self._filename = '%s.bin' % self.image_name + self.fdtmap_dtb = None + self.fdtmap_data = None + self.allow_repack = False + self._ignore_missing = ignore_missing + self.use_expanded = use_expanded + if not test: + self.ReadNode() + + def ReadNode(self): + super().ReadNode() + filename = fdt_util.GetString(self._node, 'filename') + if filename: + self._filename = filename + self.allow_repack = fdt_util.GetBool(self._node, 'allow-repack') + + @classmethod + def FromFile(cls, fname): + """Convert an image file into an Image for use in binman + + Args: + fname: Filename of image file to read + + Returns: + Image object on success + + Raises: + ValueError if something goes wrong + """ + data = tools.ReadFile(fname) + size = len(data) + + # First look for an image header + pos = image_header.LocateHeaderOffset(data) + if pos is None: + # Look for the FDT map + pos = fdtmap.LocateFdtmap(data) + if pos is None: + raise ValueError('Cannot find FDT map in image') + + # We don't know the FDT size, so check its header first + probe_dtb = fdt.Fdt.FromData( + data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256]) + dtb_size = probe_dtb.GetFdtObj().totalsize() + fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN] + fdt_data = fdtmap_data[fdtmap.FDTMAP_HDR_LEN:] + out_fname = tools.GetOutputFilename('fdtmap.in.dtb') + tools.WriteFile(out_fname, fdt_data) + dtb = fdt.Fdt(out_fname) + dtb.Scan() + + # Return an Image with the associated nodes + root = dtb.GetRoot() + image = Image('image', root, copy_to_orig=False, ignore_missing=True) + + image.image_node = fdt_util.GetString(root, 'image-node', 'image') + image.fdtmap_dtb = dtb + image.fdtmap_data = fdtmap_data + image._data = data + image._filename = fname + image.image_name, _ = os.path.splitext(fname) + return image + + def Raise(self, msg): + """Convenience function to raise an error referencing an image""" + raise ValueError("Image '%s': %s" % (self._node.path, msg)) + + def PackEntries(self): + """Pack all entries into the image""" + super().Pack(0) + + def SetImagePos(self): + # This first section in the image so it starts at 0 + super().SetImagePos(0) + + def ProcessEntryContents(self): + """Call the ProcessContents() method for each entry + + This is intended to adjust the contents as needed by the entry type. + + Returns: + True if the new data size is OK, False if expansion is needed + """ + return super().ProcessContents() + + def WriteSymbols(self): + """Write symbol values into binary files for access at run time""" + super().WriteSymbols(self) + + def BuildImage(self): + """Write the image to a file""" + fname = tools.GetOutputFilename(self._filename) + tout.Info("Writing image to '%s'" % fname) + with open(fname, 'wb') as fd: + data = self.GetPaddedData() + fd.write(data) + tout.Info("Wrote %#x bytes" % len(data)) + + def WriteMap(self): + """Write a map of the image to a .map file + + Returns: + Filename of map file written + """ + filename = '%s.map' % self.image_name + fname = tools.GetOutputFilename(filename) + with open(fname, 'w') as fd: + print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'), + file=fd) + super().WriteMap(fd, 0) + return fname + + def BuildEntryList(self): + """List the files in an image + + Returns: + List of entry.EntryInfo objects describing all entries in the image + """ + entries = [] + self.ListEntries(entries, 0) + return entries + + def FindEntryPath(self, entry_path): + """Find an entry at a given path in the image + + Args: + entry_path: Path to entry (e.g. /ro-section/u-boot') + + Returns: + Entry object corresponding to that past + + Raises: + ValueError if no entry found + """ + parts = entry_path.split('/') + entries = self.GetEntries() + parent = '/' + for part in parts: + entry = entries.get(part) + if not entry: + raise ValueError("Entry '%s' not found in '%s'" % + (part, parent)) + parent = entry.GetPath() + entries = entry.GetEntries() + return entry + + def ReadData(self, decomp=True): + tout.Debug("Image '%s' ReadData(), size=%#x" % + (self.GetPath(), len(self._data))) + return self._data + + def GetListEntries(self, entry_paths): + """List the entries in an image + + This decodes the supplied image and returns a list of entries from that + image, preceded by a header. + + Args: + entry_paths: List of paths to match (each can have wildcards). Only + entries whose names match one of these paths will be printed + + Returns: + String error message if something went wrong, otherwise + 3-Tuple: + List of EntryInfo objects + List of lines, each + List of text columns, each a string + List of widths of each column + """ + def _EntryToStrings(entry): + """Convert an entry to a list of strings, one for each column + + Args: + entry: EntryInfo object containing information to output + + Returns: + List of strings, one for each field in entry + """ + def _AppendHex(val): + """Append a hex value, or an empty string if val is None + + Args: + val: Integer value, or None if none + """ + args.append('' if val is None else '>%x' % val) + + args = [' ' * entry.indent + entry.name] + _AppendHex(entry.image_pos) + _AppendHex(entry.size) + args.append(entry.etype) + _AppendHex(entry.offset) + _AppendHex(entry.uncomp_size) + return args + + def _DoLine(lines, line): + """Add a line to the output list + + This adds a line (a list of columns) to the output list. It also updates + the widths[] array with the maximum width of each column + + Args: + lines: List of lines to add to + line: List of strings, one for each column + """ + for i, item in enumerate(line): + widths[i] = max(widths[i], len(item)) + lines.append(line) + + def _NameInPaths(fname, entry_paths): + """Check if a filename is in a list of wildcarded paths + + Args: + fname: Filename to check + entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*', + 'section/u-boot']) + + Returns: + True if any wildcard matches the filename (using Unix filename + pattern matching, not regular expressions) + False if not + """ + for path in entry_paths: + if fnmatch.fnmatch(fname, path): + return True + return False + + entries = self.BuildEntryList() + + # This is our list of lines. Each item in the list is a list of strings, one + # for each column + lines = [] + HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset', + 'Uncomp-size'] + num_columns = len(HEADER) + + # This records the width of each column, calculated as the maximum width of + # all the strings in that column + widths = [0] * num_columns + _DoLine(lines, HEADER) + + # We won't print anything unless it has at least this indent. So at the + # start we will print nothing, unless a path matches (or there are no + # entry paths) + MAX_INDENT = 100 + min_indent = MAX_INDENT + path_stack = [] + path = '' + indent = 0 + selected_entries = [] + for entry in entries: + if entry.indent > indent: + path_stack.append(path) + elif entry.indent < indent: + path_stack.pop() + if path_stack: + path = path_stack[-1] + '/' + entry.name + indent = entry.indent + + # If there are entry paths to match and we are not looking at a + # sub-entry of a previously matched entry, we need to check the path + if entry_paths and indent <= min_indent: + if _NameInPaths(path[1:], entry_paths): + # Print this entry and all sub-entries (=higher indent) + min_indent = indent + else: + # Don't print this entry, nor any following entries until we get + # a path match + min_indent = MAX_INDENT + continue + _DoLine(lines, _EntryToStrings(entry)) + selected_entries.append(entry) + return selected_entries, lines, widths + + def LookupImageSymbol(self, sym_name, optional, msg, base_addr): + """Look up a symbol in an ELF file + + Looks up a symbol in an ELF file. Only entry types which come from an + ELF image can be used by this function. + + This searches through this image including all of its subsections. + + At present the only entry properties supported are: + offset + image_pos - 'base_addr' is added if this is not an end-at-4gb image + size + + Args: + sym_name: Symbol name in the ELF file to look up in the format + _binman_<entry>_prop_<property> where <entry> is the name of + the entry and <property> is the property to find (e.g. + _binman_u_boot_prop_offset). As a special case, you can append + _any to <entry> to have it search for any matching entry. E.g. + _binman_u_boot_any_prop_offset will match entries called u-boot, + u-boot-img and u-boot-nodtb) + optional: True if the symbol is optional. If False this function + will raise if the symbol is not found + msg: Message to display if an error occurs + base_addr: Base address of image. This is added to the returned + image_pos in most cases so that the returned position indicates + where the targeted entry/binary has actually been loaded. But + if end-at-4gb is used, this is not done, since the binary is + already assumed to be linked to the ROM position and using + execute-in-place (XIP). + + Returns: + Value that should be assigned to that symbol, or None if it was + optional and not found + + Raises: + ValueError if the symbol is invalid or not found, or references a + property which is not supported + """ + entries = OrderedDict() + entries_by_name = {} + self._CollectEntries(entries, entries_by_name, self) + return self.LookupSymbol(sym_name, optional, msg, base_addr, + entries_by_name) |