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
39 allow_repack: True to add properties to allow the image to be safely
43 copy_to_orig: Copy offset/size to orig_offset/orig_size after reading
45 test: True if this is being called from a test of Images. This this case
46 there is no device tree defining the structure of the section, so
47 we create a section manually.
49 def __init__(self, name, node, copy_to_orig=True, test=False):
50 section.Entry_section.__init__(self, None, 'section', node, test=test)
51 self.copy_to_orig = copy_to_orig
52 self.name = 'main-section'
53 self.image_name = name
54 self._filename = '%s.bin' % self.image_name
55 self.fdtmap_dtb = None
56 self.fdtmap_data = None
57 self.allow_repack = False
62 section.Entry_section.ReadNode(self)
63 filename = fdt_util.GetString(self._node, 'filename')
65 self._filename = filename
66 self.allow_repack = fdt_util.GetBool(self._node, 'allow-repack')
69 def FromFile(cls, fname):
70 """Convert an image file into an Image for use in binman
73 fname: Filename of image file to read
76 Image object on success
79 ValueError if something goes wrong
81 data = tools.ReadFile(fname)
84 # First look for an image header
85 pos = image_header.LocateHeaderOffset(data)
87 # Look for the FDT map
88 pos = fdtmap.LocateFdtmap(data)
90 raise ValueError('Cannot find FDT map in image')
92 # We don't know the FDT size, so check its header first
93 probe_dtb = fdt.Fdt.FromData(
94 data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256])
95 dtb_size = probe_dtb.GetFdtObj().totalsize()
96 fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN]
97 fdt_data = fdtmap_data[fdtmap.FDTMAP_HDR_LEN:]
98 out_fname = tools.GetOutputFilename('fdtmap.in.dtb')
99 tools.WriteFile(out_fname, fdt_data)
100 dtb = fdt.Fdt.FromData(fdt_data, out_fname)
103 # Return an Image with the associated nodes
105 image = Image('image', root, copy_to_orig=False)
106 image.image_node = fdt_util.GetString(root, 'image-node', 'image')
107 image.fdtmap_dtb = dtb
108 image.fdtmap_data = fdtmap_data
110 image._filename = fname
111 image.image_name, _ = os.path.splitext(fname)
114 def Raise(self, msg):
115 """Convenience function to raise an error referencing an image"""
116 raise ValueError("Image '%s': %s" % (self._node.path, msg))
118 def PackEntries(self):
119 """Pack all entries into the image"""
120 section.Entry_section.Pack(self, 0)
122 def SetImagePos(self):
123 # This first section in the image so it starts at 0
124 section.Entry_section.SetImagePos(self, 0)
126 def ProcessEntryContents(self):
127 """Call the ProcessContents() method for each entry
129 This is intended to adjust the contents as needed by the entry type.
132 True if the new data size is OK, False if expansion is needed
135 for entry in self._entries.values():
136 if not entry.ProcessContents():
138 tout.Debug("Entry '%s' size change" % self._node.path)
141 def WriteSymbols(self):
142 """Write symbol values into binary files for access at run time"""
143 section.Entry_section.WriteSymbols(self, self)
145 def BuildImage(self):
146 """Write the image to a file"""
147 fname = tools.GetOutputFilename(self._filename)
148 tout.Info("Writing image to '%s'" % fname)
149 with open(fname, 'wb') as fd:
150 data = self.GetData()
152 tout.Info("Wrote %#x bytes" % len(data))
155 """Write a map of the image to a .map file
158 Filename of map file written
160 filename = '%s.map' % self.image_name
161 fname = tools.GetOutputFilename(filename)
162 with open(fname, 'w') as fd:
163 print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
165 section.Entry_section.WriteMap(self, fd, 0)
168 def BuildEntryList(self):
169 """List the files in an image
172 List of entry.EntryInfo objects describing all entries in the image
175 self.ListEntries(entries, 0)
178 def FindEntryPath(self, entry_path):
179 """Find an entry at a given path in the image
182 entry_path: Path to entry (e.g. /ro-section/u-boot')
185 Entry object corresponding to that past
188 ValueError if no entry found
190 parts = entry_path.split('/')
191 entries = self.GetEntries()
194 entry = entries.get(part)
196 raise ValueError("Entry '%s' not found in '%s'" %
198 parent = entry.GetPath()
199 entries = entry.GetEntries()
202 def ReadData(self, decomp=True):
205 def GetListEntries(self, entry_paths):
206 """List the entries in an image
208 This decodes the supplied image and returns a list of entries from that
209 image, preceded by a header.
212 entry_paths: List of paths to match (each can have wildcards). Only
213 entries whose names match one of these paths will be printed
216 String error message if something went wrong, otherwise
218 List of EntryInfo objects
220 List of text columns, each a string
221 List of widths of each column
223 def _EntryToStrings(entry):
224 """Convert an entry to a list of strings, one for each column
227 entry: EntryInfo object containing information to output
230 List of strings, one for each field in entry
233 """Append a hex value, or an empty string if val is None
236 val: Integer value, or None if none
238 args.append('' if val is None else '>%x' % val)
240 args = [' ' * entry.indent + entry.name]
241 _AppendHex(entry.image_pos)
242 _AppendHex(entry.size)
243 args.append(entry.etype)
244 _AppendHex(entry.offset)
245 _AppendHex(entry.uncomp_size)
248 def _DoLine(lines, line):
249 """Add a line to the output list
251 This adds a line (a list of columns) to the output list. It also updates
252 the widths[] array with the maximum width of each column
255 lines: List of lines to add to
256 line: List of strings, one for each column
258 for i, item in enumerate(line):
259 widths[i] = max(widths[i], len(item))
262 def _NameInPaths(fname, entry_paths):
263 """Check if a filename is in a list of wildcarded paths
266 fname: Filename to check
267 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
271 True if any wildcard matches the filename (using Unix filename
272 pattern matching, not regular expressions)
275 for path in entry_paths:
276 if fnmatch.fnmatch(fname, path):
280 entries = self.BuildEntryList()
282 # This is our list of lines. Each item in the list is a list of strings, one
285 HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset',
287 num_columns = len(HEADER)
289 # This records the width of each column, calculated as the maximum width of
290 # all the strings in that column
291 widths = [0] * num_columns
292 _DoLine(lines, HEADER)
294 # We won't print anything unless it has at least this indent. So at the
295 # start we will print nothing, unless a path matches (or there are no
298 min_indent = MAX_INDENT
302 selected_entries = []
303 for entry in entries:
304 if entry.indent > indent:
305 path_stack.append(path)
306 elif entry.indent < indent:
309 path = path_stack[-1] + '/' + entry.name
310 indent = entry.indent
312 # If there are entry paths to match and we are not looking at a
313 # sub-entry of a previously matched entry, we need to check the path
314 if entry_paths and indent <= min_indent:
315 if _NameInPaths(path[1:], entry_paths):
316 # Print this entry and all sub-entries (=higher indent)
319 # Don't print this entry, nor any following entries until we get
321 min_indent = MAX_INDENT
323 _DoLine(lines, _EntryToStrings(entry))
324 selected_entries.append(entry)
325 return selected_entries, lines, widths