blob: 328b5bc568a994e80a17042878483a02cc618b7d [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 Glass4abf7842023-07-18 07:23:54 -0600161 self.no_write_symbols = False
Simon Glass2574ef62016-11-25 20:15:51 -0700162
163 @staticmethod
Simon Glassb9028bc2021-11-23 21:09:49 -0700164 def FindEntryClass(etype, expanded):
Simon Glass969616c2018-07-17 13:25:36 -0600165 """Look up the entry class for a node.
Simon Glass2574ef62016-11-25 20:15:51 -0700166
167 Args:
Simon Glass969616c2018-07-17 13:25:36 -0600168 node_node: Path name of Node object containing information about
169 the entry to create (used for errors)
170 etype: Entry type to use
Simon Glass2f859412021-03-18 20:25:04 +1300171 expanded: Use the expanded version of etype
Simon Glass2574ef62016-11-25 20:15:51 -0700172
173 Returns:
Simon Glass2f859412021-03-18 20:25:04 +1300174 The entry class object if found, else None if not found and expanded
Simon Glassb9028bc2021-11-23 21:09:49 -0700175 is True, else a tuple:
176 module name that could not be found
177 exception received
Simon Glass2574ef62016-11-25 20:15:51 -0700178 """
Simon Glasse76a3e62018-06-01 09:38:11 -0600179 # Convert something like 'u-boot@0' to 'u_boot' since we are only
180 # interested in the type.
Simon Glass2574ef62016-11-25 20:15:51 -0700181 module_name = etype.replace('-', '_')
Simon Glass2f859412021-03-18 20:25:04 +1300182
Simon Glasse76a3e62018-06-01 09:38:11 -0600183 if '@' in module_name:
184 module_name = module_name.split('@')[0]
Simon Glass2f859412021-03-18 20:25:04 +1300185 if expanded:
186 module_name += '_expanded'
Simon Glass2574ef62016-11-25 20:15:51 -0700187 module = modules.get(module_name)
188
Simon Glass691198c2018-06-01 09:38:15 -0600189 # Also allow entry-type modules to be brought in from the etype directory.
190
Simon Glass2574ef62016-11-25 20:15:51 -0700191 # Import the module if we have not already done so.
192 if not module:
193 try:
Simon Glassc585dd42020-04-17 18:09:03 -0600194 module = importlib.import_module('binman.etype.' + module_name)
Simon Glass969616c2018-07-17 13:25:36 -0600195 except ImportError as e:
Simon Glass2f859412021-03-18 20:25:04 +1300196 if expanded:
197 return None
Simon Glassb9028bc2021-11-23 21:09:49 -0700198 return module_name, e
Simon Glass2574ef62016-11-25 20:15:51 -0700199 modules[module_name] = module
200
Simon Glass969616c2018-07-17 13:25:36 -0600201 # Look up the expected class name
202 return getattr(module, 'Entry_%s' % module_name)
203
204 @staticmethod
Simon Glassb9028bc2021-11-23 21:09:49 -0700205 def Lookup(node_path, etype, expanded, missing_etype=False):
206 """Look up the entry class for a node.
207
208 Args:
209 node_node (str): Path name of Node object containing information
210 about the entry to create (used for errors)
211 etype (str): Entry type to use
212 expanded (bool): Use the expanded version of etype
213 missing_etype (bool): True to default to a blob etype if the
214 requested etype is not found
215
216 Returns:
217 The entry class object if found, else None if not found and expanded
218 is True
219
220 Raise:
221 ValueError if expanded is False and the class is not found
222 """
223 # Convert something like 'u-boot@0' to 'u_boot' since we are only
224 # interested in the type.
225 cls = Entry.FindEntryClass(etype, expanded)
226 if cls is None:
227 return None
228 elif isinstance(cls, tuple):
229 if missing_etype:
230 cls = Entry.FindEntryClass('blob', False)
231 if isinstance(cls, tuple): # This should not fail
232 module_name, e = cls
233 raise ValueError(
234 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
235 (etype, node_path, module_name, e))
236 return cls
237
238 @staticmethod
239 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glass969616c2018-07-17 13:25:36 -0600240 """Create a new entry for a node.
241
242 Args:
Simon Glassb9028bc2021-11-23 21:09:49 -0700243 section (entry_Section): Section object containing this node
244 node (Node): Node object containing information about the entry to
245 create
246 etype (str): Entry type to use, or None to work it out (used for
247 tests)
248 expanded (bool): Use the expanded version of etype
249 missing_etype (bool): True to default to a blob etype if the
250 requested etype is not found
Simon Glass969616c2018-07-17 13:25:36 -0600251
252 Returns:
253 A new Entry object of the correct type (a subclass of Entry)
254 """
255 if not etype:
256 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glassb9028bc2021-11-23 21:09:49 -0700257 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glass2f859412021-03-18 20:25:04 +1300258 if obj and expanded:
259 # Check whether to use the expanded entry
260 new_etype = etype + '-expanded'
Simon Glass7098b7f2021-03-21 18:24:30 +1300261 can_expand = not fdt_util.GetBool(node, 'no-expanded')
262 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glass2f859412021-03-18 20:25:04 +1300263 etype = new_etype
264 else:
265 obj = None
266 if not obj:
Simon Glassb9028bc2021-11-23 21:09:49 -0700267 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glass969616c2018-07-17 13:25:36 -0600268
Simon Glass2574ef62016-11-25 20:15:51 -0700269 # Call its constructor to get the object we want.
Simon Glassad5a7712018-06-01 09:38:14 -0600270 return obj(section, etype, node)
Simon Glass2574ef62016-11-25 20:15:51 -0700271
272 def ReadNode(self):
273 """Read entry information from the node
274
Simon Glass2c360cf2019-07-20 12:23:45 -0600275 This must be called as the first thing after the Entry is created.
276
Simon Glass2574ef62016-11-25 20:15:51 -0700277 This reads all the fields we recognise from the node, ready for use.
278 """
Simon Glass0cf5bce2022-08-13 11:40:44 -0600279 self.ensure_props()
Simon Glass24b97442018-07-17 13:25:51 -0600280 if 'pos' in self._node.props:
281 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glassdd156a42022-03-05 20:18:59 -0700282 if 'expand-size' in self._node.props:
283 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glasse8561af2018-08-01 15:22:37 -0600284 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glass2574ef62016-11-25 20:15:51 -0700285 self.size = fdt_util.GetInt(self._node, 'size')
Samuel Hollande2574022023-01-21 17:25:16 -0600286 self.min_size = fdt_util.GetInt(self._node, 'min-size', 0)
Simon Glassfb30e292019-07-20 12:23:51 -0600287 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
288 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
289 if self.GetImage().copy_to_orig:
290 self.orig_offset = self.offset
291 self.orig_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600292
Simon Glassb8424fa2019-07-08 14:25:46 -0600293 # These should not be set in input files, but are set in an FDT map,
294 # which is also read by this code.
295 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
296 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
297
Simon Glass2574ef62016-11-25 20:15:51 -0700298 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glass80025522022-01-29 14:14:04 -0700299 if tools.not_power_of_two(self.align):
Simon Glass2574ef62016-11-25 20:15:51 -0700300 raise ValueError("Node '%s': Alignment %s must be a power of two" %
301 (self._node.path, self.align))
Simon Glassf427c5f2021-03-21 18:24:33 +1300302 if self.section and self.align is None:
303 self.align = self.section.align_default
Simon Glass2574ef62016-11-25 20:15:51 -0700304 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
305 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
306 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glass80025522022-01-29 14:14:04 -0700307 if tools.not_power_of_two(self.align_size):
Simon Glass39dd2152019-07-08 14:25:47 -0600308 self.Raise("Alignment size %s must be a power of two" %
309 self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700310 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glasse8561af2018-08-01 15:22:37 -0600311 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassdd156a42022-03-05 20:18:59 -0700312 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassa820af72020-09-06 10:39:09 -0600313 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glass63328f12023-01-07 14:07:15 -0700314 self.optional = fdt_util.GetBool(self._node, 'optional')
Simon Glassf1ee03b2023-01-11 16:10:16 -0700315 self.overlap = fdt_util.GetBool(self._node, 'overlap')
316 if self.overlap:
317 self.required_props += ['offset', 'size']
Simon Glass2574ef62016-11-25 20:15:51 -0700318
Simon Glassa1301a22020-10-26 17:40:06 -0600319 # This is only supported by blobs and sections at present
320 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
Simon Glass49e9c002023-01-11 16:10:19 -0700321 self.offset_from_elf = fdt_util.GetPhandleNameOffset(self._node,
322 'offset-from-elf')
Simon Glassa1301a22020-10-26 17:40:06 -0600323
Simon Glasscda991e2023-02-12 17:11:15 -0700324 self.preserve = fdt_util.GetBool(self._node, 'preserve')
Simon Glass4abf7842023-07-18 07:23:54 -0600325 self.no_write_symbols = fdt_util.GetBool(self._node, 'no-write-symbols')
Simon Glasscda991e2023-02-12 17:11:15 -0700326
Simon Glass3732ec32018-09-14 04:57:18 -0600327 def GetDefaultFilename(self):
328 return None
329
Simon Glass267112e2019-07-20 12:23:28 -0600330 def GetFdts(self):
331 """Get the device trees used by this entry
Simon Glass0c9d5b52018-09-14 04:57:22 -0600332
333 Returns:
Simon Glass267112e2019-07-20 12:23:28 -0600334 Empty dict, if this entry is not a .dtb, otherwise:
335 Dict:
336 key: Filename from this entry (without the path)
Simon Glass684a4f12019-07-20 12:23:31 -0600337 value: Tuple:
Simon Glass8235dd82021-03-18 20:25:02 +1300338 Entry object for this dtb
Simon Glass684a4f12019-07-20 12:23:31 -0600339 Filename of file containing this dtb
Simon Glass0c9d5b52018-09-14 04:57:22 -0600340 """
Simon Glass267112e2019-07-20 12:23:28 -0600341 return {}
Simon Glass0c9d5b52018-09-14 04:57:22 -0600342
Simon Glassf86ddad2022-03-05 20:19:00 -0700343 def gen_entries(self):
344 """Allow entries to generate other entries
Simon Glassfcb2a7c2021-03-18 20:24:52 +1300345
346 Some entries generate subnodes automatically, from which sub-entries
347 are then created. This method allows those to be added to the binman
348 definition for the current image. An entry which implements this method
349 should call state.AddSubnode() to add a subnode and can add properties
350 with state.AddString(), etc.
351
352 An example is 'files', which produces a section containing a list of
353 files.
354 """
Simon Glassac6328c2018-09-14 04:57:28 -0600355 pass
356
Simon Glassacd6c6e2020-10-26 17:40:17 -0600357 def AddMissingProperties(self, have_image_pos):
358 """Add new properties to the device tree as needed for this entry
359
360 Args:
361 have_image_pos: True if this entry has an image position. This can
362 be False if its parent section is compressed, since compression
363 groups all entries together into a compressed block of data,
364 obscuring the start of each individual child entry
365 """
366 for prop in ['offset', 'size']:
Simon Glasse22f8fa2018-07-06 10:27:41 -0600367 if not prop in self._node.props:
Simon Glassc8135dc2018-09-14 04:57:21 -0600368 state.AddZeroProp(self._node, prop)
Simon Glassacd6c6e2020-10-26 17:40:17 -0600369 if have_image_pos and 'image-pos' not in self._node.props:
370 state.AddZeroProp(self._node, 'image-pos')
Simon Glassfb30e292019-07-20 12:23:51 -0600371 if self.GetImage().allow_repack:
372 if self.orig_offset is not None:
373 state.AddZeroProp(self._node, 'orig-offset', True)
374 if self.orig_size is not None:
375 state.AddZeroProp(self._node, 'orig-size', True)
376
Simon Glassaa2fcf92019-07-08 14:25:30 -0600377 if self.compress != 'none':
378 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300379
380 if self.update_hash:
381 err = state.CheckAddHashProp(self._node)
382 if err:
383 self.Raise(err)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600384
385 def SetCalculatedProperties(self):
386 """Set the value of device-tree properties calculated by binman"""
Simon Glassc8135dc2018-09-14 04:57:21 -0600387 state.SetInt(self._node, 'offset', self.offset)
388 state.SetInt(self._node, 'size', self.size)
Simon Glass39dd2152019-07-08 14:25:47 -0600389 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassacd6c6e2020-10-26 17:40:17 -0600390 if self.image_pos is not None:
Simon Glasseb943b12020-11-02 12:55:44 -0700391 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glassfb30e292019-07-20 12:23:51 -0600392 if self.GetImage().allow_repack:
393 if self.orig_offset is not None:
394 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
395 if self.orig_size is not None:
396 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glassaa2fcf92019-07-08 14:25:30 -0600397 if self.uncomp_size is not None:
398 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300399
400 if self.update_hash:
401 state.CheckSetHashValue(self._node, self.GetData)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600402
Simon Glass92307732018-07-06 10:27:40 -0600403 def ProcessFdt(self, fdt):
Simon Glasse219aa42018-09-14 04:57:24 -0600404 """Allow entries to adjust the device tree
405
406 Some entries need to adjust the device tree for their purposes. This
407 may involve adding or deleting properties.
408
409 Returns:
410 True if processing is complete
411 False if processing could not be completed due to a dependency.
412 This will cause the entry to be retried after others have been
413 called
414 """
Simon Glass92307732018-07-06 10:27:40 -0600415 return True
416
Simon Glass3b78d532018-06-01 09:38:21 -0600417 def SetPrefix(self, prefix):
418 """Set the name prefix for a node
419
420 Args:
421 prefix: Prefix to set, or '' to not use a prefix
422 """
423 if prefix:
424 self.name = prefix + self.name
425
Simon Glass2e1169f2018-07-06 10:27:19 -0600426 def SetContents(self, data):
427 """Set the contents of an entry
428
429 This sets both the data and content_size properties
430
431 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600432 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600433 """
434 self.data = data
435 self.contents_size = len(self.data)
436
437 def ProcessContentsUpdate(self, data):
Simon Glassd17dfea2019-07-08 14:25:33 -0600438 """Update the contents of an entry, after the size is fixed
Simon Glass2e1169f2018-07-06 10:27:19 -0600439
Simon Glassec849852019-07-08 14:25:35 -0600440 This checks that the new data is the same size as the old. If the size
441 has changed, this triggers a re-run of the packing algorithm.
Simon Glass2e1169f2018-07-06 10:27:19 -0600442
443 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600444 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600445
446 Raises:
447 ValueError if the new data size is not the same as the old
448 """
Simon Glassec849852019-07-08 14:25:35 -0600449 size_ok = True
Simon Glasse61b6f62019-07-08 14:25:37 -0600450 new_size = len(data)
Simon Glass9d8ee322019-07-20 12:23:58 -0600451 if state.AllowEntryExpansion() and new_size > self.contents_size:
452 # self.data will indicate the new size needed
453 size_ok = False
454 elif state.AllowEntryContraction() and new_size < self.contents_size:
455 size_ok = False
456
457 # If not allowed to change, try to deal with it or give up
458 if size_ok:
Simon Glasse61b6f62019-07-08 14:25:37 -0600459 if new_size > self.contents_size:
Simon Glass9d8ee322019-07-20 12:23:58 -0600460 self.Raise('Cannot update entry size from %d to %d' %
461 (self.contents_size, new_size))
462
463 # Don't let the data shrink. Pad it if necessary
464 if size_ok and new_size < self.contents_size:
Simon Glass80025522022-01-29 14:14:04 -0700465 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass9d8ee322019-07-20 12:23:58 -0600466
467 if not size_ok:
Simon Glass011f1b32022-01-29 14:14:15 -0700468 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glass80025522022-01-29 14:14:04 -0700469 self._node.path, to_hex(self.contents_size),
470 to_hex(new_size)))
Simon Glass2e1169f2018-07-06 10:27:19 -0600471 self.SetContents(data)
Simon Glassec849852019-07-08 14:25:35 -0600472 return size_ok
Simon Glass2e1169f2018-07-06 10:27:19 -0600473
Simon Glassfc5a1682022-03-05 20:19:05 -0700474 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glass2574ef62016-11-25 20:15:51 -0700475 """Figure out the contents of an entry.
476
Simon Glassfc5a1682022-03-05 20:19:05 -0700477 Args:
478 skip_entry (Entry): Entry to skip when obtaining section contents
479 fake_size (int): Size of fake file to create if needed
480
Simon Glass2574ef62016-11-25 20:15:51 -0700481 Returns:
482 True if the contents were found, False if another call is needed
Simon Glassa4948b22023-01-11 16:10:14 -0700483 after the other entries are processed, None if there is no contents
Simon Glass2574ef62016-11-25 20:15:51 -0700484 """
485 # No contents by default: subclasses can implement this
486 return True
487
Simon Glasse61b6f62019-07-08 14:25:37 -0600488 def ResetForPack(self):
489 """Reset offset/size fields so that packing can be done again"""
Simon Glassb6dff4c2019-07-20 12:23:36 -0600490 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glass80025522022-01-29 14:14:04 -0700491 (to_hex(self.offset), to_hex(self.orig_offset),
492 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass1fdb4872019-10-31 07:43:02 -0600493 self.pre_reset_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600494 self.offset = self.orig_offset
495 self.size = self.orig_size
496
Simon Glasse8561af2018-08-01 15:22:37 -0600497 def Pack(self, offset):
Simon Glassad5a7712018-06-01 09:38:14 -0600498 """Figure out how to pack the entry into the section
Simon Glass2574ef62016-11-25 20:15:51 -0700499
500 Most of the time the entries are not fully specified. There may be
501 an alignment but no size. In that case we take the size from the
502 contents of the entry.
503
Simon Glasse8561af2018-08-01 15:22:37 -0600504 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glass2574ef62016-11-25 20:15:51 -0700505
Simon Glasse8561af2018-08-01 15:22:37 -0600506 Once this function is complete, both the offset and size of the
Simon Glass2574ef62016-11-25 20:15:51 -0700507 entry will be know.
508
509 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600510 Current section offset pointer
Simon Glass2574ef62016-11-25 20:15:51 -0700511
512 Returns:
Simon Glasse8561af2018-08-01 15:22:37 -0600513 New section offset pointer (after this entry)
Simon Glass2574ef62016-11-25 20:15:51 -0700514 """
Simon Glassb6dff4c2019-07-20 12:23:36 -0600515 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glass80025522022-01-29 14:14:04 -0700516 (to_hex(self.offset), to_hex(self.size),
Simon Glassb6dff4c2019-07-20 12:23:36 -0600517 self.contents_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600518 if self.offset is None:
519 if self.offset_unset:
520 self.Raise('No offset set with offset-unset: should another '
521 'entry provide this correct offset?')
Simon Glass49e9c002023-01-11 16:10:19 -0700522 elif self.offset_from_elf:
523 self.offset = self.lookup_offset()
524 else:
525 self.offset = tools.align(offset, self.align)
Simon Glass2574ef62016-11-25 20:15:51 -0700526 needed = self.pad_before + self.contents_size + self.pad_after
Samuel Hollande2574022023-01-21 17:25:16 -0600527 needed = max(needed, self.min_size)
Simon Glass80025522022-01-29 14:14:04 -0700528 needed = tools.align(needed, self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700529 size = self.size
530 if not size:
531 size = needed
Simon Glasse8561af2018-08-01 15:22:37 -0600532 new_offset = self.offset + size
Simon Glass80025522022-01-29 14:14:04 -0700533 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glasse8561af2018-08-01 15:22:37 -0600534 if aligned_offset != new_offset:
535 size = aligned_offset - self.offset
536 new_offset = aligned_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700537
538 if not self.size:
539 self.size = size
540
541 if self.size < needed:
542 self.Raise("Entry contents size is %#x (%d) but entry size is "
543 "%#x (%d)" % (needed, needed, self.size, self.size))
544 # Check that the alignment is correct. It could be wrong if the
Simon Glasse8561af2018-08-01 15:22:37 -0600545 # and offset or size values were provided (i.e. not calculated), but
Simon Glass2574ef62016-11-25 20:15:51 -0700546 # conflict with the provided alignment values
Simon Glass80025522022-01-29 14:14:04 -0700547 if self.size != tools.align(self.size, self.align_size):
Simon Glass2574ef62016-11-25 20:15:51 -0700548 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
549 (self.size, self.size, self.align_size, self.align_size))
Simon Glass80025522022-01-29 14:14:04 -0700550 if self.offset != tools.align(self.offset, self.align):
Simon Glasse8561af2018-08-01 15:22:37 -0600551 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
552 (self.offset, self.offset, self.align, self.align))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600553 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
554 (self.offset, self.size, self.contents_size, new_offset))
Simon Glass2574ef62016-11-25 20:15:51 -0700555
Simon Glasse8561af2018-08-01 15:22:37 -0600556 return new_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700557
558 def Raise(self, msg):
559 """Convenience function to raise an error referencing a node"""
560 raise ValueError("Node '%s': %s" % (self._node.path, msg))
561
Simon Glasse1915782021-03-21 18:24:31 +1300562 def Info(self, msg):
563 """Convenience function to log info referencing a node"""
564 tag = "Info '%s'" % self._node.path
Simon Glass011f1b32022-01-29 14:14:15 -0700565 tout.detail('%30s: %s' % (tag, msg))
Simon Glasse1915782021-03-21 18:24:31 +1300566
Simon Glassb6dff4c2019-07-20 12:23:36 -0600567 def Detail(self, msg):
568 """Convenience function to log detail referencing a node"""
569 tag = "Node '%s'" % self._node.path
Simon Glass011f1b32022-01-29 14:14:15 -0700570 tout.detail('%30s: %s' % (tag, msg))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600571
Simon Glass91710b32018-07-17 13:25:32 -0600572 def GetEntryArgsOrProps(self, props, required=False):
573 """Return the values of a set of properties
574
575 Args:
576 props: List of EntryArg objects
577
578 Raises:
579 ValueError if a property is not found
580 """
581 values = []
582 missing = []
583 for prop in props:
584 python_prop = prop.name.replace('-', '_')
585 if hasattr(self, python_prop):
586 value = getattr(self, python_prop)
587 else:
588 value = None
589 if value is None:
590 value = self.GetArg(prop.name, prop.datatype)
591 if value is None and required:
592 missing.append(prop.name)
593 values.append(value)
594 if missing:
Simon Glass3fb25402021-01-06 21:35:16 -0700595 self.GetImage().MissingArgs(self, missing)
Simon Glass91710b32018-07-17 13:25:32 -0600596 return values
597
Simon Glass2574ef62016-11-25 20:15:51 -0700598 def GetPath(self):
599 """Get the path of a node
600
601 Returns:
602 Full path of the node for this entry
603 """
604 return self._node.path
605
Simon Glass27a7f772021-03-21 18:24:32 +1300606 def GetData(self, required=True):
Simon Glass72eeff12020-10-26 17:40:16 -0600607 """Get the contents of an entry
608
Simon Glass27a7f772021-03-21 18:24:32 +1300609 Args:
610 required: True if the data must be present, False if it is OK to
611 return None
612
Simon Glass72eeff12020-10-26 17:40:16 -0600613 Returns:
614 bytes content of the entry, excluding any padding. If the entry is
Simon Glass02997652023-01-11 16:10:13 -0700615 compressed, the compressed data is returned. If the entry data
Simon Glassa4948b22023-01-11 16:10:14 -0700616 is not yet available, False can be returned. If the entry data
617 is null, then None is returned.
Simon Glass72eeff12020-10-26 17:40:16 -0600618 """
Simon Glass80025522022-01-29 14:14:04 -0700619 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glass2574ef62016-11-25 20:15:51 -0700620 return self.data
621
Simon Glasse17220f2020-11-02 12:55:43 -0700622 def GetPaddedData(self, data=None):
623 """Get the data for an entry including any padding
624
625 Gets the entry data and uses its section's pad-byte value to add padding
626 before and after as defined by the pad-before and pad-after properties.
627
628 This does not consider alignment.
629
630 Returns:
631 Contents of the entry along with any pad bytes before and
632 after it (bytes)
633 """
634 if data is None:
635 data = self.GetData()
636 return self.section.GetPaddedDataForEntry(self, data)
637
Simon Glasse8561af2018-08-01 15:22:37 -0600638 def GetOffsets(self):
Simon Glass224bc662019-07-08 13:18:30 -0600639 """Get the offsets for siblings
640
641 Some entry types can contain information about the position or size of
642 other entries. An example of this is the Intel Flash Descriptor, which
643 knows where the Intel Management Engine section should go.
644
645 If this entry knows about the position of other entries, it can specify
646 this by returning values here
647
648 Returns:
649 Dict:
650 key: Entry type
651 value: List containing position and size of the given entry
Simon Glassed365eb2019-07-08 13:18:39 -0600652 type. Either can be None if not known
Simon Glass224bc662019-07-08 13:18:30 -0600653 """
Simon Glass2574ef62016-11-25 20:15:51 -0700654 return {}
655
Simon Glassed365eb2019-07-08 13:18:39 -0600656 def SetOffsetSize(self, offset, size):
657 """Set the offset and/or size of an entry
658
659 Args:
660 offset: New offset, or None to leave alone
661 size: New size, or None to leave alone
662 """
663 if offset is not None:
664 self.offset = offset
665 if size is not None:
666 self.size = size
Simon Glass2574ef62016-11-25 20:15:51 -0700667
Simon Glass9dcc8612018-08-01 15:22:42 -0600668 def SetImagePos(self, image_pos):
669 """Set the position in the image
670
671 Args:
672 image_pos: Position of this entry in the image
673 """
674 self.image_pos = image_pos + self.offset
675
Simon Glass2574ef62016-11-25 20:15:51 -0700676 def ProcessContents(self):
Simon Glassec849852019-07-08 14:25:35 -0600677 """Do any post-packing updates of entry contents
678
679 This function should call ProcessContentsUpdate() to update the entry
680 contents, if necessary, returning its return value here.
681
682 Args:
683 data: Data to set to the contents (bytes)
684
685 Returns:
686 True if the new data size is OK, False if expansion is needed
687
688 Raises:
689 ValueError if the new data size is not the same as the old and
690 state.AllowEntryExpansion() is False
691 """
692 return True
Simon Glass4ca8e042017-11-13 18:55:01 -0700693
Simon Glass8a6f56e2018-06-01 09:38:13 -0600694 def WriteSymbols(self, section):
Simon Glass4ca8e042017-11-13 18:55:01 -0700695 """Write symbol values into binary files for access at run time
696
697 Args:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600698 section: Section containing the entry
Simon Glass4ca8e042017-11-13 18:55:01 -0700699 """
Simon Glass4abf7842023-07-18 07:23:54 -0600700 if self.auto_write_symbols and not self.no_write_symbols:
Simon Glass37f85de2022-10-20 18:22:47 -0600701 # Check if we are writing symbols into an ELF file
702 is_elf = self.GetDefaultFilename() == self.elf_fname
703 elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(),
Simon Glasse0035c92023-01-11 16:10:17 -0700704 is_elf, self.elf_base_sym)
Simon Glassa91e1152018-06-01 09:38:16 -0600705
Simon Glass55f68072020-10-26 17:40:18 -0600706 def CheckEntries(self):
Simon Glasse8561af2018-08-01 15:22:37 -0600707 """Check that the entry offsets are correct
Simon Glassa91e1152018-06-01 09:38:16 -0600708
Simon Glasse8561af2018-08-01 15:22:37 -0600709 This is used for entries which have extra offset requirements (other
Simon Glassa91e1152018-06-01 09:38:16 -0600710 than having to be fully inside their section). Sub-classes can implement
711 this function and raise if there is a problem.
712 """
713 pass
Simon Glass30732662018-06-01 09:38:20 -0600714
Simon Glass3a9a2b82018-07-17 13:25:28 -0600715 @staticmethod
Simon Glasscd817d52018-09-14 04:57:36 -0600716 def GetStr(value):
717 if value is None:
718 return '<none> '
719 return '%08x' % value
720
721 @staticmethod
Simon Glass7eca7922018-07-17 13:25:49 -0600722 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glasscd817d52018-09-14 04:57:36 -0600723 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
724 Entry.GetStr(offset), Entry.GetStr(size),
725 name), file=fd)
Simon Glass3a9a2b82018-07-17 13:25:28 -0600726
Simon Glass30732662018-06-01 09:38:20 -0600727 def WriteMap(self, fd, indent):
728 """Write a map of the entry to a .map file
729
730 Args:
731 fd: File to write the map to
732 indent: Curent indent level of map (0=none, 1=one level, etc.)
733 """
Simon Glass7eca7922018-07-17 13:25:49 -0600734 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
735 self.image_pos)
Simon Glass91710b32018-07-17 13:25:32 -0600736
Simon Glassbd5cd882022-08-13 11:40:50 -0600737 # pylint: disable=assignment-from-none
Simon Glass704784b2018-07-17 13:25:38 -0600738 def GetEntries(self):
739 """Return a list of entries contained by this entry
740
741 Returns:
742 List of entries, or None if none. A normal entry has no entries
743 within it so will return None
744 """
745 return None
746
Simon Glassbd5cd882022-08-13 11:40:50 -0600747 def FindEntryByNode(self, find_node):
748 """Find a node in an entry, searching all subentries
749
750 This does a recursive search.
751
752 Args:
753 find_node (fdt.Node): Node to find
754
755 Returns:
756 Entry: entry, if found, else None
757 """
758 entries = self.GetEntries()
759 if entries:
760 for entry in entries.values():
761 if entry._node == find_node:
762 return entry
763 found = entry.FindEntryByNode(find_node)
764 if found:
765 return found
766
767 return None
768
Simon Glass91710b32018-07-17 13:25:32 -0600769 def GetArg(self, name, datatype=str):
770 """Get the value of an entry argument or device-tree-node property
771
772 Some node properties can be provided as arguments to binman. First check
773 the entry arguments, and fall back to the device tree if not found
774
775 Args:
776 name: Argument name
777 datatype: Data type (str or int)
778
779 Returns:
780 Value of argument as a string or int, or None if no value
781
782 Raises:
783 ValueError if the argument cannot be converted to in
784 """
Simon Glass29aa7362018-09-14 04:57:19 -0600785 value = state.GetEntryArg(name)
Simon Glass91710b32018-07-17 13:25:32 -0600786 if value is not None:
787 if datatype == int:
788 try:
789 value = int(value)
790 except ValueError:
791 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
792 (name, value))
793 elif datatype == str:
794 pass
795 else:
796 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
797 datatype)
798 else:
799 value = fdt_util.GetDatatype(self._node, name, datatype)
800 return value
Simon Glass969616c2018-07-17 13:25:36 -0600801
802 @staticmethod
803 def WriteDocs(modules, test_missing=None):
804 """Write out documentation about the various entry types to stdout
805
806 Args:
807 modules: List of modules to include
808 test_missing: Used for testing. This is a module to report
809 as missing
810 """
811 print('''Binman Entry Documentation
812===========================
813
814This file describes the entry types supported by binman. These entry types can
815be placed in an image one by one to build up a final firmware image. It is
816fairly easy to create new entry types. Just add a new file to the 'etype'
817directory. You can use the existing entries as examples.
818
819Note that some entries are subclasses of others, using and extending their
820features to produce new behaviours.
821
822
823''')
824 modules = sorted(modules)
825
826 # Don't show the test entry
827 if '_testing' in modules:
828 modules.remove('_testing')
829 missing = []
830 for name in modules:
Simon Glass2f859412021-03-18 20:25:04 +1300831 module = Entry.Lookup('WriteDocs', name, False)
Simon Glass969616c2018-07-17 13:25:36 -0600832 docs = getattr(module, '__doc__')
833 if test_missing == name:
834 docs = None
835 if docs:
836 lines = docs.splitlines()
837 first_line = lines[0]
838 rest = [line[4:] for line in lines[1:]]
839 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glassa7c97782022-08-07 16:33:25 -0600840
841 # Create a reference for use by rST docs
842 ref_name = f'etype_{module.__name__[6:]}'.lower()
843 print('.. _%s:' % ref_name)
844 print()
Simon Glass969616c2018-07-17 13:25:36 -0600845 print(hdr)
846 print('-' * len(hdr))
847 print('\n'.join(rest))
848 print()
849 print()
850 else:
851 missing.append(name)
852
853 if missing:
854 raise ValueError('Documentation is missing for modules: %s' %
855 ', '.join(missing))
Simon Glass639505b2018-09-14 04:57:11 -0600856
857 def GetUniqueName(self):
858 """Get a unique name for a node
859
860 Returns:
861 String containing a unique name for a node, consisting of the name
862 of all ancestors (starting from within the 'binman' node) separated
863 by a dot ('.'). This can be useful for generating unique filesnames
864 in the output directory.
865 """
866 name = self.name
867 node = self._node
868 while node.parent:
869 node = node.parent
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +0300870 if node.name in ('binman', '/'):
Simon Glass639505b2018-09-14 04:57:11 -0600871 break
872 name = '%s.%s' % (node.name, name)
873 return name
Simon Glassfa79a812018-09-14 04:57:29 -0600874
Simon Glassdd156a42022-03-05 20:18:59 -0700875 def extend_to_limit(self, limit):
876 """Extend an entry so that it ends at the given offset limit"""
Simon Glassfa79a812018-09-14 04:57:29 -0600877 if self.offset + self.size < limit:
878 self.size = limit - self.offset
879 # Request the contents again, since changing the size requires that
880 # the data grows. This should not fail, but check it to be sure.
881 if not self.ObtainContents():
882 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassc4056b82019-07-08 13:18:38 -0600883
884 def HasSibling(self, name):
885 """Check if there is a sibling of a given name
886
887 Returns:
888 True if there is an entry with this name in the the same section,
889 else False
890 """
891 return name in self.section.GetEntries()
Simon Glasscec34ba2019-07-08 14:25:28 -0600892
893 def GetSiblingImagePos(self, name):
894 """Return the image position of the given sibling
895
896 Returns:
897 Image position of sibling, or None if the sibling has no position,
898 or False if there is no such sibling
899 """
900 if not self.HasSibling(name):
901 return False
902 return self.section.GetEntries()[name].image_pos
Simon Glass6b156f82019-07-08 14:25:43 -0600903
904 @staticmethod
905 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
906 uncomp_size, offset, entry):
907 """Add a new entry to the entries list
908
909 Args:
910 entries: List (of EntryInfo objects) to add to
911 indent: Current indent level to add to list
912 name: Entry name (string)
913 etype: Entry type (string)
914 size: Entry size in bytes (int)
915 image_pos: Position within image in bytes (int)
916 uncomp_size: Uncompressed size if the entry uses compression, else
917 None
918 offset: Entry offset within parent in bytes (int)
919 entry: Entry object
920 """
921 entries.append(EntryInfo(indent, name, etype, size, image_pos,
922 uncomp_size, offset, entry))
923
924 def ListEntries(self, entries, indent):
925 """Add files in this entry to the list of entries
926
927 This can be overridden by subclasses which need different behaviour.
928
929 Args:
930 entries: List (of EntryInfo objects) to add to
931 indent: Current indent level to add to list
932 """
933 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
934 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glass4c613bf2019-07-08 14:25:50 -0600935
Simon Glass637958f2021-11-23 21:09:50 -0700936 def ReadData(self, decomp=True, alt_format=None):
Simon Glass4c613bf2019-07-08 14:25:50 -0600937 """Read the data for an entry from the image
938
939 This is used when the image has been read in and we want to extract the
940 data for a particular entry from that image.
941
942 Args:
943 decomp: True to decompress any compressed data before returning it;
944 False to return the raw, uncompressed data
945
946 Returns:
947 Entry data (bytes)
948 """
949 # Use True here so that we get an uncompressed section to work from,
950 # although compressed sections are currently not supported
Simon Glass011f1b32022-01-29 14:14:15 -0700951 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass4d8151f2019-09-25 08:56:21 -0600952 (self.section.GetPath(), self.GetPath()))
Simon Glass637958f2021-11-23 21:09:50 -0700953 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glass0cd8ace2019-07-20 12:24:04 -0600954 return data
Simon Glassaf8c45c2019-07-20 12:23:41 -0600955
Simon Glass637958f2021-11-23 21:09:50 -0700956 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass4d8151f2019-09-25 08:56:21 -0600957 """Read the data for a particular child entry
Simon Glass23f00472019-09-25 08:56:20 -0600958
959 This reads data from the parent and extracts the piece that relates to
960 the given child.
961
962 Args:
Simon Glass637958f2021-11-23 21:09:50 -0700963 child (Entry): Child entry to read data for (must be valid)
964 decomp (bool): True to decompress any compressed data before
965 returning it; False to return the raw, uncompressed data
966 alt_format (str): Alternative format to read in, or None
Simon Glass23f00472019-09-25 08:56:20 -0600967
968 Returns:
969 Data for the child (bytes)
970 """
971 pass
972
Simon Glassaf8c45c2019-07-20 12:23:41 -0600973 def LoadData(self, decomp=True):
974 data = self.ReadData(decomp)
Simon Glass072959a2019-07-20 12:23:50 -0600975 self.contents_size = len(data)
Simon Glassaf8c45c2019-07-20 12:23:41 -0600976 self.ProcessContentsUpdate(data)
977 self.Detail('Loaded data size %x' % len(data))
Simon Glass990b1742019-07-20 12:23:46 -0600978
Simon Glass637958f2021-11-23 21:09:50 -0700979 def GetAltFormat(self, data, alt_format):
980 """Read the data for an extry in an alternative format
981
982 Supported formats are list in the documentation for each entry. An
983 example is fdtmap which provides .
984
985 Args:
986 data (bytes): Data to convert (this should have been produced by the
987 entry)
988 alt_format (str): Format to use
989
990 """
991 pass
992
Simon Glass990b1742019-07-20 12:23:46 -0600993 def GetImage(self):
994 """Get the image containing this entry
995
996 Returns:
997 Image object containing this entry
998 """
999 return self.section.GetImage()
Simon Glass072959a2019-07-20 12:23:50 -06001000
1001 def WriteData(self, data, decomp=True):
1002 """Write the data to an entry in the image
1003
1004 This is used when the image has been read in and we want to replace the
1005 data for a particular entry in that image.
1006
1007 The image must be re-packed and written out afterwards.
1008
1009 Args:
1010 data: Data to replace it with
1011 decomp: True to compress the data if needed, False if data is
1012 already compressed so should be used as is
1013
1014 Returns:
1015 True if the data did not result in a resize of this entry, False if
1016 the entry must be resized
1017 """
Simon Glass1fdb4872019-10-31 07:43:02 -06001018 if self.size is not None:
1019 self.contents_size = self.size
1020 else:
1021 self.contents_size = self.pre_reset_size
Simon Glass072959a2019-07-20 12:23:50 -06001022 ok = self.ProcessContentsUpdate(data)
Simon Glass49b77e82023-03-02 17:02:44 -07001023 self.build_done = False
Simon Glass072959a2019-07-20 12:23:50 -06001024 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glassd34af7a2019-07-20 12:24:05 -06001025 section_ok = self.section.WriteChildData(self)
1026 return ok and section_ok
1027
1028 def WriteChildData(self, child):
1029 """Handle writing the data in a child entry
1030
1031 This should be called on the child's parent section after the child's
Simon Glasse796f242021-11-23 11:03:44 -07001032 data has been updated. It should update any data structures needed to
1033 validate that the update is successful.
Simon Glassd34af7a2019-07-20 12:24:05 -06001034
1035 This base-class implementation does nothing, since the base Entry object
1036 does not have any children.
1037
1038 Args:
1039 child: Child Entry that was written
1040
1041 Returns:
1042 True if the section could be updated successfully, False if the
Simon Glasse796f242021-11-23 11:03:44 -07001043 data is such that the section could not update
Simon Glassd34af7a2019-07-20 12:24:05 -06001044 """
Simon Glass49b77e82023-03-02 17:02:44 -07001045 self.build_done = False
1046 entry = self.section
1047
1048 # Now we must rebuild all sections above this one
1049 while entry and entry != entry.section:
1050 self.build_done = False
1051 entry = entry.section
1052
Simon Glassd34af7a2019-07-20 12:24:05 -06001053 return True
Simon Glass11453762019-07-20 12:23:55 -06001054
1055 def GetSiblingOrder(self):
1056 """Get the relative order of an entry amoung its siblings
1057
1058 Returns:
1059 'start' if this entry is first among siblings, 'end' if last,
1060 otherwise None
1061 """
1062 entries = list(self.section.GetEntries().values())
1063 if entries:
1064 if self == entries[0]:
1065 return 'start'
1066 elif self == entries[-1]:
1067 return 'end'
1068 return 'middle'
Simon Glass5d94cc62020-07-09 18:39:38 -06001069
1070 def SetAllowMissing(self, allow_missing):
1071 """Set whether a section allows missing external blobs
1072
1073 Args:
1074 allow_missing: True if allowed, False if not allowed
1075 """
1076 # This is meaningless for anything other than sections
1077 pass
Simon Glassa003cd32020-07-09 18:39:40 -06001078
Heiko Thiery6d451362022-01-06 11:49:41 +01001079 def SetAllowFakeBlob(self, allow_fake):
1080 """Set whether a section allows to create a fake blob
1081
1082 Args:
1083 allow_fake: True if allowed, False if not allowed
1084 """
Simon Glassceb5f912022-01-09 20:13:46 -07001085 self.allow_fake = allow_fake
Heiko Thiery6d451362022-01-06 11:49:41 +01001086
Simon Glassa003cd32020-07-09 18:39:40 -06001087 def CheckMissing(self, missing_list):
Simon Glass63328f12023-01-07 14:07:15 -07001088 """Check if the entry has missing external blobs
Simon Glassa003cd32020-07-09 18:39:40 -06001089
Simon Glass63328f12023-01-07 14:07:15 -07001090 If there are missing (non-optional) blobs, the entries are added to the
1091 list
Simon Glassa003cd32020-07-09 18:39:40 -06001092
1093 Args:
1094 missing_list: List of Entry objects to be added to
1095 """
Simon Glass63328f12023-01-07 14:07:15 -07001096 if self.missing and not self.optional:
Simon Glassa003cd32020-07-09 18:39:40 -06001097 missing_list.append(self)
Simon Glassb8f90372020-09-01 05:13:57 -06001098
Simon Glass8c0533b2022-03-05 20:19:04 -07001099 def check_fake_fname(self, fname, size=0):
Simon Glass7a602fd2022-01-12 13:10:36 -07001100 """If the file is missing and the entry allows fake blobs, fake it
1101
1102 Sets self.faked to True if faked
1103
1104 Args:
1105 fname (str): Filename to check
Simon Glass8c0533b2022-03-05 20:19:04 -07001106 size (int): Size of fake file to create
Simon Glass7a602fd2022-01-12 13:10:36 -07001107
1108 Returns:
Simon Glass214d36f2022-03-05 20:19:03 -07001109 tuple:
1110 fname (str): Filename of faked file
1111 bool: True if the blob was faked, False if not
Simon Glass7a602fd2022-01-12 13:10:36 -07001112 """
1113 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass7d3e4072022-08-07 09:46:46 -06001114 if not self.fake_fname:
1115 outfname = os.path.join(self.fake_dir, os.path.basename(fname))
1116 with open(outfname, "wb") as out:
1117 out.truncate(size)
1118 tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
1119 self.fake_fname = outfname
Simon Glass7a602fd2022-01-12 13:10:36 -07001120 self.faked = True
Simon Glass7d3e4072022-08-07 09:46:46 -06001121 return self.fake_fname, True
Simon Glass214d36f2022-03-05 20:19:03 -07001122 return fname, False
Simon Glass7a602fd2022-01-12 13:10:36 -07001123
Heiko Thiery6d451362022-01-06 11:49:41 +01001124 def CheckFakedBlobs(self, faked_blobs_list):
1125 """Check if any entries in this section have faked external blobs
1126
1127 If there are faked blobs, the entries are added to the list
1128
1129 Args:
Jonas Karlmanf2c52eb2023-02-19 22:02:04 +00001130 faked_blobs_list: List of Entry objects to be added to
Heiko Thiery6d451362022-01-06 11:49:41 +01001131 """
1132 # This is meaningless for anything other than blobs
1133 pass
1134
Simon Glass63328f12023-01-07 14:07:15 -07001135 def CheckOptional(self, optional_list):
1136 """Check if the entry has missing but optional external blobs
1137
1138 If there are missing (optional) blobs, the entries are added to the list
1139
1140 Args:
1141 optional_list (list): List of Entry objects to be added to
1142 """
1143 if self.missing and self.optional:
1144 optional_list.append(self)
1145
Simon Glassb8f90372020-09-01 05:13:57 -06001146 def GetAllowMissing(self):
1147 """Get whether a section allows missing external blobs
1148
1149 Returns:
1150 True if allowed, False if not allowed
1151 """
1152 return self.allow_missing
Simon Glassa820af72020-09-06 10:39:09 -06001153
Simon Glass66152ce2022-01-09 20:14:09 -07001154 def record_missing_bintool(self, bintool):
1155 """Record a missing bintool that was needed to produce this entry
1156
1157 Args:
1158 bintool (Bintool): Bintool that was missing
1159 """
Stefan Herbrechtsmeier486b9442022-08-19 16:25:19 +02001160 if bintool not in self.missing_bintools:
1161 self.missing_bintools.append(bintool)
Simon Glass66152ce2022-01-09 20:14:09 -07001162
1163 def check_missing_bintools(self, missing_list):
1164 """Check if any entries in this section have missing bintools
1165
1166 If there are missing bintools, these are added to the list
1167
1168 Args:
1169 missing_list: List of Bintool objects to be added to
1170 """
Stefan Herbrechtsmeier486b9442022-08-19 16:25:19 +02001171 for bintool in self.missing_bintools:
1172 if bintool not in missing_list:
1173 missing_list.append(bintool)
1174
Simon Glass66152ce2022-01-09 20:14:09 -07001175
Simon Glassa820af72020-09-06 10:39:09 -06001176 def GetHelpTags(self):
1177 """Get the tags use for missing-blob help
1178
1179 Returns:
1180 list of possible tags, most desirable first
1181 """
1182 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glassa1301a22020-10-26 17:40:06 -06001183
1184 def CompressData(self, indata):
1185 """Compress data according to the entry's compression method
1186
1187 Args:
1188 indata: Data to compress
1189
1190 Returns:
Stefan Herbrechtsmeierb2f8d612022-08-19 16:25:27 +02001191 Compressed data
Simon Glassa1301a22020-10-26 17:40:06 -06001192 """
Simon Glass789b34402020-10-26 17:40:15 -06001193 self.uncomp_data = indata
Simon Glassa1301a22020-10-26 17:40:06 -06001194 if self.compress != 'none':
1195 self.uncomp_size = len(indata)
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02001196 if self.comp_bintool.is_present():
1197 data = self.comp_bintool.compress(indata)
1198 else:
1199 self.record_missing_bintool(self.comp_bintool)
1200 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001201 else:
1202 data = indata
Simon Glassa1301a22020-10-26 17:40:06 -06001203 return data
Simon Glass2f859412021-03-18 20:25:04 +13001204
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001205 def DecompressData(self, indata):
1206 """Decompress data according to the entry's compression method
1207
1208 Args:
1209 indata: Data to decompress
1210
1211 Returns:
1212 Decompressed data
1213 """
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001214 if self.compress != 'none':
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02001215 if self.comp_bintool.is_present():
1216 data = self.comp_bintool.decompress(indata)
1217 self.uncomp_size = len(data)
1218 else:
1219 self.record_missing_bintool(self.comp_bintool)
1220 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001221 else:
1222 data = indata
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001223 self.uncomp_data = data
1224 return data
1225
Simon Glass2f859412021-03-18 20:25:04 +13001226 @classmethod
1227 def UseExpanded(cls, node, etype, new_etype):
1228 """Check whether to use an expanded entry type
1229
1230 This is called by Entry.Create() when it finds an expanded version of
1231 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1232 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1233 ignored.
1234
1235 Args:
1236 node: Node object containing information about the entry to
1237 create
1238 etype: Original entry type being used
1239 new_etype: New entry type proposed
1240
1241 Returns:
1242 True to use this entry type, False to use the original one
1243 """
Simon Glass011f1b32022-01-29 14:14:15 -07001244 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glass2f859412021-03-18 20:25:04 +13001245 (node.path, etype, new_etype))
1246 return True
Simon Glass637958f2021-11-23 21:09:50 -07001247
1248 def CheckAltFormats(self, alt_formats):
1249 """Add any alternative formats supported by this entry type
1250
1251 Args:
1252 alt_formats (dict): Dict to add alt_formats to:
1253 key: Name of alt format
1254 value: Help text
1255 """
1256 pass
Simon Glass4eae9252022-01-09 20:13:50 -07001257
Simon Glassfff147a2022-03-05 20:19:02 -07001258 def AddBintools(self, btools):
Simon Glass4eae9252022-01-09 20:13:50 -07001259 """Add the bintools used by this entry type
1260
1261 Args:
Simon Glassfff147a2022-03-05 20:19:02 -07001262 btools (dict of Bintool):
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001263
1264 Raise:
1265 ValueError if compression algorithm is not supported
Simon Glass4eae9252022-01-09 20:13:50 -07001266 """
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001267 algo = self.compress
1268 if algo != 'none':
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02001269 algos = ['bzip2', 'gzip', 'lz4', 'lzma', 'lzo', 'xz', 'zstd']
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001270 if algo not in algos:
1271 raise ValueError("Unknown algorithm '%s'" % algo)
Stefan Herbrechtsmeier9087fc52022-08-19 16:25:36 +02001272 names = {'lzma': 'lzma_alone', 'lzo': 'lzop'}
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001273 name = names.get(self.compress, self.compress)
1274 self.comp_bintool = self.AddBintool(btools, name)
Simon Glass4eae9252022-01-09 20:13:50 -07001275
1276 @classmethod
1277 def AddBintool(self, tools, name):
1278 """Add a new bintool to the tools used by this etype
1279
1280 Args:
1281 name: Name of the tool
1282 """
1283 btool = bintool.Bintool.create(name)
1284 tools[name] = btool
1285 return btool
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03001286
1287 def SetUpdateHash(self, update_hash):
1288 """Set whether this entry's "hash" subnode should be updated
1289
1290 Args:
1291 update_hash: True if hash should be updated, False if not
1292 """
1293 self.update_hash = update_hash
Simon Glass6fba35c2022-02-08 11:50:00 -07001294
Simon Glassfc5a1682022-03-05 20:19:05 -07001295 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass6fba35c2022-02-08 11:50:00 -07001296 """Put the contents of a list of entries into a file
1297
1298 Args:
1299 entries (list of Entry): Entries to collect
1300 prefix (str): Filename prefix of file to write to
Simon Glassfc5a1682022-03-05 20:19:05 -07001301 fake_size (int): Size of fake file to create if needed
Simon Glass6fba35c2022-02-08 11:50:00 -07001302
1303 If any entry does not have contents yet, this function returns False
1304 for the data.
1305
1306 Returns:
1307 Tuple:
Simon Glass43a98cc2022-03-05 20:18:58 -07001308 bytes: Concatenated data from all the entries (or None)
1309 str: Filename of file written (or None if no data)
1310 str: Unique portion of filename (or None if no data)
Simon Glass6fba35c2022-02-08 11:50:00 -07001311 """
1312 data = b''
1313 for entry in entries:
1314 # First get the input data and put it in a file. If not available,
1315 # try later.
Simon Glassfc5a1682022-03-05 20:19:05 -07001316 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass43a98cc2022-03-05 20:18:58 -07001317 return None, None, None
Simon Glass6fba35c2022-02-08 11:50:00 -07001318 data += entry.GetData()
1319 uniq = self.GetUniqueName()
1320 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1321 tools.write_file(fname, data)
1322 return data, fname, uniq
Simon Glass7d3e4072022-08-07 09:46:46 -06001323
1324 @classmethod
1325 def create_fake_dir(cls):
1326 """Create the directory for fake files"""
1327 cls.fake_dir = tools.get_output_filename('binman-fake')
1328 if not os.path.exists(cls.fake_dir):
1329 os.mkdir(cls.fake_dir)
1330 tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")
Simon Glass0cf5bce2022-08-13 11:40:44 -06001331
1332 def ensure_props(self):
1333 """Raise an exception if properties are missing
1334
1335 Args:
1336 prop_list (list of str): List of properties to check for
1337
1338 Raises:
1339 ValueError: Any property is missing
1340 """
1341 not_present = []
1342 for prop in self.required_props:
1343 if not prop in self._node.props:
1344 not_present.append(prop)
1345 if not_present:
1346 self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}")
Simon Glass1e9e61c2023-01-07 14:07:12 -07001347
1348 def mark_absent(self, msg):
1349 tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg))
1350 self.absent = True
Simon Glassad5cfe12023-01-07 14:07:14 -07001351
1352 def read_elf_segments(self):
1353 """Read segments from an entry that can generate an ELF file
1354
1355 Returns:
1356 tuple:
1357 list of segments, each:
1358 int: Segment number (0 = first)
1359 int: Start address of segment in memory
1360 bytes: Contents of segment
1361 int: entry address of ELF file
1362 """
1363 return None
Simon Glass49e9c002023-01-11 16:10:19 -07001364
1365 def lookup_offset(self):
1366 node, sym_name, offset = self.offset_from_elf
1367 entry = self.section.FindEntryByNode(node)
1368 if not entry:
1369 self.Raise("Cannot find entry for node '%s'" % node.name)
1370 if not entry.elf_fname:
1371 entry.Raise("Need elf-fname property '%s'" % node.name)
1372 val = elf.GetSymbolOffset(entry.elf_fname, sym_name,
1373 entry.elf_base_sym)
1374 return val + offset
Simon Glass49b77e82023-03-02 17:02:44 -07001375
1376 def mark_build_done(self):
1377 """Mark an entry as already built"""
1378 self.build_done = True
1379 entries = self.GetEntries()
1380 if entries:
1381 for entry in entries.values():
1382 entry.mark_build_done()
Ivan Mikhaylov3cfcaa4d2023-03-08 01:13:40 +00001383
1384 def UpdateSignatures(self, privatekey_fname, algo, input_fname):
1385 self.Raise('Updating signatures is not supported with this entry type')