blob: 1cfa024a9f9e6a77007c4f39b2b81e0a82467052 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass2574ef62016-11-25 20:15:51 -07002# Copyright (c) 2016 Google, Inc
3#
Simon Glass2574ef62016-11-25 20:15:51 -07004# Base class for all entries
5#
6
Simon Glass91710b32018-07-17 13:25:32 -06007from collections import namedtuple
Simon Glass7ccca832019-10-31 07:42:59 -06008import importlib
Simon Glass691198c2018-06-01 09:38:15 -06009import os
10import sys
Simon Glass29aa7362018-09-14 04:57:19 -060011
Simon Glassc585dd42020-04-17 18:09:03 -060012from dtoc import fdt_util
Simon Glassa997ea52020-04-17 18:09:04 -060013from patman import tools
Simon Glassc585dd42020-04-17 18:09:03 -060014from patman.tools import ToHex, ToHexSize
Simon Glassa997ea52020-04-17 18:09:04 -060015from patman import tout
Simon Glass2574ef62016-11-25 20:15:51 -070016
17modules = {}
18
Simon Glass91710b32018-07-17 13:25:32 -060019
20# An argument which can be passed to entries on the command line, in lieu of
21# device-tree properties.
22EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
23
Simon Glass6b156f82019-07-08 14:25:43 -060024# Information about an entry for use when displaying summaries
25EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
26 'image_pos', 'uncomp_size', 'offset',
27 'entry'])
Simon Glass91710b32018-07-17 13:25:32 -060028
Simon Glass2574ef62016-11-25 20:15:51 -070029class Entry(object):
Simon Glassad5a7712018-06-01 09:38:14 -060030 """An Entry in the section
Simon Glass2574ef62016-11-25 20:15:51 -070031
32 An entry corresponds to a single node in the device-tree description
Simon Glassad5a7712018-06-01 09:38:14 -060033 of the section. Each entry ends up being a part of the final section.
Simon Glass2574ef62016-11-25 20:15:51 -070034 Entries can be placed either right next to each other, or with padding
35 between them. The type of the entry determines the data that is in it.
36
37 This class is not used by itself. All entry objects are subclasses of
38 Entry.
39
40 Attributes:
Simon Glass3a9a2b82018-07-17 13:25:28 -060041 section: Section object containing this entry
Simon Glass2574ef62016-11-25 20:15:51 -070042 node: The node that created this entry
Simon Glasse8561af2018-08-01 15:22:37 -060043 offset: Offset of entry within the section, None if not known yet (in
44 which case it will be calculated by Pack())
Simon Glass2574ef62016-11-25 20:15:51 -070045 size: Entry size in bytes, None if not known
Simon Glass1fdb4872019-10-31 07:43:02 -060046 pre_reset_size: size as it was before ResetForPack(). This allows us to
47 keep track of the size we started with and detect size changes
Simon Glassaa2fcf92019-07-08 14:25:30 -060048 uncomp_size: Size of uncompressed data in bytes, if the entry is
49 compressed, else None
Simon Glass2574ef62016-11-25 20:15:51 -070050 contents_size: Size of contents in bytes, 0 by default
Simon Glassafb9caa2020-10-26 17:40:10 -060051 align: Entry start offset alignment relative to the start of the
52 containing section, or None
Simon Glass2574ef62016-11-25 20:15:51 -070053 align_size: Entry size alignment, or None
Simon Glassafb9caa2020-10-26 17:40:10 -060054 align_end: Entry end offset alignment relative to the start of the
55 containing section, or None
Simon Glassd12599d2020-10-26 17:40:09 -060056 pad_before: Number of pad bytes before the contents when it is placed
57 in the containing section, 0 if none. The pad bytes become part of
58 the entry.
59 pad_after: Number of pad bytes after the contents when it is placed in
60 the containing section, 0 if none. The pad bytes become part of
61 the entry.
62 data: Contents of entry (string of bytes). This does not include
Simon Glass789b34402020-10-26 17:40:15 -060063 padding created by pad_before or pad_after. If the entry is
64 compressed, this contains the compressed data.
65 uncomp_data: Original uncompressed data, if this entry is compressed,
66 else None
Simon Glassaa2fcf92019-07-08 14:25:30 -060067 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glasse61b6f62019-07-08 14:25:37 -060068 orig_offset: Original offset value read from node
69 orig_size: Original size value read from node
Simon Glassb8f90372020-09-01 05:13:57 -060070 missing: True if this entry is missing its contents
71 allow_missing: Allow children of this entry to be missing (used by
72 subclasses such as Entry_section)
73 external: True if this entry contains an external binary blob
Simon Glass2574ef62016-11-25 20:15:51 -070074 """
Simon Glass2c360cf2019-07-20 12:23:45 -060075 def __init__(self, section, etype, node, name_prefix=''):
Simon Glassb9ba4e02019-08-24 07:22:44 -060076 # Put this here to allow entry-docs and help to work without libfdt
77 global state
Simon Glassc585dd42020-04-17 18:09:03 -060078 from binman import state
Simon Glassb9ba4e02019-08-24 07:22:44 -060079
Simon Glassad5a7712018-06-01 09:38:14 -060080 self.section = section
Simon Glass2574ef62016-11-25 20:15:51 -070081 self.etype = etype
82 self._node = node
Simon Glass3b78d532018-06-01 09:38:21 -060083 self.name = node and (name_prefix + node.name) or 'none'
Simon Glasse8561af2018-08-01 15:22:37 -060084 self.offset = None
Simon Glass2574ef62016-11-25 20:15:51 -070085 self.size = None
Simon Glass1fdb4872019-10-31 07:43:02 -060086 self.pre_reset_size = None
Simon Glassaa2fcf92019-07-08 14:25:30 -060087 self.uncomp_size = None
Simon Glass5c350162018-07-17 13:25:47 -060088 self.data = None
Simon Glass789b34402020-10-26 17:40:15 -060089 self.uncomp_data = None
Simon Glass2574ef62016-11-25 20:15:51 -070090 self.contents_size = 0
91 self.align = None
92 self.align_size = None
93 self.align_end = None
94 self.pad_before = 0
95 self.pad_after = 0
Simon Glasse8561af2018-08-01 15:22:37 -060096 self.offset_unset = False
Simon Glass9dcc8612018-08-01 15:22:42 -060097 self.image_pos = None
Simon Glassfa79a812018-09-14 04:57:29 -060098 self._expand_size = False
Simon Glassaa2fcf92019-07-08 14:25:30 -060099 self.compress = 'none'
Simon Glassa003cd32020-07-09 18:39:40 -0600100 self.missing = False
Simon Glassb8f90372020-09-01 05:13:57 -0600101 self.external = False
102 self.allow_missing = False
Simon Glass2574ef62016-11-25 20:15:51 -0700103
104 @staticmethod
Simon Glass2f859412021-03-18 20:25:04 +1300105 def Lookup(node_path, etype, expanded):
Simon Glass969616c2018-07-17 13:25:36 -0600106 """Look up the entry class for a node.
Simon Glass2574ef62016-11-25 20:15:51 -0700107
108 Args:
Simon Glass969616c2018-07-17 13:25:36 -0600109 node_node: Path name of Node object containing information about
110 the entry to create (used for errors)
111 etype: Entry type to use
Simon Glass2f859412021-03-18 20:25:04 +1300112 expanded: Use the expanded version of etype
Simon Glass2574ef62016-11-25 20:15:51 -0700113
114 Returns:
Simon Glass2f859412021-03-18 20:25:04 +1300115 The entry class object if found, else None if not found and expanded
116 is True
117
118 Raise:
119 ValueError if expanded is False and the class is not found
Simon Glass2574ef62016-11-25 20:15:51 -0700120 """
Simon Glasse76a3e62018-06-01 09:38:11 -0600121 # Convert something like 'u-boot@0' to 'u_boot' since we are only
122 # interested in the type.
Simon Glass2574ef62016-11-25 20:15:51 -0700123 module_name = etype.replace('-', '_')
Simon Glass2f859412021-03-18 20:25:04 +1300124
Simon Glasse76a3e62018-06-01 09:38:11 -0600125 if '@' in module_name:
126 module_name = module_name.split('@')[0]
Simon Glass2f859412021-03-18 20:25:04 +1300127 if expanded:
128 module_name += '_expanded'
Simon Glass2574ef62016-11-25 20:15:51 -0700129 module = modules.get(module_name)
130
Simon Glass691198c2018-06-01 09:38:15 -0600131 # Also allow entry-type modules to be brought in from the etype directory.
132
Simon Glass2574ef62016-11-25 20:15:51 -0700133 # Import the module if we have not already done so.
134 if not module:
135 try:
Simon Glassc585dd42020-04-17 18:09:03 -0600136 module = importlib.import_module('binman.etype.' + module_name)
Simon Glass969616c2018-07-17 13:25:36 -0600137 except ImportError as e:
Simon Glass2f859412021-03-18 20:25:04 +1300138 if expanded:
139 return None
Simon Glass969616c2018-07-17 13:25:36 -0600140 raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
141 (etype, node_path, module_name, e))
Simon Glass2574ef62016-11-25 20:15:51 -0700142 modules[module_name] = module
143
Simon Glass969616c2018-07-17 13:25:36 -0600144 # Look up the expected class name
145 return getattr(module, 'Entry_%s' % module_name)
146
147 @staticmethod
Simon Glass2f859412021-03-18 20:25:04 +1300148 def Create(section, node, etype=None, expanded=False):
Simon Glass969616c2018-07-17 13:25:36 -0600149 """Create a new entry for a node.
150
151 Args:
Simon Glass2f859412021-03-18 20:25:04 +1300152 section: Section object containing this node
153 node: Node object containing information about the entry to
154 create
155 etype: Entry type to use, or None to work it out (used for tests)
156 expanded: True to use expanded versions of entries, where available
Simon Glass969616c2018-07-17 13:25:36 -0600157
158 Returns:
159 A new Entry object of the correct type (a subclass of Entry)
160 """
161 if not etype:
162 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glass2f859412021-03-18 20:25:04 +1300163 obj = Entry.Lookup(node.path, etype, expanded)
164 if obj and expanded:
165 # Check whether to use the expanded entry
166 new_etype = etype + '-expanded'
167 if obj.UseExpanded(node, etype, new_etype):
168 etype = new_etype
169 else:
170 obj = None
171 if not obj:
172 obj = Entry.Lookup(node.path, etype, False)
Simon Glass969616c2018-07-17 13:25:36 -0600173
Simon Glass2574ef62016-11-25 20:15:51 -0700174 # Call its constructor to get the object we want.
Simon Glassad5a7712018-06-01 09:38:14 -0600175 return obj(section, etype, node)
Simon Glass2574ef62016-11-25 20:15:51 -0700176
177 def ReadNode(self):
178 """Read entry information from the node
179
Simon Glass2c360cf2019-07-20 12:23:45 -0600180 This must be called as the first thing after the Entry is created.
181
Simon Glass2574ef62016-11-25 20:15:51 -0700182 This reads all the fields we recognise from the node, ready for use.
183 """
Simon Glass24b97442018-07-17 13:25:51 -0600184 if 'pos' in self._node.props:
185 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glasse8561af2018-08-01 15:22:37 -0600186 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glass2574ef62016-11-25 20:15:51 -0700187 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glassfb30e292019-07-20 12:23:51 -0600188 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
189 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
190 if self.GetImage().copy_to_orig:
191 self.orig_offset = self.offset
192 self.orig_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600193
Simon Glassb8424fa2019-07-08 14:25:46 -0600194 # These should not be set in input files, but are set in an FDT map,
195 # which is also read by this code.
196 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
197 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
198
Simon Glass2574ef62016-11-25 20:15:51 -0700199 self.align = fdt_util.GetInt(self._node, 'align')
200 if tools.NotPowerOfTwo(self.align):
201 raise ValueError("Node '%s': Alignment %s must be a power of two" %
202 (self._node.path, self.align))
203 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
204 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
205 self.align_size = fdt_util.GetInt(self._node, 'align-size')
206 if tools.NotPowerOfTwo(self.align_size):
Simon Glass39dd2152019-07-08 14:25:47 -0600207 self.Raise("Alignment size %s must be a power of two" %
208 self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700209 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glasse8561af2018-08-01 15:22:37 -0600210 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassfa79a812018-09-14 04:57:29 -0600211 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
Simon Glassa820af72020-09-06 10:39:09 -0600212 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glass2574ef62016-11-25 20:15:51 -0700213
Simon Glassa1301a22020-10-26 17:40:06 -0600214 # This is only supported by blobs and sections at present
215 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
216
Simon Glass3732ec32018-09-14 04:57:18 -0600217 def GetDefaultFilename(self):
218 return None
219
Simon Glass267112e2019-07-20 12:23:28 -0600220 def GetFdts(self):
221 """Get the device trees used by this entry
Simon Glass0c9d5b52018-09-14 04:57:22 -0600222
223 Returns:
Simon Glass267112e2019-07-20 12:23:28 -0600224 Empty dict, if this entry is not a .dtb, otherwise:
225 Dict:
226 key: Filename from this entry (without the path)
Simon Glass684a4f12019-07-20 12:23:31 -0600227 value: Tuple:
Simon Glass8235dd82021-03-18 20:25:02 +1300228 Entry object for this dtb
Simon Glass684a4f12019-07-20 12:23:31 -0600229 Filename of file containing this dtb
Simon Glass0c9d5b52018-09-14 04:57:22 -0600230 """
Simon Glass267112e2019-07-20 12:23:28 -0600231 return {}
Simon Glass0c9d5b52018-09-14 04:57:22 -0600232
Simon Glassac6328c2018-09-14 04:57:28 -0600233 def ExpandEntries(self):
Simon Glassfcb2a7c2021-03-18 20:24:52 +1300234 """Expand out entries which produce other entries
235
236 Some entries generate subnodes automatically, from which sub-entries
237 are then created. This method allows those to be added to the binman
238 definition for the current image. An entry which implements this method
239 should call state.AddSubnode() to add a subnode and can add properties
240 with state.AddString(), etc.
241
242 An example is 'files', which produces a section containing a list of
243 files.
244 """
Simon Glassac6328c2018-09-14 04:57:28 -0600245 pass
246
Simon Glassacd6c6e2020-10-26 17:40:17 -0600247 def AddMissingProperties(self, have_image_pos):
248 """Add new properties to the device tree as needed for this entry
249
250 Args:
251 have_image_pos: True if this entry has an image position. This can
252 be False if its parent section is compressed, since compression
253 groups all entries together into a compressed block of data,
254 obscuring the start of each individual child entry
255 """
256 for prop in ['offset', 'size']:
Simon Glasse22f8fa2018-07-06 10:27:41 -0600257 if not prop in self._node.props:
Simon Glassc8135dc2018-09-14 04:57:21 -0600258 state.AddZeroProp(self._node, prop)
Simon Glassacd6c6e2020-10-26 17:40:17 -0600259 if have_image_pos and 'image-pos' not in self._node.props:
260 state.AddZeroProp(self._node, 'image-pos')
Simon Glassfb30e292019-07-20 12:23:51 -0600261 if self.GetImage().allow_repack:
262 if self.orig_offset is not None:
263 state.AddZeroProp(self._node, 'orig-offset', True)
264 if self.orig_size is not None:
265 state.AddZeroProp(self._node, 'orig-size', True)
266
Simon Glassaa2fcf92019-07-08 14:25:30 -0600267 if self.compress != 'none':
268 state.AddZeroProp(self._node, 'uncomp-size')
Simon Glassae7cf032018-09-14 04:57:31 -0600269 err = state.CheckAddHashProp(self._node)
270 if err:
271 self.Raise(err)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600272
273 def SetCalculatedProperties(self):
274 """Set the value of device-tree properties calculated by binman"""
Simon Glassc8135dc2018-09-14 04:57:21 -0600275 state.SetInt(self._node, 'offset', self.offset)
276 state.SetInt(self._node, 'size', self.size)
Simon Glass39dd2152019-07-08 14:25:47 -0600277 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassacd6c6e2020-10-26 17:40:17 -0600278 if self.image_pos is not None:
Simon Glasseb943b12020-11-02 12:55:44 -0700279 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glassfb30e292019-07-20 12:23:51 -0600280 if self.GetImage().allow_repack:
281 if self.orig_offset is not None:
282 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
283 if self.orig_size is not None:
284 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glassaa2fcf92019-07-08 14:25:30 -0600285 if self.uncomp_size is not None:
286 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Simon Glassae7cf032018-09-14 04:57:31 -0600287 state.CheckSetHashValue(self._node, self.GetData)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600288
Simon Glass92307732018-07-06 10:27:40 -0600289 def ProcessFdt(self, fdt):
Simon Glasse219aa42018-09-14 04:57:24 -0600290 """Allow entries to adjust the device tree
291
292 Some entries need to adjust the device tree for their purposes. This
293 may involve adding or deleting properties.
294
295 Returns:
296 True if processing is complete
297 False if processing could not be completed due to a dependency.
298 This will cause the entry to be retried after others have been
299 called
300 """
Simon Glass92307732018-07-06 10:27:40 -0600301 return True
302
Simon Glass3b78d532018-06-01 09:38:21 -0600303 def SetPrefix(self, prefix):
304 """Set the name prefix for a node
305
306 Args:
307 prefix: Prefix to set, or '' to not use a prefix
308 """
309 if prefix:
310 self.name = prefix + self.name
311
Simon Glass2e1169f2018-07-06 10:27:19 -0600312 def SetContents(self, data):
313 """Set the contents of an entry
314
315 This sets both the data and content_size properties
316
317 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600318 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600319 """
320 self.data = data
321 self.contents_size = len(self.data)
322
323 def ProcessContentsUpdate(self, data):
Simon Glassd17dfea2019-07-08 14:25:33 -0600324 """Update the contents of an entry, after the size is fixed
Simon Glass2e1169f2018-07-06 10:27:19 -0600325
Simon Glassec849852019-07-08 14:25:35 -0600326 This checks that the new data is the same size as the old. If the size
327 has changed, this triggers a re-run of the packing algorithm.
Simon Glass2e1169f2018-07-06 10:27:19 -0600328
329 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600330 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600331
332 Raises:
333 ValueError if the new data size is not the same as the old
334 """
Simon Glassec849852019-07-08 14:25:35 -0600335 size_ok = True
Simon Glasse61b6f62019-07-08 14:25:37 -0600336 new_size = len(data)
Simon Glass9d8ee322019-07-20 12:23:58 -0600337 if state.AllowEntryExpansion() and new_size > self.contents_size:
338 # self.data will indicate the new size needed
339 size_ok = False
340 elif state.AllowEntryContraction() and new_size < self.contents_size:
341 size_ok = False
342
343 # If not allowed to change, try to deal with it or give up
344 if size_ok:
Simon Glasse61b6f62019-07-08 14:25:37 -0600345 if new_size > self.contents_size:
Simon Glass9d8ee322019-07-20 12:23:58 -0600346 self.Raise('Cannot update entry size from %d to %d' %
347 (self.contents_size, new_size))
348
349 # Don't let the data shrink. Pad it if necessary
350 if size_ok and new_size < self.contents_size:
351 data += tools.GetBytes(0, self.contents_size - new_size)
352
353 if not size_ok:
354 tout.Debug("Entry '%s' size change from %s to %s" % (
355 self._node.path, ToHex(self.contents_size),
356 ToHex(new_size)))
Simon Glass2e1169f2018-07-06 10:27:19 -0600357 self.SetContents(data)
Simon Glassec849852019-07-08 14:25:35 -0600358 return size_ok
Simon Glass2e1169f2018-07-06 10:27:19 -0600359
Simon Glass2574ef62016-11-25 20:15:51 -0700360 def ObtainContents(self):
361 """Figure out the contents of an entry.
362
363 Returns:
364 True if the contents were found, False if another call is needed
365 after the other entries are processed.
366 """
367 # No contents by default: subclasses can implement this
368 return True
369
Simon Glasse61b6f62019-07-08 14:25:37 -0600370 def ResetForPack(self):
371 """Reset offset/size fields so that packing can be done again"""
Simon Glassb6dff4c2019-07-20 12:23:36 -0600372 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
373 (ToHex(self.offset), ToHex(self.orig_offset),
374 ToHex(self.size), ToHex(self.orig_size)))
Simon Glass1fdb4872019-10-31 07:43:02 -0600375 self.pre_reset_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600376 self.offset = self.orig_offset
377 self.size = self.orig_size
378
Simon Glasse8561af2018-08-01 15:22:37 -0600379 def Pack(self, offset):
Simon Glassad5a7712018-06-01 09:38:14 -0600380 """Figure out how to pack the entry into the section
Simon Glass2574ef62016-11-25 20:15:51 -0700381
382 Most of the time the entries are not fully specified. There may be
383 an alignment but no size. In that case we take the size from the
384 contents of the entry.
385
Simon Glasse8561af2018-08-01 15:22:37 -0600386 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glass2574ef62016-11-25 20:15:51 -0700387
Simon Glasse8561af2018-08-01 15:22:37 -0600388 Once this function is complete, both the offset and size of the
Simon Glass2574ef62016-11-25 20:15:51 -0700389 entry will be know.
390
391 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600392 Current section offset pointer
Simon Glass2574ef62016-11-25 20:15:51 -0700393
394 Returns:
Simon Glasse8561af2018-08-01 15:22:37 -0600395 New section offset pointer (after this entry)
Simon Glass2574ef62016-11-25 20:15:51 -0700396 """
Simon Glassb6dff4c2019-07-20 12:23:36 -0600397 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
398 (ToHex(self.offset), ToHex(self.size),
399 self.contents_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600400 if self.offset is None:
401 if self.offset_unset:
402 self.Raise('No offset set with offset-unset: should another '
403 'entry provide this correct offset?')
404 self.offset = tools.Align(offset, self.align)
Simon Glass2574ef62016-11-25 20:15:51 -0700405 needed = self.pad_before + self.contents_size + self.pad_after
406 needed = tools.Align(needed, self.align_size)
407 size = self.size
408 if not size:
409 size = needed
Simon Glasse8561af2018-08-01 15:22:37 -0600410 new_offset = self.offset + size
411 aligned_offset = tools.Align(new_offset, self.align_end)
412 if aligned_offset != new_offset:
413 size = aligned_offset - self.offset
414 new_offset = aligned_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700415
416 if not self.size:
417 self.size = size
418
419 if self.size < needed:
420 self.Raise("Entry contents size is %#x (%d) but entry size is "
421 "%#x (%d)" % (needed, needed, self.size, self.size))
422 # Check that the alignment is correct. It could be wrong if the
Simon Glasse8561af2018-08-01 15:22:37 -0600423 # and offset or size values were provided (i.e. not calculated), but
Simon Glass2574ef62016-11-25 20:15:51 -0700424 # conflict with the provided alignment values
425 if self.size != tools.Align(self.size, self.align_size):
426 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
427 (self.size, self.size, self.align_size, self.align_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600428 if self.offset != tools.Align(self.offset, self.align):
429 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
430 (self.offset, self.offset, self.align, self.align))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600431 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
432 (self.offset, self.size, self.contents_size, new_offset))
Simon Glass2574ef62016-11-25 20:15:51 -0700433
Simon Glasse8561af2018-08-01 15:22:37 -0600434 return new_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700435
436 def Raise(self, msg):
437 """Convenience function to raise an error referencing a node"""
438 raise ValueError("Node '%s': %s" % (self._node.path, msg))
439
Simon Glassb6dff4c2019-07-20 12:23:36 -0600440 def Detail(self, msg):
441 """Convenience function to log detail referencing a node"""
442 tag = "Node '%s'" % self._node.path
443 tout.Detail('%30s: %s' % (tag, msg))
444
Simon Glass91710b32018-07-17 13:25:32 -0600445 def GetEntryArgsOrProps(self, props, required=False):
446 """Return the values of a set of properties
447
448 Args:
449 props: List of EntryArg objects
450
451 Raises:
452 ValueError if a property is not found
453 """
454 values = []
455 missing = []
456 for prop in props:
457 python_prop = prop.name.replace('-', '_')
458 if hasattr(self, python_prop):
459 value = getattr(self, python_prop)
460 else:
461 value = None
462 if value is None:
463 value = self.GetArg(prop.name, prop.datatype)
464 if value is None and required:
465 missing.append(prop.name)
466 values.append(value)
467 if missing:
Simon Glass3fb25402021-01-06 21:35:16 -0700468 self.GetImage().MissingArgs(self, missing)
Simon Glass91710b32018-07-17 13:25:32 -0600469 return values
470
Simon Glass2574ef62016-11-25 20:15:51 -0700471 def GetPath(self):
472 """Get the path of a node
473
474 Returns:
475 Full path of the node for this entry
476 """
477 return self._node.path
478
479 def GetData(self):
Simon Glass72eeff12020-10-26 17:40:16 -0600480 """Get the contents of an entry
481
482 Returns:
483 bytes content of the entry, excluding any padding. If the entry is
484 compressed, the compressed data is returned
485 """
Simon Glassb6dff4c2019-07-20 12:23:36 -0600486 self.Detail('GetData: size %s' % ToHexSize(self.data))
Simon Glass2574ef62016-11-25 20:15:51 -0700487 return self.data
488
Simon Glasse17220f2020-11-02 12:55:43 -0700489 def GetPaddedData(self, data=None):
490 """Get the data for an entry including any padding
491
492 Gets the entry data and uses its section's pad-byte value to add padding
493 before and after as defined by the pad-before and pad-after properties.
494
495 This does not consider alignment.
496
497 Returns:
498 Contents of the entry along with any pad bytes before and
499 after it (bytes)
500 """
501 if data is None:
502 data = self.GetData()
503 return self.section.GetPaddedDataForEntry(self, data)
504
Simon Glasse8561af2018-08-01 15:22:37 -0600505 def GetOffsets(self):
Simon Glass224bc662019-07-08 13:18:30 -0600506 """Get the offsets for siblings
507
508 Some entry types can contain information about the position or size of
509 other entries. An example of this is the Intel Flash Descriptor, which
510 knows where the Intel Management Engine section should go.
511
512 If this entry knows about the position of other entries, it can specify
513 this by returning values here
514
515 Returns:
516 Dict:
517 key: Entry type
518 value: List containing position and size of the given entry
Simon Glassed365eb2019-07-08 13:18:39 -0600519 type. Either can be None if not known
Simon Glass224bc662019-07-08 13:18:30 -0600520 """
Simon Glass2574ef62016-11-25 20:15:51 -0700521 return {}
522
Simon Glassed365eb2019-07-08 13:18:39 -0600523 def SetOffsetSize(self, offset, size):
524 """Set the offset and/or size of an entry
525
526 Args:
527 offset: New offset, or None to leave alone
528 size: New size, or None to leave alone
529 """
530 if offset is not None:
531 self.offset = offset
532 if size is not None:
533 self.size = size
Simon Glass2574ef62016-11-25 20:15:51 -0700534
Simon Glass9dcc8612018-08-01 15:22:42 -0600535 def SetImagePos(self, image_pos):
536 """Set the position in the image
537
538 Args:
539 image_pos: Position of this entry in the image
540 """
541 self.image_pos = image_pos + self.offset
542
Simon Glass2574ef62016-11-25 20:15:51 -0700543 def ProcessContents(self):
Simon Glassec849852019-07-08 14:25:35 -0600544 """Do any post-packing updates of entry contents
545
546 This function should call ProcessContentsUpdate() to update the entry
547 contents, if necessary, returning its return value here.
548
549 Args:
550 data: Data to set to the contents (bytes)
551
552 Returns:
553 True if the new data size is OK, False if expansion is needed
554
555 Raises:
556 ValueError if the new data size is not the same as the old and
557 state.AllowEntryExpansion() is False
558 """
559 return True
Simon Glass4ca8e042017-11-13 18:55:01 -0700560
Simon Glass8a6f56e2018-06-01 09:38:13 -0600561 def WriteSymbols(self, section):
Simon Glass4ca8e042017-11-13 18:55:01 -0700562 """Write symbol values into binary files for access at run time
563
564 Args:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600565 section: Section containing the entry
Simon Glass4ca8e042017-11-13 18:55:01 -0700566 """
567 pass
Simon Glassa91e1152018-06-01 09:38:16 -0600568
Simon Glass55f68072020-10-26 17:40:18 -0600569 def CheckEntries(self):
Simon Glasse8561af2018-08-01 15:22:37 -0600570 """Check that the entry offsets are correct
Simon Glassa91e1152018-06-01 09:38:16 -0600571
Simon Glasse8561af2018-08-01 15:22:37 -0600572 This is used for entries which have extra offset requirements (other
Simon Glassa91e1152018-06-01 09:38:16 -0600573 than having to be fully inside their section). Sub-classes can implement
574 this function and raise if there is a problem.
575 """
576 pass
Simon Glass30732662018-06-01 09:38:20 -0600577
Simon Glass3a9a2b82018-07-17 13:25:28 -0600578 @staticmethod
Simon Glasscd817d52018-09-14 04:57:36 -0600579 def GetStr(value):
580 if value is None:
581 return '<none> '
582 return '%08x' % value
583
584 @staticmethod
Simon Glass7eca7922018-07-17 13:25:49 -0600585 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glasscd817d52018-09-14 04:57:36 -0600586 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
587 Entry.GetStr(offset), Entry.GetStr(size),
588 name), file=fd)
Simon Glass3a9a2b82018-07-17 13:25:28 -0600589
Simon Glass30732662018-06-01 09:38:20 -0600590 def WriteMap(self, fd, indent):
591 """Write a map of the entry to a .map file
592
593 Args:
594 fd: File to write the map to
595 indent: Curent indent level of map (0=none, 1=one level, etc.)
596 """
Simon Glass7eca7922018-07-17 13:25:49 -0600597 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
598 self.image_pos)
Simon Glass91710b32018-07-17 13:25:32 -0600599
Simon Glass704784b2018-07-17 13:25:38 -0600600 def GetEntries(self):
601 """Return a list of entries contained by this entry
602
603 Returns:
604 List of entries, or None if none. A normal entry has no entries
605 within it so will return None
606 """
607 return None
608
Simon Glass91710b32018-07-17 13:25:32 -0600609 def GetArg(self, name, datatype=str):
610 """Get the value of an entry argument or device-tree-node property
611
612 Some node properties can be provided as arguments to binman. First check
613 the entry arguments, and fall back to the device tree if not found
614
615 Args:
616 name: Argument name
617 datatype: Data type (str or int)
618
619 Returns:
620 Value of argument as a string or int, or None if no value
621
622 Raises:
623 ValueError if the argument cannot be converted to in
624 """
Simon Glass29aa7362018-09-14 04:57:19 -0600625 value = state.GetEntryArg(name)
Simon Glass91710b32018-07-17 13:25:32 -0600626 if value is not None:
627 if datatype == int:
628 try:
629 value = int(value)
630 except ValueError:
631 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
632 (name, value))
633 elif datatype == str:
634 pass
635 else:
636 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
637 datatype)
638 else:
639 value = fdt_util.GetDatatype(self._node, name, datatype)
640 return value
Simon Glass969616c2018-07-17 13:25:36 -0600641
642 @staticmethod
643 def WriteDocs(modules, test_missing=None):
644 """Write out documentation about the various entry types to stdout
645
646 Args:
647 modules: List of modules to include
648 test_missing: Used for testing. This is a module to report
649 as missing
650 """
651 print('''Binman Entry Documentation
652===========================
653
654This file describes the entry types supported by binman. These entry types can
655be placed in an image one by one to build up a final firmware image. It is
656fairly easy to create new entry types. Just add a new file to the 'etype'
657directory. You can use the existing entries as examples.
658
659Note that some entries are subclasses of others, using and extending their
660features to produce new behaviours.
661
662
663''')
664 modules = sorted(modules)
665
666 # Don't show the test entry
667 if '_testing' in modules:
668 modules.remove('_testing')
669 missing = []
670 for name in modules:
Simon Glass2f859412021-03-18 20:25:04 +1300671 module = Entry.Lookup('WriteDocs', name, False)
Simon Glass969616c2018-07-17 13:25:36 -0600672 docs = getattr(module, '__doc__')
673 if test_missing == name:
674 docs = None
675 if docs:
676 lines = docs.splitlines()
677 first_line = lines[0]
678 rest = [line[4:] for line in lines[1:]]
679 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
680 print(hdr)
681 print('-' * len(hdr))
682 print('\n'.join(rest))
683 print()
684 print()
685 else:
686 missing.append(name)
687
688 if missing:
689 raise ValueError('Documentation is missing for modules: %s' %
690 ', '.join(missing))
Simon Glass639505b2018-09-14 04:57:11 -0600691
692 def GetUniqueName(self):
693 """Get a unique name for a node
694
695 Returns:
696 String containing a unique name for a node, consisting of the name
697 of all ancestors (starting from within the 'binman' node) separated
698 by a dot ('.'). This can be useful for generating unique filesnames
699 in the output directory.
700 """
701 name = self.name
702 node = self._node
703 while node.parent:
704 node = node.parent
705 if node.name == 'binman':
706 break
707 name = '%s.%s' % (node.name, name)
708 return name
Simon Glassfa79a812018-09-14 04:57:29 -0600709
710 def ExpandToLimit(self, limit):
711 """Expand an entry so that it ends at the given offset limit"""
712 if self.offset + self.size < limit:
713 self.size = limit - self.offset
714 # Request the contents again, since changing the size requires that
715 # the data grows. This should not fail, but check it to be sure.
716 if not self.ObtainContents():
717 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassc4056b82019-07-08 13:18:38 -0600718
719 def HasSibling(self, name):
720 """Check if there is a sibling of a given name
721
722 Returns:
723 True if there is an entry with this name in the the same section,
724 else False
725 """
726 return name in self.section.GetEntries()
Simon Glasscec34ba2019-07-08 14:25:28 -0600727
728 def GetSiblingImagePos(self, name):
729 """Return the image position of the given sibling
730
731 Returns:
732 Image position of sibling, or None if the sibling has no position,
733 or False if there is no such sibling
734 """
735 if not self.HasSibling(name):
736 return False
737 return self.section.GetEntries()[name].image_pos
Simon Glass6b156f82019-07-08 14:25:43 -0600738
739 @staticmethod
740 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
741 uncomp_size, offset, entry):
742 """Add a new entry to the entries list
743
744 Args:
745 entries: List (of EntryInfo objects) to add to
746 indent: Current indent level to add to list
747 name: Entry name (string)
748 etype: Entry type (string)
749 size: Entry size in bytes (int)
750 image_pos: Position within image in bytes (int)
751 uncomp_size: Uncompressed size if the entry uses compression, else
752 None
753 offset: Entry offset within parent in bytes (int)
754 entry: Entry object
755 """
756 entries.append(EntryInfo(indent, name, etype, size, image_pos,
757 uncomp_size, offset, entry))
758
759 def ListEntries(self, entries, indent):
760 """Add files in this entry to the list of entries
761
762 This can be overridden by subclasses which need different behaviour.
763
764 Args:
765 entries: List (of EntryInfo objects) to add to
766 indent: Current indent level to add to list
767 """
768 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
769 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glass4c613bf2019-07-08 14:25:50 -0600770
771 def ReadData(self, decomp=True):
772 """Read the data for an entry from the image
773
774 This is used when the image has been read in and we want to extract the
775 data for a particular entry from that image.
776
777 Args:
778 decomp: True to decompress any compressed data before returning it;
779 False to return the raw, uncompressed data
780
781 Returns:
782 Entry data (bytes)
783 """
784 # Use True here so that we get an uncompressed section to work from,
785 # although compressed sections are currently not supported
Simon Glass4d8151f2019-09-25 08:56:21 -0600786 tout.Debug("ReadChildData section '%s', entry '%s'" %
787 (self.section.GetPath(), self.GetPath()))
Simon Glass0cd8ace2019-07-20 12:24:04 -0600788 data = self.section.ReadChildData(self, decomp)
789 return data
Simon Glassaf8c45c2019-07-20 12:23:41 -0600790
Simon Glass23f00472019-09-25 08:56:20 -0600791 def ReadChildData(self, child, decomp=True):
Simon Glass4d8151f2019-09-25 08:56:21 -0600792 """Read the data for a particular child entry
Simon Glass23f00472019-09-25 08:56:20 -0600793
794 This reads data from the parent and extracts the piece that relates to
795 the given child.
796
797 Args:
Simon Glass4d8151f2019-09-25 08:56:21 -0600798 child: Child entry to read data for (must be valid)
Simon Glass23f00472019-09-25 08:56:20 -0600799 decomp: True to decompress any compressed data before returning it;
800 False to return the raw, uncompressed data
801
802 Returns:
803 Data for the child (bytes)
804 """
805 pass
806
Simon Glassaf8c45c2019-07-20 12:23:41 -0600807 def LoadData(self, decomp=True):
808 data = self.ReadData(decomp)
Simon Glass072959a2019-07-20 12:23:50 -0600809 self.contents_size = len(data)
Simon Glassaf8c45c2019-07-20 12:23:41 -0600810 self.ProcessContentsUpdate(data)
811 self.Detail('Loaded data size %x' % len(data))
Simon Glass990b1742019-07-20 12:23:46 -0600812
813 def GetImage(self):
814 """Get the image containing this entry
815
816 Returns:
817 Image object containing this entry
818 """
819 return self.section.GetImage()
Simon Glass072959a2019-07-20 12:23:50 -0600820
821 def WriteData(self, data, decomp=True):
822 """Write the data to an entry in the image
823
824 This is used when the image has been read in and we want to replace the
825 data for a particular entry in that image.
826
827 The image must be re-packed and written out afterwards.
828
829 Args:
830 data: Data to replace it with
831 decomp: True to compress the data if needed, False if data is
832 already compressed so should be used as is
833
834 Returns:
835 True if the data did not result in a resize of this entry, False if
836 the entry must be resized
837 """
Simon Glass1fdb4872019-10-31 07:43:02 -0600838 if self.size is not None:
839 self.contents_size = self.size
840 else:
841 self.contents_size = self.pre_reset_size
Simon Glass072959a2019-07-20 12:23:50 -0600842 ok = self.ProcessContentsUpdate(data)
843 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glassd34af7a2019-07-20 12:24:05 -0600844 section_ok = self.section.WriteChildData(self)
845 return ok and section_ok
846
847 def WriteChildData(self, child):
848 """Handle writing the data in a child entry
849
850 This should be called on the child's parent section after the child's
851 data has been updated. It
852
853 This base-class implementation does nothing, since the base Entry object
854 does not have any children.
855
856 Args:
857 child: Child Entry that was written
858
859 Returns:
860 True if the section could be updated successfully, False if the
861 data is such that the section could not updat
862 """
863 return True
Simon Glass11453762019-07-20 12:23:55 -0600864
865 def GetSiblingOrder(self):
866 """Get the relative order of an entry amoung its siblings
867
868 Returns:
869 'start' if this entry is first among siblings, 'end' if last,
870 otherwise None
871 """
872 entries = list(self.section.GetEntries().values())
873 if entries:
874 if self == entries[0]:
875 return 'start'
876 elif self == entries[-1]:
877 return 'end'
878 return 'middle'
Simon Glass5d94cc62020-07-09 18:39:38 -0600879
880 def SetAllowMissing(self, allow_missing):
881 """Set whether a section allows missing external blobs
882
883 Args:
884 allow_missing: True if allowed, False if not allowed
885 """
886 # This is meaningless for anything other than sections
887 pass
Simon Glassa003cd32020-07-09 18:39:40 -0600888
889 def CheckMissing(self, missing_list):
890 """Check if any entries in this section have missing external blobs
891
892 If there are missing blobs, the entries are added to the list
893
894 Args:
895 missing_list: List of Entry objects to be added to
896 """
897 if self.missing:
898 missing_list.append(self)
Simon Glassb8f90372020-09-01 05:13:57 -0600899
900 def GetAllowMissing(self):
901 """Get whether a section allows missing external blobs
902
903 Returns:
904 True if allowed, False if not allowed
905 """
906 return self.allow_missing
Simon Glassa820af72020-09-06 10:39:09 -0600907
908 def GetHelpTags(self):
909 """Get the tags use for missing-blob help
910
911 Returns:
912 list of possible tags, most desirable first
913 """
914 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glassa1301a22020-10-26 17:40:06 -0600915
916 def CompressData(self, indata):
917 """Compress data according to the entry's compression method
918
919 Args:
920 indata: Data to compress
921
922 Returns:
923 Compressed data (first word is the compressed size)
924 """
Simon Glass789b34402020-10-26 17:40:15 -0600925 self.uncomp_data = indata
Simon Glassa1301a22020-10-26 17:40:06 -0600926 if self.compress != 'none':
927 self.uncomp_size = len(indata)
928 data = tools.Compress(indata, self.compress)
929 return data
Simon Glass2f859412021-03-18 20:25:04 +1300930
931 @classmethod
932 def UseExpanded(cls, node, etype, new_etype):
933 """Check whether to use an expanded entry type
934
935 This is called by Entry.Create() when it finds an expanded version of
936 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
937 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
938 ignored.
939
940 Args:
941 node: Node object containing information about the entry to
942 create
943 etype: Original entry type being used
944 new_etype: New entry type proposed
945
946 Returns:
947 True to use this entry type, False to use the original one
948 """
949 tout.Info("Node '%s': etype '%s': %s selected" %
950 (node.path, etype, new_etype))
951 return True