blob: d73f30134052d365b49df914909501f62b3114e6 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassbf7fd502016-11-25 20:15:51 -07002# Copyright (c) 2016 Google, Inc
3#
Simon Glassbf7fd502016-11-25 20:15:51 -07004# Base class for all entries
5#
6
Simon Glass53af22a2018-07-17 13:25:32 -06007from collections import namedtuple
Simon Glassb4cf5f12019-10-31 07:42:59 -06008import importlib
Simon Glassbadf0ec2018-06-01 09:38:15 -06009import os
Simon Glass790ba9f2022-01-12 13:10:36 -070010import pathlib
Simon Glassbadf0ec2018-06-01 09:38:15 -060011import sys
Simon Glass7960a0a2022-08-07 09:46:46 -060012import time
Simon Glassc55a50f2018-09-14 04:57:19 -060013
Simon Glass386c63c2022-01-09 20:13:50 -070014from binman import bintool
Simon Glass3fbba552022-10-20 18:22:46 -060015from binman import elf
Simon Glass16287932020-04-17 18:09:03 -060016from dtoc import fdt_util
Simon Glassbf776672020-04-17 18:09:04 -060017from patman import tools
Simon Glassc1aa66e2022-01-29 14:14:04 -070018from patman.tools import to_hex, to_hex_size
Simon Glassbf776672020-04-17 18:09:04 -060019from patman import tout
Simon Glassbf7fd502016-11-25 20:15:51 -070020
21modules = {}
22
Simon Glass8d2ef3e2022-02-11 13:23:21 -070023# This is imported if needed
24state = None
Simon Glass53af22a2018-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 Glass41b8ba02019-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 Glass53af22a2018-07-17 13:25:32 -060034
Simon Glassbf7fd502016-11-25 20:15:51 -070035class Entry(object):
Simon Glass25ac0e62018-06-01 09:38:14 -060036 """An Entry in the section
Simon Glassbf7fd502016-11-25 20:15:51 -070037
38 An entry corresponds to a single node in the device-tree description
Simon Glass25ac0e62018-06-01 09:38:14 -060039 of the section. Each entry ends up being a part of the final section.
Simon Glassbf7fd502016-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 Glass8122f392018-07-17 13:25:28 -060047 section: Section object containing this entry
Simon Glassbf7fd502016-11-25 20:15:51 -070048 node: The node that created this entry
Simon Glass3ab95982018-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 Glassbf7fd502016-11-25 20:15:51 -070051 size: Entry size in bytes, None if not known
Simon Glass9a5d3dc2019-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 Glass8287ee82019-07-08 14:25:30 -060054 uncomp_size: Size of uncompressed data in bytes, if the entry is
55 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070056 contents_size: Size of contents in bytes, 0 by default
Simon Glass4eec34c2020-10-26 17:40:10 -060057 align: Entry start offset alignment relative to the start of the
58 containing section, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070059 align_size: Entry size alignment, or None
Simon Glass4eec34c2020-10-26 17:40:10 -060060 align_end: Entry end offset alignment relative to the start of the
61 containing section, or None
Simon Glassf90d9062020-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 Glass97c3e9a2020-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 Glass8287ee82019-07-08 14:25:30 -060073 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassc52c9e72019-07-08 14:25:37 -060074 orig_offset: Original offset value read from node
75 orig_size: Original size value read from node
Simon Glass67a05012023-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 Glass87958982020-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 Thierya89c8f22022-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 Glass87958982020-09-01 05:13:57 -060083 external: True if this entry contains an external binary blob
Simon Glass386c63c2022-01-09 20:13:50 -070084 bintools: Bintools used by this entry (only populated for Image)
Simon Glass4f9ee832022-01-09 20:14:09 -070085 missing_bintools: List of missing bintools for this entry
Alper Nebi Yasakee813c82022-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 Herbrechtsmeierec7d27d2022-08-19 16:25:30 +020088 comp_bintool: Bintools used for compress and decompress data
Simon Glass7960a0a2022-08-07 09:46:46 -060089 fake_fname: Fake filename, if one was created, else None
Simon Glasscdadada2022-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 Glass3fbba552022-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 Glassc8c9f312023-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 Glass67a05012023-01-07 14:07:15 -0700100 optional (bool): True if this entry contains an optional external blob
Simon Glassbf7fd502016-11-25 20:15:51 -0700101 """
Simon Glass7960a0a2022-08-07 09:46:46 -0600102 fake_dir = None
103
Simon Glass3fbba552022-10-20 18:22:46 -0600104 def __init__(self, section, etype, node, name_prefix='',
105 auto_write_symbols=False):
Simon Glass8dbb7442019-08-24 07:22:44 -0600106 # Put this here to allow entry-docs and help to work without libfdt
107 global state
Simon Glass16287932020-04-17 18:09:03 -0600108 from binman import state
Simon Glass8dbb7442019-08-24 07:22:44 -0600109
Simon Glass25ac0e62018-06-01 09:38:14 -0600110 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -0700111 self.etype = etype
112 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -0600113 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -0600114 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700115 self.size = None
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600116 self.pre_reset_size = None
Simon Glass8287ee82019-07-08 14:25:30 -0600117 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -0600118 self.data = None
Simon Glass97c3e9a2020-10-26 17:40:15 -0600119 self.uncomp_data = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700120 self.contents_size = 0
121 self.align = None
122 self.align_size = None
123 self.align_end = None
124 self.pad_before = 0
125 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -0600126 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -0600127 self.image_pos = None
Simon Glass80a66ae2022-03-05 20:18:59 -0700128 self.extend_size = False
Simon Glass8287ee82019-07-08 14:25:30 -0600129 self.compress = 'none'
Simon Glassb1cca952020-07-09 18:39:40 -0600130 self.missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100131 self.faked = False
Simon Glass87958982020-09-01 05:13:57 -0600132 self.external = False
133 self.allow_missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100134 self.allow_fake = False
Simon Glass386c63c2022-01-09 20:13:50 -0700135 self.bintools = {}
Simon Glass4f9ee832022-01-09 20:14:09 -0700136 self.missing_bintools = []
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300137 self.update_hash = True
Simon Glass7960a0a2022-08-07 09:46:46 -0600138 self.fake_fname = None
Simon Glasscdadada2022-08-13 11:40:44 -0600139 self.required_props = []
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +0200140 self.comp_bintool = None
Simon Glass3fbba552022-10-20 18:22:46 -0600141 self.elf_fname = None
142 self.auto_write_symbols = auto_write_symbols
Simon Glassc8c9f312023-01-07 14:07:12 -0700143 self.absent = False
Simon Glass67a05012023-01-07 14:07:15 -0700144 self.optional = False
Simon Glassbf7fd502016-11-25 20:15:51 -0700145
146 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700147 def FindEntryClass(etype, expanded):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600148 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -0700149
150 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -0600151 node_node: Path name of Node object containing information about
152 the entry to create (used for errors)
153 etype: Entry type to use
Simon Glassb35fb172021-03-18 20:25:04 +1300154 expanded: Use the expanded version of etype
Simon Glassbf7fd502016-11-25 20:15:51 -0700155
156 Returns:
Simon Glassb35fb172021-03-18 20:25:04 +1300157 The entry class object if found, else None if not found and expanded
Simon Glass858436d2021-11-23 21:09:49 -0700158 is True, else a tuple:
159 module name that could not be found
160 exception received
Simon Glassbf7fd502016-11-25 20:15:51 -0700161 """
Simon Glassdd57c132018-06-01 09:38:11 -0600162 # Convert something like 'u-boot@0' to 'u_boot' since we are only
163 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700164 module_name = etype.replace('-', '_')
Simon Glassb35fb172021-03-18 20:25:04 +1300165
Simon Glassdd57c132018-06-01 09:38:11 -0600166 if '@' in module_name:
167 module_name = module_name.split('@')[0]
Simon Glassb35fb172021-03-18 20:25:04 +1300168 if expanded:
169 module_name += '_expanded'
Simon Glassbf7fd502016-11-25 20:15:51 -0700170 module = modules.get(module_name)
171
Simon Glassbadf0ec2018-06-01 09:38:15 -0600172 # Also allow entry-type modules to be brought in from the etype directory.
173
Simon Glassbf7fd502016-11-25 20:15:51 -0700174 # Import the module if we have not already done so.
175 if not module:
176 try:
Simon Glass16287932020-04-17 18:09:03 -0600177 module = importlib.import_module('binman.etype.' + module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600178 except ImportError as e:
Simon Glassb35fb172021-03-18 20:25:04 +1300179 if expanded:
180 return None
Simon Glass858436d2021-11-23 21:09:49 -0700181 return module_name, e
Simon Glassbf7fd502016-11-25 20:15:51 -0700182 modules[module_name] = module
183
Simon Glassfd8d1f72018-07-17 13:25:36 -0600184 # Look up the expected class name
185 return getattr(module, 'Entry_%s' % module_name)
186
187 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700188 def Lookup(node_path, etype, expanded, missing_etype=False):
189 """Look up the entry class for a node.
190
191 Args:
192 node_node (str): Path name of Node object containing information
193 about the entry to create (used for errors)
194 etype (str): Entry type to use
195 expanded (bool): Use the expanded version of etype
196 missing_etype (bool): True to default to a blob etype if the
197 requested etype is not found
198
199 Returns:
200 The entry class object if found, else None if not found and expanded
201 is True
202
203 Raise:
204 ValueError if expanded is False and the class is not found
205 """
206 # Convert something like 'u-boot@0' to 'u_boot' since we are only
207 # interested in the type.
208 cls = Entry.FindEntryClass(etype, expanded)
209 if cls is None:
210 return None
211 elif isinstance(cls, tuple):
212 if missing_etype:
213 cls = Entry.FindEntryClass('blob', False)
214 if isinstance(cls, tuple): # This should not fail
215 module_name, e = cls
216 raise ValueError(
217 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
218 (etype, node_path, module_name, e))
219 return cls
220
221 @staticmethod
222 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600223 """Create a new entry for a node.
224
225 Args:
Simon Glass858436d2021-11-23 21:09:49 -0700226 section (entry_Section): Section object containing this node
227 node (Node): Node object containing information about the entry to
228 create
229 etype (str): Entry type to use, or None to work it out (used for
230 tests)
231 expanded (bool): Use the expanded version of etype
232 missing_etype (bool): True to default to a blob etype if the
233 requested etype is not found
Simon Glassfd8d1f72018-07-17 13:25:36 -0600234
235 Returns:
236 A new Entry object of the correct type (a subclass of Entry)
237 """
238 if not etype:
239 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glass858436d2021-11-23 21:09:49 -0700240 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glassb35fb172021-03-18 20:25:04 +1300241 if obj and expanded:
242 # Check whether to use the expanded entry
243 new_etype = etype + '-expanded'
Simon Glass3d433382021-03-21 18:24:30 +1300244 can_expand = not fdt_util.GetBool(node, 'no-expanded')
245 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glassb35fb172021-03-18 20:25:04 +1300246 etype = new_etype
247 else:
248 obj = None
249 if not obj:
Simon Glass858436d2021-11-23 21:09:49 -0700250 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600251
Simon Glassbf7fd502016-11-25 20:15:51 -0700252 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600253 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700254
255 def ReadNode(self):
256 """Read entry information from the node
257
Simon Glassc6bd6e22019-07-20 12:23:45 -0600258 This must be called as the first thing after the Entry is created.
259
Simon Glassbf7fd502016-11-25 20:15:51 -0700260 This reads all the fields we recognise from the node, ready for use.
261 """
Simon Glasscdadada2022-08-13 11:40:44 -0600262 self.ensure_props()
Simon Glass15a587c2018-07-17 13:25:51 -0600263 if 'pos' in self._node.props:
264 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass80a66ae2022-03-05 20:18:59 -0700265 if 'expand-size' in self._node.props:
266 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glass3ab95982018-08-01 15:22:37 -0600267 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700268 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glass12bb1a92019-07-20 12:23:51 -0600269 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
270 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
271 if self.GetImage().copy_to_orig:
272 self.orig_offset = self.offset
273 self.orig_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600274
Simon Glassffded752019-07-08 14:25:46 -0600275 # These should not be set in input files, but are set in an FDT map,
276 # which is also read by this code.
277 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
278 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
279
Simon Glassbf7fd502016-11-25 20:15:51 -0700280 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700281 if tools.not_power_of_two(self.align):
Simon Glassbf7fd502016-11-25 20:15:51 -0700282 raise ValueError("Node '%s': Alignment %s must be a power of two" %
283 (self._node.path, self.align))
Simon Glass5ff9fed2021-03-21 18:24:33 +1300284 if self.section and self.align is None:
285 self.align = self.section.align_default
Simon Glassbf7fd502016-11-25 20:15:51 -0700286 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
287 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
288 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700289 if tools.not_power_of_two(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600290 self.Raise("Alignment size %s must be a power of two" %
291 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700292 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600293 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glass80a66ae2022-03-05 20:18:59 -0700294 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassb2381432020-09-06 10:39:09 -0600295 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glass67a05012023-01-07 14:07:15 -0700296 self.optional = fdt_util.GetBool(self._node, 'optional')
Simon Glassbf7fd502016-11-25 20:15:51 -0700297
Simon Glass87c96292020-10-26 17:40:06 -0600298 # This is only supported by blobs and sections at present
299 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
300
Simon Glass6c234bf2018-09-14 04:57:18 -0600301 def GetDefaultFilename(self):
302 return None
303
Simon Glassa8adb6d2019-07-20 12:23:28 -0600304 def GetFdts(self):
305 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600306
307 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600308 Empty dict, if this entry is not a .dtb, otherwise:
309 Dict:
310 key: Filename from this entry (without the path)
Simon Glass4bdd3002019-07-20 12:23:31 -0600311 value: Tuple:
Simon Glassadb67bb2021-03-18 20:25:02 +1300312 Entry object for this dtb
Simon Glass4bdd3002019-07-20 12:23:31 -0600313 Filename of file containing this dtb
Simon Glass539aece2018-09-14 04:57:22 -0600314 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600315 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600316
Simon Glassc9ee33a2022-03-05 20:19:00 -0700317 def gen_entries(self):
318 """Allow entries to generate other entries
Simon Glassa01d1a22021-03-18 20:24:52 +1300319
320 Some entries generate subnodes automatically, from which sub-entries
321 are then created. This method allows those to be added to the binman
322 definition for the current image. An entry which implements this method
323 should call state.AddSubnode() to add a subnode and can add properties
324 with state.AddString(), etc.
325
326 An example is 'files', which produces a section containing a list of
327 files.
328 """
Simon Glass0a98b282018-09-14 04:57:28 -0600329 pass
330
Simon Glassa9fad072020-10-26 17:40:17 -0600331 def AddMissingProperties(self, have_image_pos):
332 """Add new properties to the device tree as needed for this entry
333
334 Args:
335 have_image_pos: True if this entry has an image position. This can
336 be False if its parent section is compressed, since compression
337 groups all entries together into a compressed block of data,
338 obscuring the start of each individual child entry
339 """
340 for prop in ['offset', 'size']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600341 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600342 state.AddZeroProp(self._node, prop)
Simon Glassa9fad072020-10-26 17:40:17 -0600343 if have_image_pos and 'image-pos' not in self._node.props:
344 state.AddZeroProp(self._node, 'image-pos')
Simon Glass12bb1a92019-07-20 12:23:51 -0600345 if self.GetImage().allow_repack:
346 if self.orig_offset is not None:
347 state.AddZeroProp(self._node, 'orig-offset', True)
348 if self.orig_size is not None:
349 state.AddZeroProp(self._node, 'orig-size', True)
350
Simon Glass8287ee82019-07-08 14:25:30 -0600351 if self.compress != 'none':
352 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300353
354 if self.update_hash:
355 err = state.CheckAddHashProp(self._node)
356 if err:
357 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600358
359 def SetCalculatedProperties(self):
360 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600361 state.SetInt(self._node, 'offset', self.offset)
362 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600363 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassa9fad072020-10-26 17:40:17 -0600364 if self.image_pos is not None:
Simon Glass08594d42020-11-02 12:55:44 -0700365 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass12bb1a92019-07-20 12:23:51 -0600366 if self.GetImage().allow_repack:
367 if self.orig_offset is not None:
368 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
369 if self.orig_size is not None:
370 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glass8287ee82019-07-08 14:25:30 -0600371 if self.uncomp_size is not None:
372 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300373
374 if self.update_hash:
375 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600376
Simon Glassecab8972018-07-06 10:27:40 -0600377 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600378 """Allow entries to adjust the device tree
379
380 Some entries need to adjust the device tree for their purposes. This
381 may involve adding or deleting properties.
382
383 Returns:
384 True if processing is complete
385 False if processing could not be completed due to a dependency.
386 This will cause the entry to be retried after others have been
387 called
388 """
Simon Glassecab8972018-07-06 10:27:40 -0600389 return True
390
Simon Glassc8d48ef2018-06-01 09:38:21 -0600391 def SetPrefix(self, prefix):
392 """Set the name prefix for a node
393
394 Args:
395 prefix: Prefix to set, or '' to not use a prefix
396 """
397 if prefix:
398 self.name = prefix + self.name
399
Simon Glass5c890232018-07-06 10:27:19 -0600400 def SetContents(self, data):
401 """Set the contents of an entry
402
403 This sets both the data and content_size properties
404
405 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600406 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600407 """
408 self.data = data
409 self.contents_size = len(self.data)
410
411 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600412 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600413
Simon Glassa0dcaf22019-07-08 14:25:35 -0600414 This checks that the new data is the same size as the old. If the size
415 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600416
417 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600418 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600419
420 Raises:
421 ValueError if the new data size is not the same as the old
422 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600423 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600424 new_size = len(data)
Simon Glass61ec04f2019-07-20 12:23:58 -0600425 if state.AllowEntryExpansion() and new_size > self.contents_size:
426 # self.data will indicate the new size needed
427 size_ok = False
428 elif state.AllowEntryContraction() and new_size < self.contents_size:
429 size_ok = False
430
431 # If not allowed to change, try to deal with it or give up
432 if size_ok:
Simon Glassc52c9e72019-07-08 14:25:37 -0600433 if new_size > self.contents_size:
Simon Glass61ec04f2019-07-20 12:23:58 -0600434 self.Raise('Cannot update entry size from %d to %d' %
435 (self.contents_size, new_size))
436
437 # Don't let the data shrink. Pad it if necessary
438 if size_ok and new_size < self.contents_size:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700439 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass61ec04f2019-07-20 12:23:58 -0600440
441 if not size_ok:
Simon Glassf3385a52022-01-29 14:14:15 -0700442 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glassc1aa66e2022-01-29 14:14:04 -0700443 self._node.path, to_hex(self.contents_size),
444 to_hex(new_size)))
Simon Glass5c890232018-07-06 10:27:19 -0600445 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600446 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600447
Simon Glass72e423c2022-03-05 20:19:05 -0700448 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glassbf7fd502016-11-25 20:15:51 -0700449 """Figure out the contents of an entry.
450
Simon Glass72e423c2022-03-05 20:19:05 -0700451 Args:
452 skip_entry (Entry): Entry to skip when obtaining section contents
453 fake_size (int): Size of fake file to create if needed
454
Simon Glassbf7fd502016-11-25 20:15:51 -0700455 Returns:
456 True if the contents were found, False if another call is needed
457 after the other entries are processed.
458 """
459 # No contents by default: subclasses can implement this
460 return True
461
Simon Glassc52c9e72019-07-08 14:25:37 -0600462 def ResetForPack(self):
463 """Reset offset/size fields so that packing can be done again"""
Simon Glass9f297b02019-07-20 12:23:36 -0600464 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700465 (to_hex(self.offset), to_hex(self.orig_offset),
466 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600467 self.pre_reset_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600468 self.offset = self.orig_offset
469 self.size = self.orig_size
470
Simon Glass3ab95982018-08-01 15:22:37 -0600471 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600472 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700473
474 Most of the time the entries are not fully specified. There may be
475 an alignment but no size. In that case we take the size from the
476 contents of the entry.
477
Simon Glass3ab95982018-08-01 15:22:37 -0600478 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700479
Simon Glass3ab95982018-08-01 15:22:37 -0600480 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700481 entry will be know.
482
483 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600484 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700485
486 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600487 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700488 """
Simon Glass9f297b02019-07-20 12:23:36 -0600489 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700490 (to_hex(self.offset), to_hex(self.size),
Simon Glass9f297b02019-07-20 12:23:36 -0600491 self.contents_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600492 if self.offset is None:
493 if self.offset_unset:
494 self.Raise('No offset set with offset-unset: should another '
495 'entry provide this correct offset?')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700496 self.offset = tools.align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700497 needed = self.pad_before + self.contents_size + self.pad_after
Simon Glassc1aa66e2022-01-29 14:14:04 -0700498 needed = tools.align(needed, self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700499 size = self.size
500 if not size:
501 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600502 new_offset = self.offset + size
Simon Glassc1aa66e2022-01-29 14:14:04 -0700503 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glass3ab95982018-08-01 15:22:37 -0600504 if aligned_offset != new_offset:
505 size = aligned_offset - self.offset
506 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700507
508 if not self.size:
509 self.size = size
510
511 if self.size < needed:
512 self.Raise("Entry contents size is %#x (%d) but entry size is "
513 "%#x (%d)" % (needed, needed, self.size, self.size))
514 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600515 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700516 # conflict with the provided alignment values
Simon Glassc1aa66e2022-01-29 14:14:04 -0700517 if self.size != tools.align(self.size, self.align_size):
Simon Glassbf7fd502016-11-25 20:15:51 -0700518 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
519 (self.size, self.size, self.align_size, self.align_size))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700520 if self.offset != tools.align(self.offset, self.align):
Simon Glass3ab95982018-08-01 15:22:37 -0600521 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
522 (self.offset, self.offset, self.align, self.align))
Simon Glass9f297b02019-07-20 12:23:36 -0600523 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
524 (self.offset, self.size, self.contents_size, new_offset))
Simon Glassbf7fd502016-11-25 20:15:51 -0700525
Simon Glass3ab95982018-08-01 15:22:37 -0600526 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700527
528 def Raise(self, msg):
529 """Convenience function to raise an error referencing a node"""
530 raise ValueError("Node '%s': %s" % (self._node.path, msg))
531
Simon Glass189f2912021-03-21 18:24:31 +1300532 def Info(self, msg):
533 """Convenience function to log info referencing a node"""
534 tag = "Info '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700535 tout.detail('%30s: %s' % (tag, msg))
Simon Glass189f2912021-03-21 18:24:31 +1300536
Simon Glass9f297b02019-07-20 12:23:36 -0600537 def Detail(self, msg):
538 """Convenience function to log detail referencing a node"""
539 tag = "Node '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700540 tout.detail('%30s: %s' % (tag, msg))
Simon Glass9f297b02019-07-20 12:23:36 -0600541
Simon Glass53af22a2018-07-17 13:25:32 -0600542 def GetEntryArgsOrProps(self, props, required=False):
543 """Return the values of a set of properties
544
545 Args:
546 props: List of EntryArg objects
547
548 Raises:
549 ValueError if a property is not found
550 """
551 values = []
552 missing = []
553 for prop in props:
554 python_prop = prop.name.replace('-', '_')
555 if hasattr(self, python_prop):
556 value = getattr(self, python_prop)
557 else:
558 value = None
559 if value is None:
560 value = self.GetArg(prop.name, prop.datatype)
561 if value is None and required:
562 missing.append(prop.name)
563 values.append(value)
564 if missing:
Simon Glass939d1062021-01-06 21:35:16 -0700565 self.GetImage().MissingArgs(self, missing)
Simon Glass53af22a2018-07-17 13:25:32 -0600566 return values
567
Simon Glassbf7fd502016-11-25 20:15:51 -0700568 def GetPath(self):
569 """Get the path of a node
570
571 Returns:
572 Full path of the node for this entry
573 """
574 return self._node.path
575
Simon Glass631f7522021-03-21 18:24:32 +1300576 def GetData(self, required=True):
Simon Glass63e7ba62020-10-26 17:40:16 -0600577 """Get the contents of an entry
578
Simon Glass631f7522021-03-21 18:24:32 +1300579 Args:
580 required: True if the data must be present, False if it is OK to
581 return None
582
Simon Glass63e7ba62020-10-26 17:40:16 -0600583 Returns:
584 bytes content of the entry, excluding any padding. If the entry is
585 compressed, the compressed data is returned
586 """
Simon Glassc1aa66e2022-01-29 14:14:04 -0700587 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glassbf7fd502016-11-25 20:15:51 -0700588 return self.data
589
Simon Glass271a0832020-11-02 12:55:43 -0700590 def GetPaddedData(self, data=None):
591 """Get the data for an entry including any padding
592
593 Gets the entry data and uses its section's pad-byte value to add padding
594 before and after as defined by the pad-before and pad-after properties.
595
596 This does not consider alignment.
597
598 Returns:
599 Contents of the entry along with any pad bytes before and
600 after it (bytes)
601 """
602 if data is None:
603 data = self.GetData()
604 return self.section.GetPaddedDataForEntry(self, data)
605
Simon Glass3ab95982018-08-01 15:22:37 -0600606 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600607 """Get the offsets for siblings
608
609 Some entry types can contain information about the position or size of
610 other entries. An example of this is the Intel Flash Descriptor, which
611 knows where the Intel Management Engine section should go.
612
613 If this entry knows about the position of other entries, it can specify
614 this by returning values here
615
616 Returns:
617 Dict:
618 key: Entry type
619 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600620 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600621 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700622 return {}
623
Simon Glasscf549042019-07-08 13:18:39 -0600624 def SetOffsetSize(self, offset, size):
625 """Set the offset and/or size of an entry
626
627 Args:
628 offset: New offset, or None to leave alone
629 size: New size, or None to leave alone
630 """
631 if offset is not None:
632 self.offset = offset
633 if size is not None:
634 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700635
Simon Glassdbf6be92018-08-01 15:22:42 -0600636 def SetImagePos(self, image_pos):
637 """Set the position in the image
638
639 Args:
640 image_pos: Position of this entry in the image
641 """
642 self.image_pos = image_pos + self.offset
643
Simon Glassbf7fd502016-11-25 20:15:51 -0700644 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600645 """Do any post-packing updates of entry contents
646
647 This function should call ProcessContentsUpdate() to update the entry
648 contents, if necessary, returning its return value here.
649
650 Args:
651 data: Data to set to the contents (bytes)
652
653 Returns:
654 True if the new data size is OK, False if expansion is needed
655
656 Raises:
657 ValueError if the new data size is not the same as the old and
658 state.AllowEntryExpansion() is False
659 """
660 return True
Simon Glass19790632017-11-13 18:55:01 -0700661
Simon Glassf55382b2018-06-01 09:38:13 -0600662 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700663 """Write symbol values into binary files for access at run time
664
665 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600666 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700667 """
Simon Glass3fbba552022-10-20 18:22:46 -0600668 if self.auto_write_symbols:
Simon Glassd2afb9e2022-10-20 18:22:47 -0600669 # Check if we are writing symbols into an ELF file
670 is_elf = self.GetDefaultFilename() == self.elf_fname
671 elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(),
672 is_elf)
Simon Glass18546952018-06-01 09:38:16 -0600673
Simon Glass6ddd6112020-10-26 17:40:18 -0600674 def CheckEntries(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600675 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600676
Simon Glass3ab95982018-08-01 15:22:37 -0600677 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600678 than having to be fully inside their section). Sub-classes can implement
679 this function and raise if there is a problem.
680 """
681 pass
Simon Glass3b0c3822018-06-01 09:38:20 -0600682
Simon Glass8122f392018-07-17 13:25:28 -0600683 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600684 def GetStr(value):
685 if value is None:
686 return '<none> '
687 return '%08x' % value
688
689 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600690 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600691 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
692 Entry.GetStr(offset), Entry.GetStr(size),
693 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600694
Simon Glass3b0c3822018-06-01 09:38:20 -0600695 def WriteMap(self, fd, indent):
696 """Write a map of the entry to a .map file
697
698 Args:
699 fd: File to write the map to
700 indent: Curent indent level of map (0=none, 1=one level, etc.)
701 """
Simon Glass1be70d22018-07-17 13:25:49 -0600702 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
703 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600704
Simon Glassd626e822022-08-13 11:40:50 -0600705 # pylint: disable=assignment-from-none
Simon Glass11e36cc2018-07-17 13:25:38 -0600706 def GetEntries(self):
707 """Return a list of entries contained by this entry
708
709 Returns:
710 List of entries, or None if none. A normal entry has no entries
711 within it so will return None
712 """
713 return None
714
Simon Glassd626e822022-08-13 11:40:50 -0600715 def FindEntryByNode(self, find_node):
716 """Find a node in an entry, searching all subentries
717
718 This does a recursive search.
719
720 Args:
721 find_node (fdt.Node): Node to find
722
723 Returns:
724 Entry: entry, if found, else None
725 """
726 entries = self.GetEntries()
727 if entries:
728 for entry in entries.values():
729 if entry._node == find_node:
730 return entry
731 found = entry.FindEntryByNode(find_node)
732 if found:
733 return found
734
735 return None
736
Simon Glass53af22a2018-07-17 13:25:32 -0600737 def GetArg(self, name, datatype=str):
738 """Get the value of an entry argument or device-tree-node property
739
740 Some node properties can be provided as arguments to binman. First check
741 the entry arguments, and fall back to the device tree if not found
742
743 Args:
744 name: Argument name
745 datatype: Data type (str or int)
746
747 Returns:
748 Value of argument as a string or int, or None if no value
749
750 Raises:
751 ValueError if the argument cannot be converted to in
752 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600753 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600754 if value is not None:
755 if datatype == int:
756 try:
757 value = int(value)
758 except ValueError:
759 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
760 (name, value))
761 elif datatype == str:
762 pass
763 else:
764 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
765 datatype)
766 else:
767 value = fdt_util.GetDatatype(self._node, name, datatype)
768 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600769
770 @staticmethod
771 def WriteDocs(modules, test_missing=None):
772 """Write out documentation about the various entry types to stdout
773
774 Args:
775 modules: List of modules to include
776 test_missing: Used for testing. This is a module to report
777 as missing
778 """
779 print('''Binman Entry Documentation
780===========================
781
782This file describes the entry types supported by binman. These entry types can
783be placed in an image one by one to build up a final firmware image. It is
784fairly easy to create new entry types. Just add a new file to the 'etype'
785directory. You can use the existing entries as examples.
786
787Note that some entries are subclasses of others, using and extending their
788features to produce new behaviours.
789
790
791''')
792 modules = sorted(modules)
793
794 # Don't show the test entry
795 if '_testing' in modules:
796 modules.remove('_testing')
797 missing = []
798 for name in modules:
Simon Glassb35fb172021-03-18 20:25:04 +1300799 module = Entry.Lookup('WriteDocs', name, False)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600800 docs = getattr(module, '__doc__')
801 if test_missing == name:
802 docs = None
803 if docs:
804 lines = docs.splitlines()
805 first_line = lines[0]
806 rest = [line[4:] for line in lines[1:]]
807 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glass228c9b82022-08-07 16:33:25 -0600808
809 # Create a reference for use by rST docs
810 ref_name = f'etype_{module.__name__[6:]}'.lower()
811 print('.. _%s:' % ref_name)
812 print()
Simon Glassfd8d1f72018-07-17 13:25:36 -0600813 print(hdr)
814 print('-' * len(hdr))
815 print('\n'.join(rest))
816 print()
817 print()
818 else:
819 missing.append(name)
820
821 if missing:
822 raise ValueError('Documentation is missing for modules: %s' %
823 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600824
825 def GetUniqueName(self):
826 """Get a unique name for a node
827
828 Returns:
829 String containing a unique name for a node, consisting of the name
830 of all ancestors (starting from within the 'binman' node) separated
831 by a dot ('.'). This can be useful for generating unique filesnames
832 in the output directory.
833 """
834 name = self.name
835 node = self._node
836 while node.parent:
837 node = node.parent
Alper Nebi Yasak67bf2c82022-03-27 18:31:44 +0300838 if node.name in ('binman', '/'):
Simon Glassa326b492018-09-14 04:57:11 -0600839 break
840 name = '%s.%s' % (node.name, name)
841 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600842
Simon Glass80a66ae2022-03-05 20:18:59 -0700843 def extend_to_limit(self, limit):
844 """Extend an entry so that it ends at the given offset limit"""
Simon Glassba64a0b2018-09-14 04:57:29 -0600845 if self.offset + self.size < limit:
846 self.size = limit - self.offset
847 # Request the contents again, since changing the size requires that
848 # the data grows. This should not fail, but check it to be sure.
849 if not self.ObtainContents():
850 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600851
852 def HasSibling(self, name):
853 """Check if there is a sibling of a given name
854
855 Returns:
856 True if there is an entry with this name in the the same section,
857 else False
858 """
859 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600860
861 def GetSiblingImagePos(self, name):
862 """Return the image position of the given sibling
863
864 Returns:
865 Image position of sibling, or None if the sibling has no position,
866 or False if there is no such sibling
867 """
868 if not self.HasSibling(name):
869 return False
870 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600871
872 @staticmethod
873 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
874 uncomp_size, offset, entry):
875 """Add a new entry to the entries list
876
877 Args:
878 entries: List (of EntryInfo objects) to add to
879 indent: Current indent level to add to list
880 name: Entry name (string)
881 etype: Entry type (string)
882 size: Entry size in bytes (int)
883 image_pos: Position within image in bytes (int)
884 uncomp_size: Uncompressed size if the entry uses compression, else
885 None
886 offset: Entry offset within parent in bytes (int)
887 entry: Entry object
888 """
889 entries.append(EntryInfo(indent, name, etype, size, image_pos,
890 uncomp_size, offset, entry))
891
892 def ListEntries(self, entries, indent):
893 """Add files in this entry to the list of entries
894
895 This can be overridden by subclasses which need different behaviour.
896
897 Args:
898 entries: List (of EntryInfo objects) to add to
899 indent: Current indent level to add to list
900 """
901 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
902 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600903
Simon Glass943bf782021-11-23 21:09:50 -0700904 def ReadData(self, decomp=True, alt_format=None):
Simon Glassf667e452019-07-08 14:25:50 -0600905 """Read the data for an entry from the image
906
907 This is used when the image has been read in and we want to extract the
908 data for a particular entry from that image.
909
910 Args:
911 decomp: True to decompress any compressed data before returning it;
912 False to return the raw, uncompressed data
913
914 Returns:
915 Entry data (bytes)
916 """
917 # Use True here so that we get an uncompressed section to work from,
918 # although compressed sections are currently not supported
Simon Glassf3385a52022-01-29 14:14:15 -0700919 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass2d553c02019-09-25 08:56:21 -0600920 (self.section.GetPath(), self.GetPath()))
Simon Glass943bf782021-11-23 21:09:50 -0700921 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glassa9cd39e2019-07-20 12:24:04 -0600922 return data
Simon Glassd5079332019-07-20 12:23:41 -0600923
Simon Glass943bf782021-11-23 21:09:50 -0700924 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass2d553c02019-09-25 08:56:21 -0600925 """Read the data for a particular child entry
Simon Glass4e185e82019-09-25 08:56:20 -0600926
927 This reads data from the parent and extracts the piece that relates to
928 the given child.
929
930 Args:
Simon Glass943bf782021-11-23 21:09:50 -0700931 child (Entry): Child entry to read data for (must be valid)
932 decomp (bool): True to decompress any compressed data before
933 returning it; False to return the raw, uncompressed data
934 alt_format (str): Alternative format to read in, or None
Simon Glass4e185e82019-09-25 08:56:20 -0600935
936 Returns:
937 Data for the child (bytes)
938 """
939 pass
940
Simon Glassd5079332019-07-20 12:23:41 -0600941 def LoadData(self, decomp=True):
942 data = self.ReadData(decomp)
Simon Glass10f9d002019-07-20 12:23:50 -0600943 self.contents_size = len(data)
Simon Glassd5079332019-07-20 12:23:41 -0600944 self.ProcessContentsUpdate(data)
945 self.Detail('Loaded data size %x' % len(data))
Simon Glassc5ad04b2019-07-20 12:23:46 -0600946
Simon Glass943bf782021-11-23 21:09:50 -0700947 def GetAltFormat(self, data, alt_format):
948 """Read the data for an extry in an alternative format
949
950 Supported formats are list in the documentation for each entry. An
951 example is fdtmap which provides .
952
953 Args:
954 data (bytes): Data to convert (this should have been produced by the
955 entry)
956 alt_format (str): Format to use
957
958 """
959 pass
960
Simon Glassc5ad04b2019-07-20 12:23:46 -0600961 def GetImage(self):
962 """Get the image containing this entry
963
964 Returns:
965 Image object containing this entry
966 """
967 return self.section.GetImage()
Simon Glass10f9d002019-07-20 12:23:50 -0600968
969 def WriteData(self, data, decomp=True):
970 """Write the data to an entry in the image
971
972 This is used when the image has been read in and we want to replace the
973 data for a particular entry in that image.
974
975 The image must be re-packed and written out afterwards.
976
977 Args:
978 data: Data to replace it with
979 decomp: True to compress the data if needed, False if data is
980 already compressed so should be used as is
981
982 Returns:
983 True if the data did not result in a resize of this entry, False if
984 the entry must be resized
985 """
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600986 if self.size is not None:
987 self.contents_size = self.size
988 else:
989 self.contents_size = self.pre_reset_size
Simon Glass10f9d002019-07-20 12:23:50 -0600990 ok = self.ProcessContentsUpdate(data)
991 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glass7210c892019-07-20 12:24:05 -0600992 section_ok = self.section.WriteChildData(self)
993 return ok and section_ok
994
995 def WriteChildData(self, child):
996 """Handle writing the data in a child entry
997
998 This should be called on the child's parent section after the child's
Simon Glass557693e2021-11-23 11:03:44 -0700999 data has been updated. It should update any data structures needed to
1000 validate that the update is successful.
Simon Glass7210c892019-07-20 12:24:05 -06001001
1002 This base-class implementation does nothing, since the base Entry object
1003 does not have any children.
1004
1005 Args:
1006 child: Child Entry that was written
1007
1008 Returns:
1009 True if the section could be updated successfully, False if the
Simon Glass557693e2021-11-23 11:03:44 -07001010 data is such that the section could not update
Simon Glass7210c892019-07-20 12:24:05 -06001011 """
1012 return True
Simon Glasseba1f0c2019-07-20 12:23:55 -06001013
1014 def GetSiblingOrder(self):
1015 """Get the relative order of an entry amoung its siblings
1016
1017 Returns:
1018 'start' if this entry is first among siblings, 'end' if last,
1019 otherwise None
1020 """
1021 entries = list(self.section.GetEntries().values())
1022 if entries:
1023 if self == entries[0]:
1024 return 'start'
1025 elif self == entries[-1]:
1026 return 'end'
1027 return 'middle'
Simon Glass4f9f1052020-07-09 18:39:38 -06001028
1029 def SetAllowMissing(self, allow_missing):
1030 """Set whether a section allows missing external blobs
1031
1032 Args:
1033 allow_missing: True if allowed, False if not allowed
1034 """
1035 # This is meaningless for anything other than sections
1036 pass
Simon Glassb1cca952020-07-09 18:39:40 -06001037
Heiko Thierya89c8f22022-01-06 11:49:41 +01001038 def SetAllowFakeBlob(self, allow_fake):
1039 """Set whether a section allows to create a fake blob
1040
1041 Args:
1042 allow_fake: True if allowed, False if not allowed
1043 """
Simon Glassf4590e02022-01-09 20:13:46 -07001044 self.allow_fake = allow_fake
Heiko Thierya89c8f22022-01-06 11:49:41 +01001045
Simon Glassb1cca952020-07-09 18:39:40 -06001046 def CheckMissing(self, missing_list):
Simon Glass67a05012023-01-07 14:07:15 -07001047 """Check if the entry has missing external blobs
Simon Glassb1cca952020-07-09 18:39:40 -06001048
Simon Glass67a05012023-01-07 14:07:15 -07001049 If there are missing (non-optional) blobs, the entries are added to the
1050 list
Simon Glassb1cca952020-07-09 18:39:40 -06001051
1052 Args:
1053 missing_list: List of Entry objects to be added to
1054 """
Simon Glass67a05012023-01-07 14:07:15 -07001055 if self.missing and not self.optional:
Simon Glassb1cca952020-07-09 18:39:40 -06001056 missing_list.append(self)
Simon Glass87958982020-09-01 05:13:57 -06001057
Simon Glass3817ad42022-03-05 20:19:04 -07001058 def check_fake_fname(self, fname, size=0):
Simon Glass790ba9f2022-01-12 13:10:36 -07001059 """If the file is missing and the entry allows fake blobs, fake it
1060
1061 Sets self.faked to True if faked
1062
1063 Args:
1064 fname (str): Filename to check
Simon Glass3817ad42022-03-05 20:19:04 -07001065 size (int): Size of fake file to create
Simon Glass790ba9f2022-01-12 13:10:36 -07001066
1067 Returns:
Simon Glass9a0a2e92022-03-05 20:19:03 -07001068 tuple:
1069 fname (str): Filename of faked file
1070 bool: True if the blob was faked, False if not
Simon Glass790ba9f2022-01-12 13:10:36 -07001071 """
1072 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass7960a0a2022-08-07 09:46:46 -06001073 if not self.fake_fname:
1074 outfname = os.path.join(self.fake_dir, os.path.basename(fname))
1075 with open(outfname, "wb") as out:
1076 out.truncate(size)
1077 tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
1078 self.fake_fname = outfname
Simon Glass790ba9f2022-01-12 13:10:36 -07001079 self.faked = True
Simon Glass7960a0a2022-08-07 09:46:46 -06001080 return self.fake_fname, True
Simon Glass9a0a2e92022-03-05 20:19:03 -07001081 return fname, False
Simon Glass790ba9f2022-01-12 13:10:36 -07001082
Heiko Thierya89c8f22022-01-06 11:49:41 +01001083 def CheckFakedBlobs(self, faked_blobs_list):
1084 """Check if any entries in this section have faked external blobs
1085
1086 If there are faked blobs, the entries are added to the list
1087
1088 Args:
1089 fake_blobs_list: List of Entry objects to be added to
1090 """
1091 # This is meaningless for anything other than blobs
1092 pass
1093
Simon Glass67a05012023-01-07 14:07:15 -07001094 def CheckOptional(self, optional_list):
1095 """Check if the entry has missing but optional external blobs
1096
1097 If there are missing (optional) blobs, the entries are added to the list
1098
1099 Args:
1100 optional_list (list): List of Entry objects to be added to
1101 """
1102 if self.missing and self.optional:
1103 optional_list.append(self)
1104
Simon Glass87958982020-09-01 05:13:57 -06001105 def GetAllowMissing(self):
1106 """Get whether a section allows missing external blobs
1107
1108 Returns:
1109 True if allowed, False if not allowed
1110 """
1111 return self.allow_missing
Simon Glassb2381432020-09-06 10:39:09 -06001112
Simon Glass4f9ee832022-01-09 20:14:09 -07001113 def record_missing_bintool(self, bintool):
1114 """Record a missing bintool that was needed to produce this entry
1115
1116 Args:
1117 bintool (Bintool): Bintool that was missing
1118 """
Stefan Herbrechtsmeierfacc3782022-08-19 16:25:19 +02001119 if bintool not in self.missing_bintools:
1120 self.missing_bintools.append(bintool)
Simon Glass4f9ee832022-01-09 20:14:09 -07001121
1122 def check_missing_bintools(self, missing_list):
1123 """Check if any entries in this section have missing bintools
1124
1125 If there are missing bintools, these are added to the list
1126
1127 Args:
1128 missing_list: List of Bintool objects to be added to
1129 """
Stefan Herbrechtsmeierfacc3782022-08-19 16:25:19 +02001130 for bintool in self.missing_bintools:
1131 if bintool not in missing_list:
1132 missing_list.append(bintool)
1133
Simon Glass4f9ee832022-01-09 20:14:09 -07001134
Simon Glassb2381432020-09-06 10:39:09 -06001135 def GetHelpTags(self):
1136 """Get the tags use for missing-blob help
1137
1138 Returns:
1139 list of possible tags, most desirable first
1140 """
1141 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glass87c96292020-10-26 17:40:06 -06001142
1143 def CompressData(self, indata):
1144 """Compress data according to the entry's compression method
1145
1146 Args:
1147 indata: Data to compress
1148
1149 Returns:
Stefan Herbrechtsmeier4f463e32022-08-19 16:25:27 +02001150 Compressed data
Simon Glass87c96292020-10-26 17:40:06 -06001151 """
Simon Glass97c3e9a2020-10-26 17:40:15 -06001152 self.uncomp_data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001153 if self.compress != 'none':
1154 self.uncomp_size = len(indata)
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02001155 if self.comp_bintool.is_present():
1156 data = self.comp_bintool.compress(indata)
1157 else:
1158 self.record_missing_bintool(self.comp_bintool)
1159 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001160 else:
1161 data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001162 return data
Simon Glassb35fb172021-03-18 20:25:04 +13001163
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001164 def DecompressData(self, indata):
1165 """Decompress data according to the entry's compression method
1166
1167 Args:
1168 indata: Data to decompress
1169
1170 Returns:
1171 Decompressed data
1172 """
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001173 if self.compress != 'none':
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02001174 if self.comp_bintool.is_present():
1175 data = self.comp_bintool.decompress(indata)
1176 self.uncomp_size = len(data)
1177 else:
1178 self.record_missing_bintool(self.comp_bintool)
1179 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001180 else:
1181 data = indata
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001182 self.uncomp_data = data
1183 return data
1184
Simon Glassb35fb172021-03-18 20:25:04 +13001185 @classmethod
1186 def UseExpanded(cls, node, etype, new_etype):
1187 """Check whether to use an expanded entry type
1188
1189 This is called by Entry.Create() when it finds an expanded version of
1190 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1191 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1192 ignored.
1193
1194 Args:
1195 node: Node object containing information about the entry to
1196 create
1197 etype: Original entry type being used
1198 new_etype: New entry type proposed
1199
1200 Returns:
1201 True to use this entry type, False to use the original one
1202 """
Simon Glassf3385a52022-01-29 14:14:15 -07001203 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glassb35fb172021-03-18 20:25:04 +13001204 (node.path, etype, new_etype))
1205 return True
Simon Glass943bf782021-11-23 21:09:50 -07001206
1207 def CheckAltFormats(self, alt_formats):
1208 """Add any alternative formats supported by this entry type
1209
1210 Args:
1211 alt_formats (dict): Dict to add alt_formats to:
1212 key: Name of alt format
1213 value: Help text
1214 """
1215 pass
Simon Glass386c63c2022-01-09 20:13:50 -07001216
Simon Glassae9a4572022-03-05 20:19:02 -07001217 def AddBintools(self, btools):
Simon Glass386c63c2022-01-09 20:13:50 -07001218 """Add the bintools used by this entry type
1219
1220 Args:
Simon Glassae9a4572022-03-05 20:19:02 -07001221 btools (dict of Bintool):
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001222
1223 Raise:
1224 ValueError if compression algorithm is not supported
Simon Glass386c63c2022-01-09 20:13:50 -07001225 """
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001226 algo = self.compress
1227 if algo != 'none':
Stefan Herbrechtsmeiercd15b642022-08-19 16:25:38 +02001228 algos = ['bzip2', 'gzip', 'lz4', 'lzma', 'lzo', 'xz', 'zstd']
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001229 if algo not in algos:
1230 raise ValueError("Unknown algorithm '%s'" % algo)
Stefan Herbrechtsmeier7b26a462022-08-19 16:25:36 +02001231 names = {'lzma': 'lzma_alone', 'lzo': 'lzop'}
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001232 name = names.get(self.compress, self.compress)
1233 self.comp_bintool = self.AddBintool(btools, name)
Simon Glass386c63c2022-01-09 20:13:50 -07001234
1235 @classmethod
1236 def AddBintool(self, tools, name):
1237 """Add a new bintool to the tools used by this etype
1238
1239 Args:
1240 name: Name of the tool
1241 """
1242 btool = bintool.Bintool.create(name)
1243 tools[name] = btool
1244 return btool
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03001245
1246 def SetUpdateHash(self, update_hash):
1247 """Set whether this entry's "hash" subnode should be updated
1248
1249 Args:
1250 update_hash: True if hash should be updated, False if not
1251 """
1252 self.update_hash = update_hash
Simon Glass81b71c32022-02-08 11:50:00 -07001253
Simon Glass72e423c2022-03-05 20:19:05 -07001254 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass81b71c32022-02-08 11:50:00 -07001255 """Put the contents of a list of entries into a file
1256
1257 Args:
1258 entries (list of Entry): Entries to collect
1259 prefix (str): Filename prefix of file to write to
Simon Glass72e423c2022-03-05 20:19:05 -07001260 fake_size (int): Size of fake file to create if needed
Simon Glass81b71c32022-02-08 11:50:00 -07001261
1262 If any entry does not have contents yet, this function returns False
1263 for the data.
1264
1265 Returns:
1266 Tuple:
Simon Glass6d427c42022-03-05 20:18:58 -07001267 bytes: Concatenated data from all the entries (or None)
1268 str: Filename of file written (or None if no data)
1269 str: Unique portion of filename (or None if no data)
Simon Glass81b71c32022-02-08 11:50:00 -07001270 """
1271 data = b''
1272 for entry in entries:
1273 # First get the input data and put it in a file. If not available,
1274 # try later.
Simon Glass72e423c2022-03-05 20:19:05 -07001275 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass6d427c42022-03-05 20:18:58 -07001276 return None, None, None
Simon Glass81b71c32022-02-08 11:50:00 -07001277 data += entry.GetData()
1278 uniq = self.GetUniqueName()
1279 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1280 tools.write_file(fname, data)
1281 return data, fname, uniq
Simon Glass7960a0a2022-08-07 09:46:46 -06001282
1283 @classmethod
1284 def create_fake_dir(cls):
1285 """Create the directory for fake files"""
1286 cls.fake_dir = tools.get_output_filename('binman-fake')
1287 if not os.path.exists(cls.fake_dir):
1288 os.mkdir(cls.fake_dir)
1289 tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")
Simon Glasscdadada2022-08-13 11:40:44 -06001290
1291 def ensure_props(self):
1292 """Raise an exception if properties are missing
1293
1294 Args:
1295 prop_list (list of str): List of properties to check for
1296
1297 Raises:
1298 ValueError: Any property is missing
1299 """
1300 not_present = []
1301 for prop in self.required_props:
1302 if not prop in self._node.props:
1303 not_present.append(prop)
1304 if not_present:
1305 self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}")
Simon Glassc8c9f312023-01-07 14:07:12 -07001306
1307 def mark_absent(self, msg):
1308 tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg))
1309 self.absent = True
Simon Glass2f80c5e2023-01-07 14:07:14 -07001310
1311 def read_elf_segments(self):
1312 """Read segments from an entry that can generate an ELF file
1313
1314 Returns:
1315 tuple:
1316 list of segments, each:
1317 int: Segment number (0 = first)
1318 int: Start address of segment in memory
1319 bytes: Contents of segment
1320 int: entry address of ELF file
1321 """
1322 return None