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 BuildSection(self, fd, base_offset):
146 """Write the section to a file"""
148 fd.write(self.GetData())
150 def BuildImage(self):
151 """Write the image to a file"""
152 fname = tools.GetOutputFilename(self._filename)
153 with open(fname, 'wb') as fd:
154 self.BuildSection(fd, 0)
157 """Write a map of the image to a .map file
160 Filename of map file written
162 filename = '%s.map' % self.image_name
163 fname = tools.GetOutputFilename(filename)
164 with open(fname, 'w') as fd:
165 print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
167 section.Entry_section.WriteMap(self, fd, 0)
170 def BuildEntryList(self):
171 """List the files in an image
174 List of entry.EntryInfo objects describing all entries in the image
177 self.ListEntries(entries, 0)
180 def FindEntryPath(self, entry_path):
181 """Find an entry at a given path in the image
184 entry_path: Path to entry (e.g. /ro-section/u-boot')
187 Entry object corresponding to that past
190 ValueError if no entry found
192 parts = entry_path.split('/')
193 entries = self.GetEntries()
196 entry = entries.get(part)
198 raise ValueError("Entry '%s' not found in '%s'" %
200 parent = entry.GetPath()
201 entries = entry.GetEntries()
204 def ReadData(self, decomp=True):
207 def GetListEntries(self, entry_paths):
208 """List the entries in an image
210 This decodes the supplied image and returns a list of entries from that
211 image, preceded by a header.
214 entry_paths: List of paths to match (each can have wildcards). Only
215 entries whose names match one of these paths will be printed
218 String error message if something went wrong, otherwise
220 List of EntryInfo objects
222 List of text columns, each a string
223 List of widths of each column
225 def _EntryToStrings(entry):
226 """Convert an entry to a list of strings, one for each column
229 entry: EntryInfo object containing information to output
232 List of strings, one for each field in entry
235 """Append a hex value, or an empty string if val is None
238 val: Integer value, or None if none
240 args.append('' if val is None else '>%x' % val)
242 args = [' ' * entry.indent + entry.name]
243 _AppendHex(entry.image_pos)
244 _AppendHex(entry.size)
245 args.append(entry.etype)
246 _AppendHex(entry.offset)
247 _AppendHex(entry.uncomp_size)
250 def _DoLine(lines, line):
251 """Add a line to the output list
253 This adds a line (a list of columns) to the output list. It also updates
254 the widths[] array with the maximum width of each column
257 lines: List of lines to add to
258 line: List of strings, one for each column
260 for i, item in enumerate(line):
261 widths[i] = max(widths[i], len(item))
264 def _NameInPaths(fname, entry_paths):
265 """Check if a filename is in a list of wildcarded paths
268 fname: Filename to check
269 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
273 True if any wildcard matches the filename (using Unix filename
274 pattern matching, not regular expressions)
277 for path in entry_paths:
278 if fnmatch.fnmatch(fname, path):
282 entries = self.BuildEntryList()
284 # This is our list of lines. Each item in the list is a list of strings, one
287 HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset',
289 num_columns = len(HEADER)
291 # This records the width of each column, calculated as the maximum width of
292 # all the strings in that column
293 widths = [0] * num_columns
294 _DoLine(lines, HEADER)
296 # We won't print anything unless it has at least this indent. So at the
297 # start we will print nothing, unless a path matches (or there are no
300 min_indent = MAX_INDENT
304 selected_entries = []
305 for entry in entries:
306 if entry.indent > indent:
307 path_stack.append(path)
308 elif entry.indent < indent:
311 path = path_stack[-1] + '/' + entry.name
312 indent = entry.indent
314 # If there are entry paths to match and we are not looking at a
315 # sub-entry of a previously matched entry, we need to check the path
316 if entry_paths and indent <= min_indent:
317 if _NameInPaths(path[1:], entry_paths):
318 # Print this entry and all sub-entries (=higher indent)
321 # Don't print this entry, nor any following entries until we get
323 min_indent = MAX_INDENT
325 _DoLine(lines, _EntryToStrings(entry))
326 selected_entries.append(entry)
327 return selected_entries, lines, widths