blob: 28a75109900f41db91e06886b5e261dbc2cc6e17 [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 Glassad35ce52022-01-09 20:14:03 -070015from binman import comp_util
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
Simon Glass7960a0a2022-08-07 09:46:46 -060086 fake_fname: Fake filename, if one was created, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070087 """
Simon Glass7960a0a2022-08-07 09:46:46 -060088 fake_dir = None
89
Simon Glassc6bd6e22019-07-20 12:23:45 -060090 def __init__(self, section, etype, node, name_prefix=''):
Simon Glass8dbb7442019-08-24 07:22:44 -060091 # Put this here to allow entry-docs and help to work without libfdt
92 global state
Simon Glass16287932020-04-17 18:09:03 -060093 from binman import state
Simon Glass8dbb7442019-08-24 07:22:44 -060094
Simon Glass25ac0e62018-06-01 09:38:14 -060095 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -070096 self.etype = etype
97 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -060098 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -060099 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700100 self.size = None
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600101 self.pre_reset_size = None
Simon Glass8287ee82019-07-08 14:25:30 -0600102 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -0600103 self.data = None
Simon Glass97c3e9a2020-10-26 17:40:15 -0600104 self.uncomp_data = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700105 self.contents_size = 0
106 self.align = None
107 self.align_size = None
108 self.align_end = None
109 self.pad_before = 0
110 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -0600111 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -0600112 self.image_pos = None
Simon Glass80a66ae2022-03-05 20:18:59 -0700113 self.extend_size = False
Simon Glass8287ee82019-07-08 14:25:30 -0600114 self.compress = 'none'
Simon Glassb1cca952020-07-09 18:39:40 -0600115 self.missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100116 self.faked = False
Simon Glass87958982020-09-01 05:13:57 -0600117 self.external = False
118 self.allow_missing = False
Heiko Thierya89c8f22022-01-06 11:49:41 +0100119 self.allow_fake = False
Simon Glass386c63c2022-01-09 20:13:50 -0700120 self.bintools = {}
Simon Glass4f9ee832022-01-09 20:14:09 -0700121 self.missing_bintools = []
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300122 self.update_hash = True
Simon Glass7960a0a2022-08-07 09:46:46 -0600123 self.fake_fname = None
Simon Glassbf7fd502016-11-25 20:15:51 -0700124
125 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700126 def FindEntryClass(etype, expanded):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600127 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -0700128
129 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -0600130 node_node: Path name of Node object containing information about
131 the entry to create (used for errors)
132 etype: Entry type to use
Simon Glassb35fb172021-03-18 20:25:04 +1300133 expanded: Use the expanded version of etype
Simon Glassbf7fd502016-11-25 20:15:51 -0700134
135 Returns:
Simon Glassb35fb172021-03-18 20:25:04 +1300136 The entry class object if found, else None if not found and expanded
Simon Glass858436d2021-11-23 21:09:49 -0700137 is True, else a tuple:
138 module name that could not be found
139 exception received
Simon Glassbf7fd502016-11-25 20:15:51 -0700140 """
Simon Glassdd57c132018-06-01 09:38:11 -0600141 # Convert something like 'u-boot@0' to 'u_boot' since we are only
142 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700143 module_name = etype.replace('-', '_')
Simon Glassb35fb172021-03-18 20:25:04 +1300144
Simon Glassdd57c132018-06-01 09:38:11 -0600145 if '@' in module_name:
146 module_name = module_name.split('@')[0]
Simon Glassb35fb172021-03-18 20:25:04 +1300147 if expanded:
148 module_name += '_expanded'
Simon Glassbf7fd502016-11-25 20:15:51 -0700149 module = modules.get(module_name)
150
Simon Glassbadf0ec2018-06-01 09:38:15 -0600151 # Also allow entry-type modules to be brought in from the etype directory.
152
Simon Glassbf7fd502016-11-25 20:15:51 -0700153 # Import the module if we have not already done so.
154 if not module:
155 try:
Simon Glass16287932020-04-17 18:09:03 -0600156 module = importlib.import_module('binman.etype.' + module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600157 except ImportError as e:
Simon Glassb35fb172021-03-18 20:25:04 +1300158 if expanded:
159 return None
Simon Glass858436d2021-11-23 21:09:49 -0700160 return module_name, e
Simon Glassbf7fd502016-11-25 20:15:51 -0700161 modules[module_name] = module
162
Simon Glassfd8d1f72018-07-17 13:25:36 -0600163 # Look up the expected class name
164 return getattr(module, 'Entry_%s' % module_name)
165
166 @staticmethod
Simon Glass858436d2021-11-23 21:09:49 -0700167 def Lookup(node_path, etype, expanded, missing_etype=False):
168 """Look up the entry class for a node.
169
170 Args:
171 node_node (str): Path name of Node object containing information
172 about the entry to create (used for errors)
173 etype (str): Entry type to use
174 expanded (bool): Use the expanded version of etype
175 missing_etype (bool): True to default to a blob etype if the
176 requested etype is not found
177
178 Returns:
179 The entry class object if found, else None if not found and expanded
180 is True
181
182 Raise:
183 ValueError if expanded is False and the class is not found
184 """
185 # Convert something like 'u-boot@0' to 'u_boot' since we are only
186 # interested in the type.
187 cls = Entry.FindEntryClass(etype, expanded)
188 if cls is None:
189 return None
190 elif isinstance(cls, tuple):
191 if missing_etype:
192 cls = Entry.FindEntryClass('blob', False)
193 if isinstance(cls, tuple): # This should not fail
194 module_name, e = cls
195 raise ValueError(
196 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
197 (etype, node_path, module_name, e))
198 return cls
199
200 @staticmethod
201 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glassfd8d1f72018-07-17 13:25:36 -0600202 """Create a new entry for a node.
203
204 Args:
Simon Glass858436d2021-11-23 21:09:49 -0700205 section (entry_Section): Section object containing this node
206 node (Node): Node object containing information about the entry to
207 create
208 etype (str): Entry type to use, or None to work it out (used for
209 tests)
210 expanded (bool): Use the expanded version of etype
211 missing_etype (bool): True to default to a blob etype if the
212 requested etype is not found
Simon Glassfd8d1f72018-07-17 13:25:36 -0600213
214 Returns:
215 A new Entry object of the correct type (a subclass of Entry)
216 """
217 if not etype:
218 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glass858436d2021-11-23 21:09:49 -0700219 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glassb35fb172021-03-18 20:25:04 +1300220 if obj and expanded:
221 # Check whether to use the expanded entry
222 new_etype = etype + '-expanded'
Simon Glass3d433382021-03-21 18:24:30 +1300223 can_expand = not fdt_util.GetBool(node, 'no-expanded')
224 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glassb35fb172021-03-18 20:25:04 +1300225 etype = new_etype
226 else:
227 obj = None
228 if not obj:
Simon Glass858436d2021-11-23 21:09:49 -0700229 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600230
Simon Glassbf7fd502016-11-25 20:15:51 -0700231 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600232 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700233
234 def ReadNode(self):
235 """Read entry information from the node
236
Simon Glassc6bd6e22019-07-20 12:23:45 -0600237 This must be called as the first thing after the Entry is created.
238
Simon Glassbf7fd502016-11-25 20:15:51 -0700239 This reads all the fields we recognise from the node, ready for use.
240 """
Simon Glass15a587c2018-07-17 13:25:51 -0600241 if 'pos' in self._node.props:
242 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass80a66ae2022-03-05 20:18:59 -0700243 if 'expand-size' in self._node.props:
244 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glass3ab95982018-08-01 15:22:37 -0600245 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700246 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glass12bb1a92019-07-20 12:23:51 -0600247 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
248 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
249 if self.GetImage().copy_to_orig:
250 self.orig_offset = self.offset
251 self.orig_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600252
Simon Glassffded752019-07-08 14:25:46 -0600253 # These should not be set in input files, but are set in an FDT map,
254 # which is also read by this code.
255 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
256 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
257
Simon Glassbf7fd502016-11-25 20:15:51 -0700258 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700259 if tools.not_power_of_two(self.align):
Simon Glassbf7fd502016-11-25 20:15:51 -0700260 raise ValueError("Node '%s': Alignment %s must be a power of two" %
261 (self._node.path, self.align))
Simon Glass5ff9fed2021-03-21 18:24:33 +1300262 if self.section and self.align is None:
263 self.align = self.section.align_default
Simon Glassbf7fd502016-11-25 20:15:51 -0700264 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
265 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
266 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700267 if tools.not_power_of_two(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600268 self.Raise("Alignment size %s must be a power of two" %
269 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700270 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600271 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glass80a66ae2022-03-05 20:18:59 -0700272 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassb2381432020-09-06 10:39:09 -0600273 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glassbf7fd502016-11-25 20:15:51 -0700274
Simon Glass87c96292020-10-26 17:40:06 -0600275 # This is only supported by blobs and sections at present
276 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
277
Simon Glass6c234bf2018-09-14 04:57:18 -0600278 def GetDefaultFilename(self):
279 return None
280
Simon Glassa8adb6d2019-07-20 12:23:28 -0600281 def GetFdts(self):
282 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600283
284 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600285 Empty dict, if this entry is not a .dtb, otherwise:
286 Dict:
287 key: Filename from this entry (without the path)
Simon Glass4bdd3002019-07-20 12:23:31 -0600288 value: Tuple:
Simon Glassadb67bb2021-03-18 20:25:02 +1300289 Entry object for this dtb
Simon Glass4bdd3002019-07-20 12:23:31 -0600290 Filename of file containing this dtb
Simon Glass539aece2018-09-14 04:57:22 -0600291 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600292 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600293
Simon Glassc9ee33a2022-03-05 20:19:00 -0700294 def gen_entries(self):
295 """Allow entries to generate other entries
Simon Glassa01d1a22021-03-18 20:24:52 +1300296
297 Some entries generate subnodes automatically, from which sub-entries
298 are then created. This method allows those to be added to the binman
299 definition for the current image. An entry which implements this method
300 should call state.AddSubnode() to add a subnode and can add properties
301 with state.AddString(), etc.
302
303 An example is 'files', which produces a section containing a list of
304 files.
305 """
Simon Glass0a98b282018-09-14 04:57:28 -0600306 pass
307
Simon Glassa9fad072020-10-26 17:40:17 -0600308 def AddMissingProperties(self, have_image_pos):
309 """Add new properties to the device tree as needed for this entry
310
311 Args:
312 have_image_pos: True if this entry has an image position. This can
313 be False if its parent section is compressed, since compression
314 groups all entries together into a compressed block of data,
315 obscuring the start of each individual child entry
316 """
317 for prop in ['offset', 'size']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600318 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600319 state.AddZeroProp(self._node, prop)
Simon Glassa9fad072020-10-26 17:40:17 -0600320 if have_image_pos and 'image-pos' not in self._node.props:
321 state.AddZeroProp(self._node, 'image-pos')
Simon Glass12bb1a92019-07-20 12:23:51 -0600322 if self.GetImage().allow_repack:
323 if self.orig_offset is not None:
324 state.AddZeroProp(self._node, 'orig-offset', True)
325 if self.orig_size is not None:
326 state.AddZeroProp(self._node, 'orig-size', True)
327
Simon Glass8287ee82019-07-08 14:25:30 -0600328 if self.compress != 'none':
329 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300330
331 if self.update_hash:
332 err = state.CheckAddHashProp(self._node)
333 if err:
334 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600335
336 def SetCalculatedProperties(self):
337 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600338 state.SetInt(self._node, 'offset', self.offset)
339 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600340 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassa9fad072020-10-26 17:40:17 -0600341 if self.image_pos is not None:
Simon Glass08594d42020-11-02 12:55:44 -0700342 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass12bb1a92019-07-20 12:23:51 -0600343 if self.GetImage().allow_repack:
344 if self.orig_offset is not None:
345 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
346 if self.orig_size is not None:
347 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glass8287ee82019-07-08 14:25:30 -0600348 if self.uncomp_size is not None:
349 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasakee813c82022-02-09 22:02:35 +0300350
351 if self.update_hash:
352 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600353
Simon Glassecab8972018-07-06 10:27:40 -0600354 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600355 """Allow entries to adjust the device tree
356
357 Some entries need to adjust the device tree for their purposes. This
358 may involve adding or deleting properties.
359
360 Returns:
361 True if processing is complete
362 False if processing could not be completed due to a dependency.
363 This will cause the entry to be retried after others have been
364 called
365 """
Simon Glassecab8972018-07-06 10:27:40 -0600366 return True
367
Simon Glassc8d48ef2018-06-01 09:38:21 -0600368 def SetPrefix(self, prefix):
369 """Set the name prefix for a node
370
371 Args:
372 prefix: Prefix to set, or '' to not use a prefix
373 """
374 if prefix:
375 self.name = prefix + self.name
376
Simon Glass5c890232018-07-06 10:27:19 -0600377 def SetContents(self, data):
378 """Set the contents of an entry
379
380 This sets both the data and content_size properties
381
382 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600383 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600384 """
385 self.data = data
386 self.contents_size = len(self.data)
387
388 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600389 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600390
Simon Glassa0dcaf22019-07-08 14:25:35 -0600391 This checks that the new data is the same size as the old. If the size
392 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600393
394 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600395 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600396
397 Raises:
398 ValueError if the new data size is not the same as the old
399 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600400 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600401 new_size = len(data)
Simon Glass61ec04f2019-07-20 12:23:58 -0600402 if state.AllowEntryExpansion() and new_size > self.contents_size:
403 # self.data will indicate the new size needed
404 size_ok = False
405 elif state.AllowEntryContraction() and new_size < self.contents_size:
406 size_ok = False
407
408 # If not allowed to change, try to deal with it or give up
409 if size_ok:
Simon Glassc52c9e72019-07-08 14:25:37 -0600410 if new_size > self.contents_size:
Simon Glass61ec04f2019-07-20 12:23:58 -0600411 self.Raise('Cannot update entry size from %d to %d' %
412 (self.contents_size, new_size))
413
414 # Don't let the data shrink. Pad it if necessary
415 if size_ok and new_size < self.contents_size:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700416 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass61ec04f2019-07-20 12:23:58 -0600417
418 if not size_ok:
Simon Glassf3385a52022-01-29 14:14:15 -0700419 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glassc1aa66e2022-01-29 14:14:04 -0700420 self._node.path, to_hex(self.contents_size),
421 to_hex(new_size)))
Simon Glass5c890232018-07-06 10:27:19 -0600422 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600423 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600424
Simon Glass72e423c2022-03-05 20:19:05 -0700425 def ObtainContents(self, skip_entry=None, fake_size=0):
Simon Glassbf7fd502016-11-25 20:15:51 -0700426 """Figure out the contents of an entry.
427
Simon Glass72e423c2022-03-05 20:19:05 -0700428 Args:
429 skip_entry (Entry): Entry to skip when obtaining section contents
430 fake_size (int): Size of fake file to create if needed
431
Simon Glassbf7fd502016-11-25 20:15:51 -0700432 Returns:
433 True if the contents were found, False if another call is needed
434 after the other entries are processed.
435 """
436 # No contents by default: subclasses can implement this
437 return True
438
Simon Glassc52c9e72019-07-08 14:25:37 -0600439 def ResetForPack(self):
440 """Reset offset/size fields so that packing can be done again"""
Simon Glass9f297b02019-07-20 12:23:36 -0600441 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700442 (to_hex(self.offset), to_hex(self.orig_offset),
443 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600444 self.pre_reset_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600445 self.offset = self.orig_offset
446 self.size = self.orig_size
447
Simon Glass3ab95982018-08-01 15:22:37 -0600448 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600449 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700450
451 Most of the time the entries are not fully specified. There may be
452 an alignment but no size. In that case we take the size from the
453 contents of the entry.
454
Simon Glass3ab95982018-08-01 15:22:37 -0600455 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700456
Simon Glass3ab95982018-08-01 15:22:37 -0600457 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700458 entry will be know.
459
460 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600461 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700462
463 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600464 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700465 """
Simon Glass9f297b02019-07-20 12:23:36 -0600466 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glassc1aa66e2022-01-29 14:14:04 -0700467 (to_hex(self.offset), to_hex(self.size),
Simon Glass9f297b02019-07-20 12:23:36 -0600468 self.contents_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600469 if self.offset is None:
470 if self.offset_unset:
471 self.Raise('No offset set with offset-unset: should another '
472 'entry provide this correct offset?')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700473 self.offset = tools.align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700474 needed = self.pad_before + self.contents_size + self.pad_after
Simon Glassc1aa66e2022-01-29 14:14:04 -0700475 needed = tools.align(needed, self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700476 size = self.size
477 if not size:
478 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600479 new_offset = self.offset + size
Simon Glassc1aa66e2022-01-29 14:14:04 -0700480 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glass3ab95982018-08-01 15:22:37 -0600481 if aligned_offset != new_offset:
482 size = aligned_offset - self.offset
483 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700484
485 if not self.size:
486 self.size = size
487
488 if self.size < needed:
489 self.Raise("Entry contents size is %#x (%d) but entry size is "
490 "%#x (%d)" % (needed, needed, self.size, self.size))
491 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600492 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700493 # conflict with the provided alignment values
Simon Glassc1aa66e2022-01-29 14:14:04 -0700494 if self.size != tools.align(self.size, self.align_size):
Simon Glassbf7fd502016-11-25 20:15:51 -0700495 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
496 (self.size, self.size, self.align_size, self.align_size))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700497 if self.offset != tools.align(self.offset, self.align):
Simon Glass3ab95982018-08-01 15:22:37 -0600498 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
499 (self.offset, self.offset, self.align, self.align))
Simon Glass9f297b02019-07-20 12:23:36 -0600500 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
501 (self.offset, self.size, self.contents_size, new_offset))
Simon Glassbf7fd502016-11-25 20:15:51 -0700502
Simon Glass3ab95982018-08-01 15:22:37 -0600503 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700504
505 def Raise(self, msg):
506 """Convenience function to raise an error referencing a node"""
507 raise ValueError("Node '%s': %s" % (self._node.path, msg))
508
Simon Glass189f2912021-03-21 18:24:31 +1300509 def Info(self, msg):
510 """Convenience function to log info referencing a node"""
511 tag = "Info '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700512 tout.detail('%30s: %s' % (tag, msg))
Simon Glass189f2912021-03-21 18:24:31 +1300513
Simon Glass9f297b02019-07-20 12:23:36 -0600514 def Detail(self, msg):
515 """Convenience function to log detail referencing a node"""
516 tag = "Node '%s'" % self._node.path
Simon Glassf3385a52022-01-29 14:14:15 -0700517 tout.detail('%30s: %s' % (tag, msg))
Simon Glass9f297b02019-07-20 12:23:36 -0600518
Simon Glass53af22a2018-07-17 13:25:32 -0600519 def GetEntryArgsOrProps(self, props, required=False):
520 """Return the values of a set of properties
521
522 Args:
523 props: List of EntryArg objects
524
525 Raises:
526 ValueError if a property is not found
527 """
528 values = []
529 missing = []
530 for prop in props:
531 python_prop = prop.name.replace('-', '_')
532 if hasattr(self, python_prop):
533 value = getattr(self, python_prop)
534 else:
535 value = None
536 if value is None:
537 value = self.GetArg(prop.name, prop.datatype)
538 if value is None and required:
539 missing.append(prop.name)
540 values.append(value)
541 if missing:
Simon Glass939d1062021-01-06 21:35:16 -0700542 self.GetImage().MissingArgs(self, missing)
Simon Glass53af22a2018-07-17 13:25:32 -0600543 return values
544
Simon Glassbf7fd502016-11-25 20:15:51 -0700545 def GetPath(self):
546 """Get the path of a node
547
548 Returns:
549 Full path of the node for this entry
550 """
551 return self._node.path
552
Simon Glass631f7522021-03-21 18:24:32 +1300553 def GetData(self, required=True):
Simon Glass63e7ba62020-10-26 17:40:16 -0600554 """Get the contents of an entry
555
Simon Glass631f7522021-03-21 18:24:32 +1300556 Args:
557 required: True if the data must be present, False if it is OK to
558 return None
559
Simon Glass63e7ba62020-10-26 17:40:16 -0600560 Returns:
561 bytes content of the entry, excluding any padding. If the entry is
562 compressed, the compressed data is returned
563 """
Simon Glassc1aa66e2022-01-29 14:14:04 -0700564 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glassbf7fd502016-11-25 20:15:51 -0700565 return self.data
566
Simon Glass271a0832020-11-02 12:55:43 -0700567 def GetPaddedData(self, data=None):
568 """Get the data for an entry including any padding
569
570 Gets the entry data and uses its section's pad-byte value to add padding
571 before and after as defined by the pad-before and pad-after properties.
572
573 This does not consider alignment.
574
575 Returns:
576 Contents of the entry along with any pad bytes before and
577 after it (bytes)
578 """
579 if data is None:
580 data = self.GetData()
581 return self.section.GetPaddedDataForEntry(self, data)
582
Simon Glass3ab95982018-08-01 15:22:37 -0600583 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600584 """Get the offsets for siblings
585
586 Some entry types can contain information about the position or size of
587 other entries. An example of this is the Intel Flash Descriptor, which
588 knows where the Intel Management Engine section should go.
589
590 If this entry knows about the position of other entries, it can specify
591 this by returning values here
592
593 Returns:
594 Dict:
595 key: Entry type
596 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600597 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600598 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700599 return {}
600
Simon Glasscf549042019-07-08 13:18:39 -0600601 def SetOffsetSize(self, offset, size):
602 """Set the offset and/or size of an entry
603
604 Args:
605 offset: New offset, or None to leave alone
606 size: New size, or None to leave alone
607 """
608 if offset is not None:
609 self.offset = offset
610 if size is not None:
611 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700612
Simon Glassdbf6be92018-08-01 15:22:42 -0600613 def SetImagePos(self, image_pos):
614 """Set the position in the image
615
616 Args:
617 image_pos: Position of this entry in the image
618 """
619 self.image_pos = image_pos + self.offset
620
Simon Glassbf7fd502016-11-25 20:15:51 -0700621 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600622 """Do any post-packing updates of entry contents
623
624 This function should call ProcessContentsUpdate() to update the entry
625 contents, if necessary, returning its return value here.
626
627 Args:
628 data: Data to set to the contents (bytes)
629
630 Returns:
631 True if the new data size is OK, False if expansion is needed
632
633 Raises:
634 ValueError if the new data size is not the same as the old and
635 state.AllowEntryExpansion() is False
636 """
637 return True
Simon Glass19790632017-11-13 18:55:01 -0700638
Simon Glassf55382b2018-06-01 09:38:13 -0600639 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700640 """Write symbol values into binary files for access at run time
641
642 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600643 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700644 """
645 pass
Simon Glass18546952018-06-01 09:38:16 -0600646
Simon Glass6ddd6112020-10-26 17:40:18 -0600647 def CheckEntries(self):
Simon Glass3ab95982018-08-01 15:22:37 -0600648 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600649
Simon Glass3ab95982018-08-01 15:22:37 -0600650 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600651 than having to be fully inside their section). Sub-classes can implement
652 this function and raise if there is a problem.
653 """
654 pass
Simon Glass3b0c38212018-06-01 09:38:20 -0600655
Simon Glass8122f392018-07-17 13:25:28 -0600656 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600657 def GetStr(value):
658 if value is None:
659 return '<none> '
660 return '%08x' % value
661
662 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600663 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600664 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
665 Entry.GetStr(offset), Entry.GetStr(size),
666 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600667
Simon Glass3b0c38212018-06-01 09:38:20 -0600668 def WriteMap(self, fd, indent):
669 """Write a map of the entry to a .map file
670
671 Args:
672 fd: File to write the map to
673 indent: Curent indent level of map (0=none, 1=one level, etc.)
674 """
Simon Glass1be70d22018-07-17 13:25:49 -0600675 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
676 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600677
Simon Glass11e36cc2018-07-17 13:25:38 -0600678 def GetEntries(self):
679 """Return a list of entries contained by this entry
680
681 Returns:
682 List of entries, or None if none. A normal entry has no entries
683 within it so will return None
684 """
685 return None
686
Simon Glass53af22a2018-07-17 13:25:32 -0600687 def GetArg(self, name, datatype=str):
688 """Get the value of an entry argument or device-tree-node property
689
690 Some node properties can be provided as arguments to binman. First check
691 the entry arguments, and fall back to the device tree if not found
692
693 Args:
694 name: Argument name
695 datatype: Data type (str or int)
696
697 Returns:
698 Value of argument as a string or int, or None if no value
699
700 Raises:
701 ValueError if the argument cannot be converted to in
702 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600703 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600704 if value is not None:
705 if datatype == int:
706 try:
707 value = int(value)
708 except ValueError:
709 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
710 (name, value))
711 elif datatype == str:
712 pass
713 else:
714 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
715 datatype)
716 else:
717 value = fdt_util.GetDatatype(self._node, name, datatype)
718 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600719
720 @staticmethod
721 def WriteDocs(modules, test_missing=None):
722 """Write out documentation about the various entry types to stdout
723
724 Args:
725 modules: List of modules to include
726 test_missing: Used for testing. This is a module to report
727 as missing
728 """
729 print('''Binman Entry Documentation
730===========================
731
732This file describes the entry types supported by binman. These entry types can
733be placed in an image one by one to build up a final firmware image. It is
734fairly easy to create new entry types. Just add a new file to the 'etype'
735directory. You can use the existing entries as examples.
736
737Note that some entries are subclasses of others, using and extending their
738features to produce new behaviours.
739
740
741''')
742 modules = sorted(modules)
743
744 # Don't show the test entry
745 if '_testing' in modules:
746 modules.remove('_testing')
747 missing = []
748 for name in modules:
Simon Glassb35fb172021-03-18 20:25:04 +1300749 module = Entry.Lookup('WriteDocs', name, False)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600750 docs = getattr(module, '__doc__')
751 if test_missing == name:
752 docs = None
753 if docs:
754 lines = docs.splitlines()
755 first_line = lines[0]
756 rest = [line[4:] for line in lines[1:]]
757 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
Simon Glass228c9b82022-08-07 16:33:25 -0600758
759 # Create a reference for use by rST docs
760 ref_name = f'etype_{module.__name__[6:]}'.lower()
761 print('.. _%s:' % ref_name)
762 print()
Simon Glassfd8d1f72018-07-17 13:25:36 -0600763 print(hdr)
764 print('-' * len(hdr))
765 print('\n'.join(rest))
766 print()
767 print()
768 else:
769 missing.append(name)
770
771 if missing:
772 raise ValueError('Documentation is missing for modules: %s' %
773 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600774
775 def GetUniqueName(self):
776 """Get a unique name for a node
777
778 Returns:
779 String containing a unique name for a node, consisting of the name
780 of all ancestors (starting from within the 'binman' node) separated
781 by a dot ('.'). This can be useful for generating unique filesnames
782 in the output directory.
783 """
784 name = self.name
785 node = self._node
786 while node.parent:
787 node = node.parent
Alper Nebi Yasak67bf2c82022-03-27 18:31:44 +0300788 if node.name in ('binman', '/'):
Simon Glassa326b492018-09-14 04:57:11 -0600789 break
790 name = '%s.%s' % (node.name, name)
791 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600792
Simon Glass80a66ae2022-03-05 20:18:59 -0700793 def extend_to_limit(self, limit):
794 """Extend an entry so that it ends at the given offset limit"""
Simon Glassba64a0b2018-09-14 04:57:29 -0600795 if self.offset + self.size < limit:
796 self.size = limit - self.offset
797 # Request the contents again, since changing the size requires that
798 # the data grows. This should not fail, but check it to be sure.
799 if not self.ObtainContents():
800 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600801
802 def HasSibling(self, name):
803 """Check if there is a sibling of a given name
804
805 Returns:
806 True if there is an entry with this name in the the same section,
807 else False
808 """
809 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600810
811 def GetSiblingImagePos(self, name):
812 """Return the image position of the given sibling
813
814 Returns:
815 Image position of sibling, or None if the sibling has no position,
816 or False if there is no such sibling
817 """
818 if not self.HasSibling(name):
819 return False
820 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600821
822 @staticmethod
823 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
824 uncomp_size, offset, entry):
825 """Add a new entry to the entries list
826
827 Args:
828 entries: List (of EntryInfo objects) to add to
829 indent: Current indent level to add to list
830 name: Entry name (string)
831 etype: Entry type (string)
832 size: Entry size in bytes (int)
833 image_pos: Position within image in bytes (int)
834 uncomp_size: Uncompressed size if the entry uses compression, else
835 None
836 offset: Entry offset within parent in bytes (int)
837 entry: Entry object
838 """
839 entries.append(EntryInfo(indent, name, etype, size, image_pos,
840 uncomp_size, offset, entry))
841
842 def ListEntries(self, entries, indent):
843 """Add files in this entry to the list of entries
844
845 This can be overridden by subclasses which need different behaviour.
846
847 Args:
848 entries: List (of EntryInfo objects) to add to
849 indent: Current indent level to add to list
850 """
851 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
852 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600853
Simon Glass943bf782021-11-23 21:09:50 -0700854 def ReadData(self, decomp=True, alt_format=None):
Simon Glassf667e452019-07-08 14:25:50 -0600855 """Read the data for an entry from the image
856
857 This is used when the image has been read in and we want to extract the
858 data for a particular entry from that image.
859
860 Args:
861 decomp: True to decompress any compressed data before returning it;
862 False to return the raw, uncompressed data
863
864 Returns:
865 Entry data (bytes)
866 """
867 # Use True here so that we get an uncompressed section to work from,
868 # although compressed sections are currently not supported
Simon Glassf3385a52022-01-29 14:14:15 -0700869 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass2d553c02019-09-25 08:56:21 -0600870 (self.section.GetPath(), self.GetPath()))
Simon Glass943bf782021-11-23 21:09:50 -0700871 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glassa9cd39e2019-07-20 12:24:04 -0600872 return data
Simon Glassd5079332019-07-20 12:23:41 -0600873
Simon Glass943bf782021-11-23 21:09:50 -0700874 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass2d553c02019-09-25 08:56:21 -0600875 """Read the data for a particular child entry
Simon Glass4e185e82019-09-25 08:56:20 -0600876
877 This reads data from the parent and extracts the piece that relates to
878 the given child.
879
880 Args:
Simon Glass943bf782021-11-23 21:09:50 -0700881 child (Entry): Child entry to read data for (must be valid)
882 decomp (bool): True to decompress any compressed data before
883 returning it; False to return the raw, uncompressed data
884 alt_format (str): Alternative format to read in, or None
Simon Glass4e185e82019-09-25 08:56:20 -0600885
886 Returns:
887 Data for the child (bytes)
888 """
889 pass
890
Simon Glassd5079332019-07-20 12:23:41 -0600891 def LoadData(self, decomp=True):
892 data = self.ReadData(decomp)
Simon Glass10f9d002019-07-20 12:23:50 -0600893 self.contents_size = len(data)
Simon Glassd5079332019-07-20 12:23:41 -0600894 self.ProcessContentsUpdate(data)
895 self.Detail('Loaded data size %x' % len(data))
Simon Glassc5ad04b2019-07-20 12:23:46 -0600896
Simon Glass943bf782021-11-23 21:09:50 -0700897 def GetAltFormat(self, data, alt_format):
898 """Read the data for an extry in an alternative format
899
900 Supported formats are list in the documentation for each entry. An
901 example is fdtmap which provides .
902
903 Args:
904 data (bytes): Data to convert (this should have been produced by the
905 entry)
906 alt_format (str): Format to use
907
908 """
909 pass
910
Simon Glassc5ad04b2019-07-20 12:23:46 -0600911 def GetImage(self):
912 """Get the image containing this entry
913
914 Returns:
915 Image object containing this entry
916 """
917 return self.section.GetImage()
Simon Glass10f9d002019-07-20 12:23:50 -0600918
919 def WriteData(self, data, decomp=True):
920 """Write the data to an entry in the image
921
922 This is used when the image has been read in and we want to replace the
923 data for a particular entry in that image.
924
925 The image must be re-packed and written out afterwards.
926
927 Args:
928 data: Data to replace it with
929 decomp: True to compress the data if needed, False if data is
930 already compressed so should be used as is
931
932 Returns:
933 True if the data did not result in a resize of this entry, False if
934 the entry must be resized
935 """
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600936 if self.size is not None:
937 self.contents_size = self.size
938 else:
939 self.contents_size = self.pre_reset_size
Simon Glass10f9d002019-07-20 12:23:50 -0600940 ok = self.ProcessContentsUpdate(data)
941 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glass7210c892019-07-20 12:24:05 -0600942 section_ok = self.section.WriteChildData(self)
943 return ok and section_ok
944
945 def WriteChildData(self, child):
946 """Handle writing the data in a child entry
947
948 This should be called on the child's parent section after the child's
Simon Glass557693e2021-11-23 11:03:44 -0700949 data has been updated. It should update any data structures needed to
950 validate that the update is successful.
Simon Glass7210c892019-07-20 12:24:05 -0600951
952 This base-class implementation does nothing, since the base Entry object
953 does not have any children.
954
955 Args:
956 child: Child Entry that was written
957
958 Returns:
959 True if the section could be updated successfully, False if the
Simon Glass557693e2021-11-23 11:03:44 -0700960 data is such that the section could not update
Simon Glass7210c892019-07-20 12:24:05 -0600961 """
962 return True
Simon Glasseba1f0c2019-07-20 12:23:55 -0600963
964 def GetSiblingOrder(self):
965 """Get the relative order of an entry amoung its siblings
966
967 Returns:
968 'start' if this entry is first among siblings, 'end' if last,
969 otherwise None
970 """
971 entries = list(self.section.GetEntries().values())
972 if entries:
973 if self == entries[0]:
974 return 'start'
975 elif self == entries[-1]:
976 return 'end'
977 return 'middle'
Simon Glass4f9f1052020-07-09 18:39:38 -0600978
979 def SetAllowMissing(self, allow_missing):
980 """Set whether a section allows missing external blobs
981
982 Args:
983 allow_missing: True if allowed, False if not allowed
984 """
985 # This is meaningless for anything other than sections
986 pass
Simon Glassb1cca952020-07-09 18:39:40 -0600987
Heiko Thierya89c8f22022-01-06 11:49:41 +0100988 def SetAllowFakeBlob(self, allow_fake):
989 """Set whether a section allows to create a fake blob
990
991 Args:
992 allow_fake: True if allowed, False if not allowed
993 """
Simon Glassf4590e02022-01-09 20:13:46 -0700994 self.allow_fake = allow_fake
Heiko Thierya89c8f22022-01-06 11:49:41 +0100995
Simon Glassb1cca952020-07-09 18:39:40 -0600996 def CheckMissing(self, missing_list):
997 """Check if any entries in this section have missing external blobs
998
999 If there are missing blobs, the entries are added to the list
1000
1001 Args:
1002 missing_list: List of Entry objects to be added to
1003 """
1004 if self.missing:
1005 missing_list.append(self)
Simon Glass87958982020-09-01 05:13:57 -06001006
Simon Glass3817ad42022-03-05 20:19:04 -07001007 def check_fake_fname(self, fname, size=0):
Simon Glass790ba9f2022-01-12 13:10:36 -07001008 """If the file is missing and the entry allows fake blobs, fake it
1009
1010 Sets self.faked to True if faked
1011
1012 Args:
1013 fname (str): Filename to check
Simon Glass3817ad42022-03-05 20:19:04 -07001014 size (int): Size of fake file to create
Simon Glass790ba9f2022-01-12 13:10:36 -07001015
1016 Returns:
Simon Glass9a0a2e92022-03-05 20:19:03 -07001017 tuple:
1018 fname (str): Filename of faked file
1019 bool: True if the blob was faked, False if not
Simon Glass790ba9f2022-01-12 13:10:36 -07001020 """
1021 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass7960a0a2022-08-07 09:46:46 -06001022 if not self.fake_fname:
1023 outfname = os.path.join(self.fake_dir, os.path.basename(fname))
1024 with open(outfname, "wb") as out:
1025 out.truncate(size)
1026 tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
1027 self.fake_fname = outfname
Simon Glass790ba9f2022-01-12 13:10:36 -07001028 self.faked = True
Simon Glass7960a0a2022-08-07 09:46:46 -06001029 return self.fake_fname, True
Simon Glass9a0a2e92022-03-05 20:19:03 -07001030 return fname, False
Simon Glass790ba9f2022-01-12 13:10:36 -07001031
Heiko Thierya89c8f22022-01-06 11:49:41 +01001032 def CheckFakedBlobs(self, faked_blobs_list):
1033 """Check if any entries in this section have faked external blobs
1034
1035 If there are faked blobs, the entries are added to the list
1036
1037 Args:
1038 fake_blobs_list: List of Entry objects to be added to
1039 """
1040 # This is meaningless for anything other than blobs
1041 pass
1042
Simon Glass87958982020-09-01 05:13:57 -06001043 def GetAllowMissing(self):
1044 """Get whether a section allows missing external blobs
1045
1046 Returns:
1047 True if allowed, False if not allowed
1048 """
1049 return self.allow_missing
Simon Glassb2381432020-09-06 10:39:09 -06001050
Simon Glass4f9ee832022-01-09 20:14:09 -07001051 def record_missing_bintool(self, bintool):
1052 """Record a missing bintool that was needed to produce this entry
1053
1054 Args:
1055 bintool (Bintool): Bintool that was missing
1056 """
1057 self.missing_bintools.append(bintool)
1058
1059 def check_missing_bintools(self, missing_list):
1060 """Check if any entries in this section have missing bintools
1061
1062 If there are missing bintools, these are added to the list
1063
1064 Args:
1065 missing_list: List of Bintool objects to be added to
1066 """
1067 missing_list += self.missing_bintools
1068
Simon Glassb2381432020-09-06 10:39:09 -06001069 def GetHelpTags(self):
1070 """Get the tags use for missing-blob help
1071
1072 Returns:
1073 list of possible tags, most desirable first
1074 """
1075 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glass87c96292020-10-26 17:40:06 -06001076
1077 def CompressData(self, indata):
1078 """Compress data according to the entry's compression method
1079
1080 Args:
1081 indata: Data to compress
1082
1083 Returns:
1084 Compressed data (first word is the compressed size)
1085 """
Simon Glass97c3e9a2020-10-26 17:40:15 -06001086 self.uncomp_data = indata
Simon Glass87c96292020-10-26 17:40:06 -06001087 if self.compress != 'none':
1088 self.uncomp_size = len(indata)
Simon Glass0d1e95a2022-01-09 20:14:04 -07001089 data = comp_util.compress(indata, self.compress)
Simon Glass87c96292020-10-26 17:40:06 -06001090 return data
Simon Glassb35fb172021-03-18 20:25:04 +13001091
1092 @classmethod
1093 def UseExpanded(cls, node, etype, new_etype):
1094 """Check whether to use an expanded entry type
1095
1096 This is called by Entry.Create() when it finds an expanded version of
1097 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1098 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1099 ignored.
1100
1101 Args:
1102 node: Node object containing information about the entry to
1103 create
1104 etype: Original entry type being used
1105 new_etype: New entry type proposed
1106
1107 Returns:
1108 True to use this entry type, False to use the original one
1109 """
Simon Glassf3385a52022-01-29 14:14:15 -07001110 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glassb35fb172021-03-18 20:25:04 +13001111 (node.path, etype, new_etype))
1112 return True
Simon Glass943bf782021-11-23 21:09:50 -07001113
1114 def CheckAltFormats(self, alt_formats):
1115 """Add any alternative formats supported by this entry type
1116
1117 Args:
1118 alt_formats (dict): Dict to add alt_formats to:
1119 key: Name of alt format
1120 value: Help text
1121 """
1122 pass
Simon Glass386c63c2022-01-09 20:13:50 -07001123
Simon Glassae9a4572022-03-05 20:19:02 -07001124 def AddBintools(self, btools):
Simon Glass386c63c2022-01-09 20:13:50 -07001125 """Add the bintools used by this entry type
1126
1127 Args:
Simon Glassae9a4572022-03-05 20:19:02 -07001128 btools (dict of Bintool):
Simon Glass386c63c2022-01-09 20:13:50 -07001129 """
1130 pass
1131
1132 @classmethod
1133 def AddBintool(self, tools, name):
1134 """Add a new bintool to the tools used by this etype
1135
1136 Args:
1137 name: Name of the tool
1138 """
1139 btool = bintool.Bintool.create(name)
1140 tools[name] = btool
1141 return btool
Alper Nebi Yasakee813c82022-02-09 22:02:35 +03001142
1143 def SetUpdateHash(self, update_hash):
1144 """Set whether this entry's "hash" subnode should be updated
1145
1146 Args:
1147 update_hash: True if hash should be updated, False if not
1148 """
1149 self.update_hash = update_hash
Simon Glass81b71c32022-02-08 11:50:00 -07001150
Simon Glass72e423c2022-03-05 20:19:05 -07001151 def collect_contents_to_file(self, entries, prefix, fake_size=0):
Simon Glass81b71c32022-02-08 11:50:00 -07001152 """Put the contents of a list of entries into a file
1153
1154 Args:
1155 entries (list of Entry): Entries to collect
1156 prefix (str): Filename prefix of file to write to
Simon Glass72e423c2022-03-05 20:19:05 -07001157 fake_size (int): Size of fake file to create if needed
Simon Glass81b71c32022-02-08 11:50:00 -07001158
1159 If any entry does not have contents yet, this function returns False
1160 for the data.
1161
1162 Returns:
1163 Tuple:
Simon Glass6d427c42022-03-05 20:18:58 -07001164 bytes: Concatenated data from all the entries (or None)
1165 str: Filename of file written (or None if no data)
1166 str: Unique portion of filename (or None if no data)
Simon Glass81b71c32022-02-08 11:50:00 -07001167 """
1168 data = b''
1169 for entry in entries:
1170 # First get the input data and put it in a file. If not available,
1171 # try later.
Simon Glass72e423c2022-03-05 20:19:05 -07001172 if not entry.ObtainContents(fake_size=fake_size):
Simon Glass6d427c42022-03-05 20:18:58 -07001173 return None, None, None
Simon Glass81b71c32022-02-08 11:50:00 -07001174 data += entry.GetData()
1175 uniq = self.GetUniqueName()
1176 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1177 tools.write_file(fname, data)
1178 return data, fname, uniq
Simon Glass7960a0a2022-08-07 09:46:46 -06001179
1180 @classmethod
1181 def create_fake_dir(cls):
1182 """Create the directory for fake files"""
1183 cls.fake_dir = tools.get_output_filename('binman-fake')
1184 if not os.path.exists(cls.fake_dir):
1185 os.mkdir(cls.fake_dir)
1186 tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")