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