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
16 from entry import Entry
17 from etype import fdtmap
18 from etype import image_header
19 from etype import section
24 class Image(section.Entry_section):
25 """A Image, representing an output from binman
27 An image is comprised of a collection of entries each containing binary
28 data. The image size must be large enough to hold all of this data.
30 This class implements the various operations needed for images.
33 filename: Output filename for image
36 test: True if this is being called from a test of Images. This this case
37 there is no device tree defining the structure of the section, so
38 we create a section manually.
40 def __init__(self, name, node, test=False):
42 section.Entry_section.__init__(self, None, 'section', node, test)
43 self.name = 'main-section'
44 self.image_name = name
45 self._filename = '%s.bin' % self.image_name
47 filename = fdt_util.GetString(self._node, 'filename')
49 self._filename = filename
52 def FromFile(cls, fname):
53 """Convert an image file into an Image for use in binman
56 fname: Filename of image file to read
59 Image object on success
62 ValueError if something goes wrong
64 data = tools.ReadFile(fname)
67 # First look for an image header
68 pos = image_header.LocateHeaderOffset(data)
70 # Look for the FDT map
71 pos = fdtmap.LocateFdtmap(data)
73 raise ValueError('Cannot find FDT map in image')
75 # We don't know the FDT size, so check its header first
76 probe_dtb = fdt.Fdt.FromData(
77 data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256])
78 dtb_size = probe_dtb.GetFdtObj().totalsize()
79 fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN]
80 dtb = fdt.Fdt.FromData(fdtmap_data[fdtmap.FDTMAP_HDR_LEN:])
83 # Return an Image with the associated nodes
84 return Image('image', dtb.GetRoot())
87 """Convenience function to raise an error referencing an image"""
88 raise ValueError("Image '%s': %s" % (self._node.path, msg))
90 def PackEntries(self):
91 """Pack all entries into the image"""
92 section.Entry_section.Pack(self, 0)
94 def SetImagePos(self):
95 # This first section in the image so it starts at 0
96 section.Entry_section.SetImagePos(self, 0)
98 def ProcessEntryContents(self):
99 """Call the ProcessContents() method for each entry
101 This is intended to adjust the contents as needed by the entry type.
104 True if the new data size is OK, False if expansion is needed
107 for entry in self._entries.values():
108 if not entry.ProcessContents():
110 print("Entry '%s' size change" % self._node.path)
113 def WriteSymbols(self):
114 """Write symbol values into binary files for access at run time"""
115 section.Entry_section.WriteSymbols(self, self)
117 def BuildSection(self, fd, base_offset):
118 """Write the section to a file"""
120 fd.write(self.GetData())
122 def BuildImage(self):
123 """Write the image to a file"""
124 fname = tools.GetOutputFilename(self._filename)
125 with open(fname, 'wb') as fd:
126 self.BuildSection(fd, 0)
129 """Write a map of the image to a .map file
132 Filename of map file written
134 filename = '%s.map' % self.image_name
135 fname = tools.GetOutputFilename(filename)
136 with open(fname, 'w') as fd:
137 print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
139 section.Entry_section.WriteMap(self, fd, 0)
142 def BuildEntryList(self):
143 """List the files in an image
146 List of entry.EntryInfo objects describing all entries in the image
149 self.ListEntries(entries, 0)
152 def FindEntryPath(self, entry_path):
153 """Find an entry at a given path in the image
156 entry_path: Path to entry (e.g. /ro-section/u-boot')
159 Entry object corresponding to that past
162 ValueError if no entry found
164 parts = entry_path.split('/')
165 entries = self.GetEntries()
168 entry = entries.get(part)
170 raise ValueError("Entry '%s' not found in '%s'" %
172 parent = entry.GetPath()
173 entries = entry.GetEntries()
176 def ReadData(self, decomp=True):
179 def GetListEntries(self, entry_paths):
180 """List the entries in an image
182 This decodes the supplied image and returns a list of entries from that
183 image, preceded by a header.
186 entry_paths: List of paths to match (each can have wildcards). Only
187 entries whose names match one of these paths will be printed
190 String error message if something went wrong, otherwise
192 List of EntryInfo objects
194 List of text columns, each a string
195 List of widths of each column
197 def _EntryToStrings(entry):
198 """Convert an entry to a list of strings, one for each column
201 entry: EntryInfo object containing information to output
204 List of strings, one for each field in entry
207 """Append a hex value, or an empty string if val is None
210 val: Integer value, or None if none
212 args.append('' if val is None else '>%x' % val)
214 args = [' ' * entry.indent + entry.name]
215 _AppendHex(entry.image_pos)
216 _AppendHex(entry.size)
217 args.append(entry.etype)
218 _AppendHex(entry.offset)
219 _AppendHex(entry.uncomp_size)
222 def _DoLine(lines, line):
223 """Add a line to the output list
225 This adds a line (a list of columns) to the output list. It also updates
226 the widths[] array with the maximum width of each column
229 lines: List of lines to add to
230 line: List of strings, one for each column
232 for i, item in enumerate(line):
233 widths[i] = max(widths[i], len(item))
236 def _NameInPaths(fname, entry_paths):
237 """Check if a filename is in a list of wildcarded paths
240 fname: Filename to check
241 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
245 True if any wildcard matches the filename (using Unix filename
246 pattern matching, not regular expressions)
249 for path in entry_paths:
250 if fnmatch.fnmatch(fname, path):
254 entries = self.BuildEntryList()
256 # This is our list of lines. Each item in the list is a list of strings, one
259 HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset',
261 num_columns = len(HEADER)
263 # This records the width of each column, calculated as the maximum width of
264 # all the strings in that column
265 widths = [0] * num_columns
266 _DoLine(lines, HEADER)
268 # We won't print anything unless it has at least this indent. So at the
269 # start we will print nothing, unless a path matches (or there are no
272 min_indent = MAX_INDENT
276 selected_entries = []
277 for entry in entries:
278 if entry.indent > indent:
279 path_stack.append(path)
280 elif entry.indent < indent:
283 path = path_stack[-1] + '/' + entry.name
284 indent = entry.indent
286 # If there are entry paths to match and we are not looking at a
287 # sub-entry of a previously matched entry, we need to check the path
288 if entry_paths and indent <= min_indent:
289 if _NameInPaths(path[1:], entry_paths):
290 # Print this entry and all sub-entries (=higher indent)
293 # Don't print this entry, nor any following entries until we get
295 min_indent = MAX_INDENT
297 _DoLine(lines, _EntryToStrings(entry))
298 selected_entries.append(entry)
299 return selected_entries, lines, widths