blob: 173c9131cbbaa291744ae5d9e84bf484875aaddf [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassbf7fd502016-11-25 20:15:51 -07002# Copyright (c) 2016 Google, Inc
3#
Simon Glassbf7fd502016-11-25 20:15:51 -07004# Base class for all entries
5#
6
Simon Glass53af22a2018-07-17 13:25:32 -06007from collections import namedtuple
Simon Glassb4cf5f12019-10-31 07:42:59 -06008import importlib
Simon Glassbadf0ec2018-06-01 09:38:15 -06009import os
10import sys
Simon Glassc55a50f2018-09-14 04:57:19 -060011
Simon Glass16287932020-04-17 18:09:03 -060012from dtoc import fdt_util
Simon Glassbf776672020-04-17 18:09:04 -060013from patman import tools
Simon Glass16287932020-04-17 18:09:03 -060014from patman.tools import ToHex, ToHexSize
Simon Glassbf776672020-04-17 18:09:04 -060015from patman import tout
Simon Glassbf7fd502016-11-25 20:15:51 -070016
17modules = {}
18
Simon Glass53af22a2018-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 Glass41b8ba02019-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 Glass53af22a2018-07-17 13:25:32 -060028
Simon Glassbf7fd502016-11-25 20:15:51 -070029class Entry(object):
Simon Glass25ac0e62018-06-01 09:38:14 -060030 """An Entry in the section
Simon Glassbf7fd502016-11-25 20:15:51 -070031
32 An entry corresponds to a single node in the device-tree description
Simon Glass25ac0e62018-06-01 09:38:14 -060033 of the section. Each entry ends up being a part of the final section.
Simon Glassbf7fd502016-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 Glass8122f392018-07-17 13:25:28 -060041 section: Section object containing this entry
Simon Glassbf7fd502016-11-25 20:15:51 -070042 node: The node that created this entry
Simon Glass3ab95982018-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 Glassbf7fd502016-11-25 20:15:51 -070045 size: Entry size in bytes, None if not known
Simon Glass9a5d3dc2019-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 Glass8287ee82019-07-08 14:25:30 -060048 uncomp_size: Size of uncompressed data in bytes, if the entry is
49 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070050 contents_size: Size of contents in bytes, 0 by default
Simon Glass3ab95982018-08-01 15:22:37 -060051 align: Entry start offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070052 align_size: Entry size alignment, or None
Simon Glass3ab95982018-08-01 15:22:37 -060053 align_end: Entry end offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070054 pad_before: Number of pad bytes before the contents, 0 if none
55 pad_after: Number of pad bytes after the contents, 0 if none
56 data: Contents of entry (string of bytes)
Simon Glass8287ee82019-07-08 14:25:30 -060057 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassc52c9e72019-07-08 14:25:37 -060058 orig_offset: Original offset value read from node
59 orig_size: Original size value read from node
Simon Glass87958982020-09-01 05:13:57 -060060 missing: True if this entry is missing its contents
61 allow_missing: Allow children of this entry to be missing (used by
62 subclasses such as Entry_section)
63 external: True if this entry contains an external binary blob
Simon Glassbf7fd502016-11-25 20:15:51 -070064 """
Simon Glassc6bd6e22019-07-20 12:23:45 -060065 def __init__(self, section, etype, node, name_prefix=''):
Simon Glass8dbb7442019-08-24 07:22:44 -060066 # Put this here to allow entry-docs and help to work without libfdt
67 global state
Simon Glass16287932020-04-17 18:09:03 -060068 from binman import state
Simon Glass8dbb7442019-08-24 07:22:44 -060069
Simon Glass25ac0e62018-06-01 09:38:14 -060070 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -070071 self.etype = etype
72 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -060073 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -060074 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -070075 self.size = None
Simon Glass9a5d3dc2019-10-31 07:43:02 -060076 self.pre_reset_size = None
Simon Glass8287ee82019-07-08 14:25:30 -060077 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -060078 self.data = None
Simon Glassbf7fd502016-11-25 20:15:51 -070079 self.contents_size = 0
80 self.align = None
81 self.align_size = None
82 self.align_end = None
83 self.pad_before = 0
84 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -060085 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -060086 self.image_pos = None
Simon Glassba64a0b2018-09-14 04:57:29 -060087 self._expand_size = False
Simon Glass8287ee82019-07-08 14:25:30 -060088 self.compress = 'none'
Simon Glassb1cca952020-07-09 18:39:40 -060089 self.missing = False
Simon Glass87958982020-09-01 05:13:57 -060090 self.external = False
91 self.allow_missing = False
Simon Glassbf7fd502016-11-25 20:15:51 -070092
93 @staticmethod
Simon Glassc073ced2019-07-08 14:25:31 -060094 def Lookup(node_path, etype):
Simon Glassfd8d1f72018-07-17 13:25:36 -060095 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -070096
97 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -060098 node_node: Path name of Node object containing information about
99 the entry to create (used for errors)
100 etype: Entry type to use
Simon Glassbf7fd502016-11-25 20:15:51 -0700101
102 Returns:
Simon Glassfd8d1f72018-07-17 13:25:36 -0600103 The entry class object if found, else None
Simon Glassbf7fd502016-11-25 20:15:51 -0700104 """
Simon Glassdd57c132018-06-01 09:38:11 -0600105 # Convert something like 'u-boot@0' to 'u_boot' since we are only
106 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700107 module_name = etype.replace('-', '_')
Simon Glassdd57c132018-06-01 09:38:11 -0600108 if '@' in module_name:
109 module_name = module_name.split('@')[0]
Simon Glassbf7fd502016-11-25 20:15:51 -0700110 module = modules.get(module_name)
111
Simon Glassbadf0ec2018-06-01 09:38:15 -0600112 # Also allow entry-type modules to be brought in from the etype directory.
113
Simon Glassbf7fd502016-11-25 20:15:51 -0700114 # Import the module if we have not already done so.
115 if not module:
116 try:
Simon Glass16287932020-04-17 18:09:03 -0600117 module = importlib.import_module('binman.etype.' + module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600118 except ImportError as e:
119 raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
120 (etype, node_path, module_name, e))
Simon Glassbf7fd502016-11-25 20:15:51 -0700121 modules[module_name] = module
122
Simon Glassfd8d1f72018-07-17 13:25:36 -0600123 # Look up the expected class name
124 return getattr(module, 'Entry_%s' % module_name)
125
126 @staticmethod
127 def Create(section, node, etype=None):
128 """Create a new entry for a node.
129
130 Args:
131 section: Section object containing this node
132 node: Node object containing information about the entry to
133 create
134 etype: Entry type to use, or None to work it out (used for tests)
135
136 Returns:
137 A new Entry object of the correct type (a subclass of Entry)
138 """
139 if not etype:
140 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glassc073ced2019-07-08 14:25:31 -0600141 obj = Entry.Lookup(node.path, etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600142
Simon Glassbf7fd502016-11-25 20:15:51 -0700143 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600144 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700145
146 def ReadNode(self):
147 """Read entry information from the node
148
Simon Glassc6bd6e22019-07-20 12:23:45 -0600149 This must be called as the first thing after the Entry is created.
150
Simon Glassbf7fd502016-11-25 20:15:51 -0700151 This reads all the fields we recognise from the node, ready for use.
152 """
Simon Glass15a587c2018-07-17 13:25:51 -0600153 if 'pos' in self._node.props:
154 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass3ab95982018-08-01 15:22:37 -0600155 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700156 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glass12bb1a92019-07-20 12:23:51 -0600157 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
158 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
159 if self.GetImage().copy_to_orig:
160 self.orig_offset = self.offset
161 self.orig_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600162
Simon Glassffded752019-07-08 14:25:46 -0600163 # These should not be set in input files, but are set in an FDT map,
164 # which is also read by this code.
165 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
166 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
167
Simon Glassbf7fd502016-11-25 20:15:51 -0700168 self.align = fdt_util.GetInt(self._node, 'align')
169 if tools.NotPowerOfTwo(self.align):
170 raise ValueError("Node '%s': Alignment %s must be a power of two" %
171 (self._node.path, self.align))
172 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
173 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
174 self.align_size = fdt_util.GetInt(self._node, 'align-size')
175 if tools.NotPowerOfTwo(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600176 self.Raise("Alignment size %s must be a power of two" %
177 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700178 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600179 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassba64a0b2018-09-14 04:57:29 -0600180 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
Simon Glassb2381432020-09-06 10:39:09 -0600181 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glassbf7fd502016-11-25 20:15:51 -0700182
Simon Glass87c96292020-10-26 17:40:06 -0600183 # This is only supported by blobs and sections at present
184 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
185
Simon Glass6c234bf2018-09-14 04:57:18 -0600186 def GetDefaultFilename(self):
187 return None
188
Simon Glassa8adb6d2019-07-20 12:23:28 -0600189 def GetFdts(self):
190 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600191
192 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600193 Empty dict, if this entry is not a .dtb, otherwise:
194 Dict:
195 key: Filename from this entry (without the path)
Simon Glass4bdd3002019-07-20 12:23:31 -0600196 value: Tuple:
197 Fdt object for this dtb, or None if not available
198 Filename of file containing this dtb
Simon Glass539aece2018-09-14 04:57:22 -0600199 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600200 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600201
Simon Glass0a98b282018-09-14 04:57:28 -0600202 def ExpandEntries(self):
203 pass
204
Simon Glass078ab1a2018-07-06 10:27:41 -0600205 def AddMissingProperties(self):
206 """Add new properties to the device tree as needed for this entry"""
Simon Glassdbf6be92018-08-01 15:22:42 -0600207 for prop in ['offset', 'size', 'image-pos']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600208 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600209 state.AddZeroProp(self._node, prop)
Simon Glass12bb1a92019-07-20 12:23:51 -0600210 if self.GetImage().allow_repack:
211 if self.orig_offset is not None:
212 state.AddZeroProp(self._node, 'orig-offset', True)
213 if self.orig_size is not None:
214 state.AddZeroProp(self._node, 'orig-size', True)
215
Simon Glass8287ee82019-07-08 14:25:30 -0600216 if self.compress != 'none':
217 state.AddZeroProp(self._node, 'uncomp-size')
Simon Glasse0e5df92018-09-14 04:57:31 -0600218 err = state.CheckAddHashProp(self._node)
219 if err:
220 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600221
222 def SetCalculatedProperties(self):
223 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600224 state.SetInt(self._node, 'offset', self.offset)
225 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600226 base = self.section.GetRootSkipAtStart() if self.section else 0
227 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass12bb1a92019-07-20 12:23:51 -0600228 if self.GetImage().allow_repack:
229 if self.orig_offset is not None:
230 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
231 if self.orig_size is not None:
232 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glass8287ee82019-07-08 14:25:30 -0600233 if self.uncomp_size is not None:
234 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Simon Glasse0e5df92018-09-14 04:57:31 -0600235 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600236
Simon Glassecab8972018-07-06 10:27:40 -0600237 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600238 """Allow entries to adjust the device tree
239
240 Some entries need to adjust the device tree for their purposes. This
241 may involve adding or deleting properties.
242
243 Returns:
244 True if processing is complete
245 False if processing could not be completed due to a dependency.
246 This will cause the entry to be retried after others have been
247 called
248 """
Simon Glassecab8972018-07-06 10:27:40 -0600249 return True
250
Simon Glassc8d48ef2018-06-01 09:38:21 -0600251 def SetPrefix(self, prefix):
252 """Set the name prefix for a node
253
254 Args:
255 prefix: Prefix to set, or '' to not use a prefix
256 """
257 if prefix:
258 self.name = prefix + self.name
259
Simon Glass5c890232018-07-06 10:27:19 -0600260 def SetContents(self, data):
261 """Set the contents of an entry
262
263 This sets both the data and content_size properties
264
265 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600266 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600267 """
268 self.data = data
269 self.contents_size = len(self.data)
270
271 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600272 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600273
Simon Glassa0dcaf22019-07-08 14:25:35 -0600274 This checks that the new data is the same size as the old. If the size
275 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600276
277 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600278 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600279
280 Raises:
281 ValueError if the new data size is not the same as the old
282 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600283 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600284 new_size = len(data)
Simon Glass61ec04f2019-07-20 12:23:58 -0600285 if state.AllowEntryExpansion() and new_size > self.contents_size:
286 # self.data will indicate the new size needed
287 size_ok = False
288 elif state.AllowEntryContraction() and new_size < self.contents_size:
289 size_ok = False
290
291 # If not allowed to change, try to deal with it or give up
292 if size_ok:
Simon Glassc52c9e72019-07-08 14:25:37 -0600293 if new_size > self.contents_size:
Simon Glass61ec04f2019-07-20 12:23:58 -0600294 self.Raise('Cannot update entry size from %d to %d' %
295 (self.contents_size, new_size))
296
297 # Don't let the data shrink. Pad it if necessary
298 if size_ok and new_size < self.contents_size:
299 data += tools.GetBytes(0, self.contents_size - new_size)
300
301 if not size_ok:
302 tout.Debug("Entry '%s' size change from %s to %s" % (
303 self._node.path, ToHex(self.contents_size),
304 ToHex(new_size)))
Simon Glass5c890232018-07-06 10:27:19 -0600305 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600306 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600307
Simon Glassbf7fd502016-11-25 20:15:51 -0700308 def ObtainContents(self):
309 """Figure out the contents of an entry.
310
311 Returns:
312 True if the contents were found, False if another call is needed
313 after the other entries are processed.
314 """
315 # No contents by default: subclasses can implement this
316 return True
317
Simon Glassc52c9e72019-07-08 14:25:37 -0600318 def ResetForPack(self):
319 """Reset offset/size fields so that packing can be done again"""
Simon Glass9f297b02019-07-20 12:23:36 -0600320 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
321 (ToHex(self.offset), ToHex(self.orig_offset),
322 ToHex(self.size), ToHex(self.orig_size)))
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600323 self.pre_reset_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600324 self.offset = self.orig_offset
325 self.size = self.orig_size
326
Simon Glass3ab95982018-08-01 15:22:37 -0600327 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600328 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700329
330 Most of the time the entries are not fully specified. There may be
331 an alignment but no size. In that case we take the size from the
332 contents of the entry.
333
Simon Glass3ab95982018-08-01 15:22:37 -0600334 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700335
Simon Glass3ab95982018-08-01 15:22:37 -0600336 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700337 entry will be know.
338
339 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600340 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700341
342 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600343 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700344 """
Simon Glass9f297b02019-07-20 12:23:36 -0600345 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
346 (ToHex(self.offset), ToHex(self.size),
347 self.contents_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600348 if self.offset is None:
349 if self.offset_unset:
350 self.Raise('No offset set with offset-unset: should another '
351 'entry provide this correct offset?')
352 self.offset = tools.Align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700353 needed = self.pad_before + self.contents_size + self.pad_after
354 needed = tools.Align(needed, self.align_size)
355 size = self.size
356 if not size:
357 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600358 new_offset = self.offset + size
359 aligned_offset = tools.Align(new_offset, self.align_end)
360 if aligned_offset != new_offset:
361 size = aligned_offset - self.offset
362 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700363
364 if not self.size:
365 self.size = size
366
367 if self.size < needed:
368 self.Raise("Entry contents size is %#x (%d) but entry size is "
369 "%#x (%d)" % (needed, needed, self.size, self.size))
370 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600371 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700372 # conflict with the provided alignment values
373 if self.size != tools.Align(self.size, self.align_size):
374 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
375 (self.size, self.size, self.align_size, self.align_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600376 if self.offset != tools.Align(self.offset, self.align):
377 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
378 (self.offset, self.offset, self.align, self.align))
Simon Glass9f297b02019-07-20 12:23:36 -0600379 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
380 (self.offset, self.size, self.contents_size, new_offset))
Simon Glassbf7fd502016-11-25 20:15:51 -0700381
Simon Glass3ab95982018-08-01 15:22:37 -0600382 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700383
384 def Raise(self, msg):
385 """Convenience function to raise an error referencing a node"""
386 raise ValueError("Node '%s': %s" % (self._node.path, msg))
387
Simon Glass9f297b02019-07-20 12:23:36 -0600388 def Detail(self, msg):
389 """Convenience function to log detail referencing a node"""
390 tag = "Node '%s'" % self._node.path
391 tout.Detail('%30s: %s' % (tag, msg))
392
Simon Glass53af22a2018-07-17 13:25:32 -0600393 def GetEntryArgsOrProps(self, props, required=False):
394 """Return the values of a set of properties
395
396 Args:
397 props: List of EntryArg objects
398
399 Raises:
400 ValueError if a property is not found
401 """
402 values = []
403 missing = []
404 for prop in props:
405 python_prop = prop.name.replace('-', '_')
406 if hasattr(self, python_prop):
407 value = getattr(self, python_prop)
408 else:
409 value = None
410 if value is None:
411 value = self.GetArg(prop.name, prop.datatype)
412 if value is None and required:
413 missing.append(prop.name)
414 values.append(value)
415 if missing:
416 self.Raise('Missing required properties/entry args: %s' %
417 (', '.join(missing)))
418 return values
419
Simon Glassbf7fd502016-11-25 20:15:51 -0700420 def GetPath(self):
421 """Get the path of a node
422
423 Returns:
424 Full path of the node for this entry
425 """
426 return self._node.path
427
428 def GetData(self):
Simon Glass9f297b02019-07-20 12:23:36 -0600429 self.Detail('GetData: size %s' % ToHexSize(self.data))
Simon Glassbf7fd502016-11-25 20:15:51 -0700430 return self.data
431
Simon Glass3ab95982018-08-01 15:22:37 -0600432 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600433 """Get the offsets for siblings
434
435 Some entry types can contain information about the position or size of
436 other entries. An example of this is the Intel Flash Descriptor, which
437 knows where the Intel Management Engine section should go.
438
439 If this entry knows about the position of other entries, it can specify
440 this by returning values here
441
442 Returns:
443 Dict:
444 key: Entry type
445 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600446 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600447 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700448 return {}
449
Simon Glasscf549042019-07-08 13:18:39 -0600450 def SetOffsetSize(self, offset, size):
451 """Set the offset and/or size of an entry
452
453 Args:
454 offset: New offset, or None to leave alone
455 size: New size, or None to leave alone
456 """
457 if offset is not None:
458 self.offset = offset
459 if size is not None:
460 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700461
Simon Glassdbf6be92018-08-01 15:22:42 -0600462 def SetImagePos(self, image_pos):
463 """Set the position in the image
464
465 Args:
466 image_pos: Position of this entry in the image
467 """
468 self.image_pos = image_pos + self.offset
469
Simon Glassbf7fd502016-11-25 20:15:51 -0700470 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600471 """Do any post-packing updates of entry contents
472
473 This function should call ProcessContentsUpdate() to update the entry
474 contents, if necessary, returning its return value here.
475
476 Args:
477 data: Data to set to the contents (bytes)
478
479 Returns:
480 True if the new data size is OK, False if expansion is needed
481
482 Raises:
483 ValueError if the new data size is not the same as the old and
484 state.AllowEntryExpansion() is False
485 """
486 return True
Simon Glass19790632017-11-13 18:55:01 -0700487
Simon Glassf55382b2018-06-01 09:38:13 -0600488 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700489 """Write symbol values into binary files for access at run time
490
491 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600492 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700493 """
494 pass
Simon Glass18546952018-06-01 09:38:16 -0600495
Simon Glass3ab95982018-08-01 15:22:37 -0600496 def CheckOffset(self):
497 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600498
Simon Glass3ab95982018-08-01 15:22:37 -0600499 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600500 than having to be fully inside their section). Sub-classes can implement
501 this function and raise if there is a problem.
502 """
503 pass
Simon Glass3b0c3822018-06-01 09:38:20 -0600504
Simon Glass8122f392018-07-17 13:25:28 -0600505 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600506 def GetStr(value):
507 if value is None:
508 return '<none> '
509 return '%08x' % value
510
511 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600512 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600513 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
514 Entry.GetStr(offset), Entry.GetStr(size),
515 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600516
Simon Glass3b0c3822018-06-01 09:38:20 -0600517 def WriteMap(self, fd, indent):
518 """Write a map of the entry to a .map file
519
520 Args:
521 fd: File to write the map to
522 indent: Curent indent level of map (0=none, 1=one level, etc.)
523 """
Simon Glass1be70d22018-07-17 13:25:49 -0600524 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
525 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600526
Simon Glass11e36cc2018-07-17 13:25:38 -0600527 def GetEntries(self):
528 """Return a list of entries contained by this entry
529
530 Returns:
531 List of entries, or None if none. A normal entry has no entries
532 within it so will return None
533 """
534 return None
535
Simon Glass53af22a2018-07-17 13:25:32 -0600536 def GetArg(self, name, datatype=str):
537 """Get the value of an entry argument or device-tree-node property
538
539 Some node properties can be provided as arguments to binman. First check
540 the entry arguments, and fall back to the device tree if not found
541
542 Args:
543 name: Argument name
544 datatype: Data type (str or int)
545
546 Returns:
547 Value of argument as a string or int, or None if no value
548
549 Raises:
550 ValueError if the argument cannot be converted to in
551 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600552 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600553 if value is not None:
554 if datatype == int:
555 try:
556 value = int(value)
557 except ValueError:
558 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
559 (name, value))
560 elif datatype == str:
561 pass
562 else:
563 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
564 datatype)
565 else:
566 value = fdt_util.GetDatatype(self._node, name, datatype)
567 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600568
569 @staticmethod
570 def WriteDocs(modules, test_missing=None):
571 """Write out documentation about the various entry types to stdout
572
573 Args:
574 modules: List of modules to include
575 test_missing: Used for testing. This is a module to report
576 as missing
577 """
578 print('''Binman Entry Documentation
579===========================
580
581This file describes the entry types supported by binman. These entry types can
582be placed in an image one by one to build up a final firmware image. It is
583fairly easy to create new entry types. Just add a new file to the 'etype'
584directory. You can use the existing entries as examples.
585
586Note that some entries are subclasses of others, using and extending their
587features to produce new behaviours.
588
589
590''')
591 modules = sorted(modules)
592
593 # Don't show the test entry
594 if '_testing' in modules:
595 modules.remove('_testing')
596 missing = []
597 for name in modules:
Simon Glass16287932020-04-17 18:09:03 -0600598 module = Entry.Lookup('WriteDocs', name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600599 docs = getattr(module, '__doc__')
600 if test_missing == name:
601 docs = None
602 if docs:
603 lines = docs.splitlines()
604 first_line = lines[0]
605 rest = [line[4:] for line in lines[1:]]
606 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
607 print(hdr)
608 print('-' * len(hdr))
609 print('\n'.join(rest))
610 print()
611 print()
612 else:
613 missing.append(name)
614
615 if missing:
616 raise ValueError('Documentation is missing for modules: %s' %
617 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600618
619 def GetUniqueName(self):
620 """Get a unique name for a node
621
622 Returns:
623 String containing a unique name for a node, consisting of the name
624 of all ancestors (starting from within the 'binman' node) separated
625 by a dot ('.'). This can be useful for generating unique filesnames
626 in the output directory.
627 """
628 name = self.name
629 node = self._node
630 while node.parent:
631 node = node.parent
632 if node.name == 'binman':
633 break
634 name = '%s.%s' % (node.name, name)
635 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600636
637 def ExpandToLimit(self, limit):
638 """Expand an entry so that it ends at the given offset limit"""
639 if self.offset + self.size < limit:
640 self.size = limit - self.offset
641 # Request the contents again, since changing the size requires that
642 # the data grows. This should not fail, but check it to be sure.
643 if not self.ObtainContents():
644 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600645
646 def HasSibling(self, name):
647 """Check if there is a sibling of a given name
648
649 Returns:
650 True if there is an entry with this name in the the same section,
651 else False
652 """
653 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600654
655 def GetSiblingImagePos(self, name):
656 """Return the image position of the given sibling
657
658 Returns:
659 Image position of sibling, or None if the sibling has no position,
660 or False if there is no such sibling
661 """
662 if not self.HasSibling(name):
663 return False
664 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600665
666 @staticmethod
667 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
668 uncomp_size, offset, entry):
669 """Add a new entry to the entries list
670
671 Args:
672 entries: List (of EntryInfo objects) to add to
673 indent: Current indent level to add to list
674 name: Entry name (string)
675 etype: Entry type (string)
676 size: Entry size in bytes (int)
677 image_pos: Position within image in bytes (int)
678 uncomp_size: Uncompressed size if the entry uses compression, else
679 None
680 offset: Entry offset within parent in bytes (int)
681 entry: Entry object
682 """
683 entries.append(EntryInfo(indent, name, etype, size, image_pos,
684 uncomp_size, offset, entry))
685
686 def ListEntries(self, entries, indent):
687 """Add files in this entry to the list of entries
688
689 This can be overridden by subclasses which need different behaviour.
690
691 Args:
692 entries: List (of EntryInfo objects) to add to
693 indent: Current indent level to add to list
694 """
695 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
696 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600697
698 def ReadData(self, decomp=True):
699 """Read the data for an entry from the image
700
701 This is used when the image has been read in and we want to extract the
702 data for a particular entry from that image.
703
704 Args:
705 decomp: True to decompress any compressed data before returning it;
706 False to return the raw, uncompressed data
707
708 Returns:
709 Entry data (bytes)
710 """
711 # Use True here so that we get an uncompressed section to work from,
712 # although compressed sections are currently not supported
Simon Glass2d553c02019-09-25 08:56:21 -0600713 tout.Debug("ReadChildData section '%s', entry '%s'" %
714 (self.section.GetPath(), self.GetPath()))
Simon Glassa9cd39e2019-07-20 12:24:04 -0600715 data = self.section.ReadChildData(self, decomp)
716 return data
Simon Glassd5079332019-07-20 12:23:41 -0600717
Simon Glass4e185e82019-09-25 08:56:20 -0600718 def ReadChildData(self, child, decomp=True):
Simon Glass2d553c02019-09-25 08:56:21 -0600719 """Read the data for a particular child entry
Simon Glass4e185e82019-09-25 08:56:20 -0600720
721 This reads data from the parent and extracts the piece that relates to
722 the given child.
723
724 Args:
Simon Glass2d553c02019-09-25 08:56:21 -0600725 child: Child entry to read data for (must be valid)
Simon Glass4e185e82019-09-25 08:56:20 -0600726 decomp: True to decompress any compressed data before returning it;
727 False to return the raw, uncompressed data
728
729 Returns:
730 Data for the child (bytes)
731 """
732 pass
733
Simon Glassd5079332019-07-20 12:23:41 -0600734 def LoadData(self, decomp=True):
735 data = self.ReadData(decomp)
Simon Glass10f9d002019-07-20 12:23:50 -0600736 self.contents_size = len(data)
Simon Glassd5079332019-07-20 12:23:41 -0600737 self.ProcessContentsUpdate(data)
738 self.Detail('Loaded data size %x' % len(data))
Simon Glassc5ad04b2019-07-20 12:23:46 -0600739
740 def GetImage(self):
741 """Get the image containing this entry
742
743 Returns:
744 Image object containing this entry
745 """
746 return self.section.GetImage()
Simon Glass10f9d002019-07-20 12:23:50 -0600747
748 def WriteData(self, data, decomp=True):
749 """Write the data to an entry in the image
750
751 This is used when the image has been read in and we want to replace the
752 data for a particular entry in that image.
753
754 The image must be re-packed and written out afterwards.
755
756 Args:
757 data: Data to replace it with
758 decomp: True to compress the data if needed, False if data is
759 already compressed so should be used as is
760
761 Returns:
762 True if the data did not result in a resize of this entry, False if
763 the entry must be resized
764 """
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600765 if self.size is not None:
766 self.contents_size = self.size
767 else:
768 self.contents_size = self.pre_reset_size
Simon Glass10f9d002019-07-20 12:23:50 -0600769 ok = self.ProcessContentsUpdate(data)
770 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glass7210c892019-07-20 12:24:05 -0600771 section_ok = self.section.WriteChildData(self)
772 return ok and section_ok
773
774 def WriteChildData(self, child):
775 """Handle writing the data in a child entry
776
777 This should be called on the child's parent section after the child's
778 data has been updated. It
779
780 This base-class implementation does nothing, since the base Entry object
781 does not have any children.
782
783 Args:
784 child: Child Entry that was written
785
786 Returns:
787 True if the section could be updated successfully, False if the
788 data is such that the section could not updat
789 """
790 return True
Simon Glasseba1f0c2019-07-20 12:23:55 -0600791
792 def GetSiblingOrder(self):
793 """Get the relative order of an entry amoung its siblings
794
795 Returns:
796 'start' if this entry is first among siblings, 'end' if last,
797 otherwise None
798 """
799 entries = list(self.section.GetEntries().values())
800 if entries:
801 if self == entries[0]:
802 return 'start'
803 elif self == entries[-1]:
804 return 'end'
805 return 'middle'
Simon Glass4f9f1052020-07-09 18:39:38 -0600806
807 def SetAllowMissing(self, allow_missing):
808 """Set whether a section allows missing external blobs
809
810 Args:
811 allow_missing: True if allowed, False if not allowed
812 """
813 # This is meaningless for anything other than sections
814 pass
Simon Glassb1cca952020-07-09 18:39:40 -0600815
816 def CheckMissing(self, missing_list):
817 """Check if any entries in this section have missing external blobs
818
819 If there are missing blobs, the entries are added to the list
820
821 Args:
822 missing_list: List of Entry objects to be added to
823 """
824 if self.missing:
825 missing_list.append(self)
Simon Glass87958982020-09-01 05:13:57 -0600826
827 def GetAllowMissing(self):
828 """Get whether a section allows missing external blobs
829
830 Returns:
831 True if allowed, False if not allowed
832 """
833 return self.allow_missing
Simon Glassb2381432020-09-06 10:39:09 -0600834
835 def GetHelpTags(self):
836 """Get the tags use for missing-blob help
837
838 Returns:
839 list of possible tags, most desirable first
840 """
841 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glass87c96292020-10-26 17:40:06 -0600842
843 def CompressData(self, indata):
844 """Compress data according to the entry's compression method
845
846 Args:
847 indata: Data to compress
848
849 Returns:
850 Compressed data (first word is the compressed size)
851 """
852 if self.compress != 'none':
853 self.uncomp_size = len(indata)
854 data = tools.Compress(indata, self.compress)
855 return data