blob: de51d29589133f271fe3503357945c2223cf5aa4 [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 Glass87958982020-09-01 05:13:57 -060076 missing: True if this entry is missing its contents
77 allow_missing: Allow children of this entry to be missing (used by
78 subclasses such as Entry_section)
Heiko Thierya89c8f22022-01-06 11:49:41 +010079 allow_fake: Allow creating a dummy fake file if the blob file is not
80 available. This is mainly used for testing.
Simon Glass87958982020-09-01 05:13:57 -060081 external: True if this entry contains an external binary blob
Simon Glass386c63c2022-01-09 20:13:50 -070082 bintools: Bintools used by this entry (only populated for Image)
Simon Glass4f9ee832022-01-09 20:14:09 -070083 missing_bintools: List of missing bintools for this entry
Alper Nebi Yasakee813c82022-02-09 22:02:35 +030084 update_hash: True if this entry's "hash" subnode should be
85 updated with a hash of the entry contents
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +020086 comp_bintool: Bintools used for compress and decompress data
Simon Glass7960a0a2022-08-07 09:46:46 -060087 fake_fname: Fake filename, if one was created, else None
Simon Glasscdadada2022-08-13 11:40:44 -060088 required_props (dict of str): Properties which must be present. This can
89 be added to by subclasses
Simon Glass3fbba552022-10-20 18:22:46 -060090 elf_fname (str): Filename of the ELF file, if this entry holds an ELF
91 file, or is a binary file produced from an ELF file
92 auto_write_symbols (bool): True to write ELF symbols into this entry's
93 contents
Simon Glassc8c9f312023-01-07 14:07:12 -070094 absent (bool): True if this entry is absent. This can be controlled by
95 the entry itself, allowing it to vanish in certain circumstances.
96 An absent entry is removed during processing so that it does not
97 appear in the map
Simon Glassbf7fd502016-11-25 20:15:51 -070098 """
Simon Glass7960a0a2022-08-07 09:46:46 -060099 fake_dir = None
100
Simon Glass3fbba552022-10-20 18:22:46 -0600101 def __init__(self, section, etype, node, name_prefix='',
102 auto_write_symbols=False):
Simon Glass8dbb7442019-08-24 07:22:44 -0600103 # Put this here to allow entry-docs and help to work without libfdt
104 global state
Simon Glass16287932020-04-17 18:09:03 -0600105 from binman import state
Simon Glass8dbb7442019-08-24 07:22:44 -0600106
Simon Glass25ac0e62018-06-01 09:38:14 -0600107 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -0700108 self.etype = etype
109 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -0600110 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -0600111 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700112 self.size = None
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600113 self.pre_reset_size = None
Simon Glass8287ee82019-07-08 14:25:30 -0600114 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -0600115 self.data = None
Simon Glass97c3e9a2020-10-26 17:40:15 -0600116 self.uncomp_data = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700117 self.contents_size = 0
118 self.align = None
119 self.align_size = None
120 self.align_end = None
121 self.pad_before = 0
122 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -0600123 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -0600124 self.image_pos = None
Simon Glass80a66ae2022-03-05 20:18:59 -0700125 self.extend_size = False
Simon Glass8287ee82019-07-08 14:25:30 -0600126 self.compress = 'none'
Simon Glassb1cca952020-07-09 18:39:40 -0600127 self.missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100128 self.faked = False
Simon Glass87958982020-09-01 05:13:57 -0600129 self.external = False
130 self.allow_missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100131 self.allow_fake = False
Simon Glass386c63c2022-01-09 20:13:50 -0700132 self.bintools = {}
Simon Glass4f9ee832022-01-09 20:14:09 -0700133 self.missing_bintools = []
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300134 self.update_hash = True
Simon Glass7960a0a2022-08-07 09:46:46 -0600135 self.fake_fname = None
Simon Glasscdadada2022-08-13 11:40:44 -0600136 self.required_props = []
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +0200137 self.comp_bintool = None
Simon Glass3fbba552022-10-20 18:22:46 -0600138 self.elf_fname = None
139 self.auto_write_symbols = auto_write_symbols
Simon Glassc8c9f312023-01-07 14:07:12 -0700140 self.absent = False
Simon Glassbf7fd502016-11-25 20:15:51 -0700141
142 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700143 def FindEntryClass(etype, expanded):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600144 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -0700145
146 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -0600147 node_node: Path name of Node object containing information about
148 the entry to create (used for errors)
149 etype: Entry type to use
Simon Glassb35fb172021-03-18 20:25:04 +1300150 expanded: Use the expanded version of etype
Simon Glassbf7fd502016-11-25 20:15:51 -0700151
152 Returns:
Simon Glassb35fb172021-03-18 20:25:04 +1300153 The entry class object if found, else None if not found and expanded
Simon Glass858436d2021-11-23 21:09:49 -0700154 is True, else a tuple:
155 module name that could not be found
156 exception received
Simon Glassbf7fd502016-11-25 20:15:51 -0700157 """
Simon Glassdd57c132018-06-01 09:38:11 -0600158 # Convert something like 'u-boot@0' to 'u_boot' since we are only
159 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700160 module_name = etype.replace('-', '_')
Simon Glassb35fb172021-03-18 20:25:04 +1300161
Simon Glassdd57c132018-06-01 09:38:11 -0600162 if '@' in module_name:
163 module_name = module_name.split('@')[0]
Simon Glassb35fb172021-03-18 20:25:04 +1300164 if expanded:
165 module_name += '_expanded'
Simon Glassbf7fd502016-11-25 20:15:51 -0700166 module = modules.get(module_name)
167
Simon Glassbadf0ec2018-06-01 09:38:15 -0600168 # Also allow entry-type modules to be brought in from the etype directory.
169
Simon Glassbf7fd502016-11-25 20:15:51 -0700170 # Import the module if we have not already done so.
171 if not module:
172 try:
Simon Glass16287932020-04-17 18:09:03 -0600173 module = importlib.import_module('binman.etype.' + module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600174 except ImportError as e:
Simon Glassb35fb172021-03-18 20:25:04 +1300175 if expanded:
176 return None
Simon Glass858436d2021-11-23 21:09:49 -0700177 return module_name, e
Simon Glassbf7fd502016-11-25 20:15:51 -0700178 modules[module_name] = module
179
Simon Glassfd8d1f72018-07-17 13:25:36 -0600180 # Look up the expected class name
181 return getattr(module, 'Entry_%s' % module_name)
182
183 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700184 def Lookup(node_path, etype, expanded, missing_etype=False):
185 """Look up the entry class for a node.
186
187 Args:
188 node_node (str): Path name of Node object containing information
189 about the entry to create (used for errors)
190 etype (str): Entry type to use
191 expanded (bool): Use the expanded version of etype
192 missing_etype (bool): True to default to a blob etype if the
193 requested etype is not found
194
195 Returns:
196 The entry class object if found, else None if not found and expanded
197 is True
198
199 Raise:
200 ValueError if expanded is False and the class is not found
201 """
202 # Convert something like 'u-boot@0' to 'u_boot' since we are only
203 # interested in the type.
204 cls = Entry.FindEntryClass(etype, expanded)
205 if cls is None:
206 return None
207 elif isinstance(cls, tuple):
208 if missing_etype:
209 cls = Entry.FindEntryClass('blob', False)
210 if isinstance(cls, tuple): # This should not fail
211 module_name, e = cls
212 raise ValueError(
213 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
214 (etype, node_path, module_name, e))
215 return cls
216
217 @staticmethod
218 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600219 """Create a new entry for a node.
220
221 Args:
Simon Glass858436d2021-11-23 21:09:49 -0700222 section (entry_Section): Section object containing this node
223 node (Node): Node object containing information about the entry to
224 create
225 etype (str): Entry type to use, or None to work it out (used for
226 tests)
227 expanded (bool): Use the expanded version of etype
228 missing_etype (bool): True to default to a blob etype if the
229 requested etype is not found
Simon Glassfd8d1f72018-07-17 13:25:36 -0600230
231 Returns:
232 A new Entry object of the correct type (a subclass of Entry)
233 """
234 if not etype:
235 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glass858436d2021-11-23 21:09:49 -0700236 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glassb35fb172021-03-18 20:25:04 +1300237 if obj and expanded:
238 # Check whether to use the expanded entry
239 new_etype = etype + '-expanded'
Simon Glass3d433382021-03-21 18:24:30 +1300240 can_expand = not fdt_util.GetBool(node, 'no-expanded')
241 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glassb35fb172021-03-18 20:25:04 +1300242 etype = new_etype
243 else:
244 obj = None
245 if not obj:
Simon Glass858436d2021-11-23 21:09:49 -0700246 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600247
Simon Glassbf7fd502016-11-25 20:15:51 -0700248 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600249 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700250
251 def ReadNode(self):
252 """Read entry information from the node
253
Simon Glassc6bd6e22019-07-20 12:23:45 -0600254 This must be called as the first thing after the Entry is created.
255
Simon Glassbf7fd502016-11-25 20:15:51 -0700256 This reads all the fields we recognise from the node, ready for use.
257 """
Simon Glasscdadada2022-08-13 11:40:44 -0600258 self.ensure_props()
Simon Glass15a587c2018-07-17 13:25:51 -0600259 if 'pos' in self._node.props:
260 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass80a66ae2022-03-05 20:18:59 -0700261 if 'expand-size' in self._node.props:
262 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glass3ab95982018-08-01 15:22:37 -0600263 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700264 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glass12bb1a92019-07-20 12:23:51 -0600265 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
266 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
267 if self.GetImage().copy_to_orig:
268 self.orig_offset = self.offset
269 self.orig_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600270
Simon Glassffded752019-07-08 14:25:46 -0600271 # These should not be set in input files, but are set in an FDT map,
272 # which is also read by this code.
273 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
274 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
275
Simon Glassbf7fd502016-11-25 20:15:51 -0700276 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700277 if tools.not_power_of_two(self.align):
Simon Glassbf7fd502016-11-25 20:15:51 -0700278 raise ValueError("Node '%s': Alignment %s must be a power of two" %
279 (self._node.path, self.align))
Simon Glass5ff9fed2021-03-21 18:24:33 +1300280 if self.section and self.align is None:
281 self.align = self.section.align_default
Simon Glassbf7fd502016-11-25 20:15:51 -0700282 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
283 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
284 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700285 if tools.not_power_of_two(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600286 self.Raise("Alignment size %s must be a power of two" %
287 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700288 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600289 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glass80a66ae2022-03-05 20:18:59 -0700290 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassb2381432020-09-06 10:39:09 -0600291 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glassbf7fd502016-11-25 20:15:51 -0700292
Simon Glass87c96292020-10-26 17:40:06 -0600293 # This is only supported by blobs and sections at present
294 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
295
Simon Glass6c234bf2018-09-14 04:57:18 -0600296 def GetDefaultFilename(self):
297 return None
298
Simon Glassa8adb6d2019-07-20 12:23:28 -0600299 def GetFdts(self):
300 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600301
302 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600303 Empty dict, if this entry is not a .dtb, otherwise:
304 Dict:
305 key: Filename from this entry (without the path)
Simon Glass4bdd3002019-07-20 12:23:31 -0600306 value: Tuple:
Simon Glassadb67bb2021-03-18 20:25:02 +1300307 Entry object for this dtb
Simon Glass4bdd3002019-07-20 12:23:31 -0600308 Filename of file containing this dtb
Simon Glass539aece2018-09-14 04:57:22 -0600309 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600310 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600311
Simon Glassc9ee33a2022-03-05 20:19:00 -0700312 def gen_entries(self):
313 """Allow entries to generate other entries
Simon Glassa01d1a22021-03-18 20:24:52 +1300314
315 Some entries generate subnodes automatically, from which sub-entries
316 are then created. This method allows those to be added to the binman
317 definition for the current image. An entry which implements this method
318 should call state.AddSubnode() to add a subnode and can add properties
319 with state.AddString(), etc.
320
321 An example is 'files', which produces a section containing a list of
322 files.
323 """
Simon Glass0a98b282018-09-14 04:57:28 -0600324 pass
325
Simon Glassa9fad072020-10-26 17:40:17 -0600326 def AddMissingProperties(self, have_image_pos):
327 """Add new properties to the device tree as needed for this entry
328
329 Args:
330 have_image_pos: True if this entry has an image position. This can
331 be False if its parent section is compressed, since compression
332 groups all entries together into a compressed block of data,
333 obscuring the start of each individual child entry
334 """
335 for prop in ['offset', 'size']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600336 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600337 state.AddZeroProp(self._node, prop)
Simon Glassa9fad072020-10-26 17:40:17 -0600338 if have_image_pos and 'image-pos' not in self._node.props:
339 state.AddZeroProp(self._node, 'image-pos')
Simon Glass12bb1a92019-07-20 12:23:51 -0600340 if self.GetImage().allow_repack:
341 if self.orig_offset is not None:
342 state.AddZeroProp(self._node, 'orig-offset', True)
343 if self.orig_size is not None:
344 state.AddZeroProp(self._node, 'orig-size', True)
345
Simon Glass8287ee82019-07-08 14:25:30 -0600346 if self.compress != 'none':
347 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300348
349 if self.update_hash:
350 err = state.CheckAddHashProp(self._node)
351 if err:
352 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600353
354 def SetCalculatedProperties(self):
355 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600356 state.SetInt(self._node, 'offset', self.offset)
357 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600358 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassa9fad072020-10-26 17:40:17 -0600359 if self.image_pos is not None:
Simon Glass08594d42020-11-02 12:55:44 -0700360 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass12bb1a92019-07-20 12:23:51 -0600361 if self.GetImage().allow_repack:
362 if self.orig_offset is not None:
363 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
364 if self.orig_size is not None:
365 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glass8287ee82019-07-08 14:25:30 -0600366 if self.uncomp_size is not None:
367 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300368
369 if self.update_hash:
370 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600371
Simon Glassecab8972018-07-06 10:27:40 -0600372 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600373 """Allow entries to adjust the device tree
374
375 Some entries need to adjust the device tree for their purposes. This
376 may involve adding or deleting properties.
377
378 Returns:
379 True if processing is complete
380 False if processing could not be completed due to a dependency.
381 This will cause the entry to be retried after others have been
382 called
383 """
Simon Glassecab8972018-07-06 10:27:40 -0600384 return True
385
Simon Glassc8d48ef2018-06-01 09:38:21 -0600386 def SetPrefix(self, prefix):
387 """Set the name prefix for a node
388
389 Args:
390 prefix: Prefix to set, or '' to not use a prefix
391 """
392 if prefix:
393 self.name = prefix + self.name
394
Simon Glass5c890232018-07-06 10:27:19 -0600395 def SetContents(self, data):
396 """Set the contents of an entry
397
398 This sets both the data and content_size properties
399
400 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600401 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600402 """
403 self.data = data
404 self.contents_size = len(self.data)
405
406 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600407 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600408
Simon Glassa0dcaf22019-07-08 14:25:35 -0600409 This checks that the new data is the same size as the old. If the size
410 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600411
412 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600413 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600414
415 Raises:
416 ValueError if the new data size is not the same as the old
417 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600418 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600419 new_size = len(data)
Simon Glass61ec04f2019-07-20 12:23:58 -0600420 if state.AllowEntryExpansion() and new_size > self.contents_size:
421 # self.data will indicate the new size needed
422 size_ok = False
423 elif state.AllowEntryContraction() and new_size < self.contents_size:
424 size_ok = False
425
426 # If not allowed to change, try to deal with it or give up
427 if size_ok:
Simon Glassc52c9e72019-07-08 14:25:37 -0600428 if new_size > self.contents_size:
Simon Glass61ec04f2019-07-20 12:23:58 -0600429 self.Raise('Cannot update entry size from %d to %d' %
430 (self.contents_size, new_size))
431
432 # Don't let the data shrink. Pad it if necessary
433 if size_ok and new_size < self.contents_size:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700434 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass61ec04f2019-07-20 12:23:58 -0600435
436 if not size_ok:
Simon Glassf3385a52022-01-29 14:14:15 -0700437 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glassc1aa66e2022-01-29 14:14:04 -0700438 self._node.path, to_hex(self.contents_size),
439 to_hex(new_size)))
Simon Glass5c890232018-07-06 10:27:19 -0600440 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600441 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600442
Simon Glass72e423c2022-03-05 20:19:05 -0700443 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glassbf7fd502016-11-25 20:15:51 -0700444 """Figure out the contents of an entry.
445
Simon Glass72e423c2022-03-05 20:19:05 -0700446 Args:
447 skip_entry (Entry): Entry to skip when obtaining section contents
448 fake_size (int): Size of fake file to create if needed
449
Simon Glassbf7fd502016-11-25 20:15:51 -0700450 Returns:
451 True if the contents were found, False if another call is needed
452 after the other entries are processed.
453 """
454 # No contents by default: subclasses can implement this
455 return True
456
Simon Glassc52c9e72019-07-08 14:25:37 -0600457 def ResetForPack(self):
458 """Reset offset/size fields so that packing can be done again"""
Simon Glass9f297b02019-07-20 12:23:36 -0600459 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700460 (to_hex(self.offset), to_hex(self.orig_offset),
461 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600462 self.pre_reset_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600463 self.offset = self.orig_offset
464 self.size = self.orig_size
465
Simon Glass3ab95982018-08-01 15:22:37 -0600466 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600467 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700468
469 Most of the time the entries are not fully specified. There may be
470 an alignment but no size. In that case we take the size from the
471 contents of the entry.
472
Simon Glass3ab95982018-08-01 15:22:37 -0600473 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700474
Simon Glass3ab95982018-08-01 15:22:37 -0600475 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700476 entry will be know.
477
478 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600479 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700480
481 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600482 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700483 """
Simon Glass9f297b02019-07-20 12:23:36 -0600484 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700485 (to_hex(self.offset), to_hex(self.size),
Simon Glass9f297b02019-07-20 12:23:36 -0600486 self.contents_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600487 if self.offset is None:
488 if self.offset_unset:
489 self.Raise('No offset set with offset-unset: should another '
490 'entry provide this correct offset?')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700491 self.offset = tools.align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700492 needed = self.pad_before + self.contents_size + self.pad_after
Simon Glassc1aa66e2022-01-29 14:14:04 -0700493 needed = tools.align(needed, self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700494 size = self.size
495 if not size:
496 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600497 new_offset = self.offset + size
Simon Glassc1aa66e2022-01-29 14:14:04 -0700498 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glass3ab95982018-08-01 15:22:37 -0600499 if aligned_offset != new_offset:
500 size = aligned_offset - self.offset
501 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700502
503 if not self.size:
504 self.size = size
505
506 if self.size < needed:
507 self.Raise("Entry contents size is %#x (%d) but entry size is "
508 "%#x (%d)" % (needed, needed, self.size, self.size))
509 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600510 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700511 # conflict with the provided alignment values
Simon Glassc1aa66e2022-01-29 14:14:04 -0700512 if self.size != tools.align(self.size, self.align_size):
Simon Glassbf7fd502016-11-25 20:15:51 -0700513 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
514 (self.size, self.size, self.align_size, self.align_size))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700515 if self.offset != tools.align(self.offset, self.align):
Simon Glass3ab95982018-08-01 15:22:37 -0600516 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
517 (self.offset, self.offset, self.align, self.align))
Simon Glass9f297b02019-07-20 12:23:36 -0600518 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
519 (self.offset, self.size, self.contents_size, new_offset))
Simon Glassbf7fd502016-11-25 20:15:51 -0700520
Simon Glass3ab95982018-08-01 15:22:37 -0600521 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700522
523 def Raise(self, msg):
524 """Convenience function to raise an error referencing a node"""
525 raise ValueError("Node '%s': %s" % (self._node.path, msg))
526
Simon Glass189f2912021-03-21 18:24:31 +1300527 def Info(self, msg):
528 """Convenience function to log info referencing a node"""
529 tag = "Info '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700530 tout.detail('%30s: %s' % (tag, msg))
Simon Glass189f2912021-03-21 18:24:31 +1300531
Simon Glass9f297b02019-07-20 12:23:36 -0600532 def Detail(self, msg):
533 """Convenience function to log detail referencing a node"""
534 tag = "Node '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700535 tout.detail('%30s: %s' % (tag, msg))
Simon Glass9f297b02019-07-20 12:23:36 -0600536
Simon Glass53af22a2018-07-17 13:25:32 -0600537 def GetEntryArgsOrProps(self, props, required=False):
538 """Return the values of a set of properties
539
540 Args:
541 props: List of EntryArg objects
542
543 Raises:
544 ValueError if a property is not found
545 """
546 values = []
547 missing = []
548 for prop in props:
549 python_prop = prop.name.replace('-', '_')
550 if hasattr(self, python_prop):
551 value = getattr(self, python_prop)
552 else:
553 value = None
554 if value is None:
555 value = self.GetArg(prop.name, prop.datatype)
556 if value is None and required:
557 missing.append(prop.name)
558 values.append(value)
559 if missing:
Simon Glass939d1062021-01-06 21:35:16 -0700560 self.GetImage().MissingArgs(self, missing)
Simon Glass53af22a2018-07-17 13:25:32 -0600561 return values
562
Simon Glassbf7fd502016-11-25 20:15:51 -0700563 def GetPath(self):
564 """Get the path of a node
565
566 Returns:
567 Full path of the node for this entry
568 """
569 return self._node.path
570
Simon Glass631f7522021-03-21 18:24:32 +1300571 def GetData(self, required=True):
Simon Glass63e7ba62020-10-26 17:40:16 -0600572 """Get the contents of an entry
573
Simon Glass631f7522021-03-21 18:24:32 +1300574 Args:
575 required: True if the data must be present, False if it is OK to
576 return None
577
Simon Glass63e7ba62020-10-26 17:40:16 -0600578 Returns:
579 bytes content of the entry, excluding any padding. If the entry is
580 compressed, the compressed data is returned
581 """
Simon Glassc1aa66e2022-01-29 14:14:04 -0700582 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glassbf7fd502016-11-25 20:15:51 -0700583 return self.data
584
Simon Glass271a0832020-11-02 12:55:43 -0700585 def GetPaddedData(self, data=None):
586 """Get the data for an entry including any padding
587
588 Gets the entry data and uses its section's pad-byte value to add padding
589 before and after as defined by the pad-before and pad-after properties.
590
591 This does not consider alignment.
592
593 Returns:
594 Contents of the entry along with any pad bytes before and
595 after it (bytes)
596 """
597 if data is None:
598 data = self.GetData()
599 return self.section.GetPaddedDataForEntry(self, data)
600
Simon Glass3ab95982018-08-01 15:22:37 -0600601 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600602 """Get the offsets for siblings
603
604 Some entry types can contain information about the position or size of
605 other entries. An example of this is the Intel Flash Descriptor, which
606 knows where the Intel Management Engine section should go.
607
608 If this entry knows about the position of other entries, it can specify
609 this by returning values here
610
611 Returns:
612 Dict:
613 key: Entry type
614 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600615 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600616 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700617 return {}
618
Simon Glasscf549042019-07-08 13:18:39 -0600619 def SetOffsetSize(self, offset, size):
620 """Set the offset and/or size of an entry
621
622 Args:
623 offset: New offset, or None to leave alone
624 size: New size, or None to leave alone
625 """
626 if offset is not None:
627 self.offset = offset
628 if size is not None:
629 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700630
Simon Glassdbf6be92018-08-01 15:22:42 -0600631 def SetImagePos(self, image_pos):
632 """Set the position in the image
633
634 Args:
635 image_pos: Position of this entry in the image
636 """
637 self.image_pos = image_pos + self.offset
638
Simon Glassbf7fd502016-11-25 20:15:51 -0700639 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600640 """Do any post-packing updates of entry contents
641
642 This function should call ProcessContentsUpdate() to update the entry
643 contents, if necessary, returning its return value here.
644
645 Args:
646 data: Data to set to the contents (bytes)
647
648 Returns:
649 True if the new data size is OK, False if expansion is needed
650
651 Raises:
652 ValueError if the new data size is not the same as the old and
653 state.AllowEntryExpansion() is False
654 """
655 return True
Simon Glass19790632017-11-13 18:55:01 -0700656
Simon Glassf55382b2018-06-01 09:38:13 -0600657 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700658 """Write symbol values into binary files for access at run time
659
660 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600661 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700662 """
Simon Glass3fbba552022-10-20 18:22:46 -0600663 if self.auto_write_symbols:
Simon Glassd2afb9e2022-10-20 18:22:47 -0600664 # Check if we are writing symbols into an ELF file
665 is_elf = self.GetDefaultFilename() == self.elf_fname
666 elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(),
667 is_elf)
Simon Glass18546952018-06-01 09:38:16 -0600668
Simon Glass6ddd6112020-10-26 17:40:18 -0600669 def CheckEntries(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600670 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600671
Simon Glass3ab95982018-08-01 15:22:37 -0600672 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600673 than having to be fully inside their section). Sub-classes can implement
674 this function and raise if there is a problem.
675 """
676 pass
Simon Glass3b0c38212018-06-01 09:38:20 -0600677
Simon Glass8122f392018-07-17 13:25:28 -0600678 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600679 def GetStr(value):
680 if value is None:
681 return '<none> '
682 return '%08x' % value
683
684 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600685 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600686 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
687 Entry.GetStr(offset), Entry.GetStr(size),
688 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600689
Simon Glass3b0c38212018-06-01 09:38:20 -0600690 def WriteMap(self, fd, indent):
691 """Write a map of the entry to a .map file
692
693 Args:
694 fd: File to write the map to
695 indent: Curent indent level of map (0=none, 1=one level, etc.)
696 """
Simon Glass1be70d22018-07-17 13:25:49 -0600697 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
698 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600699
Simon Glassd626e822022-08-13 11:40:50 -0600700 # pylint: disable=assignment-from-none
Simon Glass11e36cc2018-07-17 13:25:38 -0600701 def GetEntries(self):
702 """Return a list of entries contained by this entry
703
704 Returns:
705 List of entries, or None if none. A normal entry has no entries
706 within it so will return None
707 """
708 return None
709
Simon Glassd626e822022-08-13 11:40:50 -0600710 def FindEntryByNode(self, find_node):
711 """Find a node in an entry, searching all subentries
712
713 This does a recursive search.
714
715 Args:
716 find_node (fdt.Node): Node to find
717
718 Returns:
719 Entry: entry, if found, else None
720 """
721 entries = self.GetEntries()
722 if entries:
723 for entry in entries.values():
724 if entry._node == find_node:
725 return entry
726 found = entry.FindEntryByNode(find_node)
727 if found:
728 return found
729
730 return None
731
Simon Glass53af22a2018-07-17 13:25:32 -0600732 def GetArg(self, name, datatype=str):
733 """Get the value of an entry argument or device-tree-node property
734
735 Some node properties can be provided as arguments to binman. First check
736 the entry arguments, and fall back to the device tree if not found
737
738 Args:
739 name: Argument name
740 datatype: Data type (str or int)
741
742 Returns:
743 Value of argument as a string or int, or None if no value
744
745 Raises:
746 ValueError if the argument cannot be converted to in
747 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600748 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600749 if value is not None:
750 if datatype == int:
751 try:
752 value = int(value)
753 except ValueError:
754 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
755 (name, value))
756 elif datatype == str:
757 pass
758 else:
759 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
760 datatype)
761 else:
762 value = fdt_util.GetDatatype(self._node, name, datatype)
763 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600764
765 @staticmethod
766 def WriteDocs(modules, test_missing=None):
767 """Write out documentation about the various entry types to stdout
768
769 Args:
770 modules: List of modules to include
771 test_missing: Used for testing. This is a module to report
772 as missing
773 """
774 print('''Binman Entry Documentation
775===========================
776
777This file describes the entry types supported by binman. These entry types can
778be placed in an image one by one to build up a final firmware image. It is
779fairly easy to create new entry types. Just add a new file to the 'etype'
780directory. You can use the existing entries as examples.
781
782Note that some entries are subclasses of others, using and extending their
783features to produce new behaviours.
784
785
786''')
787 modules = sorted(modules)
788
789 # Don't show the test entry
790 if '_testing' in modules:
791 modules.remove('_testing')
792 missing = []
793 for name in modules:
Simon Glassb35fb172021-03-18 20:25:04 +1300794 module = Entry.Lookup('WriteDocs', name, False)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600795 docs = getattr(module, '__doc__')
796 if test_missing == name:
797 docs = None
798 if docs:
799 lines = docs.splitlines()
800 first_line = lines[0]
801 rest = [line[4:] for line in lines[1:]]
802 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glass228c9b82022-08-07 16:33:25 -0600803
804 # Create a reference for use by rST docs
805 ref_name = f'etype_{module.__name__[6:]}'.lower()
806 print('.. _%s:' % ref_name)
807 print()
Simon Glassfd8d1f72018-07-17 13:25:36 -0600808 print(hdr)
809 print('-' * len(hdr))
810 print('\n'.join(rest))
811 print()
812 print()
813 else:
814 missing.append(name)
815
816 if missing:
817 raise ValueError('Documentation is missing for modules: %s' %
818 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600819
820 def GetUniqueName(self):
821 """Get a unique name for a node
822
823 Returns:
824 String containing a unique name for a node, consisting of the name
825 of all ancestors (starting from within the 'binman' node) separated
826 by a dot ('.'). This can be useful for generating unique filesnames
827 in the output directory.
828 """
829 name = self.name
830 node = self._node
831 while node.parent:
832 node = node.parent
Alper Nebi Yasak67bf2c82022-03-27 18:31:44 +0300833 if node.name in ('binman', '/'):
Simon Glassa326b492018-09-14 04:57:11 -0600834 break
835 name = '%s.%s' % (node.name, name)
836 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600837
Simon Glass80a66ae2022-03-05 20:18:59 -0700838 def extend_to_limit(self, limit):
839 """Extend an entry so that it ends at the given offset limit"""
Simon Glassba64a0b2018-09-14 04:57:29 -0600840 if self.offset + self.size < limit:
841 self.size = limit - self.offset
842 # Request the contents again, since changing the size requires that
843 # the data grows. This should not fail, but check it to be sure.
844 if not self.ObtainContents():
845 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600846
847 def HasSibling(self, name):
848 """Check if there is a sibling of a given name
849
850 Returns:
851 True if there is an entry with this name in the the same section,
852 else False
853 """
854 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600855
856 def GetSiblingImagePos(self, name):
857 """Return the image position of the given sibling
858
859 Returns:
860 Image position of sibling, or None if the sibling has no position,
861 or False if there is no such sibling
862 """
863 if not self.HasSibling(name):
864 return False
865 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600866
867 @staticmethod
868 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
869 uncomp_size, offset, entry):
870 """Add a new entry to the entries list
871
872 Args:
873 entries: List (of EntryInfo objects) to add to
874 indent: Current indent level to add to list
875 name: Entry name (string)
876 etype: Entry type (string)
877 size: Entry size in bytes (int)
878 image_pos: Position within image in bytes (int)
879 uncomp_size: Uncompressed size if the entry uses compression, else
880 None
881 offset: Entry offset within parent in bytes (int)
882 entry: Entry object
883 """
884 entries.append(EntryInfo(indent, name, etype, size, image_pos,
885 uncomp_size, offset, entry))
886
887 def ListEntries(self, entries, indent):
888 """Add files in this entry to the list of entries
889
890 This can be overridden by subclasses which need different behaviour.
891
892 Args:
893 entries: List (of EntryInfo objects) to add to
894 indent: Current indent level to add to list
895 """
896 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
897 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600898
Simon Glass943bf782021-11-23 21:09:50 -0700899 def ReadData(self, decomp=True, alt_format=None):
Simon Glassf667e452019-07-08 14:25:50 -0600900 """Read the data for an entry from the image
901
902 This is used when the image has been read in and we want to extract the
903 data for a particular entry from that image.
904
905 Args:
906 decomp: True to decompress any compressed data before returning it;
907 False to return the raw, uncompressed data
908
909 Returns:
910 Entry data (bytes)
911 """
912 # Use True here so that we get an uncompressed section to work from,
913 # although compressed sections are currently not supported
Simon Glassf3385a52022-01-29 14:14:15 -0700914 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass2d553c02019-09-25 08:56:21 -0600915 (self.section.GetPath(), self.GetPath()))
Simon Glass943bf782021-11-23 21:09:50 -0700916 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glassa9cd39e2019-07-20 12:24:04 -0600917 return data
Simon Glassd5079332019-07-20 12:23:41 -0600918
Simon Glass943bf782021-11-23 21:09:50 -0700919 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass2d553c02019-09-25 08:56:21 -0600920 """Read the data for a particular child entry
Simon Glass4e185e82019-09-25 08:56:20 -0600921
922 This reads data from the parent and extracts the piece that relates to
923 the given child.
924
925 Args:
Simon Glass943bf782021-11-23 21:09:50 -0700926 child (Entry): Child entry to read data for (must be valid)
927 decomp (bool): True to decompress any compressed data before
928 returning it; False to return the raw, uncompressed data
929 alt_format (str): Alternative format to read in, or None
Simon Glass4e185e82019-09-25 08:56:20 -0600930
931 Returns:
932 Data for the child (bytes)
933 """
934 pass
935
Simon Glassd5079332019-07-20 12:23:41 -0600936 def LoadData(self, decomp=True):
937 data = self.ReadData(decomp)
Simon Glass10f9d002019-07-20 12:23:50 -0600938 self.contents_size = len(data)
Simon Glassd5079332019-07-20 12:23:41 -0600939 self.ProcessContentsUpdate(data)
940 self.Detail('Loaded data size %x' % len(data))
Simon Glassc5ad04b2019-07-20 12:23:46 -0600941
Simon Glass943bf782021-11-23 21:09:50 -0700942 def GetAltFormat(self, data, alt_format):
943 """Read the data for an extry in an alternative format
944
945 Supported formats are list in the documentation for each entry. An
946 example is fdtmap which provides .
947
948 Args:
949 data (bytes): Data to convert (this should have been produced by the
950 entry)
951 alt_format (str): Format to use
952
953 """
954 pass
955
Simon Glassc5ad04b2019-07-20 12:23:46 -0600956 def GetImage(self):
957 """Get the image containing this entry
958
959 Returns:
960 Image object containing this entry
961 """
962 return self.section.GetImage()
Simon Glass10f9d002019-07-20 12:23:50 -0600963
964 def WriteData(self, data, decomp=True):
965 """Write the data to an entry in the image
966
967 This is used when the image has been read in and we want to replace the
968 data for a particular entry in that image.
969
970 The image must be re-packed and written out afterwards.
971
972 Args:
973 data: Data to replace it with
974 decomp: True to compress the data if needed, False if data is
975 already compressed so should be used as is
976
977 Returns:
978 True if the data did not result in a resize of this entry, False if
979 the entry must be resized
980 """
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600981 if self.size is not None:
982 self.contents_size = self.size
983 else:
984 self.contents_size = self.pre_reset_size
Simon Glass10f9d002019-07-20 12:23:50 -0600985 ok = self.ProcessContentsUpdate(data)
986 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glass7210c892019-07-20 12:24:05 -0600987 section_ok = self.section.WriteChildData(self)
988 return ok and section_ok
989
990 def WriteChildData(self, child):
991 """Handle writing the data in a child entry
992
993 This should be called on the child's parent section after the child's
Simon Glass557693e2021-11-23 11:03:44 -0700994 data has been updated. It should update any data structures needed to
995 validate that the update is successful.
Simon Glass7210c892019-07-20 12:24:05 -0600996
997 This base-class implementation does nothing, since the base Entry object
998 does not have any children.
999
1000 Args:
1001 child: Child Entry that was written
1002
1003 Returns:
1004 True if the section could be updated successfully, False if the
Simon Glass557693e2021-11-23 11:03:44 -07001005 data is such that the section could not update
Simon Glass7210c892019-07-20 12:24:05 -06001006 """
1007 return True
Simon Glasseba1f0c2019-07-20 12:23:55 -06001008
1009 def GetSiblingOrder(self):
1010 """Get the relative order of an entry amoung its siblings
1011
1012 Returns:
1013 'start' if this entry is first among siblings, 'end' if last,
1014 otherwise None
1015 """
1016 entries = list(self.section.GetEntries().values())
1017 if entries:
1018 if self == entries[0]:
1019 return 'start'
1020 elif self == entries[-1]:
1021 return 'end'
1022 return 'middle'
Simon Glass4f9f1052020-07-09 18:39:38 -06001023
1024 def SetAllowMissing(self, allow_missing):
1025 """Set whether a section allows missing external blobs
1026
1027 Args:
1028 allow_missing: True if allowed, False if not allowed
1029 """
1030 # This is meaningless for anything other than sections
1031 pass
Simon Glassb1cca952020-07-09 18:39:40 -06001032
Heiko Thierya89c8f22022-01-06 11:49:41 +01001033 def SetAllowFakeBlob(self, allow_fake):
1034 """Set whether a section allows to create a fake blob
1035
1036 Args:
1037 allow_fake: True if allowed, False if not allowed
1038 """
Simon Glassf4590e02022-01-09 20:13:46 -07001039 self.allow_fake = allow_fake
Heiko Thierya89c8f22022-01-06 11:49:41 +01001040
Simon Glassb1cca952020-07-09 18:39:40 -06001041 def CheckMissing(self, missing_list):
1042 """Check if any entries in this section have missing external blobs
1043
1044 If there are missing blobs, the entries are added to the list
1045
1046 Args:
1047 missing_list: List of Entry objects to be added to
1048 """
1049 if self.missing:
1050 missing_list.append(self)
Simon Glass87958982020-09-01 05:13:57 -06001051
Simon Glass3817ad42022-03-05 20:19:04 -07001052 def check_fake_fname(self, fname, size=0):
Simon Glass790ba9f2022-01-12 13:10:36 -07001053 """If the file is missing and the entry allows fake blobs, fake it
1054
1055 Sets self.faked to True if faked
1056
1057 Args:
1058 fname (str): Filename to check
Simon Glass3817ad42022-03-05 20:19:04 -07001059 size (int): Size of fake file to create
Simon Glass790ba9f2022-01-12 13:10:36 -07001060
1061 Returns:
Simon Glass9a0a2e92022-03-05 20:19:03 -07001062 tuple:
1063 fname (str): Filename of faked file
1064 bool: True if the blob was faked, False if not
Simon Glass790ba9f2022-01-12 13:10:36 -07001065 """
1066 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass7960a0a2022-08-07 09:46:46 -06001067 if not self.fake_fname:
1068 outfname = os.path.join(self.fake_dir, os.path.basename(fname))
1069 with open(outfname, "wb") as out:
1070 out.truncate(size)
1071 tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
1072 self.fake_fname = outfname
Simon Glass790ba9f2022-01-12 13:10:36 -07001073 self.faked = True
Simon Glass7960a0a2022-08-07 09:46:46 -06001074 return self.fake_fname, True
Simon Glass9a0a2e92022-03-05 20:19:03 -07001075 return fname, False
Simon Glass790ba9f2022-01-12 13:10:36 -07001076
Heiko Thierya89c8f22022-01-06 11:49:41 +01001077 def CheckFakedBlobs(self, faked_blobs_list):
1078 """Check if any entries in this section have faked external blobs
1079
1080 If there are faked blobs, the entries are added to the list
1081
1082 Args:
1083 fake_blobs_list: List of Entry objects to be added to
1084 """
1085 # This is meaningless for anything other than blobs
1086 pass
1087
Simon Glass87958982020-09-01 05:13:57 -06001088 def GetAllowMissing(self):
1089 """Get whether a section allows missing external blobs
1090
1091 Returns:
1092 True if allowed, False if not allowed
1093 """
1094 return self.allow_missing
Simon Glassb2381432020-09-06 10:39:09 -06001095
Simon Glass4f9ee832022-01-09 20:14:09 -07001096 def record_missing_bintool(self, bintool):
1097 """Record a missing bintool that was needed to produce this entry
1098
1099 Args:
1100 bintool (Bintool): Bintool that was missing
1101 """
Stefan Herbrechtsmeierfacc3782022-08-19 16:25:19 +02001102 if bintool not in self.missing_bintools:
1103 self.missing_bintools.append(bintool)
Simon Glass4f9ee832022-01-09 20:14:09 -07001104
1105 def check_missing_bintools(self, missing_list):
1106 """Check if any entries in this section have missing bintools
1107
1108 If there are missing bintools, these are added to the list
1109
1110 Args:
1111 missing_list: List of Bintool objects to be added to
1112 """
Stefan Herbrechtsmeierfacc3782022-08-19 16:25:19 +02001113 for bintool in self.missing_bintools:
1114 if bintool not in missing_list:
1115 missing_list.append(bintool)
1116
Simon Glass4f9ee832022-01-09 20:14:09 -07001117
Simon Glassb2381432020-09-06 10:39:09 -06001118 def GetHelpTags(self):
1119 """Get the tags use for missing-blob help
1120
1121 Returns:
1122 list of possible tags, most desirable first
1123 """
1124 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glass87c96292020-10-26 17:40:06 -06001125
1126 def CompressData(self, indata):
1127 """Compress data according to the entry's compression method
1128
1129 Args:
1130 indata: Data to compress
1131
1132 Returns:
Stefan Herbrechtsmeier4f463e32022-08-19 16:25:27 +02001133 Compressed data
Simon Glass87c96292020-10-26 17:40:06 -06001134 """
Simon Glass97c3e9a2020-10-26 17:40:15 -06001135 self.uncomp_data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001136 if self.compress != 'none':
1137 self.uncomp_size = len(indata)
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02001138 if self.comp_bintool.is_present():
1139 data = self.comp_bintool.compress(indata)
1140 else:
1141 self.record_missing_bintool(self.comp_bintool)
1142 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001143 else:
1144 data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001145 return data
Simon Glassb35fb172021-03-18 20:25:04 +13001146
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001147 def DecompressData(self, indata):
1148 """Decompress data according to the entry's compression method
1149
1150 Args:
1151 indata: Data to decompress
1152
1153 Returns:
1154 Decompressed data
1155 """
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001156 if self.compress != 'none':
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02001157 if self.comp_bintool.is_present():
1158 data = self.comp_bintool.decompress(indata)
1159 self.uncomp_size = len(data)
1160 else:
1161 self.record_missing_bintool(self.comp_bintool)
1162 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001163 else:
1164 data = indata
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001165 self.uncomp_data = data
1166 return data
1167
Simon Glassb35fb172021-03-18 20:25:04 +13001168 @classmethod
1169 def UseExpanded(cls, node, etype, new_etype):
1170 """Check whether to use an expanded entry type
1171
1172 This is called by Entry.Create() when it finds an expanded version of
1173 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1174 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1175 ignored.
1176
1177 Args:
1178 node: Node object containing information about the entry to
1179 create
1180 etype: Original entry type being used
1181 new_etype: New entry type proposed
1182
1183 Returns:
1184 True to use this entry type, False to use the original one
1185 """
Simon Glassf3385a52022-01-29 14:14:15 -07001186 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glassb35fb172021-03-18 20:25:04 +13001187 (node.path, etype, new_etype))
1188 return True
Simon Glass943bf782021-11-23 21:09:50 -07001189
1190 def CheckAltFormats(self, alt_formats):
1191 """Add any alternative formats supported by this entry type
1192
1193 Args:
1194 alt_formats (dict): Dict to add alt_formats to:
1195 key: Name of alt format
1196 value: Help text
1197 """
1198 pass
Simon Glass386c63c2022-01-09 20:13:50 -07001199
Simon Glassae9a4572022-03-05 20:19:02 -07001200 def AddBintools(self, btools):
Simon Glass386c63c2022-01-09 20:13:50 -07001201 """Add the bintools used by this entry type
1202
1203 Args:
Simon Glassae9a4572022-03-05 20:19:02 -07001204 btools (dict of Bintool):
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001205
1206 Raise:
1207 ValueError if compression algorithm is not supported
Simon Glass386c63c2022-01-09 20:13:50 -07001208 """
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001209 algo = self.compress
1210 if algo != 'none':
Stefan Herbrechtsmeiercd15b642022-08-19 16:25:38 +02001211 algos = ['bzip2', 'gzip', 'lz4', 'lzma', 'lzo', 'xz', 'zstd']
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001212 if algo not in algos:
1213 raise ValueError("Unknown algorithm '%s'" % algo)
Stefan Herbrechtsmeier7b26a462022-08-19 16:25:36 +02001214 names = {'lzma': 'lzma_alone', 'lzo': 'lzop'}
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001215 name = names.get(self.compress, self.compress)
1216 self.comp_bintool = self.AddBintool(btools, name)
Simon Glass386c63c2022-01-09 20:13:50 -07001217
1218 @classmethod
1219 def AddBintool(self, tools, name):
1220 """Add a new bintool to the tools used by this etype
1221
1222 Args:
1223 name: Name of the tool
1224 """
1225 btool = bintool.Bintool.create(name)
1226 tools[name] = btool
1227 return btool
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03001228
1229 def SetUpdateHash(self, update_hash):
1230 """Set whether this entry's "hash" subnode should be updated
1231
1232 Args:
1233 update_hash: True if hash should be updated, False if not
1234 """
1235 self.update_hash = update_hash
Simon Glass81b71c32022-02-08 11:50:00 -07001236
Simon Glass72e423c2022-03-05 20:19:05 -07001237 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass81b71c32022-02-08 11:50:00 -07001238 """Put the contents of a list of entries into a file
1239
1240 Args:
1241 entries (list of Entry): Entries to collect
1242 prefix (str): Filename prefix of file to write to
Simon Glass72e423c2022-03-05 20:19:05 -07001243 fake_size (int): Size of fake file to create if needed
Simon Glass81b71c32022-02-08 11:50:00 -07001244
1245 If any entry does not have contents yet, this function returns False
1246 for the data.
1247
1248 Returns:
1249 Tuple:
Simon Glass6d427c42022-03-05 20:18:58 -07001250 bytes: Concatenated data from all the entries (or None)
1251 str: Filename of file written (or None if no data)
1252 str: Unique portion of filename (or None if no data)
Simon Glass81b71c32022-02-08 11:50:00 -07001253 """
1254 data = b''
1255 for entry in entries:
1256 # First get the input data and put it in a file. If not available,
1257 # try later.
Simon Glass72e423c2022-03-05 20:19:05 -07001258 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass6d427c42022-03-05 20:18:58 -07001259 return None, None, None
Simon Glass81b71c32022-02-08 11:50:00 -07001260 data += entry.GetData()
1261 uniq = self.GetUniqueName()
1262 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1263 tools.write_file(fname, data)
1264 return data, fname, uniq
Simon Glass7960a0a2022-08-07 09:46:46 -06001265
1266 @classmethod
1267 def create_fake_dir(cls):
1268 """Create the directory for fake files"""
1269 cls.fake_dir = tools.get_output_filename('binman-fake')
1270 if not os.path.exists(cls.fake_dir):
1271 os.mkdir(cls.fake_dir)
1272 tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")
Simon Glasscdadada2022-08-13 11:40:44 -06001273
1274 def ensure_props(self):
1275 """Raise an exception if properties are missing
1276
1277 Args:
1278 prop_list (list of str): List of properties to check for
1279
1280 Raises:
1281 ValueError: Any property is missing
1282 """
1283 not_present = []
1284 for prop in self.required_props:
1285 if not prop in self._node.props:
1286 not_present.append(prop)
1287 if not_present:
1288 self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}")
Simon Glassc8c9f312023-01-07 14:07:12 -07001289
1290 def mark_absent(self, msg):
1291 tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg))
1292 self.absent = True
Simon Glass2f80c5e2023-01-07 14:07:14 -07001293
1294 def read_elf_segments(self):
1295 """Read segments from an entry that can generate an ELF file
1296
1297 Returns:
1298 tuple:
1299 list of segments, each:
1300 int: Segment number (0 = first)
1301 int: Start address of segment in memory
1302 bytes: Contents of segment
1303 int: entry address of ELF file
1304 """
1305 return None