mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-03 21:48:15 +00:00 
			
		
		
		
	Some x86 sections have special offsets which currently result in empty data being returned from the 'extract' command. Fix this by taking account of the skip-at-start property. Add a little more debugging while we are here. Signed-off-by: Simon Glass <sjg@chromium.org> Acked-by: Bin Meng <bmeng.cn@gmail.com>
		
			
				
	
	
		
			329 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			329 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# 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 __future__ import print_function
 | 
						|
 | 
						|
from collections import OrderedDict
 | 
						|
import fnmatch
 | 
						|
from operator import attrgetter
 | 
						|
import os
 | 
						|
import re
 | 
						|
import sys
 | 
						|
 | 
						|
from entry import Entry
 | 
						|
from etype import fdtmap
 | 
						|
from etype import image_header
 | 
						|
from etype import section
 | 
						|
import fdt
 | 
						|
import fdt_util
 | 
						|
import tools
 | 
						|
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.
 | 
						|
    """
 | 
						|
    def __init__(self, name, node, copy_to_orig=True, test=False):
 | 
						|
        section.Entry_section.__init__(self, 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
 | 
						|
        if not test:
 | 
						|
            self.ReadNode()
 | 
						|
 | 
						|
    def ReadNode(self):
 | 
						|
        section.Entry_section.ReadNode(self)
 | 
						|
        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)
 | 
						|
 | 
						|
        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"""
 | 
						|
        section.Entry_section.Pack(self, 0)
 | 
						|
 | 
						|
    def SetImagePos(self):
 | 
						|
        # This first section in the image so it starts at 0
 | 
						|
        section.Entry_section.SetImagePos(self, 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
 | 
						|
        """
 | 
						|
        sizes_ok = True
 | 
						|
        for entry in self._entries.values():
 | 
						|
            if not entry.ProcessContents():
 | 
						|
                sizes_ok = False
 | 
						|
                tout.Debug("Entry '%s' size change" % self._node.path)
 | 
						|
        return sizes_ok
 | 
						|
 | 
						|
    def WriteSymbols(self):
 | 
						|
        """Write symbol values into binary files for access at run time"""
 | 
						|
        section.Entry_section.WriteSymbols(self, 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.GetData()
 | 
						|
            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)
 | 
						|
            section.Entry_section.WriteMap(self, 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
 |