blob: ea2190b23a39e598f76c1232fe90f4aed3de7250 [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 Glass16287932020-04-17 18:09:03 -060015from dtoc import fdt_util
Simon Glassbf776672020-04-17 18:09:04 -060016from patman import tools
Simon Glassc1aa66e2022-01-29 14:14:04 -070017from patman.tools import to_hex, to_hex_size
Simon Glassbf776672020-04-17 18:09:04 -060018from patman import tout
Simon Glassbf7fd502016-11-25 20:15:51 -070019
20modules = {}
21
Simon Glass8d2ef3e2022-02-11 13:23:21 -070022# This is imported if needed
23state = None
Simon Glass53af22a2018-07-17 13:25:32 -060024
25# An argument which can be passed to entries on the command line, in lieu of
26# device-tree properties.
27EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
28
Simon Glass41b8ba02019-07-08 14:25:43 -060029# Information about an entry for use when displaying summaries
30EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
31 'image_pos', 'uncomp_size', 'offset',
32 'entry'])
Simon Glass53af22a2018-07-17 13:25:32 -060033
Simon Glassbf7fd502016-11-25 20:15:51 -070034class Entry(object):
Simon Glass25ac0e62018-06-01 09:38:14 -060035 """An Entry in the section
Simon Glassbf7fd502016-11-25 20:15:51 -070036
37 An entry corresponds to a single node in the device-tree description
Simon Glass25ac0e62018-06-01 09:38:14 -060038 of the section. Each entry ends up being a part of the final section.
Simon Glassbf7fd502016-11-25 20:15:51 -070039 Entries can be placed either right next to each other, or with padding
40 between them. The type of the entry determines the data that is in it.
41
42 This class is not used by itself. All entry objects are subclasses of
43 Entry.
44
45 Attributes:
Simon Glass8122f392018-07-17 13:25:28 -060046 section: Section object containing this entry
Simon Glassbf7fd502016-11-25 20:15:51 -070047 node: The node that created this entry
Simon Glass3ab95982018-08-01 15:22:37 -060048 offset: Offset of entry within the section, None if not known yet (in
49 which case it will be calculated by Pack())
Simon Glassbf7fd502016-11-25 20:15:51 -070050 size: Entry size in bytes, None if not known
Simon Glass9a5d3dc2019-10-31 07:43:02 -060051 pre_reset_size: size as it was before ResetForPack(). This allows us to
52 keep track of the size we started with and detect size changes
Simon Glass8287ee82019-07-08 14:25:30 -060053 uncomp_size: Size of uncompressed data in bytes, if the entry is
54 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070055 contents_size: Size of contents in bytes, 0 by default
Simon Glass4eec34c2020-10-26 17:40:10 -060056 align: Entry start offset alignment relative to the start of the
57 containing section, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070058 align_size: Entry size alignment, or None
Simon Glass4eec34c2020-10-26 17:40:10 -060059 align_end: Entry end offset alignment relative to the start of the
60 containing section, or None
Simon Glassf90d9062020-10-26 17:40:09 -060061 pad_before: Number of pad bytes before the contents when it is placed
62 in the containing section, 0 if none. The pad bytes become part of
63 the entry.
64 pad_after: Number of pad bytes after the contents when it is placed in
65 the containing section, 0 if none. The pad bytes become part of
66 the entry.
67 data: Contents of entry (string of bytes). This does not include
Simon Glass97c3e9a2020-10-26 17:40:15 -060068 padding created by pad_before or pad_after. If the entry is
69 compressed, this contains the compressed data.
70 uncomp_data: Original uncompressed data, if this entry is compressed,
71 else None
Simon Glass8287ee82019-07-08 14:25:30 -060072 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassc52c9e72019-07-08 14:25:37 -060073 orig_offset: Original offset value read from node
74 orig_size: Original size value read from node
Simon Glass87958982020-09-01 05:13:57 -060075 missing: True if this entry is missing its contents
76 allow_missing: Allow children of this entry to be missing (used by
77 subclasses such as Entry_section)
Heiko Thierya89c8f22022-01-06 11:49:41 +010078 allow_fake: Allow creating a dummy fake file if the blob file is not
79 available. This is mainly used for testing.
Simon Glass87958982020-09-01 05:13:57 -060080 external: True if this entry contains an external binary blob
Simon Glass386c63c2022-01-09 20:13:50 -070081 bintools: Bintools used by this entry (only populated for Image)
Simon Glass4f9ee832022-01-09 20:14:09 -070082 missing_bintools: List of missing bintools for this entry
Alper Nebi Yasakee813c82022-02-09 22:02:35 +030083 update_hash: True if this entry's "hash" subnode should be
84 updated with a hash of the entry contents
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +020085 comp_bintool: Bintools used for compress and decompress data
Simon Glass7960a0a2022-08-07 09:46:46 -060086 fake_fname: Fake filename, if one was created, else None
Simon Glasscdadada2022-08-13 11:40:44 -060087 required_props (dict of str): Properties which must be present. This can
88 be added to by subclasses
Simon Glassbf7fd502016-11-25 20:15:51 -070089 """
Simon Glass7960a0a2022-08-07 09:46:46 -060090 fake_dir = None
91
Simon Glassc6bd6e22019-07-20 12:23:45 -060092 def __init__(self, section, etype, node, name_prefix=''):
Simon Glass8dbb7442019-08-24 07:22:44 -060093 # Put this here to allow entry-docs and help to work without libfdt
94 global state
Simon Glass16287932020-04-17 18:09:03 -060095 from binman import state
Simon Glass8dbb7442019-08-24 07:22:44 -060096
Simon Glass25ac0e62018-06-01 09:38:14 -060097 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -070098 self.etype = etype
99 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -0600100 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -0600101 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700102 self.size = None
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600103 self.pre_reset_size = None
Simon Glass8287ee82019-07-08 14:25:30 -0600104 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -0600105 self.data = None
Simon Glass97c3e9a2020-10-26 17:40:15 -0600106 self.uncomp_data = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700107 self.contents_size = 0
108 self.align = None
109 self.align_size = None
110 self.align_end = None
111 self.pad_before = 0
112 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -0600113 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -0600114 self.image_pos = None
Simon Glass80a66ae2022-03-05 20:18:59 -0700115 self.extend_size = False
Simon Glass8287ee82019-07-08 14:25:30 -0600116 self.compress = 'none'
Simon Glassb1cca952020-07-09 18:39:40 -0600117 self.missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100118 self.faked = False
Simon Glass87958982020-09-01 05:13:57 -0600119 self.external = False
120 self.allow_missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100121 self.allow_fake = False
Simon Glass386c63c2022-01-09 20:13:50 -0700122 self.bintools = {}
Simon Glass4f9ee832022-01-09 20:14:09 -0700123 self.missing_bintools = []
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300124 self.update_hash = True
Simon Glass7960a0a2022-08-07 09:46:46 -0600125 self.fake_fname = None
Simon Glasscdadada2022-08-13 11:40:44 -0600126 self.required_props = []
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +0200127 self.comp_bintool = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700128
129 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700130 def FindEntryClass(etype, expanded):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600131 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -0700132
133 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -0600134 node_node: Path name of Node object containing information about
135 the entry to create (used for errors)
136 etype: Entry type to use
Simon Glassb35fb172021-03-18 20:25:04 +1300137 expanded: Use the expanded version of etype
Simon Glassbf7fd502016-11-25 20:15:51 -0700138
139 Returns:
Simon Glassb35fb172021-03-18 20:25:04 +1300140 The entry class object if found, else None if not found and expanded
Simon Glass858436d2021-11-23 21:09:49 -0700141 is True, else a tuple:
142 module name that could not be found
143 exception received
Simon Glassbf7fd502016-11-25 20:15:51 -0700144 """
Simon Glassdd57c132018-06-01 09:38:11 -0600145 # Convert something like 'u-boot@0' to 'u_boot' since we are only
146 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700147 module_name = etype.replace('-', '_')
Simon Glassb35fb172021-03-18 20:25:04 +1300148
Simon Glassdd57c132018-06-01 09:38:11 -0600149 if '@' in module_name:
150 module_name = module_name.split('@')[0]
Simon Glassb35fb172021-03-18 20:25:04 +1300151 if expanded:
152 module_name += '_expanded'
Simon Glassbf7fd502016-11-25 20:15:51 -0700153 module = modules.get(module_name)
154
Simon Glassbadf0ec2018-06-01 09:38:15 -0600155 # Also allow entry-type modules to be brought in from the etype directory.
156
Simon Glassbf7fd502016-11-25 20:15:51 -0700157 # Import the module if we have not already done so.
158 if not module:
159 try:
Simon Glass16287932020-04-17 18:09:03 -0600160 module = importlib.import_module('binman.etype.' + module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600161 except ImportError as e:
Simon Glassb35fb172021-03-18 20:25:04 +1300162 if expanded:
163 return None
Simon Glass858436d2021-11-23 21:09:49 -0700164 return module_name, e
Simon Glassbf7fd502016-11-25 20:15:51 -0700165 modules[module_name] = module
166
Simon Glassfd8d1f72018-07-17 13:25:36 -0600167 # Look up the expected class name
168 return getattr(module, 'Entry_%s' % module_name)
169
170 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700171 def Lookup(node_path, etype, expanded, missing_etype=False):
172 """Look up the entry class for a node.
173
174 Args:
175 node_node (str): Path name of Node object containing information
176 about the entry to create (used for errors)
177 etype (str): Entry type to use
178 expanded (bool): Use the expanded version of etype
179 missing_etype (bool): True to default to a blob etype if the
180 requested etype is not found
181
182 Returns:
183 The entry class object if found, else None if not found and expanded
184 is True
185
186 Raise:
187 ValueError if expanded is False and the class is not found
188 """
189 # Convert something like 'u-boot@0' to 'u_boot' since we are only
190 # interested in the type.
191 cls = Entry.FindEntryClass(etype, expanded)
192 if cls is None:
193 return None
194 elif isinstance(cls, tuple):
195 if missing_etype:
196 cls = Entry.FindEntryClass('blob', False)
197 if isinstance(cls, tuple): # This should not fail
198 module_name, e = cls
199 raise ValueError(
200 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
201 (etype, node_path, module_name, e))
202 return cls
203
204 @staticmethod
205 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600206 """Create a new entry for a node.
207
208 Args:
Simon Glass858436d2021-11-23 21:09:49 -0700209 section (entry_Section): Section object containing this node
210 node (Node): Node object containing information about the entry to
211 create
212 etype (str): Entry type to use, or None to work it out (used for
213 tests)
214 expanded (bool): Use the expanded version of etype
215 missing_etype (bool): True to default to a blob etype if the
216 requested etype is not found
Simon Glassfd8d1f72018-07-17 13:25:36 -0600217
218 Returns:
219 A new Entry object of the correct type (a subclass of Entry)
220 """
221 if not etype:
222 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glass858436d2021-11-23 21:09:49 -0700223 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glassb35fb172021-03-18 20:25:04 +1300224 if obj and expanded:
225 # Check whether to use the expanded entry
226 new_etype = etype + '-expanded'
Simon Glass3d433382021-03-21 18:24:30 +1300227 can_expand = not fdt_util.GetBool(node, 'no-expanded')
228 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glassb35fb172021-03-18 20:25:04 +1300229 etype = new_etype
230 else:
231 obj = None
232 if not obj:
Simon Glass858436d2021-11-23 21:09:49 -0700233 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600234
Simon Glassbf7fd502016-11-25 20:15:51 -0700235 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600236 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700237
238 def ReadNode(self):
239 """Read entry information from the node
240
Simon Glassc6bd6e22019-07-20 12:23:45 -0600241 This must be called as the first thing after the Entry is created.
242
Simon Glassbf7fd502016-11-25 20:15:51 -0700243 This reads all the fields we recognise from the node, ready for use.
244 """
Simon Glasscdadada2022-08-13 11:40:44 -0600245 self.ensure_props()
Simon Glass15a587c2018-07-17 13:25:51 -0600246 if 'pos' in self._node.props:
247 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass80a66ae2022-03-05 20:18:59 -0700248 if 'expand-size' in self._node.props:
249 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glass3ab95982018-08-01 15:22:37 -0600250 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700251 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glass12bb1a92019-07-20 12:23:51 -0600252 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
253 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
254 if self.GetImage().copy_to_orig:
255 self.orig_offset = self.offset
256 self.orig_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600257
Simon Glassffded752019-07-08 14:25:46 -0600258 # These should not be set in input files, but are set in an FDT map,
259 # which is also read by this code.
260 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
261 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
262
Simon Glassbf7fd502016-11-25 20:15:51 -0700263 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700264 if tools.not_power_of_two(self.align):
Simon Glassbf7fd502016-11-25 20:15:51 -0700265 raise ValueError("Node '%s': Alignment %s must be a power of two" %
266 (self._node.path, self.align))
Simon Glass5ff9fed2021-03-21 18:24:33 +1300267 if self.section and self.align is None:
268 self.align = self.section.align_default
Simon Glassbf7fd502016-11-25 20:15:51 -0700269 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
270 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
271 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700272 if tools.not_power_of_two(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600273 self.Raise("Alignment size %s must be a power of two" %
274 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700275 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600276 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glass80a66ae2022-03-05 20:18:59 -0700277 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassb2381432020-09-06 10:39:09 -0600278 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glassbf7fd502016-11-25 20:15:51 -0700279
Simon Glass87c96292020-10-26 17:40:06 -0600280 # This is only supported by blobs and sections at present
281 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
282
Simon Glass6c234bf2018-09-14 04:57:18 -0600283 def GetDefaultFilename(self):
284 return None
285
Simon Glassa8adb6d2019-07-20 12:23:28 -0600286 def GetFdts(self):
287 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600288
289 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600290 Empty dict, if this entry is not a .dtb, otherwise:
291 Dict:
292 key: Filename from this entry (without the path)
Simon Glass4bdd3002019-07-20 12:23:31 -0600293 value: Tuple:
Simon Glassadb67bb2021-03-18 20:25:02 +1300294 Entry object for this dtb
Simon Glass4bdd3002019-07-20 12:23:31 -0600295 Filename of file containing this dtb
Simon Glass539aece2018-09-14 04:57:22 -0600296 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600297 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600298
Simon Glassc9ee33a2022-03-05 20:19:00 -0700299 def gen_entries(self):
300 """Allow entries to generate other entries
Simon Glassa01d1a22021-03-18 20:24:52 +1300301
302 Some entries generate subnodes automatically, from which sub-entries
303 are then created. This method allows those to be added to the binman
304 definition for the current image. An entry which implements this method
305 should call state.AddSubnode() to add a subnode and can add properties
306 with state.AddString(), etc.
307
308 An example is 'files', which produces a section containing a list of
309 files.
310 """
Simon Glass0a98b282018-09-14 04:57:28 -0600311 pass
312
Simon Glassa9fad072020-10-26 17:40:17 -0600313 def AddMissingProperties(self, have_image_pos):
314 """Add new properties to the device tree as needed for this entry
315
316 Args:
317 have_image_pos: True if this entry has an image position. This can
318 be False if its parent section is compressed, since compression
319 groups all entries together into a compressed block of data,
320 obscuring the start of each individual child entry
321 """
322 for prop in ['offset', 'size']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600323 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600324 state.AddZeroProp(self._node, prop)
Simon Glassa9fad072020-10-26 17:40:17 -0600325 if have_image_pos and 'image-pos' not in self._node.props:
326 state.AddZeroProp(self._node, 'image-pos')
Simon Glass12bb1a92019-07-20 12:23:51 -0600327 if self.GetImage().allow_repack:
328 if self.orig_offset is not None:
329 state.AddZeroProp(self._node, 'orig-offset', True)
330 if self.orig_size is not None:
331 state.AddZeroProp(self._node, 'orig-size', True)
332
Simon Glass8287ee82019-07-08 14:25:30 -0600333 if self.compress != 'none':
334 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300335
336 if self.update_hash:
337 err = state.CheckAddHashProp(self._node)
338 if err:
339 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600340
341 def SetCalculatedProperties(self):
342 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600343 state.SetInt(self._node, 'offset', self.offset)
344 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600345 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassa9fad072020-10-26 17:40:17 -0600346 if self.image_pos is not None:
Simon Glass08594d42020-11-02 12:55:44 -0700347 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass12bb1a92019-07-20 12:23:51 -0600348 if self.GetImage().allow_repack:
349 if self.orig_offset is not None:
350 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
351 if self.orig_size is not None:
352 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glass8287ee82019-07-08 14:25:30 -0600353 if self.uncomp_size is not None:
354 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300355
356 if self.update_hash:
357 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600358
Simon Glassecab8972018-07-06 10:27:40 -0600359 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600360 """Allow entries to adjust the device tree
361
362 Some entries need to adjust the device tree for their purposes. This
363 may involve adding or deleting properties.
364
365 Returns:
366 True if processing is complete
367 False if processing could not be completed due to a dependency.
368 This will cause the entry to be retried after others have been
369 called
370 """
Simon Glassecab8972018-07-06 10:27:40 -0600371 return True
372
Simon Glassc8d48ef2018-06-01 09:38:21 -0600373 def SetPrefix(self, prefix):
374 """Set the name prefix for a node
375
376 Args:
377 prefix: Prefix to set, or '' to not use a prefix
378 """
379 if prefix:
380 self.name = prefix + self.name
381
Simon Glass5c890232018-07-06 10:27:19 -0600382 def SetContents(self, data):
383 """Set the contents of an entry
384
385 This sets both the data and content_size properties
386
387 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600388 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600389 """
390 self.data = data
391 self.contents_size = len(self.data)
392
393 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600394 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600395
Simon Glassa0dcaf22019-07-08 14:25:35 -0600396 This checks that the new data is the same size as the old. If the size
397 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600398
399 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600400 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600401
402 Raises:
403 ValueError if the new data size is not the same as the old
404 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600405 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600406 new_size = len(data)
Simon Glass61ec04f2019-07-20 12:23:58 -0600407 if state.AllowEntryExpansion() and new_size > self.contents_size:
408 # self.data will indicate the new size needed
409 size_ok = False
410 elif state.AllowEntryContraction() and new_size < self.contents_size:
411 size_ok = False
412
413 # If not allowed to change, try to deal with it or give up
414 if size_ok:
Simon Glassc52c9e72019-07-08 14:25:37 -0600415 if new_size > self.contents_size:
Simon Glass61ec04f2019-07-20 12:23:58 -0600416 self.Raise('Cannot update entry size from %d to %d' %
417 (self.contents_size, new_size))
418
419 # Don't let the data shrink. Pad it if necessary
420 if size_ok and new_size < self.contents_size:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700421 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass61ec04f2019-07-20 12:23:58 -0600422
423 if not size_ok:
Simon Glassf3385a52022-01-29 14:14:15 -0700424 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glassc1aa66e2022-01-29 14:14:04 -0700425 self._node.path, to_hex(self.contents_size),
426 to_hex(new_size)))
Simon Glass5c890232018-07-06 10:27:19 -0600427 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600428 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600429
Simon Glass72e423c2022-03-05 20:19:05 -0700430 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glassbf7fd502016-11-25 20:15:51 -0700431 """Figure out the contents of an entry.
432
Simon Glass72e423c2022-03-05 20:19:05 -0700433 Args:
434 skip_entry (Entry): Entry to skip when obtaining section contents
435 fake_size (int): Size of fake file to create if needed
436
Simon Glassbf7fd502016-11-25 20:15:51 -0700437 Returns:
438 True if the contents were found, False if another call is needed
439 after the other entries are processed.
440 """
441 # No contents by default: subclasses can implement this
442 return True
443
Simon Glassc52c9e72019-07-08 14:25:37 -0600444 def ResetForPack(self):
445 """Reset offset/size fields so that packing can be done again"""
Simon Glass9f297b02019-07-20 12:23:36 -0600446 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700447 (to_hex(self.offset), to_hex(self.orig_offset),
448 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600449 self.pre_reset_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600450 self.offset = self.orig_offset
451 self.size = self.orig_size
452
Simon Glass3ab95982018-08-01 15:22:37 -0600453 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600454 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700455
456 Most of the time the entries are not fully specified. There may be
457 an alignment but no size. In that case we take the size from the
458 contents of the entry.
459
Simon Glass3ab95982018-08-01 15:22:37 -0600460 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700461
Simon Glass3ab95982018-08-01 15:22:37 -0600462 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700463 entry will be know.
464
465 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600466 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700467
468 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600469 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700470 """
Simon Glass9f297b02019-07-20 12:23:36 -0600471 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700472 (to_hex(self.offset), to_hex(self.size),
Simon Glass9f297b02019-07-20 12:23:36 -0600473 self.contents_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600474 if self.offset is None:
475 if self.offset_unset:
476 self.Raise('No offset set with offset-unset: should another '
477 'entry provide this correct offset?')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700478 self.offset = tools.align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700479 needed = self.pad_before + self.contents_size + self.pad_after
Simon Glassc1aa66e2022-01-29 14:14:04 -0700480 needed = tools.align(needed, self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700481 size = self.size
482 if not size:
483 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600484 new_offset = self.offset + size
Simon Glassc1aa66e2022-01-29 14:14:04 -0700485 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glass3ab95982018-08-01 15:22:37 -0600486 if aligned_offset != new_offset:
487 size = aligned_offset - self.offset
488 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700489
490 if not self.size:
491 self.size = size
492
493 if self.size < needed:
494 self.Raise("Entry contents size is %#x (%d) but entry size is "
495 "%#x (%d)" % (needed, needed, self.size, self.size))
496 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600497 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700498 # conflict with the provided alignment values
Simon Glassc1aa66e2022-01-29 14:14:04 -0700499 if self.size != tools.align(self.size, self.align_size):
Simon Glassbf7fd502016-11-25 20:15:51 -0700500 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
501 (self.size, self.size, self.align_size, self.align_size))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700502 if self.offset != tools.align(self.offset, self.align):
Simon Glass3ab95982018-08-01 15:22:37 -0600503 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
504 (self.offset, self.offset, self.align, self.align))
Simon Glass9f297b02019-07-20 12:23:36 -0600505 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
506 (self.offset, self.size, self.contents_size, new_offset))
Simon Glassbf7fd502016-11-25 20:15:51 -0700507
Simon Glass3ab95982018-08-01 15:22:37 -0600508 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700509
510 def Raise(self, msg):
511 """Convenience function to raise an error referencing a node"""
512 raise ValueError("Node '%s': %s" % (self._node.path, msg))
513
Simon Glass189f2912021-03-21 18:24:31 +1300514 def Info(self, msg):
515 """Convenience function to log info referencing a node"""
516 tag = "Info '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700517 tout.detail('%30s: %s' % (tag, msg))
Simon Glass189f2912021-03-21 18:24:31 +1300518
Simon Glass9f297b02019-07-20 12:23:36 -0600519 def Detail(self, msg):
520 """Convenience function to log detail referencing a node"""
521 tag = "Node '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700522 tout.detail('%30s: %s' % (tag, msg))
Simon Glass9f297b02019-07-20 12:23:36 -0600523
Simon Glass53af22a2018-07-17 13:25:32 -0600524 def GetEntryArgsOrProps(self, props, required=False):
525 """Return the values of a set of properties
526
527 Args:
528 props: List of EntryArg objects
529
530 Raises:
531 ValueError if a property is not found
532 """
533 values = []
534 missing = []
535 for prop in props:
536 python_prop = prop.name.replace('-', '_')
537 if hasattr(self, python_prop):
538 value = getattr(self, python_prop)
539 else:
540 value = None
541 if value is None:
542 value = self.GetArg(prop.name, prop.datatype)
543 if value is None and required:
544 missing.append(prop.name)
545 values.append(value)
546 if missing:
Simon Glass939d1062021-01-06 21:35:16 -0700547 self.GetImage().MissingArgs(self, missing)
Simon Glass53af22a2018-07-17 13:25:32 -0600548 return values
549
Simon Glassbf7fd502016-11-25 20:15:51 -0700550 def GetPath(self):
551 """Get the path of a node
552
553 Returns:
554 Full path of the node for this entry
555 """
556 return self._node.path
557
Simon Glass631f7522021-03-21 18:24:32 +1300558 def GetData(self, required=True):
Simon Glass63e7ba62020-10-26 17:40:16 -0600559 """Get the contents of an entry
560
Simon Glass631f7522021-03-21 18:24:32 +1300561 Args:
562 required: True if the data must be present, False if it is OK to
563 return None
564
Simon Glass63e7ba62020-10-26 17:40:16 -0600565 Returns:
566 bytes content of the entry, excluding any padding. If the entry is
567 compressed, the compressed data is returned
568 """
Simon Glassc1aa66e2022-01-29 14:14:04 -0700569 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glassbf7fd502016-11-25 20:15:51 -0700570 return self.data
571
Simon Glass271a0832020-11-02 12:55:43 -0700572 def GetPaddedData(self, data=None):
573 """Get the data for an entry including any padding
574
575 Gets the entry data and uses its section's pad-byte value to add padding
576 before and after as defined by the pad-before and pad-after properties.
577
578 This does not consider alignment.
579
580 Returns:
581 Contents of the entry along with any pad bytes before and
582 after it (bytes)
583 """
584 if data is None:
585 data = self.GetData()
586 return self.section.GetPaddedDataForEntry(self, data)
587
Simon Glass3ab95982018-08-01 15:22:37 -0600588 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600589 """Get the offsets for siblings
590
591 Some entry types can contain information about the position or size of
592 other entries. An example of this is the Intel Flash Descriptor, which
593 knows where the Intel Management Engine section should go.
594
595 If this entry knows about the position of other entries, it can specify
596 this by returning values here
597
598 Returns:
599 Dict:
600 key: Entry type
601 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600602 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600603 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700604 return {}
605
Simon Glasscf549042019-07-08 13:18:39 -0600606 def SetOffsetSize(self, offset, size):
607 """Set the offset and/or size of an entry
608
609 Args:
610 offset: New offset, or None to leave alone
611 size: New size, or None to leave alone
612 """
613 if offset is not None:
614 self.offset = offset
615 if size is not None:
616 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700617
Simon Glassdbf6be92018-08-01 15:22:42 -0600618 def SetImagePos(self, image_pos):
619 """Set the position in the image
620
621 Args:
622 image_pos: Position of this entry in the image
623 """
624 self.image_pos = image_pos + self.offset
625
Simon Glassbf7fd502016-11-25 20:15:51 -0700626 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600627 """Do any post-packing updates of entry contents
628
629 This function should call ProcessContentsUpdate() to update the entry
630 contents, if necessary, returning its return value here.
631
632 Args:
633 data: Data to set to the contents (bytes)
634
635 Returns:
636 True if the new data size is OK, False if expansion is needed
637
638 Raises:
639 ValueError if the new data size is not the same as the old and
640 state.AllowEntryExpansion() is False
641 """
642 return True
Simon Glass19790632017-11-13 18:55:01 -0700643
Simon Glassf55382b2018-06-01 09:38:13 -0600644 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700645 """Write symbol values into binary files for access at run time
646
647 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600648 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700649 """
650 pass
Simon Glass18546952018-06-01 09:38:16 -0600651
Simon Glass6ddd6112020-10-26 17:40:18 -0600652 def CheckEntries(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600653 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600654
Simon Glass3ab95982018-08-01 15:22:37 -0600655 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600656 than having to be fully inside their section). Sub-classes can implement
657 this function and raise if there is a problem.
658 """
659 pass
Simon Glass3b0c3822018-06-01 09:38:20 -0600660
Simon Glass8122f392018-07-17 13:25:28 -0600661 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600662 def GetStr(value):
663 if value is None:
664 return '<none> '
665 return '%08x' % value
666
667 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600668 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600669 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
670 Entry.GetStr(offset), Entry.GetStr(size),
671 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600672
Simon Glass3b0c3822018-06-01 09:38:20 -0600673 def WriteMap(self, fd, indent):
674 """Write a map of the entry to a .map file
675
676 Args:
677 fd: File to write the map to
678 indent: Curent indent level of map (0=none, 1=one level, etc.)
679 """
Simon Glass1be70d22018-07-17 13:25:49 -0600680 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
681 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600682
Simon Glassd626e822022-08-13 11:40:50 -0600683 # pylint: disable=assignment-from-none
Simon Glass11e36cc2018-07-17 13:25:38 -0600684 def GetEntries(self):
685 """Return a list of entries contained by this entry
686
687 Returns:
688 List of entries, or None if none. A normal entry has no entries
689 within it so will return None
690 """
691 return None
692
Simon Glassd626e822022-08-13 11:40:50 -0600693 def FindEntryByNode(self, find_node):
694 """Find a node in an entry, searching all subentries
695
696 This does a recursive search.
697
698 Args:
699 find_node (fdt.Node): Node to find
700
701 Returns:
702 Entry: entry, if found, else None
703 """
704 entries = self.GetEntries()
705 if entries:
706 for entry in entries.values():
707 if entry._node == find_node:
708 return entry
709 found = entry.FindEntryByNode(find_node)
710 if found:
711 return found
712
713 return None
714
Simon Glass53af22a2018-07-17 13:25:32 -0600715 def GetArg(self, name, datatype=str):
716 """Get the value of an entry argument or device-tree-node property
717
718 Some node properties can be provided as arguments to binman. First check
719 the entry arguments, and fall back to the device tree if not found
720
721 Args:
722 name: Argument name
723 datatype: Data type (str or int)
724
725 Returns:
726 Value of argument as a string or int, or None if no value
727
728 Raises:
729 ValueError if the argument cannot be converted to in
730 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600731 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600732 if value is not None:
733 if datatype == int:
734 try:
735 value = int(value)
736 except ValueError:
737 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
738 (name, value))
739 elif datatype == str:
740 pass
741 else:
742 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
743 datatype)
744 else:
745 value = fdt_util.GetDatatype(self._node, name, datatype)
746 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600747
748 @staticmethod
749 def WriteDocs(modules, test_missing=None):
750 """Write out documentation about the various entry types to stdout
751
752 Args:
753 modules: List of modules to include
754 test_missing: Used for testing. This is a module to report
755 as missing
756 """
757 print('''Binman Entry Documentation
758===========================
759
760This file describes the entry types supported by binman. These entry types can
761be placed in an image one by one to build up a final firmware image. It is
762fairly easy to create new entry types. Just add a new file to the 'etype'
763directory. You can use the existing entries as examples.
764
765Note that some entries are subclasses of others, using and extending their
766features to produce new behaviours.
767
768
769''')
770 modules = sorted(modules)
771
772 # Don't show the test entry
773 if '_testing' in modules:
774 modules.remove('_testing')
775 missing = []
776 for name in modules:
Simon Glassb35fb172021-03-18 20:25:04 +1300777 module = Entry.Lookup('WriteDocs', name, False)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600778 docs = getattr(module, '__doc__')
779 if test_missing == name:
780 docs = None
781 if docs:
782 lines = docs.splitlines()
783 first_line = lines[0]
784 rest = [line[4:] for line in lines[1:]]
785 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glass228c9b82022-08-07 16:33:25 -0600786
787 # Create a reference for use by rST docs
788 ref_name = f'etype_{module.__name__[6:]}'.lower()
789 print('.. _%s:' % ref_name)
790 print()
Simon Glassfd8d1f72018-07-17 13:25:36 -0600791 print(hdr)
792 print('-' * len(hdr))
793 print('\n'.join(rest))
794 print()
795 print()
796 else:
797 missing.append(name)
798
799 if missing:
800 raise ValueError('Documentation is missing for modules: %s' %
801 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600802
803 def GetUniqueName(self):
804 """Get a unique name for a node
805
806 Returns:
807 String containing a unique name for a node, consisting of the name
808 of all ancestors (starting from within the 'binman' node) separated
809 by a dot ('.'). This can be useful for generating unique filesnames
810 in the output directory.
811 """
812 name = self.name
813 node = self._node
814 while node.parent:
815 node = node.parent
Alper Nebi Yasak67bf2c82022-03-27 18:31:44 +0300816 if node.name in ('binman', '/'):
Simon Glassa326b492018-09-14 04:57:11 -0600817 break
818 name = '%s.%s' % (node.name, name)
819 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600820
Simon Glass80a66ae2022-03-05 20:18:59 -0700821 def extend_to_limit(self, limit):
822 """Extend an entry so that it ends at the given offset limit"""
Simon Glassba64a0b2018-09-14 04:57:29 -0600823 if self.offset + self.size < limit:
824 self.size = limit - self.offset
825 # Request the contents again, since changing the size requires that
826 # the data grows. This should not fail, but check it to be sure.
827 if not self.ObtainContents():
828 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600829
830 def HasSibling(self, name):
831 """Check if there is a sibling of a given name
832
833 Returns:
834 True if there is an entry with this name in the the same section,
835 else False
836 """
837 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600838
839 def GetSiblingImagePos(self, name):
840 """Return the image position of the given sibling
841
842 Returns:
843 Image position of sibling, or None if the sibling has no position,
844 or False if there is no such sibling
845 """
846 if not self.HasSibling(name):
847 return False
848 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600849
850 @staticmethod
851 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
852 uncomp_size, offset, entry):
853 """Add a new entry to the entries list
854
855 Args:
856 entries: List (of EntryInfo objects) to add to
857 indent: Current indent level to add to list
858 name: Entry name (string)
859 etype: Entry type (string)
860 size: Entry size in bytes (int)
861 image_pos: Position within image in bytes (int)
862 uncomp_size: Uncompressed size if the entry uses compression, else
863 None
864 offset: Entry offset within parent in bytes (int)
865 entry: Entry object
866 """
867 entries.append(EntryInfo(indent, name, etype, size, image_pos,
868 uncomp_size, offset, entry))
869
870 def ListEntries(self, entries, indent):
871 """Add files in this entry to the list of entries
872
873 This can be overridden by subclasses which need different behaviour.
874
875 Args:
876 entries: List (of EntryInfo objects) to add to
877 indent: Current indent level to add to list
878 """
879 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
880 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600881
Simon Glass943bf782021-11-23 21:09:50 -0700882 def ReadData(self, decomp=True, alt_format=None):
Simon Glassf667e452019-07-08 14:25:50 -0600883 """Read the data for an entry from the image
884
885 This is used when the image has been read in and we want to extract the
886 data for a particular entry from that image.
887
888 Args:
889 decomp: True to decompress any compressed data before returning it;
890 False to return the raw, uncompressed data
891
892 Returns:
893 Entry data (bytes)
894 """
895 # Use True here so that we get an uncompressed section to work from,
896 # although compressed sections are currently not supported
Simon Glassf3385a52022-01-29 14:14:15 -0700897 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass2d553c02019-09-25 08:56:21 -0600898 (self.section.GetPath(), self.GetPath()))
Simon Glass943bf782021-11-23 21:09:50 -0700899 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glassa9cd39e2019-07-20 12:24:04 -0600900 return data
Simon Glassd5079332019-07-20 12:23:41 -0600901
Simon Glass943bf782021-11-23 21:09:50 -0700902 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass2d553c02019-09-25 08:56:21 -0600903 """Read the data for a particular child entry
Simon Glass4e185e82019-09-25 08:56:20 -0600904
905 This reads data from the parent and extracts the piece that relates to
906 the given child.
907
908 Args:
Simon Glass943bf782021-11-23 21:09:50 -0700909 child (Entry): Child entry to read data for (must be valid)
910 decomp (bool): True to decompress any compressed data before
911 returning it; False to return the raw, uncompressed data
912 alt_format (str): Alternative format to read in, or None
Simon Glass4e185e82019-09-25 08:56:20 -0600913
914 Returns:
915 Data for the child (bytes)
916 """
917 pass
918
Simon Glassd5079332019-07-20 12:23:41 -0600919 def LoadData(self, decomp=True):
920 data = self.ReadData(decomp)
Simon Glass10f9d002019-07-20 12:23:50 -0600921 self.contents_size = len(data)
Simon Glassd5079332019-07-20 12:23:41 -0600922 self.ProcessContentsUpdate(data)
923 self.Detail('Loaded data size %x' % len(data))
Simon Glassc5ad04b2019-07-20 12:23:46 -0600924
Simon Glass943bf782021-11-23 21:09:50 -0700925 def GetAltFormat(self, data, alt_format):
926 """Read the data for an extry in an alternative format
927
928 Supported formats are list in the documentation for each entry. An
929 example is fdtmap which provides .
930
931 Args:
932 data (bytes): Data to convert (this should have been produced by the
933 entry)
934 alt_format (str): Format to use
935
936 """
937 pass
938
Simon Glassc5ad04b2019-07-20 12:23:46 -0600939 def GetImage(self):
940 """Get the image containing this entry
941
942 Returns:
943 Image object containing this entry
944 """
945 return self.section.GetImage()
Simon Glass10f9d002019-07-20 12:23:50 -0600946
947 def WriteData(self, data, decomp=True):
948 """Write the data to an entry in the image
949
950 This is used when the image has been read in and we want to replace the
951 data for a particular entry in that image.
952
953 The image must be re-packed and written out afterwards.
954
955 Args:
956 data: Data to replace it with
957 decomp: True to compress the data if needed, False if data is
958 already compressed so should be used as is
959
960 Returns:
961 True if the data did not result in a resize of this entry, False if
962 the entry must be resized
963 """
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600964 if self.size is not None:
965 self.contents_size = self.size
966 else:
967 self.contents_size = self.pre_reset_size
Simon Glass10f9d002019-07-20 12:23:50 -0600968 ok = self.ProcessContentsUpdate(data)
969 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glass7210c892019-07-20 12:24:05 -0600970 section_ok = self.section.WriteChildData(self)
971 return ok and section_ok
972
973 def WriteChildData(self, child):
974 """Handle writing the data in a child entry
975
976 This should be called on the child's parent section after the child's
Simon Glass557693e2021-11-23 11:03:44 -0700977 data has been updated. It should update any data structures needed to
978 validate that the update is successful.
Simon Glass7210c892019-07-20 12:24:05 -0600979
980 This base-class implementation does nothing, since the base Entry object
981 does not have any children.
982
983 Args:
984 child: Child Entry that was written
985
986 Returns:
987 True if the section could be updated successfully, False if the
Simon Glass557693e2021-11-23 11:03:44 -0700988 data is such that the section could not update
Simon Glass7210c892019-07-20 12:24:05 -0600989 """
990 return True
Simon Glasseba1f0c2019-07-20 12:23:55 -0600991
992 def GetSiblingOrder(self):
993 """Get the relative order of an entry amoung its siblings
994
995 Returns:
996 'start' if this entry is first among siblings, 'end' if last,
997 otherwise None
998 """
999 entries = list(self.section.GetEntries().values())
1000 if entries:
1001 if self == entries[0]:
1002 return 'start'
1003 elif self == entries[-1]:
1004 return 'end'
1005 return 'middle'
Simon Glass4f9f1052020-07-09 18:39:38 -06001006
1007 def SetAllowMissing(self, allow_missing):
1008 """Set whether a section allows missing external blobs
1009
1010 Args:
1011 allow_missing: True if allowed, False if not allowed
1012 """
1013 # This is meaningless for anything other than sections
1014 pass
Simon Glassb1cca952020-07-09 18:39:40 -06001015
Heiko Thierya89c8f22022-01-06 11:49:41 +01001016 def SetAllowFakeBlob(self, allow_fake):
1017 """Set whether a section allows to create a fake blob
1018
1019 Args:
1020 allow_fake: True if allowed, False if not allowed
1021 """
Simon Glassf4590e02022-01-09 20:13:46 -07001022 self.allow_fake = allow_fake
Heiko Thierya89c8f22022-01-06 11:49:41 +01001023
Simon Glassb1cca952020-07-09 18:39:40 -06001024 def CheckMissing(self, missing_list):
1025 """Check if any entries in this section have missing external blobs
1026
1027 If there are missing blobs, the entries are added to the list
1028
1029 Args:
1030 missing_list: List of Entry objects to be added to
1031 """
1032 if self.missing:
1033 missing_list.append(self)
Simon Glass87958982020-09-01 05:13:57 -06001034
Simon Glass3817ad42022-03-05 20:19:04 -07001035 def check_fake_fname(self, fname, size=0):
Simon Glass790ba9f2022-01-12 13:10:36 -07001036 """If the file is missing and the entry allows fake blobs, fake it
1037
1038 Sets self.faked to True if faked
1039
1040 Args:
1041 fname (str): Filename to check
Simon Glass3817ad42022-03-05 20:19:04 -07001042 size (int): Size of fake file to create
Simon Glass790ba9f2022-01-12 13:10:36 -07001043
1044 Returns:
Simon Glass9a0a2e92022-03-05 20:19:03 -07001045 tuple:
1046 fname (str): Filename of faked file
1047 bool: True if the blob was faked, False if not
Simon Glass790ba9f2022-01-12 13:10:36 -07001048 """
1049 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass7960a0a2022-08-07 09:46:46 -06001050 if not self.fake_fname:
1051 outfname = os.path.join(self.fake_dir, os.path.basename(fname))
1052 with open(outfname, "wb") as out:
1053 out.truncate(size)
1054 tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
1055 self.fake_fname = outfname
Simon Glass790ba9f2022-01-12 13:10:36 -07001056 self.faked = True
Simon Glass7960a0a2022-08-07 09:46:46 -06001057 return self.fake_fname, True
Simon Glass9a0a2e92022-03-05 20:19:03 -07001058 return fname, False
Simon Glass790ba9f2022-01-12 13:10:36 -07001059
Heiko Thierya89c8f22022-01-06 11:49:41 +01001060 def CheckFakedBlobs(self, faked_blobs_list):
1061 """Check if any entries in this section have faked external blobs
1062
1063 If there are faked blobs, the entries are added to the list
1064
1065 Args:
1066 fake_blobs_list: List of Entry objects to be added to
1067 """
1068 # This is meaningless for anything other than blobs
1069 pass
1070
Simon Glass87958982020-09-01 05:13:57 -06001071 def GetAllowMissing(self):
1072 """Get whether a section allows missing external blobs
1073
1074 Returns:
1075 True if allowed, False if not allowed
1076 """
1077 return self.allow_missing
Simon Glassb2381432020-09-06 10:39:09 -06001078
Simon Glass4f9ee832022-01-09 20:14:09 -07001079 def record_missing_bintool(self, bintool):
1080 """Record a missing bintool that was needed to produce this entry
1081
1082 Args:
1083 bintool (Bintool): Bintool that was missing
1084 """
Stefan Herbrechtsmeierfacc3782022-08-19 16:25:19 +02001085 if bintool not in self.missing_bintools:
1086 self.missing_bintools.append(bintool)
Simon Glass4f9ee832022-01-09 20:14:09 -07001087
1088 def check_missing_bintools(self, missing_list):
1089 """Check if any entries in this section have missing bintools
1090
1091 If there are missing bintools, these are added to the list
1092
1093 Args:
1094 missing_list: List of Bintool objects to be added to
1095 """
Stefan Herbrechtsmeierfacc3782022-08-19 16:25:19 +02001096 for bintool in self.missing_bintools:
1097 if bintool not in missing_list:
1098 missing_list.append(bintool)
1099
Simon Glass4f9ee832022-01-09 20:14:09 -07001100
Simon Glassb2381432020-09-06 10:39:09 -06001101 def GetHelpTags(self):
1102 """Get the tags use for missing-blob help
1103
1104 Returns:
1105 list of possible tags, most desirable first
1106 """
1107 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glass87c96292020-10-26 17:40:06 -06001108
1109 def CompressData(self, indata):
1110 """Compress data according to the entry's compression method
1111
1112 Args:
1113 indata: Data to compress
1114
1115 Returns:
Stefan Herbrechtsmeier4f463e32022-08-19 16:25:27 +02001116 Compressed data
Simon Glass87c96292020-10-26 17:40:06 -06001117 """
Simon Glass97c3e9a2020-10-26 17:40:15 -06001118 self.uncomp_data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001119 if self.compress != 'none':
1120 self.uncomp_size = len(indata)
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02001121 if self.comp_bintool.is_present():
1122 data = self.comp_bintool.compress(indata)
1123 else:
1124 self.record_missing_bintool(self.comp_bintool)
1125 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001126 else:
1127 data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001128 return data
Simon Glassb35fb172021-03-18 20:25:04 +13001129
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001130 def DecompressData(self, indata):
1131 """Decompress data according to the entry's compression method
1132
1133 Args:
1134 indata: Data to decompress
1135
1136 Returns:
1137 Decompressed data
1138 """
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001139 if self.compress != 'none':
Stefan Herbrechtsmeierc3665a82022-08-19 16:25:31 +02001140 if self.comp_bintool.is_present():
1141 data = self.comp_bintool.decompress(indata)
1142 self.uncomp_size = len(data)
1143 else:
1144 self.record_missing_bintool(self.comp_bintool)
1145 data = tools.get_bytes(0, 1024)
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001146 else:
1147 data = indata
Stefan Herbrechtsmeier204a27b2022-08-19 16:25:24 +02001148 self.uncomp_data = data
1149 return data
1150
Simon Glassb35fb172021-03-18 20:25:04 +13001151 @classmethod
1152 def UseExpanded(cls, node, etype, new_etype):
1153 """Check whether to use an expanded entry type
1154
1155 This is called by Entry.Create() when it finds an expanded version of
1156 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1157 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1158 ignored.
1159
1160 Args:
1161 node: Node object containing information about the entry to
1162 create
1163 etype: Original entry type being used
1164 new_etype: New entry type proposed
1165
1166 Returns:
1167 True to use this entry type, False to use the original one
1168 """
Simon Glassf3385a52022-01-29 14:14:15 -07001169 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glassb35fb172021-03-18 20:25:04 +13001170 (node.path, etype, new_etype))
1171 return True
Simon Glass943bf782021-11-23 21:09:50 -07001172
1173 def CheckAltFormats(self, alt_formats):
1174 """Add any alternative formats supported by this entry type
1175
1176 Args:
1177 alt_formats (dict): Dict to add alt_formats to:
1178 key: Name of alt format
1179 value: Help text
1180 """
1181 pass
Simon Glass386c63c2022-01-09 20:13:50 -07001182
Simon Glassae9a4572022-03-05 20:19:02 -07001183 def AddBintools(self, btools):
Simon Glass386c63c2022-01-09 20:13:50 -07001184 """Add the bintools used by this entry type
1185
1186 Args:
Simon Glassae9a4572022-03-05 20:19:02 -07001187 btools (dict of Bintool):
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001188
1189 Raise:
1190 ValueError if compression algorithm is not supported
Simon Glass386c63c2022-01-09 20:13:50 -07001191 """
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001192 algo = self.compress
1193 if algo != 'none':
Stefan Herbrechtsmeier7b26a462022-08-19 16:25:36 +02001194 algos = ['bzip2', 'gzip', 'lz4', 'lzma', 'lzo']
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001195 if algo not in algos:
1196 raise ValueError("Unknown algorithm '%s'" % algo)
Stefan Herbrechtsmeier7b26a462022-08-19 16:25:36 +02001197 names = {'lzma': 'lzma_alone', 'lzo': 'lzop'}
Stefan Herbrechtsmeierec7d27d2022-08-19 16:25:30 +02001198 name = names.get(self.compress, self.compress)
1199 self.comp_bintool = self.AddBintool(btools, name)
Simon Glass386c63c2022-01-09 20:13:50 -07001200
1201 @classmethod
1202 def AddBintool(self, tools, name):
1203 """Add a new bintool to the tools used by this etype
1204
1205 Args:
1206 name: Name of the tool
1207 """
1208 btool = bintool.Bintool.create(name)
1209 tools[name] = btool
1210 return btool
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03001211
1212 def SetUpdateHash(self, update_hash):
1213 """Set whether this entry's "hash" subnode should be updated
1214
1215 Args:
1216 update_hash: True if hash should be updated, False if not
1217 """
1218 self.update_hash = update_hash
Simon Glass81b71c32022-02-08 11:50:00 -07001219
Simon Glass72e423c2022-03-05 20:19:05 -07001220 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass81b71c32022-02-08 11:50:00 -07001221 """Put the contents of a list of entries into a file
1222
1223 Args:
1224 entries (list of Entry): Entries to collect
1225 prefix (str): Filename prefix of file to write to
Simon Glass72e423c2022-03-05 20:19:05 -07001226 fake_size (int): Size of fake file to create if needed
Simon Glass81b71c32022-02-08 11:50:00 -07001227
1228 If any entry does not have contents yet, this function returns False
1229 for the data.
1230
1231 Returns:
1232 Tuple:
Simon Glass6d427c42022-03-05 20:18:58 -07001233 bytes: Concatenated data from all the entries (or None)
1234 str: Filename of file written (or None if no data)
1235 str: Unique portion of filename (or None if no data)
Simon Glass81b71c32022-02-08 11:50:00 -07001236 """
1237 data = b''
1238 for entry in entries:
1239 # First get the input data and put it in a file. If not available,
1240 # try later.
Simon Glass72e423c2022-03-05 20:19:05 -07001241 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass6d427c42022-03-05 20:18:58 -07001242 return None, None, None
Simon Glass81b71c32022-02-08 11:50:00 -07001243 data += entry.GetData()
1244 uniq = self.GetUniqueName()
1245 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1246 tools.write_file(fname, data)
1247 return data, fname, uniq
Simon Glass7960a0a2022-08-07 09:46:46 -06001248
1249 @classmethod
1250 def create_fake_dir(cls):
1251 """Create the directory for fake files"""
1252 cls.fake_dir = tools.get_output_filename('binman-fake')
1253 if not os.path.exists(cls.fake_dir):
1254 os.mkdir(cls.fake_dir)
1255 tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")
Simon Glasscdadada2022-08-13 11:40:44 -06001256
1257 def ensure_props(self):
1258 """Raise an exception if properties are missing
1259
1260 Args:
1261 prop_list (list of str): List of properties to check for
1262
1263 Raises:
1264 ValueError: Any property is missing
1265 """
1266 not_present = []
1267 for prop in self.required_props:
1268 if not prop in self._node.props:
1269 not_present.append(prop)
1270 if not_present:
1271 self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}")