blob: aca08e62d3a0c14bd1afd64a00295e15f9d207a7 [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 Glass7960a0a2022-08-07 09:46:46 -060012import time
Simon Glassc55a50f2018-09-14 04:57:19 -060013
Simon Glass386c63c2022-01-09 20:13:50 -070014from binman import bintool
Simon Glass3fbba552022-10-20 18:22:46 -060015from binman import elf
Simon Glass16287932020-04-17 18:09:03 -060016from dtoc import fdt_util
Simon Glassbf776672020-04-17 18:09:04 -060017from patman import tools
Simon Glassc1aa66e2022-01-29 14:14:04 -070018from patman.tools import to_hex, to_hex_size
Simon Glassbf776672020-04-17 18:09:04 -060019from patman import tout
Simon Glassbf7fd502016-11-25 20:15:51 -070020
21modules = {}
22
Simon Glass8d2ef3e2022-02-11 13:23:21 -070023# This is imported if needed
24state = None
Simon Glass53af22a2018-07-17 13:25:32 -060025
26# An argument which can be passed to entries on the command line, in lieu of
27# device-tree properties.
28EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
29
Simon Glass41b8ba02019-07-08 14:25:43 -060030# Information about an entry for use when displaying summaries
31EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
32 'image_pos', 'uncomp_size', 'offset',
33 'entry'])
Simon Glass53af22a2018-07-17 13:25:32 -060034
Simon Glassbf7fd502016-11-25 20:15:51 -070035class Entry(object):
Simon Glass25ac0e62018-06-01 09:38:14 -060036 """An Entry in the section
Simon Glassbf7fd502016-11-25 20:15:51 -070037
38 An entry corresponds to a single node in the device-tree description
Simon Glass25ac0e62018-06-01 09:38:14 -060039 of the section. Each entry ends up being a part of the final section.
Simon Glassbf7fd502016-11-25 20:15:51 -070040 Entries can be placed either right next to each other, or with padding
41 between them. The type of the entry determines the data that is in it.
42
43 This class is not used by itself. All entry objects are subclasses of
44 Entry.
45
46 Attributes:
Simon Glass8122f392018-07-17 13:25:28 -060047 section: Section object containing this entry
Simon Glassbf7fd502016-11-25 20:15:51 -070048 node: The node that created this entry
Simon Glass3ab95982018-08-01 15:22:37 -060049 offset: Offset of entry within the section, None if not known yet (in
50 which case it will be calculated by Pack())
Simon Glassbf7fd502016-11-25 20:15:51 -070051 size: Entry size in bytes, None if not known
Simon Glass9a5d3dc2019-10-31 07:43:02 -060052 pre_reset_size: size as it was before ResetForPack(). This allows us to
53 keep track of the size we started with and detect size changes
Simon Glass8287ee82019-07-08 14:25:30 -060054 uncomp_size: Size of uncompressed data in bytes, if the entry is
55 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070056 contents_size: Size of contents in bytes, 0 by default
Simon Glass4eec34c2020-10-26 17:40:10 -060057 align: Entry start offset alignment relative to the start of the
58 containing section, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070059 align_size: Entry size alignment, or None
Simon Glass4eec34c2020-10-26 17:40:10 -060060 align_end: Entry end offset alignment relative to the start of the
61 containing section, or None
Simon Glassf90d9062020-10-26 17:40:09 -060062 pad_before: Number of pad bytes before the contents when it is placed
63 in the containing section, 0 if none. The pad bytes become part of
64 the entry.
65 pad_after: Number of pad bytes after the contents when it is placed in
66 the containing section, 0 if none. The pad bytes become part of
67 the entry.
68 data: Contents of entry (string of bytes). This does not include
Simon Glass97c3e9a2020-10-26 17:40:15 -060069 padding created by pad_before or pad_after. If the entry is
70 compressed, this contains the compressed data.
71 uncomp_data: Original uncompressed data, if this entry is compressed,
72 else None
Simon Glass8287ee82019-07-08 14:25:30 -060073 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassc52c9e72019-07-08 14:25:37 -060074 orig_offset: Original offset value read from node
75 orig_size: Original size value read from node
Simon Glass67a05012023-01-07 14:07:15 -070076 missing: True if this entry is missing its contents. Note that if it is
77 optional, this entry will not appear in the list generated by
78 entry.CheckMissing() since it is considered OK for it to be missing.
Simon Glass87958982020-09-01 05:13:57 -060079 allow_missing: Allow children of this entry to be missing (used by
80 subclasses such as Entry_section)
Heiko Thierya89c8f22022-01-06 11:49:41 +010081 allow_fake: Allow creating a dummy fake file if the blob file is not
82 available. This is mainly used for testing.
Simon Glass87958982020-09-01 05:13:57 -060083 external: True if this entry contains an external binary blob
Simon Glass386c63c2022-01-09 20:13:50 -070084 bintools: Bintools used by this entry (only populated for Image)
Simon Glass4f9ee832022-01-09 20:14:09 -070085 missing_bintools: List of missing bintools for this entry
Alper Nebi Yasakee813c82022-02-09 22:02:35 +030086 update_hash: True if this entry's "hash" subnode should be
87 updated with a hash of the entry contents
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +020088 comp_bintool: Bintools used for compress and decompress data
Simon Glass7960a0a2022-08-07 09:46:46 -060089 fake_fname: Fake filename, if one was created, else None
Simon Glasscdadada2022-08-13 11:40:44 -060090 required_props (dict of str): Properties which must be present. This can
91 be added to by subclasses
Simon Glass3fbba552022-10-20 18:22:46 -060092 elf_fname (str): Filename of the ELF file, if this entry holds an ELF
93 file, or is a binary file produced from an ELF file
94 auto_write_symbols (bool): True to write ELF symbols into this entry's
95 contents
Simon Glassc8c9f312023-01-07 14:07:12 -070096 absent (bool): True if this entry is absent. This can be controlled by
97 the entry itself, allowing it to vanish in certain circumstances.
98 An absent entry is removed during processing so that it does not
99 appear in the map
Simon Glass67a05012023-01-07 14:07:15 -0700100 optional (bool): True if this entry contains an optional external blob
Simon Glass9766f692023-01-11 16:10:16 -0700101 overlap (bool): True if this entry overlaps with others
Simon Glassbf7fd502016-11-25 20:15:51 -0700102 """
Simon Glass7960a0a2022-08-07 09:46:46 -0600103 fake_dir = None
104
Simon Glass3fbba552022-10-20 18:22:46 -0600105 def __init__(self, section, etype, node, name_prefix='',
106 auto_write_symbols=False):
Simon Glass8dbb7442019-08-24 07:22:44 -0600107 # Put this here to allow entry-docs and help to work without libfdt
108 global state
Simon Glass16287932020-04-17 18:09:03 -0600109 from binman import state
Simon Glass8dbb7442019-08-24 07:22:44 -0600110
Simon Glass25ac0e62018-06-01 09:38:14 -0600111 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -0700112 self.etype = etype
113 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -0600114 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -0600115 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700116 self.size = None
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600117 self.pre_reset_size = None
Simon Glass8287ee82019-07-08 14:25:30 -0600118 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -0600119 self.data = None
Simon Glass97c3e9a2020-10-26 17:40:15 -0600120 self.uncomp_data = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700121 self.contents_size = 0
122 self.align = None
123 self.align_size = None
124 self.align_end = None
125 self.pad_before = 0
126 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -0600127 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -0600128 self.image_pos = None
Simon Glass80a66ae2022-03-05 20:18:59 -0700129 self.extend_size = False
Simon Glass8287ee82019-07-08 14:25:30 -0600130 self.compress = 'none'
Simon Glassb1cca952020-07-09 18:39:40 -0600131 self.missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100132 self.faked = False
Simon Glass87958982020-09-01 05:13:57 -0600133 self.external = False
134 self.allow_missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100135 self.allow_fake = False
Simon Glass386c63c2022-01-09 20:13:50 -0700136 self.bintools = {}
Simon Glass4f9ee832022-01-09 20:14:09 -0700137 self.missing_bintools = []
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300138 self.update_hash = True
Simon Glass7960a0a2022-08-07 09:46:46 -0600139 self.fake_fname = None
Simon Glasscdadada2022-08-13 11:40:44 -0600140 self.required_props = []
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +0200141 self.comp_bintool = None
Simon Glass3fbba552022-10-20 18:22:46 -0600142 self.elf_fname = None
143 self.auto_write_symbols = auto_write_symbols
Simon Glassc8c9f312023-01-07 14:07:12 -0700144 self.absent = False
Simon Glass67a05012023-01-07 14:07:15 -0700145 self.optional = False
Simon Glass9766f692023-01-11 16:10:16 -0700146 self.overlap = False
Simon Glassc1157862023-01-11 16:10:17 -0700147 self.elf_base_sym = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700148
149 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700150 def FindEntryClass(etype, expanded):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600151 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -0700152
153 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -0600154 node_node: Path name of Node object containing information about
155 the entry to create (used for errors)
156 etype: Entry type to use
Simon Glassb35fb172021-03-18 20:25:04 +1300157 expanded: Use the expanded version of etype
Simon Glassbf7fd502016-11-25 20:15:51 -0700158
159 Returns:
Simon Glassb35fb172021-03-18 20:25:04 +1300160 The entry class object if found, else None if not found and expanded
Simon Glass858436d2021-11-23 21:09:49 -0700161 is True, else a tuple:
162 module name that could not be found
163 exception received
Simon Glassbf7fd502016-11-25 20:15:51 -0700164 """
Simon Glassdd57c132018-06-01 09:38:11 -0600165 # Convert something like 'u-boot@0' to 'u_boot' since we are only
166 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700167 module_name = etype.replace('-', '_')
Simon Glassb35fb172021-03-18 20:25:04 +1300168
Simon Glassdd57c132018-06-01 09:38:11 -0600169 if '@' in module_name:
170 module_name = module_name.split('@')[0]
Simon Glassb35fb172021-03-18 20:25:04 +1300171 if expanded:
172 module_name += '_expanded'
Simon Glassbf7fd502016-11-25 20:15:51 -0700173 module = modules.get(module_name)
174
Simon Glassbadf0ec2018-06-01 09:38:15 -0600175 # Also allow entry-type modules to be brought in from the etype directory.
176
Simon Glassbf7fd502016-11-25 20:15:51 -0700177 # Import the module if we have not already done so.
178 if not module:
179 try:
Simon Glass16287932020-04-17 18:09:03 -0600180 module = importlib.import_module('binman.etype.' + module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600181 except ImportError as e:
Simon Glassb35fb172021-03-18 20:25:04 +1300182 if expanded:
183 return None
Simon Glass858436d2021-11-23 21:09:49 -0700184 return module_name, e
Simon Glassbf7fd502016-11-25 20:15:51 -0700185 modules[module_name] = module
186
Simon Glassfd8d1f72018-07-17 13:25:36 -0600187 # Look up the expected class name
188 return getattr(module, 'Entry_%s' % module_name)
189
190 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700191 def Lookup(node_path, etype, expanded, missing_etype=False):
192 """Look up the entry class for a node.
193
194 Args:
195 node_node (str): Path name of Node object containing information
196 about the entry to create (used for errors)
197 etype (str): Entry type to use
198 expanded (bool): Use the expanded version of etype
199 missing_etype (bool): True to default to a blob etype if the
200 requested etype is not found
201
202 Returns:
203 The entry class object if found, else None if not found and expanded
204 is True
205
206 Raise:
207 ValueError if expanded is False and the class is not found
208 """
209 # Convert something like 'u-boot@0' to 'u_boot' since we are only
210 # interested in the type.
211 cls = Entry.FindEntryClass(etype, expanded)
212 if cls is None:
213 return None
214 elif isinstance(cls, tuple):
215 if missing_etype:
216 cls = Entry.FindEntryClass('blob', False)
217 if isinstance(cls, tuple): # This should not fail
218 module_name, e = cls
219 raise ValueError(
220 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
221 (etype, node_path, module_name, e))
222 return cls
223
224 @staticmethod
225 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600226 """Create a new entry for a node.
227
228 Args:
Simon Glass858436d2021-11-23 21:09:49 -0700229 section (entry_Section): Section object containing this node
230 node (Node): Node object containing information about the entry to
231 create
232 etype (str): Entry type to use, or None to work it out (used for
233 tests)
234 expanded (bool): Use the expanded version of etype
235 missing_etype (bool): True to default to a blob etype if the
236 requested etype is not found
Simon Glassfd8d1f72018-07-17 13:25:36 -0600237
238 Returns:
239 A new Entry object of the correct type (a subclass of Entry)
240 """
241 if not etype:
242 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glass858436d2021-11-23 21:09:49 -0700243 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glassb35fb172021-03-18 20:25:04 +1300244 if obj and expanded:
245 # Check whether to use the expanded entry
246 new_etype = etype + '-expanded'
Simon Glass3d433382021-03-21 18:24:30 +1300247 can_expand = not fdt_util.GetBool(node, 'no-expanded')
248 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glassb35fb172021-03-18 20:25:04 +1300249 etype = new_etype
250 else:
251 obj = None
252 if not obj:
Simon Glass858436d2021-11-23 21:09:49 -0700253 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600254
Simon Glassbf7fd502016-11-25 20:15:51 -0700255 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600256 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700257
258 def ReadNode(self):
259 """Read entry information from the node
260
Simon Glassc6bd6e22019-07-20 12:23:45 -0600261 This must be called as the first thing after the Entry is created.
262
Simon Glassbf7fd502016-11-25 20:15:51 -0700263 This reads all the fields we recognise from the node, ready for use.
264 """
Simon Glasscdadada2022-08-13 11:40:44 -0600265 self.ensure_props()
Simon Glass15a587c2018-07-17 13:25:51 -0600266 if 'pos' in self._node.props:
267 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass80a66ae2022-03-05 20:18:59 -0700268 if 'expand-size' in self._node.props:
269 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glass3ab95982018-08-01 15:22:37 -0600270 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700271 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glass12bb1a92019-07-20 12:23:51 -0600272 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
273 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
274 if self.GetImage().copy_to_orig:
275 self.orig_offset = self.offset
276 self.orig_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600277
Simon Glassffded752019-07-08 14:25:46 -0600278 # These should not be set in input files, but are set in an FDT map,
279 # which is also read by this code.
280 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
281 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
282
Simon Glassbf7fd502016-11-25 20:15:51 -0700283 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700284 if tools.not_power_of_two(self.align):
Simon Glassbf7fd502016-11-25 20:15:51 -0700285 raise ValueError("Node '%s': Alignment %s must be a power of two" %
286 (self._node.path, self.align))
Simon Glass5ff9fed2021-03-21 18:24:33 +1300287 if self.section and self.align is None:
288 self.align = self.section.align_default
Simon Glassbf7fd502016-11-25 20:15:51 -0700289 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
290 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
291 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700292 if tools.not_power_of_two(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600293 self.Raise("Alignment size %s must be a power of two" %
294 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700295 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600296 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glass80a66ae2022-03-05 20:18:59 -0700297 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassb2381432020-09-06 10:39:09 -0600298 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glass67a05012023-01-07 14:07:15 -0700299 self.optional = fdt_util.GetBool(self._node, 'optional')
Simon Glass9766f692023-01-11 16:10:16 -0700300 self.overlap = fdt_util.GetBool(self._node, 'overlap')
301 if self.overlap:
302 self.required_props += ['offset', 'size']
Simon Glassbf7fd502016-11-25 20:15:51 -0700303
Simon Glass87c96292020-10-26 17:40:06 -0600304 # This is only supported by blobs and sections at present
305 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
306
Simon Glass6c234bf2018-09-14 04:57:18 -0600307 def GetDefaultFilename(self):
308 return None
309
Simon Glassa8adb6d2019-07-20 12:23:28 -0600310 def GetFdts(self):
311 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600312
313 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600314 Empty dict, if this entry is not a .dtb, otherwise:
315 Dict:
316 key: Filename from this entry (without the path)
Simon Glass4bdd3002019-07-20 12:23:31 -0600317 value: Tuple:
Simon Glassadb67bb2021-03-18 20:25:02 +1300318 Entry object for this dtb
Simon Glass4bdd3002019-07-20 12:23:31 -0600319 Filename of file containing this dtb
Simon Glass539aece2018-09-14 04:57:22 -0600320 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600321 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600322
Simon Glassc9ee33a2022-03-05 20:19:00 -0700323 def gen_entries(self):
324 """Allow entries to generate other entries
Simon Glassa01d1a22021-03-18 20:24:52 +1300325
326 Some entries generate subnodes automatically, from which sub-entries
327 are then created. This method allows those to be added to the binman
328 definition for the current image. An entry which implements this method
329 should call state.AddSubnode() to add a subnode and can add properties
330 with state.AddString(), etc.
331
332 An example is 'files', which produces a section containing a list of
333 files.
334 """
Simon Glass0a98b282018-09-14 04:57:28 -0600335 pass
336
Simon Glassa9fad072020-10-26 17:40:17 -0600337 def AddMissingProperties(self, have_image_pos):
338 """Add new properties to the device tree as needed for this entry
339
340 Args:
341 have_image_pos: True if this entry has an image position. This can
342 be False if its parent section is compressed, since compression
343 groups all entries together into a compressed block of data,
344 obscuring the start of each individual child entry
345 """
346 for prop in ['offset', 'size']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600347 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600348 state.AddZeroProp(self._node, prop)
Simon Glassa9fad072020-10-26 17:40:17 -0600349 if have_image_pos and 'image-pos' not in self._node.props:
350 state.AddZeroProp(self._node, 'image-pos')
Simon Glass12bb1a92019-07-20 12:23:51 -0600351 if self.GetImage().allow_repack:
352 if self.orig_offset is not None:
353 state.AddZeroProp(self._node, 'orig-offset', True)
354 if self.orig_size is not None:
355 state.AddZeroProp(self._node, 'orig-size', True)
356
Simon Glass8287ee82019-07-08 14:25:30 -0600357 if self.compress != 'none':
358 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300359
360 if self.update_hash:
361 err = state.CheckAddHashProp(self._node)
362 if err:
363 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600364
365 def SetCalculatedProperties(self):
366 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600367 state.SetInt(self._node, 'offset', self.offset)
368 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600369 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassa9fad072020-10-26 17:40:17 -0600370 if self.image_pos is not None:
Simon Glass08594d42020-11-02 12:55:44 -0700371 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass12bb1a92019-07-20 12:23:51 -0600372 if self.GetImage().allow_repack:
373 if self.orig_offset is not None:
374 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
375 if self.orig_size is not None:
376 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glass8287ee82019-07-08 14:25:30 -0600377 if self.uncomp_size is not None:
378 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300379
380 if self.update_hash:
381 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600382
Simon Glassecab8972018-07-06 10:27:40 -0600383 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600384 """Allow entries to adjust the device tree
385
386 Some entries need to adjust the device tree for their purposes. This
387 may involve adding or deleting properties.
388
389 Returns:
390 True if processing is complete
391 False if processing could not be completed due to a dependency.
392 This will cause the entry to be retried after others have been
393 called
394 """
Simon Glassecab8972018-07-06 10:27:40 -0600395 return True
396
Simon Glassc8d48ef2018-06-01 09:38:21 -0600397 def SetPrefix(self, prefix):
398 """Set the name prefix for a node
399
400 Args:
401 prefix: Prefix to set, or '' to not use a prefix
402 """
403 if prefix:
404 self.name = prefix + self.name
405
Simon Glass5c890232018-07-06 10:27:19 -0600406 def SetContents(self, data):
407 """Set the contents of an entry
408
409 This sets both the data and content_size properties
410
411 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600412 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600413 """
414 self.data = data
415 self.contents_size = len(self.data)
416
417 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600418 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600419
Simon Glassa0dcaf22019-07-08 14:25:35 -0600420 This checks that the new data is the same size as the old. If the size
421 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600422
423 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600424 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600425
426 Raises:
427 ValueError if the new data size is not the same as the old
428 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600429 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600430 new_size = len(data)
Simon Glass61ec04f2019-07-20 12:23:58 -0600431 if state.AllowEntryExpansion() and new_size > self.contents_size:
432 # self.data will indicate the new size needed
433 size_ok = False
434 elif state.AllowEntryContraction() and new_size < self.contents_size:
435 size_ok = False
436
437 # If not allowed to change, try to deal with it or give up
438 if size_ok:
Simon Glassc52c9e72019-07-08 14:25:37 -0600439 if new_size > self.contents_size:
Simon Glass61ec04f2019-07-20 12:23:58 -0600440 self.Raise('Cannot update entry size from %d to %d' %
441 (self.contents_size, new_size))
442
443 # Don't let the data shrink. Pad it if necessary
444 if size_ok and new_size < self.contents_size:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700445 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass61ec04f2019-07-20 12:23:58 -0600446
447 if not size_ok:
Simon Glassf3385a52022-01-29 14:14:15 -0700448 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glassc1aa66e2022-01-29 14:14:04 -0700449 self._node.path, to_hex(self.contents_size),
450 to_hex(new_size)))
Simon Glass5c890232018-07-06 10:27:19 -0600451 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600452 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600453
Simon Glass72e423c2022-03-05 20:19:05 -0700454 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glassbf7fd502016-11-25 20:15:51 -0700455 """Figure out the contents of an entry.
456
Simon Glass72e423c2022-03-05 20:19:05 -0700457 Args:
458 skip_entry (Entry): Entry to skip when obtaining section contents
459 fake_size (int): Size of fake file to create if needed
460
Simon Glassbf7fd502016-11-25 20:15:51 -0700461 Returns:
462 True if the contents were found, False if another call is needed
Simon Glass62ef2f72023-01-11 16:10:14 -0700463 after the other entries are processed, None if there is no contents
Simon Glassbf7fd502016-11-25 20:15:51 -0700464 """
465 # No contents by default: subclasses can implement this
466 return True
467
Simon Glassc52c9e72019-07-08 14:25:37 -0600468 def ResetForPack(self):
469 """Reset offset/size fields so that packing can be done again"""
Simon Glass9f297b02019-07-20 12:23:36 -0600470 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700471 (to_hex(self.offset), to_hex(self.orig_offset),
472 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600473 self.pre_reset_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600474 self.offset = self.orig_offset
475 self.size = self.orig_size
476
Simon Glass3ab95982018-08-01 15:22:37 -0600477 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600478 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700479
480 Most of the time the entries are not fully specified. There may be
481 an alignment but no size. In that case we take the size from the
482 contents of the entry.
483
Simon Glass3ab95982018-08-01 15:22:37 -0600484 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700485
Simon Glass3ab95982018-08-01 15:22:37 -0600486 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700487 entry will be know.
488
489 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600490 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700491
492 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600493 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700494 """
Simon Glass9f297b02019-07-20 12:23:36 -0600495 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700496 (to_hex(self.offset), to_hex(self.size),
Simon Glass9f297b02019-07-20 12:23:36 -0600497 self.contents_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600498 if self.offset is None:
499 if self.offset_unset:
500 self.Raise('No offset set with offset-unset: should another '
501 'entry provide this correct offset?')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700502 self.offset = tools.align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700503 needed = self.pad_before + self.contents_size + self.pad_after
Simon Glassc1aa66e2022-01-29 14:14:04 -0700504 needed = tools.align(needed, self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700505 size = self.size
506 if not size:
507 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600508 new_offset = self.offset + size
Simon Glassc1aa66e2022-01-29 14:14:04 -0700509 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glass3ab95982018-08-01 15:22:37 -0600510 if aligned_offset != new_offset:
511 size = aligned_offset - self.offset
512 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700513
514 if not self.size:
515 self.size = size
516
517 if self.size < needed:
518 self.Raise("Entry contents size is %#x (%d) but entry size is "
519 "%#x (%d)" % (needed, needed, self.size, self.size))
520 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600521 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700522 # conflict with the provided alignment values
Simon Glassc1aa66e2022-01-29 14:14:04 -0700523 if self.size != tools.align(self.size, self.align_size):
Simon Glassbf7fd502016-11-25 20:15:51 -0700524 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
525 (self.size, self.size, self.align_size, self.align_size))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700526 if self.offset != tools.align(self.offset, self.align):
Simon Glass3ab95982018-08-01 15:22:37 -0600527 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
528 (self.offset, self.offset, self.align, self.align))
Simon Glass9f297b02019-07-20 12:23:36 -0600529 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
530 (self.offset, self.size, self.contents_size, new_offset))
Simon Glassbf7fd502016-11-25 20:15:51 -0700531
Simon Glass3ab95982018-08-01 15:22:37 -0600532 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700533
534 def Raise(self, msg):
535 """Convenience function to raise an error referencing a node"""
536 raise ValueError("Node '%s': %s" % (self._node.path, msg))
537
Simon Glass189f2912021-03-21 18:24:31 +1300538 def Info(self, msg):
539 """Convenience function to log info referencing a node"""
540 tag = "Info '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700541 tout.detail('%30s: %s' % (tag, msg))
Simon Glass189f2912021-03-21 18:24:31 +1300542
Simon Glass9f297b02019-07-20 12:23:36 -0600543 def Detail(self, msg):
544 """Convenience function to log detail referencing a node"""
545 tag = "Node '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700546 tout.detail('%30s: %s' % (tag, msg))
Simon Glass9f297b02019-07-20 12:23:36 -0600547
Simon Glass53af22a2018-07-17 13:25:32 -0600548 def GetEntryArgsOrProps(self, props, required=False):
549 """Return the values of a set of properties
550
551 Args:
552 props: List of EntryArg objects
553
554 Raises:
555 ValueError if a property is not found
556 """
557 values = []
558 missing = []
559 for prop in props:
560 python_prop = prop.name.replace('-', '_')
561 if hasattr(self, python_prop):
562 value = getattr(self, python_prop)
563 else:
564 value = None
565 if value is None:
566 value = self.GetArg(prop.name, prop.datatype)
567 if value is None and required:
568 missing.append(prop.name)
569 values.append(value)
570 if missing:
Simon Glass939d1062021-01-06 21:35:16 -0700571 self.GetImage().MissingArgs(self, missing)
Simon Glass53af22a2018-07-17 13:25:32 -0600572 return values
573
Simon Glassbf7fd502016-11-25 20:15:51 -0700574 def GetPath(self):
575 """Get the path of a node
576
577 Returns:
578 Full path of the node for this entry
579 """
580 return self._node.path
581
Simon Glass631f7522021-03-21 18:24:32 +1300582 def GetData(self, required=True):
Simon Glass63e7ba62020-10-26 17:40:16 -0600583 """Get the contents of an entry
584
Simon Glass631f7522021-03-21 18:24:32 +1300585 Args:
586 required: True if the data must be present, False if it is OK to
587 return None
588
Simon Glass63e7ba62020-10-26 17:40:16 -0600589 Returns:
590 bytes content of the entry, excluding any padding. If the entry is
Simon Glass4331d662023-01-11 16:10:13 -0700591 compressed, the compressed data is returned. If the entry data
Simon Glass62ef2f72023-01-11 16:10:14 -0700592 is not yet available, False can be returned. If the entry data
593 is null, then None is returned.
Simon Glass63e7ba62020-10-26 17:40:16 -0600594 """
Simon Glassc1aa66e2022-01-29 14:14:04 -0700595 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glassbf7fd502016-11-25 20:15:51 -0700596 return self.data
597
Simon Glass271a0832020-11-02 12:55:43 -0700598 def GetPaddedData(self, data=None):
599 """Get the data for an entry including any padding
600
601 Gets the entry data and uses its section's pad-byte value to add padding
602 before and after as defined by the pad-before and pad-after properties.
603
604 This does not consider alignment.
605
606 Returns:
607 Contents of the entry along with any pad bytes before and
608 after it (bytes)
609 """
610 if data is None:
611 data = self.GetData()
612 return self.section.GetPaddedDataForEntry(self, data)
613
Simon Glass3ab95982018-08-01 15:22:37 -0600614 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600615 """Get the offsets for siblings
616
617 Some entry types can contain information about the position or size of
618 other entries. An example of this is the Intel Flash Descriptor, which
619 knows where the Intel Management Engine section should go.
620
621 If this entry knows about the position of other entries, it can specify
622 this by returning values here
623
624 Returns:
625 Dict:
626 key: Entry type
627 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600628 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600629 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700630 return {}
631
Simon Glasscf549042019-07-08 13:18:39 -0600632 def SetOffsetSize(self, offset, size):
633 """Set the offset and/or size of an entry
634
635 Args:
636 offset: New offset, or None to leave alone
637 size: New size, or None to leave alone
638 """
639 if offset is not None:
640 self.offset = offset
641 if size is not None:
642 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700643
Simon Glassdbf6be92018-08-01 15:22:42 -0600644 def SetImagePos(self, image_pos):
645 """Set the position in the image
646
647 Args:
648 image_pos: Position of this entry in the image
649 """
650 self.image_pos = image_pos + self.offset
651
Simon Glassbf7fd502016-11-25 20:15:51 -0700652 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600653 """Do any post-packing updates of entry contents
654
655 This function should call ProcessContentsUpdate() to update the entry
656 contents, if necessary, returning its return value here.
657
658 Args:
659 data: Data to set to the contents (bytes)
660
661 Returns:
662 True if the new data size is OK, False if expansion is needed
663
664 Raises:
665 ValueError if the new data size is not the same as the old and
666 state.AllowEntryExpansion() is False
667 """
668 return True
Simon Glass19790632017-11-13 18:55:01 -0700669
Simon Glassf55382b2018-06-01 09:38:13 -0600670 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700671 """Write symbol values into binary files for access at run time
672
673 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600674 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700675 """
Simon Glass3fbba552022-10-20 18:22:46 -0600676 if self.auto_write_symbols:
Simon Glassd2afb9e2022-10-20 18:22:47 -0600677 # Check if we are writing symbols into an ELF file
678 is_elf = self.GetDefaultFilename() == self.elf_fname
679 elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(),
Simon Glassc1157862023-01-11 16:10:17 -0700680 is_elf, self.elf_base_sym)
Simon Glass18546952018-06-01 09:38:16 -0600681
Simon Glass6ddd6112020-10-26 17:40:18 -0600682 def CheckEntries(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600683 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600684
Simon Glass3ab95982018-08-01 15:22:37 -0600685 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600686 than having to be fully inside their section). Sub-classes can implement
687 this function and raise if there is a problem.
688 """
689 pass
Simon Glass3b0c38212018-06-01 09:38:20 -0600690
Simon Glass8122f392018-07-17 13:25:28 -0600691 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600692 def GetStr(value):
693 if value is None:
694 return '<none> '
695 return '%08x' % value
696
697 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600698 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600699 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
700 Entry.GetStr(offset), Entry.GetStr(size),
701 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600702
Simon Glass3b0c38212018-06-01 09:38:20 -0600703 def WriteMap(self, fd, indent):
704 """Write a map of the entry to a .map file
705
706 Args:
707 fd: File to write the map to
708 indent: Curent indent level of map (0=none, 1=one level, etc.)
709 """
Simon Glass1be70d22018-07-17 13:25:49 -0600710 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
711 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600712
Simon Glassd626e822022-08-13 11:40:50 -0600713 # pylint: disable=assignment-from-none
Simon Glass11e36cc2018-07-17 13:25:38 -0600714 def GetEntries(self):
715 """Return a list of entries contained by this entry
716
717 Returns:
718 List of entries, or None if none. A normal entry has no entries
719 within it so will return None
720 """
721 return None
722
Simon Glassd626e822022-08-13 11:40:50 -0600723 def FindEntryByNode(self, find_node):
724 """Find a node in an entry, searching all subentries
725
726 This does a recursive search.
727
728 Args:
729 find_node (fdt.Node): Node to find
730
731 Returns:
732 Entry: entry, if found, else None
733 """
734 entries = self.GetEntries()
735 if entries:
736 for entry in entries.values():
737 if entry._node == find_node:
738 return entry
739 found = entry.FindEntryByNode(find_node)
740 if found:
741 return found
742
743 return None
744
Simon Glass53af22a2018-07-17 13:25:32 -0600745 def GetArg(self, name, datatype=str):
746 """Get the value of an entry argument or device-tree-node property
747
748 Some node properties can be provided as arguments to binman. First check
749 the entry arguments, and fall back to the device tree if not found
750
751 Args:
752 name: Argument name
753 datatype: Data type (str or int)
754
755 Returns:
756 Value of argument as a string or int, or None if no value
757
758 Raises:
759 ValueError if the argument cannot be converted to in
760 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600761 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600762 if value is not None:
763 if datatype == int:
764 try:
765 value = int(value)
766 except ValueError:
767 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
768 (name, value))
769 elif datatype == str:
770 pass
771 else:
772 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
773 datatype)
774 else:
775 value = fdt_util.GetDatatype(self._node, name, datatype)
776 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600777
778 @staticmethod
779 def WriteDocs(modules, test_missing=None):
780 """Write out documentation about the various entry types to stdout
781
782 Args:
783 modules: List of modules to include
784 test_missing: Used for testing. This is a module to report
785 as missing
786 """
787 print('''Binman Entry Documentation
788===========================
789
790This file describes the entry types supported by binman. These entry types can
791be placed in an image one by one to build up a final firmware image. It is
792fairly easy to create new entry types. Just add a new file to the 'etype'
793directory. You can use the existing entries as examples.
794
795Note that some entries are subclasses of others, using and extending their
796features to produce new behaviours.
797
798
799''')
800 modules = sorted(modules)
801
802 # Don't show the test entry
803 if '_testing' in modules:
804 modules.remove('_testing')
805 missing = []
806 for name in modules:
Simon Glassb35fb172021-03-18 20:25:04 +1300807 module = Entry.Lookup('WriteDocs', name, False)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600808 docs = getattr(module, '__doc__')
809 if test_missing == name:
810 docs = None
811 if docs:
812 lines = docs.splitlines()
813 first_line = lines[0]
814 rest = [line[4:] for line in lines[1:]]
815 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glass228c9b82022-08-07 16:33:25 -0600816
817 # Create a reference for use by rST docs
818 ref_name = f'etype_{module.__name__[6:]}'.lower()
819 print('.. _%s:' % ref_name)
820 print()
Simon Glassfd8d1f72018-07-17 13:25:36 -0600821 print(hdr)
822 print('-' * len(hdr))
823 print('\n'.join(rest))
824 print()
825 print()
826 else:
827 missing.append(name)
828
829 if missing:
830 raise ValueError('Documentation is missing for modules: %s' %
831 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600832
833 def GetUniqueName(self):
834 """Get a unique name for a node
835
836 Returns:
837 String containing a unique name for a node, consisting of the name
838 of all ancestors (starting from within the 'binman' node) separated
839 by a dot ('.'). This can be useful for generating unique filesnames
840 in the output directory.
841 """
842 name = self.name
843 node = self._node
844 while node.parent:
845 node = node.parent
Alper Nebi Yasak67bf2c82022-03-27 18:31:44 +0300846 if node.name in ('binman', '/'):
Simon Glassa326b492018-09-14 04:57:11 -0600847 break
848 name = '%s.%s' % (node.name, name)
849 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600850
Simon Glass80a66ae2022-03-05 20:18:59 -0700851 def extend_to_limit(self, limit):
852 """Extend an entry so that it ends at the given offset limit"""
Simon Glassba64a0b2018-09-14 04:57:29 -0600853 if self.offset + self.size < limit:
854 self.size = limit - self.offset
855 # Request the contents again, since changing the size requires that
856 # the data grows. This should not fail, but check it to be sure.
857 if not self.ObtainContents():
858 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600859
860 def HasSibling(self, name):
861 """Check if there is a sibling of a given name
862
863 Returns:
864 True if there is an entry with this name in the the same section,
865 else False
866 """
867 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600868
869 def GetSiblingImagePos(self, name):
870 """Return the image position of the given sibling
871
872 Returns:
873 Image position of sibling, or None if the sibling has no position,
874 or False if there is no such sibling
875 """
876 if not self.HasSibling(name):
877 return False
878 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600879
880 @staticmethod
881 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
882 uncomp_size, offset, entry):
883 """Add a new entry to the entries list
884
885 Args:
886 entries: List (of EntryInfo objects) to add to
887 indent: Current indent level to add to list
888 name: Entry name (string)
889 etype: Entry type (string)
890 size: Entry size in bytes (int)
891 image_pos: Position within image in bytes (int)
892 uncomp_size: Uncompressed size if the entry uses compression, else
893 None
894 offset: Entry offset within parent in bytes (int)
895 entry: Entry object
896 """
897 entries.append(EntryInfo(indent, name, etype, size, image_pos,
898 uncomp_size, offset, entry))
899
900 def ListEntries(self, entries, indent):
901 """Add files in this entry to the list of entries
902
903 This can be overridden by subclasses which need different behaviour.
904
905 Args:
906 entries: List (of EntryInfo objects) to add to
907 indent: Current indent level to add to list
908 """
909 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
910 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600911
Simon Glass943bf782021-11-23 21:09:50 -0700912 def ReadData(self, decomp=True, alt_format=None):
Simon Glassf667e452019-07-08 14:25:50 -0600913 """Read the data for an entry from the image
914
915 This is used when the image has been read in and we want to extract the
916 data for a particular entry from that image.
917
918 Args:
919 decomp: True to decompress any compressed data before returning it;
920 False to return the raw, uncompressed data
921
922 Returns:
923 Entry data (bytes)
924 """
925 # Use True here so that we get an uncompressed section to work from,
926 # although compressed sections are currently not supported
Simon Glassf3385a52022-01-29 14:14:15 -0700927 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass2d553c02019-09-25 08:56:21 -0600928 (self.section.GetPath(), self.GetPath()))
Simon Glass943bf782021-11-23 21:09:50 -0700929 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glassa9cd39e2019-07-20 12:24:04 -0600930 return data
Simon Glassd5079332019-07-20 12:23:41 -0600931
Simon Glass943bf782021-11-23 21:09:50 -0700932 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass2d553c02019-09-25 08:56:21 -0600933 """Read the data for a particular child entry
Simon Glass4e185e82019-09-25 08:56:20 -0600934
935 This reads data from the parent and extracts the piece that relates to
936 the given child.
937
938 Args:
Simon Glass943bf782021-11-23 21:09:50 -0700939 child (Entry): Child entry to read data for (must be valid)
940 decomp (bool): True to decompress any compressed data before
941 returning it; False to return the raw, uncompressed data
942 alt_format (str): Alternative format to read in, or None
Simon Glass4e185e82019-09-25 08:56:20 -0600943
944 Returns:
945 Data for the child (bytes)
946 """
947 pass
948
Simon Glassd5079332019-07-20 12:23:41 -0600949 def LoadData(self, decomp=True):
950 data = self.ReadData(decomp)
Simon Glass10f9d002019-07-20 12:23:50 -0600951 self.contents_size = len(data)
Simon Glassd5079332019-07-20 12:23:41 -0600952 self.ProcessContentsUpdate(data)
953 self.Detail('Loaded data size %x' % len(data))
Simon Glassc5ad04b2019-07-20 12:23:46 -0600954
Simon Glass943bf782021-11-23 21:09:50 -0700955 def GetAltFormat(self, data, alt_format):
956 """Read the data for an extry in an alternative format
957
958 Supported formats are list in the documentation for each entry. An
959 example is fdtmap which provides .
960
961 Args:
962 data (bytes): Data to convert (this should have been produced by the
963 entry)
964 alt_format (str): Format to use
965
966 """
967 pass
968
Simon Glassc5ad04b2019-07-20 12:23:46 -0600969 def GetImage(self):
970 """Get the image containing this entry
971
972 Returns:
973 Image object containing this entry
974 """
975 return self.section.GetImage()
Simon Glass10f9d002019-07-20 12:23:50 -0600976
977 def WriteData(self, data, decomp=True):
978 """Write the data to an entry in the image
979
980 This is used when the image has been read in and we want to replace the
981 data for a particular entry in that image.
982
983 The image must be re-packed and written out afterwards.
984
985 Args:
986 data: Data to replace it with
987 decomp: True to compress the data if needed, False if data is
988 already compressed so should be used as is
989
990 Returns:
991 True if the data did not result in a resize of this entry, False if
992 the entry must be resized
993 """
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600994 if self.size is not None:
995 self.contents_size = self.size
996 else:
997 self.contents_size = self.pre_reset_size
Simon Glass10f9d002019-07-20 12:23:50 -0600998 ok = self.ProcessContentsUpdate(data)
999 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glass7210c892019-07-20 12:24:05 -06001000 section_ok = self.section.WriteChildData(self)
1001 return ok and section_ok
1002
1003 def WriteChildData(self, child):
1004 """Handle writing the data in a child entry
1005
1006 This should be called on the child's parent section after the child's
Simon Glass557693e2021-11-23 11:03:44 -07001007 data has been updated. It should update any data structures needed to
1008 validate that the update is successful.
Simon Glass7210c892019-07-20 12:24:05 -06001009
1010 This base-class implementation does nothing, since the base Entry object
1011 does not have any children.
1012
1013 Args:
1014 child: Child Entry that was written
1015
1016 Returns:
1017 True if the section could be updated successfully, False if the
Simon Glass557693e2021-11-23 11:03:44 -07001018 data is such that the section could not update
Simon Glass7210c892019-07-20 12:24:05 -06001019 """
1020 return True
Simon Glasseba1f0c2019-07-20 12:23:55 -06001021
1022 def GetSiblingOrder(self):
1023 """Get the relative order of an entry amoung its siblings
1024
1025 Returns:
1026 'start' if this entry is first among siblings, 'end' if last,
1027 otherwise None
1028 """
1029 entries = list(self.section.GetEntries().values())
1030 if entries:
1031 if self == entries[0]:
1032 return 'start'
1033 elif self == entries[-1]:
1034 return 'end'
1035 return 'middle'
Simon Glass4f9f1052020-07-09 18:39:38 -06001036
1037 def SetAllowMissing(self, allow_missing):
1038 """Set whether a section allows missing external blobs
1039
1040 Args:
1041 allow_missing: True if allowed, False if not allowed
1042 """
1043 # This is meaningless for anything other than sections
1044 pass
Simon Glassb1cca952020-07-09 18:39:40 -06001045
Heiko Thierya89c8f22022-01-06 11:49:41 +01001046 def SetAllowFakeBlob(self, allow_fake):
1047 """Set whether a section allows to create a fake blob
1048
1049 Args:
1050 allow_fake: True if allowed, False if not allowed
1051 """
Simon Glassf4590e02022-01-09 20:13:46 -07001052 self.allow_fake = allow_fake
Heiko Thierya89c8f22022-01-06 11:49:41 +01001053
Simon Glassb1cca952020-07-09 18:39:40 -06001054 def CheckMissing(self, missing_list):
Simon Glass67a05012023-01-07 14:07:15 -07001055 """Check if the entry has missing external blobs
Simon Glassb1cca952020-07-09 18:39:40 -06001056
Simon Glass67a05012023-01-07 14:07:15 -07001057 If there are missing (non-optional) blobs, the entries are added to the
1058 list
Simon Glassb1cca952020-07-09 18:39:40 -06001059
1060 Args:
1061 missing_list: List of Entry objects to be added to
1062 """
Simon Glass67a05012023-01-07 14:07:15 -07001063 if self.missing and not self.optional:
Simon Glassb1cca952020-07-09 18:39:40 -06001064 missing_list.append(self)
Simon Glass87958982020-09-01 05:13:57 -06001065
Simon Glass3817ad42022-03-05 20:19:04 -07001066 def check_fake_fname(self, fname, size=0):
Simon Glass790ba9f2022-01-12 13:10:36 -07001067 """If the file is missing and the entry allows fake blobs, fake it
1068
1069 Sets self.faked to True if faked
1070
1071 Args:
1072 fname (str): Filename to check
Simon Glass3817ad42022-03-05 20:19:04 -07001073 size (int): Size of fake file to create
Simon Glass790ba9f2022-01-12 13:10:36 -07001074
1075 Returns:
Simon Glass9a0a2e92022-03-05 20:19:03 -07001076 tuple:
1077 fname (str): Filename of faked file
1078 bool: True if the blob was faked, False if not
Simon Glass790ba9f2022-01-12 13:10:36 -07001079 """
1080 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass7960a0a2022-08-07 09:46:46 -06001081 if not self.fake_fname:
1082 outfname = os.path.join(self.fake_dir, os.path.basename(fname))
1083 with open(outfname, "wb") as out:
1084 out.truncate(size)
1085 tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
1086 self.fake_fname = outfname
Simon Glass790ba9f2022-01-12 13:10:36 -07001087 self.faked = True
Simon Glass7960a0a2022-08-07 09:46:46 -06001088 return self.fake_fname, True
Simon Glass9a0a2e92022-03-05 20:19:03 -07001089 return fname, False
Simon Glass790ba9f2022-01-12 13:10:36 -07001090
Heiko Thierya89c8f22022-01-06 11:49:41 +01001091 def CheckFakedBlobs(self, faked_blobs_list):
1092 """Check if any entries in this section have faked external blobs
1093
1094 If there are faked blobs, the entries are added to the list
1095
1096 Args:
1097 fake_blobs_list: List of Entry objects to be added to
1098 """
1099 # This is meaningless for anything other than blobs
1100 pass
1101
Simon Glass67a05012023-01-07 14:07:15 -07001102 def CheckOptional(self, optional_list):
1103 """Check if the entry has missing but optional external blobs
1104
1105 If there are missing (optional) blobs, the entries are added to the list
1106
1107 Args:
1108 optional_list (list): List of Entry objects to be added to
1109 """
1110 if self.missing and self.optional:
1111 optional_list.append(self)
1112
Simon Glass87958982020-09-01 05:13:57 -06001113 def GetAllowMissing(self):
1114 """Get whether a section allows missing external blobs
1115
1116 Returns:
1117 True if allowed, False if not allowed
1118 """
1119 return self.allow_missing
Simon Glassb2381432020-09-06 10:39:09 -06001120
Simon Glass4f9ee832022-01-09 20:14:09 -07001121 def record_missing_bintool(self, bintool):
1122 """Record a missing bintool that was needed to produce this entry
1123
1124 Args:
1125 bintool (Bintool): Bintool that was missing
1126 """
Stefan Herbrechtsmeierfacc3782022-08-19 16:25:19 +02001127 if bintool not in self.missing_bintools:
1128 self.missing_bintools.append(bintool)
Simon Glass4f9ee832022-01-09 20:14:09 -07001129
1130 def check_missing_bintools(self, missing_list):
1131 """Check if any entries in this section have missing bintools
1132
1133 If there are missing bintools, these are added to the list
1134
1135 Args:
1136 missing_list: List of Bintool objects to be added to
1137 """
Stefan Herbrechtsmeierfacc3782022-08-19 16:25:19 +02001138 for bintool in self.missing_bintools:
1139 if bintool not in missing_list:
1140 missing_list.append(bintool)
1141
Simon Glass4f9ee832022-01-09 20:14:09 -07001142
Simon Glassb2381432020-09-06 10:39:09 -06001143 def GetHelpTags(self):
1144 """Get the tags use for missing-blob help
1145
1146 Returns:
1147 list of possible tags, most desirable first
1148 """
1149 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glass87c96292020-10-26 17:40:06 -06001150
1151 def CompressData(self, indata):
1152 """Compress data according to the entry's compression method
1153
1154 Args:
1155 indata: Data to compress
1156
1157 Returns:
Stefan Herbrechtsmeier4f463e32022-08-19 16:25:27 +02001158 Compressed data
Simon Glass87c96292020-10-26 17:40:06 -06001159 """
Simon Glass97c3e9a2020-10-26 17:40:15 -06001160 self.uncomp_data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001161 if self.compress != 'none':
1162 self.uncomp_size = len(indata)
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02001163 if self.comp_bintool.is_present():
1164 data = self.comp_bintool.compress(indata)
1165 else:
1166 self.record_missing_bintool(self.comp_bintool)
1167 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001168 else:
1169 data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001170 return data
Simon Glassb35fb172021-03-18 20:25:04 +13001171
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001172 def DecompressData(self, indata):
1173 """Decompress data according to the entry's compression method
1174
1175 Args:
1176 indata: Data to decompress
1177
1178 Returns:
1179 Decompressed data
1180 """
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001181 if self.compress != 'none':
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02001182 if self.comp_bintool.is_present():
1183 data = self.comp_bintool.decompress(indata)
1184 self.uncomp_size = len(data)
1185 else:
1186 self.record_missing_bintool(self.comp_bintool)
1187 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001188 else:
1189 data = indata
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001190 self.uncomp_data = data
1191 return data
1192
Simon Glassb35fb172021-03-18 20:25:04 +13001193 @classmethod
1194 def UseExpanded(cls, node, etype, new_etype):
1195 """Check whether to use an expanded entry type
1196
1197 This is called by Entry.Create() when it finds an expanded version of
1198 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1199 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1200 ignored.
1201
1202 Args:
1203 node: Node object containing information about the entry to
1204 create
1205 etype: Original entry type being used
1206 new_etype: New entry type proposed
1207
1208 Returns:
1209 True to use this entry type, False to use the original one
1210 """
Simon Glassf3385a52022-01-29 14:14:15 -07001211 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glassb35fb172021-03-18 20:25:04 +13001212 (node.path, etype, new_etype))
1213 return True
Simon Glass943bf782021-11-23 21:09:50 -07001214
1215 def CheckAltFormats(self, alt_formats):
1216 """Add any alternative formats supported by this entry type
1217
1218 Args:
1219 alt_formats (dict): Dict to add alt_formats to:
1220 key: Name of alt format
1221 value: Help text
1222 """
1223 pass
Simon Glass386c63c2022-01-09 20:13:50 -07001224
Simon Glassae9a4572022-03-05 20:19:02 -07001225 def AddBintools(self, btools):
Simon Glass386c63c2022-01-09 20:13:50 -07001226 """Add the bintools used by this entry type
1227
1228 Args:
Simon Glassae9a4572022-03-05 20:19:02 -07001229 btools (dict of Bintool):
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001230
1231 Raise:
1232 ValueError if compression algorithm is not supported
Simon Glass386c63c2022-01-09 20:13:50 -07001233 """
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001234 algo = self.compress
1235 if algo != 'none':
Stefan Herbrechtsmeiercd15b642022-08-19 16:25:38 +02001236 algos = ['bzip2', 'gzip', 'lz4', 'lzma', 'lzo', 'xz', 'zstd']
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001237 if algo not in algos:
1238 raise ValueError("Unknown algorithm '%s'" % algo)
Stefan Herbrechtsmeier7b26a462022-08-19 16:25:36 +02001239 names = {'lzma': 'lzma_alone', 'lzo': 'lzop'}
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001240 name = names.get(self.compress, self.compress)
1241 self.comp_bintool = self.AddBintool(btools, name)
Simon Glass386c63c2022-01-09 20:13:50 -07001242
1243 @classmethod
1244 def AddBintool(self, tools, name):
1245 """Add a new bintool to the tools used by this etype
1246
1247 Args:
1248 name: Name of the tool
1249 """
1250 btool = bintool.Bintool.create(name)
1251 tools[name] = btool
1252 return btool
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03001253
1254 def SetUpdateHash(self, update_hash):
1255 """Set whether this entry's "hash" subnode should be updated
1256
1257 Args:
1258 update_hash: True if hash should be updated, False if not
1259 """
1260 self.update_hash = update_hash
Simon Glass81b71c32022-02-08 11:50:00 -07001261
Simon Glass72e423c2022-03-05 20:19:05 -07001262 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass81b71c32022-02-08 11:50:00 -07001263 """Put the contents of a list of entries into a file
1264
1265 Args:
1266 entries (list of Entry): Entries to collect
1267 prefix (str): Filename prefix of file to write to
Simon Glass72e423c2022-03-05 20:19:05 -07001268 fake_size (int): Size of fake file to create if needed
Simon Glass81b71c32022-02-08 11:50:00 -07001269
1270 If any entry does not have contents yet, this function returns False
1271 for the data.
1272
1273 Returns:
1274 Tuple:
Simon Glass6d427c42022-03-05 20:18:58 -07001275 bytes: Concatenated data from all the entries (or None)
1276 str: Filename of file written (or None if no data)
1277 str: Unique portion of filename (or None if no data)
Simon Glass81b71c32022-02-08 11:50:00 -07001278 """
1279 data = b''
1280 for entry in entries:
1281 # First get the input data and put it in a file. If not available,
1282 # try later.
Simon Glass72e423c2022-03-05 20:19:05 -07001283 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass6d427c42022-03-05 20:18:58 -07001284 return None, None, None
Simon Glass81b71c32022-02-08 11:50:00 -07001285 data += entry.GetData()
1286 uniq = self.GetUniqueName()
1287 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1288 tools.write_file(fname, data)
1289 return data, fname, uniq
Simon Glass7960a0a2022-08-07 09:46:46 -06001290
1291 @classmethod
1292 def create_fake_dir(cls):
1293 """Create the directory for fake files"""
1294 cls.fake_dir = tools.get_output_filename('binman-fake')
1295 if not os.path.exists(cls.fake_dir):
1296 os.mkdir(cls.fake_dir)
1297 tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")
Simon Glasscdadada2022-08-13 11:40:44 -06001298
1299 def ensure_props(self):
1300 """Raise an exception if properties are missing
1301
1302 Args:
1303 prop_list (list of str): List of properties to check for
1304
1305 Raises:
1306 ValueError: Any property is missing
1307 """
1308 not_present = []
1309 for prop in self.required_props:
1310 if not prop in self._node.props:
1311 not_present.append(prop)
1312 if not_present:
1313 self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}")
Simon Glassc8c9f312023-01-07 14:07:12 -07001314
1315 def mark_absent(self, msg):
1316 tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg))
1317 self.absent = True
Simon Glass2f80c5e2023-01-07 14:07:14 -07001318
1319 def read_elf_segments(self):
1320 """Read segments from an entry that can generate an ELF file
1321
1322 Returns:
1323 tuple:
1324 list of segments, each:
1325 int: Segment number (0 = first)
1326 int: Start address of segment in memory
1327 bytes: Contents of segment
1328 int: entry address of ELF file
1329 """
1330 return None