]>
git.ipfire.org Git - people/ms/u-boot.git/blob - tools/binman/image.py
1 # Copyright (c) 2016 Google, Inc
2 # Written by Simon Glass <sjg@chromium.org>
4 # SPDX-License-Identifier: GPL-2.0+
6 # Class for an image, the output of binman
9 from __future__
import print_function
11 from collections
import OrderedDict
12 from operator
import attrgetter
20 """A Image, representing an output from binman
22 An image is comprised of a collection of entries each containing binary
23 data. The image size must be large enough to hold all of this data.
25 This class implements the various operations needed for images.
28 _node: Node object that contains the image definition in device tree
30 _size: Image size in bytes, or None if not known yet
31 _align_size: Image size alignment, or None
32 _pad_before: Number of bytes before the first entry starts. This
33 effectively changes the place where entry position 0 starts
34 _pad_after: Number of bytes after the last entry ends. The last
35 entry will finish on or before this boundary
36 _pad_byte: Byte to use to pad the image where there is no entry
37 _filename: Output filename for image
38 _sort: True if entries should be sorted by position, False if they
39 must be in-order in the device tree description
40 _skip_at_start: Number of bytes before the first entry starts. These
41 effecively adjust the starting position of entries. For example,
42 if _pad_before is 16, then the first entry would start at 16.
43 An entry with pos = 20 would in fact be written at position 4
45 _end_4gb: Indicates that the image ends at the 4GB boundary. This is
46 used for x86 images, which want to use positions such that a
47 memory address (like 0xff800000) is the first entry position.
48 This causes _skip_at_start to be set to the starting memory
50 _entries: OrderedDict() of entries
52 def __init__(self
, name
, node
, test
=False):
56 from entry
import Entry
61 self
._align
_size
= None
65 self
._filename
= '%s.bin' % self
._name
67 self
._skip
_at
_start
= 0
69 self
._entries
= OrderedDict()
76 """Read properties from the image node"""
77 self
._size
= fdt_util
.GetInt(self
._node
, 'size')
78 self
._align
_size
= fdt_util
.GetInt(self
._node
, 'align-size')
79 if tools
.NotPowerOfTwo(self
._align
_size
):
80 self
._Raise
("Alignment size %s must be a power of two" %
82 self
._pad
_before
= fdt_util
.GetInt(self
._node
, 'pad-before', 0)
83 self
._pad
_after
= fdt_util
.GetInt(self
._node
, 'pad-after', 0)
84 self
._pad
_byte
= fdt_util
.GetInt(self
._node
, 'pad-byte', 0)
85 filename
= fdt_util
.GetString(self
._node
, 'filename')
87 self
._filename
= filename
88 self
._sort
= fdt_util
.GetBool(self
._node
, 'sort-by-pos')
89 self
._end
_4gb
= fdt_util
.GetBool(self
._node
, 'end-at-4gb')
90 if self
._end
_4gb
and not self
._size
:
91 self
._Raise
("Image size must be provided when using end-at-4gb")
93 self
._skip
_at
_start
= 0x100000000 - self
._size
96 """Check that the image contents does not exceed its size, etc."""
98 for entry
in self
._entries
.values():
99 contents_size
= max(contents_size
, entry
.pos
+ entry
.size
)
101 contents_size
-= self
._skip
_at
_start
105 size
= self
._pad
_before
+ contents_size
+ self
._pad
_after
106 size
= tools
.Align(size
, self
._align
_size
)
108 if self
._size
and contents_size
> self
._size
:
109 self
._Raise
("contents size %#x (%d) exceeds image size %#x (%d)" %
110 (contents_size
, contents_size
, self
._size
, self
._size
))
113 if self
._size
!= tools
.Align(self
._size
, self
._align
_size
):
114 self
._Raise
("Size %#x (%d) does not match align-size %#x (%d)" %
115 (self
._size
, self
._size
, self
._align
_size
, self
._align
_size
))
117 def _Raise(self
, msg
):
118 """Raises an error for this image
121 msg: Error message to use in the raise string
125 raise ValueError("Image '%s': %s" % (self
._node
.path
, msg
))
128 """Get the path of an image (in the FDT)
131 Full path of the node for this image
133 return self
._node
.path
135 def _ReadEntries(self
):
136 for node
in self
._node
.subnodes
:
137 self
._entries
[node
.name
] = Entry
.Create(self
, node
)
139 def FindEntryType(self
, etype
):
140 """Find an entry type in the image
143 etype: Entry type to find
145 entry matching that type, or None if not found
147 for entry
in self
._entries
.values():
148 if entry
.etype
== etype
:
152 def GetEntryContents(self
):
153 """Call ObtainContents() for each entry
155 This calls each entry's ObtainContents() a few times until they all
156 return True. We stop calling an entry's function once it returns
157 True. This allows the contents of one entry to depend on another.
159 After 3 rounds we give up since it's likely an error.
161 todo
= self
._entries
.values()
162 for passnum
in range(3):
165 if not entry
.ObtainContents():
166 next_todo
.append(entry
)
171 def _SetEntryPosSize(self
, name
, pos
, size
):
172 """Set the position and size of an entry
175 name: Entry name to update
179 entry
= self
._entries
.get(name
)
181 self
._Raise
("Unable to set pos/size for unknown entry '%s'" % name
)
182 entry
.SetPositionSize(self
._skip
_at
_start
+ pos
, size
)
184 def GetEntryPositions(self
):
185 """Handle entries that want to set the position/size of other entries
187 This calls each entry's GetPositions() method. If it returns a list
188 of entries to update, it updates them.
190 for entry
in self
._entries
.values():
191 pos_dict
= entry
.GetPositions()
192 for name
, info
in pos_dict
.iteritems():
193 self
._SetEntryPosSize
(name
, *info
)
195 def PackEntries(self
):
196 """Pack all entries into the image"""
197 pos
= self
._skip
_at
_start
198 for entry
in self
._entries
.values():
199 pos
= entry
.Pack(pos
)
201 def _SortEntries(self
):
202 """Sort entries by position"""
203 entries
= sorted(self
._entries
.values(), key
=lambda entry
: entry
.pos
)
204 self
._entries
.clear()
205 for entry
in entries
:
206 self
._entries
[entry
._node
.name
] = entry
208 def CheckEntries(self
):
209 """Check that entries do not overlap or extend outside the image"""
214 for entry
in self
._entries
.values():
215 if (entry
.pos
< self
._skip
_at
_start
or
216 entry
.pos
>= self
._skip
_at
_start
+ self
._size
):
217 entry
.Raise("Position %#x (%d) is outside the image starting "
219 (entry
.pos
, entry
.pos
, self
._skip
_at
_start
,
220 self
._skip
_at
_start
))
222 entry
.Raise("Position %#x (%d) overlaps with previous entry '%s' "
223 "ending at %#x (%d)" %
224 (entry
.pos
, entry
.pos
, prev_name
, pos
, pos
))
225 pos
= entry
.pos
+ entry
.size
226 prev_name
= entry
.GetPath()
228 def ProcessEntryContents(self
):
229 """Call the ProcessContents() method for each entry
231 This is intended to adjust the contents as needed by the entry type.
233 for entry
in self
._entries
.values():
234 entry
.ProcessContents()
236 def WriteSymbols(self
):
237 """Write symbol values into binary files for access at run time"""
238 for entry
in self
._entries
.values():
239 entry
.WriteSymbols(self
)
241 def BuildImage(self
):
242 """Write the image to a file"""
243 fname
= tools
.GetOutputFilename(self
._filename
)
244 with
open(fname
, 'wb') as fd
:
245 fd
.write(chr(self
._pad
_byte
) * self
._size
)
247 for entry
in self
._entries
.values():
248 data
= entry
.GetData()
249 fd
.seek(self
._pad
_before
+ entry
.pos
- self
._skip
_at
_start
)
252 def LookupSymbol(self
, sym_name
, optional
, msg
):
253 """Look up a symbol in an ELF file
255 Looks up a symbol in an ELF file. Only entry types which come from an
256 ELF image can be used by this function.
258 At present the only entry property supported is pos.
261 sym_name: Symbol name in the ELF file to look up in the format
262 _binman_<entry>_prop_<property> where <entry> is the name of
263 the entry and <property> is the property to find (e.g.
264 _binman_u_boot_prop_pos). As a special case, you can append
265 _any to <entry> to have it search for any matching entry. E.g.
266 _binman_u_boot_any_prop_pos will match entries called u-boot,
267 u-boot-img and u-boot-nodtb)
268 optional: True if the symbol is optional. If False this function
269 will raise if the symbol is not found
270 msg: Message to display if an error occurs
273 Value that should be assigned to that symbol, or None if it was
274 optional and not found
277 ValueError if the symbol is invalid or not found, or references a
278 property which is not supported
280 m
= re
.match(r
'^_binman_(\w+)_prop_(\w+)$', sym_name
)
282 raise ValueError("%s: Symbol '%s' has invalid format" %
284 entry_name
, prop_name
= m
.groups()
285 entry_name
= entry_name
.replace('_', '-')
286 entry
= self
._entries
.get(entry_name
)
288 if entry_name
.endswith('-any'):
289 root
= entry_name
[:-4]
290 for name
in self
._entries
:
291 if name
.startswith(root
):
292 rest
= name
[len(root
):]
293 if rest
in ['', '-img', '-nodtb']:
294 entry
= self
._entries
[name
]
296 err
= ("%s: Entry '%s' not found in list (%s)" %
297 (msg
, entry_name
, ','.join(self
._entries
.keys())))
299 print('Warning: %s' % err
, file=sys
.stderr
)
301 raise ValueError(err
)
302 if prop_name
== 'pos':
305 raise ValueError("%s: No such property '%s'" % (msg
, prop_name
))