]> Git Repo - J-u-boot.git/blame - tools/binman/image.py
patman: Move to absolute imports
[J-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
8from collections import OrderedDict
61f564d1 9import fnmatch
bf7fd50b 10from operator import attrgetter
10f9d006 11import os
19790632
SG
12import re
13import sys
bf7fd50b 14
16287933
SG
15from binman.entry import Entry
16from binman.etype import fdtmap
17from binman.etype import image_header
18from binman.etype import section
19from dtoc import fdt
20from dtoc import fdt_util
bf776679
SG
21from patman import tools
22from patman import tout
bf7fd50b 23
8beb11ea 24class Image(section.Entry_section):
bf7fd50b
SG
25 """A Image, representing an output from binman
26
27 An image is comprised of a collection of entries each containing binary
28 data. The image size must be large enough to hold all of this data.
29
30 This class implements the various operations needed for images.
31
8beb11ea
SG
32 Attributes:
33 filename: Output filename for image
589d8f91
SG
34 image_node: Name of node containing the description for this image
35 fdtmap_dtb: Fdt object for the fdtmap when loading from a file
36 fdtmap_data: Contents of the fdtmap when loading from a file
12bb1a99
SG
37 allow_repack: True to add properties to allow the image to be safely
38 repacked later
7ae5f315
SG
39
40 Args:
12bb1a99
SG
41 copy_to_orig: Copy offset/size to orig_offset/orig_size after reading
42 from the device tree
7ae5f315
SG
43 test: True if this is being called from a test of Images. This this case
44 there is no device tree defining the structure of the section, so
45 we create a section manually.
bf7fd50b 46 """
12bb1a99
SG
47 def __init__(self, name, node, copy_to_orig=True, test=False):
48 section.Entry_section.__init__(self, None, 'section', node, test=test)
49 self.copy_to_orig = copy_to_orig
8beb11ea
SG
50 self.name = 'main-section'
51 self.image_name = name
52 self._filename = '%s.bin' % self.image_name
589d8f91
SG
53 self.fdtmap_dtb = None
54 self.fdtmap_data = None
12bb1a99 55 self.allow_repack = False
8beb11ea 56 if not test:
c6bd6e23
SG
57 self.ReadNode()
58
59 def ReadNode(self):
60 section.Entry_section.ReadNode(self)
61 filename = fdt_util.GetString(self._node, 'filename')
62 if filename:
63 self._filename = filename
12bb1a99 64 self.allow_repack = fdt_util.GetBool(self._node, 'allow-repack')
bf7fd50b 65
ffded752
SG
66 @classmethod
67 def FromFile(cls, fname):
68 """Convert an image file into an Image for use in binman
69
70 Args:
71 fname: Filename of image file to read
72
73 Returns:
74 Image object on success
75
76 Raises:
77 ValueError if something goes wrong
78 """
79 data = tools.ReadFile(fname)
80 size = len(data)
81
82 # First look for an image header
83 pos = image_header.LocateHeaderOffset(data)
84 if pos is None:
85 # Look for the FDT map
86 pos = fdtmap.LocateFdtmap(data)
87 if pos is None:
88 raise ValueError('Cannot find FDT map in image')
89
90 # We don't know the FDT size, so check its header first
91 probe_dtb = fdt.Fdt.FromData(
92 data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256])
93 dtb_size = probe_dtb.GetFdtObj().totalsize()
94 fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN]
96b6c506
SG
95 fdt_data = fdtmap_data[fdtmap.FDTMAP_HDR_LEN:]
96 out_fname = tools.GetOutputFilename('fdtmap.in.dtb')
97 tools.WriteFile(out_fname, fdt_data)
51014aab 98 dtb = fdt.Fdt(out_fname)
ffded752
SG
99 dtb.Scan()
100
101 # Return an Image with the associated nodes
589d8f91 102 root = dtb.GetRoot()
12bb1a99 103 image = Image('image', root, copy_to_orig=False)
51014aab 104
589d8f91
SG
105 image.image_node = fdt_util.GetString(root, 'image-node', 'image')
106 image.fdtmap_dtb = dtb
107 image.fdtmap_data = fdtmap_data
f667e45b 108 image._data = data
10f9d006
SG
109 image._filename = fname
110 image.image_name, _ = os.path.splitext(fname)
f667e45b 111 return image
ffded752 112
c52c9e7d
SG
113 def Raise(self, msg):
114 """Convenience function to raise an error referencing an image"""
115 raise ValueError("Image '%s': %s" % (self._node.path, msg))
116
bf7fd50b
SG
117 def PackEntries(self):
118 """Pack all entries into the image"""
8beb11ea 119 section.Entry_section.Pack(self, 0)
078ab1a2 120
dbf6be9f 121 def SetImagePos(self):
8beb11ea
SG
122 # This first section in the image so it starts at 0
123 section.Entry_section.SetImagePos(self, 0)
dbf6be9f 124
bf7fd50b
SG
125 def ProcessEntryContents(self):
126 """Call the ProcessContents() method for each entry
127
128 This is intended to adjust the contents as needed by the entry type.
a0dcaf20
SG
129
130 Returns:
131 True if the new data size is OK, False if expansion is needed
bf7fd50b 132 """
8beb11ea
SG
133 sizes_ok = True
134 for entry in self._entries.values():
135 if not entry.ProcessContents():
136 sizes_ok = False
eea264ea 137 tout.Debug("Entry '%s' size change" % self._node.path)
8beb11ea 138 return sizes_ok
bf7fd50b 139
19790632
SG
140 def WriteSymbols(self):
141 """Write symbol values into binary files for access at run time"""
8beb11ea
SG
142 section.Entry_section.WriteSymbols(self, self)
143
bf7fd50b
SG
144 def BuildImage(self):
145 """Write the image to a file"""
146 fname = tools.GetOutputFilename(self._filename)
7400107e 147 tout.Info("Writing image to '%s'" % fname)
bf7fd50b 148 with open(fname, 'wb') as fd:
7400107e
SG
149 data = self.GetData()
150 fd.write(data)
151 tout.Info("Wrote %#x bytes" % len(data))
3b0c3821
SG
152
153 def WriteMap(self):
163ed6c3
SG
154 """Write a map of the image to a .map file
155
156 Returns:
157 Filename of map file written
158 """
8beb11ea 159 filename = '%s.map' % self.image_name
3b0c3821
SG
160 fname = tools.GetOutputFilename(filename)
161 with open(fname, 'w') as fd:
1be70d20
SG
162 print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
163 file=fd)
8beb11ea 164 section.Entry_section.WriteMap(self, fd, 0)
163ed6c3 165 return fname
41b8ba09
SG
166
167 def BuildEntryList(self):
168 """List the files in an image
169
170 Returns:
171 List of entry.EntryInfo objects describing all entries in the image
172 """
173 entries = []
8beb11ea 174 self.ListEntries(entries, 0)
41b8ba09 175 return entries
61f564d1
SG
176
177 def FindEntryPath(self, entry_path):
178 """Find an entry at a given path in the image
179
180 Args:
181 entry_path: Path to entry (e.g. /ro-section/u-boot')
182
183 Returns:
184 Entry object corresponding to that past
185
186 Raises:
187 ValueError if no entry found
188 """
189 parts = entry_path.split('/')
190 entries = self.GetEntries()
191 parent = '/'
192 for part in parts:
193 entry = entries.get(part)
194 if not entry:
195 raise ValueError("Entry '%s' not found in '%s'" %
196 (part, parent))
197 parent = entry.GetPath()
198 entries = entry.GetEntries()
199 return entry
200
201 def ReadData(self, decomp=True):
2d553c00
SG
202 tout.Debug("Image '%s' ReadData(), size=%#x" %
203 (self.GetPath(), len(self._data)))
61f564d1
SG
204 return self._data
205
206 def GetListEntries(self, entry_paths):
207 """List the entries in an image
208
209 This decodes the supplied image and returns a list of entries from that
210 image, preceded by a header.
211
212 Args:
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
215
216 Returns:
217 String error message if something went wrong, otherwise
218 3-Tuple:
219 List of EntryInfo objects
220 List of lines, each
221 List of text columns, each a string
222 List of widths of each column
223 """
224 def _EntryToStrings(entry):
225 """Convert an entry to a list of strings, one for each column
226
227 Args:
228 entry: EntryInfo object containing information to output
229
230 Returns:
231 List of strings, one for each field in entry
232 """
233 def _AppendHex(val):
234 """Append a hex value, or an empty string if val is None
235
236 Args:
237 val: Integer value, or None if none
238 """
239 args.append('' if val is None else '>%x' % val)
240
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)
247 return args
248
249 def _DoLine(lines, line):
250 """Add a line to the output list
251
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
254
255 Args:
256 lines: List of lines to add to
257 line: List of strings, one for each column
258 """
259 for i, item in enumerate(line):
260 widths[i] = max(widths[i], len(item))
261 lines.append(line)
262
263 def _NameInPaths(fname, entry_paths):
264 """Check if a filename is in a list of wildcarded paths
265
266 Args:
267 fname: Filename to check
268 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
269 'section/u-boot'])
270
271 Returns:
272 True if any wildcard matches the filename (using Unix filename
273 pattern matching, not regular expressions)
274 False if not
275 """
276 for path in entry_paths:
277 if fnmatch.fnmatch(fname, path):
278 return True
279 return False
280
281 entries = self.BuildEntryList()
282
283 # This is our list of lines. Each item in the list is a list of strings, one
284 # for each column
285 lines = []
286 HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset',
287 'Uncomp-size']
288 num_columns = len(HEADER)
289
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)
294
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
297 # entry paths)
298 MAX_INDENT = 100
299 min_indent = MAX_INDENT
300 path_stack = []
301 path = ''
302 indent = 0
303 selected_entries = []
304 for entry in entries:
305 if entry.indent > indent:
306 path_stack.append(path)
307 elif entry.indent < indent:
308 path_stack.pop()
309 if path_stack:
310 path = path_stack[-1] + '/' + entry.name
311 indent = entry.indent
312
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)
318 min_indent = indent
319 else:
320 # Don't print this entry, nor any following entries until we get
321 # a path match
322 min_indent = MAX_INDENT
323 continue
324 _DoLine(lines, _EntryToStrings(entry))
325 selected_entries.append(entry)
326 return selected_entries, lines, widths
This page took 0.230054 seconds and 4 git commands to generate.