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
25 class Image(section.Entry_section):
26 """A Image, representing an output from binman
28 An image is comprised of a collection of entries each containing binary
29 data. The image size must be large enough to hold all of this data.
31 This class implements the various operations needed for images.
34 filename: Output filename for image
35 image_node: Name of node containing the description for this image
36 fdtmap_dtb: Fdt object for the fdtmap when loading from a file
37 fdtmap_data: Contents of the fdtmap when loading from a file
40 test: True if this is being called from a test of Images. This this case
41 there is no device tree defining the structure of the section, so
42 we create a section manually.
44 def __init__(self, name, node, test=False):
45 section.Entry_section.__init__(self, None, 'section', node, test)
46 self.name = 'main-section'
47 self.image_name = name
48 self._filename = '%s.bin' % self.image_name
49 self.fdtmap_dtb = None
50 self.fdtmap_data = None
55 section.Entry_section.ReadNode(self)
56 filename = fdt_util.GetString(self._node, 'filename')
58 self._filename = filename
61 def FromFile(cls, fname):
62 """Convert an image file into an Image for use in binman
65 fname: Filename of image file to read
68 Image object on success
71 ValueError if something goes wrong
73 data = tools.ReadFile(fname)
76 # First look for an image header
77 pos = image_header.LocateHeaderOffset(data)
79 # Look for the FDT map
80 pos = fdtmap.LocateFdtmap(data)
82 raise ValueError('Cannot find FDT map in image')
84 # We don't know the FDT size, so check its header first
85 probe_dtb = fdt.Fdt.FromData(
86 data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256])
87 dtb_size = probe_dtb.GetFdtObj().totalsize()
88 fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN]
89 dtb = fdt.Fdt.FromData(fdtmap_data[fdtmap.FDTMAP_HDR_LEN:])
92 # Return an Image with the associated nodes
94 image = Image('image', root)
95 image.image_node = fdt_util.GetString(root, 'image-node', 'image')
96 image.fdtmap_dtb = dtb
97 image.fdtmap_data = fdtmap_data
101 def Raise(self, msg):
102 """Convenience function to raise an error referencing an image"""
103 raise ValueError("Image '%s': %s" % (self._node.path, msg))
105 def PackEntries(self):
106 """Pack all entries into the image"""
107 section.Entry_section.Pack(self, 0)
109 def SetImagePos(self):
110 # This first section in the image so it starts at 0
111 section.Entry_section.SetImagePos(self, 0)
113 def ProcessEntryContents(self):
114 """Call the ProcessContents() method for each entry
116 This is intended to adjust the contents as needed by the entry type.
119 True if the new data size is OK, False if expansion is needed
122 for entry in self._entries.values():
123 if not entry.ProcessContents():
125 tout.Debug("Entry '%s' size change" % self._node.path)
128 def WriteSymbols(self):
129 """Write symbol values into binary files for access at run time"""
130 section.Entry_section.WriteSymbols(self, self)
132 def BuildSection(self, fd, base_offset):
133 """Write the section to a file"""
135 fd.write(self.GetData())
137 def BuildImage(self):
138 """Write the image to a file"""
139 fname = tools.GetOutputFilename(self._filename)
140 with open(fname, 'wb') as fd:
141 self.BuildSection(fd, 0)
144 """Write a map of the image to a .map file
147 Filename of map file written
149 filename = '%s.map' % self.image_name
150 fname = tools.GetOutputFilename(filename)
151 with open(fname, 'w') as fd:
152 print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
154 section.Entry_section.WriteMap(self, fd, 0)
157 def BuildEntryList(self):
158 """List the files in an image
161 List of entry.EntryInfo objects describing all entries in the image
164 self.ListEntries(entries, 0)
167 def FindEntryPath(self, entry_path):
168 """Find an entry at a given path in the image
171 entry_path: Path to entry (e.g. /ro-section/u-boot')
174 Entry object corresponding to that past
177 ValueError if no entry found
179 parts = entry_path.split('/')
180 entries = self.GetEntries()
183 entry = entries.get(part)
185 raise ValueError("Entry '%s' not found in '%s'" %
187 parent = entry.GetPath()
188 entries = entry.GetEntries()
191 def ReadData(self, decomp=True):
194 def GetListEntries(self, entry_paths):
195 """List the entries in an image
197 This decodes the supplied image and returns a list of entries from that
198 image, preceded by a header.
201 entry_paths: List of paths to match (each can have wildcards). Only
202 entries whose names match one of these paths will be printed
205 String error message if something went wrong, otherwise
207 List of EntryInfo objects
209 List of text columns, each a string
210 List of widths of each column
212 def _EntryToStrings(entry):
213 """Convert an entry to a list of strings, one for each column
216 entry: EntryInfo object containing information to output
219 List of strings, one for each field in entry
222 """Append a hex value, or an empty string if val is None
225 val: Integer value, or None if none
227 args.append('' if val is None else '>%x' % val)
229 args = [' ' * entry.indent + entry.name]
230 _AppendHex(entry.image_pos)
231 _AppendHex(entry.size)
232 args.append(entry.etype)
233 _AppendHex(entry.offset)
234 _AppendHex(entry.uncomp_size)
237 def _DoLine(lines, line):
238 """Add a line to the output list
240 This adds a line (a list of columns) to the output list. It also updates
241 the widths[] array with the maximum width of each column
244 lines: List of lines to add to
245 line: List of strings, one for each column
247 for i, item in enumerate(line):
248 widths[i] = max(widths[i], len(item))
251 def _NameInPaths(fname, entry_paths):
252 """Check if a filename is in a list of wildcarded paths
255 fname: Filename to check
256 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
260 True if any wildcard matches the filename (using Unix filename
261 pattern matching, not regular expressions)
264 for path in entry_paths:
265 if fnmatch.fnmatch(fname, path):
269 entries = self.BuildEntryList()
271 # This is our list of lines. Each item in the list is a list of strings, one
274 HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset',
276 num_columns = len(HEADER)
278 # This records the width of each column, calculated as the maximum width of
279 # all the strings in that column
280 widths = [0] * num_columns
281 _DoLine(lines, HEADER)
283 # We won't print anything unless it has at least this indent. So at the
284 # start we will print nothing, unless a path matches (or there are no
287 min_indent = MAX_INDENT
291 selected_entries = []
292 for entry in entries:
293 if entry.indent > indent:
294 path_stack.append(path)
295 elif entry.indent < indent:
298 path = path_stack[-1] + '/' + entry.name
299 indent = entry.indent
301 # If there are entry paths to match and we are not looking at a
302 # sub-entry of a previously matched entry, we need to check the path
303 if entry_paths and indent <= min_indent:
304 if _NameInPaths(path[1:], entry_paths):
305 # Print this entry and all sub-entries (=higher indent)
308 # Don't print this entry, nor any following entries until we get
310 min_indent = MAX_INDENT
312 _DoLine(lines, _EntryToStrings(entry))
313 selected_entries.append(entry)
314 return selected_entries, lines, widths