blob: 85dc339726b3d167dc0d5d6abea2b275487101ce [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 Glassc4738312021-11-23 11:03:43 -0700109 self.expand_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 Glass3ab95982018-08-01 15:22:37 -0600238 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700239 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glass12bb1a92019-07-20 12:23:51 -0600240 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
241 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
242 if self.GetImage().copy_to_orig:
243 self.orig_offset = self.offset
244 self.orig_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600245
Simon Glassffded752019-07-08 14:25:46 -0600246 # These should not be set in input files, but are set in an FDT map,
247 # which is also read by this code.
248 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
249 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
250
Simon Glassbf7fd502016-11-25 20:15:51 -0700251 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700252 if tools.not_power_of_two(self.align):
Simon Glassbf7fd502016-11-25 20:15:51 -0700253 raise ValueError("Node '%s': Alignment %s must be a power of two" %
254 (self._node.path, self.align))
Simon Glass5ff9fed2021-03-21 18:24:33 +1300255 if self.section and self.align is None:
256 self.align = self.section.align_default
Simon Glassbf7fd502016-11-25 20:15:51 -0700257 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
258 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
259 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700260 if tools.not_power_of_two(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600261 self.Raise("Alignment size %s must be a power of two" %
262 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700263 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600264 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassba64a0b2018-09-14 04:57:29 -0600265 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
Simon Glassb2381432020-09-06 10:39:09 -0600266 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glassbf7fd502016-11-25 20:15:51 -0700267
Simon Glass87c96292020-10-26 17:40:06 -0600268 # This is only supported by blobs and sections at present
269 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
270
Simon Glass6c234bf2018-09-14 04:57:18 -0600271 def GetDefaultFilename(self):
272 return None
273
Simon Glassa8adb6d2019-07-20 12:23:28 -0600274 def GetFdts(self):
275 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600276
277 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600278 Empty dict, if this entry is not a .dtb, otherwise:
279 Dict:
280 key: Filename from this entry (without the path)
Simon Glass4bdd3002019-07-20 12:23:31 -0600281 value: Tuple:
Simon Glassadb67bb2021-03-18 20:25:02 +1300282 Entry object for this dtb
Simon Glass4bdd3002019-07-20 12:23:31 -0600283 Filename of file containing this dtb
Simon Glass539aece2018-09-14 04:57:22 -0600284 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600285 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600286
Simon Glass0a98b282018-09-14 04:57:28 -0600287 def ExpandEntries(self):
Simon Glassa01d1a22021-03-18 20:24:52 +1300288 """Expand out entries which produce other entries
289
290 Some entries generate subnodes automatically, from which sub-entries
291 are then created. This method allows those to be added to the binman
292 definition for the current image. An entry which implements this method
293 should call state.AddSubnode() to add a subnode and can add properties
294 with state.AddString(), etc.
295
296 An example is 'files', which produces a section containing a list of
297 files.
298 """
Simon Glass0a98b282018-09-14 04:57:28 -0600299 pass
300
Simon Glassa9fad072020-10-26 17:40:17 -0600301 def AddMissingProperties(self, have_image_pos):
302 """Add new properties to the device tree as needed for this entry
303
304 Args:
305 have_image_pos: True if this entry has an image position. This can
306 be False if its parent section is compressed, since compression
307 groups all entries together into a compressed block of data,
308 obscuring the start of each individual child entry
309 """
310 for prop in ['offset', 'size']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600311 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600312 state.AddZeroProp(self._node, prop)
Simon Glassa9fad072020-10-26 17:40:17 -0600313 if have_image_pos and 'image-pos' not in self._node.props:
314 state.AddZeroProp(self._node, 'image-pos')
Simon Glass12bb1a92019-07-20 12:23:51 -0600315 if self.GetImage().allow_repack:
316 if self.orig_offset is not None:
317 state.AddZeroProp(self._node, 'orig-offset', True)
318 if self.orig_size is not None:
319 state.AddZeroProp(self._node, 'orig-size', True)
320
Simon Glass8287ee82019-07-08 14:25:30 -0600321 if self.compress != 'none':
322 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300323
324 if self.update_hash:
325 err = state.CheckAddHashProp(self._node)
326 if err:
327 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600328
329 def SetCalculatedProperties(self):
330 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600331 state.SetInt(self._node, 'offset', self.offset)
332 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600333 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassa9fad072020-10-26 17:40:17 -0600334 if self.image_pos is not None:
Simon Glass08594d42020-11-02 12:55:44 -0700335 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass12bb1a92019-07-20 12:23:51 -0600336 if self.GetImage().allow_repack:
337 if self.orig_offset is not None:
338 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
339 if self.orig_size is not None:
340 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glass8287ee82019-07-08 14:25:30 -0600341 if self.uncomp_size is not None:
342 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300343
344 if self.update_hash:
345 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600346
Simon Glassecab8972018-07-06 10:27:40 -0600347 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600348 """Allow entries to adjust the device tree
349
350 Some entries need to adjust the device tree for their purposes. This
351 may involve adding or deleting properties.
352
353 Returns:
354 True if processing is complete
355 False if processing could not be completed due to a dependency.
356 This will cause the entry to be retried after others have been
357 called
358 """
Simon Glassecab8972018-07-06 10:27:40 -0600359 return True
360
Simon Glassc8d48ef2018-06-01 09:38:21 -0600361 def SetPrefix(self, prefix):
362 """Set the name prefix for a node
363
364 Args:
365 prefix: Prefix to set, or '' to not use a prefix
366 """
367 if prefix:
368 self.name = prefix + self.name
369
Simon Glass5c890232018-07-06 10:27:19 -0600370 def SetContents(self, data):
371 """Set the contents of an entry
372
373 This sets both the data and content_size properties
374
375 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600376 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600377 """
378 self.data = data
379 self.contents_size = len(self.data)
380
381 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600382 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600383
Simon Glassa0dcaf22019-07-08 14:25:35 -0600384 This checks that the new data is the same size as the old. If the size
385 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600386
387 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600388 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600389
390 Raises:
391 ValueError if the new data size is not the same as the old
392 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600393 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600394 new_size = len(data)
Simon Glass61ec04f2019-07-20 12:23:58 -0600395 if state.AllowEntryExpansion() and new_size > self.contents_size:
396 # self.data will indicate the new size needed
397 size_ok = False
398 elif state.AllowEntryContraction() and new_size < self.contents_size:
399 size_ok = False
400
401 # If not allowed to change, try to deal with it or give up
402 if size_ok:
Simon Glassc52c9e72019-07-08 14:25:37 -0600403 if new_size > self.contents_size:
Simon Glass61ec04f2019-07-20 12:23:58 -0600404 self.Raise('Cannot update entry size from %d to %d' %
405 (self.contents_size, new_size))
406
407 # Don't let the data shrink. Pad it if necessary
408 if size_ok and new_size < self.contents_size:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700409 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass61ec04f2019-07-20 12:23:58 -0600410
411 if not size_ok:
Simon Glassf3385a52022-01-29 14:14:15 -0700412 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glassc1aa66e2022-01-29 14:14:04 -0700413 self._node.path, to_hex(self.contents_size),
414 to_hex(new_size)))
Simon Glass5c890232018-07-06 10:27:19 -0600415 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600416 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600417
Simon Glassbf7fd502016-11-25 20:15:51 -0700418 def ObtainContents(self):
419 """Figure out the contents of an entry.
420
421 Returns:
422 True if the contents were found, False if another call is needed
423 after the other entries are processed.
424 """
425 # No contents by default: subclasses can implement this
426 return True
427
Simon Glassc52c9e72019-07-08 14:25:37 -0600428 def ResetForPack(self):
429 """Reset offset/size fields so that packing can be done again"""
Simon Glass9f297b02019-07-20 12:23:36 -0600430 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700431 (to_hex(self.offset), to_hex(self.orig_offset),
432 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600433 self.pre_reset_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600434 self.offset = self.orig_offset
435 self.size = self.orig_size
436
Simon Glass3ab95982018-08-01 15:22:37 -0600437 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600438 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700439
440 Most of the time the entries are not fully specified. There may be
441 an alignment but no size. In that case we take the size from the
442 contents of the entry.
443
Simon Glass3ab95982018-08-01 15:22:37 -0600444 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700445
Simon Glass3ab95982018-08-01 15:22:37 -0600446 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700447 entry will be know.
448
449 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600450 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700451
452 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600453 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700454 """
Simon Glass9f297b02019-07-20 12:23:36 -0600455 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700456 (to_hex(self.offset), to_hex(self.size),
Simon Glass9f297b02019-07-20 12:23:36 -0600457 self.contents_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600458 if self.offset is None:
459 if self.offset_unset:
460 self.Raise('No offset set with offset-unset: should another '
461 'entry provide this correct offset?')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700462 self.offset = tools.align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700463 needed = self.pad_before + self.contents_size + self.pad_after
Simon Glassc1aa66e2022-01-29 14:14:04 -0700464 needed = tools.align(needed, self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700465 size = self.size
466 if not size:
467 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600468 new_offset = self.offset + size
Simon Glassc1aa66e2022-01-29 14:14:04 -0700469 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glass3ab95982018-08-01 15:22:37 -0600470 if aligned_offset != new_offset:
471 size = aligned_offset - self.offset
472 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700473
474 if not self.size:
475 self.size = size
476
477 if self.size < needed:
478 self.Raise("Entry contents size is %#x (%d) but entry size is "
479 "%#x (%d)" % (needed, needed, self.size, self.size))
480 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600481 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700482 # conflict with the provided alignment values
Simon Glassc1aa66e2022-01-29 14:14:04 -0700483 if self.size != tools.align(self.size, self.align_size):
Simon Glassbf7fd502016-11-25 20:15:51 -0700484 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
485 (self.size, self.size, self.align_size, self.align_size))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700486 if self.offset != tools.align(self.offset, self.align):
Simon Glass3ab95982018-08-01 15:22:37 -0600487 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
488 (self.offset, self.offset, self.align, self.align))
Simon Glass9f297b02019-07-20 12:23:36 -0600489 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
490 (self.offset, self.size, self.contents_size, new_offset))
Simon Glassbf7fd502016-11-25 20:15:51 -0700491
Simon Glass3ab95982018-08-01 15:22:37 -0600492 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700493
494 def Raise(self, msg):
495 """Convenience function to raise an error referencing a node"""
496 raise ValueError("Node '%s': %s" % (self._node.path, msg))
497
Simon Glass189f2912021-03-21 18:24:31 +1300498 def Info(self, msg):
499 """Convenience function to log info referencing a node"""
500 tag = "Info '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700501 tout.detail('%30s: %s' % (tag, msg))
Simon Glass189f2912021-03-21 18:24:31 +1300502
Simon Glass9f297b02019-07-20 12:23:36 -0600503 def Detail(self, msg):
504 """Convenience function to log detail referencing a node"""
505 tag = "Node '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700506 tout.detail('%30s: %s' % (tag, msg))
Simon Glass9f297b02019-07-20 12:23:36 -0600507
Simon Glass53af22a2018-07-17 13:25:32 -0600508 def GetEntryArgsOrProps(self, props, required=False):
509 """Return the values of a set of properties
510
511 Args:
512 props: List of EntryArg objects
513
514 Raises:
515 ValueError if a property is not found
516 """
517 values = []
518 missing = []
519 for prop in props:
520 python_prop = prop.name.replace('-', '_')
521 if hasattr(self, python_prop):
522 value = getattr(self, python_prop)
523 else:
524 value = None
525 if value is None:
526 value = self.GetArg(prop.name, prop.datatype)
527 if value is None and required:
528 missing.append(prop.name)
529 values.append(value)
530 if missing:
Simon Glass939d1062021-01-06 21:35:16 -0700531 self.GetImage().MissingArgs(self, missing)
Simon Glass53af22a2018-07-17 13:25:32 -0600532 return values
533
Simon Glassbf7fd502016-11-25 20:15:51 -0700534 def GetPath(self):
535 """Get the path of a node
536
537 Returns:
538 Full path of the node for this entry
539 """
540 return self._node.path
541
Simon Glass631f7522021-03-21 18:24:32 +1300542 def GetData(self, required=True):
Simon Glass63e7ba62020-10-26 17:40:16 -0600543 """Get the contents of an entry
544
Simon Glass631f7522021-03-21 18:24:32 +1300545 Args:
546 required: True if the data must be present, False if it is OK to
547 return None
548
Simon Glass63e7ba62020-10-26 17:40:16 -0600549 Returns:
550 bytes content of the entry, excluding any padding. If the entry is
551 compressed, the compressed data is returned
552 """
Simon Glassc1aa66e2022-01-29 14:14:04 -0700553 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glassbf7fd502016-11-25 20:15:51 -0700554 return self.data
555
Simon Glass271a0832020-11-02 12:55:43 -0700556 def GetPaddedData(self, data=None):
557 """Get the data for an entry including any padding
558
559 Gets the entry data and uses its section's pad-byte value to add padding
560 before and after as defined by the pad-before and pad-after properties.
561
562 This does not consider alignment.
563
564 Returns:
565 Contents of the entry along with any pad bytes before and
566 after it (bytes)
567 """
568 if data is None:
569 data = self.GetData()
570 return self.section.GetPaddedDataForEntry(self, data)
571
Simon Glass3ab95982018-08-01 15:22:37 -0600572 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600573 """Get the offsets for siblings
574
575 Some entry types can contain information about the position or size of
576 other entries. An example of this is the Intel Flash Descriptor, which
577 knows where the Intel Management Engine section should go.
578
579 If this entry knows about the position of other entries, it can specify
580 this by returning values here
581
582 Returns:
583 Dict:
584 key: Entry type
585 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600586 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600587 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700588 return {}
589
Simon Glasscf549042019-07-08 13:18:39 -0600590 def SetOffsetSize(self, offset, size):
591 """Set the offset and/or size of an entry
592
593 Args:
594 offset: New offset, or None to leave alone
595 size: New size, or None to leave alone
596 """
597 if offset is not None:
598 self.offset = offset
599 if size is not None:
600 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700601
Simon Glassdbf6be92018-08-01 15:22:42 -0600602 def SetImagePos(self, image_pos):
603 """Set the position in the image
604
605 Args:
606 image_pos: Position of this entry in the image
607 """
608 self.image_pos = image_pos + self.offset
609
Simon Glassbf7fd502016-11-25 20:15:51 -0700610 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600611 """Do any post-packing updates of entry contents
612
613 This function should call ProcessContentsUpdate() to update the entry
614 contents, if necessary, returning its return value here.
615
616 Args:
617 data: Data to set to the contents (bytes)
618
619 Returns:
620 True if the new data size is OK, False if expansion is needed
621
622 Raises:
623 ValueError if the new data size is not the same as the old and
624 state.AllowEntryExpansion() is False
625 """
626 return True
Simon Glass19790632017-11-13 18:55:01 -0700627
Simon Glassf55382b2018-06-01 09:38:13 -0600628 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700629 """Write symbol values into binary files for access at run time
630
631 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600632 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700633 """
634 pass
Simon Glass18546952018-06-01 09:38:16 -0600635
Simon Glass6ddd6112020-10-26 17:40:18 -0600636 def CheckEntries(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600637 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600638
Simon Glass3ab95982018-08-01 15:22:37 -0600639 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600640 than having to be fully inside their section). Sub-classes can implement
641 this function and raise if there is a problem.
642 """
643 pass
Simon Glass3b0c3822018-06-01 09:38:20 -0600644
Simon Glass8122f392018-07-17 13:25:28 -0600645 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600646 def GetStr(value):
647 if value is None:
648 return '<none> '
649 return '%08x' % value
650
651 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600652 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600653 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
654 Entry.GetStr(offset), Entry.GetStr(size),
655 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600656
Simon Glass3b0c3822018-06-01 09:38:20 -0600657 def WriteMap(self, fd, indent):
658 """Write a map of the entry to a .map file
659
660 Args:
661 fd: File to write the map to
662 indent: Curent indent level of map (0=none, 1=one level, etc.)
663 """
Simon Glass1be70d22018-07-17 13:25:49 -0600664 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
665 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600666
Simon Glass11e36cc2018-07-17 13:25:38 -0600667 def GetEntries(self):
668 """Return a list of entries contained by this entry
669
670 Returns:
671 List of entries, or None if none. A normal entry has no entries
672 within it so will return None
673 """
674 return None
675
Simon Glass53af22a2018-07-17 13:25:32 -0600676 def GetArg(self, name, datatype=str):
677 """Get the value of an entry argument or device-tree-node property
678
679 Some node properties can be provided as arguments to binman. First check
680 the entry arguments, and fall back to the device tree if not found
681
682 Args:
683 name: Argument name
684 datatype: Data type (str or int)
685
686 Returns:
687 Value of argument as a string or int, or None if no value
688
689 Raises:
690 ValueError if the argument cannot be converted to in
691 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600692 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600693 if value is not None:
694 if datatype == int:
695 try:
696 value = int(value)
697 except ValueError:
698 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
699 (name, value))
700 elif datatype == str:
701 pass
702 else:
703 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
704 datatype)
705 else:
706 value = fdt_util.GetDatatype(self._node, name, datatype)
707 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600708
709 @staticmethod
710 def WriteDocs(modules, test_missing=None):
711 """Write out documentation about the various entry types to stdout
712
713 Args:
714 modules: List of modules to include
715 test_missing: Used for testing. This is a module to report
716 as missing
717 """
718 print('''Binman Entry Documentation
719===========================
720
721This file describes the entry types supported by binman. These entry types can
722be placed in an image one by one to build up a final firmware image. It is
723fairly easy to create new entry types. Just add a new file to the 'etype'
724directory. You can use the existing entries as examples.
725
726Note that some entries are subclasses of others, using and extending their
727features to produce new behaviours.
728
729
730''')
731 modules = sorted(modules)
732
733 # Don't show the test entry
734 if '_testing' in modules:
735 modules.remove('_testing')
736 missing = []
737 for name in modules:
Simon Glassb35fb172021-03-18 20:25:04 +1300738 module = Entry.Lookup('WriteDocs', name, False)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600739 docs = getattr(module, '__doc__')
740 if test_missing == name:
741 docs = None
742 if docs:
743 lines = docs.splitlines()
744 first_line = lines[0]
745 rest = [line[4:] for line in lines[1:]]
746 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
747 print(hdr)
748 print('-' * len(hdr))
749 print('\n'.join(rest))
750 print()
751 print()
752 else:
753 missing.append(name)
754
755 if missing:
756 raise ValueError('Documentation is missing for modules: %s' %
757 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600758
759 def GetUniqueName(self):
760 """Get a unique name for a node
761
762 Returns:
763 String containing a unique name for a node, consisting of the name
764 of all ancestors (starting from within the 'binman' node) separated
765 by a dot ('.'). This can be useful for generating unique filesnames
766 in the output directory.
767 """
768 name = self.name
769 node = self._node
770 while node.parent:
771 node = node.parent
772 if node.name == 'binman':
773 break
774 name = '%s.%s' % (node.name, name)
775 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600776
777 def ExpandToLimit(self, limit):
778 """Expand an entry so that it ends at the given offset limit"""
779 if self.offset + self.size < limit:
780 self.size = limit - self.offset
781 # Request the contents again, since changing the size requires that
782 # the data grows. This should not fail, but check it to be sure.
783 if not self.ObtainContents():
784 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600785
786 def HasSibling(self, name):
787 """Check if there is a sibling of a given name
788
789 Returns:
790 True if there is an entry with this name in the the same section,
791 else False
792 """
793 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600794
795 def GetSiblingImagePos(self, name):
796 """Return the image position of the given sibling
797
798 Returns:
799 Image position of sibling, or None if the sibling has no position,
800 or False if there is no such sibling
801 """
802 if not self.HasSibling(name):
803 return False
804 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600805
806 @staticmethod
807 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
808 uncomp_size, offset, entry):
809 """Add a new entry to the entries list
810
811 Args:
812 entries: List (of EntryInfo objects) to add to
813 indent: Current indent level to add to list
814 name: Entry name (string)
815 etype: Entry type (string)
816 size: Entry size in bytes (int)
817 image_pos: Position within image in bytes (int)
818 uncomp_size: Uncompressed size if the entry uses compression, else
819 None
820 offset: Entry offset within parent in bytes (int)
821 entry: Entry object
822 """
823 entries.append(EntryInfo(indent, name, etype, size, image_pos,
824 uncomp_size, offset, entry))
825
826 def ListEntries(self, entries, indent):
827 """Add files in this entry to the list of entries
828
829 This can be overridden by subclasses which need different behaviour.
830
831 Args:
832 entries: List (of EntryInfo objects) to add to
833 indent: Current indent level to add to list
834 """
835 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
836 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600837
Simon Glass943bf782021-11-23 21:09:50 -0700838 def ReadData(self, decomp=True, alt_format=None):
Simon Glassf667e452019-07-08 14:25:50 -0600839 """Read the data for an entry from the image
840
841 This is used when the image has been read in and we want to extract the
842 data for a particular entry from that image.
843
844 Args:
845 decomp: True to decompress any compressed data before returning it;
846 False to return the raw, uncompressed data
847
848 Returns:
849 Entry data (bytes)
850 """
851 # Use True here so that we get an uncompressed section to work from,
852 # although compressed sections are currently not supported
Simon Glassf3385a52022-01-29 14:14:15 -0700853 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass2d553c02019-09-25 08:56:21 -0600854 (self.section.GetPath(), self.GetPath()))
Simon Glass943bf782021-11-23 21:09:50 -0700855 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glassa9cd39e2019-07-20 12:24:04 -0600856 return data
Simon Glassd5079332019-07-20 12:23:41 -0600857
Simon Glass943bf782021-11-23 21:09:50 -0700858 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass2d553c02019-09-25 08:56:21 -0600859 """Read the data for a particular child entry
Simon Glass4e185e82019-09-25 08:56:20 -0600860
861 This reads data from the parent and extracts the piece that relates to
862 the given child.
863
864 Args:
Simon Glass943bf782021-11-23 21:09:50 -0700865 child (Entry): Child entry to read data for (must be valid)
866 decomp (bool): True to decompress any compressed data before
867 returning it; False to return the raw, uncompressed data
868 alt_format (str): Alternative format to read in, or None
Simon Glass4e185e82019-09-25 08:56:20 -0600869
870 Returns:
871 Data for the child (bytes)
872 """
873 pass
874
Simon Glassd5079332019-07-20 12:23:41 -0600875 def LoadData(self, decomp=True):
876 data = self.ReadData(decomp)
Simon Glass10f9d002019-07-20 12:23:50 -0600877 self.contents_size = len(data)
Simon Glassd5079332019-07-20 12:23:41 -0600878 self.ProcessContentsUpdate(data)
879 self.Detail('Loaded data size %x' % len(data))
Simon Glassc5ad04b2019-07-20 12:23:46 -0600880
Simon Glass943bf782021-11-23 21:09:50 -0700881 def GetAltFormat(self, data, alt_format):
882 """Read the data for an extry in an alternative format
883
884 Supported formats are list in the documentation for each entry. An
885 example is fdtmap which provides .
886
887 Args:
888 data (bytes): Data to convert (this should have been produced by the
889 entry)
890 alt_format (str): Format to use
891
892 """
893 pass
894
Simon Glassc5ad04b2019-07-20 12:23:46 -0600895 def GetImage(self):
896 """Get the image containing this entry
897
898 Returns:
899 Image object containing this entry
900 """
901 return self.section.GetImage()
Simon Glass10f9d002019-07-20 12:23:50 -0600902
903 def WriteData(self, data, decomp=True):
904 """Write the data to an entry in the image
905
906 This is used when the image has been read in and we want to replace the
907 data for a particular entry in that image.
908
909 The image must be re-packed and written out afterwards.
910
911 Args:
912 data: Data to replace it with
913 decomp: True to compress the data if needed, False if data is
914 already compressed so should be used as is
915
916 Returns:
917 True if the data did not result in a resize of this entry, False if
918 the entry must be resized
919 """
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600920 if self.size is not None:
921 self.contents_size = self.size
922 else:
923 self.contents_size = self.pre_reset_size
Simon Glass10f9d002019-07-20 12:23:50 -0600924 ok = self.ProcessContentsUpdate(data)
925 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glass7210c892019-07-20 12:24:05 -0600926 section_ok = self.section.WriteChildData(self)
927 return ok and section_ok
928
929 def WriteChildData(self, child):
930 """Handle writing the data in a child entry
931
932 This should be called on the child's parent section after the child's
Simon Glass557693e2021-11-23 11:03:44 -0700933 data has been updated. It should update any data structures needed to
934 validate that the update is successful.
Simon Glass7210c892019-07-20 12:24:05 -0600935
936 This base-class implementation does nothing, since the base Entry object
937 does not have any children.
938
939 Args:
940 child: Child Entry that was written
941
942 Returns:
943 True if the section could be updated successfully, False if the
Simon Glass557693e2021-11-23 11:03:44 -0700944 data is such that the section could not update
Simon Glass7210c892019-07-20 12:24:05 -0600945 """
946 return True
Simon Glasseba1f0c2019-07-20 12:23:55 -0600947
948 def GetSiblingOrder(self):
949 """Get the relative order of an entry amoung its siblings
950
951 Returns:
952 'start' if this entry is first among siblings, 'end' if last,
953 otherwise None
954 """
955 entries = list(self.section.GetEntries().values())
956 if entries:
957 if self == entries[0]:
958 return 'start'
959 elif self == entries[-1]:
960 return 'end'
961 return 'middle'
Simon Glass4f9f1052020-07-09 18:39:38 -0600962
963 def SetAllowMissing(self, allow_missing):
964 """Set whether a section allows missing external blobs
965
966 Args:
967 allow_missing: True if allowed, False if not allowed
968 """
969 # This is meaningless for anything other than sections
970 pass
Simon Glassb1cca952020-07-09 18:39:40 -0600971
Heiko Thierya89c8f22022-01-06 11:49:41 +0100972 def SetAllowFakeBlob(self, allow_fake):
973 """Set whether a section allows to create a fake blob
974
975 Args:
976 allow_fake: True if allowed, False if not allowed
977 """
Simon Glassf4590e02022-01-09 20:13:46 -0700978 self.allow_fake = allow_fake
Heiko Thierya89c8f22022-01-06 11:49:41 +0100979
Simon Glassb1cca952020-07-09 18:39:40 -0600980 def CheckMissing(self, missing_list):
981 """Check if any entries in this section have missing external blobs
982
983 If there are missing blobs, the entries are added to the list
984
985 Args:
986 missing_list: List of Entry objects to be added to
987 """
988 if self.missing:
989 missing_list.append(self)
Simon Glass87958982020-09-01 05:13:57 -0600990
Simon Glass790ba9f2022-01-12 13:10:36 -0700991 def check_fake_fname(self, fname):
992 """If the file is missing and the entry allows fake blobs, fake it
993
994 Sets self.faked to True if faked
995
996 Args:
997 fname (str): Filename to check
998
999 Returns:
1000 fname (str): Filename of faked file
1001 """
1002 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glassc1aa66e2022-01-29 14:14:04 -07001003 outfname = tools.get_output_filename(os.path.basename(fname))
Simon Glass790ba9f2022-01-12 13:10:36 -07001004 with open(outfname, "wb") as out:
1005 out.truncate(1024)
1006 self.faked = True
1007 return outfname
1008 return fname
1009
Heiko Thierya89c8f22022-01-06 11:49:41 +01001010 def CheckFakedBlobs(self, faked_blobs_list):
1011 """Check if any entries in this section have faked external blobs
1012
1013 If there are faked blobs, the entries are added to the list
1014
1015 Args:
1016 fake_blobs_list: List of Entry objects to be added to
1017 """
1018 # This is meaningless for anything other than blobs
1019 pass
1020
Simon Glass87958982020-09-01 05:13:57 -06001021 def GetAllowMissing(self):
1022 """Get whether a section allows missing external blobs
1023
1024 Returns:
1025 True if allowed, False if not allowed
1026 """
1027 return self.allow_missing
Simon Glassb2381432020-09-06 10:39:09 -06001028
Simon Glass4f9ee832022-01-09 20:14:09 -07001029 def record_missing_bintool(self, bintool):
1030 """Record a missing bintool that was needed to produce this entry
1031
1032 Args:
1033 bintool (Bintool): Bintool that was missing
1034 """
1035 self.missing_bintools.append(bintool)
1036
1037 def check_missing_bintools(self, missing_list):
1038 """Check if any entries in this section have missing bintools
1039
1040 If there are missing bintools, these are added to the list
1041
1042 Args:
1043 missing_list: List of Bintool objects to be added to
1044 """
1045 missing_list += self.missing_bintools
1046
Simon Glassb2381432020-09-06 10:39:09 -06001047 def GetHelpTags(self):
1048 """Get the tags use for missing-blob help
1049
1050 Returns:
1051 list of possible tags, most desirable first
1052 """
1053 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glass87c96292020-10-26 17:40:06 -06001054
1055 def CompressData(self, indata):
1056 """Compress data according to the entry's compression method
1057
1058 Args:
1059 indata: Data to compress
1060
1061 Returns:
1062 Compressed data (first word is the compressed size)
1063 """
Simon Glass97c3e9a2020-10-26 17:40:15 -06001064 self.uncomp_data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001065 if self.compress != 'none':
1066 self.uncomp_size = len(indata)
Simon Glass0d1e95a2022-01-09 20:14:04 -07001067 data = comp_util.compress(indata, self.compress)
Simon Glass87c96292020-10-26 17:40:06 -06001068 return data
Simon Glassb35fb172021-03-18 20:25:04 +13001069
1070 @classmethod
1071 def UseExpanded(cls, node, etype, new_etype):
1072 """Check whether to use an expanded entry type
1073
1074 This is called by Entry.Create() when it finds an expanded version of
1075 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1076 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1077 ignored.
1078
1079 Args:
1080 node: Node object containing information about the entry to
1081 create
1082 etype: Original entry type being used
1083 new_etype: New entry type proposed
1084
1085 Returns:
1086 True to use this entry type, False to use the original one
1087 """
Simon Glassf3385a52022-01-29 14:14:15 -07001088 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glassb35fb172021-03-18 20:25:04 +13001089 (node.path, etype, new_etype))
1090 return True
Simon Glass943bf782021-11-23 21:09:50 -07001091
1092 def CheckAltFormats(self, alt_formats):
1093 """Add any alternative formats supported by this entry type
1094
1095 Args:
1096 alt_formats (dict): Dict to add alt_formats to:
1097 key: Name of alt format
1098 value: Help text
1099 """
1100 pass
Simon Glass386c63c2022-01-09 20:13:50 -07001101
1102 def AddBintools(self, tools):
1103 """Add the bintools used by this entry type
1104
1105 Args:
1106 tools (dict of Bintool):
1107 """
1108 pass
1109
1110 @classmethod
1111 def AddBintool(self, tools, name):
1112 """Add a new bintool to the tools used by this etype
1113
1114 Args:
1115 name: Name of the tool
1116 """
1117 btool = bintool.Bintool.create(name)
1118 tools[name] = btool
1119 return btool
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03001120
1121 def SetUpdateHash(self, update_hash):
1122 """Set whether this entry's "hash" subnode should be updated
1123
1124 Args:
1125 update_hash: True if hash should be updated, False if not
1126 """
1127 self.update_hash = update_hash
Simon Glass81b71c32022-02-08 11:50:00 -07001128
1129 def collect_contents_to_file(self, entries, prefix):
1130 """Put the contents of a list of entries into a file
1131
1132 Args:
1133 entries (list of Entry): Entries to collect
1134 prefix (str): Filename prefix of file to write to
1135
1136 If any entry does not have contents yet, this function returns False
1137 for the data.
1138
1139 Returns:
1140 Tuple:
1141 bytes: Concatenated data from all the entries (or False)
1142 str: Filename of file written (or False if no data)
1143 str: Unique portion of filename (or False if no data)
1144 """
1145 data = b''
1146 for entry in entries:
1147 # First get the input data and put it in a file. If not available,
1148 # try later.
1149 if not entry.ObtainContents():
1150 return False, False, False
1151 data += entry.GetData()
1152 uniq = self.GetUniqueName()
1153 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1154 tools.write_file(fname, data)
1155 return data, fname, uniq