blob: 5eacc5fa6c42a45692f87cf7ff7dfe17595a2c54 [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
Samuel Hollandb01ae032023-01-21 17:25:16 -060052 min_size: Minimum entry size in bytes
Simon Glass9a5d3dc2019-10-31 07:43:02 -060053 pre_reset_size: size as it was before ResetForPack(). This allows us to
54 keep track of the size we started with and detect size changes
Simon Glass8287ee82019-07-08 14:25:30 -060055 uncomp_size: Size of uncompressed data in bytes, if the entry is
56 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070057 contents_size: Size of contents in bytes, 0 by default
Simon Glass4eec34c2020-10-26 17:40:10 -060058 align: Entry start offset alignment relative to the start of the
59 containing section, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070060 align_size: Entry size alignment, or None
Simon Glass4eec34c2020-10-26 17:40:10 -060061 align_end: Entry end offset alignment relative to the start of the
62 containing section, or None
Simon Glassf90d9062020-10-26 17:40:09 -060063 pad_before: Number of pad bytes before the contents when it is placed
64 in the containing section, 0 if none. The pad bytes become part of
65 the entry.
66 pad_after: Number of pad bytes after the contents when it is placed in
67 the containing section, 0 if none. The pad bytes become part of
68 the entry.
69 data: Contents of entry (string of bytes). This does not include
Simon Glass97c3e9a2020-10-26 17:40:15 -060070 padding created by pad_before or pad_after. If the entry is
71 compressed, this contains the compressed data.
72 uncomp_data: Original uncompressed data, if this entry is compressed,
73 else None
Simon Glass8287ee82019-07-08 14:25:30 -060074 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassc52c9e72019-07-08 14:25:37 -060075 orig_offset: Original offset value read from node
76 orig_size: Original size value read from node
Simon Glass67a05012023-01-07 14:07:15 -070077 missing: True if this entry is missing its contents. Note that if it is
78 optional, this entry will not appear in the list generated by
79 entry.CheckMissing() since it is considered OK for it to be missing.
Simon Glass87958982020-09-01 05:13:57 -060080 allow_missing: Allow children of this entry to be missing (used by
81 subclasses such as Entry_section)
Heiko Thierya89c8f22022-01-06 11:49:41 +010082 allow_fake: Allow creating a dummy fake file if the blob file is not
83 available. This is mainly used for testing.
Simon Glass87958982020-09-01 05:13:57 -060084 external: True if this entry contains an external binary blob
Simon Glass386c63c2022-01-09 20:13:50 -070085 bintools: Bintools used by this entry (only populated for Image)
Simon Glass4f9ee832022-01-09 20:14:09 -070086 missing_bintools: List of missing bintools for this entry
Alper Nebi Yasakee813c82022-02-09 22:02:35 +030087 update_hash: True if this entry's "hash" subnode should be
88 updated with a hash of the entry contents
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +020089 comp_bintool: Bintools used for compress and decompress data
Simon Glass7960a0a2022-08-07 09:46:46 -060090 fake_fname: Fake filename, if one was created, else None
Simon Glasscdadada2022-08-13 11:40:44 -060091 required_props (dict of str): Properties which must be present. This can
92 be added to by subclasses
Simon Glass3fbba552022-10-20 18:22:46 -060093 elf_fname (str): Filename of the ELF file, if this entry holds an ELF
94 file, or is a binary file produced from an ELF file
95 auto_write_symbols (bool): True to write ELF symbols into this entry's
96 contents
Simon Glassc8c9f312023-01-07 14:07:12 -070097 absent (bool): True if this entry is absent. This can be controlled by
98 the entry itself, allowing it to vanish in certain circumstances.
99 An absent entry is removed during processing so that it does not
100 appear in the map
Simon Glass67a05012023-01-07 14:07:15 -0700101 optional (bool): True if this entry contains an optional external blob
Simon Glass9766f692023-01-11 16:10:16 -0700102 overlap (bool): True if this entry overlaps with others
Simon Glassbf7fd502016-11-25 20:15:51 -0700103 """
Simon Glass7960a0a2022-08-07 09:46:46 -0600104 fake_dir = None
105
Simon Glass3fbba552022-10-20 18:22:46 -0600106 def __init__(self, section, etype, node, name_prefix='',
107 auto_write_symbols=False):
Simon Glass8dbb7442019-08-24 07:22:44 -0600108 # Put this here to allow entry-docs and help to work without libfdt
109 global state
Simon Glass16287932020-04-17 18:09:03 -0600110 from binman import state
Simon Glass8dbb7442019-08-24 07:22:44 -0600111
Simon Glass25ac0e62018-06-01 09:38:14 -0600112 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -0700113 self.etype = etype
114 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -0600115 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -0600116 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700117 self.size = None
Samuel Hollandb01ae032023-01-21 17:25:16 -0600118 self.min_size = 0
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600119 self.pre_reset_size = None
Simon Glass8287ee82019-07-08 14:25:30 -0600120 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -0600121 self.data = None
Simon Glass97c3e9a2020-10-26 17:40:15 -0600122 self.uncomp_data = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700123 self.contents_size = 0
124 self.align = None
125 self.align_size = None
126 self.align_end = None
127 self.pad_before = 0
128 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -0600129 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -0600130 self.image_pos = None
Simon Glass80a66ae2022-03-05 20:18:59 -0700131 self.extend_size = False
Simon Glass8287ee82019-07-08 14:25:30 -0600132 self.compress = 'none'
Simon Glassb1cca952020-07-09 18:39:40 -0600133 self.missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100134 self.faked = False
Simon Glass87958982020-09-01 05:13:57 -0600135 self.external = False
136 self.allow_missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100137 self.allow_fake = False
Simon Glass386c63c2022-01-09 20:13:50 -0700138 self.bintools = {}
Simon Glass4f9ee832022-01-09 20:14:09 -0700139 self.missing_bintools = []
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300140 self.update_hash = True
Simon Glass7960a0a2022-08-07 09:46:46 -0600141 self.fake_fname = None
Simon Glasscdadada2022-08-13 11:40:44 -0600142 self.required_props = []
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +0200143 self.comp_bintool = None
Simon Glass3fbba552022-10-20 18:22:46 -0600144 self.elf_fname = None
145 self.auto_write_symbols = auto_write_symbols
Simon Glassc8c9f312023-01-07 14:07:12 -0700146 self.absent = False
Simon Glass67a05012023-01-07 14:07:15 -0700147 self.optional = False
Simon Glass9766f692023-01-11 16:10:16 -0700148 self.overlap = False
Simon Glassc1157862023-01-11 16:10:17 -0700149 self.elf_base_sym = None
Simon Glass571bc4e2023-01-11 16:10:19 -0700150 self.offset_from_elf = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700151
152 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700153 def FindEntryClass(etype, expanded):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600154 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -0700155
156 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -0600157 node_node: Path name of Node object containing information about
158 the entry to create (used for errors)
159 etype: Entry type to use
Simon Glassb35fb172021-03-18 20:25:04 +1300160 expanded: Use the expanded version of etype
Simon Glassbf7fd502016-11-25 20:15:51 -0700161
162 Returns:
Simon Glassb35fb172021-03-18 20:25:04 +1300163 The entry class object if found, else None if not found and expanded
Simon Glass858436d2021-11-23 21:09:49 -0700164 is True, else a tuple:
165 module name that could not be found
166 exception received
Simon Glassbf7fd502016-11-25 20:15:51 -0700167 """
Simon Glassdd57c132018-06-01 09:38:11 -0600168 # Convert something like 'u-boot@0' to 'u_boot' since we are only
169 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700170 module_name = etype.replace('-', '_')
Simon Glassb35fb172021-03-18 20:25:04 +1300171
Simon Glassdd57c132018-06-01 09:38:11 -0600172 if '@' in module_name:
173 module_name = module_name.split('@')[0]
Simon Glassb35fb172021-03-18 20:25:04 +1300174 if expanded:
175 module_name += '_expanded'
Simon Glassbf7fd502016-11-25 20:15:51 -0700176 module = modules.get(module_name)
177
Simon Glassbadf0ec2018-06-01 09:38:15 -0600178 # Also allow entry-type modules to be brought in from the etype directory.
179
Simon Glassbf7fd502016-11-25 20:15:51 -0700180 # Import the module if we have not already done so.
181 if not module:
182 try:
Simon Glass16287932020-04-17 18:09:03 -0600183 module = importlib.import_module('binman.etype.' + module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600184 except ImportError as e:
Simon Glassb35fb172021-03-18 20:25:04 +1300185 if expanded:
186 return None
Simon Glass858436d2021-11-23 21:09:49 -0700187 return module_name, e
Simon Glassbf7fd502016-11-25 20:15:51 -0700188 modules[module_name] = module
189
Simon Glassfd8d1f72018-07-17 13:25:36 -0600190 # Look up the expected class name
191 return getattr(module, 'Entry_%s' % module_name)
192
193 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700194 def Lookup(node_path, etype, expanded, missing_etype=False):
195 """Look up the entry class for a node.
196
197 Args:
198 node_node (str): Path name of Node object containing information
199 about the entry to create (used for errors)
200 etype (str): Entry type to use
201 expanded (bool): Use the expanded version of etype
202 missing_etype (bool): True to default to a blob etype if the
203 requested etype is not found
204
205 Returns:
206 The entry class object if found, else None if not found and expanded
207 is True
208
209 Raise:
210 ValueError if expanded is False and the class is not found
211 """
212 # Convert something like 'u-boot@0' to 'u_boot' since we are only
213 # interested in the type.
214 cls = Entry.FindEntryClass(etype, expanded)
215 if cls is None:
216 return None
217 elif isinstance(cls, tuple):
218 if missing_etype:
219 cls = Entry.FindEntryClass('blob', False)
220 if isinstance(cls, tuple): # This should not fail
221 module_name, e = cls
222 raise ValueError(
223 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
224 (etype, node_path, module_name, e))
225 return cls
226
227 @staticmethod
228 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600229 """Create a new entry for a node.
230
231 Args:
Simon Glass858436d2021-11-23 21:09:49 -0700232 section (entry_Section): Section object containing this node
233 node (Node): Node object containing information about the entry to
234 create
235 etype (str): Entry type to use, or None to work it out (used for
236 tests)
237 expanded (bool): Use the expanded version of etype
238 missing_etype (bool): True to default to a blob etype if the
239 requested etype is not found
Simon Glassfd8d1f72018-07-17 13:25:36 -0600240
241 Returns:
242 A new Entry object of the correct type (a subclass of Entry)
243 """
244 if not etype:
245 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glass858436d2021-11-23 21:09:49 -0700246 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glassb35fb172021-03-18 20:25:04 +1300247 if obj and expanded:
248 # Check whether to use the expanded entry
249 new_etype = etype + '-expanded'
Simon Glass3d433382021-03-21 18:24:30 +1300250 can_expand = not fdt_util.GetBool(node, 'no-expanded')
251 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glassb35fb172021-03-18 20:25:04 +1300252 etype = new_etype
253 else:
254 obj = None
255 if not obj:
Simon Glass858436d2021-11-23 21:09:49 -0700256 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600257
Simon Glassbf7fd502016-11-25 20:15:51 -0700258 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600259 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700260
261 def ReadNode(self):
262 """Read entry information from the node
263
Simon Glassc6bd6e22019-07-20 12:23:45 -0600264 This must be called as the first thing after the Entry is created.
265
Simon Glassbf7fd502016-11-25 20:15:51 -0700266 This reads all the fields we recognise from the node, ready for use.
267 """
Simon Glasscdadada2022-08-13 11:40:44 -0600268 self.ensure_props()
Simon Glass15a587c2018-07-17 13:25:51 -0600269 if 'pos' in self._node.props:
270 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass80a66ae2022-03-05 20:18:59 -0700271 if 'expand-size' in self._node.props:
272 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glass3ab95982018-08-01 15:22:37 -0600273 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700274 self.size = fdt_util.GetInt(self._node, 'size')
Samuel Hollandb01ae032023-01-21 17:25:16 -0600275 self.min_size = fdt_util.GetInt(self._node, 'min-size', 0)
Simon Glass12bb1a92019-07-20 12:23:51 -0600276 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
277 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
278 if self.GetImage().copy_to_orig:
279 self.orig_offset = self.offset
280 self.orig_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600281
Simon Glassffded752019-07-08 14:25:46 -0600282 # These should not be set in input files, but are set in an FDT map,
283 # which is also read by this code.
284 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
285 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
286
Simon Glassbf7fd502016-11-25 20:15:51 -0700287 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700288 if tools.not_power_of_two(self.align):
Simon Glassbf7fd502016-11-25 20:15:51 -0700289 raise ValueError("Node '%s': Alignment %s must be a power of two" %
290 (self._node.path, self.align))
Simon Glass5ff9fed2021-03-21 18:24:33 +1300291 if self.section and self.align is None:
292 self.align = self.section.align_default
Simon Glassbf7fd502016-11-25 20:15:51 -0700293 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
294 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
295 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700296 if tools.not_power_of_two(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600297 self.Raise("Alignment size %s must be a power of two" %
298 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700299 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600300 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glass80a66ae2022-03-05 20:18:59 -0700301 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassb2381432020-09-06 10:39:09 -0600302 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glass67a05012023-01-07 14:07:15 -0700303 self.optional = fdt_util.GetBool(self._node, 'optional')
Simon Glass9766f692023-01-11 16:10:16 -0700304 self.overlap = fdt_util.GetBool(self._node, 'overlap')
305 if self.overlap:
306 self.required_props += ['offset', 'size']
Simon Glassbf7fd502016-11-25 20:15:51 -0700307
Simon Glass87c96292020-10-26 17:40:06 -0600308 # This is only supported by blobs and sections at present
309 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
Simon Glass571bc4e2023-01-11 16:10:19 -0700310 self.offset_from_elf = fdt_util.GetPhandleNameOffset(self._node,
311 'offset-from-elf')
Simon Glass87c96292020-10-26 17:40:06 -0600312
Simon Glass6c234bf2018-09-14 04:57:18 -0600313 def GetDefaultFilename(self):
314 return None
315
Simon Glassa8adb6d2019-07-20 12:23:28 -0600316 def GetFdts(self):
317 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600318
319 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600320 Empty dict, if this entry is not a .dtb, otherwise:
321 Dict:
322 key: Filename from this entry (without the path)
Simon Glass4bdd3002019-07-20 12:23:31 -0600323 value: Tuple:
Simon Glassadb67bb2021-03-18 20:25:02 +1300324 Entry object for this dtb
Simon Glass4bdd3002019-07-20 12:23:31 -0600325 Filename of file containing this dtb
Simon Glass539aece2018-09-14 04:57:22 -0600326 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600327 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600328
Simon Glassc9ee33a2022-03-05 20:19:00 -0700329 def gen_entries(self):
330 """Allow entries to generate other entries
Simon Glassa01d1a22021-03-18 20:24:52 +1300331
332 Some entries generate subnodes automatically, from which sub-entries
333 are then created. This method allows those to be added to the binman
334 definition for the current image. An entry which implements this method
335 should call state.AddSubnode() to add a subnode and can add properties
336 with state.AddString(), etc.
337
338 An example is 'files', which produces a section containing a list of
339 files.
340 """
Simon Glass0a98b282018-09-14 04:57:28 -0600341 pass
342
Simon Glassa9fad072020-10-26 17:40:17 -0600343 def AddMissingProperties(self, have_image_pos):
344 """Add new properties to the device tree as needed for this entry
345
346 Args:
347 have_image_pos: True if this entry has an image position. This can
348 be False if its parent section is compressed, since compression
349 groups all entries together into a compressed block of data,
350 obscuring the start of each individual child entry
351 """
352 for prop in ['offset', 'size']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600353 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600354 state.AddZeroProp(self._node, prop)
Simon Glassa9fad072020-10-26 17:40:17 -0600355 if have_image_pos and 'image-pos' not in self._node.props:
356 state.AddZeroProp(self._node, 'image-pos')
Simon Glass12bb1a92019-07-20 12:23:51 -0600357 if self.GetImage().allow_repack:
358 if self.orig_offset is not None:
359 state.AddZeroProp(self._node, 'orig-offset', True)
360 if self.orig_size is not None:
361 state.AddZeroProp(self._node, 'orig-size', True)
362
Simon Glass8287ee82019-07-08 14:25:30 -0600363 if self.compress != 'none':
364 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300365
366 if self.update_hash:
367 err = state.CheckAddHashProp(self._node)
368 if err:
369 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600370
371 def SetCalculatedProperties(self):
372 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600373 state.SetInt(self._node, 'offset', self.offset)
374 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600375 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassa9fad072020-10-26 17:40:17 -0600376 if self.image_pos is not None:
Simon Glass08594d42020-11-02 12:55:44 -0700377 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass12bb1a92019-07-20 12:23:51 -0600378 if self.GetImage().allow_repack:
379 if self.orig_offset is not None:
380 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
381 if self.orig_size is not None:
382 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glass8287ee82019-07-08 14:25:30 -0600383 if self.uncomp_size is not None:
384 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300385
386 if self.update_hash:
387 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600388
Simon Glassecab8972018-07-06 10:27:40 -0600389 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600390 """Allow entries to adjust the device tree
391
392 Some entries need to adjust the device tree for their purposes. This
393 may involve adding or deleting properties.
394
395 Returns:
396 True if processing is complete
397 False if processing could not be completed due to a dependency.
398 This will cause the entry to be retried after others have been
399 called
400 """
Simon Glassecab8972018-07-06 10:27:40 -0600401 return True
402
Simon Glassc8d48ef2018-06-01 09:38:21 -0600403 def SetPrefix(self, prefix):
404 """Set the name prefix for a node
405
406 Args:
407 prefix: Prefix to set, or '' to not use a prefix
408 """
409 if prefix:
410 self.name = prefix + self.name
411
Simon Glass5c890232018-07-06 10:27:19 -0600412 def SetContents(self, data):
413 """Set the contents of an entry
414
415 This sets both the data and content_size properties
416
417 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600418 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600419 """
420 self.data = data
421 self.contents_size = len(self.data)
422
423 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600424 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600425
Simon Glassa0dcaf22019-07-08 14:25:35 -0600426 This checks that the new data is the same size as the old. If the size
427 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600428
429 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600430 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600431
432 Raises:
433 ValueError if the new data size is not the same as the old
434 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600435 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600436 new_size = len(data)
Simon Glass61ec04f2019-07-20 12:23:58 -0600437 if state.AllowEntryExpansion() and new_size > self.contents_size:
438 # self.data will indicate the new size needed
439 size_ok = False
440 elif state.AllowEntryContraction() and new_size < self.contents_size:
441 size_ok = False
442
443 # If not allowed to change, try to deal with it or give up
444 if size_ok:
Simon Glassc52c9e72019-07-08 14:25:37 -0600445 if new_size > self.contents_size:
Simon Glass61ec04f2019-07-20 12:23:58 -0600446 self.Raise('Cannot update entry size from %d to %d' %
447 (self.contents_size, new_size))
448
449 # Don't let the data shrink. Pad it if necessary
450 if size_ok and new_size < self.contents_size:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700451 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass61ec04f2019-07-20 12:23:58 -0600452
453 if not size_ok:
Simon Glassf3385a52022-01-29 14:14:15 -0700454 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glassc1aa66e2022-01-29 14:14:04 -0700455 self._node.path, to_hex(self.contents_size),
456 to_hex(new_size)))
Simon Glass5c890232018-07-06 10:27:19 -0600457 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600458 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600459
Simon Glass72e423c2022-03-05 20:19:05 -0700460 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glassbf7fd502016-11-25 20:15:51 -0700461 """Figure out the contents of an entry.
462
Simon Glass72e423c2022-03-05 20:19:05 -0700463 Args:
464 skip_entry (Entry): Entry to skip when obtaining section contents
465 fake_size (int): Size of fake file to create if needed
466
Simon Glassbf7fd502016-11-25 20:15:51 -0700467 Returns:
468 True if the contents were found, False if another call is needed
Simon Glass62ef2f72023-01-11 16:10:14 -0700469 after the other entries are processed, None if there is no contents
Simon Glassbf7fd502016-11-25 20:15:51 -0700470 """
471 # No contents by default: subclasses can implement this
472 return True
473
Simon Glassc52c9e72019-07-08 14:25:37 -0600474 def ResetForPack(self):
475 """Reset offset/size fields so that packing can be done again"""
Simon Glass9f297b02019-07-20 12:23:36 -0600476 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700477 (to_hex(self.offset), to_hex(self.orig_offset),
478 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600479 self.pre_reset_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600480 self.offset = self.orig_offset
481 self.size = self.orig_size
482
Simon Glass3ab95982018-08-01 15:22:37 -0600483 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600484 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700485
486 Most of the time the entries are not fully specified. There may be
487 an alignment but no size. In that case we take the size from the
488 contents of the entry.
489
Simon Glass3ab95982018-08-01 15:22:37 -0600490 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700491
Simon Glass3ab95982018-08-01 15:22:37 -0600492 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700493 entry will be know.
494
495 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600496 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700497
498 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600499 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700500 """
Simon Glass9f297b02019-07-20 12:23:36 -0600501 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700502 (to_hex(self.offset), to_hex(self.size),
Simon Glass9f297b02019-07-20 12:23:36 -0600503 self.contents_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600504 if self.offset is None:
505 if self.offset_unset:
506 self.Raise('No offset set with offset-unset: should another '
507 'entry provide this correct offset?')
Simon Glass571bc4e2023-01-11 16:10:19 -0700508 elif self.offset_from_elf:
509 self.offset = self.lookup_offset()
510 else:
511 self.offset = tools.align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700512 needed = self.pad_before + self.contents_size + self.pad_after
Samuel Hollandb01ae032023-01-21 17:25:16 -0600513 needed = max(needed, self.min_size)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700514 needed = tools.align(needed, self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700515 size = self.size
516 if not size:
517 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600518 new_offset = self.offset + size
Simon Glassc1aa66e2022-01-29 14:14:04 -0700519 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glass3ab95982018-08-01 15:22:37 -0600520 if aligned_offset != new_offset:
521 size = aligned_offset - self.offset
522 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700523
524 if not self.size:
525 self.size = size
526
527 if self.size < needed:
528 self.Raise("Entry contents size is %#x (%d) but entry size is "
529 "%#x (%d)" % (needed, needed, self.size, self.size))
530 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600531 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700532 # conflict with the provided alignment values
Simon Glassc1aa66e2022-01-29 14:14:04 -0700533 if self.size != tools.align(self.size, self.align_size):
Simon Glassbf7fd502016-11-25 20:15:51 -0700534 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
535 (self.size, self.size, self.align_size, self.align_size))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700536 if self.offset != tools.align(self.offset, self.align):
Simon Glass3ab95982018-08-01 15:22:37 -0600537 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
538 (self.offset, self.offset, self.align, self.align))
Simon Glass9f297b02019-07-20 12:23:36 -0600539 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
540 (self.offset, self.size, self.contents_size, new_offset))
Simon Glassbf7fd502016-11-25 20:15:51 -0700541
Simon Glass3ab95982018-08-01 15:22:37 -0600542 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700543
544 def Raise(self, msg):
545 """Convenience function to raise an error referencing a node"""
546 raise ValueError("Node '%s': %s" % (self._node.path, msg))
547
Simon Glass189f2912021-03-21 18:24:31 +1300548 def Info(self, msg):
549 """Convenience function to log info referencing a node"""
550 tag = "Info '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700551 tout.detail('%30s: %s' % (tag, msg))
Simon Glass189f2912021-03-21 18:24:31 +1300552
Simon Glass9f297b02019-07-20 12:23:36 -0600553 def Detail(self, msg):
554 """Convenience function to log detail referencing a node"""
555 tag = "Node '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700556 tout.detail('%30s: %s' % (tag, msg))
Simon Glass9f297b02019-07-20 12:23:36 -0600557
Simon Glass53af22a2018-07-17 13:25:32 -0600558 def GetEntryArgsOrProps(self, props, required=False):
559 """Return the values of a set of properties
560
561 Args:
562 props: List of EntryArg objects
563
564 Raises:
565 ValueError if a property is not found
566 """
567 values = []
568 missing = []
569 for prop in props:
570 python_prop = prop.name.replace('-', '_')
571 if hasattr(self, python_prop):
572 value = getattr(self, python_prop)
573 else:
574 value = None
575 if value is None:
576 value = self.GetArg(prop.name, prop.datatype)
577 if value is None and required:
578 missing.append(prop.name)
579 values.append(value)
580 if missing:
Simon Glass939d1062021-01-06 21:35:16 -0700581 self.GetImage().MissingArgs(self, missing)
Simon Glass53af22a2018-07-17 13:25:32 -0600582 return values
583
Simon Glassbf7fd502016-11-25 20:15:51 -0700584 def GetPath(self):
585 """Get the path of a node
586
587 Returns:
588 Full path of the node for this entry
589 """
590 return self._node.path
591
Simon Glass631f7522021-03-21 18:24:32 +1300592 def GetData(self, required=True):
Simon Glass63e7ba62020-10-26 17:40:16 -0600593 """Get the contents of an entry
594
Simon Glass631f7522021-03-21 18:24:32 +1300595 Args:
596 required: True if the data must be present, False if it is OK to
597 return None
598
Simon Glass63e7ba62020-10-26 17:40:16 -0600599 Returns:
600 bytes content of the entry, excluding any padding. If the entry is
Simon Glass4331d662023-01-11 16:10:13 -0700601 compressed, the compressed data is returned. If the entry data
Simon Glass62ef2f72023-01-11 16:10:14 -0700602 is not yet available, False can be returned. If the entry data
603 is null, then None is returned.
Simon Glass63e7ba62020-10-26 17:40:16 -0600604 """
Simon Glassc1aa66e2022-01-29 14:14:04 -0700605 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glassbf7fd502016-11-25 20:15:51 -0700606 return self.data
607
Simon Glass271a0832020-11-02 12:55:43 -0700608 def GetPaddedData(self, data=None):
609 """Get the data for an entry including any padding
610
611 Gets the entry data and uses its section's pad-byte value to add padding
612 before and after as defined by the pad-before and pad-after properties.
613
614 This does not consider alignment.
615
616 Returns:
617 Contents of the entry along with any pad bytes before and
618 after it (bytes)
619 """
620 if data is None:
621 data = self.GetData()
622 return self.section.GetPaddedDataForEntry(self, data)
623
Simon Glass3ab95982018-08-01 15:22:37 -0600624 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600625 """Get the offsets for siblings
626
627 Some entry types can contain information about the position or size of
628 other entries. An example of this is the Intel Flash Descriptor, which
629 knows where the Intel Management Engine section should go.
630
631 If this entry knows about the position of other entries, it can specify
632 this by returning values here
633
634 Returns:
635 Dict:
636 key: Entry type
637 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600638 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600639 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700640 return {}
641
Simon Glasscf549042019-07-08 13:18:39 -0600642 def SetOffsetSize(self, offset, size):
643 """Set the offset and/or size of an entry
644
645 Args:
646 offset: New offset, or None to leave alone
647 size: New size, or None to leave alone
648 """
649 if offset is not None:
650 self.offset = offset
651 if size is not None:
652 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700653
Simon Glassdbf6be92018-08-01 15:22:42 -0600654 def SetImagePos(self, image_pos):
655 """Set the position in the image
656
657 Args:
658 image_pos: Position of this entry in the image
659 """
660 self.image_pos = image_pos + self.offset
661
Simon Glassbf7fd502016-11-25 20:15:51 -0700662 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600663 """Do any post-packing updates of entry contents
664
665 This function should call ProcessContentsUpdate() to update the entry
666 contents, if necessary, returning its return value here.
667
668 Args:
669 data: Data to set to the contents (bytes)
670
671 Returns:
672 True if the new data size is OK, False if expansion is needed
673
674 Raises:
675 ValueError if the new data size is not the same as the old and
676 state.AllowEntryExpansion() is False
677 """
678 return True
Simon Glass19790632017-11-13 18:55:01 -0700679
Simon Glassf55382b2018-06-01 09:38:13 -0600680 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700681 """Write symbol values into binary files for access at run time
682
683 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600684 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700685 """
Simon Glass3fbba552022-10-20 18:22:46 -0600686 if self.auto_write_symbols:
Simon Glassd2afb9e2022-10-20 18:22:47 -0600687 # Check if we are writing symbols into an ELF file
688 is_elf = self.GetDefaultFilename() == self.elf_fname
689 elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(),
Simon Glassc1157862023-01-11 16:10:17 -0700690 is_elf, self.elf_base_sym)
Simon Glass18546952018-06-01 09:38:16 -0600691
Simon Glass6ddd6112020-10-26 17:40:18 -0600692 def CheckEntries(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600693 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600694
Simon Glass3ab95982018-08-01 15:22:37 -0600695 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600696 than having to be fully inside their section). Sub-classes can implement
697 this function and raise if there is a problem.
698 """
699 pass
Simon Glass3b0c3822018-06-01 09:38:20 -0600700
Simon Glass8122f392018-07-17 13:25:28 -0600701 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600702 def GetStr(value):
703 if value is None:
704 return '<none> '
705 return '%08x' % value
706
707 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600708 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600709 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
710 Entry.GetStr(offset), Entry.GetStr(size),
711 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600712
Simon Glass3b0c3822018-06-01 09:38:20 -0600713 def WriteMap(self, fd, indent):
714 """Write a map of the entry to a .map file
715
716 Args:
717 fd: File to write the map to
718 indent: Curent indent level of map (0=none, 1=one level, etc.)
719 """
Simon Glass1be70d22018-07-17 13:25:49 -0600720 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
721 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600722
Simon Glassd626e822022-08-13 11:40:50 -0600723 # pylint: disable=assignment-from-none
Simon Glass11e36cc2018-07-17 13:25:38 -0600724 def GetEntries(self):
725 """Return a list of entries contained by this entry
726
727 Returns:
728 List of entries, or None if none. A normal entry has no entries
729 within it so will return None
730 """
731 return None
732
Simon Glassd626e822022-08-13 11:40:50 -0600733 def FindEntryByNode(self, find_node):
734 """Find a node in an entry, searching all subentries
735
736 This does a recursive search.
737
738 Args:
739 find_node (fdt.Node): Node to find
740
741 Returns:
742 Entry: entry, if found, else None
743 """
744 entries = self.GetEntries()
745 if entries:
746 for entry in entries.values():
747 if entry._node == find_node:
748 return entry
749 found = entry.FindEntryByNode(find_node)
750 if found:
751 return found
752
753 return None
754
Simon Glass53af22a2018-07-17 13:25:32 -0600755 def GetArg(self, name, datatype=str):
756 """Get the value of an entry argument or device-tree-node property
757
758 Some node properties can be provided as arguments to binman. First check
759 the entry arguments, and fall back to the device tree if not found
760
761 Args:
762 name: Argument name
763 datatype: Data type (str or int)
764
765 Returns:
766 Value of argument as a string or int, or None if no value
767
768 Raises:
769 ValueError if the argument cannot be converted to in
770 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600771 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600772 if value is not None:
773 if datatype == int:
774 try:
775 value = int(value)
776 except ValueError:
777 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
778 (name, value))
779 elif datatype == str:
780 pass
781 else:
782 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
783 datatype)
784 else:
785 value = fdt_util.GetDatatype(self._node, name, datatype)
786 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600787
788 @staticmethod
789 def WriteDocs(modules, test_missing=None):
790 """Write out documentation about the various entry types to stdout
791
792 Args:
793 modules: List of modules to include
794 test_missing: Used for testing. This is a module to report
795 as missing
796 """
797 print('''Binman Entry Documentation
798===========================
799
800This file describes the entry types supported by binman. These entry types can
801be placed in an image one by one to build up a final firmware image. It is
802fairly easy to create new entry types. Just add a new file to the 'etype'
803directory. You can use the existing entries as examples.
804
805Note that some entries are subclasses of others, using and extending their
806features to produce new behaviours.
807
808
809''')
810 modules = sorted(modules)
811
812 # Don't show the test entry
813 if '_testing' in modules:
814 modules.remove('_testing')
815 missing = []
816 for name in modules:
Simon Glassb35fb172021-03-18 20:25:04 +1300817 module = Entry.Lookup('WriteDocs', name, False)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600818 docs = getattr(module, '__doc__')
819 if test_missing == name:
820 docs = None
821 if docs:
822 lines = docs.splitlines()
823 first_line = lines[0]
824 rest = [line[4:] for line in lines[1:]]
825 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glass228c9b82022-08-07 16:33:25 -0600826
827 # Create a reference for use by rST docs
828 ref_name = f'etype_{module.__name__[6:]}'.lower()
829 print('.. _%s:' % ref_name)
830 print()
Simon Glassfd8d1f72018-07-17 13:25:36 -0600831 print(hdr)
832 print('-' * len(hdr))
833 print('\n'.join(rest))
834 print()
835 print()
836 else:
837 missing.append(name)
838
839 if missing:
840 raise ValueError('Documentation is missing for modules: %s' %
841 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600842
843 def GetUniqueName(self):
844 """Get a unique name for a node
845
846 Returns:
847 String containing a unique name for a node, consisting of the name
848 of all ancestors (starting from within the 'binman' node) separated
849 by a dot ('.'). This can be useful for generating unique filesnames
850 in the output directory.
851 """
852 name = self.name
853 node = self._node
854 while node.parent:
855 node = node.parent
Alper Nebi Yasak67bf2c82022-03-27 18:31:44 +0300856 if node.name in ('binman', '/'):
Simon Glassa326b492018-09-14 04:57:11 -0600857 break
858 name = '%s.%s' % (node.name, name)
859 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600860
Simon Glass80a66ae2022-03-05 20:18:59 -0700861 def extend_to_limit(self, limit):
862 """Extend an entry so that it ends at the given offset limit"""
Simon Glassba64a0b2018-09-14 04:57:29 -0600863 if self.offset + self.size < limit:
864 self.size = limit - self.offset
865 # Request the contents again, since changing the size requires that
866 # the data grows. This should not fail, but check it to be sure.
867 if not self.ObtainContents():
868 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600869
870 def HasSibling(self, name):
871 """Check if there is a sibling of a given name
872
873 Returns:
874 True if there is an entry with this name in the the same section,
875 else False
876 """
877 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600878
879 def GetSiblingImagePos(self, name):
880 """Return the image position of the given sibling
881
882 Returns:
883 Image position of sibling, or None if the sibling has no position,
884 or False if there is no such sibling
885 """
886 if not self.HasSibling(name):
887 return False
888 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600889
890 @staticmethod
891 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
892 uncomp_size, offset, entry):
893 """Add a new entry to the entries list
894
895 Args:
896 entries: List (of EntryInfo objects) to add to
897 indent: Current indent level to add to list
898 name: Entry name (string)
899 etype: Entry type (string)
900 size: Entry size in bytes (int)
901 image_pos: Position within image in bytes (int)
902 uncomp_size: Uncompressed size if the entry uses compression, else
903 None
904 offset: Entry offset within parent in bytes (int)
905 entry: Entry object
906 """
907 entries.append(EntryInfo(indent, name, etype, size, image_pos,
908 uncomp_size, offset, entry))
909
910 def ListEntries(self, entries, indent):
911 """Add files in this entry to the list of entries
912
913 This can be overridden by subclasses which need different behaviour.
914
915 Args:
916 entries: List (of EntryInfo objects) to add to
917 indent: Current indent level to add to list
918 """
919 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
920 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600921
Simon Glass943bf782021-11-23 21:09:50 -0700922 def ReadData(self, decomp=True, alt_format=None):
Simon Glassf667e452019-07-08 14:25:50 -0600923 """Read the data for an entry from the image
924
925 This is used when the image has been read in and we want to extract the
926 data for a particular entry from that image.
927
928 Args:
929 decomp: True to decompress any compressed data before returning it;
930 False to return the raw, uncompressed data
931
932 Returns:
933 Entry data (bytes)
934 """
935 # Use True here so that we get an uncompressed section to work from,
936 # although compressed sections are currently not supported
Simon Glassf3385a52022-01-29 14:14:15 -0700937 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass2d553c02019-09-25 08:56:21 -0600938 (self.section.GetPath(), self.GetPath()))
Simon Glass943bf782021-11-23 21:09:50 -0700939 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glassa9cd39e2019-07-20 12:24:04 -0600940 return data
Simon Glassd5079332019-07-20 12:23:41 -0600941
Simon Glass943bf782021-11-23 21:09:50 -0700942 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass2d553c02019-09-25 08:56:21 -0600943 """Read the data for a particular child entry
Simon Glass4e185e82019-09-25 08:56:20 -0600944
945 This reads data from the parent and extracts the piece that relates to
946 the given child.
947
948 Args:
Simon Glass943bf782021-11-23 21:09:50 -0700949 child (Entry): Child entry to read data for (must be valid)
950 decomp (bool): True to decompress any compressed data before
951 returning it; False to return the raw, uncompressed data
952 alt_format (str): Alternative format to read in, or None
Simon Glass4e185e82019-09-25 08:56:20 -0600953
954 Returns:
955 Data for the child (bytes)
956 """
957 pass
958
Simon Glassd5079332019-07-20 12:23:41 -0600959 def LoadData(self, decomp=True):
960 data = self.ReadData(decomp)
Simon Glass10f9d002019-07-20 12:23:50 -0600961 self.contents_size = len(data)
Simon Glassd5079332019-07-20 12:23:41 -0600962 self.ProcessContentsUpdate(data)
963 self.Detail('Loaded data size %x' % len(data))
Simon Glassc5ad04b2019-07-20 12:23:46 -0600964
Simon Glass943bf782021-11-23 21:09:50 -0700965 def GetAltFormat(self, data, alt_format):
966 """Read the data for an extry in an alternative format
967
968 Supported formats are list in the documentation for each entry. An
969 example is fdtmap which provides .
970
971 Args:
972 data (bytes): Data to convert (this should have been produced by the
973 entry)
974 alt_format (str): Format to use
975
976 """
977 pass
978
Simon Glassc5ad04b2019-07-20 12:23:46 -0600979 def GetImage(self):
980 """Get the image containing this entry
981
982 Returns:
983 Image object containing this entry
984 """
985 return self.section.GetImage()
Simon Glass10f9d002019-07-20 12:23:50 -0600986
987 def WriteData(self, data, decomp=True):
988 """Write the data to an entry in the image
989
990 This is used when the image has been read in and we want to replace the
991 data for a particular entry in that image.
992
993 The image must be re-packed and written out afterwards.
994
995 Args:
996 data: Data to replace it with
997 decomp: True to compress the data if needed, False if data is
998 already compressed so should be used as is
999
1000 Returns:
1001 True if the data did not result in a resize of this entry, False if
1002 the entry must be resized
1003 """
Simon Glass9a5d3dc2019-10-31 07:43:02 -06001004 if self.size is not None:
1005 self.contents_size = self.size
1006 else:
1007 self.contents_size = self.pre_reset_size
Simon Glass10f9d002019-07-20 12:23:50 -06001008 ok = self.ProcessContentsUpdate(data)
1009 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glass7210c892019-07-20 12:24:05 -06001010 section_ok = self.section.WriteChildData(self)
1011 return ok and section_ok
1012
1013 def WriteChildData(self, child):
1014 """Handle writing the data in a child entry
1015
1016 This should be called on the child's parent section after the child's
Simon Glass557693e2021-11-23 11:03:44 -07001017 data has been updated. It should update any data structures needed to
1018 validate that the update is successful.
Simon Glass7210c892019-07-20 12:24:05 -06001019
1020 This base-class implementation does nothing, since the base Entry object
1021 does not have any children.
1022
1023 Args:
1024 child: Child Entry that was written
1025
1026 Returns:
1027 True if the section could be updated successfully, False if the
Simon Glass557693e2021-11-23 11:03:44 -07001028 data is such that the section could not update
Simon Glass7210c892019-07-20 12:24:05 -06001029 """
1030 return True
Simon Glasseba1f0c2019-07-20 12:23:55 -06001031
1032 def GetSiblingOrder(self):
1033 """Get the relative order of an entry amoung its siblings
1034
1035 Returns:
1036 'start' if this entry is first among siblings, 'end' if last,
1037 otherwise None
1038 """
1039 entries = list(self.section.GetEntries().values())
1040 if entries:
1041 if self == entries[0]:
1042 return 'start'
1043 elif self == entries[-1]:
1044 return 'end'
1045 return 'middle'
Simon Glass4f9f1052020-07-09 18:39:38 -06001046
1047 def SetAllowMissing(self, allow_missing):
1048 """Set whether a section allows missing external blobs
1049
1050 Args:
1051 allow_missing: True if allowed, False if not allowed
1052 """
1053 # This is meaningless for anything other than sections
1054 pass
Simon Glassb1cca952020-07-09 18:39:40 -06001055
Heiko Thierya89c8f22022-01-06 11:49:41 +01001056 def SetAllowFakeBlob(self, allow_fake):
1057 """Set whether a section allows to create a fake blob
1058
1059 Args:
1060 allow_fake: True if allowed, False if not allowed
1061 """
Simon Glassf4590e02022-01-09 20:13:46 -07001062 self.allow_fake = allow_fake
Heiko Thierya89c8f22022-01-06 11:49:41 +01001063
Simon Glassb1cca952020-07-09 18:39:40 -06001064 def CheckMissing(self, missing_list):
Simon Glass67a05012023-01-07 14:07:15 -07001065 """Check if the entry has missing external blobs
Simon Glassb1cca952020-07-09 18:39:40 -06001066
Simon Glass67a05012023-01-07 14:07:15 -07001067 If there are missing (non-optional) blobs, the entries are added to the
1068 list
Simon Glassb1cca952020-07-09 18:39:40 -06001069
1070 Args:
1071 missing_list: List of Entry objects to be added to
1072 """
Simon Glass67a05012023-01-07 14:07:15 -07001073 if self.missing and not self.optional:
Simon Glassb1cca952020-07-09 18:39:40 -06001074 missing_list.append(self)
Simon Glass87958982020-09-01 05:13:57 -06001075
Simon Glass3817ad42022-03-05 20:19:04 -07001076 def check_fake_fname(self, fname, size=0):
Simon Glass790ba9f2022-01-12 13:10:36 -07001077 """If the file is missing and the entry allows fake blobs, fake it
1078
1079 Sets self.faked to True if faked
1080
1081 Args:
1082 fname (str): Filename to check
Simon Glass3817ad42022-03-05 20:19:04 -07001083 size (int): Size of fake file to create
Simon Glass790ba9f2022-01-12 13:10:36 -07001084
1085 Returns:
Simon Glass9a0a2e92022-03-05 20:19:03 -07001086 tuple:
1087 fname (str): Filename of faked file
1088 bool: True if the blob was faked, False if not
Simon Glass790ba9f2022-01-12 13:10:36 -07001089 """
1090 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass7960a0a2022-08-07 09:46:46 -06001091 if not self.fake_fname:
1092 outfname = os.path.join(self.fake_dir, os.path.basename(fname))
1093 with open(outfname, "wb") as out:
1094 out.truncate(size)
1095 tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
1096 self.fake_fname = outfname
Simon Glass790ba9f2022-01-12 13:10:36 -07001097 self.faked = True
Simon Glass7960a0a2022-08-07 09:46:46 -06001098 return self.fake_fname, True
Simon Glass9a0a2e92022-03-05 20:19:03 -07001099 return fname, False
Simon Glass790ba9f2022-01-12 13:10:36 -07001100
Heiko Thierya89c8f22022-01-06 11:49:41 +01001101 def CheckFakedBlobs(self, faked_blobs_list):
1102 """Check if any entries in this section have faked external blobs
1103
1104 If there are faked blobs, the entries are added to the list
1105
1106 Args:
1107 fake_blobs_list: List of Entry objects to be added to
1108 """
1109 # This is meaningless for anything other than blobs
1110 pass
1111
Simon Glass67a05012023-01-07 14:07:15 -07001112 def CheckOptional(self, optional_list):
1113 """Check if the entry has missing but optional external blobs
1114
1115 If there are missing (optional) blobs, the entries are added to the list
1116
1117 Args:
1118 optional_list (list): List of Entry objects to be added to
1119 """
1120 if self.missing and self.optional:
1121 optional_list.append(self)
1122
Simon Glass87958982020-09-01 05:13:57 -06001123 def GetAllowMissing(self):
1124 """Get whether a section allows missing external blobs
1125
1126 Returns:
1127 True if allowed, False if not allowed
1128 """
1129 return self.allow_missing
Simon Glassb2381432020-09-06 10:39:09 -06001130
Simon Glass4f9ee832022-01-09 20:14:09 -07001131 def record_missing_bintool(self, bintool):
1132 """Record a missing bintool that was needed to produce this entry
1133
1134 Args:
1135 bintool (Bintool): Bintool that was missing
1136 """
Stefan Herbrechtsmeierfacc3782022-08-19 16:25:19 +02001137 if bintool not in self.missing_bintools:
1138 self.missing_bintools.append(bintool)
Simon Glass4f9ee832022-01-09 20:14:09 -07001139
1140 def check_missing_bintools(self, missing_list):
1141 """Check if any entries in this section have missing bintools
1142
1143 If there are missing bintools, these are added to the list
1144
1145 Args:
1146 missing_list: List of Bintool objects to be added to
1147 """
Stefan Herbrechtsmeierfacc3782022-08-19 16:25:19 +02001148 for bintool in self.missing_bintools:
1149 if bintool not in missing_list:
1150 missing_list.append(bintool)
1151
Simon Glass4f9ee832022-01-09 20:14:09 -07001152
Simon Glassb2381432020-09-06 10:39:09 -06001153 def GetHelpTags(self):
1154 """Get the tags use for missing-blob help
1155
1156 Returns:
1157 list of possible tags, most desirable first
1158 """
1159 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glass87c96292020-10-26 17:40:06 -06001160
1161 def CompressData(self, indata):
1162 """Compress data according to the entry's compression method
1163
1164 Args:
1165 indata: Data to compress
1166
1167 Returns:
Stefan Herbrechtsmeier4f463e32022-08-19 16:25:27 +02001168 Compressed data
Simon Glass87c96292020-10-26 17:40:06 -06001169 """
Simon Glass97c3e9a2020-10-26 17:40:15 -06001170 self.uncomp_data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001171 if self.compress != 'none':
1172 self.uncomp_size = len(indata)
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02001173 if self.comp_bintool.is_present():
1174 data = self.comp_bintool.compress(indata)
1175 else:
1176 self.record_missing_bintool(self.comp_bintool)
1177 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001178 else:
1179 data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001180 return data
Simon Glassb35fb172021-03-18 20:25:04 +13001181
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001182 def DecompressData(self, indata):
1183 """Decompress data according to the entry's compression method
1184
1185 Args:
1186 indata: Data to decompress
1187
1188 Returns:
1189 Decompressed data
1190 """
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001191 if self.compress != 'none':
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02001192 if self.comp_bintool.is_present():
1193 data = self.comp_bintool.decompress(indata)
1194 self.uncomp_size = len(data)
1195 else:
1196 self.record_missing_bintool(self.comp_bintool)
1197 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001198 else:
1199 data = indata
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001200 self.uncomp_data = data
1201 return data
1202
Simon Glassb35fb172021-03-18 20:25:04 +13001203 @classmethod
1204 def UseExpanded(cls, node, etype, new_etype):
1205 """Check whether to use an expanded entry type
1206
1207 This is called by Entry.Create() when it finds an expanded version of
1208 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1209 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1210 ignored.
1211
1212 Args:
1213 node: Node object containing information about the entry to
1214 create
1215 etype: Original entry type being used
1216 new_etype: New entry type proposed
1217
1218 Returns:
1219 True to use this entry type, False to use the original one
1220 """
Simon Glassf3385a52022-01-29 14:14:15 -07001221 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glassb35fb172021-03-18 20:25:04 +13001222 (node.path, etype, new_etype))
1223 return True
Simon Glass943bf782021-11-23 21:09:50 -07001224
1225 def CheckAltFormats(self, alt_formats):
1226 """Add any alternative formats supported by this entry type
1227
1228 Args:
1229 alt_formats (dict): Dict to add alt_formats to:
1230 key: Name of alt format
1231 value: Help text
1232 """
1233 pass
Simon Glass386c63c2022-01-09 20:13:50 -07001234
Simon Glassae9a4572022-03-05 20:19:02 -07001235 def AddBintools(self, btools):
Simon Glass386c63c2022-01-09 20:13:50 -07001236 """Add the bintools used by this entry type
1237
1238 Args:
Simon Glassae9a4572022-03-05 20:19:02 -07001239 btools (dict of Bintool):
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001240
1241 Raise:
1242 ValueError if compression algorithm is not supported
Simon Glass386c63c2022-01-09 20:13:50 -07001243 """
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001244 algo = self.compress
1245 if algo != 'none':
Stefan Herbrechtsmeiercd15b642022-08-19 16:25:38 +02001246 algos = ['bzip2', 'gzip', 'lz4', 'lzma', 'lzo', 'xz', 'zstd']
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001247 if algo not in algos:
1248 raise ValueError("Unknown algorithm '%s'" % algo)
Stefan Herbrechtsmeier7b26a462022-08-19 16:25:36 +02001249 names = {'lzma': 'lzma_alone', 'lzo': 'lzop'}
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001250 name = names.get(self.compress, self.compress)
1251 self.comp_bintool = self.AddBintool(btools, name)
Simon Glass386c63c2022-01-09 20:13:50 -07001252
1253 @classmethod
1254 def AddBintool(self, tools, name):
1255 """Add a new bintool to the tools used by this etype
1256
1257 Args:
1258 name: Name of the tool
1259 """
1260 btool = bintool.Bintool.create(name)
1261 tools[name] = btool
1262 return btool
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03001263
1264 def SetUpdateHash(self, update_hash):
1265 """Set whether this entry's "hash" subnode should be updated
1266
1267 Args:
1268 update_hash: True if hash should be updated, False if not
1269 """
1270 self.update_hash = update_hash
Simon Glass81b71c32022-02-08 11:50:00 -07001271
Simon Glass72e423c2022-03-05 20:19:05 -07001272 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass81b71c32022-02-08 11:50:00 -07001273 """Put the contents of a list of entries into a file
1274
1275 Args:
1276 entries (list of Entry): Entries to collect
1277 prefix (str): Filename prefix of file to write to
Simon Glass72e423c2022-03-05 20:19:05 -07001278 fake_size (int): Size of fake file to create if needed
Simon Glass81b71c32022-02-08 11:50:00 -07001279
1280 If any entry does not have contents yet, this function returns False
1281 for the data.
1282
1283 Returns:
1284 Tuple:
Simon Glass6d427c42022-03-05 20:18:58 -07001285 bytes: Concatenated data from all the entries (or None)
1286 str: Filename of file written (or None if no data)
1287 str: Unique portion of filename (or None if no data)
Simon Glass81b71c32022-02-08 11:50:00 -07001288 """
1289 data = b''
1290 for entry in entries:
1291 # First get the input data and put it in a file. If not available,
1292 # try later.
Simon Glass72e423c2022-03-05 20:19:05 -07001293 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass6d427c42022-03-05 20:18:58 -07001294 return None, None, None
Simon Glass81b71c32022-02-08 11:50:00 -07001295 data += entry.GetData()
1296 uniq = self.GetUniqueName()
1297 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1298 tools.write_file(fname, data)
1299 return data, fname, uniq
Simon Glass7960a0a2022-08-07 09:46:46 -06001300
1301 @classmethod
1302 def create_fake_dir(cls):
1303 """Create the directory for fake files"""
1304 cls.fake_dir = tools.get_output_filename('binman-fake')
1305 if not os.path.exists(cls.fake_dir):
1306 os.mkdir(cls.fake_dir)
1307 tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")
Simon Glasscdadada2022-08-13 11:40:44 -06001308
1309 def ensure_props(self):
1310 """Raise an exception if properties are missing
1311
1312 Args:
1313 prop_list (list of str): List of properties to check for
1314
1315 Raises:
1316 ValueError: Any property is missing
1317 """
1318 not_present = []
1319 for prop in self.required_props:
1320 if not prop in self._node.props:
1321 not_present.append(prop)
1322 if not_present:
1323 self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}")
Simon Glassc8c9f312023-01-07 14:07:12 -07001324
1325 def mark_absent(self, msg):
1326 tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg))
1327 self.absent = True
Simon Glass2f80c5e2023-01-07 14:07:14 -07001328
1329 def read_elf_segments(self):
1330 """Read segments from an entry that can generate an ELF file
1331
1332 Returns:
1333 tuple:
1334 list of segments, each:
1335 int: Segment number (0 = first)
1336 int: Start address of segment in memory
1337 bytes: Contents of segment
1338 int: entry address of ELF file
1339 """
1340 return None
Simon Glass571bc4e2023-01-11 16:10:19 -07001341
1342 def lookup_offset(self):
1343 node, sym_name, offset = self.offset_from_elf
1344 entry = self.section.FindEntryByNode(node)
1345 if not entry:
1346 self.Raise("Cannot find entry for node '%s'" % node.name)
1347 if not entry.elf_fname:
1348 entry.Raise("Need elf-fname property '%s'" % node.name)
1349 val = elf.GetSymbolOffset(entry.elf_fname, sym_name,
1350 entry.elf_base_sym)
1351 return val + offset