blob: 39456906a47719a9c088c03fcd536a98b9797228 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass2574ef62016-11-25 20:15:51 -07002# Copyright (c) 2016 Google, Inc
3#
Simon Glass2574ef62016-11-25 20:15:51 -07004# Base class for all entries
5#
6
Simon Glass91710b32018-07-17 13:25:32 -06007from collections import namedtuple
Simon Glass7ccca832019-10-31 07:42:59 -06008import importlib
Simon Glass691198c2018-06-01 09:38:15 -06009import os
Simon Glass7a602fd2022-01-12 13:10:36 -070010import pathlib
Simon Glass691198c2018-06-01 09:38:15 -060011import sys
Simon Glass7d3e4072022-08-07 09:46:46 -060012import time
Simon Glass29aa7362018-09-14 04:57:19 -060013
Simon Glass4eae9252022-01-09 20:13:50 -070014from binman import bintool
Simon Glass6fc079e2022-10-20 18:22:46 -060015from binman import elf
Simon Glassc585dd42020-04-17 18:09:03 -060016from dtoc import fdt_util
Simon Glass131444f2023-02-23 18:18:04 -070017from u_boot_pylib import tools
18from u_boot_pylib.tools import to_hex, to_hex_size
19from u_boot_pylib import tout
Simon Glass2574ef62016-11-25 20:15:51 -070020
21modules = {}
22
Simon Glass2a0fa982022-02-11 13:23:21 -070023# This is imported if needed
24state = None
Simon Glass91710b32018-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 Glass6b156f82019-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 Glass91710b32018-07-17 13:25:32 -060034
Simon Glass2574ef62016-11-25 20:15:51 -070035class Entry(object):
Simon Glassad5a7712018-06-01 09:38:14 -060036 """An Entry in the section
Simon Glass2574ef62016-11-25 20:15:51 -070037
38 An entry corresponds to a single node in the device-tree description
Simon Glassad5a7712018-06-01 09:38:14 -060039 of the section. Each entry ends up being a part of the final section.
Simon Glass2574ef62016-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 Glass3a9a2b82018-07-17 13:25:28 -060047 section: Section object containing this entry
Simon Glass2574ef62016-11-25 20:15:51 -070048 node: The node that created this entry
Simon Glasse8561af2018-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 Glass2574ef62016-11-25 20:15:51 -070051 size: Entry size in bytes, None if not known
Samuel Hollande2574022023-01-21 17:25:16 -060052 min_size: Minimum entry size in bytes
Simon Glass1fdb4872019-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 Glassaa2fcf92019-07-08 14:25:30 -060055 uncomp_size: Size of uncompressed data in bytes, if the entry is
56 compressed, else None
Simon Glass2574ef62016-11-25 20:15:51 -070057 contents_size: Size of contents in bytes, 0 by default
Simon Glassafb9caa2020-10-26 17:40:10 -060058 align: Entry start offset alignment relative to the start of the
59 containing section, or None
Simon Glass2574ef62016-11-25 20:15:51 -070060 align_size: Entry size alignment, or None
Simon Glassafb9caa2020-10-26 17:40:10 -060061 align_end: Entry end offset alignment relative to the start of the
62 containing section, or None
Simon Glassd12599d2020-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 Glass789b34402020-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 Glassaa2fcf92019-07-08 14:25:30 -060074 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glasse61b6f62019-07-08 14:25:37 -060075 orig_offset: Original offset value read from node
76 orig_size: Original size value read from node
Simon Glass63328f12023-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 Glassb8f90372020-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 Thiery6d451362022-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 Glassb8f90372020-09-01 05:13:57 -060084 external: True if this entry contains an external binary blob
Simon Glass4eae9252022-01-09 20:13:50 -070085 bintools: Bintools used by this entry (only populated for Image)
Simon Glass66152ce2022-01-09 20:14:09 -070086 missing_bintools: List of missing bintools for this entry
Alper Nebi Yasak1e4ffd82022-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 Herbrechtsmeiera6e0b502022-08-19 16:25:30 +020089 comp_bintool: Bintools used for compress and decompress data
Simon Glass7d3e4072022-08-07 09:46:46 -060090 fake_fname: Fake filename, if one was created, else None
Simon Glass0cf5bce2022-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 Glass6fc079e2022-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 Glass1e9e61c2023-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 Glass63328f12023-01-07 14:07:15 -0700101 optional (bool): True if this entry contains an optional external blob
Simon Glassf1ee03b2023-01-11 16:10:16 -0700102 overlap (bool): True if this entry overlaps with others
Simon Glasscda991e2023-02-12 17:11:15 -0700103 preserve (bool): True if this entry should be preserved when updating
104 firmware. This means that it will not be changed by the update.
105 This is just a signal: enforcement of this is up to the updater.
106 This flag does not automatically propagate down to child entries.
Simon Glass49b77e82023-03-02 17:02:44 -0700107 build_done (bool): Indicates that the entry data has been built and does
108 not need to be done again. This is only used with 'binman replace',
109 to stop sections from being rebuilt if their entries have not been
110 replaced
Simon Glass2574ef62016-11-25 20:15:51 -0700111 """
Simon Glass7d3e4072022-08-07 09:46:46 -0600112 fake_dir = None
113
Simon Glass6fc079e2022-10-20 18:22:46 -0600114 def __init__(self, section, etype, node, name_prefix='',
115 auto_write_symbols=False):
Simon Glassb9ba4e02019-08-24 07:22:44 -0600116 # Put this here to allow entry-docs and help to work without libfdt
117 global state
Simon Glassc585dd42020-04-17 18:09:03 -0600118 from binman import state
Simon Glassb9ba4e02019-08-24 07:22:44 -0600119
Simon Glassad5a7712018-06-01 09:38:14 -0600120 self.section = section
Simon Glass2574ef62016-11-25 20:15:51 -0700121 self.etype = etype
122 self._node = node
Simon Glass3b78d532018-06-01 09:38:21 -0600123 self.name = node and (name_prefix + node.name) or 'none'
Simon Glasse8561af2018-08-01 15:22:37 -0600124 self.offset = None
Simon Glass2574ef62016-11-25 20:15:51 -0700125 self.size = None
Samuel Hollande2574022023-01-21 17:25:16 -0600126 self.min_size = 0
Simon Glass1fdb4872019-10-31 07:43:02 -0600127 self.pre_reset_size = None
Simon Glassaa2fcf92019-07-08 14:25:30 -0600128 self.uncomp_size = None
Simon Glass5c350162018-07-17 13:25:47 -0600129 self.data = None
Simon Glass789b34402020-10-26 17:40:15 -0600130 self.uncomp_data = None
Simon Glass2574ef62016-11-25 20:15:51 -0700131 self.contents_size = 0
132 self.align = None
133 self.align_size = None
134 self.align_end = None
135 self.pad_before = 0
136 self.pad_after = 0
Simon Glasse8561af2018-08-01 15:22:37 -0600137 self.offset_unset = False
Simon Glass9dcc8612018-08-01 15:22:42 -0600138 self.image_pos = None
Simon Glassdd156a42022-03-05 20:18:59 -0700139 self.extend_size = False
Simon Glassaa2fcf92019-07-08 14:25:30 -0600140 self.compress = 'none'
Simon Glassa003cd32020-07-09 18:39:40 -0600141 self.missing = False
Heiko Thiery6d451362022-01-06 11:49:41 +0100142 self.faked = False
Simon Glassb8f90372020-09-01 05:13:57 -0600143 self.external = False
144 self.allow_missing = False
Heiko Thiery6d451362022-01-06 11:49:41 +0100145 self.allow_fake = False
Simon Glass4eae9252022-01-09 20:13:50 -0700146 self.bintools = {}
Simon Glass66152ce2022-01-09 20:14:09 -0700147 self.missing_bintools = []
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300148 self.update_hash = True
Simon Glass7d3e4072022-08-07 09:46:46 -0600149 self.fake_fname = None
Simon Glass0cf5bce2022-08-13 11:40:44 -0600150 self.required_props = []
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +0200151 self.comp_bintool = None
Simon Glass6fc079e2022-10-20 18:22:46 -0600152 self.elf_fname = None
153 self.auto_write_symbols = auto_write_symbols
Simon Glass1e9e61c2023-01-07 14:07:12 -0700154 self.absent = False
Simon Glass63328f12023-01-07 14:07:15 -0700155 self.optional = False
Simon Glassf1ee03b2023-01-11 16:10:16 -0700156 self.overlap = False
Simon Glasse0035c92023-01-11 16:10:17 -0700157 self.elf_base_sym = None
Simon Glass49e9c002023-01-11 16:10:19 -0700158 self.offset_from_elf = None
Simon Glasscda991e2023-02-12 17:11:15 -0700159 self.preserve = False
Simon Glass49b77e82023-03-02 17:02:44 -0700160 self.build_done = False
Simon Glass2574ef62016-11-25 20:15:51 -0700161
162 @staticmethod
Simon Glassb9028bc2021-11-23 21:09:49 -0700163 def FindEntryClass(etype, expanded):
Simon Glass969616c2018-07-17 13:25:36 -0600164 """Look up the entry class for a node.
Simon Glass2574ef62016-11-25 20:15:51 -0700165
166 Args:
Simon Glass969616c2018-07-17 13:25:36 -0600167 node_node: Path name of Node object containing information about
168 the entry to create (used for errors)
169 etype: Entry type to use
Simon Glass2f859412021-03-18 20:25:04 +1300170 expanded: Use the expanded version of etype
Simon Glass2574ef62016-11-25 20:15:51 -0700171
172 Returns:
Simon Glass2f859412021-03-18 20:25:04 +1300173 The entry class object if found, else None if not found and expanded
Simon Glassb9028bc2021-11-23 21:09:49 -0700174 is True, else a tuple:
175 module name that could not be found
176 exception received
Simon Glass2574ef62016-11-25 20:15:51 -0700177 """
Simon Glasse76a3e62018-06-01 09:38:11 -0600178 # Convert something like 'u-boot@0' to 'u_boot' since we are only
179 # interested in the type.
Simon Glass2574ef62016-11-25 20:15:51 -0700180 module_name = etype.replace('-', '_')
Simon Glass2f859412021-03-18 20:25:04 +1300181
Simon Glasse76a3e62018-06-01 09:38:11 -0600182 if '@' in module_name:
183 module_name = module_name.split('@')[0]
Simon Glass2f859412021-03-18 20:25:04 +1300184 if expanded:
185 module_name += '_expanded'
Simon Glass2574ef62016-11-25 20:15:51 -0700186 module = modules.get(module_name)
187
Simon Glass691198c2018-06-01 09:38:15 -0600188 # Also allow entry-type modules to be brought in from the etype directory.
189
Simon Glass2574ef62016-11-25 20:15:51 -0700190 # Import the module if we have not already done so.
191 if not module:
192 try:
Simon Glassc585dd42020-04-17 18:09:03 -0600193 module = importlib.import_module('binman.etype.' + module_name)
Simon Glass969616c2018-07-17 13:25:36 -0600194 except ImportError as e:
Simon Glass2f859412021-03-18 20:25:04 +1300195 if expanded:
196 return None
Simon Glassb9028bc2021-11-23 21:09:49 -0700197 return module_name, e
Simon Glass2574ef62016-11-25 20:15:51 -0700198 modules[module_name] = module
199
Simon Glass969616c2018-07-17 13:25:36 -0600200 # Look up the expected class name
201 return getattr(module, 'Entry_%s' % module_name)
202
203 @staticmethod
Simon Glassb9028bc2021-11-23 21:09:49 -0700204 def Lookup(node_path, etype, expanded, missing_etype=False):
205 """Look up the entry class for a node.
206
207 Args:
208 node_node (str): Path name of Node object containing information
209 about the entry to create (used for errors)
210 etype (str): Entry type to use
211 expanded (bool): Use the expanded version of etype
212 missing_etype (bool): True to default to a blob etype if the
213 requested etype is not found
214
215 Returns:
216 The entry class object if found, else None if not found and expanded
217 is True
218
219 Raise:
220 ValueError if expanded is False and the class is not found
221 """
222 # Convert something like 'u-boot@0' to 'u_boot' since we are only
223 # interested in the type.
224 cls = Entry.FindEntryClass(etype, expanded)
225 if cls is None:
226 return None
227 elif isinstance(cls, tuple):
228 if missing_etype:
229 cls = Entry.FindEntryClass('blob', False)
230 if isinstance(cls, tuple): # This should not fail
231 module_name, e = cls
232 raise ValueError(
233 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
234 (etype, node_path, module_name, e))
235 return cls
236
237 @staticmethod
238 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glass969616c2018-07-17 13:25:36 -0600239 """Create a new entry for a node.
240
241 Args:
Simon Glassb9028bc2021-11-23 21:09:49 -0700242 section (entry_Section): Section object containing this node
243 node (Node): Node object containing information about the entry to
244 create
245 etype (str): Entry type to use, or None to work it out (used for
246 tests)
247 expanded (bool): Use the expanded version of etype
248 missing_etype (bool): True to default to a blob etype if the
249 requested etype is not found
Simon Glass969616c2018-07-17 13:25:36 -0600250
251 Returns:
252 A new Entry object of the correct type (a subclass of Entry)
253 """
254 if not etype:
255 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glassb9028bc2021-11-23 21:09:49 -0700256 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glass2f859412021-03-18 20:25:04 +1300257 if obj and expanded:
258 # Check whether to use the expanded entry
259 new_etype = etype + '-expanded'
Simon Glass7098b7f2021-03-21 18:24:30 +1300260 can_expand = not fdt_util.GetBool(node, 'no-expanded')
261 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glass2f859412021-03-18 20:25:04 +1300262 etype = new_etype
263 else:
264 obj = None
265 if not obj:
Simon Glassb9028bc2021-11-23 21:09:49 -0700266 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glass969616c2018-07-17 13:25:36 -0600267
Simon Glass2574ef62016-11-25 20:15:51 -0700268 # Call its constructor to get the object we want.
Simon Glassad5a7712018-06-01 09:38:14 -0600269 return obj(section, etype, node)
Simon Glass2574ef62016-11-25 20:15:51 -0700270
271 def ReadNode(self):
272 """Read entry information from the node
273
Simon Glass2c360cf2019-07-20 12:23:45 -0600274 This must be called as the first thing after the Entry is created.
275
Simon Glass2574ef62016-11-25 20:15:51 -0700276 This reads all the fields we recognise from the node, ready for use.
277 """
Simon Glass0cf5bce2022-08-13 11:40:44 -0600278 self.ensure_props()
Simon Glass24b97442018-07-17 13:25:51 -0600279 if 'pos' in self._node.props:
280 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glassdd156a42022-03-05 20:18:59 -0700281 if 'expand-size' in self._node.props:
282 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glasse8561af2018-08-01 15:22:37 -0600283 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glass2574ef62016-11-25 20:15:51 -0700284 self.size = fdt_util.GetInt(self._node, 'size')
Samuel Hollande2574022023-01-21 17:25:16 -0600285 self.min_size = fdt_util.GetInt(self._node, 'min-size', 0)
Simon Glassfb30e292019-07-20 12:23:51 -0600286 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
287 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
288 if self.GetImage().copy_to_orig:
289 self.orig_offset = self.offset
290 self.orig_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600291
Simon Glassb8424fa2019-07-08 14:25:46 -0600292 # These should not be set in input files, but are set in an FDT map,
293 # which is also read by this code.
294 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
295 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
296
Simon Glass2574ef62016-11-25 20:15:51 -0700297 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glass80025522022-01-29 14:14:04 -0700298 if tools.not_power_of_two(self.align):
Simon Glass2574ef62016-11-25 20:15:51 -0700299 raise ValueError("Node '%s': Alignment %s must be a power of two" %
300 (self._node.path, self.align))
Simon Glassf427c5f2021-03-21 18:24:33 +1300301 if self.section and self.align is None:
302 self.align = self.section.align_default
Simon Glass2574ef62016-11-25 20:15:51 -0700303 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
304 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
305 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glass80025522022-01-29 14:14:04 -0700306 if tools.not_power_of_two(self.align_size):
Simon Glass39dd2152019-07-08 14:25:47 -0600307 self.Raise("Alignment size %s must be a power of two" %
308 self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700309 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glasse8561af2018-08-01 15:22:37 -0600310 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassdd156a42022-03-05 20:18:59 -0700311 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassa820af72020-09-06 10:39:09 -0600312 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glass63328f12023-01-07 14:07:15 -0700313 self.optional = fdt_util.GetBool(self._node, 'optional')
Simon Glassf1ee03b2023-01-11 16:10:16 -0700314 self.overlap = fdt_util.GetBool(self._node, 'overlap')
315 if self.overlap:
316 self.required_props += ['offset', 'size']
Simon Glass2574ef62016-11-25 20:15:51 -0700317
Simon Glassa1301a22020-10-26 17:40:06 -0600318 # This is only supported by blobs and sections at present
319 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
Simon Glass49e9c002023-01-11 16:10:19 -0700320 self.offset_from_elf = fdt_util.GetPhandleNameOffset(self._node,
321 'offset-from-elf')
Simon Glassa1301a22020-10-26 17:40:06 -0600322
Simon Glasscda991e2023-02-12 17:11:15 -0700323 self.preserve = fdt_util.GetBool(self._node, 'preserve')
324
Simon Glass3732ec32018-09-14 04:57:18 -0600325 def GetDefaultFilename(self):
326 return None
327
Simon Glass267112e2019-07-20 12:23:28 -0600328 def GetFdts(self):
329 """Get the device trees used by this entry
Simon Glass0c9d5b52018-09-14 04:57:22 -0600330
331 Returns:
Simon Glass267112e2019-07-20 12:23:28 -0600332 Empty dict, if this entry is not a .dtb, otherwise:
333 Dict:
334 key: Filename from this entry (without the path)
Simon Glass684a4f12019-07-20 12:23:31 -0600335 value: Tuple:
Simon Glass8235dd82021-03-18 20:25:02 +1300336 Entry object for this dtb
Simon Glass684a4f12019-07-20 12:23:31 -0600337 Filename of file containing this dtb
Simon Glass0c9d5b52018-09-14 04:57:22 -0600338 """
Simon Glass267112e2019-07-20 12:23:28 -0600339 return {}
Simon Glass0c9d5b52018-09-14 04:57:22 -0600340
Simon Glassf86ddad2022-03-05 20:19:00 -0700341 def gen_entries(self):
342 """Allow entries to generate other entries
Simon Glassfcb2a7c2021-03-18 20:24:52 +1300343
344 Some entries generate subnodes automatically, from which sub-entries
345 are then created. This method allows those to be added to the binman
346 definition for the current image. An entry which implements this method
347 should call state.AddSubnode() to add a subnode and can add properties
348 with state.AddString(), etc.
349
350 An example is 'files', which produces a section containing a list of
351 files.
352 """
Simon Glassac6328c2018-09-14 04:57:28 -0600353 pass
354
Simon Glassacd6c6e2020-10-26 17:40:17 -0600355 def AddMissingProperties(self, have_image_pos):
356 """Add new properties to the device tree as needed for this entry
357
358 Args:
359 have_image_pos: True if this entry has an image position. This can
360 be False if its parent section is compressed, since compression
361 groups all entries together into a compressed block of data,
362 obscuring the start of each individual child entry
363 """
364 for prop in ['offset', 'size']:
Simon Glasse22f8fa2018-07-06 10:27:41 -0600365 if not prop in self._node.props:
Simon Glassc8135dc2018-09-14 04:57:21 -0600366 state.AddZeroProp(self._node, prop)
Simon Glassacd6c6e2020-10-26 17:40:17 -0600367 if have_image_pos and 'image-pos' not in self._node.props:
368 state.AddZeroProp(self._node, 'image-pos')
Simon Glassfb30e292019-07-20 12:23:51 -0600369 if self.GetImage().allow_repack:
370 if self.orig_offset is not None:
371 state.AddZeroProp(self._node, 'orig-offset', True)
372 if self.orig_size is not None:
373 state.AddZeroProp(self._node, 'orig-size', True)
374
Simon Glassaa2fcf92019-07-08 14:25:30 -0600375 if self.compress != 'none':
376 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300377
378 if self.update_hash:
379 err = state.CheckAddHashProp(self._node)
380 if err:
381 self.Raise(err)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600382
383 def SetCalculatedProperties(self):
384 """Set the value of device-tree properties calculated by binman"""
Simon Glassc8135dc2018-09-14 04:57:21 -0600385 state.SetInt(self._node, 'offset', self.offset)
386 state.SetInt(self._node, 'size', self.size)
Simon Glass39dd2152019-07-08 14:25:47 -0600387 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassacd6c6e2020-10-26 17:40:17 -0600388 if self.image_pos is not None:
Simon Glasseb943b12020-11-02 12:55:44 -0700389 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glassfb30e292019-07-20 12:23:51 -0600390 if self.GetImage().allow_repack:
391 if self.orig_offset is not None:
392 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
393 if self.orig_size is not None:
394 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glassaa2fcf92019-07-08 14:25:30 -0600395 if self.uncomp_size is not None:
396 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300397
398 if self.update_hash:
399 state.CheckSetHashValue(self._node, self.GetData)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600400
Simon Glass92307732018-07-06 10:27:40 -0600401 def ProcessFdt(self, fdt):
Simon Glasse219aa42018-09-14 04:57:24 -0600402 """Allow entries to adjust the device tree
403
404 Some entries need to adjust the device tree for their purposes. This
405 may involve adding or deleting properties.
406
407 Returns:
408 True if processing is complete
409 False if processing could not be completed due to a dependency.
410 This will cause the entry to be retried after others have been
411 called
412 """
Simon Glass92307732018-07-06 10:27:40 -0600413 return True
414
Simon Glass3b78d532018-06-01 09:38:21 -0600415 def SetPrefix(self, prefix):
416 """Set the name prefix for a node
417
418 Args:
419 prefix: Prefix to set, or '' to not use a prefix
420 """
421 if prefix:
422 self.name = prefix + self.name
423
Simon Glass2e1169f2018-07-06 10:27:19 -0600424 def SetContents(self, data):
425 """Set the contents of an entry
426
427 This sets both the data and content_size properties
428
429 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600430 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600431 """
432 self.data = data
433 self.contents_size = len(self.data)
434
435 def ProcessContentsUpdate(self, data):
Simon Glassd17dfea2019-07-08 14:25:33 -0600436 """Update the contents of an entry, after the size is fixed
Simon Glass2e1169f2018-07-06 10:27:19 -0600437
Simon Glassec849852019-07-08 14:25:35 -0600438 This checks that the new data is the same size as the old. If the size
439 has changed, this triggers a re-run of the packing algorithm.
Simon Glass2e1169f2018-07-06 10:27:19 -0600440
441 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600442 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600443
444 Raises:
445 ValueError if the new data size is not the same as the old
446 """
Simon Glassec849852019-07-08 14:25:35 -0600447 size_ok = True
Simon Glasse61b6f62019-07-08 14:25:37 -0600448 new_size = len(data)
Simon Glass9d8ee322019-07-20 12:23:58 -0600449 if state.AllowEntryExpansion() and new_size > self.contents_size:
450 # self.data will indicate the new size needed
451 size_ok = False
452 elif state.AllowEntryContraction() and new_size < self.contents_size:
453 size_ok = False
454
455 # If not allowed to change, try to deal with it or give up
456 if size_ok:
Simon Glasse61b6f62019-07-08 14:25:37 -0600457 if new_size > self.contents_size:
Simon Glass9d8ee322019-07-20 12:23:58 -0600458 self.Raise('Cannot update entry size from %d to %d' %
459 (self.contents_size, new_size))
460
461 # Don't let the data shrink. Pad it if necessary
462 if size_ok and new_size < self.contents_size:
Simon Glass80025522022-01-29 14:14:04 -0700463 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass9d8ee322019-07-20 12:23:58 -0600464
465 if not size_ok:
Simon Glass011f1b32022-01-29 14:14:15 -0700466 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glass80025522022-01-29 14:14:04 -0700467 self._node.path, to_hex(self.contents_size),
468 to_hex(new_size)))
Simon Glass2e1169f2018-07-06 10:27:19 -0600469 self.SetContents(data)
Simon Glassec849852019-07-08 14:25:35 -0600470 return size_ok
Simon Glass2e1169f2018-07-06 10:27:19 -0600471
Simon Glassfc5a1682022-03-05 20:19:05 -0700472 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glass2574ef62016-11-25 20:15:51 -0700473 """Figure out the contents of an entry.
474
Simon Glassfc5a1682022-03-05 20:19:05 -0700475 Args:
476 skip_entry (Entry): Entry to skip when obtaining section contents
477 fake_size (int): Size of fake file to create if needed
478
Simon Glass2574ef62016-11-25 20:15:51 -0700479 Returns:
480 True if the contents were found, False if another call is needed
Simon Glassa4948b22023-01-11 16:10:14 -0700481 after the other entries are processed, None if there is no contents
Simon Glass2574ef62016-11-25 20:15:51 -0700482 """
483 # No contents by default: subclasses can implement this
484 return True
485
Simon Glasse61b6f62019-07-08 14:25:37 -0600486 def ResetForPack(self):
487 """Reset offset/size fields so that packing can be done again"""
Simon Glassb6dff4c2019-07-20 12:23:36 -0600488 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glass80025522022-01-29 14:14:04 -0700489 (to_hex(self.offset), to_hex(self.orig_offset),
490 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass1fdb4872019-10-31 07:43:02 -0600491 self.pre_reset_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600492 self.offset = self.orig_offset
493 self.size = self.orig_size
494
Simon Glasse8561af2018-08-01 15:22:37 -0600495 def Pack(self, offset):
Simon Glassad5a7712018-06-01 09:38:14 -0600496 """Figure out how to pack the entry into the section
Simon Glass2574ef62016-11-25 20:15:51 -0700497
498 Most of the time the entries are not fully specified. There may be
499 an alignment but no size. In that case we take the size from the
500 contents of the entry.
501
Simon Glasse8561af2018-08-01 15:22:37 -0600502 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glass2574ef62016-11-25 20:15:51 -0700503
Simon Glasse8561af2018-08-01 15:22:37 -0600504 Once this function is complete, both the offset and size of the
Simon Glass2574ef62016-11-25 20:15:51 -0700505 entry will be know.
506
507 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600508 Current section offset pointer
Simon Glass2574ef62016-11-25 20:15:51 -0700509
510 Returns:
Simon Glasse8561af2018-08-01 15:22:37 -0600511 New section offset pointer (after this entry)
Simon Glass2574ef62016-11-25 20:15:51 -0700512 """
Simon Glassb6dff4c2019-07-20 12:23:36 -0600513 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glass80025522022-01-29 14:14:04 -0700514 (to_hex(self.offset), to_hex(self.size),
Simon Glassb6dff4c2019-07-20 12:23:36 -0600515 self.contents_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600516 if self.offset is None:
517 if self.offset_unset:
518 self.Raise('No offset set with offset-unset: should another '
519 'entry provide this correct offset?')
Simon Glass49e9c002023-01-11 16:10:19 -0700520 elif self.offset_from_elf:
521 self.offset = self.lookup_offset()
522 else:
523 self.offset = tools.align(offset, self.align)
Simon Glass2574ef62016-11-25 20:15:51 -0700524 needed = self.pad_before + self.contents_size + self.pad_after
Samuel Hollande2574022023-01-21 17:25:16 -0600525 needed = max(needed, self.min_size)
Simon Glass80025522022-01-29 14:14:04 -0700526 needed = tools.align(needed, self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700527 size = self.size
528 if not size:
529 size = needed
Simon Glasse8561af2018-08-01 15:22:37 -0600530 new_offset = self.offset + size
Simon Glass80025522022-01-29 14:14:04 -0700531 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glasse8561af2018-08-01 15:22:37 -0600532 if aligned_offset != new_offset:
533 size = aligned_offset - self.offset
534 new_offset = aligned_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700535
536 if not self.size:
537 self.size = size
538
539 if self.size < needed:
540 self.Raise("Entry contents size is %#x (%d) but entry size is "
541 "%#x (%d)" % (needed, needed, self.size, self.size))
542 # Check that the alignment is correct. It could be wrong if the
Simon Glasse8561af2018-08-01 15:22:37 -0600543 # and offset or size values were provided (i.e. not calculated), but
Simon Glass2574ef62016-11-25 20:15:51 -0700544 # conflict with the provided alignment values
Simon Glass80025522022-01-29 14:14:04 -0700545 if self.size != tools.align(self.size, self.align_size):
Simon Glass2574ef62016-11-25 20:15:51 -0700546 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
547 (self.size, self.size, self.align_size, self.align_size))
Simon Glass80025522022-01-29 14:14:04 -0700548 if self.offset != tools.align(self.offset, self.align):
Simon Glasse8561af2018-08-01 15:22:37 -0600549 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
550 (self.offset, self.offset, self.align, self.align))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600551 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
552 (self.offset, self.size, self.contents_size, new_offset))
Simon Glass2574ef62016-11-25 20:15:51 -0700553
Simon Glasse8561af2018-08-01 15:22:37 -0600554 return new_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700555
556 def Raise(self, msg):
557 """Convenience function to raise an error referencing a node"""
558 raise ValueError("Node '%s': %s" % (self._node.path, msg))
559
Simon Glasse1915782021-03-21 18:24:31 +1300560 def Info(self, msg):
561 """Convenience function to log info referencing a node"""
562 tag = "Info '%s'" % self._node.path
Simon Glass011f1b32022-01-29 14:14:15 -0700563 tout.detail('%30s: %s' % (tag, msg))
Simon Glasse1915782021-03-21 18:24:31 +1300564
Simon Glassb6dff4c2019-07-20 12:23:36 -0600565 def Detail(self, msg):
566 """Convenience function to log detail referencing a node"""
567 tag = "Node '%s'" % self._node.path
Simon Glass011f1b32022-01-29 14:14:15 -0700568 tout.detail('%30s: %s' % (tag, msg))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600569
Simon Glass91710b32018-07-17 13:25:32 -0600570 def GetEntryArgsOrProps(self, props, required=False):
571 """Return the values of a set of properties
572
573 Args:
574 props: List of EntryArg objects
575
576 Raises:
577 ValueError if a property is not found
578 """
579 values = []
580 missing = []
581 for prop in props:
582 python_prop = prop.name.replace('-', '_')
583 if hasattr(self, python_prop):
584 value = getattr(self, python_prop)
585 else:
586 value = None
587 if value is None:
588 value = self.GetArg(prop.name, prop.datatype)
589 if value is None and required:
590 missing.append(prop.name)
591 values.append(value)
592 if missing:
Simon Glass3fb25402021-01-06 21:35:16 -0700593 self.GetImage().MissingArgs(self, missing)
Simon Glass91710b32018-07-17 13:25:32 -0600594 return values
595
Simon Glass2574ef62016-11-25 20:15:51 -0700596 def GetPath(self):
597 """Get the path of a node
598
599 Returns:
600 Full path of the node for this entry
601 """
602 return self._node.path
603
Simon Glass27a7f772021-03-21 18:24:32 +1300604 def GetData(self, required=True):
Simon Glass72eeff12020-10-26 17:40:16 -0600605 """Get the contents of an entry
606
Simon Glass27a7f772021-03-21 18:24:32 +1300607 Args:
608 required: True if the data must be present, False if it is OK to
609 return None
610
Simon Glass72eeff12020-10-26 17:40:16 -0600611 Returns:
612 bytes content of the entry, excluding any padding. If the entry is
Simon Glass02997652023-01-11 16:10:13 -0700613 compressed, the compressed data is returned. If the entry data
Simon Glassa4948b22023-01-11 16:10:14 -0700614 is not yet available, False can be returned. If the entry data
615 is null, then None is returned.
Simon Glass72eeff12020-10-26 17:40:16 -0600616 """
Simon Glass80025522022-01-29 14:14:04 -0700617 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glass2574ef62016-11-25 20:15:51 -0700618 return self.data
619
Simon Glasse17220f2020-11-02 12:55:43 -0700620 def GetPaddedData(self, data=None):
621 """Get the data for an entry including any padding
622
623 Gets the entry data and uses its section's pad-byte value to add padding
624 before and after as defined by the pad-before and pad-after properties.
625
626 This does not consider alignment.
627
628 Returns:
629 Contents of the entry along with any pad bytes before and
630 after it (bytes)
631 """
632 if data is None:
633 data = self.GetData()
634 return self.section.GetPaddedDataForEntry(self, data)
635
Simon Glasse8561af2018-08-01 15:22:37 -0600636 def GetOffsets(self):
Simon Glass224bc662019-07-08 13:18:30 -0600637 """Get the offsets for siblings
638
639 Some entry types can contain information about the position or size of
640 other entries. An example of this is the Intel Flash Descriptor, which
641 knows where the Intel Management Engine section should go.
642
643 If this entry knows about the position of other entries, it can specify
644 this by returning values here
645
646 Returns:
647 Dict:
648 key: Entry type
649 value: List containing position and size of the given entry
Simon Glassed365eb2019-07-08 13:18:39 -0600650 type. Either can be None if not known
Simon Glass224bc662019-07-08 13:18:30 -0600651 """
Simon Glass2574ef62016-11-25 20:15:51 -0700652 return {}
653
Simon Glassed365eb2019-07-08 13:18:39 -0600654 def SetOffsetSize(self, offset, size):
655 """Set the offset and/or size of an entry
656
657 Args:
658 offset: New offset, or None to leave alone
659 size: New size, or None to leave alone
660 """
661 if offset is not None:
662 self.offset = offset
663 if size is not None:
664 self.size = size
Simon Glass2574ef62016-11-25 20:15:51 -0700665
Simon Glass9dcc8612018-08-01 15:22:42 -0600666 def SetImagePos(self, image_pos):
667 """Set the position in the image
668
669 Args:
670 image_pos: Position of this entry in the image
671 """
672 self.image_pos = image_pos + self.offset
673
Simon Glass2574ef62016-11-25 20:15:51 -0700674 def ProcessContents(self):
Simon Glassec849852019-07-08 14:25:35 -0600675 """Do any post-packing updates of entry contents
676
677 This function should call ProcessContentsUpdate() to update the entry
678 contents, if necessary, returning its return value here.
679
680 Args:
681 data: Data to set to the contents (bytes)
682
683 Returns:
684 True if the new data size is OK, False if expansion is needed
685
686 Raises:
687 ValueError if the new data size is not the same as the old and
688 state.AllowEntryExpansion() is False
689 """
690 return True
Simon Glass4ca8e042017-11-13 18:55:01 -0700691
Simon Glass8a6f56e2018-06-01 09:38:13 -0600692 def WriteSymbols(self, section):
Simon Glass4ca8e042017-11-13 18:55:01 -0700693 """Write symbol values into binary files for access at run time
694
695 Args:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600696 section: Section containing the entry
Simon Glass4ca8e042017-11-13 18:55:01 -0700697 """
Simon Glass6fc079e2022-10-20 18:22:46 -0600698 if self.auto_write_symbols:
Simon Glass37f85de2022-10-20 18:22:47 -0600699 # Check if we are writing symbols into an ELF file
700 is_elf = self.GetDefaultFilename() == self.elf_fname
701 elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(),
Simon Glasse0035c92023-01-11 16:10:17 -0700702 is_elf, self.elf_base_sym)
Simon Glassa91e1152018-06-01 09:38:16 -0600703
Simon Glass55f68072020-10-26 17:40:18 -0600704 def CheckEntries(self):
Simon Glasse8561af2018-08-01 15:22:37 -0600705 """Check that the entry offsets are correct
Simon Glassa91e1152018-06-01 09:38:16 -0600706
Simon Glasse8561af2018-08-01 15:22:37 -0600707 This is used for entries which have extra offset requirements (other
Simon Glassa91e1152018-06-01 09:38:16 -0600708 than having to be fully inside their section). Sub-classes can implement
709 this function and raise if there is a problem.
710 """
711 pass
Simon Glass30732662018-06-01 09:38:20 -0600712
Simon Glass3a9a2b82018-07-17 13:25:28 -0600713 @staticmethod
Simon Glasscd817d52018-09-14 04:57:36 -0600714 def GetStr(value):
715 if value is None:
716 return '<none> '
717 return '%08x' % value
718
719 @staticmethod
Simon Glass7eca7922018-07-17 13:25:49 -0600720 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glasscd817d52018-09-14 04:57:36 -0600721 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
722 Entry.GetStr(offset), Entry.GetStr(size),
723 name), file=fd)
Simon Glass3a9a2b82018-07-17 13:25:28 -0600724
Simon Glass30732662018-06-01 09:38:20 -0600725 def WriteMap(self, fd, indent):
726 """Write a map of the entry to a .map file
727
728 Args:
729 fd: File to write the map to
730 indent: Curent indent level of map (0=none, 1=one level, etc.)
731 """
Simon Glass7eca7922018-07-17 13:25:49 -0600732 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
733 self.image_pos)
Simon Glass91710b32018-07-17 13:25:32 -0600734
Simon Glassbd5cd882022-08-13 11:40:50 -0600735 # pylint: disable=assignment-from-none
Simon Glass704784b2018-07-17 13:25:38 -0600736 def GetEntries(self):
737 """Return a list of entries contained by this entry
738
739 Returns:
740 List of entries, or None if none. A normal entry has no entries
741 within it so will return None
742 """
743 return None
744
Simon Glassbd5cd882022-08-13 11:40:50 -0600745 def FindEntryByNode(self, find_node):
746 """Find a node in an entry, searching all subentries
747
748 This does a recursive search.
749
750 Args:
751 find_node (fdt.Node): Node to find
752
753 Returns:
754 Entry: entry, if found, else None
755 """
756 entries = self.GetEntries()
757 if entries:
758 for entry in entries.values():
759 if entry._node == find_node:
760 return entry
761 found = entry.FindEntryByNode(find_node)
762 if found:
763 return found
764
765 return None
766
Simon Glass91710b32018-07-17 13:25:32 -0600767 def GetArg(self, name, datatype=str):
768 """Get the value of an entry argument or device-tree-node property
769
770 Some node properties can be provided as arguments to binman. First check
771 the entry arguments, and fall back to the device tree if not found
772
773 Args:
774 name: Argument name
775 datatype: Data type (str or int)
776
777 Returns:
778 Value of argument as a string or int, or None if no value
779
780 Raises:
781 ValueError if the argument cannot be converted to in
782 """
Simon Glass29aa7362018-09-14 04:57:19 -0600783 value = state.GetEntryArg(name)
Simon Glass91710b32018-07-17 13:25:32 -0600784 if value is not None:
785 if datatype == int:
786 try:
787 value = int(value)
788 except ValueError:
789 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
790 (name, value))
791 elif datatype == str:
792 pass
793 else:
794 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
795 datatype)
796 else:
797 value = fdt_util.GetDatatype(self._node, name, datatype)
798 return value
Simon Glass969616c2018-07-17 13:25:36 -0600799
800 @staticmethod
801 def WriteDocs(modules, test_missing=None):
802 """Write out documentation about the various entry types to stdout
803
804 Args:
805 modules: List of modules to include
806 test_missing: Used for testing. This is a module to report
807 as missing
808 """
809 print('''Binman Entry Documentation
810===========================
811
812This file describes the entry types supported by binman. These entry types can
813be placed in an image one by one to build up a final firmware image. It is
814fairly easy to create new entry types. Just add a new file to the 'etype'
815directory. You can use the existing entries as examples.
816
817Note that some entries are subclasses of others, using and extending their
818features to produce new behaviours.
819
820
821''')
822 modules = sorted(modules)
823
824 # Don't show the test entry
825 if '_testing' in modules:
826 modules.remove('_testing')
827 missing = []
828 for name in modules:
Simon Glass2f859412021-03-18 20:25:04 +1300829 module = Entry.Lookup('WriteDocs', name, False)
Simon Glass969616c2018-07-17 13:25:36 -0600830 docs = getattr(module, '__doc__')
831 if test_missing == name:
832 docs = None
833 if docs:
834 lines = docs.splitlines()
835 first_line = lines[0]
836 rest = [line[4:] for line in lines[1:]]
837 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glassa7c97782022-08-07 16:33:25 -0600838
839 # Create a reference for use by rST docs
840 ref_name = f'etype_{module.__name__[6:]}'.lower()
841 print('.. _%s:' % ref_name)
842 print()
Simon Glass969616c2018-07-17 13:25:36 -0600843 print(hdr)
844 print('-' * len(hdr))
845 print('\n'.join(rest))
846 print()
847 print()
848 else:
849 missing.append(name)
850
851 if missing:
852 raise ValueError('Documentation is missing for modules: %s' %
853 ', '.join(missing))
Simon Glass639505b2018-09-14 04:57:11 -0600854
855 def GetUniqueName(self):
856 """Get a unique name for a node
857
858 Returns:
859 String containing a unique name for a node, consisting of the name
860 of all ancestors (starting from within the 'binman' node) separated
861 by a dot ('.'). This can be useful for generating unique filesnames
862 in the output directory.
863 """
864 name = self.name
865 node = self._node
866 while node.parent:
867 node = node.parent
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +0300868 if node.name in ('binman', '/'):
Simon Glass639505b2018-09-14 04:57:11 -0600869 break
870 name = '%s.%s' % (node.name, name)
871 return name
Simon Glassfa79a812018-09-14 04:57:29 -0600872
Simon Glassdd156a42022-03-05 20:18:59 -0700873 def extend_to_limit(self, limit):
874 """Extend an entry so that it ends at the given offset limit"""
Simon Glassfa79a812018-09-14 04:57:29 -0600875 if self.offset + self.size < limit:
876 self.size = limit - self.offset
877 # Request the contents again, since changing the size requires that
878 # the data grows. This should not fail, but check it to be sure.
879 if not self.ObtainContents():
880 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassc4056b82019-07-08 13:18:38 -0600881
882 def HasSibling(self, name):
883 """Check if there is a sibling of a given name
884
885 Returns:
886 True if there is an entry with this name in the the same section,
887 else False
888 """
889 return name in self.section.GetEntries()
Simon Glasscec34ba2019-07-08 14:25:28 -0600890
891 def GetSiblingImagePos(self, name):
892 """Return the image position of the given sibling
893
894 Returns:
895 Image position of sibling, or None if the sibling has no position,
896 or False if there is no such sibling
897 """
898 if not self.HasSibling(name):
899 return False
900 return self.section.GetEntries()[name].image_pos
Simon Glass6b156f82019-07-08 14:25:43 -0600901
902 @staticmethod
903 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
904 uncomp_size, offset, entry):
905 """Add a new entry to the entries list
906
907 Args:
908 entries: List (of EntryInfo objects) to add to
909 indent: Current indent level to add to list
910 name: Entry name (string)
911 etype: Entry type (string)
912 size: Entry size in bytes (int)
913 image_pos: Position within image in bytes (int)
914 uncomp_size: Uncompressed size if the entry uses compression, else
915 None
916 offset: Entry offset within parent in bytes (int)
917 entry: Entry object
918 """
919 entries.append(EntryInfo(indent, name, etype, size, image_pos,
920 uncomp_size, offset, entry))
921
922 def ListEntries(self, entries, indent):
923 """Add files in this entry to the list of entries
924
925 This can be overridden by subclasses which need different behaviour.
926
927 Args:
928 entries: List (of EntryInfo objects) to add to
929 indent: Current indent level to add to list
930 """
931 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
932 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glass4c613bf2019-07-08 14:25:50 -0600933
Simon Glass637958f2021-11-23 21:09:50 -0700934 def ReadData(self, decomp=True, alt_format=None):
Simon Glass4c613bf2019-07-08 14:25:50 -0600935 """Read the data for an entry from the image
936
937 This is used when the image has been read in and we want to extract the
938 data for a particular entry from that image.
939
940 Args:
941 decomp: True to decompress any compressed data before returning it;
942 False to return the raw, uncompressed data
943
944 Returns:
945 Entry data (bytes)
946 """
947 # Use True here so that we get an uncompressed section to work from,
948 # although compressed sections are currently not supported
Simon Glass011f1b32022-01-29 14:14:15 -0700949 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass4d8151f2019-09-25 08:56:21 -0600950 (self.section.GetPath(), self.GetPath()))
Simon Glass637958f2021-11-23 21:09:50 -0700951 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glass0cd8ace2019-07-20 12:24:04 -0600952 return data
Simon Glassaf8c45c2019-07-20 12:23:41 -0600953
Simon Glass637958f2021-11-23 21:09:50 -0700954 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass4d8151f2019-09-25 08:56:21 -0600955 """Read the data for a particular child entry
Simon Glass23f00472019-09-25 08:56:20 -0600956
957 This reads data from the parent and extracts the piece that relates to
958 the given child.
959
960 Args:
Simon Glass637958f2021-11-23 21:09:50 -0700961 child (Entry): Child entry to read data for (must be valid)
962 decomp (bool): True to decompress any compressed data before
963 returning it; False to return the raw, uncompressed data
964 alt_format (str): Alternative format to read in, or None
Simon Glass23f00472019-09-25 08:56:20 -0600965
966 Returns:
967 Data for the child (bytes)
968 """
969 pass
970
Simon Glassaf8c45c2019-07-20 12:23:41 -0600971 def LoadData(self, decomp=True):
972 data = self.ReadData(decomp)
Simon Glass072959a2019-07-20 12:23:50 -0600973 self.contents_size = len(data)
Simon Glassaf8c45c2019-07-20 12:23:41 -0600974 self.ProcessContentsUpdate(data)
975 self.Detail('Loaded data size %x' % len(data))
Simon Glass990b1742019-07-20 12:23:46 -0600976
Simon Glass637958f2021-11-23 21:09:50 -0700977 def GetAltFormat(self, data, alt_format):
978 """Read the data for an extry in an alternative format
979
980 Supported formats are list in the documentation for each entry. An
981 example is fdtmap which provides .
982
983 Args:
984 data (bytes): Data to convert (this should have been produced by the
985 entry)
986 alt_format (str): Format to use
987
988 """
989 pass
990
Simon Glass990b1742019-07-20 12:23:46 -0600991 def GetImage(self):
992 """Get the image containing this entry
993
994 Returns:
995 Image object containing this entry
996 """
997 return self.section.GetImage()
Simon Glass072959a2019-07-20 12:23:50 -0600998
999 def WriteData(self, data, decomp=True):
1000 """Write the data to an entry in the image
1001
1002 This is used when the image has been read in and we want to replace the
1003 data for a particular entry in that image.
1004
1005 The image must be re-packed and written out afterwards.
1006
1007 Args:
1008 data: Data to replace it with
1009 decomp: True to compress the data if needed, False if data is
1010 already compressed so should be used as is
1011
1012 Returns:
1013 True if the data did not result in a resize of this entry, False if
1014 the entry must be resized
1015 """
Simon Glass1fdb4872019-10-31 07:43:02 -06001016 if self.size is not None:
1017 self.contents_size = self.size
1018 else:
1019 self.contents_size = self.pre_reset_size
Simon Glass072959a2019-07-20 12:23:50 -06001020 ok = self.ProcessContentsUpdate(data)
Simon Glass49b77e82023-03-02 17:02:44 -07001021 self.build_done = False
Simon Glass072959a2019-07-20 12:23:50 -06001022 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glassd34af7a2019-07-20 12:24:05 -06001023 section_ok = self.section.WriteChildData(self)
1024 return ok and section_ok
1025
1026 def WriteChildData(self, child):
1027 """Handle writing the data in a child entry
1028
1029 This should be called on the child's parent section after the child's
Simon Glasse796f242021-11-23 11:03:44 -07001030 data has been updated. It should update any data structures needed to
1031 validate that the update is successful.
Simon Glassd34af7a2019-07-20 12:24:05 -06001032
1033 This base-class implementation does nothing, since the base Entry object
1034 does not have any children.
1035
1036 Args:
1037 child: Child Entry that was written
1038
1039 Returns:
1040 True if the section could be updated successfully, False if the
Simon Glasse796f242021-11-23 11:03:44 -07001041 data is such that the section could not update
Simon Glassd34af7a2019-07-20 12:24:05 -06001042 """
Simon Glass49b77e82023-03-02 17:02:44 -07001043 self.build_done = False
1044 entry = self.section
1045
1046 # Now we must rebuild all sections above this one
1047 while entry and entry != entry.section:
1048 self.build_done = False
1049 entry = entry.section
1050
Simon Glassd34af7a2019-07-20 12:24:05 -06001051 return True
Simon Glass11453762019-07-20 12:23:55 -06001052
1053 def GetSiblingOrder(self):
1054 """Get the relative order of an entry amoung its siblings
1055
1056 Returns:
1057 'start' if this entry is first among siblings, 'end' if last,
1058 otherwise None
1059 """
1060 entries = list(self.section.GetEntries().values())
1061 if entries:
1062 if self == entries[0]:
1063 return 'start'
1064 elif self == entries[-1]:
1065 return 'end'
1066 return 'middle'
Simon Glass5d94cc62020-07-09 18:39:38 -06001067
1068 def SetAllowMissing(self, allow_missing):
1069 """Set whether a section allows missing external blobs
1070
1071 Args:
1072 allow_missing: True if allowed, False if not allowed
1073 """
1074 # This is meaningless for anything other than sections
1075 pass
Simon Glassa003cd32020-07-09 18:39:40 -06001076
Heiko Thiery6d451362022-01-06 11:49:41 +01001077 def SetAllowFakeBlob(self, allow_fake):
1078 """Set whether a section allows to create a fake blob
1079
1080 Args:
1081 allow_fake: True if allowed, False if not allowed
1082 """
Simon Glassceb5f912022-01-09 20:13:46 -07001083 self.allow_fake = allow_fake
Heiko Thiery6d451362022-01-06 11:49:41 +01001084
Simon Glassa003cd32020-07-09 18:39:40 -06001085 def CheckMissing(self, missing_list):
Simon Glass63328f12023-01-07 14:07:15 -07001086 """Check if the entry has missing external blobs
Simon Glassa003cd32020-07-09 18:39:40 -06001087
Simon Glass63328f12023-01-07 14:07:15 -07001088 If there are missing (non-optional) blobs, the entries are added to the
1089 list
Simon Glassa003cd32020-07-09 18:39:40 -06001090
1091 Args:
1092 missing_list: List of Entry objects to be added to
1093 """
Simon Glass63328f12023-01-07 14:07:15 -07001094 if self.missing and not self.optional:
Simon Glassa003cd32020-07-09 18:39:40 -06001095 missing_list.append(self)
Simon Glassb8f90372020-09-01 05:13:57 -06001096
Simon Glass8c0533b2022-03-05 20:19:04 -07001097 def check_fake_fname(self, fname, size=0):
Simon Glass7a602fd2022-01-12 13:10:36 -07001098 """If the file is missing and the entry allows fake blobs, fake it
1099
1100 Sets self.faked to True if faked
1101
1102 Args:
1103 fname (str): Filename to check
Simon Glass8c0533b2022-03-05 20:19:04 -07001104 size (int): Size of fake file to create
Simon Glass7a602fd2022-01-12 13:10:36 -07001105
1106 Returns:
Simon Glass214d36f2022-03-05 20:19:03 -07001107 tuple:
1108 fname (str): Filename of faked file
1109 bool: True if the blob was faked, False if not
Simon Glass7a602fd2022-01-12 13:10:36 -07001110 """
1111 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass7d3e4072022-08-07 09:46:46 -06001112 if not self.fake_fname:
1113 outfname = os.path.join(self.fake_dir, os.path.basename(fname))
1114 with open(outfname, "wb") as out:
1115 out.truncate(size)
1116 tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
1117 self.fake_fname = outfname
Simon Glass7a602fd2022-01-12 13:10:36 -07001118 self.faked = True
Simon Glass7d3e4072022-08-07 09:46:46 -06001119 return self.fake_fname, True
Simon Glass214d36f2022-03-05 20:19:03 -07001120 return fname, False
Simon Glass7a602fd2022-01-12 13:10:36 -07001121
Heiko Thiery6d451362022-01-06 11:49:41 +01001122 def CheckFakedBlobs(self, faked_blobs_list):
1123 """Check if any entries in this section have faked external blobs
1124
1125 If there are faked blobs, the entries are added to the list
1126
1127 Args:
Jonas Karlmanf2c52eb2023-02-19 22:02:04 +00001128 faked_blobs_list: List of Entry objects to be added to
Heiko Thiery6d451362022-01-06 11:49:41 +01001129 """
1130 # This is meaningless for anything other than blobs
1131 pass
1132
Simon Glass63328f12023-01-07 14:07:15 -07001133 def CheckOptional(self, optional_list):
1134 """Check if the entry has missing but optional external blobs
1135
1136 If there are missing (optional) blobs, the entries are added to the list
1137
1138 Args:
1139 optional_list (list): List of Entry objects to be added to
1140 """
1141 if self.missing and self.optional:
1142 optional_list.append(self)
1143
Simon Glassb8f90372020-09-01 05:13:57 -06001144 def GetAllowMissing(self):
1145 """Get whether a section allows missing external blobs
1146
1147 Returns:
1148 True if allowed, False if not allowed
1149 """
1150 return self.allow_missing
Simon Glassa820af72020-09-06 10:39:09 -06001151
Simon Glass66152ce2022-01-09 20:14:09 -07001152 def record_missing_bintool(self, bintool):
1153 """Record a missing bintool that was needed to produce this entry
1154
1155 Args:
1156 bintool (Bintool): Bintool that was missing
1157 """
Stefan Herbrechtsmeier486b9442022-08-19 16:25:19 +02001158 if bintool not in self.missing_bintools:
1159 self.missing_bintools.append(bintool)
Simon Glass66152ce2022-01-09 20:14:09 -07001160
1161 def check_missing_bintools(self, missing_list):
1162 """Check if any entries in this section have missing bintools
1163
1164 If there are missing bintools, these are added to the list
1165
1166 Args:
1167 missing_list: List of Bintool objects to be added to
1168 """
Stefan Herbrechtsmeier486b9442022-08-19 16:25:19 +02001169 for bintool in self.missing_bintools:
1170 if bintool not in missing_list:
1171 missing_list.append(bintool)
1172
Simon Glass66152ce2022-01-09 20:14:09 -07001173
Simon Glassa820af72020-09-06 10:39:09 -06001174 def GetHelpTags(self):
1175 """Get the tags use for missing-blob help
1176
1177 Returns:
1178 list of possible tags, most desirable first
1179 """
1180 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glassa1301a22020-10-26 17:40:06 -06001181
1182 def CompressData(self, indata):
1183 """Compress data according to the entry's compression method
1184
1185 Args:
1186 indata: Data to compress
1187
1188 Returns:
Stefan Herbrechtsmeierb2f8d612022-08-19 16:25:27 +02001189 Compressed data
Simon Glassa1301a22020-10-26 17:40:06 -06001190 """
Simon Glass789b34402020-10-26 17:40:15 -06001191 self.uncomp_data = indata
Simon Glassa1301a22020-10-26 17:40:06 -06001192 if self.compress != 'none':
1193 self.uncomp_size = len(indata)
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02001194 if self.comp_bintool.is_present():
1195 data = self.comp_bintool.compress(indata)
1196 else:
1197 self.record_missing_bintool(self.comp_bintool)
1198 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001199 else:
1200 data = indata
Simon Glassa1301a22020-10-26 17:40:06 -06001201 return data
Simon Glass2f859412021-03-18 20:25:04 +13001202
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001203 def DecompressData(self, indata):
1204 """Decompress data according to the entry's compression method
1205
1206 Args:
1207 indata: Data to decompress
1208
1209 Returns:
1210 Decompressed data
1211 """
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001212 if self.compress != 'none':
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02001213 if self.comp_bintool.is_present():
1214 data = self.comp_bintool.decompress(indata)
1215 self.uncomp_size = len(data)
1216 else:
1217 self.record_missing_bintool(self.comp_bintool)
1218 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001219 else:
1220 data = indata
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001221 self.uncomp_data = data
1222 return data
1223
Simon Glass2f859412021-03-18 20:25:04 +13001224 @classmethod
1225 def UseExpanded(cls, node, etype, new_etype):
1226 """Check whether to use an expanded entry type
1227
1228 This is called by Entry.Create() when it finds an expanded version of
1229 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1230 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1231 ignored.
1232
1233 Args:
1234 node: Node object containing information about the entry to
1235 create
1236 etype: Original entry type being used
1237 new_etype: New entry type proposed
1238
1239 Returns:
1240 True to use this entry type, False to use the original one
1241 """
Simon Glass011f1b32022-01-29 14:14:15 -07001242 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glass2f859412021-03-18 20:25:04 +13001243 (node.path, etype, new_etype))
1244 return True
Simon Glass637958f2021-11-23 21:09:50 -07001245
1246 def CheckAltFormats(self, alt_formats):
1247 """Add any alternative formats supported by this entry type
1248
1249 Args:
1250 alt_formats (dict): Dict to add alt_formats to:
1251 key: Name of alt format
1252 value: Help text
1253 """
1254 pass
Simon Glass4eae9252022-01-09 20:13:50 -07001255
Simon Glassfff147a2022-03-05 20:19:02 -07001256 def AddBintools(self, btools):
Simon Glass4eae9252022-01-09 20:13:50 -07001257 """Add the bintools used by this entry type
1258
1259 Args:
Simon Glassfff147a2022-03-05 20:19:02 -07001260 btools (dict of Bintool):
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001261
1262 Raise:
1263 ValueError if compression algorithm is not supported
Simon Glass4eae9252022-01-09 20:13:50 -07001264 """
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001265 algo = self.compress
1266 if algo != 'none':
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02001267 algos = ['bzip2', 'gzip', 'lz4', 'lzma', 'lzo', 'xz', 'zstd']
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001268 if algo not in algos:
1269 raise ValueError("Unknown algorithm '%s'" % algo)
Stefan Herbrechtsmeier9087fc52022-08-19 16:25:36 +02001270 names = {'lzma': 'lzma_alone', 'lzo': 'lzop'}
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001271 name = names.get(self.compress, self.compress)
1272 self.comp_bintool = self.AddBintool(btools, name)
Simon Glass4eae9252022-01-09 20:13:50 -07001273
1274 @classmethod
1275 def AddBintool(self, tools, name):
1276 """Add a new bintool to the tools used by this etype
1277
1278 Args:
1279 name: Name of the tool
1280 """
1281 btool = bintool.Bintool.create(name)
1282 tools[name] = btool
1283 return btool
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03001284
1285 def SetUpdateHash(self, update_hash):
1286 """Set whether this entry's "hash" subnode should be updated
1287
1288 Args:
1289 update_hash: True if hash should be updated, False if not
1290 """
1291 self.update_hash = update_hash
Simon Glass6fba35c2022-02-08 11:50:00 -07001292
Simon Glassfc5a1682022-03-05 20:19:05 -07001293 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass6fba35c2022-02-08 11:50:00 -07001294 """Put the contents of a list of entries into a file
1295
1296 Args:
1297 entries (list of Entry): Entries to collect
1298 prefix (str): Filename prefix of file to write to
Simon Glassfc5a1682022-03-05 20:19:05 -07001299 fake_size (int): Size of fake file to create if needed
Simon Glass6fba35c2022-02-08 11:50:00 -07001300
1301 If any entry does not have contents yet, this function returns False
1302 for the data.
1303
1304 Returns:
1305 Tuple:
Simon Glass43a98cc2022-03-05 20:18:58 -07001306 bytes: Concatenated data from all the entries (or None)
1307 str: Filename of file written (or None if no data)
1308 str: Unique portion of filename (or None if no data)
Simon Glass6fba35c2022-02-08 11:50:00 -07001309 """
1310 data = b''
1311 for entry in entries:
1312 # First get the input data and put it in a file. If not available,
1313 # try later.
Simon Glassfc5a1682022-03-05 20:19:05 -07001314 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass43a98cc2022-03-05 20:18:58 -07001315 return None, None, None
Simon Glass6fba35c2022-02-08 11:50:00 -07001316 data += entry.GetData()
1317 uniq = self.GetUniqueName()
1318 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1319 tools.write_file(fname, data)
1320 return data, fname, uniq
Simon Glass7d3e4072022-08-07 09:46:46 -06001321
1322 @classmethod
1323 def create_fake_dir(cls):
1324 """Create the directory for fake files"""
1325 cls.fake_dir = tools.get_output_filename('binman-fake')
1326 if not os.path.exists(cls.fake_dir):
1327 os.mkdir(cls.fake_dir)
1328 tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")
Simon Glass0cf5bce2022-08-13 11:40:44 -06001329
1330 def ensure_props(self):
1331 """Raise an exception if properties are missing
1332
1333 Args:
1334 prop_list (list of str): List of properties to check for
1335
1336 Raises:
1337 ValueError: Any property is missing
1338 """
1339 not_present = []
1340 for prop in self.required_props:
1341 if not prop in self._node.props:
1342 not_present.append(prop)
1343 if not_present:
1344 self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}")
Simon Glass1e9e61c2023-01-07 14:07:12 -07001345
1346 def mark_absent(self, msg):
1347 tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg))
1348 self.absent = True
Simon Glassad5cfe12023-01-07 14:07:14 -07001349
1350 def read_elf_segments(self):
1351 """Read segments from an entry that can generate an ELF file
1352
1353 Returns:
1354 tuple:
1355 list of segments, each:
1356 int: Segment number (0 = first)
1357 int: Start address of segment in memory
1358 bytes: Contents of segment
1359 int: entry address of ELF file
1360 """
1361 return None
Simon Glass49e9c002023-01-11 16:10:19 -07001362
1363 def lookup_offset(self):
1364 node, sym_name, offset = self.offset_from_elf
1365 entry = self.section.FindEntryByNode(node)
1366 if not entry:
1367 self.Raise("Cannot find entry for node '%s'" % node.name)
1368 if not entry.elf_fname:
1369 entry.Raise("Need elf-fname property '%s'" % node.name)
1370 val = elf.GetSymbolOffset(entry.elf_fname, sym_name,
1371 entry.elf_base_sym)
1372 return val + offset
Simon Glass49b77e82023-03-02 17:02:44 -07001373
1374 def mark_build_done(self):
1375 """Mark an entry as already built"""
1376 self.build_done = True
1377 entries = self.GetEntries()
1378 if entries:
1379 for entry in entries.values():
1380 entry.mark_build_done()
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00001381
1382 def UpdateSignatures(self, privatekey_fname, algo, input_fname):
1383 self.Raise('Updating signatures is not supported with this entry type')