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):
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
53 filename = fdt_util.GetString(self._node, 'filename')
55 self._filename = filename
58 def FromFile(cls, fname):
59 """Convert an image file into an Image for use in binman
62 fname: Filename of image file to read
65 Image object on success
68 ValueError if something goes wrong
70 data = tools.ReadFile(fname)
73 # First look for an image header
74 pos = image_header.LocateHeaderOffset(data)
76 # Look for the FDT map
77 pos = fdtmap.LocateFdtmap(data)
79 raise ValueError('Cannot find FDT map in image')
81 # We don't know the FDT size, so check its header first
82 probe_dtb = fdt.Fdt.FromData(
83 data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256])
84 dtb_size = probe_dtb.GetFdtObj().totalsize()
85 fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN]
86 dtb = fdt.Fdt.FromData(fdtmap_data[fdtmap.FDTMAP_HDR_LEN:])
89 # Return an Image with the associated nodes
91 image = Image('image', root)
92 image.image_node = fdt_util.GetString(root, 'image-node', 'image')
93 image.fdtmap_dtb = dtb
94 image.fdtmap_data = fdtmap_data
99 """Convenience function to raise an error referencing an image"""
100 raise ValueError("Image '%s': %s" % (self._node.path, msg))
102 def PackEntries(self):
103 """Pack all entries into the image"""
104 section.Entry_section.Pack(self, 0)
106 def SetImagePos(self):
107 # This first section in the image so it starts at 0
108 section.Entry_section.SetImagePos(self, 0)
110 def ProcessEntryContents(self):
111 """Call the ProcessContents() method for each entry
113 This is intended to adjust the contents as needed by the entry type.
116 True if the new data size is OK, False if expansion is needed
119 for entry in self._entries.values():
120 if not entry.ProcessContents():
122 tout.Debug("Entry '%s' size change" % self._node.path)
125 def WriteSymbols(self):
126 """Write symbol values into binary files for access at run time"""
127 section.Entry_section.WriteSymbols(self, self)
129 def BuildSection(self, fd, base_offset):
130 """Write the section to a file"""
132 fd.write(self.GetData())
134 def BuildImage(self):
135 """Write the image to a file"""
136 fname = tools.GetOutputFilename(self._filename)
137 with open(fname, 'wb') as fd:
138 self.BuildSection(fd, 0)
141 """Write a map of the image to a .map file
144 Filename of map file written
146 filename = '%s.map' % self.image_name
147 fname = tools.GetOutputFilename(filename)
148 with open(fname, 'w') as fd:
149 print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
151 section.Entry_section.WriteMap(self, fd, 0)
154 def BuildEntryList(self):
155 """List the files in an image
158 List of entry.EntryInfo objects describing all entries in the image
161 self.ListEntries(entries, 0)
164 def FindEntryPath(self, entry_path):
165 """Find an entry at a given path in the image
168 entry_path: Path to entry (e.g. /ro-section/u-boot')
171 Entry object corresponding to that past
174 ValueError if no entry found
176 parts = entry_path.split('/')
177 entries = self.GetEntries()
180 entry = entries.get(part)
182 raise ValueError("Entry '%s' not found in '%s'" %
184 parent = entry.GetPath()
185 entries = entry.GetEntries()
188 def ReadData(self, decomp=True):
191 def GetListEntries(self, entry_paths):
192 """List the entries in an image
194 This decodes the supplied image and returns a list of entries from that
195 image, preceded by a header.
198 entry_paths: List of paths to match (each can have wildcards). Only
199 entries whose names match one of these paths will be printed
202 String error message if something went wrong, otherwise
204 List of EntryInfo objects
206 List of text columns, each a string
207 List of widths of each column
209 def _EntryToStrings(entry):
210 """Convert an entry to a list of strings, one for each column
213 entry: EntryInfo object containing information to output
216 List of strings, one for each field in entry
219 """Append a hex value, or an empty string if val is None
222 val: Integer value, or None if none
224 args.append('' if val is None else '>%x' % val)
226 args = [' ' * entry.indent + entry.name]
227 _AppendHex(entry.image_pos)
228 _AppendHex(entry.size)
229 args.append(entry.etype)
230 _AppendHex(entry.offset)
231 _AppendHex(entry.uncomp_size)
234 def _DoLine(lines, line):
235 """Add a line to the output list
237 This adds a line (a list of columns) to the output list. It also updates
238 the widths[] array with the maximum width of each column
241 lines: List of lines to add to
242 line: List of strings, one for each column
244 for i, item in enumerate(line):
245 widths[i] = max(widths[i], len(item))
248 def _NameInPaths(fname, entry_paths):
249 """Check if a filename is in a list of wildcarded paths
252 fname: Filename to check
253 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
257 True if any wildcard matches the filename (using Unix filename
258 pattern matching, not regular expressions)
261 for path in entry_paths:
262 if fnmatch.fnmatch(fname, path):
266 entries = self.BuildEntryList()
268 # This is our list of lines. Each item in the list is a list of strings, one
271 HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset',
273 num_columns = len(HEADER)
275 # This records the width of each column, calculated as the maximum width of
276 # all the strings in that column
277 widths = [0] * num_columns
278 _DoLine(lines, HEADER)
280 # We won't print anything unless it has at least this indent. So at the
281 # start we will print nothing, unless a path matches (or there are no
284 min_indent = MAX_INDENT
288 selected_entries = []
289 for entry in entries:
290 if entry.indent > indent:
291 path_stack.append(path)
292 elif entry.indent < indent:
295 path = path_stack[-1] + '/' + entry.name
296 indent = entry.indent
298 # If there are entry paths to match and we are not looking at a
299 # sub-entry of a previously matched entry, we need to check the path
300 if entry_paths and indent <= min_indent:
301 if _NameInPaths(path[1:], entry_paths):
302 # Print this entry and all sub-entries (=higher indent)
305 # Don't print this entry, nor any following entries until we get
307 min_indent = MAX_INDENT
309 _DoLine(lines, _EntryToStrings(entry))
310 selected_entries.append(entry)
311 return selected_entries, lines, widths