blob: aca08e62d3a0c14bd1afd64a00295e15f9d207a7 [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 Glass2574ef62016-11-25 20:15:51 -0700148
149 @staticmethod
Simon Glassb9028bc2021-11-23 21:09:49 -0700150 def FindEntryClass(etype, expanded):
Simon Glass969616c2018-07-17 13:25:36 -0600151 """Look up the entry class for a node.
Simon Glass2574ef62016-11-25 20:15:51 -0700152
153 Args:
Simon Glass969616c2018-07-17 13:25:36 -0600154 node_node: Path name of Node object containing information about
155 the entry to create (used for errors)
156 etype: Entry type to use
Simon Glass2f859412021-03-18 20:25:04 +1300157 expanded: Use the expanded version of etype
Simon Glass2574ef62016-11-25 20:15:51 -0700158
159 Returns:
Simon Glass2f859412021-03-18 20:25:04 +1300160 The entry class object if found, else None if not found and expanded
Simon Glassb9028bc2021-11-23 21:09:49 -0700161 is True, else a tuple:
162 module name that could not be found
163 exception received
Simon Glass2574ef62016-11-25 20:15:51 -0700164 """
Simon Glasse76a3e62018-06-01 09:38:11 -0600165 # Convert something like 'u-boot@0' to 'u_boot' since we are only
166 # interested in the type.
Simon Glass2574ef62016-11-25 20:15:51 -0700167 module_name = etype.replace('-', '_')
Simon Glass2f859412021-03-18 20:25:04 +1300168
Simon Glasse76a3e62018-06-01 09:38:11 -0600169 if '@' in module_name:
170 module_name = module_name.split('@')[0]
Simon Glass2f859412021-03-18 20:25:04 +1300171 if expanded:
172 module_name += '_expanded'
Simon Glass2574ef62016-11-25 20:15:51 -0700173 module = modules.get(module_name)
174
Simon Glass691198c2018-06-01 09:38:15 -0600175 # Also allow entry-type modules to be brought in from the etype directory.
176
Simon Glass2574ef62016-11-25 20:15:51 -0700177 # Import the module if we have not already done so.
178 if not module:
179 try:
Simon Glassc585dd42020-04-17 18:09:03 -0600180 module = importlib.import_module('binman.etype.' + module_name)
Simon Glass969616c2018-07-17 13:25:36 -0600181 except ImportError as e:
Simon Glass2f859412021-03-18 20:25:04 +1300182 if expanded:
183 return None
Simon Glassb9028bc2021-11-23 21:09:49 -0700184 return module_name, e
Simon Glass2574ef62016-11-25 20:15:51 -0700185 modules[module_name] = module
186
Simon Glass969616c2018-07-17 13:25:36 -0600187 # Look up the expected class name
188 return getattr(module, 'Entry_%s' % module_name)
189
190 @staticmethod
Simon Glassb9028bc2021-11-23 21:09:49 -0700191 def Lookup(node_path, etype, expanded, missing_etype=False):
192 """Look up the entry class for a node.
193
194 Args:
195 node_node (str): Path name of Node object containing information
196 about the entry to create (used for errors)
197 etype (str): Entry type to use
198 expanded (bool): Use the expanded version of etype
199 missing_etype (bool): True to default to a blob etype if the
200 requested etype is not found
201
202 Returns:
203 The entry class object if found, else None if not found and expanded
204 is True
205
206 Raise:
207 ValueError if expanded is False and the class is not found
208 """
209 # Convert something like 'u-boot@0' to 'u_boot' since we are only
210 # interested in the type.
211 cls = Entry.FindEntryClass(etype, expanded)
212 if cls is None:
213 return None
214 elif isinstance(cls, tuple):
215 if missing_etype:
216 cls = Entry.FindEntryClass('blob', False)
217 if isinstance(cls, tuple): # This should not fail
218 module_name, e = cls
219 raise ValueError(
220 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
221 (etype, node_path, module_name, e))
222 return cls
223
224 @staticmethod
225 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glass969616c2018-07-17 13:25:36 -0600226 """Create a new entry for a node.
227
228 Args:
Simon Glassb9028bc2021-11-23 21:09:49 -0700229 section (entry_Section): Section object containing this node
230 node (Node): Node object containing information about the entry to
231 create
232 etype (str): Entry type to use, or None to work it out (used for
233 tests)
234 expanded (bool): Use the expanded version of etype
235 missing_etype (bool): True to default to a blob etype if the
236 requested etype is not found
Simon Glass969616c2018-07-17 13:25:36 -0600237
238 Returns:
239 A new Entry object of the correct type (a subclass of Entry)
240 """
241 if not etype:
242 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glassb9028bc2021-11-23 21:09:49 -0700243 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glass2f859412021-03-18 20:25:04 +1300244 if obj and expanded:
245 # Check whether to use the expanded entry
246 new_etype = etype + '-expanded'
Simon Glass7098b7f2021-03-21 18:24:30 +1300247 can_expand = not fdt_util.GetBool(node, 'no-expanded')
248 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glass2f859412021-03-18 20:25:04 +1300249 etype = new_etype
250 else:
251 obj = None
252 if not obj:
Simon Glassb9028bc2021-11-23 21:09:49 -0700253 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glass969616c2018-07-17 13:25:36 -0600254
Simon Glass2574ef62016-11-25 20:15:51 -0700255 # Call its constructor to get the object we want.
Simon Glassad5a7712018-06-01 09:38:14 -0600256 return obj(section, etype, node)
Simon Glass2574ef62016-11-25 20:15:51 -0700257
258 def ReadNode(self):
259 """Read entry information from the node
260
Simon Glass2c360cf2019-07-20 12:23:45 -0600261 This must be called as the first thing after the Entry is created.
262
Simon Glass2574ef62016-11-25 20:15:51 -0700263 This reads all the fields we recognise from the node, ready for use.
264 """
Simon Glass0cf5bce2022-08-13 11:40:44 -0600265 self.ensure_props()
Simon Glass24b97442018-07-17 13:25:51 -0600266 if 'pos' in self._node.props:
267 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glassdd156a42022-03-05 20:18:59 -0700268 if 'expand-size' in self._node.props:
269 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glasse8561af2018-08-01 15:22:37 -0600270 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glass2574ef62016-11-25 20:15:51 -0700271 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glassfb30e292019-07-20 12:23:51 -0600272 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
273 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
274 if self.GetImage().copy_to_orig:
275 self.orig_offset = self.offset
276 self.orig_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600277
Simon Glassb8424fa2019-07-08 14:25:46 -0600278 # These should not be set in input files, but are set in an FDT map,
279 # which is also read by this code.
280 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
281 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
282
Simon Glass2574ef62016-11-25 20:15:51 -0700283 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glass80025522022-01-29 14:14:04 -0700284 if tools.not_power_of_two(self.align):
Simon Glass2574ef62016-11-25 20:15:51 -0700285 raise ValueError("Node '%s': Alignment %s must be a power of two" %
286 (self._node.path, self.align))
Simon Glassf427c5f2021-03-21 18:24:33 +1300287 if self.section and self.align is None:
288 self.align = self.section.align_default
Simon Glass2574ef62016-11-25 20:15:51 -0700289 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
290 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
291 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glass80025522022-01-29 14:14:04 -0700292 if tools.not_power_of_two(self.align_size):
Simon Glass39dd2152019-07-08 14:25:47 -0600293 self.Raise("Alignment size %s must be a power of two" %
294 self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700295 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glasse8561af2018-08-01 15:22:37 -0600296 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassdd156a42022-03-05 20:18:59 -0700297 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassa820af72020-09-06 10:39:09 -0600298 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glass63328f12023-01-07 14:07:15 -0700299 self.optional = fdt_util.GetBool(self._node, 'optional')
Simon Glassf1ee03b2023-01-11 16:10:16 -0700300 self.overlap = fdt_util.GetBool(self._node, 'overlap')
301 if self.overlap:
302 self.required_props += ['offset', 'size']
Simon Glass2574ef62016-11-25 20:15:51 -0700303
Simon Glassa1301a22020-10-26 17:40:06 -0600304 # This is only supported by blobs and sections at present
305 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
306
Simon Glass3732ec32018-09-14 04:57:18 -0600307 def GetDefaultFilename(self):
308 return None
309
Simon Glass267112e2019-07-20 12:23:28 -0600310 def GetFdts(self):
311 """Get the device trees used by this entry
Simon Glass0c9d5b52018-09-14 04:57:22 -0600312
313 Returns:
Simon Glass267112e2019-07-20 12:23:28 -0600314 Empty dict, if this entry is not a .dtb, otherwise:
315 Dict:
316 key: Filename from this entry (without the path)
Simon Glass684a4f12019-07-20 12:23:31 -0600317 value: Tuple:
Simon Glass8235dd82021-03-18 20:25:02 +1300318 Entry object for this dtb
Simon Glass684a4f12019-07-20 12:23:31 -0600319 Filename of file containing this dtb
Simon Glass0c9d5b52018-09-14 04:57:22 -0600320 """
Simon Glass267112e2019-07-20 12:23:28 -0600321 return {}
Simon Glass0c9d5b52018-09-14 04:57:22 -0600322
Simon Glassf86ddad2022-03-05 20:19:00 -0700323 def gen_entries(self):
324 """Allow entries to generate other entries
Simon Glassfcb2a7c2021-03-18 20:24:52 +1300325
326 Some entries generate subnodes automatically, from which sub-entries
327 are then created. This method allows those to be added to the binman
328 definition for the current image. An entry which implements this method
329 should call state.AddSubnode() to add a subnode and can add properties
330 with state.AddString(), etc.
331
332 An example is 'files', which produces a section containing a list of
333 files.
334 """
Simon Glassac6328c2018-09-14 04:57:28 -0600335 pass
336
Simon Glassacd6c6e2020-10-26 17:40:17 -0600337 def AddMissingProperties(self, have_image_pos):
338 """Add new properties to the device tree as needed for this entry
339
340 Args:
341 have_image_pos: True if this entry has an image position. This can
342 be False if its parent section is compressed, since compression
343 groups all entries together into a compressed block of data,
344 obscuring the start of each individual child entry
345 """
346 for prop in ['offset', 'size']:
Simon Glasse22f8fa2018-07-06 10:27:41 -0600347 if not prop in self._node.props:
Simon Glassc8135dc2018-09-14 04:57:21 -0600348 state.AddZeroProp(self._node, prop)
Simon Glassacd6c6e2020-10-26 17:40:17 -0600349 if have_image_pos and 'image-pos' not in self._node.props:
350 state.AddZeroProp(self._node, 'image-pos')
Simon Glassfb30e292019-07-20 12:23:51 -0600351 if self.GetImage().allow_repack:
352 if self.orig_offset is not None:
353 state.AddZeroProp(self._node, 'orig-offset', True)
354 if self.orig_size is not None:
355 state.AddZeroProp(self._node, 'orig-size', True)
356
Simon Glassaa2fcf92019-07-08 14:25:30 -0600357 if self.compress != 'none':
358 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300359
360 if self.update_hash:
361 err = state.CheckAddHashProp(self._node)
362 if err:
363 self.Raise(err)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600364
365 def SetCalculatedProperties(self):
366 """Set the value of device-tree properties calculated by binman"""
Simon Glassc8135dc2018-09-14 04:57:21 -0600367 state.SetInt(self._node, 'offset', self.offset)
368 state.SetInt(self._node, 'size', self.size)
Simon Glass39dd2152019-07-08 14:25:47 -0600369 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassacd6c6e2020-10-26 17:40:17 -0600370 if self.image_pos is not None:
Simon Glasseb943b12020-11-02 12:55:44 -0700371 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glassfb30e292019-07-20 12:23:51 -0600372 if self.GetImage().allow_repack:
373 if self.orig_offset is not None:
374 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
375 if self.orig_size is not None:
376 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glassaa2fcf92019-07-08 14:25:30 -0600377 if self.uncomp_size is not None:
378 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300379
380 if self.update_hash:
381 state.CheckSetHashValue(self._node, self.GetData)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600382
Simon Glass92307732018-07-06 10:27:40 -0600383 def ProcessFdt(self, fdt):
Simon Glasse219aa42018-09-14 04:57:24 -0600384 """Allow entries to adjust the device tree
385
386 Some entries need to adjust the device tree for their purposes. This
387 may involve adding or deleting properties.
388
389 Returns:
390 True if processing is complete
391 False if processing could not be completed due to a dependency.
392 This will cause the entry to be retried after others have been
393 called
394 """
Simon Glass92307732018-07-06 10:27:40 -0600395 return True
396
Simon Glass3b78d532018-06-01 09:38:21 -0600397 def SetPrefix(self, prefix):
398 """Set the name prefix for a node
399
400 Args:
401 prefix: Prefix to set, or '' to not use a prefix
402 """
403 if prefix:
404 self.name = prefix + self.name
405
Simon Glass2e1169f2018-07-06 10:27:19 -0600406 def SetContents(self, data):
407 """Set the contents of an entry
408
409 This sets both the data and content_size properties
410
411 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600412 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600413 """
414 self.data = data
415 self.contents_size = len(self.data)
416
417 def ProcessContentsUpdate(self, data):
Simon Glassd17dfea2019-07-08 14:25:33 -0600418 """Update the contents of an entry, after the size is fixed
Simon Glass2e1169f2018-07-06 10:27:19 -0600419
Simon Glassec849852019-07-08 14:25:35 -0600420 This checks that the new data is the same size as the old. If the size
421 has changed, this triggers a re-run of the packing algorithm.
Simon Glass2e1169f2018-07-06 10:27:19 -0600422
423 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600424 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600425
426 Raises:
427 ValueError if the new data size is not the same as the old
428 """
Simon Glassec849852019-07-08 14:25:35 -0600429 size_ok = True
Simon Glasse61b6f62019-07-08 14:25:37 -0600430 new_size = len(data)
Simon Glass9d8ee322019-07-20 12:23:58 -0600431 if state.AllowEntryExpansion() and new_size > self.contents_size:
432 # self.data will indicate the new size needed
433 size_ok = False
434 elif state.AllowEntryContraction() and new_size < self.contents_size:
435 size_ok = False
436
437 # If not allowed to change, try to deal with it or give up
438 if size_ok:
Simon Glasse61b6f62019-07-08 14:25:37 -0600439 if new_size > self.contents_size:
Simon Glass9d8ee322019-07-20 12:23:58 -0600440 self.Raise('Cannot update entry size from %d to %d' %
441 (self.contents_size, new_size))
442
443 # Don't let the data shrink. Pad it if necessary
444 if size_ok and new_size < self.contents_size:
Simon Glass80025522022-01-29 14:14:04 -0700445 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass9d8ee322019-07-20 12:23:58 -0600446
447 if not size_ok:
Simon Glass011f1b32022-01-29 14:14:15 -0700448 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glass80025522022-01-29 14:14:04 -0700449 self._node.path, to_hex(self.contents_size),
450 to_hex(new_size)))
Simon Glass2e1169f2018-07-06 10:27:19 -0600451 self.SetContents(data)
Simon Glassec849852019-07-08 14:25:35 -0600452 return size_ok
Simon Glass2e1169f2018-07-06 10:27:19 -0600453
Simon Glassfc5a1682022-03-05 20:19:05 -0700454 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glass2574ef62016-11-25 20:15:51 -0700455 """Figure out the contents of an entry.
456
Simon Glassfc5a1682022-03-05 20:19:05 -0700457 Args:
458 skip_entry (Entry): Entry to skip when obtaining section contents
459 fake_size (int): Size of fake file to create if needed
460
Simon Glass2574ef62016-11-25 20:15:51 -0700461 Returns:
462 True if the contents were found, False if another call is needed
Simon Glassa4948b22023-01-11 16:10:14 -0700463 after the other entries are processed, None if there is no contents
Simon Glass2574ef62016-11-25 20:15:51 -0700464 """
465 # No contents by default: subclasses can implement this
466 return True
467
Simon Glasse61b6f62019-07-08 14:25:37 -0600468 def ResetForPack(self):
469 """Reset offset/size fields so that packing can be done again"""
Simon Glassb6dff4c2019-07-20 12:23:36 -0600470 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glass80025522022-01-29 14:14:04 -0700471 (to_hex(self.offset), to_hex(self.orig_offset),
472 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass1fdb4872019-10-31 07:43:02 -0600473 self.pre_reset_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600474 self.offset = self.orig_offset
475 self.size = self.orig_size
476
Simon Glasse8561af2018-08-01 15:22:37 -0600477 def Pack(self, offset):
Simon Glassad5a7712018-06-01 09:38:14 -0600478 """Figure out how to pack the entry into the section
Simon Glass2574ef62016-11-25 20:15:51 -0700479
480 Most of the time the entries are not fully specified. There may be
481 an alignment but no size. In that case we take the size from the
482 contents of the entry.
483
Simon Glasse8561af2018-08-01 15:22:37 -0600484 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glass2574ef62016-11-25 20:15:51 -0700485
Simon Glasse8561af2018-08-01 15:22:37 -0600486 Once this function is complete, both the offset and size of the
Simon Glass2574ef62016-11-25 20:15:51 -0700487 entry will be know.
488
489 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600490 Current section offset pointer
Simon Glass2574ef62016-11-25 20:15:51 -0700491
492 Returns:
Simon Glasse8561af2018-08-01 15:22:37 -0600493 New section offset pointer (after this entry)
Simon Glass2574ef62016-11-25 20:15:51 -0700494 """
Simon Glassb6dff4c2019-07-20 12:23:36 -0600495 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glass80025522022-01-29 14:14:04 -0700496 (to_hex(self.offset), to_hex(self.size),
Simon Glassb6dff4c2019-07-20 12:23:36 -0600497 self.contents_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600498 if self.offset is None:
499 if self.offset_unset:
500 self.Raise('No offset set with offset-unset: should another '
501 'entry provide this correct offset?')
Simon Glass80025522022-01-29 14:14:04 -0700502 self.offset = tools.align(offset, self.align)
Simon Glass2574ef62016-11-25 20:15:51 -0700503 needed = self.pad_before + self.contents_size + self.pad_after
Simon Glass80025522022-01-29 14:14:04 -0700504 needed = tools.align(needed, self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700505 size = self.size
506 if not size:
507 size = needed
Simon Glasse8561af2018-08-01 15:22:37 -0600508 new_offset = self.offset + size
Simon Glass80025522022-01-29 14:14:04 -0700509 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glasse8561af2018-08-01 15:22:37 -0600510 if aligned_offset != new_offset:
511 size = aligned_offset - self.offset
512 new_offset = aligned_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700513
514 if not self.size:
515 self.size = size
516
517 if self.size < needed:
518 self.Raise("Entry contents size is %#x (%d) but entry size is "
519 "%#x (%d)" % (needed, needed, self.size, self.size))
520 # Check that the alignment is correct. It could be wrong if the
Simon Glasse8561af2018-08-01 15:22:37 -0600521 # and offset or size values were provided (i.e. not calculated), but
Simon Glass2574ef62016-11-25 20:15:51 -0700522 # conflict with the provided alignment values
Simon Glass80025522022-01-29 14:14:04 -0700523 if self.size != tools.align(self.size, self.align_size):
Simon Glass2574ef62016-11-25 20:15:51 -0700524 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
525 (self.size, self.size, self.align_size, self.align_size))
Simon Glass80025522022-01-29 14:14:04 -0700526 if self.offset != tools.align(self.offset, self.align):
Simon Glasse8561af2018-08-01 15:22:37 -0600527 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
528 (self.offset, self.offset, self.align, self.align))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600529 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
530 (self.offset, self.size, self.contents_size, new_offset))
Simon Glass2574ef62016-11-25 20:15:51 -0700531
Simon Glasse8561af2018-08-01 15:22:37 -0600532 return new_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700533
534 def Raise(self, msg):
535 """Convenience function to raise an error referencing a node"""
536 raise ValueError("Node '%s': %s" % (self._node.path, msg))
537
Simon Glasse1915782021-03-21 18:24:31 +1300538 def Info(self, msg):
539 """Convenience function to log info referencing a node"""
540 tag = "Info '%s'" % self._node.path
Simon Glass011f1b32022-01-29 14:14:15 -0700541 tout.detail('%30s: %s' % (tag, msg))
Simon Glasse1915782021-03-21 18:24:31 +1300542
Simon Glassb6dff4c2019-07-20 12:23:36 -0600543 def Detail(self, msg):
544 """Convenience function to log detail referencing a node"""
545 tag = "Node '%s'" % self._node.path
Simon Glass011f1b32022-01-29 14:14:15 -0700546 tout.detail('%30s: %s' % (tag, msg))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600547
Simon Glass91710b32018-07-17 13:25:32 -0600548 def GetEntryArgsOrProps(self, props, required=False):
549 """Return the values of a set of properties
550
551 Args:
552 props: List of EntryArg objects
553
554 Raises:
555 ValueError if a property is not found
556 """
557 values = []
558 missing = []
559 for prop in props:
560 python_prop = prop.name.replace('-', '_')
561 if hasattr(self, python_prop):
562 value = getattr(self, python_prop)
563 else:
564 value = None
565 if value is None:
566 value = self.GetArg(prop.name, prop.datatype)
567 if value is None and required:
568 missing.append(prop.name)
569 values.append(value)
570 if missing:
Simon Glass3fb25402021-01-06 21:35:16 -0700571 self.GetImage().MissingArgs(self, missing)
Simon Glass91710b32018-07-17 13:25:32 -0600572 return values
573
Simon Glass2574ef62016-11-25 20:15:51 -0700574 def GetPath(self):
575 """Get the path of a node
576
577 Returns:
578 Full path of the node for this entry
579 """
580 return self._node.path
581
Simon Glass27a7f772021-03-21 18:24:32 +1300582 def GetData(self, required=True):
Simon Glass72eeff12020-10-26 17:40:16 -0600583 """Get the contents of an entry
584
Simon Glass27a7f772021-03-21 18:24:32 +1300585 Args:
586 required: True if the data must be present, False if it is OK to
587 return None
588
Simon Glass72eeff12020-10-26 17:40:16 -0600589 Returns:
590 bytes content of the entry, excluding any padding. If the entry is
Simon Glass02997652023-01-11 16:10:13 -0700591 compressed, the compressed data is returned. If the entry data
Simon Glassa4948b22023-01-11 16:10:14 -0700592 is not yet available, False can be returned. If the entry data
593 is null, then None is returned.
Simon Glass72eeff12020-10-26 17:40:16 -0600594 """
Simon Glass80025522022-01-29 14:14:04 -0700595 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glass2574ef62016-11-25 20:15:51 -0700596 return self.data
597
Simon Glasse17220f2020-11-02 12:55:43 -0700598 def GetPaddedData(self, data=None):
599 """Get the data for an entry including any padding
600
601 Gets the entry data and uses its section's pad-byte value to add padding
602 before and after as defined by the pad-before and pad-after properties.
603
604 This does not consider alignment.
605
606 Returns:
607 Contents of the entry along with any pad bytes before and
608 after it (bytes)
609 """
610 if data is None:
611 data = self.GetData()
612 return self.section.GetPaddedDataForEntry(self, data)
613
Simon Glasse8561af2018-08-01 15:22:37 -0600614 def GetOffsets(self):
Simon Glass224bc662019-07-08 13:18:30 -0600615 """Get the offsets for siblings
616
617 Some entry types can contain information about the position or size of
618 other entries. An example of this is the Intel Flash Descriptor, which
619 knows where the Intel Management Engine section should go.
620
621 If this entry knows about the position of other entries, it can specify
622 this by returning values here
623
624 Returns:
625 Dict:
626 key: Entry type
627 value: List containing position and size of the given entry
Simon Glassed365eb2019-07-08 13:18:39 -0600628 type. Either can be None if not known
Simon Glass224bc662019-07-08 13:18:30 -0600629 """
Simon Glass2574ef62016-11-25 20:15:51 -0700630 return {}
631
Simon Glassed365eb2019-07-08 13:18:39 -0600632 def SetOffsetSize(self, offset, size):
633 """Set the offset and/or size of an entry
634
635 Args:
636 offset: New offset, or None to leave alone
637 size: New size, or None to leave alone
638 """
639 if offset is not None:
640 self.offset = offset
641 if size is not None:
642 self.size = size
Simon Glass2574ef62016-11-25 20:15:51 -0700643
Simon Glass9dcc8612018-08-01 15:22:42 -0600644 def SetImagePos(self, image_pos):
645 """Set the position in the image
646
647 Args:
648 image_pos: Position of this entry in the image
649 """
650 self.image_pos = image_pos + self.offset
651
Simon Glass2574ef62016-11-25 20:15:51 -0700652 def ProcessContents(self):
Simon Glassec849852019-07-08 14:25:35 -0600653 """Do any post-packing updates of entry contents
654
655 This function should call ProcessContentsUpdate() to update the entry
656 contents, if necessary, returning its return value here.
657
658 Args:
659 data: Data to set to the contents (bytes)
660
661 Returns:
662 True if the new data size is OK, False if expansion is needed
663
664 Raises:
665 ValueError if the new data size is not the same as the old and
666 state.AllowEntryExpansion() is False
667 """
668 return True
Simon Glass4ca8e042017-11-13 18:55:01 -0700669
Simon Glass8a6f56e2018-06-01 09:38:13 -0600670 def WriteSymbols(self, section):
Simon Glass4ca8e042017-11-13 18:55:01 -0700671 """Write symbol values into binary files for access at run time
672
673 Args:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600674 section: Section containing the entry
Simon Glass4ca8e042017-11-13 18:55:01 -0700675 """
Simon Glass6fc079e2022-10-20 18:22:46 -0600676 if self.auto_write_symbols:
Simon Glass37f85de2022-10-20 18:22:47 -0600677 # Check if we are writing symbols into an ELF file
678 is_elf = self.GetDefaultFilename() == self.elf_fname
679 elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(),
Simon Glasse0035c92023-01-11 16:10:17 -0700680 is_elf, self.elf_base_sym)
Simon Glassa91e1152018-06-01 09:38:16 -0600681
Simon Glass55f68072020-10-26 17:40:18 -0600682 def CheckEntries(self):
Simon Glasse8561af2018-08-01 15:22:37 -0600683 """Check that the entry offsets are correct
Simon Glassa91e1152018-06-01 09:38:16 -0600684
Simon Glasse8561af2018-08-01 15:22:37 -0600685 This is used for entries which have extra offset requirements (other
Simon Glassa91e1152018-06-01 09:38:16 -0600686 than having to be fully inside their section). Sub-classes can implement
687 this function and raise if there is a problem.
688 """
689 pass
Simon Glass30732662018-06-01 09:38:20 -0600690
Simon Glass3a9a2b82018-07-17 13:25:28 -0600691 @staticmethod
Simon Glasscd817d52018-09-14 04:57:36 -0600692 def GetStr(value):
693 if value is None:
694 return '<none> '
695 return '%08x' % value
696
697 @staticmethod
Simon Glass7eca7922018-07-17 13:25:49 -0600698 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glasscd817d52018-09-14 04:57:36 -0600699 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
700 Entry.GetStr(offset), Entry.GetStr(size),
701 name), file=fd)
Simon Glass3a9a2b82018-07-17 13:25:28 -0600702
Simon Glass30732662018-06-01 09:38:20 -0600703 def WriteMap(self, fd, indent):
704 """Write a map of the entry to a .map file
705
706 Args:
707 fd: File to write the map to
708 indent: Curent indent level of map (0=none, 1=one level, etc.)
709 """
Simon Glass7eca7922018-07-17 13:25:49 -0600710 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
711 self.image_pos)
Simon Glass91710b32018-07-17 13:25:32 -0600712
Simon Glassbd5cd882022-08-13 11:40:50 -0600713 # pylint: disable=assignment-from-none
Simon Glass704784b2018-07-17 13:25:38 -0600714 def GetEntries(self):
715 """Return a list of entries contained by this entry
716
717 Returns:
718 List of entries, or None if none. A normal entry has no entries
719 within it so will return None
720 """
721 return None
722
Simon Glassbd5cd882022-08-13 11:40:50 -0600723 def FindEntryByNode(self, find_node):
724 """Find a node in an entry, searching all subentries
725
726 This does a recursive search.
727
728 Args:
729 find_node (fdt.Node): Node to find
730
731 Returns:
732 Entry: entry, if found, else None
733 """
734 entries = self.GetEntries()
735 if entries:
736 for entry in entries.values():
737 if entry._node == find_node:
738 return entry
739 found = entry.FindEntryByNode(find_node)
740 if found:
741 return found
742
743 return None
744
Simon Glass91710b32018-07-17 13:25:32 -0600745 def GetArg(self, name, datatype=str):
746 """Get the value of an entry argument or device-tree-node property
747
748 Some node properties can be provided as arguments to binman. First check
749 the entry arguments, and fall back to the device tree if not found
750
751 Args:
752 name: Argument name
753 datatype: Data type (str or int)
754
755 Returns:
756 Value of argument as a string or int, or None if no value
757
758 Raises:
759 ValueError if the argument cannot be converted to in
760 """
Simon Glass29aa7362018-09-14 04:57:19 -0600761 value = state.GetEntryArg(name)
Simon Glass91710b32018-07-17 13:25:32 -0600762 if value is not None:
763 if datatype == int:
764 try:
765 value = int(value)
766 except ValueError:
767 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
768 (name, value))
769 elif datatype == str:
770 pass
771 else:
772 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
773 datatype)
774 else:
775 value = fdt_util.GetDatatype(self._node, name, datatype)
776 return value
Simon Glass969616c2018-07-17 13:25:36 -0600777
778 @staticmethod
779 def WriteDocs(modules, test_missing=None):
780 """Write out documentation about the various entry types to stdout
781
782 Args:
783 modules: List of modules to include
784 test_missing: Used for testing. This is a module to report
785 as missing
786 """
787 print('''Binman Entry Documentation
788===========================
789
790This file describes the entry types supported by binman. These entry types can
791be placed in an image one by one to build up a final firmware image. It is
792fairly easy to create new entry types. Just add a new file to the 'etype'
793directory. You can use the existing entries as examples.
794
795Note that some entries are subclasses of others, using and extending their
796features to produce new behaviours.
797
798
799''')
800 modules = sorted(modules)
801
802 # Don't show the test entry
803 if '_testing' in modules:
804 modules.remove('_testing')
805 missing = []
806 for name in modules:
Simon Glass2f859412021-03-18 20:25:04 +1300807 module = Entry.Lookup('WriteDocs', name, False)
Simon Glass969616c2018-07-17 13:25:36 -0600808 docs = getattr(module, '__doc__')
809 if test_missing == name:
810 docs = None
811 if docs:
812 lines = docs.splitlines()
813 first_line = lines[0]
814 rest = [line[4:] for line in lines[1:]]
815 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glassa7c97782022-08-07 16:33:25 -0600816
817 # Create a reference for use by rST docs
818 ref_name = f'etype_{module.__name__[6:]}'.lower()
819 print('.. _%s:' % ref_name)
820 print()
Simon Glass969616c2018-07-17 13:25:36 -0600821 print(hdr)
822 print('-' * len(hdr))
823 print('\n'.join(rest))
824 print()
825 print()
826 else:
827 missing.append(name)
828
829 if missing:
830 raise ValueError('Documentation is missing for modules: %s' %
831 ', '.join(missing))
Simon Glass639505b2018-09-14 04:57:11 -0600832
833 def GetUniqueName(self):
834 """Get a unique name for a node
835
836 Returns:
837 String containing a unique name for a node, consisting of the name
838 of all ancestors (starting from within the 'binman' node) separated
839 by a dot ('.'). This can be useful for generating unique filesnames
840 in the output directory.
841 """
842 name = self.name
843 node = self._node
844 while node.parent:
845 node = node.parent
Alper Nebi Yasak5cff63f2022-03-27 18:31:44 +0300846 if node.name in ('binman', '/'):
Simon Glass639505b2018-09-14 04:57:11 -0600847 break
848 name = '%s.%s' % (node.name, name)
849 return name
Simon Glassfa79a812018-09-14 04:57:29 -0600850
Simon Glassdd156a42022-03-05 20:18:59 -0700851 def extend_to_limit(self, limit):
852 """Extend an entry so that it ends at the given offset limit"""
Simon Glassfa79a812018-09-14 04:57:29 -0600853 if self.offset + self.size < limit:
854 self.size = limit - self.offset
855 # Request the contents again, since changing the size requires that
856 # the data grows. This should not fail, but check it to be sure.
857 if not self.ObtainContents():
858 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassc4056b82019-07-08 13:18:38 -0600859
860 def HasSibling(self, name):
861 """Check if there is a sibling of a given name
862
863 Returns:
864 True if there is an entry with this name in the the same section,
865 else False
866 """
867 return name in self.section.GetEntries()
Simon Glasscec34ba2019-07-08 14:25:28 -0600868
869 def GetSiblingImagePos(self, name):
870 """Return the image position of the given sibling
871
872 Returns:
873 Image position of sibling, or None if the sibling has no position,
874 or False if there is no such sibling
875 """
876 if not self.HasSibling(name):
877 return False
878 return self.section.GetEntries()[name].image_pos
Simon Glass6b156f82019-07-08 14:25:43 -0600879
880 @staticmethod
881 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
882 uncomp_size, offset, entry):
883 """Add a new entry to the entries list
884
885 Args:
886 entries: List (of EntryInfo objects) to add to
887 indent: Current indent level to add to list
888 name: Entry name (string)
889 etype: Entry type (string)
890 size: Entry size in bytes (int)
891 image_pos: Position within image in bytes (int)
892 uncomp_size: Uncompressed size if the entry uses compression, else
893 None
894 offset: Entry offset within parent in bytes (int)
895 entry: Entry object
896 """
897 entries.append(EntryInfo(indent, name, etype, size, image_pos,
898 uncomp_size, offset, entry))
899
900 def ListEntries(self, entries, indent):
901 """Add files in this entry to the list of entries
902
903 This can be overridden by subclasses which need different behaviour.
904
905 Args:
906 entries: List (of EntryInfo objects) to add to
907 indent: Current indent level to add to list
908 """
909 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
910 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glass4c613bf2019-07-08 14:25:50 -0600911
Simon Glass637958f2021-11-23 21:09:50 -0700912 def ReadData(self, decomp=True, alt_format=None):
Simon Glass4c613bf2019-07-08 14:25:50 -0600913 """Read the data for an entry from the image
914
915 This is used when the image has been read in and we want to extract the
916 data for a particular entry from that image.
917
918 Args:
919 decomp: True to decompress any compressed data before returning it;
920 False to return the raw, uncompressed data
921
922 Returns:
923 Entry data (bytes)
924 """
925 # Use True here so that we get an uncompressed section to work from,
926 # although compressed sections are currently not supported
Simon Glass011f1b32022-01-29 14:14:15 -0700927 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass4d8151f2019-09-25 08:56:21 -0600928 (self.section.GetPath(), self.GetPath()))
Simon Glass637958f2021-11-23 21:09:50 -0700929 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glass0cd8ace2019-07-20 12:24:04 -0600930 return data
Simon Glassaf8c45c2019-07-20 12:23:41 -0600931
Simon Glass637958f2021-11-23 21:09:50 -0700932 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass4d8151f2019-09-25 08:56:21 -0600933 """Read the data for a particular child entry
Simon Glass23f00472019-09-25 08:56:20 -0600934
935 This reads data from the parent and extracts the piece that relates to
936 the given child.
937
938 Args:
Simon Glass637958f2021-11-23 21:09:50 -0700939 child (Entry): Child entry to read data for (must be valid)
940 decomp (bool): True to decompress any compressed data before
941 returning it; False to return the raw, uncompressed data
942 alt_format (str): Alternative format to read in, or None
Simon Glass23f00472019-09-25 08:56:20 -0600943
944 Returns:
945 Data for the child (bytes)
946 """
947 pass
948
Simon Glassaf8c45c2019-07-20 12:23:41 -0600949 def LoadData(self, decomp=True):
950 data = self.ReadData(decomp)
Simon Glass072959a2019-07-20 12:23:50 -0600951 self.contents_size = len(data)
Simon Glassaf8c45c2019-07-20 12:23:41 -0600952 self.ProcessContentsUpdate(data)
953 self.Detail('Loaded data size %x' % len(data))
Simon Glass990b1742019-07-20 12:23:46 -0600954
Simon Glass637958f2021-11-23 21:09:50 -0700955 def GetAltFormat(self, data, alt_format):
956 """Read the data for an extry in an alternative format
957
958 Supported formats are list in the documentation for each entry. An
959 example is fdtmap which provides .
960
961 Args:
962 data (bytes): Data to convert (this should have been produced by the
963 entry)
964 alt_format (str): Format to use
965
966 """
967 pass
968
Simon Glass990b1742019-07-20 12:23:46 -0600969 def GetImage(self):
970 """Get the image containing this entry
971
972 Returns:
973 Image object containing this entry
974 """
975 return self.section.GetImage()
Simon Glass072959a2019-07-20 12:23:50 -0600976
977 def WriteData(self, data, decomp=True):
978 """Write the data to an entry in the image
979
980 This is used when the image has been read in and we want to replace the
981 data for a particular entry in that image.
982
983 The image must be re-packed and written out afterwards.
984
985 Args:
986 data: Data to replace it with
987 decomp: True to compress the data if needed, False if data is
988 already compressed so should be used as is
989
990 Returns:
991 True if the data did not result in a resize of this entry, False if
992 the entry must be resized
993 """
Simon Glass1fdb4872019-10-31 07:43:02 -0600994 if self.size is not None:
995 self.contents_size = self.size
996 else:
997 self.contents_size = self.pre_reset_size
Simon Glass072959a2019-07-20 12:23:50 -0600998 ok = self.ProcessContentsUpdate(data)
999 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glassd34af7a2019-07-20 12:24:05 -06001000 section_ok = self.section.WriteChildData(self)
1001 return ok and section_ok
1002
1003 def WriteChildData(self, child):
1004 """Handle writing the data in a child entry
1005
1006 This should be called on the child's parent section after the child's
Simon Glasse796f242021-11-23 11:03:44 -07001007 data has been updated. It should update any data structures needed to
1008 validate that the update is successful.
Simon Glassd34af7a2019-07-20 12:24:05 -06001009
1010 This base-class implementation does nothing, since the base Entry object
1011 does not have any children.
1012
1013 Args:
1014 child: Child Entry that was written
1015
1016 Returns:
1017 True if the section could be updated successfully, False if the
Simon Glasse796f242021-11-23 11:03:44 -07001018 data is such that the section could not update
Simon Glassd34af7a2019-07-20 12:24:05 -06001019 """
1020 return True
Simon Glass11453762019-07-20 12:23:55 -06001021
1022 def GetSiblingOrder(self):
1023 """Get the relative order of an entry amoung its siblings
1024
1025 Returns:
1026 'start' if this entry is first among siblings, 'end' if last,
1027 otherwise None
1028 """
1029 entries = list(self.section.GetEntries().values())
1030 if entries:
1031 if self == entries[0]:
1032 return 'start'
1033 elif self == entries[-1]:
1034 return 'end'
1035 return 'middle'
Simon Glass5d94cc62020-07-09 18:39:38 -06001036
1037 def SetAllowMissing(self, allow_missing):
1038 """Set whether a section allows missing external blobs
1039
1040 Args:
1041 allow_missing: True if allowed, False if not allowed
1042 """
1043 # This is meaningless for anything other than sections
1044 pass
Simon Glassa003cd32020-07-09 18:39:40 -06001045
Heiko Thiery6d451362022-01-06 11:49:41 +01001046 def SetAllowFakeBlob(self, allow_fake):
1047 """Set whether a section allows to create a fake blob
1048
1049 Args:
1050 allow_fake: True if allowed, False if not allowed
1051 """
Simon Glassceb5f912022-01-09 20:13:46 -07001052 self.allow_fake = allow_fake
Heiko Thiery6d451362022-01-06 11:49:41 +01001053
Simon Glassa003cd32020-07-09 18:39:40 -06001054 def CheckMissing(self, missing_list):
Simon Glass63328f12023-01-07 14:07:15 -07001055 """Check if the entry has missing external blobs
Simon Glassa003cd32020-07-09 18:39:40 -06001056
Simon Glass63328f12023-01-07 14:07:15 -07001057 If there are missing (non-optional) blobs, the entries are added to the
1058 list
Simon Glassa003cd32020-07-09 18:39:40 -06001059
1060 Args:
1061 missing_list: List of Entry objects to be added to
1062 """
Simon Glass63328f12023-01-07 14:07:15 -07001063 if self.missing and not self.optional:
Simon Glassa003cd32020-07-09 18:39:40 -06001064 missing_list.append(self)
Simon Glassb8f90372020-09-01 05:13:57 -06001065
Simon Glass8c0533b2022-03-05 20:19:04 -07001066 def check_fake_fname(self, fname, size=0):
Simon Glass7a602fd2022-01-12 13:10:36 -07001067 """If the file is missing and the entry allows fake blobs, fake it
1068
1069 Sets self.faked to True if faked
1070
1071 Args:
1072 fname (str): Filename to check
Simon Glass8c0533b2022-03-05 20:19:04 -07001073 size (int): Size of fake file to create
Simon Glass7a602fd2022-01-12 13:10:36 -07001074
1075 Returns:
Simon Glass214d36f2022-03-05 20:19:03 -07001076 tuple:
1077 fname (str): Filename of faked file
1078 bool: True if the blob was faked, False if not
Simon Glass7a602fd2022-01-12 13:10:36 -07001079 """
1080 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass7d3e4072022-08-07 09:46:46 -06001081 if not self.fake_fname:
1082 outfname = os.path.join(self.fake_dir, os.path.basename(fname))
1083 with open(outfname, "wb") as out:
1084 out.truncate(size)
1085 tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
1086 self.fake_fname = outfname
Simon Glass7a602fd2022-01-12 13:10:36 -07001087 self.faked = True
Simon Glass7d3e4072022-08-07 09:46:46 -06001088 return self.fake_fname, True
Simon Glass214d36f2022-03-05 20:19:03 -07001089 return fname, False
Simon Glass7a602fd2022-01-12 13:10:36 -07001090
Heiko Thiery6d451362022-01-06 11:49:41 +01001091 def CheckFakedBlobs(self, faked_blobs_list):
1092 """Check if any entries in this section have faked external blobs
1093
1094 If there are faked blobs, the entries are added to the list
1095
1096 Args:
1097 fake_blobs_list: List of Entry objects to be added to
1098 """
1099 # This is meaningless for anything other than blobs
1100 pass
1101
Simon Glass63328f12023-01-07 14:07:15 -07001102 def CheckOptional(self, optional_list):
1103 """Check if the entry has missing but optional external blobs
1104
1105 If there are missing (optional) blobs, the entries are added to the list
1106
1107 Args:
1108 optional_list (list): List of Entry objects to be added to
1109 """
1110 if self.missing and self.optional:
1111 optional_list.append(self)
1112
Simon Glassb8f90372020-09-01 05:13:57 -06001113 def GetAllowMissing(self):
1114 """Get whether a section allows missing external blobs
1115
1116 Returns:
1117 True if allowed, False if not allowed
1118 """
1119 return self.allow_missing
Simon Glassa820af72020-09-06 10:39:09 -06001120
Simon Glass66152ce2022-01-09 20:14:09 -07001121 def record_missing_bintool(self, bintool):
1122 """Record a missing bintool that was needed to produce this entry
1123
1124 Args:
1125 bintool (Bintool): Bintool that was missing
1126 """
Stefan Herbrechtsmeier486b9442022-08-19 16:25:19 +02001127 if bintool not in self.missing_bintools:
1128 self.missing_bintools.append(bintool)
Simon Glass66152ce2022-01-09 20:14:09 -07001129
1130 def check_missing_bintools(self, missing_list):
1131 """Check if any entries in this section have missing bintools
1132
1133 If there are missing bintools, these are added to the list
1134
1135 Args:
1136 missing_list: List of Bintool objects to be added to
1137 """
Stefan Herbrechtsmeier486b9442022-08-19 16:25:19 +02001138 for bintool in self.missing_bintools:
1139 if bintool not in missing_list:
1140 missing_list.append(bintool)
1141
Simon Glass66152ce2022-01-09 20:14:09 -07001142
Simon Glassa820af72020-09-06 10:39:09 -06001143 def GetHelpTags(self):
1144 """Get the tags use for missing-blob help
1145
1146 Returns:
1147 list of possible tags, most desirable first
1148 """
1149 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glassa1301a22020-10-26 17:40:06 -06001150
1151 def CompressData(self, indata):
1152 """Compress data according to the entry's compression method
1153
1154 Args:
1155 indata: Data to compress
1156
1157 Returns:
Stefan Herbrechtsmeierb2f8d612022-08-19 16:25:27 +02001158 Compressed data
Simon Glassa1301a22020-10-26 17:40:06 -06001159 """
Simon Glass789b34402020-10-26 17:40:15 -06001160 self.uncomp_data = indata
Simon Glassa1301a22020-10-26 17:40:06 -06001161 if self.compress != 'none':
1162 self.uncomp_size = len(indata)
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02001163 if self.comp_bintool.is_present():
1164 data = self.comp_bintool.compress(indata)
1165 else:
1166 self.record_missing_bintool(self.comp_bintool)
1167 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001168 else:
1169 data = indata
Simon Glassa1301a22020-10-26 17:40:06 -06001170 return data
Simon Glass2f859412021-03-18 20:25:04 +13001171
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001172 def DecompressData(self, indata):
1173 """Decompress data according to the entry's compression method
1174
1175 Args:
1176 indata: Data to decompress
1177
1178 Returns:
1179 Decompressed data
1180 """
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001181 if self.compress != 'none':
Stefan Herbrechtsmeier94813a02022-08-19 16:25:31 +02001182 if self.comp_bintool.is_present():
1183 data = self.comp_bintool.decompress(indata)
1184 self.uncomp_size = len(data)
1185 else:
1186 self.record_missing_bintool(self.comp_bintool)
1187 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001188 else:
1189 data = indata
Stefan Herbrechtsmeier7f128a72022-08-19 16:25:24 +02001190 self.uncomp_data = data
1191 return data
1192
Simon Glass2f859412021-03-18 20:25:04 +13001193 @classmethod
1194 def UseExpanded(cls, node, etype, new_etype):
1195 """Check whether to use an expanded entry type
1196
1197 This is called by Entry.Create() when it finds an expanded version of
1198 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1199 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1200 ignored.
1201
1202 Args:
1203 node: Node object containing information about the entry to
1204 create
1205 etype: Original entry type being used
1206 new_etype: New entry type proposed
1207
1208 Returns:
1209 True to use this entry type, False to use the original one
1210 """
Simon Glass011f1b32022-01-29 14:14:15 -07001211 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glass2f859412021-03-18 20:25:04 +13001212 (node.path, etype, new_etype))
1213 return True
Simon Glass637958f2021-11-23 21:09:50 -07001214
1215 def CheckAltFormats(self, alt_formats):
1216 """Add any alternative formats supported by this entry type
1217
1218 Args:
1219 alt_formats (dict): Dict to add alt_formats to:
1220 key: Name of alt format
1221 value: Help text
1222 """
1223 pass
Simon Glass4eae9252022-01-09 20:13:50 -07001224
Simon Glassfff147a2022-03-05 20:19:02 -07001225 def AddBintools(self, btools):
Simon Glass4eae9252022-01-09 20:13:50 -07001226 """Add the bintools used by this entry type
1227
1228 Args:
Simon Glassfff147a2022-03-05 20:19:02 -07001229 btools (dict of Bintool):
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001230
1231 Raise:
1232 ValueError if compression algorithm is not supported
Simon Glass4eae9252022-01-09 20:13:50 -07001233 """
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001234 algo = self.compress
1235 if algo != 'none':
Stefan Herbrechtsmeiera5e4dcb2022-08-19 16:25:38 +02001236 algos = ['bzip2', 'gzip', 'lz4', 'lzma', 'lzo', 'xz', 'zstd']
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001237 if algo not in algos:
1238 raise ValueError("Unknown algorithm '%s'" % algo)
Stefan Herbrechtsmeier9087fc52022-08-19 16:25:36 +02001239 names = {'lzma': 'lzma_alone', 'lzo': 'lzop'}
Stefan Herbrechtsmeiera6e0b502022-08-19 16:25:30 +02001240 name = names.get(self.compress, self.compress)
1241 self.comp_bintool = self.AddBintool(btools, name)
Simon Glass4eae9252022-01-09 20:13:50 -07001242
1243 @classmethod
1244 def AddBintool(self, tools, name):
1245 """Add a new bintool to the tools used by this etype
1246
1247 Args:
1248 name: Name of the tool
1249 """
1250 btool = bintool.Bintool.create(name)
1251 tools[name] = btool
1252 return btool
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03001253
1254 def SetUpdateHash(self, update_hash):
1255 """Set whether this entry's "hash" subnode should be updated
1256
1257 Args:
1258 update_hash: True if hash should be updated, False if not
1259 """
1260 self.update_hash = update_hash
Simon Glass6fba35c2022-02-08 11:50:00 -07001261
Simon Glassfc5a1682022-03-05 20:19:05 -07001262 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass6fba35c2022-02-08 11:50:00 -07001263 """Put the contents of a list of entries into a file
1264
1265 Args:
1266 entries (list of Entry): Entries to collect
1267 prefix (str): Filename prefix of file to write to
Simon Glassfc5a1682022-03-05 20:19:05 -07001268 fake_size (int): Size of fake file to create if needed
Simon Glass6fba35c2022-02-08 11:50:00 -07001269
1270 If any entry does not have contents yet, this function returns False
1271 for the data.
1272
1273 Returns:
1274 Tuple:
Simon Glass43a98cc2022-03-05 20:18:58 -07001275 bytes: Concatenated data from all the entries (or None)
1276 str: Filename of file written (or None if no data)
1277 str: Unique portion of filename (or None if no data)
Simon Glass6fba35c2022-02-08 11:50:00 -07001278 """
1279 data = b''
1280 for entry in entries:
1281 # First get the input data and put it in a file. If not available,
1282 # try later.
Simon Glassfc5a1682022-03-05 20:19:05 -07001283 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass43a98cc2022-03-05 20:18:58 -07001284 return None, None, None
Simon Glass6fba35c2022-02-08 11:50:00 -07001285 data += entry.GetData()
1286 uniq = self.GetUniqueName()
1287 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1288 tools.write_file(fname, data)
1289 return data, fname, uniq
Simon Glass7d3e4072022-08-07 09:46:46 -06001290
1291 @classmethod
1292 def create_fake_dir(cls):
1293 """Create the directory for fake files"""
1294 cls.fake_dir = tools.get_output_filename('binman-fake')
1295 if not os.path.exists(cls.fake_dir):
1296 os.mkdir(cls.fake_dir)
1297 tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")
Simon Glass0cf5bce2022-08-13 11:40:44 -06001298
1299 def ensure_props(self):
1300 """Raise an exception if properties are missing
1301
1302 Args:
1303 prop_list (list of str): List of properties to check for
1304
1305 Raises:
1306 ValueError: Any property is missing
1307 """
1308 not_present = []
1309 for prop in self.required_props:
1310 if not prop in self._node.props:
1311 not_present.append(prop)
1312 if not_present:
1313 self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}")
Simon Glass1e9e61c2023-01-07 14:07:12 -07001314
1315 def mark_absent(self, msg):
1316 tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg))
1317 self.absent = True
Simon Glassad5cfe12023-01-07 14:07:14 -07001318
1319 def read_elf_segments(self):
1320 """Read segments from an entry that can generate an ELF file
1321
1322 Returns:
1323 tuple:
1324 list of segments, each:
1325 int: Segment number (0 = first)
1326 int: Start address of segment in memory
1327 bytes: Contents of segment
1328 int: entry address of ELF file
1329 """
1330 return None