]> Git Repo - u-boot.git/blame - tools/binman/image.py
binman: Allow for logging information to be displayed
[u-boot.git] / tools / binman / image.py
CommitLineData
83d290c5 1# SPDX-License-Identifier: GPL-2.0+
bf7fd50b
SG
2# Copyright (c) 2016 Google, Inc
3# Written by Simon Glass <[email protected]>
4#
bf7fd50b
SG
5# Class for an image, the output of binman
6#
7
19790632
SG
8from __future__ import print_function
9
bf7fd50b 10from collections import OrderedDict
61f564d1 11import fnmatch
bf7fd50b 12from operator import attrgetter
19790632
SG
13import re
14import sys
bf7fd50b 15
8beb11ea 16from entry import Entry
ffded752
SG
17from etype import fdtmap
18from etype import image_header
8beb11ea 19from etype import section
ffded752 20import fdt
bf7fd50b
SG
21import fdt_util
22import tools
eea264ea 23import tout
bf7fd50b 24
8beb11ea 25class Image(section.Entry_section):
bf7fd50b
SG
26 """A Image, representing an output from binman
27
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.
30
31 This class implements the various operations needed for images.
32
8beb11ea
SG
33 Attributes:
34 filename: Output filename for image
7ae5f315
SG
35
36 Args:
37 test: True if this is being called from a test of Images. This this case
38 there is no device tree defining the structure of the section, so
39 we create a section manually.
bf7fd50b 40 """
19790632 41 def __init__(self, name, node, test=False):
8beb11ea
SG
42 self.image = self
43 section.Entry_section.__init__(self, None, 'section', node, test)
44 self.name = 'main-section'
45 self.image_name = name
46 self._filename = '%s.bin' % self.image_name
47 if not test:
48 filename = fdt_util.GetString(self._node, 'filename')
49 if filename:
50 self._filename = filename
bf7fd50b 51
ffded752
SG
52 @classmethod
53 def FromFile(cls, fname):
54 """Convert an image file into an Image for use in binman
55
56 Args:
57 fname: Filename of image file to read
58
59 Returns:
60 Image object on success
61
62 Raises:
63 ValueError if something goes wrong
64 """
65 data = tools.ReadFile(fname)
66 size = len(data)
67
68 # First look for an image header
69 pos = image_header.LocateHeaderOffset(data)
70 if pos is None:
71 # Look for the FDT map
72 pos = fdtmap.LocateFdtmap(data)
73 if pos is None:
74 raise ValueError('Cannot find FDT map in image')
75
76 # We don't know the FDT size, so check its header first
77 probe_dtb = fdt.Fdt.FromData(
78 data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256])
79 dtb_size = probe_dtb.GetFdtObj().totalsize()
80 fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN]
81 dtb = fdt.Fdt.FromData(fdtmap_data[fdtmap.FDTMAP_HDR_LEN:])
82 dtb.Scan()
83
84 # Return an Image with the associated nodes
85 return Image('image', dtb.GetRoot())
86
c52c9e7d
SG
87 def Raise(self, msg):
88 """Convenience function to raise an error referencing an image"""
89 raise ValueError("Image '%s': %s" % (self._node.path, msg))
90
bf7fd50b
SG
91 def PackEntries(self):
92 """Pack all entries into the image"""
8beb11ea 93 section.Entry_section.Pack(self, 0)
078ab1a2 94
dbf6be9f 95 def SetImagePos(self):
8beb11ea
SG
96 # This first section in the image so it starts at 0
97 section.Entry_section.SetImagePos(self, 0)
dbf6be9f 98
bf7fd50b
SG
99 def ProcessEntryContents(self):
100 """Call the ProcessContents() method for each entry
101
102 This is intended to adjust the contents as needed by the entry type.
a0dcaf20
SG
103
104 Returns:
105 True if the new data size is OK, False if expansion is needed
bf7fd50b 106 """
8beb11ea
SG
107 sizes_ok = True
108 for entry in self._entries.values():
109 if not entry.ProcessContents():
110 sizes_ok = False
eea264ea 111 tout.Debug("Entry '%s' size change" % self._node.path)
8beb11ea 112 return sizes_ok
bf7fd50b 113
19790632
SG
114 def WriteSymbols(self):
115 """Write symbol values into binary files for access at run time"""
8beb11ea
SG
116 section.Entry_section.WriteSymbols(self, self)
117
118 def BuildSection(self, fd, base_offset):
119 """Write the section to a file"""
120 fd.seek(base_offset)
121 fd.write(self.GetData())
19790632 122
bf7fd50b
SG
123 def BuildImage(self):
124 """Write the image to a file"""
125 fname = tools.GetOutputFilename(self._filename)
126 with open(fname, 'wb') as fd:
8beb11ea 127 self.BuildSection(fd, 0)
3b0c3821
SG
128
129 def WriteMap(self):
163ed6c3
SG
130 """Write a map of the image to a .map file
131
132 Returns:
133 Filename of map file written
134 """
8beb11ea 135 filename = '%s.map' % self.image_name
3b0c3821
SG
136 fname = tools.GetOutputFilename(filename)
137 with open(fname, 'w') as fd:
1be70d20
SG
138 print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
139 file=fd)
8beb11ea 140 section.Entry_section.WriteMap(self, fd, 0)
163ed6c3 141 return fname
41b8ba09
SG
142
143 def BuildEntryList(self):
144 """List the files in an image
145
146 Returns:
147 List of entry.EntryInfo objects describing all entries in the image
148 """
149 entries = []
8beb11ea 150 self.ListEntries(entries, 0)
41b8ba09 151 return entries
61f564d1
SG
152
153 def FindEntryPath(self, entry_path):
154 """Find an entry at a given path in the image
155
156 Args:
157 entry_path: Path to entry (e.g. /ro-section/u-boot')
158
159 Returns:
160 Entry object corresponding to that past
161
162 Raises:
163 ValueError if no entry found
164 """
165 parts = entry_path.split('/')
166 entries = self.GetEntries()
167 parent = '/'
168 for part in parts:
169 entry = entries.get(part)
170 if not entry:
171 raise ValueError("Entry '%s' not found in '%s'" %
172 (part, parent))
173 parent = entry.GetPath()
174 entries = entry.GetEntries()
175 return entry
176
177 def ReadData(self, decomp=True):
178 return self._data
179
180 def GetListEntries(self, entry_paths):
181 """List the entries in an image
182
183 This decodes the supplied image and returns a list of entries from that
184 image, preceded by a header.
185
186 Args:
187 entry_paths: List of paths to match (each can have wildcards). Only
188 entries whose names match one of these paths will be printed
189
190 Returns:
191 String error message if something went wrong, otherwise
192 3-Tuple:
193 List of EntryInfo objects
194 List of lines, each
195 List of text columns, each a string
196 List of widths of each column
197 """
198 def _EntryToStrings(entry):
199 """Convert an entry to a list of strings, one for each column
200
201 Args:
202 entry: EntryInfo object containing information to output
203
204 Returns:
205 List of strings, one for each field in entry
206 """
207 def _AppendHex(val):
208 """Append a hex value, or an empty string if val is None
209
210 Args:
211 val: Integer value, or None if none
212 """
213 args.append('' if val is None else '>%x' % val)
214
215 args = [' ' * entry.indent + entry.name]
216 _AppendHex(entry.image_pos)
217 _AppendHex(entry.size)
218 args.append(entry.etype)
219 _AppendHex(entry.offset)
220 _AppendHex(entry.uncomp_size)
221 return args
222
223 def _DoLine(lines, line):
224 """Add a line to the output list
225
226 This adds a line (a list of columns) to the output list. It also updates
227 the widths[] array with the maximum width of each column
228
229 Args:
230 lines: List of lines to add to
231 line: List of strings, one for each column
232 """
233 for i, item in enumerate(line):
234 widths[i] = max(widths[i], len(item))
235 lines.append(line)
236
237 def _NameInPaths(fname, entry_paths):
238 """Check if a filename is in a list of wildcarded paths
239
240 Args:
241 fname: Filename to check
242 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
243 'section/u-boot'])
244
245 Returns:
246 True if any wildcard matches the filename (using Unix filename
247 pattern matching, not regular expressions)
248 False if not
249 """
250 for path in entry_paths:
251 if fnmatch.fnmatch(fname, path):
252 return True
253 return False
254
255 entries = self.BuildEntryList()
256
257 # This is our list of lines. Each item in the list is a list of strings, one
258 # for each column
259 lines = []
260 HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset',
261 'Uncomp-size']
262 num_columns = len(HEADER)
263
264 # This records the width of each column, calculated as the maximum width of
265 # all the strings in that column
266 widths = [0] * num_columns
267 _DoLine(lines, HEADER)
268
269 # We won't print anything unless it has at least this indent. So at the
270 # start we will print nothing, unless a path matches (or there are no
271 # entry paths)
272 MAX_INDENT = 100
273 min_indent = MAX_INDENT
274 path_stack = []
275 path = ''
276 indent = 0
277 selected_entries = []
278 for entry in entries:
279 if entry.indent > indent:
280 path_stack.append(path)
281 elif entry.indent < indent:
282 path_stack.pop()
283 if path_stack:
284 path = path_stack[-1] + '/' + entry.name
285 indent = entry.indent
286
287 # If there are entry paths to match and we are not looking at a
288 # sub-entry of a previously matched entry, we need to check the path
289 if entry_paths and indent <= min_indent:
290 if _NameInPaths(path[1:], entry_paths):
291 # Print this entry and all sub-entries (=higher indent)
292 min_indent = indent
293 else:
294 # Don't print this entry, nor any following entries until we get
295 # a path match
296 min_indent = MAX_INDENT
297 continue
298 _DoLine(lines, _EntryToStrings(entry))
299 selected_entries.append(entry)
300 return selected_entries, lines, widths
This page took 0.12455 seconds and 4 git commands to generate.