blob: 5d8696e32a509643773ada955ab0f79af155d3eb [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 Glassa997ea52020-04-17 18:09:04 -060017from patman import tools
Simon Glass80025522022-01-29 14:14:04 -070018from patman.tools import to_hex, to_hex_size
Simon Glassa997ea52020-04-17 18:09:04 -060019from patman 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
Simon Glass1fdb4872019-10-31 07:43:02 -060052 pre_reset_size: size as it was before ResetForPack(). This allows us to
53 keep track of the size we started with and detect size changes
Simon Glassaa2fcf92019-07-08 14:25:30 -060054 uncomp_size: Size of uncompressed data in bytes, if the entry is
55 compressed, else None
Simon Glass2574ef62016-11-25 20:15:51 -070056 contents_size: Size of contents in bytes, 0 by default
Simon Glassafb9caa2020-10-26 17:40:10 -060057 align: Entry start offset alignment relative to the start of the
58 containing section, or None
Simon Glass2574ef62016-11-25 20:15:51 -070059 align_size: Entry size alignment, or None
Simon Glassafb9caa2020-10-26 17:40:10 -060060 align_end: Entry end offset alignment relative to the start of the
61 containing section, or None
Simon Glassd12599d2020-10-26 17:40:09 -060062 pad_before: Number of pad bytes before the contents when it is placed
63 in the containing section, 0 if none. The pad bytes become part of
64 the entry.
65 pad_after: Number of pad bytes after the contents when it is placed in
66 the containing section, 0 if none. The pad bytes become part of
67 the entry.
68 data: Contents of entry (string of bytes). This does not include
Simon Glass789b34402020-10-26 17:40:15 -060069 padding created by pad_before or pad_after. If the entry is
70 compressed, this contains the compressed data.
71 uncomp_data: Original uncompressed data, if this entry is compressed,
72 else None
Simon Glassaa2fcf92019-07-08 14:25:30 -060073 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glasse61b6f62019-07-08 14:25:37 -060074 orig_offset: Original offset value read from node
75 orig_size: Original size value read from node
Simon Glass63328f12023-01-07 14:07:15 -070076 missing: True if this entry is missing its contents. Note that if it is
77 optional, this entry will not appear in the list generated by
78 entry.CheckMissing() since it is considered OK for it to be missing.
Simon Glassb8f90372020-09-01 05:13:57 -060079 allow_missing: Allow children of this entry to be missing (used by
80 subclasses such as Entry_section)
Heiko Thiery6d451362022-01-06 11:49:41 +010081 allow_fake: Allow creating a dummy fake file if the blob file is not
82 available. This is mainly used for testing.
Simon Glassb8f90372020-09-01 05:13:57 -060083 external: True if this entry contains an external binary blob
Simon Glass4eae9252022-01-09 20:13:50 -070084 bintools: Bintools used by this entry (only populated for Image)
Simon Glass66152ce2022-01-09 20:14:09 -070085 missing_bintools: List of missing bintools for this entry
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +030086 update_hash: True if this entry's "hash" subnode should be
87 updated with a hash of the entry contents
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +020088 comp_bintool: Bintools used for compress and decompress data
Simon Glass7d3e4072022-08-07 09:46:46 -060089 fake_fname: Fake filename, if one was created, else None
Simon Glass0cf5bce2022-08-13 11:40:44 -060090 required_props (dict of str): Properties which must be present. This can
91 be added to by subclasses
Simon Glass6fc079e2022-10-20 18:22:46 -060092 elf_fname (str): Filename of the ELF file, if this entry holds an ELF
93 file, or is a binary file produced from an ELF file
94 auto_write_symbols (bool): True to write ELF symbols into this entry's
95 contents
Simon Glass1e9e61c2023-01-07 14:07:12 -070096 absent (bool): True if this entry is absent. This can be controlled by
97 the entry itself, allowing it to vanish in certain circumstances.
98 An absent entry is removed during processing so that it does not
99 appear in the map
Simon Glass63328f12023-01-07 14:07:15 -0700100 optional (bool): True if this entry contains an optional external blob
Simon Glassf1ee03b2023-01-11 16:10:16 -0700101 overlap (bool): True if this entry overlaps with others
Simon Glass2574ef62016-11-25 20:15:51 -0700102 """
Simon Glass7d3e4072022-08-07 09:46:46 -0600103 fake_dir = None
104
Simon Glass6fc079e2022-10-20 18:22:46 -0600105 def __init__(self, section, etype, node, name_prefix='',
106 auto_write_symbols=False):
Simon Glassb9ba4e02019-08-24 07:22:44 -0600107 # Put this here to allow entry-docs and help to work without libfdt
108 global state
Simon Glassc585dd42020-04-17 18:09:03 -0600109 from binman import state
Simon Glassb9ba4e02019-08-24 07:22:44 -0600110
Simon Glassad5a7712018-06-01 09:38:14 -0600111 self.section = section
Simon Glass2574ef62016-11-25 20:15:51 -0700112 self.etype = etype
113 self._node = node
Simon Glass3b78d532018-06-01 09:38:21 -0600114 self.name = node and (name_prefix + node.name) or 'none'
Simon Glasse8561af2018-08-01 15:22:37 -0600115 self.offset = None
Simon Glass2574ef62016-11-25 20:15:51 -0700116 self.size = None
Simon Glass1fdb4872019-10-31 07:43:02 -0600117 self.pre_reset_size = None
Simon Glassaa2fcf92019-07-08 14:25:30 -0600118 self.uncomp_size = None
Simon Glass5c350162018-07-17 13:25:47 -0600119 self.data = None
Simon Glass789b34402020-10-26 17:40:15 -0600120 self.uncomp_data = None
Simon Glass2574ef62016-11-25 20:15:51 -0700121 self.contents_size = 0
122 self.align = None
123 self.align_size = None
124 self.align_end = None
125 self.pad_before = 0
126 self.pad_after = 0
Simon Glasse8561af2018-08-01 15:22:37 -0600127 self.offset_unset = False
Simon Glass9dcc8612018-08-01 15:22:42 -0600128 self.image_pos = None
Simon Glassdd156a42022-03-05 20:18:59 -0700129 self.extend_size = False
Simon Glassaa2fcf92019-07-08 14:25:30 -0600130 self.compress = 'none'
Simon Glassa003cd32020-07-09 18:39:40 -0600131 self.missing = False
Heiko Thiery6d451362022-01-06 11:49:41 +0100132 self.faked = False
Simon Glassb8f90372020-09-01 05:13:57 -0600133 self.external = False
134 self.allow_missing = False
Heiko Thiery6d451362022-01-06 11:49:41 +0100135 self.allow_fake = False
Simon Glass4eae9252022-01-09 20:13:50 -0700136 self.bintools = {}
Simon Glass66152ce2022-01-09 20:14:09 -0700137 self.missing_bintools = []
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300138 self.update_hash = True
Simon Glass7d3e4072022-08-07 09:46:46 -0600139 self.fake_fname = None
Simon Glass0cf5bce2022-08-13 11:40:44 -0600140 self.required_props = []
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +0200141 self.comp_bintool = None
Simon Glass6fc079e2022-10-20 18:22:46 -0600142 self.elf_fname = None
143 self.auto_write_symbols = auto_write_symbols
Simon Glass1e9e61c2023-01-07 14:07:12 -0700144 self.absent = False
Simon Glass63328f12023-01-07 14:07:15 -0700145 self.optional = False
Simon Glassf1ee03b2023-01-11 16:10:16 -0700146 self.overlap = False
Simon Glasse0035c92023-01-11 16:10:17 -0700147 self.elf_base_sym = None
Simon Glass49e9c002023-01-11 16:10:19 -0700148 self.offset_from_elf = None
Simon Glass2574ef62016-11-25 20:15:51 -0700149
150 @staticmethod
Simon Glassb9028bc2021-11-23 21:09:49 -0700151 def FindEntryClass(etype, expanded):
Simon Glass969616c2018-07-17 13:25:36 -0600152 """Look up the entry class for a node.
Simon Glass2574ef62016-11-25 20:15:51 -0700153
154 Args:
Simon Glass969616c2018-07-17 13:25:36 -0600155 node_node: Path name of Node object containing information about
156 the entry to create (used for errors)
157 etype: Entry type to use
Simon Glass2f859412021-03-18 20:25:04 +1300158 expanded: Use the expanded version of etype
Simon Glass2574ef62016-11-25 20:15:51 -0700159
160 Returns:
Simon Glass2f859412021-03-18 20:25:04 +1300161 The entry class object if found, else None if not found and expanded
Simon Glassb9028bc2021-11-23 21:09:49 -0700162 is True, else a tuple:
163 module name that could not be found
164 exception received
Simon Glass2574ef62016-11-25 20:15:51 -0700165 """
Simon Glasse76a3e62018-06-01 09:38:11 -0600166 # Convert something like 'u-boot@0' to 'u_boot' since we are only
167 # interested in the type.
Simon Glass2574ef62016-11-25 20:15:51 -0700168 module_name = etype.replace('-', '_')
Simon Glass2f859412021-03-18 20:25:04 +1300169
Simon Glasse76a3e62018-06-01 09:38:11 -0600170 if '@' in module_name:
171 module_name = module_name.split('@')[0]
Simon Glass2f859412021-03-18 20:25:04 +1300172 if expanded:
173 module_name += '_expanded'
Simon Glass2574ef62016-11-25 20:15:51 -0700174 module = modules.get(module_name)
175
Simon Glass691198c2018-06-01 09:38:15 -0600176 # Also allow entry-type modules to be brought in from the etype directory.
177
Simon Glass2574ef62016-11-25 20:15:51 -0700178 # Import the module if we have not already done so.
179 if not module:
180 try:
Simon Glassc585dd42020-04-17 18:09:03 -0600181 module = importlib.import_module('binman.etype.' + module_name)
Simon Glass969616c2018-07-17 13:25:36 -0600182 except ImportError as e:
Simon Glass2f859412021-03-18 20:25:04 +1300183 if expanded:
184 return None
Simon Glassb9028bc2021-11-23 21:09:49 -0700185 return module_name, e
Simon Glass2574ef62016-11-25 20:15:51 -0700186 modules[module_name] = module
187
Simon Glass969616c2018-07-17 13:25:36 -0600188 # Look up the expected class name
189 return getattr(module, 'Entry_%s' % module_name)
190
191 @staticmethod
Simon Glassb9028bc2021-11-23 21:09:49 -0700192 def Lookup(node_path, etype, expanded, missing_etype=False):
193 """Look up the entry class for a node.
194
195 Args:
196 node_node (str): Path name of Node object containing information
197 about the entry to create (used for errors)
198 etype (str): Entry type to use
199 expanded (bool): Use the expanded version of etype
200 missing_etype (bool): True to default to a blob etype if the
201 requested etype is not found
202
203 Returns:
204 The entry class object if found, else None if not found and expanded
205 is True
206
207 Raise:
208 ValueError if expanded is False and the class is not found
209 """
210 # Convert something like 'u-boot@0' to 'u_boot' since we are only
211 # interested in the type.
212 cls = Entry.FindEntryClass(etype, expanded)
213 if cls is None:
214 return None
215 elif isinstance(cls, tuple):
216 if missing_etype:
217 cls = Entry.FindEntryClass('blob', False)
218 if isinstance(cls, tuple): # This should not fail
219 module_name, e = cls
220 raise ValueError(
221 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
222 (etype, node_path, module_name, e))
223 return cls
224
225 @staticmethod
226 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glass969616c2018-07-17 13:25:36 -0600227 """Create a new entry for a node.
228
229 Args:
Simon Glassb9028bc2021-11-23 21:09:49 -0700230 section (entry_Section): Section object containing this node
231 node (Node): Node object containing information about the entry to
232 create
233 etype (str): Entry type to use, or None to work it out (used for
234 tests)
235 expanded (bool): Use the expanded version of etype
236 missing_etype (bool): True to default to a blob etype if the
237 requested etype is not found
Simon Glass969616c2018-07-17 13:25:36 -0600238
239 Returns:
240 A new Entry object of the correct type (a subclass of Entry)
241 """
242 if not etype:
243 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glassb9028bc2021-11-23 21:09:49 -0700244 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glass2f859412021-03-18 20:25:04 +1300245 if obj and expanded:
246 # Check whether to use the expanded entry
247 new_etype = etype + '-expanded'
Simon Glass7098b7f2021-03-21 18:24:30 +1300248 can_expand = not fdt_util.GetBool(node, 'no-expanded')
249 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glass2f859412021-03-18 20:25:04 +1300250 etype = new_etype
251 else:
252 obj = None
253 if not obj:
Simon Glassb9028bc2021-11-23 21:09:49 -0700254 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glass969616c2018-07-17 13:25:36 -0600255
Simon Glass2574ef62016-11-25 20:15:51 -0700256 # Call its constructor to get the object we want.
Simon Glassad5a7712018-06-01 09:38:14 -0600257 return obj(section, etype, node)
Simon Glass2574ef62016-11-25 20:15:51 -0700258
259 def ReadNode(self):
260 """Read entry information from the node
261
Simon Glass2c360cf2019-07-20 12:23:45 -0600262 This must be called as the first thing after the Entry is created.
263
Simon Glass2574ef62016-11-25 20:15:51 -0700264 This reads all the fields we recognise from the node, ready for use.
265 """
Simon Glass0cf5bce2022-08-13 11:40:44 -0600266 self.ensure_props()
Simon Glass24b97442018-07-17 13:25:51 -0600267 if 'pos' in self._node.props:
268 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glassdd156a42022-03-05 20:18:59 -0700269 if 'expand-size' in self._node.props:
270 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glasse8561af2018-08-01 15:22:37 -0600271 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glass2574ef62016-11-25 20:15:51 -0700272 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glassfb30e292019-07-20 12:23:51 -0600273 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
274 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
275 if self.GetImage().copy_to_orig:
276 self.orig_offset = self.offset
277 self.orig_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600278
Simon Glassb8424fa2019-07-08 14:25:46 -0600279 # These should not be set in input files, but are set in an FDT map,
280 # which is also read by this code.
281 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
282 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
283
Simon Glass2574ef62016-11-25 20:15:51 -0700284 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glass80025522022-01-29 14:14:04 -0700285 if tools.not_power_of_two(self.align):
Simon Glass2574ef62016-11-25 20:15:51 -0700286 raise ValueError("Node '%s': Alignment %s must be a power of two" %
287 (self._node.path, self.align))
Simon Glassf427c5f2021-03-21 18:24:33 +1300288 if self.section and self.align is None:
289 self.align = self.section.align_default
Simon Glass2574ef62016-11-25 20:15:51 -0700290 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
291 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
292 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glass80025522022-01-29 14:14:04 -0700293 if tools.not_power_of_two(self.align_size):
Simon Glass39dd2152019-07-08 14:25:47 -0600294 self.Raise("Alignment size %s must be a power of two" %
295 self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700296 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glasse8561af2018-08-01 15:22:37 -0600297 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassdd156a42022-03-05 20:18:59 -0700298 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassa820af72020-09-06 10:39:09 -0600299 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glass63328f12023-01-07 14:07:15 -0700300 self.optional = fdt_util.GetBool(self._node, 'optional')
Simon Glassf1ee03b2023-01-11 16:10:16 -0700301 self.overlap = fdt_util.GetBool(self._node, 'overlap')
302 if self.overlap:
303 self.required_props += ['offset', 'size']
Simon Glass2574ef62016-11-25 20:15:51 -0700304
Simon Glassa1301a22020-10-26 17:40:06 -0600305 # This is only supported by blobs and sections at present
306 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
Simon Glass49e9c002023-01-11 16:10:19 -0700307 self.offset_from_elf = fdt_util.GetPhandleNameOffset(self._node,
308 'offset-from-elf')
Simon Glassa1301a22020-10-26 17:40:06 -0600309
Simon Glass3732ec32018-09-14 04:57:18 -0600310 def GetDefaultFilename(self):
311 return None
312
Simon Glass267112e2019-07-20 12:23:28 -0600313 def GetFdts(self):
314 """Get the device trees used by this entry
Simon Glass0c9d5b52018-09-14 04:57:22 -0600315
316 Returns:
Simon Glass267112e2019-07-20 12:23:28 -0600317 Empty dict, if this entry is not a .dtb, otherwise:
318 Dict:
319 key: Filename from this entry (without the path)
Simon Glass684a4f12019-07-20 12:23:31 -0600320 value: Tuple:
Simon Glass8235dd82021-03-18 20:25:02 +1300321 Entry object for this dtb
Simon Glass684a4f12019-07-20 12:23:31 -0600322 Filename of file containing this dtb
Simon Glass0c9d5b52018-09-14 04:57:22 -0600323 """
Simon Glass267112e2019-07-20 12:23:28 -0600324 return {}
Simon Glass0c9d5b52018-09-14 04:57:22 -0600325
Simon Glassf86ddad2022-03-05 20:19:00 -0700326 def gen_entries(self):
327 """Allow entries to generate other entries
Simon Glassfcb2a7c2021-03-18 20:24:52 +1300328
329 Some entries generate subnodes automatically, from which sub-entries
330 are then created. This method allows those to be added to the binman
331 definition for the current image. An entry which implements this method
332 should call state.AddSubnode() to add a subnode and can add properties
333 with state.AddString(), etc.
334
335 An example is 'files', which produces a section containing a list of
336 files.
337 """
Simon Glassac6328c2018-09-14 04:57:28 -0600338 pass
339
Simon Glassacd6c6e2020-10-26 17:40:17 -0600340 def AddMissingProperties(self, have_image_pos):
341 """Add new properties to the device tree as needed for this entry
342
343 Args:
344 have_image_pos: True if this entry has an image position. This can
345 be False if its parent section is compressed, since compression
346 groups all entries together into a compressed block of data,
347 obscuring the start of each individual child entry
348 """
349 for prop in ['offset', 'size']:
Simon Glasse22f8fa2018-07-06 10:27:41 -0600350 if not prop in self._node.props:
Simon Glassc8135dc2018-09-14 04:57:21 -0600351 state.AddZeroProp(self._node, prop)
Simon Glassacd6c6e2020-10-26 17:40:17 -0600352 if have_image_pos and 'image-pos' not in self._node.props:
353 state.AddZeroProp(self._node, 'image-pos')
Simon Glassfb30e292019-07-20 12:23:51 -0600354 if self.GetImage().allow_repack:
355 if self.orig_offset is not None:
356 state.AddZeroProp(self._node, 'orig-offset', True)
357 if self.orig_size is not None:
358 state.AddZeroProp(self._node, 'orig-size', True)
359
Simon Glassaa2fcf92019-07-08 14:25:30 -0600360 if self.compress != 'none':
361 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300362
363 if self.update_hash:
364 err = state.CheckAddHashProp(self._node)
365 if err:
366 self.Raise(err)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600367
368 def SetCalculatedProperties(self):
369 """Set the value of device-tree properties calculated by binman"""
Simon Glassc8135dc2018-09-14 04:57:21 -0600370 state.SetInt(self._node, 'offset', self.offset)
371 state.SetInt(self._node, 'size', self.size)
Simon Glass39dd2152019-07-08 14:25:47 -0600372 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassacd6c6e2020-10-26 17:40:17 -0600373 if self.image_pos is not None:
Simon Glasseb943b12020-11-02 12:55:44 -0700374 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glassfb30e292019-07-20 12:23:51 -0600375 if self.GetImage().allow_repack:
376 if self.orig_offset is not None:
377 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
378 if self.orig_size is not None:
379 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glassaa2fcf92019-07-08 14:25:30 -0600380 if self.uncomp_size is not None:
381 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300382
383 if self.update_hash:
384 state.CheckSetHashValue(self._node, self.GetData)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600385
Simon Glass92307732018-07-06 10:27:40 -0600386 def ProcessFdt(self, fdt):
Simon Glasse219aa42018-09-14 04:57:24 -0600387 """Allow entries to adjust the device tree
388
389 Some entries need to adjust the device tree for their purposes. This
390 may involve adding or deleting properties.
391
392 Returns:
393 True if processing is complete
394 False if processing could not be completed due to a dependency.
395 This will cause the entry to be retried after others have been
396 called
397 """
Simon Glass92307732018-07-06 10:27:40 -0600398 return True
399
Simon Glass3b78d532018-06-01 09:38:21 -0600400 def SetPrefix(self, prefix):
401 """Set the name prefix for a node
402
403 Args:
404 prefix: Prefix to set, or '' to not use a prefix
405 """
406 if prefix:
407 self.name = prefix + self.name
408
Simon Glass2e1169f2018-07-06 10:27:19 -0600409 def SetContents(self, data):
410 """Set the contents of an entry
411
412 This sets both the data and content_size properties
413
414 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600415 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600416 """
417 self.data = data
418 self.contents_size = len(self.data)
419
420 def ProcessContentsUpdate(self, data):
Simon Glassd17dfea2019-07-08 14:25:33 -0600421 """Update the contents of an entry, after the size is fixed
Simon Glass2e1169f2018-07-06 10:27:19 -0600422
Simon Glassec849852019-07-08 14:25:35 -0600423 This checks that the new data is the same size as the old. If the size
424 has changed, this triggers a re-run of the packing algorithm.
Simon Glass2e1169f2018-07-06 10:27:19 -0600425
426 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600427 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600428
429 Raises:
430 ValueError if the new data size is not the same as the old
431 """
Simon Glassec849852019-07-08 14:25:35 -0600432 size_ok = True
Simon Glasse61b6f62019-07-08 14:25:37 -0600433 new_size = len(data)
Simon Glass9d8ee322019-07-20 12:23:58 -0600434 if state.AllowEntryExpansion() and new_size > self.contents_size:
435 # self.data will indicate the new size needed
436 size_ok = False
437 elif state.AllowEntryContraction() and new_size < self.contents_size:
438 size_ok = False
439
440 # If not allowed to change, try to deal with it or give up
441 if size_ok:
Simon Glasse61b6f62019-07-08 14:25:37 -0600442 if new_size > self.contents_size:
Simon Glass9d8ee322019-07-20 12:23:58 -0600443 self.Raise('Cannot update entry size from %d to %d' %
444 (self.contents_size, new_size))
445
446 # Don't let the data shrink. Pad it if necessary
447 if size_ok and new_size < self.contents_size:
Simon Glass80025522022-01-29 14:14:04 -0700448 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass9d8ee322019-07-20 12:23:58 -0600449
450 if not size_ok:
Simon Glass011f1b32022-01-29 14:14:15 -0700451 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glass80025522022-01-29 14:14:04 -0700452 self._node.path, to_hex(self.contents_size),
453 to_hex(new_size)))
Simon Glass2e1169f2018-07-06 10:27:19 -0600454 self.SetContents(data)
Simon Glassec849852019-07-08 14:25:35 -0600455 return size_ok
Simon Glass2e1169f2018-07-06 10:27:19 -0600456
Simon Glassfc5a1682022-03-05 20:19:05 -0700457 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glass2574ef62016-11-25 20:15:51 -0700458 """Figure out the contents of an entry.
459
Simon Glassfc5a1682022-03-05 20:19:05 -0700460 Args:
461 skip_entry (Entry): Entry to skip when obtaining section contents
462 fake_size (int): Size of fake file to create if needed
463
Simon Glass2574ef62016-11-25 20:15:51 -0700464 Returns:
465 True if the contents were found, False if another call is needed
Simon Glassa4948b22023-01-11 16:10:14 -0700466 after the other entries are processed, None if there is no contents
Simon Glass2574ef62016-11-25 20:15:51 -0700467 """
468 # No contents by default: subclasses can implement this
469 return True
470
Simon Glasse61b6f62019-07-08 14:25:37 -0600471 def ResetForPack(self):
472 """Reset offset/size fields so that packing can be done again"""
Simon Glassb6dff4c2019-07-20 12:23:36 -0600473 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glass80025522022-01-29 14:14:04 -0700474 (to_hex(self.offset), to_hex(self.orig_offset),
475 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass1fdb4872019-10-31 07:43:02 -0600476 self.pre_reset_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600477 self.offset = self.orig_offset
478 self.size = self.orig_size
479
Simon Glasse8561af2018-08-01 15:22:37 -0600480 def Pack(self, offset):
Simon Glassad5a7712018-06-01 09:38:14 -0600481 """Figure out how to pack the entry into the section
Simon Glass2574ef62016-11-25 20:15:51 -0700482
483 Most of the time the entries are not fully specified. There may be
484 an alignment but no size. In that case we take the size from the
485 contents of the entry.
486
Simon Glasse8561af2018-08-01 15:22:37 -0600487 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glass2574ef62016-11-25 20:15:51 -0700488
Simon Glasse8561af2018-08-01 15:22:37 -0600489 Once this function is complete, both the offset and size of the
Simon Glass2574ef62016-11-25 20:15:51 -0700490 entry will be know.
491
492 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600493 Current section offset pointer
Simon Glass2574ef62016-11-25 20:15:51 -0700494
495 Returns:
Simon Glasse8561af2018-08-01 15:22:37 -0600496 New section offset pointer (after this entry)
Simon Glass2574ef62016-11-25 20:15:51 -0700497 """
Simon Glassb6dff4c2019-07-20 12:23:36 -0600498 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glass80025522022-01-29 14:14:04 -0700499 (to_hex(self.offset), to_hex(self.size),
Simon Glassb6dff4c2019-07-20 12:23:36 -0600500 self.contents_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600501 if self.offset is None:
502 if self.offset_unset:
503 self.Raise('No offset set with offset-unset: should another '
504 'entry provide this correct offset?')
Simon Glass49e9c002023-01-11 16:10:19 -0700505 elif self.offset_from_elf:
506 self.offset = self.lookup_offset()
507 else:
508 self.offset = tools.align(offset, self.align)
Simon Glass2574ef62016-11-25 20:15:51 -0700509 needed = self.pad_before + self.contents_size + self.pad_after
Simon Glass80025522022-01-29 14:14:04 -0700510 needed = tools.align(needed, self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700511 size = self.size
512 if not size:
513 size = needed
Simon Glasse8561af2018-08-01 15:22:37 -0600514 new_offset = self.offset + size
Simon Glass80025522022-01-29 14:14:04 -0700515 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glasse8561af2018-08-01 15:22:37 -0600516 if aligned_offset != new_offset:
517 size = aligned_offset - self.offset
518 new_offset = aligned_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700519
520 if not self.size:
521 self.size = size
522
523 if self.size < needed:
524 self.Raise("Entry contents size is %#x (%d) but entry size is "
525 "%#x (%d)" % (needed, needed, self.size, self.size))
526 # Check that the alignment is correct. It could be wrong if the
Simon Glasse8561af2018-08-01 15:22:37 -0600527 # and offset or size values were provided (i.e. not calculated), but
Simon Glass2574ef62016-11-25 20:15:51 -0700528 # conflict with the provided alignment values
Simon Glass80025522022-01-29 14:14:04 -0700529 if self.size != tools.align(self.size, self.align_size):
Simon Glass2574ef62016-11-25 20:15:51 -0700530 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
531 (self.size, self.size, self.align_size, self.align_size))
Simon Glass80025522022-01-29 14:14:04 -0700532 if self.offset != tools.align(self.offset, self.align):
Simon Glasse8561af2018-08-01 15:22:37 -0600533 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
534 (self.offset, self.offset, self.align, self.align))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600535 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
536 (self.offset, self.size, self.contents_size, new_offset))
Simon Glass2574ef62016-11-25 20:15:51 -0700537
Simon Glasse8561af2018-08-01 15:22:37 -0600538 return new_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700539
540 def Raise(self, msg):
541 """Convenience function to raise an error referencing a node"""
542 raise ValueError("Node '%s': %s" % (self._node.path, msg))
543
Simon Glasse1915782021-03-21 18:24:31 +1300544 def Info(self, msg):
545 """Convenience function to log info referencing a node"""
546 tag = "Info '%s'" % self._node.path
Simon Glass011f1b32022-01-29 14:14:15 -0700547 tout.detail('%30s: %s' % (tag, msg))
Simon Glasse1915782021-03-21 18:24:31 +1300548
Simon Glassb6dff4c2019-07-20 12:23:36 -0600549 def Detail(self, msg):
550 """Convenience function to log detail referencing a node"""
551 tag = "Node '%s'" % self._node.path
Simon Glass011f1b32022-01-29 14:14:15 -0700552 tout.detail('%30s: %s' % (tag, msg))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600553
Simon Glass91710b32018-07-17 13:25:32 -0600554 def GetEntryArgsOrProps(self, props, required=False):
555 """Return the values of a set of properties
556
557 Args:
558 props: List of EntryArg objects
559
560 Raises:
561 ValueError if a property is not found
562 """
563 values = []
564 missing = []
565 for prop in props:
566 python_prop = prop.name.replace('-', '_')
567 if hasattr(self, python_prop):
568 value = getattr(self, python_prop)
569 else:
570 value = None
571 if value is None:
572 value = self.GetArg(prop.name, prop.datatype)
573 if value is None and required:
574 missing.append(prop.name)
575 values.append(value)
576 if missing:
Simon Glass3fb25402021-01-06 21:35:16 -0700577 self.GetImage().MissingArgs(self, missing)
Simon Glass91710b32018-07-17 13:25:32 -0600578 return values
579
Simon Glass2574ef62016-11-25 20:15:51 -0700580 def GetPath(self):
581 """Get the path of a node
582
583 Returns:
584 Full path of the node for this entry
585 """
586 return self._node.path
587
Simon Glass27a7f772021-03-21 18:24:32 +1300588 def GetData(self, required=True):
Simon Glass72eeff12020-10-26 17:40:16 -0600589 """Get the contents of an entry
590
Simon Glass27a7f772021-03-21 18:24:32 +1300591 Args:
592 required: True if the data must be present, False if it is OK to
593 return None
594
Simon Glass72eeff12020-10-26 17:40:16 -0600595 Returns:
596 bytes content of the entry, excluding any padding. If the entry is
Simon Glass02997652023-01-11 16:10:13 -0700597 compressed, the compressed data is returned. If the entry data
Simon Glassa4948b22023-01-11 16:10:14 -0700598 is not yet available, False can be returned. If the entry data
599 is null, then None is returned.
Simon Glass72eeff12020-10-26 17:40:16 -0600600 """
Simon Glass80025522022-01-29 14:14:04 -0700601 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glass2574ef62016-11-25 20:15:51 -0700602 return self.data
603
Simon Glasse17220f2020-11-02 12:55:43 -0700604 def GetPaddedData(self, data=None):
605 """Get the data for an entry including any padding
606
607 Gets the entry data and uses its section's pad-byte value to add padding
608 before and after as defined by the pad-before and pad-after properties.
609
610 This does not consider alignment.
611
612 Returns:
613 Contents of the entry along with any pad bytes before and
614 after it (bytes)
615 """
616 if data is None:
617 data = self.GetData()
618 return self.section.GetPaddedDataForEntry(self, data)
619
Simon Glasse8561af2018-08-01 15:22:37 -0600620 def GetOffsets(self):
Simon Glass224bc662019-07-08 13:18:30 -0600621 """Get the offsets for siblings
622
623 Some entry types can contain information about the position or size of
624 other entries. An example of this is the Intel Flash Descriptor, which
625 knows where the Intel Management Engine section should go.
626
627 If this entry knows about the position of other entries, it can specify
628 this by returning values here
629
630 Returns:
631 Dict:
632 key: Entry type
633 value: List containing position and size of the given entry
Simon Glassed365eb2019-07-08 13:18:39 -0600634 type. Either can be None if not known
Simon Glass224bc662019-07-08 13:18:30 -0600635 """
Simon Glass2574ef62016-11-25 20:15:51 -0700636 return {}
637
Simon Glassed365eb2019-07-08 13:18:39 -0600638 def SetOffsetSize(self, offset, size):
639 """Set the offset and/or size of an entry
640
641 Args:
642 offset: New offset, or None to leave alone
643 size: New size, or None to leave alone
644 """
645 if offset is not None:
646 self.offset = offset
647 if size is not None:
648 self.size = size
Simon Glass2574ef62016-11-25 20:15:51 -0700649
Simon Glass9dcc8612018-08-01 15:22:42 -0600650 def SetImagePos(self, image_pos):
651 """Set the position in the image
652
653 Args:
654 image_pos: Position of this entry in the image
655 """
656 self.image_pos = image_pos + self.offset
657
Simon Glass2574ef62016-11-25 20:15:51 -0700658 def ProcessContents(self):
Simon Glassec849852019-07-08 14:25:35 -0600659 """Do any post-packing updates of entry contents
660
661 This function should call ProcessContentsUpdate() to update the entry
662 contents, if necessary, returning its return value here.
663
664 Args:
665 data: Data to set to the contents (bytes)
666
667 Returns:
668 True if the new data size is OK, False if expansion is needed
669
670 Raises:
671 ValueError if the new data size is not the same as the old and
672 state.AllowEntryExpansion() is False
673 """
674 return True
Simon Glass4ca8e042017-11-13 18:55:01 -0700675
Simon Glass8a6f56e2018-06-01 09:38:13 -0600676 def WriteSymbols(self, section):
Simon Glass4ca8e042017-11-13 18:55:01 -0700677 """Write symbol values into binary files for access at run time
678
679 Args:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600680 section: Section containing the entry
Simon Glass4ca8e042017-11-13 18:55:01 -0700681 """
Simon Glass6fc079e2022-10-20 18:22:46 -0600682 if self.auto_write_symbols:
Simon Glass37f85de2022-10-20 18:22:47 -0600683 # Check if we are writing symbols into an ELF file
684 is_elf = self.GetDefaultFilename() == self.elf_fname
685 elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(),
Simon Glasse0035c92023-01-11 16:10:17 -0700686 is_elf, self.elf_base_sym)
Simon Glassa91e1152018-06-01 09:38:16 -0600687
Simon Glass55f68072020-10-26 17:40:18 -0600688 def CheckEntries(self):
Simon Glasse8561af2018-08-01 15:22:37 -0600689 """Check that the entry offsets are correct
Simon Glassa91e1152018-06-01 09:38:16 -0600690
Simon Glasse8561af2018-08-01 15:22:37 -0600691 This is used for entries which have extra offset requirements (other
Simon Glassa91e1152018-06-01 09:38:16 -0600692 than having to be fully inside their section). Sub-classes can implement
693 this function and raise if there is a problem.
694 """
695 pass
Simon Glass30732662018-06-01 09:38:20 -0600696
Simon Glass3a9a2b82018-07-17 13:25:28 -0600697 @staticmethod
Simon Glasscd817d52018-09-14 04:57:36 -0600698 def GetStr(value):
699 if value is None:
700 return '<none> '
701 return '%08x' % value
702
703 @staticmethod
Simon Glass7eca7922018-07-17 13:25:49 -0600704 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glasscd817d52018-09-14 04:57:36 -0600705 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
706 Entry.GetStr(offset), Entry.GetStr(size),
707 name), file=fd)
Simon Glass3a9a2b82018-07-17 13:25:28 -0600708
Simon Glass30732662018-06-01 09:38:20 -0600709 def WriteMap(self, fd, indent):
710 """Write a map of the entry to a .map file
711
712 Args:
713 fd: File to write the map to
714 indent: Curent indent level of map (0=none, 1=one level, etc.)
715 """
Simon Glass7eca7922018-07-17 13:25:49 -0600716 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
717 self.image_pos)
Simon Glass91710b32018-07-17 13:25:32 -0600718
Simon Glassbd5cd882022-08-13 11:40:50 -0600719 # pylint: disable=assignment-from-none
Simon Glass704784b2018-07-17 13:25:38 -0600720 def GetEntries(self):
721 """Return a list of entries contained by this entry
722
723 Returns:
724 List of entries, or None if none. A normal entry has no entries
725 within it so will return None
726 """
727 return None
728
Simon Glassbd5cd882022-08-13 11:40:50 -0600729 def FindEntryByNode(self, find_node):
730 """Find a node in an entry, searching all subentries
731
732 This does a recursive search.
733
734 Args:
735 find_node (fdt.Node): Node to find
736
737 Returns:
738 Entry: entry, if found, else None
739 """
740 entries = self.GetEntries()
741 if entries:
742 for entry in entries.values():
743 if entry._node == find_node:
744 return entry
745 found = entry.FindEntryByNode(find_node)
746 if found:
747 return found
748
749 return None
750
Simon Glass91710b32018-07-17 13:25:32 -0600751 def GetArg(self, name, datatype=str):
752 """Get the value of an entry argument or device-tree-node property
753
754 Some node properties can be provided as arguments to binman. First check
755 the entry arguments, and fall back to the device tree if not found
756
757 Args:
758 name: Argument name
759 datatype: Data type (str or int)
760
761 Returns:
762 Value of argument as a string or int, or None if no value
763
764 Raises:
765 ValueError if the argument cannot be converted to in
766 """
Simon Glass29aa7362018-09-14 04:57:19 -0600767 value = state.GetEntryArg(name)
Simon Glass91710b32018-07-17 13:25:32 -0600768 if value is not None:
769 if datatype == int:
770 try:
771 value = int(value)
772 except ValueError:
773 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
774 (name, value))
775 elif datatype == str:
776 pass
777 else:
778 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
779 datatype)
780 else:
781 value = fdt_util.GetDatatype(self._node, name, datatype)
782 return value
Simon Glass969616c2018-07-17 13:25:36 -0600783
784 @staticmethod
785 def WriteDocs(modules, test_missing=None):
786 """Write out documentation about the various entry types to stdout
787
788 Args:
789 modules: List of modules to include
790 test_missing: Used for testing. This is a module to report
791 as missing
792 """
793 print('''Binman Entry Documentation
794===========================
795
796This file describes the entry types supported by binman. These entry types can
797be placed in an image one by one to build up a final firmware image. It is
798fairly easy to create new entry types. Just add a new file to the 'etype'
799directory. You can use the existing entries as examples.
800
801Note that some entries are subclasses of others, using and extending their
802features to produce new behaviours.
803
804
805''')
806 modules = sorted(modules)
807
808 # Don't show the test entry
809 if '_testing' in modules:
810 modules.remove('_testing')
811 missing = []
812 for name in modules:
Simon Glass2f859412021-03-18 20:25:04 +1300813 module = Entry.Lookup('WriteDocs', name, False)
Simon Glass969616c2018-07-17 13:25:36 -0600814 docs = getattr(module, '__doc__')
815 if test_missing == name:
816 docs = None
817 if docs:
818 lines = docs.splitlines()
819 first_line = lines[0]
820 rest = [line[4:] for line in lines[1:]]
821 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glassa7c97782022-08-07 16:33:25 -0600822
823 # Create a reference for use by rST docs
824 ref_name = f'etype_{module.__name__[6:]}'.lower()
825 print('.. _%s:' % ref_name)
826 print()
Simon Glass969616c2018-07-17 13:25:36 -0600827 print(hdr)
828 print('-' * len(hdr))
829 print('\n'.join(rest))
830 print()
831 print()
832 else:
833 missing.append(name)
834
835 if missing:
836 raise ValueError('Documentation is missing for modules: %s' %
837 ', '.join(missing))
Simon Glass639505b2018-09-14 04:57:11 -0600838
839 def GetUniqueName(self):
840 """Get a unique name for a node
841
842 Returns:
843 String containing a unique name for a node, consisting of the name
844 of all ancestors (starting from within the 'binman' node) separated
845 by a dot ('.'). This can be useful for generating unique filesnames
846 in the output directory.
847 """
848 name = self.name
849 node = self._node
850 while node.parent:
851 node = node.parent
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +0300852 if node.name in ('binman', '/'):
Simon Glass639505b2018-09-14 04:57:11 -0600853 break
854 name = '%s.%s' % (node.name, name)
855 return name
Simon Glassfa79a812018-09-14 04:57:29 -0600856
Simon Glassdd156a42022-03-05 20:18:59 -0700857 def extend_to_limit(self, limit):
858 """Extend an entry so that it ends at the given offset limit"""
Simon Glassfa79a812018-09-14 04:57:29 -0600859 if self.offset + self.size < limit:
860 self.size = limit - self.offset
861 # Request the contents again, since changing the size requires that
862 # the data grows. This should not fail, but check it to be sure.
863 if not self.ObtainContents():
864 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassc4056b82019-07-08 13:18:38 -0600865
866 def HasSibling(self, name):
867 """Check if there is a sibling of a given name
868
869 Returns:
870 True if there is an entry with this name in the the same section,
871 else False
872 """
873 return name in self.section.GetEntries()
Simon Glasscec34ba2019-07-08 14:25:28 -0600874
875 def GetSiblingImagePos(self, name):
876 """Return the image position of the given sibling
877
878 Returns:
879 Image position of sibling, or None if the sibling has no position,
880 or False if there is no such sibling
881 """
882 if not self.HasSibling(name):
883 return False
884 return self.section.GetEntries()[name].image_pos
Simon Glass6b156f82019-07-08 14:25:43 -0600885
886 @staticmethod
887 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
888 uncomp_size, offset, entry):
889 """Add a new entry to the entries list
890
891 Args:
892 entries: List (of EntryInfo objects) to add to
893 indent: Current indent level to add to list
894 name: Entry name (string)
895 etype: Entry type (string)
896 size: Entry size in bytes (int)
897 image_pos: Position within image in bytes (int)
898 uncomp_size: Uncompressed size if the entry uses compression, else
899 None
900 offset: Entry offset within parent in bytes (int)
901 entry: Entry object
902 """
903 entries.append(EntryInfo(indent, name, etype, size, image_pos,
904 uncomp_size, offset, entry))
905
906 def ListEntries(self, entries, indent):
907 """Add files in this entry to the list of entries
908
909 This can be overridden by subclasses which need different behaviour.
910
911 Args:
912 entries: List (of EntryInfo objects) to add to
913 indent: Current indent level to add to list
914 """
915 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
916 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glass4c613bf2019-07-08 14:25:50 -0600917
Simon Glass637958f2021-11-23 21:09:50 -0700918 def ReadData(self, decomp=True, alt_format=None):
Simon Glass4c613bf2019-07-08 14:25:50 -0600919 """Read the data for an entry from the image
920
921 This is used when the image has been read in and we want to extract the
922 data for a particular entry from that image.
923
924 Args:
925 decomp: True to decompress any compressed data before returning it;
926 False to return the raw, uncompressed data
927
928 Returns:
929 Entry data (bytes)
930 """
931 # Use True here so that we get an uncompressed section to work from,
932 # although compressed sections are currently not supported
Simon Glass011f1b32022-01-29 14:14:15 -0700933 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass4d8151f2019-09-25 08:56:21 -0600934 (self.section.GetPath(), self.GetPath()))
Simon Glass637958f2021-11-23 21:09:50 -0700935 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glass0cd8ace2019-07-20 12:24:04 -0600936 return data
Simon Glassaf8c45c2019-07-20 12:23:41 -0600937
Simon Glass637958f2021-11-23 21:09:50 -0700938 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass4d8151f2019-09-25 08:56:21 -0600939 """Read the data for a particular child entry
Simon Glass23f00472019-09-25 08:56:20 -0600940
941 This reads data from the parent and extracts the piece that relates to
942 the given child.
943
944 Args:
Simon Glass637958f2021-11-23 21:09:50 -0700945 child (Entry): Child entry to read data for (must be valid)
946 decomp (bool): True to decompress any compressed data before
947 returning it; False to return the raw, uncompressed data
948 alt_format (str): Alternative format to read in, or None
Simon Glass23f00472019-09-25 08:56:20 -0600949
950 Returns:
951 Data for the child (bytes)
952 """
953 pass
954
Simon Glassaf8c45c2019-07-20 12:23:41 -0600955 def LoadData(self, decomp=True):
956 data = self.ReadData(decomp)
Simon Glass072959a2019-07-20 12:23:50 -0600957 self.contents_size = len(data)
Simon Glassaf8c45c2019-07-20 12:23:41 -0600958 self.ProcessContentsUpdate(data)
959 self.Detail('Loaded data size %x' % len(data))
Simon Glass990b1742019-07-20 12:23:46 -0600960
Simon Glass637958f2021-11-23 21:09:50 -0700961 def GetAltFormat(self, data, alt_format):
962 """Read the data for an extry in an alternative format
963
964 Supported formats are list in the documentation for each entry. An
965 example is fdtmap which provides .
966
967 Args:
968 data (bytes): Data to convert (this should have been produced by the
969 entry)
970 alt_format (str): Format to use
971
972 """
973 pass
974
Simon Glass990b1742019-07-20 12:23:46 -0600975 def GetImage(self):
976 """Get the image containing this entry
977
978 Returns:
979 Image object containing this entry
980 """
981 return self.section.GetImage()
Simon Glass072959a2019-07-20 12:23:50 -0600982
983 def WriteData(self, data, decomp=True):
984 """Write the data to an entry in the image
985
986 This is used when the image has been read in and we want to replace the
987 data for a particular entry in that image.
988
989 The image must be re-packed and written out afterwards.
990
991 Args:
992 data: Data to replace it with
993 decomp: True to compress the data if needed, False if data is
994 already compressed so should be used as is
995
996 Returns:
997 True if the data did not result in a resize of this entry, False if
998 the entry must be resized
999 """
Simon Glass1fdb4872019-10-31 07:43:02 -06001000 if self.size is not None:
1001 self.contents_size = self.size
1002 else:
1003 self.contents_size = self.pre_reset_size
Simon Glass072959a2019-07-20 12:23:50 -06001004 ok = self.ProcessContentsUpdate(data)
1005 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glassd34af7a2019-07-20 12:24:05 -06001006 section_ok = self.section.WriteChildData(self)
1007 return ok and section_ok
1008
1009 def WriteChildData(self, child):
1010 """Handle writing the data in a child entry
1011
1012 This should be called on the child's parent section after the child's
Simon Glasse796f242021-11-23 11:03:44 -07001013 data has been updated. It should update any data structures needed to
1014 validate that the update is successful.
Simon Glassd34af7a2019-07-20 12:24:05 -06001015
1016 This base-class implementation does nothing, since the base Entry object
1017 does not have any children.
1018
1019 Args:
1020 child: Child Entry that was written
1021
1022 Returns:
1023 True if the section could be updated successfully, False if the
Simon Glasse796f242021-11-23 11:03:44 -07001024 data is such that the section could not update
Simon Glassd34af7a2019-07-20 12:24:05 -06001025 """
1026 return True
Simon Glass11453762019-07-20 12:23:55 -06001027
1028 def GetSiblingOrder(self):
1029 """Get the relative order of an entry amoung its siblings
1030
1031 Returns:
1032 'start' if this entry is first among siblings, 'end' if last,
1033 otherwise None
1034 """
1035 entries = list(self.section.GetEntries().values())
1036 if entries:
1037 if self == entries[0]:
1038 return 'start'
1039 elif self == entries[-1]:
1040 return 'end'
1041 return 'middle'
Simon Glass5d94cc62020-07-09 18:39:38 -06001042
1043 def SetAllowMissing(self, allow_missing):
1044 """Set whether a section allows missing external blobs
1045
1046 Args:
1047 allow_missing: True if allowed, False if not allowed
1048 """
1049 # This is meaningless for anything other than sections
1050 pass
Simon Glassa003cd32020-07-09 18:39:40 -06001051
Heiko Thiery6d451362022-01-06 11:49:41 +01001052 def SetAllowFakeBlob(self, allow_fake):
1053 """Set whether a section allows to create a fake blob
1054
1055 Args:
1056 allow_fake: True if allowed, False if not allowed
1057 """
Simon Glassceb5f912022-01-09 20:13:46 -07001058 self.allow_fake = allow_fake
Heiko Thiery6d451362022-01-06 11:49:41 +01001059
Simon Glassa003cd32020-07-09 18:39:40 -06001060 def CheckMissing(self, missing_list):
Simon Glass63328f12023-01-07 14:07:15 -07001061 """Check if the entry has missing external blobs
Simon Glassa003cd32020-07-09 18:39:40 -06001062
Simon Glass63328f12023-01-07 14:07:15 -07001063 If there are missing (non-optional) blobs, the entries are added to the
1064 list
Simon Glassa003cd32020-07-09 18:39:40 -06001065
1066 Args:
1067 missing_list: List of Entry objects to be added to
1068 """
Simon Glass63328f12023-01-07 14:07:15 -07001069 if self.missing and not self.optional:
Simon Glassa003cd32020-07-09 18:39:40 -06001070 missing_list.append(self)
Simon Glassb8f90372020-09-01 05:13:57 -06001071
Simon Glass8c0533b2022-03-05 20:19:04 -07001072 def check_fake_fname(self, fname, size=0):
Simon Glass7a602fd2022-01-12 13:10:36 -07001073 """If the file is missing and the entry allows fake blobs, fake it
1074
1075 Sets self.faked to True if faked
1076
1077 Args:
1078 fname (str): Filename to check
Simon Glass8c0533b2022-03-05 20:19:04 -07001079 size (int): Size of fake file to create
Simon Glass7a602fd2022-01-12 13:10:36 -07001080
1081 Returns:
Simon Glass214d36f2022-03-05 20:19:03 -07001082 tuple:
1083 fname (str): Filename of faked file
1084 bool: True if the blob was faked, False if not
Simon Glass7a602fd2022-01-12 13:10:36 -07001085 """
1086 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass7d3e4072022-08-07 09:46:46 -06001087 if not self.fake_fname:
1088 outfname = os.path.join(self.fake_dir, os.path.basename(fname))
1089 with open(outfname, "wb") as out:
1090 out.truncate(size)
1091 tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
1092 self.fake_fname = outfname
Simon Glass7a602fd2022-01-12 13:10:36 -07001093 self.faked = True
Simon Glass7d3e4072022-08-07 09:46:46 -06001094 return self.fake_fname, True
Simon Glass214d36f2022-03-05 20:19:03 -07001095 return fname, False
Simon Glass7a602fd2022-01-12 13:10:36 -07001096
Heiko Thiery6d451362022-01-06 11:49:41 +01001097 def CheckFakedBlobs(self, faked_blobs_list):
1098 """Check if any entries in this section have faked external blobs
1099
1100 If there are faked blobs, the entries are added to the list
1101
1102 Args:
1103 fake_blobs_list: List of Entry objects to be added to
1104 """
1105 # This is meaningless for anything other than blobs
1106 pass
1107
Simon Glass63328f12023-01-07 14:07:15 -07001108 def CheckOptional(self, optional_list):
1109 """Check if the entry has missing but optional external blobs
1110
1111 If there are missing (optional) blobs, the entries are added to the list
1112
1113 Args:
1114 optional_list (list): List of Entry objects to be added to
1115 """
1116 if self.missing and self.optional:
1117 optional_list.append(self)
1118
Simon Glassb8f90372020-09-01 05:13:57 -06001119 def GetAllowMissing(self):
1120 """Get whether a section allows missing external blobs
1121
1122 Returns:
1123 True if allowed, False if not allowed
1124 """
1125 return self.allow_missing
Simon Glassa820af72020-09-06 10:39:09 -06001126
Simon Glass66152ce2022-01-09 20:14:09 -07001127 def record_missing_bintool(self, bintool):
1128 """Record a missing bintool that was needed to produce this entry
1129
1130 Args:
1131 bintool (Bintool): Bintool that was missing
1132 """
Stefan Herbrechtsmeier486b9442022-08-19 16:25:19 +02001133 if bintool not in self.missing_bintools:
1134 self.missing_bintools.append(bintool)
Simon Glass66152ce2022-01-09 20:14:09 -07001135
1136 def check_missing_bintools(self, missing_list):
1137 """Check if any entries in this section have missing bintools
1138
1139 If there are missing bintools, these are added to the list
1140
1141 Args:
1142 missing_list: List of Bintool objects to be added to
1143 """
Stefan Herbrechtsmeier486b9442022-08-19 16:25:19 +02001144 for bintool in self.missing_bintools:
1145 if bintool not in missing_list:
1146 missing_list.append(bintool)
1147
Simon Glass66152ce2022-01-09 20:14:09 -07001148
Simon Glassa820af72020-09-06 10:39:09 -06001149 def GetHelpTags(self):
1150 """Get the tags use for missing-blob help
1151
1152 Returns:
1153 list of possible tags, most desirable first
1154 """
1155 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glassa1301a22020-10-26 17:40:06 -06001156
1157 def CompressData(self, indata):
1158 """Compress data according to the entry's compression method
1159
1160 Args:
1161 indata: Data to compress
1162
1163 Returns:
Stefan Herbrechtsmeierb2f8d612022-08-19 16:25:27 +02001164 Compressed data
Simon Glassa1301a22020-10-26 17:40:06 -06001165 """
Simon Glass789b34402020-10-26 17:40:15 -06001166 self.uncomp_data = indata
Simon Glassa1301a22020-10-26 17:40:06 -06001167 if self.compress != 'none':
1168 self.uncomp_size = len(indata)
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02001169 if self.comp_bintool.is_present():
1170 data = self.comp_bintool.compress(indata)
1171 else:
1172 self.record_missing_bintool(self.comp_bintool)
1173 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001174 else:
1175 data = indata
Simon Glassa1301a22020-10-26 17:40:06 -06001176 return data
Simon Glass2f859412021-03-18 20:25:04 +13001177
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001178 def DecompressData(self, indata):
1179 """Decompress data according to the entry's compression method
1180
1181 Args:
1182 indata: Data to decompress
1183
1184 Returns:
1185 Decompressed data
1186 """
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001187 if self.compress != 'none':
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02001188 if self.comp_bintool.is_present():
1189 data = self.comp_bintool.decompress(indata)
1190 self.uncomp_size = len(data)
1191 else:
1192 self.record_missing_bintool(self.comp_bintool)
1193 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001194 else:
1195 data = indata
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001196 self.uncomp_data = data
1197 return data
1198
Simon Glass2f859412021-03-18 20:25:04 +13001199 @classmethod
1200 def UseExpanded(cls, node, etype, new_etype):
1201 """Check whether to use an expanded entry type
1202
1203 This is called by Entry.Create() when it finds an expanded version of
1204 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1205 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1206 ignored.
1207
1208 Args:
1209 node: Node object containing information about the entry to
1210 create
1211 etype: Original entry type being used
1212 new_etype: New entry type proposed
1213
1214 Returns:
1215 True to use this entry type, False to use the original one
1216 """
Simon Glass011f1b32022-01-29 14:14:15 -07001217 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glass2f859412021-03-18 20:25:04 +13001218 (node.path, etype, new_etype))
1219 return True
Simon Glass637958f2021-11-23 21:09:50 -07001220
1221 def CheckAltFormats(self, alt_formats):
1222 """Add any alternative formats supported by this entry type
1223
1224 Args:
1225 alt_formats (dict): Dict to add alt_formats to:
1226 key: Name of alt format
1227 value: Help text
1228 """
1229 pass
Simon Glass4eae9252022-01-09 20:13:50 -07001230
Simon Glassfff147a2022-03-05 20:19:02 -07001231 def AddBintools(self, btools):
Simon Glass4eae9252022-01-09 20:13:50 -07001232 """Add the bintools used by this entry type
1233
1234 Args:
Simon Glassfff147a2022-03-05 20:19:02 -07001235 btools (dict of Bintool):
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001236
1237 Raise:
1238 ValueError if compression algorithm is not supported
Simon Glass4eae9252022-01-09 20:13:50 -07001239 """
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001240 algo = self.compress
1241 if algo != 'none':
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02001242 algos = ['bzip2', 'gzip', 'lz4', 'lzma', 'lzo', 'xz', 'zstd']
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001243 if algo not in algos:
1244 raise ValueError("Unknown algorithm '%s'" % algo)
Stefan Herbrechtsmeier9087fc52022-08-19 16:25:36 +02001245 names = {'lzma': 'lzma_alone', 'lzo': 'lzop'}
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001246 name = names.get(self.compress, self.compress)
1247 self.comp_bintool = self.AddBintool(btools, name)
Simon Glass4eae9252022-01-09 20:13:50 -07001248
1249 @classmethod
1250 def AddBintool(self, tools, name):
1251 """Add a new bintool to the tools used by this etype
1252
1253 Args:
1254 name: Name of the tool
1255 """
1256 btool = bintool.Bintool.create(name)
1257 tools[name] = btool
1258 return btool
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03001259
1260 def SetUpdateHash(self, update_hash):
1261 """Set whether this entry's "hash" subnode should be updated
1262
1263 Args:
1264 update_hash: True if hash should be updated, False if not
1265 """
1266 self.update_hash = update_hash
Simon Glass6fba35c2022-02-08 11:50:00 -07001267
Simon Glassfc5a1682022-03-05 20:19:05 -07001268 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass6fba35c2022-02-08 11:50:00 -07001269 """Put the contents of a list of entries into a file
1270
1271 Args:
1272 entries (list of Entry): Entries to collect
1273 prefix (str): Filename prefix of file to write to
Simon Glassfc5a1682022-03-05 20:19:05 -07001274 fake_size (int): Size of fake file to create if needed
Simon Glass6fba35c2022-02-08 11:50:00 -07001275
1276 If any entry does not have contents yet, this function returns False
1277 for the data.
1278
1279 Returns:
1280 Tuple:
Simon Glass43a98cc2022-03-05 20:18:58 -07001281 bytes: Concatenated data from all the entries (or None)
1282 str: Filename of file written (or None if no data)
1283 str: Unique portion of filename (or None if no data)
Simon Glass6fba35c2022-02-08 11:50:00 -07001284 """
1285 data = b''
1286 for entry in entries:
1287 # First get the input data and put it in a file. If not available,
1288 # try later.
Simon Glassfc5a1682022-03-05 20:19:05 -07001289 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass43a98cc2022-03-05 20:18:58 -07001290 return None, None, None
Simon Glass6fba35c2022-02-08 11:50:00 -07001291 data += entry.GetData()
1292 uniq = self.GetUniqueName()
1293 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1294 tools.write_file(fname, data)
1295 return data, fname, uniq
Simon Glass7d3e4072022-08-07 09:46:46 -06001296
1297 @classmethod
1298 def create_fake_dir(cls):
1299 """Create the directory for fake files"""
1300 cls.fake_dir = tools.get_output_filename('binman-fake')
1301 if not os.path.exists(cls.fake_dir):
1302 os.mkdir(cls.fake_dir)
1303 tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")
Simon Glass0cf5bce2022-08-13 11:40:44 -06001304
1305 def ensure_props(self):
1306 """Raise an exception if properties are missing
1307
1308 Args:
1309 prop_list (list of str): List of properties to check for
1310
1311 Raises:
1312 ValueError: Any property is missing
1313 """
1314 not_present = []
1315 for prop in self.required_props:
1316 if not prop in self._node.props:
1317 not_present.append(prop)
1318 if not_present:
1319 self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}")
Simon Glass1e9e61c2023-01-07 14:07:12 -07001320
1321 def mark_absent(self, msg):
1322 tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg))
1323 self.absent = True
Simon Glassad5cfe12023-01-07 14:07:14 -07001324
1325 def read_elf_segments(self):
1326 """Read segments from an entry that can generate an ELF file
1327
1328 Returns:
1329 tuple:
1330 list of segments, each:
1331 int: Segment number (0 = first)
1332 int: Start address of segment in memory
1333 bytes: Contents of segment
1334 int: entry address of ELF file
1335 """
1336 return None
Simon Glass49e9c002023-01-11 16:10:19 -07001337
1338 def lookup_offset(self):
1339 node, sym_name, offset = self.offset_from_elf
1340 entry = self.section.FindEntryByNode(node)
1341 if not entry:
1342 self.Raise("Cannot find entry for node '%s'" % node.name)
1343 if not entry.elf_fname:
1344 entry.Raise("Need elf-fname property '%s'" % node.name)
1345 val = elf.GetSymbolOffset(entry.elf_fname, sym_name,
1346 entry.elf_base_sym)
1347 return val + offset