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(out_fname)
103 # Return an Image with the associated nodes
105 image = Image('image', root, copy_to_orig=False)
107 image.image_node = fdt_util.GetString(root, 'image-node', 'image')
108 image.fdtmap_dtb = dtb
109 image.fdtmap_data = fdtmap_data
111 image._filename = fname
112 image.image_name, _ = os.path.splitext(fname)
115 def Raise(self, msg):
116 """Convenience function to raise an error referencing an image"""
117 raise ValueError("Image '%s': %s" % (self._node.path, msg))
119 def PackEntries(self):
120 """Pack all entries into the image"""
121 section.Entry_section.Pack(self, 0)
123 def SetImagePos(self):
124 # This first section in the image so it starts at 0
125 section.Entry_section.SetImagePos(self, 0)
127 def ProcessEntryContents(self):
128 """Call the ProcessContents() method for each entry
130 This is intended to adjust the contents as needed by the entry type.
133 True if the new data size is OK, False if expansion is needed
136 for entry in self._entries.values():
137 if not entry.ProcessContents():
139 tout.Debug("Entry '%s' size change" % self._node.path)
142 def WriteSymbols(self):
143 """Write symbol values into binary files for access at run time"""
144 section.Entry_section.WriteSymbols(self, self)
146 def BuildImage(self):
147 """Write the image to a file"""
148 fname = tools.GetOutputFilename(self._filename)
149 tout.Info("Writing image to '%s'" % fname)
150 with open(fname, 'wb') as fd:
151 data = self.GetData()
153 tout.Info("Wrote %#x bytes" % len(data))
156 """Write a map of the image to a .map file
159 Filename of map file written
161 filename = '%s.map' % self.image_name
162 fname = tools.GetOutputFilename(filename)
163 with open(fname, 'w') as fd:
164 print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
166 section.Entry_section.WriteMap(self, fd, 0)
169 def BuildEntryList(self):
170 """List the files in an image
173 List of entry.EntryInfo objects describing all entries in the image
176 self.ListEntries(entries, 0)
179 def FindEntryPath(self, entry_path):
180 """Find an entry at a given path in the image
183 entry_path: Path to entry (e.g. /ro-section/u-boot')
186 Entry object corresponding to that past
189 ValueError if no entry found
191 parts = entry_path.split('/')
192 entries = self.GetEntries()
195 entry = entries.get(part)
197 raise ValueError("Entry '%s' not found in '%s'" %
199 parent = entry.GetPath()
200 entries = entry.GetEntries()
203 def ReadData(self, decomp=True):
206 def GetListEntries(self, entry_paths):
207 """List the entries in an image
209 This decodes the supplied image and returns a list of entries from that
210 image, preceded by a header.
213 entry_paths: List of paths to match (each can have wildcards). Only
214 entries whose names match one of these paths will be printed
217 String error message if something went wrong, otherwise
219 List of EntryInfo objects
221 List of text columns, each a string
222 List of widths of each column
224 def _EntryToStrings(entry):
225 """Convert an entry to a list of strings, one for each column
228 entry: EntryInfo object containing information to output
231 List of strings, one for each field in entry
234 """Append a hex value, or an empty string if val is None
237 val: Integer value, or None if none
239 args.append('' if val is None else '>%x' % val)
241 args = [' ' * entry.indent + entry.name]
242 _AppendHex(entry.image_pos)
243 _AppendHex(entry.size)
244 args.append(entry.etype)
245 _AppendHex(entry.offset)
246 _AppendHex(entry.uncomp_size)
249 def _DoLine(lines, line):
250 """Add a line to the output list
252 This adds a line (a list of columns) to the output list. It also updates
253 the widths[] array with the maximum width of each column
256 lines: List of lines to add to
257 line: List of strings, one for each column
259 for i, item in enumerate(line):
260 widths[i] = max(widths[i], len(item))
263 def _NameInPaths(fname, entry_paths):
264 """Check if a filename is in a list of wildcarded paths
267 fname: Filename to check
268 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
272 True if any wildcard matches the filename (using Unix filename
273 pattern matching, not regular expressions)
276 for path in entry_paths:
277 if fnmatch.fnmatch(fname, path):
281 entries = self.BuildEntryList()
283 # This is our list of lines. Each item in the list is a list of strings, one
286 HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset',
288 num_columns = len(HEADER)
290 # This records the width of each column, calculated as the maximum width of
291 # all the strings in that column
292 widths = [0] * num_columns
293 _DoLine(lines, HEADER)
295 # We won't print anything unless it has at least this indent. So at the
296 # start we will print nothing, unless a path matches (or there are no
299 min_indent = MAX_INDENT
303 selected_entries = []
304 for entry in entries:
305 if entry.indent > indent:
306 path_stack.append(path)
307 elif entry.indent < indent:
310 path = path_stack[-1] + '/' + entry.name
311 indent = entry.indent
313 # If there are entry paths to match and we are not looking at a
314 # sub-entry of a previously matched entry, we need to check the path
315 if entry_paths and indent <= min_indent:
316 if _NameInPaths(path[1:], entry_paths):
317 # Print this entry and all sub-entries (=higher indent)
320 # Don't print this entry, nor any following entries until we get
322 min_indent = MAX_INDENT
324 _DoLine(lines, _EntryToStrings(entry))
325 selected_entries.append(entry)
326 return selected_entries, lines, widths