blob: e3767aefa737d22726ee7dc7f09d99614eb40172 [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
Simon Glass790ba9f2022-01-12 13:10:36 -070010import pathlib
Simon Glassbadf0ec2018-06-01 09:38:15 -060011import sys
Simon Glassc55a50f2018-09-14 04:57:19 -060012
Simon Glass386c63c2022-01-09 20:13:50 -070013from binman import bintool
Simon Glassad35ce52022-01-09 20:14:03 -070014from binman import comp_util
Simon Glass16287932020-04-17 18:09:03 -060015from dtoc import fdt_util
Simon Glassbf776672020-04-17 18:09:04 -060016from patman import tools
Simon Glassc1aa66e2022-01-29 14:14:04 -070017from patman.tools import to_hex, to_hex_size
Simon Glassbf776672020-04-17 18:09:04 -060018from patman import tout
Simon Glassbf7fd502016-11-25 20:15:51 -070019
20modules = {}
21
Simon Glass8d2ef3e2022-02-11 13:23:21 -070022# This is imported if needed
23state = None
Simon Glass53af22a2018-07-17 13:25:32 -060024
25# An argument which can be passed to entries on the command line, in lieu of
26# device-tree properties.
27EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
28
Simon Glass41b8ba02019-07-08 14:25:43 -060029# Information about an entry for use when displaying summaries
30EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
31 'image_pos', 'uncomp_size', 'offset',
32 'entry'])
Simon Glass53af22a2018-07-17 13:25:32 -060033
Simon Glassbf7fd502016-11-25 20:15:51 -070034class Entry(object):
Simon Glass25ac0e62018-06-01 09:38:14 -060035 """An Entry in the section
Simon Glassbf7fd502016-11-25 20:15:51 -070036
37 An entry corresponds to a single node in the device-tree description
Simon Glass25ac0e62018-06-01 09:38:14 -060038 of the section. Each entry ends up being a part of the final section.
Simon Glassbf7fd502016-11-25 20:15:51 -070039 Entries can be placed either right next to each other, or with padding
40 between them. The type of the entry determines the data that is in it.
41
42 This class is not used by itself. All entry objects are subclasses of
43 Entry.
44
45 Attributes:
Simon Glass8122f392018-07-17 13:25:28 -060046 section: Section object containing this entry
Simon Glassbf7fd502016-11-25 20:15:51 -070047 node: The node that created this entry
Simon Glass3ab95982018-08-01 15:22:37 -060048 offset: Offset of entry within the section, None if not known yet (in
49 which case it will be calculated by Pack())
Simon Glassbf7fd502016-11-25 20:15:51 -070050 size: Entry size in bytes, None if not known
Simon Glass9a5d3dc2019-10-31 07:43:02 -060051 pre_reset_size: size as it was before ResetForPack(). This allows us to
52 keep track of the size we started with and detect size changes
Simon Glass8287ee82019-07-08 14:25:30 -060053 uncomp_size: Size of uncompressed data in bytes, if the entry is
54 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070055 contents_size: Size of contents in bytes, 0 by default
Simon Glass4eec34c2020-10-26 17:40:10 -060056 align: Entry start offset alignment relative to the start of the
57 containing section, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070058 align_size: Entry size alignment, or None
Simon Glass4eec34c2020-10-26 17:40:10 -060059 align_end: Entry end offset alignment relative to the start of the
60 containing section, or None
Simon Glassf90d9062020-10-26 17:40:09 -060061 pad_before: Number of pad bytes before the contents when it is placed
62 in the containing section, 0 if none. The pad bytes become part of
63 the entry.
64 pad_after: Number of pad bytes after the contents when it is placed in
65 the containing section, 0 if none. The pad bytes become part of
66 the entry.
67 data: Contents of entry (string of bytes). This does not include
Simon Glass97c3e9a2020-10-26 17:40:15 -060068 padding created by pad_before or pad_after. If the entry is
69 compressed, this contains the compressed data.
70 uncomp_data: Original uncompressed data, if this entry is compressed,
71 else None
Simon Glass8287ee82019-07-08 14:25:30 -060072 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassc52c9e72019-07-08 14:25:37 -060073 orig_offset: Original offset value read from node
74 orig_size: Original size value read from node
Simon Glass87958982020-09-01 05:13:57 -060075 missing: True if this entry is missing its contents
76 allow_missing: Allow children of this entry to be missing (used by
77 subclasses such as Entry_section)
Heiko Thierya89c8f22022-01-06 11:49:41 +010078 allow_fake: Allow creating a dummy fake file if the blob file is not
79 available. This is mainly used for testing.
Simon Glass87958982020-09-01 05:13:57 -060080 external: True if this entry contains an external binary blob
Simon Glass386c63c2022-01-09 20:13:50 -070081 bintools: Bintools used by this entry (only populated for Image)
Simon Glass4f9ee832022-01-09 20:14:09 -070082 missing_bintools: List of missing bintools for this entry
Alper Nebi Yasakee813c82022-02-09 22:02:35 +030083 update_hash: True if this entry's "hash" subnode should be
84 updated with a hash of the entry contents
Simon Glassbf7fd502016-11-25 20:15:51 -070085 """
Simon Glassc6bd6e22019-07-20 12:23:45 -060086 def __init__(self, section, etype, node, name_prefix=''):
Simon Glass8dbb7442019-08-24 07:22:44 -060087 # Put this here to allow entry-docs and help to work without libfdt
88 global state
Simon Glass16287932020-04-17 18:09:03 -060089 from binman import state
Simon Glass8dbb7442019-08-24 07:22:44 -060090
Simon Glass25ac0e62018-06-01 09:38:14 -060091 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -070092 self.etype = etype
93 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -060094 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -060095 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -070096 self.size = None
Simon Glass9a5d3dc2019-10-31 07:43:02 -060097 self.pre_reset_size = None
Simon Glass8287ee82019-07-08 14:25:30 -060098 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -060099 self.data = None
Simon Glass97c3e9a2020-10-26 17:40:15 -0600100 self.uncomp_data = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700101 self.contents_size = 0
102 self.align = None
103 self.align_size = None
104 self.align_end = None
105 self.pad_before = 0
106 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -0600107 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -0600108 self.image_pos = None
Simon Glass80a66ae2022-03-05 20:18:59 -0700109 self.extend_size = False
Simon Glass8287ee82019-07-08 14:25:30 -0600110 self.compress = 'none'
Simon Glassb1cca952020-07-09 18:39:40 -0600111 self.missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100112 self.faked = False
Simon Glass87958982020-09-01 05:13:57 -0600113 self.external = False
114 self.allow_missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100115 self.allow_fake = False
Simon Glass386c63c2022-01-09 20:13:50 -0700116 self.bintools = {}
Simon Glass4f9ee832022-01-09 20:14:09 -0700117 self.missing_bintools = []
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300118 self.update_hash = True
Simon Glassbf7fd502016-11-25 20:15:51 -0700119
120 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700121 def FindEntryClass(etype, expanded):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600122 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -0700123
124 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -0600125 node_node: Path name of Node object containing information about
126 the entry to create (used for errors)
127 etype: Entry type to use
Simon Glassb35fb172021-03-18 20:25:04 +1300128 expanded: Use the expanded version of etype
Simon Glassbf7fd502016-11-25 20:15:51 -0700129
130 Returns:
Simon Glassb35fb172021-03-18 20:25:04 +1300131 The entry class object if found, else None if not found and expanded
Simon Glass858436d2021-11-23 21:09:49 -0700132 is True, else a tuple:
133 module name that could not be found
134 exception received
Simon Glassbf7fd502016-11-25 20:15:51 -0700135 """
Simon Glassdd57c132018-06-01 09:38:11 -0600136 # Convert something like 'u-boot@0' to 'u_boot' since we are only
137 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700138 module_name = etype.replace('-', '_')
Simon Glassb35fb172021-03-18 20:25:04 +1300139
Simon Glassdd57c132018-06-01 09:38:11 -0600140 if '@' in module_name:
141 module_name = module_name.split('@')[0]
Simon Glassb35fb172021-03-18 20:25:04 +1300142 if expanded:
143 module_name += '_expanded'
Simon Glassbf7fd502016-11-25 20:15:51 -0700144 module = modules.get(module_name)
145
Simon Glassbadf0ec2018-06-01 09:38:15 -0600146 # Also allow entry-type modules to be brought in from the etype directory.
147
Simon Glassbf7fd502016-11-25 20:15:51 -0700148 # Import the module if we have not already done so.
149 if not module:
150 try:
Simon Glass16287932020-04-17 18:09:03 -0600151 module = importlib.import_module('binman.etype.' + module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600152 except ImportError as e:
Simon Glassb35fb172021-03-18 20:25:04 +1300153 if expanded:
154 return None
Simon Glass858436d2021-11-23 21:09:49 -0700155 return module_name, e
Simon Glassbf7fd502016-11-25 20:15:51 -0700156 modules[module_name] = module
157
Simon Glassfd8d1f72018-07-17 13:25:36 -0600158 # Look up the expected class name
159 return getattr(module, 'Entry_%s' % module_name)
160
161 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700162 def Lookup(node_path, etype, expanded, missing_etype=False):
163 """Look up the entry class for a node.
164
165 Args:
166 node_node (str): Path name of Node object containing information
167 about the entry to create (used for errors)
168 etype (str): Entry type to use
169 expanded (bool): Use the expanded version of etype
170 missing_etype (bool): True to default to a blob etype if the
171 requested etype is not found
172
173 Returns:
174 The entry class object if found, else None if not found and expanded
175 is True
176
177 Raise:
178 ValueError if expanded is False and the class is not found
179 """
180 # Convert something like 'u-boot@0' to 'u_boot' since we are only
181 # interested in the type.
182 cls = Entry.FindEntryClass(etype, expanded)
183 if cls is None:
184 return None
185 elif isinstance(cls, tuple):
186 if missing_etype:
187 cls = Entry.FindEntryClass('blob', False)
188 if isinstance(cls, tuple): # This should not fail
189 module_name, e = cls
190 raise ValueError(
191 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
192 (etype, node_path, module_name, e))
193 return cls
194
195 @staticmethod
196 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600197 """Create a new entry for a node.
198
199 Args:
Simon Glass858436d2021-11-23 21:09:49 -0700200 section (entry_Section): Section object containing this node
201 node (Node): Node object containing information about the entry to
202 create
203 etype (str): Entry type to use, or None to work it out (used for
204 tests)
205 expanded (bool): Use the expanded version of etype
206 missing_etype (bool): True to default to a blob etype if the
207 requested etype is not found
Simon Glassfd8d1f72018-07-17 13:25:36 -0600208
209 Returns:
210 A new Entry object of the correct type (a subclass of Entry)
211 """
212 if not etype:
213 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glass858436d2021-11-23 21:09:49 -0700214 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glassb35fb172021-03-18 20:25:04 +1300215 if obj and expanded:
216 # Check whether to use the expanded entry
217 new_etype = etype + '-expanded'
Simon Glass3d433382021-03-21 18:24:30 +1300218 can_expand = not fdt_util.GetBool(node, 'no-expanded')
219 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glassb35fb172021-03-18 20:25:04 +1300220 etype = new_etype
221 else:
222 obj = None
223 if not obj:
Simon Glass858436d2021-11-23 21:09:49 -0700224 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600225
Simon Glassbf7fd502016-11-25 20:15:51 -0700226 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600227 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700228
229 def ReadNode(self):
230 """Read entry information from the node
231
Simon Glassc6bd6e22019-07-20 12:23:45 -0600232 This must be called as the first thing after the Entry is created.
233
Simon Glassbf7fd502016-11-25 20:15:51 -0700234 This reads all the fields we recognise from the node, ready for use.
235 """
Simon Glass15a587c2018-07-17 13:25:51 -0600236 if 'pos' in self._node.props:
237 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass80a66ae2022-03-05 20:18:59 -0700238 if 'expand-size' in self._node.props:
239 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glass3ab95982018-08-01 15:22:37 -0600240 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700241 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glass12bb1a92019-07-20 12:23:51 -0600242 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
243 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
244 if self.GetImage().copy_to_orig:
245 self.orig_offset = self.offset
246 self.orig_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600247
Simon Glassffded752019-07-08 14:25:46 -0600248 # These should not be set in input files, but are set in an FDT map,
249 # which is also read by this code.
250 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
251 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
252
Simon Glassbf7fd502016-11-25 20:15:51 -0700253 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700254 if tools.not_power_of_two(self.align):
Simon Glassbf7fd502016-11-25 20:15:51 -0700255 raise ValueError("Node '%s': Alignment %s must be a power of two" %
256 (self._node.path, self.align))
Simon Glass5ff9fed2021-03-21 18:24:33 +1300257 if self.section and self.align is None:
258 self.align = self.section.align_default
Simon Glassbf7fd502016-11-25 20:15:51 -0700259 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
260 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
261 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700262 if tools.not_power_of_two(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600263 self.Raise("Alignment size %s must be a power of two" %
264 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700265 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600266 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glass80a66ae2022-03-05 20:18:59 -0700267 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassb2381432020-09-06 10:39:09 -0600268 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glassbf7fd502016-11-25 20:15:51 -0700269
Simon Glass87c96292020-10-26 17:40:06 -0600270 # This is only supported by blobs and sections at present
271 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
272
Simon Glass6c234bf2018-09-14 04:57:18 -0600273 def GetDefaultFilename(self):
274 return None
275
Simon Glassa8adb6d2019-07-20 12:23:28 -0600276 def GetFdts(self):
277 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600278
279 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600280 Empty dict, if this entry is not a .dtb, otherwise:
281 Dict:
282 key: Filename from this entry (without the path)
Simon Glass4bdd3002019-07-20 12:23:31 -0600283 value: Tuple:
Simon Glassadb67bb2021-03-18 20:25:02 +1300284 Entry object for this dtb
Simon Glass4bdd3002019-07-20 12:23:31 -0600285 Filename of file containing this dtb
Simon Glass539aece2018-09-14 04:57:22 -0600286 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600287 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600288
Simon Glassc9ee33a2022-03-05 20:19:00 -0700289 def gen_entries(self):
290 """Allow entries to generate other entries
Simon Glassa01d1a22021-03-18 20:24:52 +1300291
292 Some entries generate subnodes automatically, from which sub-entries
293 are then created. This method allows those to be added to the binman
294 definition for the current image. An entry which implements this method
295 should call state.AddSubnode() to add a subnode and can add properties
296 with state.AddString(), etc.
297
298 An example is 'files', which produces a section containing a list of
299 files.
300 """
Simon Glass0a98b282018-09-14 04:57:28 -0600301 pass
302
Simon Glassa9fad072020-10-26 17:40:17 -0600303 def AddMissingProperties(self, have_image_pos):
304 """Add new properties to the device tree as needed for this entry
305
306 Args:
307 have_image_pos: True if this entry has an image position. This can
308 be False if its parent section is compressed, since compression
309 groups all entries together into a compressed block of data,
310 obscuring the start of each individual child entry
311 """
312 for prop in ['offset', 'size']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600313 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600314 state.AddZeroProp(self._node, prop)
Simon Glassa9fad072020-10-26 17:40:17 -0600315 if have_image_pos and 'image-pos' not in self._node.props:
316 state.AddZeroProp(self._node, 'image-pos')
Simon Glass12bb1a92019-07-20 12:23:51 -0600317 if self.GetImage().allow_repack:
318 if self.orig_offset is not None:
319 state.AddZeroProp(self._node, 'orig-offset', True)
320 if self.orig_size is not None:
321 state.AddZeroProp(self._node, 'orig-size', True)
322
Simon Glass8287ee82019-07-08 14:25:30 -0600323 if self.compress != 'none':
324 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300325
326 if self.update_hash:
327 err = state.CheckAddHashProp(self._node)
328 if err:
329 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600330
331 def SetCalculatedProperties(self):
332 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600333 state.SetInt(self._node, 'offset', self.offset)
334 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600335 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassa9fad072020-10-26 17:40:17 -0600336 if self.image_pos is not None:
Simon Glass08594d42020-11-02 12:55:44 -0700337 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass12bb1a92019-07-20 12:23:51 -0600338 if self.GetImage().allow_repack:
339 if self.orig_offset is not None:
340 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
341 if self.orig_size is not None:
342 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glass8287ee82019-07-08 14:25:30 -0600343 if self.uncomp_size is not None:
344 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300345
346 if self.update_hash:
347 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600348
Simon Glassecab8972018-07-06 10:27:40 -0600349 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600350 """Allow entries to adjust the device tree
351
352 Some entries need to adjust the device tree for their purposes. This
353 may involve adding or deleting properties.
354
355 Returns:
356 True if processing is complete
357 False if processing could not be completed due to a dependency.
358 This will cause the entry to be retried after others have been
359 called
360 """
Simon Glassecab8972018-07-06 10:27:40 -0600361 return True
362
Simon Glassc8d48ef2018-06-01 09:38:21 -0600363 def SetPrefix(self, prefix):
364 """Set the name prefix for a node
365
366 Args:
367 prefix: Prefix to set, or '' to not use a prefix
368 """
369 if prefix:
370 self.name = prefix + self.name
371
Simon Glass5c890232018-07-06 10:27:19 -0600372 def SetContents(self, data):
373 """Set the contents of an entry
374
375 This sets both the data and content_size properties
376
377 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600378 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600379 """
380 self.data = data
381 self.contents_size = len(self.data)
382
383 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600384 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600385
Simon Glassa0dcaf22019-07-08 14:25:35 -0600386 This checks that the new data is the same size as the old. If the size
387 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600388
389 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600390 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600391
392 Raises:
393 ValueError if the new data size is not the same as the old
394 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600395 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600396 new_size = len(data)
Simon Glass61ec04f2019-07-20 12:23:58 -0600397 if state.AllowEntryExpansion() and new_size > self.contents_size:
398 # self.data will indicate the new size needed
399 size_ok = False
400 elif state.AllowEntryContraction() and new_size < self.contents_size:
401 size_ok = False
402
403 # If not allowed to change, try to deal with it or give up
404 if size_ok:
Simon Glassc52c9e72019-07-08 14:25:37 -0600405 if new_size > self.contents_size:
Simon Glass61ec04f2019-07-20 12:23:58 -0600406 self.Raise('Cannot update entry size from %d to %d' %
407 (self.contents_size, new_size))
408
409 # Don't let the data shrink. Pad it if necessary
410 if size_ok and new_size < self.contents_size:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700411 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass61ec04f2019-07-20 12:23:58 -0600412
413 if not size_ok:
Simon Glassf3385a52022-01-29 14:14:15 -0700414 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glassc1aa66e2022-01-29 14:14:04 -0700415 self._node.path, to_hex(self.contents_size),
416 to_hex(new_size)))
Simon Glass5c890232018-07-06 10:27:19 -0600417 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600418 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600419
Simon Glass72e423c2022-03-05 20:19:05 -0700420 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glassbf7fd502016-11-25 20:15:51 -0700421 """Figure out the contents of an entry.
422
Simon Glass72e423c2022-03-05 20:19:05 -0700423 Args:
424 skip_entry (Entry): Entry to skip when obtaining section contents
425 fake_size (int): Size of fake file to create if needed
426
Simon Glassbf7fd502016-11-25 20:15:51 -0700427 Returns:
428 True if the contents were found, False if another call is needed
429 after the other entries are processed.
430 """
431 # No contents by default: subclasses can implement this
432 return True
433
Simon Glassc52c9e72019-07-08 14:25:37 -0600434 def ResetForPack(self):
435 """Reset offset/size fields so that packing can be done again"""
Simon Glass9f297b02019-07-20 12:23:36 -0600436 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700437 (to_hex(self.offset), to_hex(self.orig_offset),
438 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600439 self.pre_reset_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600440 self.offset = self.orig_offset
441 self.size = self.orig_size
442
Simon Glass3ab95982018-08-01 15:22:37 -0600443 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600444 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700445
446 Most of the time the entries are not fully specified. There may be
447 an alignment but no size. In that case we take the size from the
448 contents of the entry.
449
Simon Glass3ab95982018-08-01 15:22:37 -0600450 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700451
Simon Glass3ab95982018-08-01 15:22:37 -0600452 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700453 entry will be know.
454
455 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600456 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700457
458 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600459 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700460 """
Simon Glass9f297b02019-07-20 12:23:36 -0600461 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700462 (to_hex(self.offset), to_hex(self.size),
Simon Glass9f297b02019-07-20 12:23:36 -0600463 self.contents_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600464 if self.offset is None:
465 if self.offset_unset:
466 self.Raise('No offset set with offset-unset: should another '
467 'entry provide this correct offset?')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700468 self.offset = tools.align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700469 needed = self.pad_before + self.contents_size + self.pad_after
Simon Glassc1aa66e2022-01-29 14:14:04 -0700470 needed = tools.align(needed, self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700471 size = self.size
472 if not size:
473 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600474 new_offset = self.offset + size
Simon Glassc1aa66e2022-01-29 14:14:04 -0700475 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glass3ab95982018-08-01 15:22:37 -0600476 if aligned_offset != new_offset:
477 size = aligned_offset - self.offset
478 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700479
480 if not self.size:
481 self.size = size
482
483 if self.size < needed:
484 self.Raise("Entry contents size is %#x (%d) but entry size is "
485 "%#x (%d)" % (needed, needed, self.size, self.size))
486 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600487 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700488 # conflict with the provided alignment values
Simon Glassc1aa66e2022-01-29 14:14:04 -0700489 if self.size != tools.align(self.size, self.align_size):
Simon Glassbf7fd502016-11-25 20:15:51 -0700490 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
491 (self.size, self.size, self.align_size, self.align_size))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700492 if self.offset != tools.align(self.offset, self.align):
Simon Glass3ab95982018-08-01 15:22:37 -0600493 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
494 (self.offset, self.offset, self.align, self.align))
Simon Glass9f297b02019-07-20 12:23:36 -0600495 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
496 (self.offset, self.size, self.contents_size, new_offset))
Simon Glassbf7fd502016-11-25 20:15:51 -0700497
Simon Glass3ab95982018-08-01 15:22:37 -0600498 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700499
500 def Raise(self, msg):
501 """Convenience function to raise an error referencing a node"""
502 raise ValueError("Node '%s': %s" % (self._node.path, msg))
503
Simon Glass189f2912021-03-21 18:24:31 +1300504 def Info(self, msg):
505 """Convenience function to log info referencing a node"""
506 tag = "Info '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700507 tout.detail('%30s: %s' % (tag, msg))
Simon Glass189f2912021-03-21 18:24:31 +1300508
Simon Glass9f297b02019-07-20 12:23:36 -0600509 def Detail(self, msg):
510 """Convenience function to log detail referencing a node"""
511 tag = "Node '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700512 tout.detail('%30s: %s' % (tag, msg))
Simon Glass9f297b02019-07-20 12:23:36 -0600513
Simon Glass53af22a2018-07-17 13:25:32 -0600514 def GetEntryArgsOrProps(self, props, required=False):
515 """Return the values of a set of properties
516
517 Args:
518 props: List of EntryArg objects
519
520 Raises:
521 ValueError if a property is not found
522 """
523 values = []
524 missing = []
525 for prop in props:
526 python_prop = prop.name.replace('-', '_')
527 if hasattr(self, python_prop):
528 value = getattr(self, python_prop)
529 else:
530 value = None
531 if value is None:
532 value = self.GetArg(prop.name, prop.datatype)
533 if value is None and required:
534 missing.append(prop.name)
535 values.append(value)
536 if missing:
Simon Glass939d1062021-01-06 21:35:16 -0700537 self.GetImage().MissingArgs(self, missing)
Simon Glass53af22a2018-07-17 13:25:32 -0600538 return values
539
Simon Glassbf7fd502016-11-25 20:15:51 -0700540 def GetPath(self):
541 """Get the path of a node
542
543 Returns:
544 Full path of the node for this entry
545 """
546 return self._node.path
547
Simon Glass631f7522021-03-21 18:24:32 +1300548 def GetData(self, required=True):
Simon Glass63e7ba62020-10-26 17:40:16 -0600549 """Get the contents of an entry
550
Simon Glass631f7522021-03-21 18:24:32 +1300551 Args:
552 required: True if the data must be present, False if it is OK to
553 return None
554
Simon Glass63e7ba62020-10-26 17:40:16 -0600555 Returns:
556 bytes content of the entry, excluding any padding. If the entry is
557 compressed, the compressed data is returned
558 """
Simon Glassc1aa66e2022-01-29 14:14:04 -0700559 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glassbf7fd502016-11-25 20:15:51 -0700560 return self.data
561
Simon Glass271a0832020-11-02 12:55:43 -0700562 def GetPaddedData(self, data=None):
563 """Get the data for an entry including any padding
564
565 Gets the entry data and uses its section's pad-byte value to add padding
566 before and after as defined by the pad-before and pad-after properties.
567
568 This does not consider alignment.
569
570 Returns:
571 Contents of the entry along with any pad bytes before and
572 after it (bytes)
573 """
574 if data is None:
575 data = self.GetData()
576 return self.section.GetPaddedDataForEntry(self, data)
577
Simon Glass3ab95982018-08-01 15:22:37 -0600578 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600579 """Get the offsets for siblings
580
581 Some entry types can contain information about the position or size of
582 other entries. An example of this is the Intel Flash Descriptor, which
583 knows where the Intel Management Engine section should go.
584
585 If this entry knows about the position of other entries, it can specify
586 this by returning values here
587
588 Returns:
589 Dict:
590 key: Entry type
591 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600592 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600593 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700594 return {}
595
Simon Glasscf549042019-07-08 13:18:39 -0600596 def SetOffsetSize(self, offset, size):
597 """Set the offset and/or size of an entry
598
599 Args:
600 offset: New offset, or None to leave alone
601 size: New size, or None to leave alone
602 """
603 if offset is not None:
604 self.offset = offset
605 if size is not None:
606 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700607
Simon Glassdbf6be92018-08-01 15:22:42 -0600608 def SetImagePos(self, image_pos):
609 """Set the position in the image
610
611 Args:
612 image_pos: Position of this entry in the image
613 """
614 self.image_pos = image_pos + self.offset
615
Simon Glassbf7fd502016-11-25 20:15:51 -0700616 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600617 """Do any post-packing updates of entry contents
618
619 This function should call ProcessContentsUpdate() to update the entry
620 contents, if necessary, returning its return value here.
621
622 Args:
623 data: Data to set to the contents (bytes)
624
625 Returns:
626 True if the new data size is OK, False if expansion is needed
627
628 Raises:
629 ValueError if the new data size is not the same as the old and
630 state.AllowEntryExpansion() is False
631 """
632 return True
Simon Glass19790632017-11-13 18:55:01 -0700633
Simon Glassf55382b2018-06-01 09:38:13 -0600634 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700635 """Write symbol values into binary files for access at run time
636
637 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600638 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700639 """
640 pass
Simon Glass18546952018-06-01 09:38:16 -0600641
Simon Glass6ddd6112020-10-26 17:40:18 -0600642 def CheckEntries(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600643 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600644
Simon Glass3ab95982018-08-01 15:22:37 -0600645 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600646 than having to be fully inside their section). Sub-classes can implement
647 this function and raise if there is a problem.
648 """
649 pass
Simon Glass3b0c38212018-06-01 09:38:20 -0600650
Simon Glass8122f392018-07-17 13:25:28 -0600651 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600652 def GetStr(value):
653 if value is None:
654 return '<none> '
655 return '%08x' % value
656
657 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600658 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600659 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
660 Entry.GetStr(offset), Entry.GetStr(size),
661 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600662
Simon Glass3b0c38212018-06-01 09:38:20 -0600663 def WriteMap(self, fd, indent):
664 """Write a map of the entry to a .map file
665
666 Args:
667 fd: File to write the map to
668 indent: Curent indent level of map (0=none, 1=one level, etc.)
669 """
Simon Glass1be70d22018-07-17 13:25:49 -0600670 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
671 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600672
Simon Glass11e36cc2018-07-17 13:25:38 -0600673 def GetEntries(self):
674 """Return a list of entries contained by this entry
675
676 Returns:
677 List of entries, or None if none. A normal entry has no entries
678 within it so will return None
679 """
680 return None
681
Simon Glass53af22a2018-07-17 13:25:32 -0600682 def GetArg(self, name, datatype=str):
683 """Get the value of an entry argument or device-tree-node property
684
685 Some node properties can be provided as arguments to binman. First check
686 the entry arguments, and fall back to the device tree if not found
687
688 Args:
689 name: Argument name
690 datatype: Data type (str or int)
691
692 Returns:
693 Value of argument as a string or int, or None if no value
694
695 Raises:
696 ValueError if the argument cannot be converted to in
697 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600698 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600699 if value is not None:
700 if datatype == int:
701 try:
702 value = int(value)
703 except ValueError:
704 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
705 (name, value))
706 elif datatype == str:
707 pass
708 else:
709 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
710 datatype)
711 else:
712 value = fdt_util.GetDatatype(self._node, name, datatype)
713 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600714
715 @staticmethod
716 def WriteDocs(modules, test_missing=None):
717 """Write out documentation about the various entry types to stdout
718
719 Args:
720 modules: List of modules to include
721 test_missing: Used for testing. This is a module to report
722 as missing
723 """
724 print('''Binman Entry Documentation
725===========================
726
727This file describes the entry types supported by binman. These entry types can
728be placed in an image one by one to build up a final firmware image. It is
729fairly easy to create new entry types. Just add a new file to the 'etype'
730directory. You can use the existing entries as examples.
731
732Note that some entries are subclasses of others, using and extending their
733features to produce new behaviours.
734
735
736''')
737 modules = sorted(modules)
738
739 # Don't show the test entry
740 if '_testing' in modules:
741 modules.remove('_testing')
742 missing = []
743 for name in modules:
Simon Glassb35fb172021-03-18 20:25:04 +1300744 module = Entry.Lookup('WriteDocs', name, False)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600745 docs = getattr(module, '__doc__')
746 if test_missing == name:
747 docs = None
748 if docs:
749 lines = docs.splitlines()
750 first_line = lines[0]
751 rest = [line[4:] for line in lines[1:]]
752 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glass228c9b82022-08-07 16:33:25 -0600753
754 # Create a reference for use by rST docs
755 ref_name = f'etype_{module.__name__[6:]}'.lower()
756 print('.. _%s:' % ref_name)
757 print()
Simon Glassfd8d1f72018-07-17 13:25:36 -0600758 print(hdr)
759 print('-' * len(hdr))
760 print('\n'.join(rest))
761 print()
762 print()
763 else:
764 missing.append(name)
765
766 if missing:
767 raise ValueError('Documentation is missing for modules: %s' %
768 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600769
770 def GetUniqueName(self):
771 """Get a unique name for a node
772
773 Returns:
774 String containing a unique name for a node, consisting of the name
775 of all ancestors (starting from within the 'binman' node) separated
776 by a dot ('.'). This can be useful for generating unique filesnames
777 in the output directory.
778 """
779 name = self.name
780 node = self._node
781 while node.parent:
782 node = node.parent
Alper Nebi Yasak67bf2c82022-03-27 18:31:44 +0300783 if node.name in ('binman', '/'):
Simon Glassa326b492018-09-14 04:57:11 -0600784 break
785 name = '%s.%s' % (node.name, name)
786 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600787
Simon Glass80a66ae2022-03-05 20:18:59 -0700788 def extend_to_limit(self, limit):
789 """Extend an entry so that it ends at the given offset limit"""
Simon Glassba64a0b2018-09-14 04:57:29 -0600790 if self.offset + self.size < limit:
791 self.size = limit - self.offset
792 # Request the contents again, since changing the size requires that
793 # the data grows. This should not fail, but check it to be sure.
794 if not self.ObtainContents():
795 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600796
797 def HasSibling(self, name):
798 """Check if there is a sibling of a given name
799
800 Returns:
801 True if there is an entry with this name in the the same section,
802 else False
803 """
804 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600805
806 def GetSiblingImagePos(self, name):
807 """Return the image position of the given sibling
808
809 Returns:
810 Image position of sibling, or None if the sibling has no position,
811 or False if there is no such sibling
812 """
813 if not self.HasSibling(name):
814 return False
815 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600816
817 @staticmethod
818 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
819 uncomp_size, offset, entry):
820 """Add a new entry to the entries list
821
822 Args:
823 entries: List (of EntryInfo objects) to add to
824 indent: Current indent level to add to list
825 name: Entry name (string)
826 etype: Entry type (string)
827 size: Entry size in bytes (int)
828 image_pos: Position within image in bytes (int)
829 uncomp_size: Uncompressed size if the entry uses compression, else
830 None
831 offset: Entry offset within parent in bytes (int)
832 entry: Entry object
833 """
834 entries.append(EntryInfo(indent, name, etype, size, image_pos,
835 uncomp_size, offset, entry))
836
837 def ListEntries(self, entries, indent):
838 """Add files in this entry to the list of entries
839
840 This can be overridden by subclasses which need different behaviour.
841
842 Args:
843 entries: List (of EntryInfo objects) to add to
844 indent: Current indent level to add to list
845 """
846 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
847 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600848
Simon Glass943bf782021-11-23 21:09:50 -0700849 def ReadData(self, decomp=True, alt_format=None):
Simon Glassf667e452019-07-08 14:25:50 -0600850 """Read the data for an entry from the image
851
852 This is used when the image has been read in and we want to extract the
853 data for a particular entry from that image.
854
855 Args:
856 decomp: True to decompress any compressed data before returning it;
857 False to return the raw, uncompressed data
858
859 Returns:
860 Entry data (bytes)
861 """
862 # Use True here so that we get an uncompressed section to work from,
863 # although compressed sections are currently not supported
Simon Glassf3385a52022-01-29 14:14:15 -0700864 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass2d553c02019-09-25 08:56:21 -0600865 (self.section.GetPath(), self.GetPath()))
Simon Glass943bf782021-11-23 21:09:50 -0700866 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glassa9cd39e2019-07-20 12:24:04 -0600867 return data
Simon Glassd5079332019-07-20 12:23:41 -0600868
Simon Glass943bf782021-11-23 21:09:50 -0700869 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass2d553c02019-09-25 08:56:21 -0600870 """Read the data for a particular child entry
Simon Glass4e185e82019-09-25 08:56:20 -0600871
872 This reads data from the parent and extracts the piece that relates to
873 the given child.
874
875 Args:
Simon Glass943bf782021-11-23 21:09:50 -0700876 child (Entry): Child entry to read data for (must be valid)
877 decomp (bool): True to decompress any compressed data before
878 returning it; False to return the raw, uncompressed data
879 alt_format (str): Alternative format to read in, or None
Simon Glass4e185e82019-09-25 08:56:20 -0600880
881 Returns:
882 Data for the child (bytes)
883 """
884 pass
885
Simon Glassd5079332019-07-20 12:23:41 -0600886 def LoadData(self, decomp=True):
887 data = self.ReadData(decomp)
Simon Glass10f9d002019-07-20 12:23:50 -0600888 self.contents_size = len(data)
Simon Glassd5079332019-07-20 12:23:41 -0600889 self.ProcessContentsUpdate(data)
890 self.Detail('Loaded data size %x' % len(data))
Simon Glassc5ad04b2019-07-20 12:23:46 -0600891
Simon Glass943bf782021-11-23 21:09:50 -0700892 def GetAltFormat(self, data, alt_format):
893 """Read the data for an extry in an alternative format
894
895 Supported formats are list in the documentation for each entry. An
896 example is fdtmap which provides .
897
898 Args:
899 data (bytes): Data to convert (this should have been produced by the
900 entry)
901 alt_format (str): Format to use
902
903 """
904 pass
905
Simon Glassc5ad04b2019-07-20 12:23:46 -0600906 def GetImage(self):
907 """Get the image containing this entry
908
909 Returns:
910 Image object containing this entry
911 """
912 return self.section.GetImage()
Simon Glass10f9d002019-07-20 12:23:50 -0600913
914 def WriteData(self, data, decomp=True):
915 """Write the data to an entry in the image
916
917 This is used when the image has been read in and we want to replace the
918 data for a particular entry in that image.
919
920 The image must be re-packed and written out afterwards.
921
922 Args:
923 data: Data to replace it with
924 decomp: True to compress the data if needed, False if data is
925 already compressed so should be used as is
926
927 Returns:
928 True if the data did not result in a resize of this entry, False if
929 the entry must be resized
930 """
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600931 if self.size is not None:
932 self.contents_size = self.size
933 else:
934 self.contents_size = self.pre_reset_size
Simon Glass10f9d002019-07-20 12:23:50 -0600935 ok = self.ProcessContentsUpdate(data)
936 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glass7210c892019-07-20 12:24:05 -0600937 section_ok = self.section.WriteChildData(self)
938 return ok and section_ok
939
940 def WriteChildData(self, child):
941 """Handle writing the data in a child entry
942
943 This should be called on the child's parent section after the child's
Simon Glass557693e2021-11-23 11:03:44 -0700944 data has been updated. It should update any data structures needed to
945 validate that the update is successful.
Simon Glass7210c892019-07-20 12:24:05 -0600946
947 This base-class implementation does nothing, since the base Entry object
948 does not have any children.
949
950 Args:
951 child: Child Entry that was written
952
953 Returns:
954 True if the section could be updated successfully, False if the
Simon Glass557693e2021-11-23 11:03:44 -0700955 data is such that the section could not update
Simon Glass7210c892019-07-20 12:24:05 -0600956 """
957 return True
Simon Glasseba1f0c2019-07-20 12:23:55 -0600958
959 def GetSiblingOrder(self):
960 """Get the relative order of an entry amoung its siblings
961
962 Returns:
963 'start' if this entry is first among siblings, 'end' if last,
964 otherwise None
965 """
966 entries = list(self.section.GetEntries().values())
967 if entries:
968 if self == entries[0]:
969 return 'start'
970 elif self == entries[-1]:
971 return 'end'
972 return 'middle'
Simon Glass4f9f1052020-07-09 18:39:38 -0600973
974 def SetAllowMissing(self, allow_missing):
975 """Set whether a section allows missing external blobs
976
977 Args:
978 allow_missing: True if allowed, False if not allowed
979 """
980 # This is meaningless for anything other than sections
981 pass
Simon Glassb1cca952020-07-09 18:39:40 -0600982
Heiko Thierya89c8f22022-01-06 11:49:41 +0100983 def SetAllowFakeBlob(self, allow_fake):
984 """Set whether a section allows to create a fake blob
985
986 Args:
987 allow_fake: True if allowed, False if not allowed
988 """
Simon Glassf4590e02022-01-09 20:13:46 -0700989 self.allow_fake = allow_fake
Heiko Thierya89c8f22022-01-06 11:49:41 +0100990
Simon Glassb1cca952020-07-09 18:39:40 -0600991 def CheckMissing(self, missing_list):
992 """Check if any entries in this section have missing external blobs
993
994 If there are missing blobs, the entries are added to the list
995
996 Args:
997 missing_list: List of Entry objects to be added to
998 """
999 if self.missing:
1000 missing_list.append(self)
Simon Glass87958982020-09-01 05:13:57 -06001001
Simon Glass3817ad42022-03-05 20:19:04 -07001002 def check_fake_fname(self, fname, size=0):
Simon Glass790ba9f2022-01-12 13:10:36 -07001003 """If the file is missing and the entry allows fake blobs, fake it
1004
1005 Sets self.faked to True if faked
1006
1007 Args:
1008 fname (str): Filename to check
Simon Glass3817ad42022-03-05 20:19:04 -07001009 size (int): Size of fake file to create
Simon Glass790ba9f2022-01-12 13:10:36 -07001010
1011 Returns:
Simon Glass9a0a2e92022-03-05 20:19:03 -07001012 tuple:
1013 fname (str): Filename of faked file
1014 bool: True if the blob was faked, False if not
Simon Glass790ba9f2022-01-12 13:10:36 -07001015 """
1016 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glassc1aa66e2022-01-29 14:14:04 -07001017 outfname = tools.get_output_filename(os.path.basename(fname))
Simon Glass790ba9f2022-01-12 13:10:36 -07001018 with open(outfname, "wb") as out:
Simon Glass3817ad42022-03-05 20:19:04 -07001019 out.truncate(size)
Simon Glass790ba9f2022-01-12 13:10:36 -07001020 self.faked = True
Simon Glass9a0a2e92022-03-05 20:19:03 -07001021 tout.info(f"Entry '{self._node.path}': Faked file '{outfname}'")
1022 return outfname, True
1023 return fname, False
Simon Glass790ba9f2022-01-12 13:10:36 -07001024
Heiko Thierya89c8f22022-01-06 11:49:41 +01001025 def CheckFakedBlobs(self, faked_blobs_list):
1026 """Check if any entries in this section have faked external blobs
1027
1028 If there are faked blobs, the entries are added to the list
1029
1030 Args:
1031 fake_blobs_list: List of Entry objects to be added to
1032 """
1033 # This is meaningless for anything other than blobs
1034 pass
1035
Simon Glass87958982020-09-01 05:13:57 -06001036 def GetAllowMissing(self):
1037 """Get whether a section allows missing external blobs
1038
1039 Returns:
1040 True if allowed, False if not allowed
1041 """
1042 return self.allow_missing
Simon Glassb2381432020-09-06 10:39:09 -06001043
Simon Glass4f9ee832022-01-09 20:14:09 -07001044 def record_missing_bintool(self, bintool):
1045 """Record a missing bintool that was needed to produce this entry
1046
1047 Args:
1048 bintool (Bintool): Bintool that was missing
1049 """
1050 self.missing_bintools.append(bintool)
1051
1052 def check_missing_bintools(self, missing_list):
1053 """Check if any entries in this section have missing bintools
1054
1055 If there are missing bintools, these are added to the list
1056
1057 Args:
1058 missing_list: List of Bintool objects to be added to
1059 """
1060 missing_list += self.missing_bintools
1061
Simon Glassb2381432020-09-06 10:39:09 -06001062 def GetHelpTags(self):
1063 """Get the tags use for missing-blob help
1064
1065 Returns:
1066 list of possible tags, most desirable first
1067 """
1068 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glass87c96292020-10-26 17:40:06 -06001069
1070 def CompressData(self, indata):
1071 """Compress data according to the entry's compression method
1072
1073 Args:
1074 indata: Data to compress
1075
1076 Returns:
1077 Compressed data (first word is the compressed size)
1078 """
Simon Glass97c3e9a2020-10-26 17:40:15 -06001079 self.uncomp_data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001080 if self.compress != 'none':
1081 self.uncomp_size = len(indata)
Simon Glass0d1e95a2022-01-09 20:14:04 -07001082 data = comp_util.compress(indata, self.compress)
Simon Glass87c96292020-10-26 17:40:06 -06001083 return data
Simon Glassb35fb172021-03-18 20:25:04 +13001084
1085 @classmethod
1086 def UseExpanded(cls, node, etype, new_etype):
1087 """Check whether to use an expanded entry type
1088
1089 This is called by Entry.Create() when it finds an expanded version of
1090 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1091 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1092 ignored.
1093
1094 Args:
1095 node: Node object containing information about the entry to
1096 create
1097 etype: Original entry type being used
1098 new_etype: New entry type proposed
1099
1100 Returns:
1101 True to use this entry type, False to use the original one
1102 """
Simon Glassf3385a52022-01-29 14:14:15 -07001103 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glassb35fb172021-03-18 20:25:04 +13001104 (node.path, etype, new_etype))
1105 return True
Simon Glass943bf782021-11-23 21:09:50 -07001106
1107 def CheckAltFormats(self, alt_formats):
1108 """Add any alternative formats supported by this entry type
1109
1110 Args:
1111 alt_formats (dict): Dict to add alt_formats to:
1112 key: Name of alt format
1113 value: Help text
1114 """
1115 pass
Simon Glass386c63c2022-01-09 20:13:50 -07001116
Simon Glassae9a4572022-03-05 20:19:02 -07001117 def AddBintools(self, btools):
Simon Glass386c63c2022-01-09 20:13:50 -07001118 """Add the bintools used by this entry type
1119
1120 Args:
Simon Glassae9a4572022-03-05 20:19:02 -07001121 btools (dict of Bintool):
Simon Glass386c63c2022-01-09 20:13:50 -07001122 """
1123 pass
1124
1125 @classmethod
1126 def AddBintool(self, tools, name):
1127 """Add a new bintool to the tools used by this etype
1128
1129 Args:
1130 name: Name of the tool
1131 """
1132 btool = bintool.Bintool.create(name)
1133 tools[name] = btool
1134 return btool
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03001135
1136 def SetUpdateHash(self, update_hash):
1137 """Set whether this entry's "hash" subnode should be updated
1138
1139 Args:
1140 update_hash: True if hash should be updated, False if not
1141 """
1142 self.update_hash = update_hash
Simon Glass81b71c32022-02-08 11:50:00 -07001143
Simon Glass72e423c2022-03-05 20:19:05 -07001144 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass81b71c32022-02-08 11:50:00 -07001145 """Put the contents of a list of entries into a file
1146
1147 Args:
1148 entries (list of Entry): Entries to collect
1149 prefix (str): Filename prefix of file to write to
Simon Glass72e423c2022-03-05 20:19:05 -07001150 fake_size (int): Size of fake file to create if needed
Simon Glass81b71c32022-02-08 11:50:00 -07001151
1152 If any entry does not have contents yet, this function returns False
1153 for the data.
1154
1155 Returns:
1156 Tuple:
Simon Glass6d427c42022-03-05 20:18:58 -07001157 bytes: Concatenated data from all the entries (or None)
1158 str: Filename of file written (or None if no data)
1159 str: Unique portion of filename (or None if no data)
Simon Glass81b71c32022-02-08 11:50:00 -07001160 """
1161 data = b''
1162 for entry in entries:
1163 # First get the input data and put it in a file. If not available,
1164 # try later.
Simon Glass72e423c2022-03-05 20:19:05 -07001165 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass6d427c42022-03-05 20:18:58 -07001166 return None, None, None
Simon Glass81b71c32022-02-08 11:50:00 -07001167 data += entry.GetData()
1168 uniq = self.GetUniqueName()
1169 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1170 tools.write_file(fname, data)
1171 return data, fname, uniq