mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 03:58:17 +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
 |