1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
5 # Class for an image, the output of binman
8 from __future__ import print_function
10 from collections import OrderedDict
12 from operator import attrgetter
17 from entry import Entry
18 from etype import fdtmap
19 from etype import image_header
20 from etype import section
26 class Image(section.Entry_section):
27 """A Image, representing an output from binman
29 An image is comprised of a collection of entries each containing binary
30 data. The image size must be large enough to hold all of this data.
32 This class implements the various operations needed for images.
35 filename: Output filename for image
36 image_node: Name of node containing the description for this image
37 fdtmap_dtb: Fdt object for the fdtmap when loading from a file
38 fdtmap_data: Contents of the fdtmap when loading from a file
41 test: True if this is being called from a test of Images. This this case
42 there is no device tree defining the structure of the section, so
43 we create a section manually.
45 def __init__(self, name, node, test=False):
46 section.Entry_section.__init__(self, None, 'section', node, test)
47 self.name = 'main-section'
48 self.image_name = name
49 self._filename = '%s.bin' % self.image_name
50 self.fdtmap_dtb = None
51 self.fdtmap_data = None
56 section.Entry_section.ReadNode(self)
57 filename = fdt_util.GetString(self._node, 'filename')
59 self._filename = filename
62 def FromFile(cls, fname):
63 """Convert an image file into an Image for use in binman
66 fname: Filename of image file to read
69 Image object on success
72 ValueError if something goes wrong
74 data = tools.ReadFile(fname)
77 # First look for an image header
78 pos = image_header.LocateHeaderOffset(data)
80 # Look for the FDT map
81 pos = fdtmap.LocateFdtmap(data)
83 raise ValueError('Cannot find FDT map in image')
85 # We don't know the FDT size, so check its header first
86 probe_dtb = fdt.Fdt.FromData(
87 data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256])
88 dtb_size = probe_dtb.GetFdtObj().totalsize()
89 fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN]
90 dtb = fdt.Fdt.FromData(fdtmap_data[fdtmap.FDTMAP_HDR_LEN:])
93 # Return an Image with the associated nodes
95 image = Image('image', root)
96 image.image_node = fdt_util.GetString(root, 'image-node', 'image')
97 image.fdtmap_dtb = dtb
98 image.fdtmap_data = fdtmap_data
100 image._filename = fname
101 image.image_name, _ = os.path.splitext(fname)
104 def Raise(self, msg):
105 """Convenience function to raise an error referencing an image"""
106 raise ValueError("Image '%s': %s" % (self._node.path, msg))
108 def PackEntries(self):
109 """Pack all entries into the image"""
110 section.Entry_section.Pack(self, 0)
112 def SetImagePos(self):
113 # This first section in the image so it starts at 0
114 section.Entry_section.SetImagePos(self, 0)
116 def ProcessEntryContents(self):
117 """Call the ProcessContents() method for each entry
119 This is intended to adjust the contents as needed by the entry type.
122 True if the new data size is OK, False if expansion is needed
125 for entry in self._entries.values():
126 if not entry.ProcessContents():
128 tout.Debug("Entry '%s' size change" % self._node.path)
131 def WriteSymbols(self):
132 """Write symbol values into binary files for access at run time"""
133 section.Entry_section.WriteSymbols(self, self)
135 def BuildSection(self, fd, base_offset):
136 """Write the section to a file"""
138 fd.write(self.GetData())
140 def BuildImage(self):
141 """Write the image to a file"""
142 fname = tools.GetOutputFilename(self._filename)
143 with open(fname, 'wb') as fd:
144 self.BuildSection(fd, 0)
147 """Write a map of the image to a .map file
150 Filename of map file written
152 filename = '%s.map' % self.image_name
153 fname = tools.GetOutputFilename(filename)
154 with open(fname, 'w') as fd:
155 print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
157 section.Entry_section.WriteMap(self, fd, 0)
160 def BuildEntryList(self):
161 """List the files in an image
164 List of entry.EntryInfo objects describing all entries in the image
167 self.ListEntries(entries, 0)
170 def FindEntryPath(self, entry_path):
171 """Find an entry at a given path in the image
174 entry_path: Path to entry (e.g. /ro-section/u-boot')
177 Entry object corresponding to that past
180 ValueError if no entry found
182 parts = entry_path.split('/')
183 entries = self.GetEntries()
186 entry = entries.get(part)
188 raise ValueError("Entry '%s' not found in '%s'" %
190 parent = entry.GetPath()
191 entries = entry.GetEntries()
194 def ReadData(self, decomp=True):
197 def GetListEntries(self, entry_paths):
198 """List the entries in an image
200 This decodes the supplied image and returns a list of entries from that
201 image, preceded by a header.
204 entry_paths: List of paths to match (each can have wildcards). Only
205 entries whose names match one of these paths will be printed
208 String error message if something went wrong, otherwise
210 List of EntryInfo objects
212 List of text columns, each a string
213 List of widths of each column
215 def _EntryToStrings(entry):
216 """Convert an entry to a list of strings, one for each column
219 entry: EntryInfo object containing information to output
222 List of strings, one for each field in entry
225 """Append a hex value, or an empty string if val is None
228 val: Integer value, or None if none
230 args.append('' if val is None else '>%x' % val)
232 args = [' ' * entry.indent + entry.name]
233 _AppendHex(entry.image_pos)
234 _AppendHex(entry.size)
235 args.append(entry.etype)
236 _AppendHex(entry.offset)
237 _AppendHex(entry.uncomp_size)
240 def _DoLine(lines, line):
241 """Add a line to the output list
243 This adds a line (a list of columns) to the output list. It also updates
244 the widths[] array with the maximum width of each column
247 lines: List of lines to add to
248 line: List of strings, one for each column
250 for i, item in enumerate(line):
251 widths[i] = max(widths[i], len(item))
254 def _NameInPaths(fname, entry_paths):
255 """Check if a filename is in a list of wildcarded paths
258 fname: Filename to check
259 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
263 True if any wildcard matches the filename (using Unix filename
264 pattern matching, not regular expressions)
267 for path in entry_paths:
268 if fnmatch.fnmatch(fname, path):
272 entries = self.BuildEntryList()
274 # This is our list of lines. Each item in the list is a list of strings, one
277 HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset',
279 num_columns = len(HEADER)
281 # This records the width of each column, calculated as the maximum width of
282 # all the strings in that column
283 widths = [0] * num_columns
284 _DoLine(lines, HEADER)
286 # We won't print anything unless it has at least this indent. So at the
287 # start we will print nothing, unless a path matches (or there are no
290 min_indent = MAX_INDENT
294 selected_entries = []
295 for entry in entries:
296 if entry.indent > indent:
297 path_stack.append(path)
298 elif entry.indent < indent:
301 path = path_stack[-1] + '/' + entry.name
302 indent = entry.indent
304 # If there are entry paths to match and we are not looking at a
305 # sub-entry of a previously matched entry, we need to check the path
306 if entry_paths and indent <= min_indent:
307 if _NameInPaths(path[1:], entry_paths):
308 # Print this entry and all sub-entries (=higher indent)
311 # Don't print this entry, nor any following entries until we get
313 min_indent = MAX_INDENT
315 _DoLine(lines, _EntryToStrings(entry))
316 selected_entries.append(entry)
317 return selected_entries, lines, widths