blob: 786c959911fb5c46cd961283eaf3c59c0316b2df [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass2574ef62016-11-25 20:15:51 -07002# Copyright (c) 2016 Google, Inc
3#
Simon Glass2574ef62016-11-25 20:15:51 -07004# Base class for all entries
5#
6
Simon Glass91710b32018-07-17 13:25:32 -06007from collections import namedtuple
Simon Glass7ccca832019-10-31 07:42:59 -06008import importlib
Simon Glass691198c2018-06-01 09:38:15 -06009import os
Simon Glass7a602fd2022-01-12 13:10:36 -070010import pathlib
Simon Glass691198c2018-06-01 09:38:15 -060011import sys
Simon Glass29aa7362018-09-14 04:57:19 -060012
Simon Glass4eae9252022-01-09 20:13:50 -070013from binman import bintool
Simon Glass3ac7d832022-01-09 20:14:03 -070014from binman import comp_util
Simon Glassc585dd42020-04-17 18:09:03 -060015from dtoc import fdt_util
Simon Glassa997ea52020-04-17 18:09:04 -060016from patman import tools
Simon Glass80025522022-01-29 14:14:04 -070017from patman.tools import to_hex, to_hex_size
Simon Glassa997ea52020-04-17 18:09:04 -060018from patman import tout
Simon Glass2574ef62016-11-25 20:15:51 -070019
20modules = {}
21
Simon Glass2a0fa982022-02-11 13:23:21 -070022# This is imported if needed
23state = None
Simon Glass91710b32018-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 Glass6b156f82019-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 Glass91710b32018-07-17 13:25:32 -060033
Simon Glass2574ef62016-11-25 20:15:51 -070034class Entry(object):
Simon Glassad5a7712018-06-01 09:38:14 -060035 """An Entry in the section
Simon Glass2574ef62016-11-25 20:15:51 -070036
37 An entry corresponds to a single node in the device-tree description
Simon Glassad5a7712018-06-01 09:38:14 -060038 of the section. Each entry ends up being a part of the final section.
Simon Glass2574ef62016-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 Glass3a9a2b82018-07-17 13:25:28 -060046 section: Section object containing this entry
Simon Glass2574ef62016-11-25 20:15:51 -070047 node: The node that created this entry
Simon Glasse8561af2018-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 Glass2574ef62016-11-25 20:15:51 -070050 size: Entry size in bytes, None if not known
Simon Glass1fdb4872019-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 Glassaa2fcf92019-07-08 14:25:30 -060053 uncomp_size: Size of uncompressed data in bytes, if the entry is
54 compressed, else None
Simon Glass2574ef62016-11-25 20:15:51 -070055 contents_size: Size of contents in bytes, 0 by default
Simon Glassafb9caa2020-10-26 17:40:10 -060056 align: Entry start offset alignment relative to the start of the
57 containing section, or None
Simon Glass2574ef62016-11-25 20:15:51 -070058 align_size: Entry size alignment, or None
Simon Glassafb9caa2020-10-26 17:40:10 -060059 align_end: Entry end offset alignment relative to the start of the
60 containing section, or None
Simon Glassd12599d2020-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 Glass789b34402020-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 Glassaa2fcf92019-07-08 14:25:30 -060072 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glasse61b6f62019-07-08 14:25:37 -060073 orig_offset: Original offset value read from node
74 orig_size: Original size value read from node
Simon Glassb8f90372020-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 Thiery6d451362022-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 Glassb8f90372020-09-01 05:13:57 -060080 external: True if this entry contains an external binary blob
Simon Glass4eae9252022-01-09 20:13:50 -070081 bintools: Bintools used by this entry (only populated for Image)
Simon Glass66152ce2022-01-09 20:14:09 -070082 missing_bintools: List of missing bintools for this entry
Alper Nebi Yasak1e4ffd82022-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
Simon Glass2574ef62016-11-25 20:15:51 -070085 """
Simon Glass2c360cf2019-07-20 12:23:45 -060086 def __init__(self, section, etype, node, name_prefix=''):
Simon Glassb9ba4e02019-08-24 07:22:44 -060087 # Put this here to allow entry-docs and help to work without libfdt
88 global state
Simon Glassc585dd42020-04-17 18:09:03 -060089 from binman import state
Simon Glassb9ba4e02019-08-24 07:22:44 -060090
Simon Glassad5a7712018-06-01 09:38:14 -060091 self.section = section
Simon Glass2574ef62016-11-25 20:15:51 -070092 self.etype = etype
93 self._node = node
Simon Glass3b78d532018-06-01 09:38:21 -060094 self.name = node and (name_prefix + node.name) or 'none'
Simon Glasse8561af2018-08-01 15:22:37 -060095 self.offset = None
Simon Glass2574ef62016-11-25 20:15:51 -070096 self.size = None
Simon Glass1fdb4872019-10-31 07:43:02 -060097 self.pre_reset_size = None
Simon Glassaa2fcf92019-07-08 14:25:30 -060098 self.uncomp_size = None
Simon Glass5c350162018-07-17 13:25:47 -060099 self.data = None
Simon Glass789b34402020-10-26 17:40:15 -0600100 self.uncomp_data = None
Simon Glass2574ef62016-11-25 20:15:51 -0700101 self.contents_size = 0
102 self.align = None
103 self.align_size = None
104 self.align_end = None
105 self.pad_before = 0
106 self.pad_after = 0
Simon Glasse8561af2018-08-01 15:22:37 -0600107 self.offset_unset = False
Simon Glass9dcc8612018-08-01 15:22:42 -0600108 self.image_pos = None
Simon Glassdd156a42022-03-05 20:18:59 -0700109 self.extend_size = False
Simon Glassaa2fcf92019-07-08 14:25:30 -0600110 self.compress = 'none'
Simon Glassa003cd32020-07-09 18:39:40 -0600111 self.missing = False
Heiko Thiery6d451362022-01-06 11:49:41 +0100112 self.faked = False
Simon Glassb8f90372020-09-01 05:13:57 -0600113 self.external = False
114 self.allow_missing = False
Heiko Thiery6d451362022-01-06 11:49:41 +0100115 self.allow_fake = False
Simon Glass4eae9252022-01-09 20:13:50 -0700116 self.bintools = {}
Simon Glass66152ce2022-01-09 20:14:09 -0700117 self.missing_bintools = []
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300118 self.update_hash = True
Simon Glass2574ef62016-11-25 20:15:51 -0700119
120 @staticmethod
Simon Glassb9028bc2021-11-23 21:09:49 -0700121 def FindEntryClass(etype, expanded):
Simon Glass969616c2018-07-17 13:25:36 -0600122 """Look up the entry class for a node.
Simon Glass2574ef62016-11-25 20:15:51 -0700123
124 Args:
Simon Glass969616c2018-07-17 13:25:36 -0600125 node_node: Path name of Node object containing information about
126 the entry to create (used for errors)
127 etype: Entry type to use
Simon Glass2f859412021-03-18 20:25:04 +1300128 expanded: Use the expanded version of etype
Simon Glass2574ef62016-11-25 20:15:51 -0700129
130 Returns:
Simon Glass2f859412021-03-18 20:25:04 +1300131 The entry class object if found, else None if not found and expanded
Simon Glassb9028bc2021-11-23 21:09:49 -0700132 is True, else a tuple:
133 module name that could not be found
134 exception received
Simon Glass2574ef62016-11-25 20:15:51 -0700135 """
Simon Glasse76a3e62018-06-01 09:38:11 -0600136 # Convert something like 'u-boot@0' to 'u_boot' since we are only
137 # interested in the type.
Simon Glass2574ef62016-11-25 20:15:51 -0700138 module_name = etype.replace('-', '_')
Simon Glass2f859412021-03-18 20:25:04 +1300139
Simon Glasse76a3e62018-06-01 09:38:11 -0600140 if '@' in module_name:
141 module_name = module_name.split('@')[0]
Simon Glass2f859412021-03-18 20:25:04 +1300142 if expanded:
143 module_name += '_expanded'
Simon Glass2574ef62016-11-25 20:15:51 -0700144 module = modules.get(module_name)
145
Simon Glass691198c2018-06-01 09:38:15 -0600146 # Also allow entry-type modules to be brought in from the etype directory.
147
Simon Glass2574ef62016-11-25 20:15:51 -0700148 # Import the module if we have not already done so.
149 if not module:
150 try:
Simon Glassc585dd42020-04-17 18:09:03 -0600151 module = importlib.import_module('binman.etype.' + module_name)
Simon Glass969616c2018-07-17 13:25:36 -0600152 except ImportError as e:
Simon Glass2f859412021-03-18 20:25:04 +1300153 if expanded:
154 return None
Simon Glassb9028bc2021-11-23 21:09:49 -0700155 return module_name, e
Simon Glass2574ef62016-11-25 20:15:51 -0700156 modules[module_name] = module
157
Simon Glass969616c2018-07-17 13:25:36 -0600158 # Look up the expected class name
159 return getattr(module, 'Entry_%s' % module_name)
160
161 @staticmethod
Simon Glassb9028bc2021-11-23 21:09:49 -0700162 def Lookup(node_path, etype, expanded, missing_etype=False):
163 """Look up the entry class for a node.
164
165 Args:
166 node_node (str): Path name of Node object containing information
167 about the entry to create (used for errors)
168 etype (str): Entry type to use
169 expanded (bool): Use the expanded version of etype
170 missing_etype (bool): True to default to a blob etype if the
171 requested etype is not found
172
173 Returns:
174 The entry class object if found, else None if not found and expanded
175 is True
176
177 Raise:
178 ValueError if expanded is False and the class is not found
179 """
180 # Convert something like 'u-boot@0' to 'u_boot' since we are only
181 # interested in the type.
182 cls = Entry.FindEntryClass(etype, expanded)
183 if cls is None:
184 return None
185 elif isinstance(cls, tuple):
186 if missing_etype:
187 cls = Entry.FindEntryClass('blob', False)
188 if isinstance(cls, tuple): # This should not fail
189 module_name, e = cls
190 raise ValueError(
191 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
192 (etype, node_path, module_name, e))
193 return cls
194
195 @staticmethod
196 def Create(section, node, etype=None, expanded=False, missing_etype=False):
Simon Glass969616c2018-07-17 13:25:36 -0600197 """Create a new entry for a node.
198
199 Args:
Simon Glassb9028bc2021-11-23 21:09:49 -0700200 section (entry_Section): Section object containing this node
201 node (Node): Node object containing information about the entry to
202 create
203 etype (str): Entry type to use, or None to work it out (used for
204 tests)
205 expanded (bool): Use the expanded version of etype
206 missing_etype (bool): True to default to a blob etype if the
207 requested etype is not found
Simon Glass969616c2018-07-17 13:25:36 -0600208
209 Returns:
210 A new Entry object of the correct type (a subclass of Entry)
211 """
212 if not etype:
213 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glassb9028bc2021-11-23 21:09:49 -0700214 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
Simon Glass2f859412021-03-18 20:25:04 +1300215 if obj and expanded:
216 # Check whether to use the expanded entry
217 new_etype = etype + '-expanded'
Simon Glass7098b7f2021-03-21 18:24:30 +1300218 can_expand = not fdt_util.GetBool(node, 'no-expanded')
219 if can_expand and obj.UseExpanded(node, etype, new_etype):
Simon Glass2f859412021-03-18 20:25:04 +1300220 etype = new_etype
221 else:
222 obj = None
223 if not obj:
Simon Glassb9028bc2021-11-23 21:09:49 -0700224 obj = Entry.Lookup(node.path, etype, False, missing_etype)
Simon Glass969616c2018-07-17 13:25:36 -0600225
Simon Glass2574ef62016-11-25 20:15:51 -0700226 # Call its constructor to get the object we want.
Simon Glassad5a7712018-06-01 09:38:14 -0600227 return obj(section, etype, node)
Simon Glass2574ef62016-11-25 20:15:51 -0700228
229 def ReadNode(self):
230 """Read entry information from the node
231
Simon Glass2c360cf2019-07-20 12:23:45 -0600232 This must be called as the first thing after the Entry is created.
233
Simon Glass2574ef62016-11-25 20:15:51 -0700234 This reads all the fields we recognise from the node, ready for use.
235 """
Simon Glass24b97442018-07-17 13:25:51 -0600236 if 'pos' in self._node.props:
237 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glassdd156a42022-03-05 20:18:59 -0700238 if 'expand-size' in self._node.props:
239 self.Raise("Please use 'extend-size' instead of 'expand-size'")
Simon Glasse8561af2018-08-01 15:22:37 -0600240 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glass2574ef62016-11-25 20:15:51 -0700241 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glassfb30e292019-07-20 12:23:51 -0600242 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
243 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
244 if self.GetImage().copy_to_orig:
245 self.orig_offset = self.offset
246 self.orig_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600247
Simon Glassb8424fa2019-07-08 14:25:46 -0600248 # These should not be set in input files, but are set in an FDT map,
249 # which is also read by this code.
250 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
251 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
252
Simon Glass2574ef62016-11-25 20:15:51 -0700253 self.align = fdt_util.GetInt(self._node, 'align')
Simon Glass80025522022-01-29 14:14:04 -0700254 if tools.not_power_of_two(self.align):
Simon Glass2574ef62016-11-25 20:15:51 -0700255 raise ValueError("Node '%s': Alignment %s must be a power of two" %
256 (self._node.path, self.align))
Simon Glassf427c5f2021-03-21 18:24:33 +1300257 if self.section and self.align is None:
258 self.align = self.section.align_default
Simon Glass2574ef62016-11-25 20:15:51 -0700259 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
260 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
261 self.align_size = fdt_util.GetInt(self._node, 'align-size')
Simon Glass80025522022-01-29 14:14:04 -0700262 if tools.not_power_of_two(self.align_size):
Simon Glass39dd2152019-07-08 14:25:47 -0600263 self.Raise("Alignment size %s must be a power of two" %
264 self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700265 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glasse8561af2018-08-01 15:22:37 -0600266 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassdd156a42022-03-05 20:18:59 -0700267 self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
Simon Glassa820af72020-09-06 10:39:09 -0600268 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glass2574ef62016-11-25 20:15:51 -0700269
Simon Glassa1301a22020-10-26 17:40:06 -0600270 # This is only supported by blobs and sections at present
271 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
272
Simon Glass3732ec32018-09-14 04:57:18 -0600273 def GetDefaultFilename(self):
274 return None
275
Simon Glass267112e2019-07-20 12:23:28 -0600276 def GetFdts(self):
277 """Get the device trees used by this entry
Simon Glass0c9d5b52018-09-14 04:57:22 -0600278
279 Returns:
Simon Glass267112e2019-07-20 12:23:28 -0600280 Empty dict, if this entry is not a .dtb, otherwise:
281 Dict:
282 key: Filename from this entry (without the path)
Simon Glass684a4f12019-07-20 12:23:31 -0600283 value: Tuple:
Simon Glass8235dd82021-03-18 20:25:02 +1300284 Entry object for this dtb
Simon Glass684a4f12019-07-20 12:23:31 -0600285 Filename of file containing this dtb
Simon Glass0c9d5b52018-09-14 04:57:22 -0600286 """
Simon Glass267112e2019-07-20 12:23:28 -0600287 return {}
Simon Glass0c9d5b52018-09-14 04:57:22 -0600288
Simon Glassf86ddad2022-03-05 20:19:00 -0700289 def gen_entries(self):
290 """Allow entries to generate other entries
Simon Glassfcb2a7c2021-03-18 20:24:52 +1300291
292 Some entries generate subnodes automatically, from which sub-entries
293 are then created. This method allows those to be added to the binman
294 definition for the current image. An entry which implements this method
295 should call state.AddSubnode() to add a subnode and can add properties
296 with state.AddString(), etc.
297
298 An example is 'files', which produces a section containing a list of
299 files.
300 """
Simon Glassac6328c2018-09-14 04:57:28 -0600301 pass
302
Simon Glassacd6c6e2020-10-26 17:40:17 -0600303 def AddMissingProperties(self, have_image_pos):
304 """Add new properties to the device tree as needed for this entry
305
306 Args:
307 have_image_pos: True if this entry has an image position. This can
308 be False if its parent section is compressed, since compression
309 groups all entries together into a compressed block of data,
310 obscuring the start of each individual child entry
311 """
312 for prop in ['offset', 'size']:
Simon Glasse22f8fa2018-07-06 10:27:41 -0600313 if not prop in self._node.props:
Simon Glassc8135dc2018-09-14 04:57:21 -0600314 state.AddZeroProp(self._node, prop)
Simon Glassacd6c6e2020-10-26 17:40:17 -0600315 if have_image_pos and 'image-pos' not in self._node.props:
316 state.AddZeroProp(self._node, 'image-pos')
Simon Glassfb30e292019-07-20 12:23:51 -0600317 if self.GetImage().allow_repack:
318 if self.orig_offset is not None:
319 state.AddZeroProp(self._node, 'orig-offset', True)
320 if self.orig_size is not None:
321 state.AddZeroProp(self._node, 'orig-size', True)
322
Simon Glassaa2fcf92019-07-08 14:25:30 -0600323 if self.compress != 'none':
324 state.AddZeroProp(self._node, 'uncomp-size')
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300325
326 if self.update_hash:
327 err = state.CheckAddHashProp(self._node)
328 if err:
329 self.Raise(err)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600330
331 def SetCalculatedProperties(self):
332 """Set the value of device-tree properties calculated by binman"""
Simon Glassc8135dc2018-09-14 04:57:21 -0600333 state.SetInt(self._node, 'offset', self.offset)
334 state.SetInt(self._node, 'size', self.size)
Simon Glass39dd2152019-07-08 14:25:47 -0600335 base = self.section.GetRootSkipAtStart() if self.section else 0
Simon Glassacd6c6e2020-10-26 17:40:17 -0600336 if self.image_pos is not None:
Simon Glasseb943b12020-11-02 12:55:44 -0700337 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glassfb30e292019-07-20 12:23:51 -0600338 if self.GetImage().allow_repack:
339 if self.orig_offset is not None:
340 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
341 if self.orig_size is not None:
342 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glassaa2fcf92019-07-08 14:25:30 -0600343 if self.uncomp_size is not None:
344 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +0300345
346 if self.update_hash:
347 state.CheckSetHashValue(self._node, self.GetData)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600348
Simon Glass92307732018-07-06 10:27:40 -0600349 def ProcessFdt(self, fdt):
Simon Glasse219aa42018-09-14 04:57:24 -0600350 """Allow entries to adjust the device tree
351
352 Some entries need to adjust the device tree for their purposes. This
353 may involve adding or deleting properties.
354
355 Returns:
356 True if processing is complete
357 False if processing could not be completed due to a dependency.
358 This will cause the entry to be retried after others have been
359 called
360 """
Simon Glass92307732018-07-06 10:27:40 -0600361 return True
362
Simon Glass3b78d532018-06-01 09:38:21 -0600363 def SetPrefix(self, prefix):
364 """Set the name prefix for a node
365
366 Args:
367 prefix: Prefix to set, or '' to not use a prefix
368 """
369 if prefix:
370 self.name = prefix + self.name
371
Simon Glass2e1169f2018-07-06 10:27:19 -0600372 def SetContents(self, data):
373 """Set the contents of an entry
374
375 This sets both the data and content_size properties
376
377 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600378 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600379 """
380 self.data = data
381 self.contents_size = len(self.data)
382
383 def ProcessContentsUpdate(self, data):
Simon Glassd17dfea2019-07-08 14:25:33 -0600384 """Update the contents of an entry, after the size is fixed
Simon Glass2e1169f2018-07-06 10:27:19 -0600385
Simon Glassec849852019-07-08 14:25:35 -0600386 This checks that the new data is the same size as the old. If the size
387 has changed, this triggers a re-run of the packing algorithm.
Simon Glass2e1169f2018-07-06 10:27:19 -0600388
389 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600390 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600391
392 Raises:
393 ValueError if the new data size is not the same as the old
394 """
Simon Glassec849852019-07-08 14:25:35 -0600395 size_ok = True
Simon Glasse61b6f62019-07-08 14:25:37 -0600396 new_size = len(data)
Simon Glass9d8ee322019-07-20 12:23:58 -0600397 if state.AllowEntryExpansion() and new_size > self.contents_size:
398 # self.data will indicate the new size needed
399 size_ok = False
400 elif state.AllowEntryContraction() and new_size < self.contents_size:
401 size_ok = False
402
403 # If not allowed to change, try to deal with it or give up
404 if size_ok:
Simon Glasse61b6f62019-07-08 14:25:37 -0600405 if new_size > self.contents_size:
Simon Glass9d8ee322019-07-20 12:23:58 -0600406 self.Raise('Cannot update entry size from %d to %d' %
407 (self.contents_size, new_size))
408
409 # Don't let the data shrink. Pad it if necessary
410 if size_ok and new_size < self.contents_size:
Simon Glass80025522022-01-29 14:14:04 -0700411 data += tools.get_bytes(0, self.contents_size - new_size)
Simon Glass9d8ee322019-07-20 12:23:58 -0600412
413 if not size_ok:
Simon Glass011f1b32022-01-29 14:14:15 -0700414 tout.debug("Entry '%s' size change from %s to %s" % (
Simon Glass80025522022-01-29 14:14:04 -0700415 self._node.path, to_hex(self.contents_size),
416 to_hex(new_size)))
Simon Glass2e1169f2018-07-06 10:27:19 -0600417 self.SetContents(data)
Simon Glassec849852019-07-08 14:25:35 -0600418 return size_ok
Simon Glass2e1169f2018-07-06 10:27:19 -0600419
Simon Glass2574ef62016-11-25 20:15:51 -0700420 def ObtainContents(self):
421 """Figure out the contents of an entry.
422
423 Returns:
424 True if the contents were found, False if another call is needed
425 after the other entries are processed.
426 """
427 # No contents by default: subclasses can implement this
428 return True
429
Simon Glasse61b6f62019-07-08 14:25:37 -0600430 def ResetForPack(self):
431 """Reset offset/size fields so that packing can be done again"""
Simon Glassb6dff4c2019-07-20 12:23:36 -0600432 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
Simon Glass80025522022-01-29 14:14:04 -0700433 (to_hex(self.offset), to_hex(self.orig_offset),
434 to_hex(self.size), to_hex(self.orig_size)))
Simon Glass1fdb4872019-10-31 07:43:02 -0600435 self.pre_reset_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600436 self.offset = self.orig_offset
437 self.size = self.orig_size
438
Simon Glasse8561af2018-08-01 15:22:37 -0600439 def Pack(self, offset):
Simon Glassad5a7712018-06-01 09:38:14 -0600440 """Figure out how to pack the entry into the section
Simon Glass2574ef62016-11-25 20:15:51 -0700441
442 Most of the time the entries are not fully specified. There may be
443 an alignment but no size. In that case we take the size from the
444 contents of the entry.
445
Simon Glasse8561af2018-08-01 15:22:37 -0600446 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glass2574ef62016-11-25 20:15:51 -0700447
Simon Glasse8561af2018-08-01 15:22:37 -0600448 Once this function is complete, both the offset and size of the
Simon Glass2574ef62016-11-25 20:15:51 -0700449 entry will be know.
450
451 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600452 Current section offset pointer
Simon Glass2574ef62016-11-25 20:15:51 -0700453
454 Returns:
Simon Glasse8561af2018-08-01 15:22:37 -0600455 New section offset pointer (after this entry)
Simon Glass2574ef62016-11-25 20:15:51 -0700456 """
Simon Glassb6dff4c2019-07-20 12:23:36 -0600457 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
Simon Glass80025522022-01-29 14:14:04 -0700458 (to_hex(self.offset), to_hex(self.size),
Simon Glassb6dff4c2019-07-20 12:23:36 -0600459 self.contents_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600460 if self.offset is None:
461 if self.offset_unset:
462 self.Raise('No offset set with offset-unset: should another '
463 'entry provide this correct offset?')
Simon Glass80025522022-01-29 14:14:04 -0700464 self.offset = tools.align(offset, self.align)
Simon Glass2574ef62016-11-25 20:15:51 -0700465 needed = self.pad_before + self.contents_size + self.pad_after
Simon Glass80025522022-01-29 14:14:04 -0700466 needed = tools.align(needed, self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700467 size = self.size
468 if not size:
469 size = needed
Simon Glasse8561af2018-08-01 15:22:37 -0600470 new_offset = self.offset + size
Simon Glass80025522022-01-29 14:14:04 -0700471 aligned_offset = tools.align(new_offset, self.align_end)
Simon Glasse8561af2018-08-01 15:22:37 -0600472 if aligned_offset != new_offset:
473 size = aligned_offset - self.offset
474 new_offset = aligned_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700475
476 if not self.size:
477 self.size = size
478
479 if self.size < needed:
480 self.Raise("Entry contents size is %#x (%d) but entry size is "
481 "%#x (%d)" % (needed, needed, self.size, self.size))
482 # Check that the alignment is correct. It could be wrong if the
Simon Glasse8561af2018-08-01 15:22:37 -0600483 # and offset or size values were provided (i.e. not calculated), but
Simon Glass2574ef62016-11-25 20:15:51 -0700484 # conflict with the provided alignment values
Simon Glass80025522022-01-29 14:14:04 -0700485 if self.size != tools.align(self.size, self.align_size):
Simon Glass2574ef62016-11-25 20:15:51 -0700486 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
487 (self.size, self.size, self.align_size, self.align_size))
Simon Glass80025522022-01-29 14:14:04 -0700488 if self.offset != tools.align(self.offset, self.align):
Simon Glasse8561af2018-08-01 15:22:37 -0600489 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
490 (self.offset, self.offset, self.align, self.align))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600491 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
492 (self.offset, self.size, self.contents_size, new_offset))
Simon Glass2574ef62016-11-25 20:15:51 -0700493
Simon Glasse8561af2018-08-01 15:22:37 -0600494 return new_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700495
496 def Raise(self, msg):
497 """Convenience function to raise an error referencing a node"""
498 raise ValueError("Node '%s': %s" % (self._node.path, msg))
499
Simon Glasse1915782021-03-21 18:24:31 +1300500 def Info(self, msg):
501 """Convenience function to log info referencing a node"""
502 tag = "Info '%s'" % self._node.path
Simon Glass011f1b32022-01-29 14:14:15 -0700503 tout.detail('%30s: %s' % (tag, msg))
Simon Glasse1915782021-03-21 18:24:31 +1300504
Simon Glassb6dff4c2019-07-20 12:23:36 -0600505 def Detail(self, msg):
506 """Convenience function to log detail referencing a node"""
507 tag = "Node '%s'" % self._node.path
Simon Glass011f1b32022-01-29 14:14:15 -0700508 tout.detail('%30s: %s' % (tag, msg))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600509
Simon Glass91710b32018-07-17 13:25:32 -0600510 def GetEntryArgsOrProps(self, props, required=False):
511 """Return the values of a set of properties
512
513 Args:
514 props: List of EntryArg objects
515
516 Raises:
517 ValueError if a property is not found
518 """
519 values = []
520 missing = []
521 for prop in props:
522 python_prop = prop.name.replace('-', '_')
523 if hasattr(self, python_prop):
524 value = getattr(self, python_prop)
525 else:
526 value = None
527 if value is None:
528 value = self.GetArg(prop.name, prop.datatype)
529 if value is None and required:
530 missing.append(prop.name)
531 values.append(value)
532 if missing:
Simon Glass3fb25402021-01-06 21:35:16 -0700533 self.GetImage().MissingArgs(self, missing)
Simon Glass91710b32018-07-17 13:25:32 -0600534 return values
535
Simon Glass2574ef62016-11-25 20:15:51 -0700536 def GetPath(self):
537 """Get the path of a node
538
539 Returns:
540 Full path of the node for this entry
541 """
542 return self._node.path
543
Simon Glass27a7f772021-03-21 18:24:32 +1300544 def GetData(self, required=True):
Simon Glass72eeff12020-10-26 17:40:16 -0600545 """Get the contents of an entry
546
Simon Glass27a7f772021-03-21 18:24:32 +1300547 Args:
548 required: True if the data must be present, False if it is OK to
549 return None
550
Simon Glass72eeff12020-10-26 17:40:16 -0600551 Returns:
552 bytes content of the entry, excluding any padding. If the entry is
553 compressed, the compressed data is returned
554 """
Simon Glass80025522022-01-29 14:14:04 -0700555 self.Detail('GetData: size %s' % to_hex_size(self.data))
Simon Glass2574ef62016-11-25 20:15:51 -0700556 return self.data
557
Simon Glasse17220f2020-11-02 12:55:43 -0700558 def GetPaddedData(self, data=None):
559 """Get the data for an entry including any padding
560
561 Gets the entry data and uses its section's pad-byte value to add padding
562 before and after as defined by the pad-before and pad-after properties.
563
564 This does not consider alignment.
565
566 Returns:
567 Contents of the entry along with any pad bytes before and
568 after it (bytes)
569 """
570 if data is None:
571 data = self.GetData()
572 return self.section.GetPaddedDataForEntry(self, data)
573
Simon Glasse8561af2018-08-01 15:22:37 -0600574 def GetOffsets(self):
Simon Glass224bc662019-07-08 13:18:30 -0600575 """Get the offsets for siblings
576
577 Some entry types can contain information about the position or size of
578 other entries. An example of this is the Intel Flash Descriptor, which
579 knows where the Intel Management Engine section should go.
580
581 If this entry knows about the position of other entries, it can specify
582 this by returning values here
583
584 Returns:
585 Dict:
586 key: Entry type
587 value: List containing position and size of the given entry
Simon Glassed365eb2019-07-08 13:18:39 -0600588 type. Either can be None if not known
Simon Glass224bc662019-07-08 13:18:30 -0600589 """
Simon Glass2574ef62016-11-25 20:15:51 -0700590 return {}
591
Simon Glassed365eb2019-07-08 13:18:39 -0600592 def SetOffsetSize(self, offset, size):
593 """Set the offset and/or size of an entry
594
595 Args:
596 offset: New offset, or None to leave alone
597 size: New size, or None to leave alone
598 """
599 if offset is not None:
600 self.offset = offset
601 if size is not None:
602 self.size = size
Simon Glass2574ef62016-11-25 20:15:51 -0700603
Simon Glass9dcc8612018-08-01 15:22:42 -0600604 def SetImagePos(self, image_pos):
605 """Set the position in the image
606
607 Args:
608 image_pos: Position of this entry in the image
609 """
610 self.image_pos = image_pos + self.offset
611
Simon Glass2574ef62016-11-25 20:15:51 -0700612 def ProcessContents(self):
Simon Glassec849852019-07-08 14:25:35 -0600613 """Do any post-packing updates of entry contents
614
615 This function should call ProcessContentsUpdate() to update the entry
616 contents, if necessary, returning its return value here.
617
618 Args:
619 data: Data to set to the contents (bytes)
620
621 Returns:
622 True if the new data size is OK, False if expansion is needed
623
624 Raises:
625 ValueError if the new data size is not the same as the old and
626 state.AllowEntryExpansion() is False
627 """
628 return True
Simon Glass4ca8e042017-11-13 18:55:01 -0700629
Simon Glass8a6f56e2018-06-01 09:38:13 -0600630 def WriteSymbols(self, section):
Simon Glass4ca8e042017-11-13 18:55:01 -0700631 """Write symbol values into binary files for access at run time
632
633 Args:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600634 section: Section containing the entry
Simon Glass4ca8e042017-11-13 18:55:01 -0700635 """
636 pass
Simon Glassa91e1152018-06-01 09:38:16 -0600637
Simon Glass55f68072020-10-26 17:40:18 -0600638 def CheckEntries(self):
Simon Glasse8561af2018-08-01 15:22:37 -0600639 """Check that the entry offsets are correct
Simon Glassa91e1152018-06-01 09:38:16 -0600640
Simon Glasse8561af2018-08-01 15:22:37 -0600641 This is used for entries which have extra offset requirements (other
Simon Glassa91e1152018-06-01 09:38:16 -0600642 than having to be fully inside their section). Sub-classes can implement
643 this function and raise if there is a problem.
644 """
645 pass
Simon Glass30732662018-06-01 09:38:20 -0600646
Simon Glass3a9a2b82018-07-17 13:25:28 -0600647 @staticmethod
Simon Glasscd817d52018-09-14 04:57:36 -0600648 def GetStr(value):
649 if value is None:
650 return '<none> '
651 return '%08x' % value
652
653 @staticmethod
Simon Glass7eca7922018-07-17 13:25:49 -0600654 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glasscd817d52018-09-14 04:57:36 -0600655 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
656 Entry.GetStr(offset), Entry.GetStr(size),
657 name), file=fd)
Simon Glass3a9a2b82018-07-17 13:25:28 -0600658
Simon Glass30732662018-06-01 09:38:20 -0600659 def WriteMap(self, fd, indent):
660 """Write a map of the entry to a .map file
661
662 Args:
663 fd: File to write the map to
664 indent: Curent indent level of map (0=none, 1=one level, etc.)
665 """
Simon Glass7eca7922018-07-17 13:25:49 -0600666 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
667 self.image_pos)
Simon Glass91710b32018-07-17 13:25:32 -0600668
Simon Glass704784b2018-07-17 13:25:38 -0600669 def GetEntries(self):
670 """Return a list of entries contained by this entry
671
672 Returns:
673 List of entries, or None if none. A normal entry has no entries
674 within it so will return None
675 """
676 return None
677
Simon Glass91710b32018-07-17 13:25:32 -0600678 def GetArg(self, name, datatype=str):
679 """Get the value of an entry argument or device-tree-node property
680
681 Some node properties can be provided as arguments to binman. First check
682 the entry arguments, and fall back to the device tree if not found
683
684 Args:
685 name: Argument name
686 datatype: Data type (str or int)
687
688 Returns:
689 Value of argument as a string or int, or None if no value
690
691 Raises:
692 ValueError if the argument cannot be converted to in
693 """
Simon Glass29aa7362018-09-14 04:57:19 -0600694 value = state.GetEntryArg(name)
Simon Glass91710b32018-07-17 13:25:32 -0600695 if value is not None:
696 if datatype == int:
697 try:
698 value = int(value)
699 except ValueError:
700 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
701 (name, value))
702 elif datatype == str:
703 pass
704 else:
705 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
706 datatype)
707 else:
708 value = fdt_util.GetDatatype(self._node, name, datatype)
709 return value
Simon Glass969616c2018-07-17 13:25:36 -0600710
711 @staticmethod
712 def WriteDocs(modules, test_missing=None):
713 """Write out documentation about the various entry types to stdout
714
715 Args:
716 modules: List of modules to include
717 test_missing: Used for testing. This is a module to report
718 as missing
719 """
720 print('''Binman Entry Documentation
721===========================
722
723This file describes the entry types supported by binman. These entry types can
724be placed in an image one by one to build up a final firmware image. It is
725fairly easy to create new entry types. Just add a new file to the 'etype'
726directory. You can use the existing entries as examples.
727
728Note that some entries are subclasses of others, using and extending their
729features to produce new behaviours.
730
731
732''')
733 modules = sorted(modules)
734
735 # Don't show the test entry
736 if '_testing' in modules:
737 modules.remove('_testing')
738 missing = []
739 for name in modules:
Simon Glass2f859412021-03-18 20:25:04 +1300740 module = Entry.Lookup('WriteDocs', name, False)
Simon Glass969616c2018-07-17 13:25:36 -0600741 docs = getattr(module, '__doc__')
742 if test_missing == name:
743 docs = None
744 if docs:
745 lines = docs.splitlines()
746 first_line = lines[0]
747 rest = [line[4:] for line in lines[1:]]
748 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
749 print(hdr)
750 print('-' * len(hdr))
751 print('\n'.join(rest))
752 print()
753 print()
754 else:
755 missing.append(name)
756
757 if missing:
758 raise ValueError('Documentation is missing for modules: %s' %
759 ', '.join(missing))
Simon Glass639505b2018-09-14 04:57:11 -0600760
761 def GetUniqueName(self):
762 """Get a unique name for a node
763
764 Returns:
765 String containing a unique name for a node, consisting of the name
766 of all ancestors (starting from within the 'binman' node) separated
767 by a dot ('.'). This can be useful for generating unique filesnames
768 in the output directory.
769 """
770 name = self.name
771 node = self._node
772 while node.parent:
773 node = node.parent
774 if node.name == 'binman':
775 break
776 name = '%s.%s' % (node.name, name)
777 return name
Simon Glassfa79a812018-09-14 04:57:29 -0600778
Simon Glassdd156a42022-03-05 20:18:59 -0700779 def extend_to_limit(self, limit):
780 """Extend an entry so that it ends at the given offset limit"""
Simon Glassfa79a812018-09-14 04:57:29 -0600781 if self.offset + self.size < limit:
782 self.size = limit - self.offset
783 # Request the contents again, since changing the size requires that
784 # the data grows. This should not fail, but check it to be sure.
785 if not self.ObtainContents():
786 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassc4056b82019-07-08 13:18:38 -0600787
788 def HasSibling(self, name):
789 """Check if there is a sibling of a given name
790
791 Returns:
792 True if there is an entry with this name in the the same section,
793 else False
794 """
795 return name in self.section.GetEntries()
Simon Glasscec34ba2019-07-08 14:25:28 -0600796
797 def GetSiblingImagePos(self, name):
798 """Return the image position of the given sibling
799
800 Returns:
801 Image position of sibling, or None if the sibling has no position,
802 or False if there is no such sibling
803 """
804 if not self.HasSibling(name):
805 return False
806 return self.section.GetEntries()[name].image_pos
Simon Glass6b156f82019-07-08 14:25:43 -0600807
808 @staticmethod
809 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
810 uncomp_size, offset, entry):
811 """Add a new entry to the entries list
812
813 Args:
814 entries: List (of EntryInfo objects) to add to
815 indent: Current indent level to add to list
816 name: Entry name (string)
817 etype: Entry type (string)
818 size: Entry size in bytes (int)
819 image_pos: Position within image in bytes (int)
820 uncomp_size: Uncompressed size if the entry uses compression, else
821 None
822 offset: Entry offset within parent in bytes (int)
823 entry: Entry object
824 """
825 entries.append(EntryInfo(indent, name, etype, size, image_pos,
826 uncomp_size, offset, entry))
827
828 def ListEntries(self, entries, indent):
829 """Add files in this entry to the list of entries
830
831 This can be overridden by subclasses which need different behaviour.
832
833 Args:
834 entries: List (of EntryInfo objects) to add to
835 indent: Current indent level to add to list
836 """
837 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
838 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glass4c613bf2019-07-08 14:25:50 -0600839
Simon Glass637958f2021-11-23 21:09:50 -0700840 def ReadData(self, decomp=True, alt_format=None):
Simon Glass4c613bf2019-07-08 14:25:50 -0600841 """Read the data for an entry from the image
842
843 This is used when the image has been read in and we want to extract the
844 data for a particular entry from that image.
845
846 Args:
847 decomp: True to decompress any compressed data before returning it;
848 False to return the raw, uncompressed data
849
850 Returns:
851 Entry data (bytes)
852 """
853 # Use True here so that we get an uncompressed section to work from,
854 # although compressed sections are currently not supported
Simon Glass011f1b32022-01-29 14:14:15 -0700855 tout.debug("ReadChildData section '%s', entry '%s'" %
Simon Glass4d8151f2019-09-25 08:56:21 -0600856 (self.section.GetPath(), self.GetPath()))
Simon Glass637958f2021-11-23 21:09:50 -0700857 data = self.section.ReadChildData(self, decomp, alt_format)
Simon Glass0cd8ace2019-07-20 12:24:04 -0600858 return data
Simon Glassaf8c45c2019-07-20 12:23:41 -0600859
Simon Glass637958f2021-11-23 21:09:50 -0700860 def ReadChildData(self, child, decomp=True, alt_format=None):
Simon Glass4d8151f2019-09-25 08:56:21 -0600861 """Read the data for a particular child entry
Simon Glass23f00472019-09-25 08:56:20 -0600862
863 This reads data from the parent and extracts the piece that relates to
864 the given child.
865
866 Args:
Simon Glass637958f2021-11-23 21:09:50 -0700867 child (Entry): Child entry to read data for (must be valid)
868 decomp (bool): True to decompress any compressed data before
869 returning it; False to return the raw, uncompressed data
870 alt_format (str): Alternative format to read in, or None
Simon Glass23f00472019-09-25 08:56:20 -0600871
872 Returns:
873 Data for the child (bytes)
874 """
875 pass
876
Simon Glassaf8c45c2019-07-20 12:23:41 -0600877 def LoadData(self, decomp=True):
878 data = self.ReadData(decomp)
Simon Glass072959a2019-07-20 12:23:50 -0600879 self.contents_size = len(data)
Simon Glassaf8c45c2019-07-20 12:23:41 -0600880 self.ProcessContentsUpdate(data)
881 self.Detail('Loaded data size %x' % len(data))
Simon Glass990b1742019-07-20 12:23:46 -0600882
Simon Glass637958f2021-11-23 21:09:50 -0700883 def GetAltFormat(self, data, alt_format):
884 """Read the data for an extry in an alternative format
885
886 Supported formats are list in the documentation for each entry. An
887 example is fdtmap which provides .
888
889 Args:
890 data (bytes): Data to convert (this should have been produced by the
891 entry)
892 alt_format (str): Format to use
893
894 """
895 pass
896
Simon Glass990b1742019-07-20 12:23:46 -0600897 def GetImage(self):
898 """Get the image containing this entry
899
900 Returns:
901 Image object containing this entry
902 """
903 return self.section.GetImage()
Simon Glass072959a2019-07-20 12:23:50 -0600904
905 def WriteData(self, data, decomp=True):
906 """Write the data to an entry in the image
907
908 This is used when the image has been read in and we want to replace the
909 data for a particular entry in that image.
910
911 The image must be re-packed and written out afterwards.
912
913 Args:
914 data: Data to replace it with
915 decomp: True to compress the data if needed, False if data is
916 already compressed so should be used as is
917
918 Returns:
919 True if the data did not result in a resize of this entry, False if
920 the entry must be resized
921 """
Simon Glass1fdb4872019-10-31 07:43:02 -0600922 if self.size is not None:
923 self.contents_size = self.size
924 else:
925 self.contents_size = self.pre_reset_size
Simon Glass072959a2019-07-20 12:23:50 -0600926 ok = self.ProcessContentsUpdate(data)
927 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glassd34af7a2019-07-20 12:24:05 -0600928 section_ok = self.section.WriteChildData(self)
929 return ok and section_ok
930
931 def WriteChildData(self, child):
932 """Handle writing the data in a child entry
933
934 This should be called on the child's parent section after the child's
Simon Glasse796f242021-11-23 11:03:44 -0700935 data has been updated. It should update any data structures needed to
936 validate that the update is successful.
Simon Glassd34af7a2019-07-20 12:24:05 -0600937
938 This base-class implementation does nothing, since the base Entry object
939 does not have any children.
940
941 Args:
942 child: Child Entry that was written
943
944 Returns:
945 True if the section could be updated successfully, False if the
Simon Glasse796f242021-11-23 11:03:44 -0700946 data is such that the section could not update
Simon Glassd34af7a2019-07-20 12:24:05 -0600947 """
948 return True
Simon Glass11453762019-07-20 12:23:55 -0600949
950 def GetSiblingOrder(self):
951 """Get the relative order of an entry amoung its siblings
952
953 Returns:
954 'start' if this entry is first among siblings, 'end' if last,
955 otherwise None
956 """
957 entries = list(self.section.GetEntries().values())
958 if entries:
959 if self == entries[0]:
960 return 'start'
961 elif self == entries[-1]:
962 return 'end'
963 return 'middle'
Simon Glass5d94cc62020-07-09 18:39:38 -0600964
965 def SetAllowMissing(self, allow_missing):
966 """Set whether a section allows missing external blobs
967
968 Args:
969 allow_missing: True if allowed, False if not allowed
970 """
971 # This is meaningless for anything other than sections
972 pass
Simon Glassa003cd32020-07-09 18:39:40 -0600973
Heiko Thiery6d451362022-01-06 11:49:41 +0100974 def SetAllowFakeBlob(self, allow_fake):
975 """Set whether a section allows to create a fake blob
976
977 Args:
978 allow_fake: True if allowed, False if not allowed
979 """
Simon Glassceb5f912022-01-09 20:13:46 -0700980 self.allow_fake = allow_fake
Heiko Thiery6d451362022-01-06 11:49:41 +0100981
Simon Glassa003cd32020-07-09 18:39:40 -0600982 def CheckMissing(self, missing_list):
983 """Check if any entries in this section have missing external blobs
984
985 If there are missing blobs, the entries are added to the list
986
987 Args:
988 missing_list: List of Entry objects to be added to
989 """
990 if self.missing:
991 missing_list.append(self)
Simon Glassb8f90372020-09-01 05:13:57 -0600992
Simon Glass7a602fd2022-01-12 13:10:36 -0700993 def check_fake_fname(self, fname):
994 """If the file is missing and the entry allows fake blobs, fake it
995
996 Sets self.faked to True if faked
997
998 Args:
999 fname (str): Filename to check
1000
1001 Returns:
1002 fname (str): Filename of faked file
1003 """
1004 if self.allow_fake and not pathlib.Path(fname).is_file():
Simon Glass80025522022-01-29 14:14:04 -07001005 outfname = tools.get_output_filename(os.path.basename(fname))
Simon Glass7a602fd2022-01-12 13:10:36 -07001006 with open(outfname, "wb") as out:
1007 out.truncate(1024)
1008 self.faked = True
1009 return outfname
1010 return fname
1011
Heiko Thiery6d451362022-01-06 11:49:41 +01001012 def CheckFakedBlobs(self, faked_blobs_list):
1013 """Check if any entries in this section have faked external blobs
1014
1015 If there are faked blobs, the entries are added to the list
1016
1017 Args:
1018 fake_blobs_list: List of Entry objects to be added to
1019 """
1020 # This is meaningless for anything other than blobs
1021 pass
1022
Simon Glassb8f90372020-09-01 05:13:57 -06001023 def GetAllowMissing(self):
1024 """Get whether a section allows missing external blobs
1025
1026 Returns:
1027 True if allowed, False if not allowed
1028 """
1029 return self.allow_missing
Simon Glassa820af72020-09-06 10:39:09 -06001030
Simon Glass66152ce2022-01-09 20:14:09 -07001031 def record_missing_bintool(self, bintool):
1032 """Record a missing bintool that was needed to produce this entry
1033
1034 Args:
1035 bintool (Bintool): Bintool that was missing
1036 """
1037 self.missing_bintools.append(bintool)
1038
1039 def check_missing_bintools(self, missing_list):
1040 """Check if any entries in this section have missing bintools
1041
1042 If there are missing bintools, these are added to the list
1043
1044 Args:
1045 missing_list: List of Bintool objects to be added to
1046 """
1047 missing_list += self.missing_bintools
1048
Simon Glassa820af72020-09-06 10:39:09 -06001049 def GetHelpTags(self):
1050 """Get the tags use for missing-blob help
1051
1052 Returns:
1053 list of possible tags, most desirable first
1054 """
1055 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glassa1301a22020-10-26 17:40:06 -06001056
1057 def CompressData(self, indata):
1058 """Compress data according to the entry's compression method
1059
1060 Args:
1061 indata: Data to compress
1062
1063 Returns:
1064 Compressed data (first word is the compressed size)
1065 """
Simon Glass789b34402020-10-26 17:40:15 -06001066 self.uncomp_data = indata
Simon Glassa1301a22020-10-26 17:40:06 -06001067 if self.compress != 'none':
1068 self.uncomp_size = len(indata)
Simon Glassdd5c14ec2022-01-09 20:14:04 -07001069 data = comp_util.compress(indata, self.compress)
Simon Glassa1301a22020-10-26 17:40:06 -06001070 return data
Simon Glass2f859412021-03-18 20:25:04 +13001071
1072 @classmethod
1073 def UseExpanded(cls, node, etype, new_etype):
1074 """Check whether to use an expanded entry type
1075
1076 This is called by Entry.Create() when it finds an expanded version of
1077 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1078 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1079 ignored.
1080
1081 Args:
1082 node: Node object containing information about the entry to
1083 create
1084 etype: Original entry type being used
1085 new_etype: New entry type proposed
1086
1087 Returns:
1088 True to use this entry type, False to use the original one
1089 """
Simon Glass011f1b32022-01-29 14:14:15 -07001090 tout.info("Node '%s': etype '%s': %s selected" %
Simon Glass2f859412021-03-18 20:25:04 +13001091 (node.path, etype, new_etype))
1092 return True
Simon Glass637958f2021-11-23 21:09:50 -07001093
1094 def CheckAltFormats(self, alt_formats):
1095 """Add any alternative formats supported by this entry type
1096
1097 Args:
1098 alt_formats (dict): Dict to add alt_formats to:
1099 key: Name of alt format
1100 value: Help text
1101 """
1102 pass
Simon Glass4eae9252022-01-09 20:13:50 -07001103
Simon Glassfff147a2022-03-05 20:19:02 -07001104 def AddBintools(self, btools):
Simon Glass4eae9252022-01-09 20:13:50 -07001105 """Add the bintools used by this entry type
1106
1107 Args:
Simon Glassfff147a2022-03-05 20:19:02 -07001108 btools (dict of Bintool):
Simon Glass4eae9252022-01-09 20:13:50 -07001109 """
1110 pass
1111
1112 @classmethod
1113 def AddBintool(self, tools, name):
1114 """Add a new bintool to the tools used by this etype
1115
1116 Args:
1117 name: Name of the tool
1118 """
1119 btool = bintool.Bintool.create(name)
1120 tools[name] = btool
1121 return btool
Alper Nebi Yasak1e4ffd82022-02-09 22:02:35 +03001122
1123 def SetUpdateHash(self, update_hash):
1124 """Set whether this entry's "hash" subnode should be updated
1125
1126 Args:
1127 update_hash: True if hash should be updated, False if not
1128 """
1129 self.update_hash = update_hash
Simon Glass6fba35c2022-02-08 11:50:00 -07001130
1131 def collect_contents_to_file(self, entries, prefix):
1132 """Put the contents of a list of entries into a file
1133
1134 Args:
1135 entries (list of Entry): Entries to collect
1136 prefix (str): Filename prefix of file to write to
1137
1138 If any entry does not have contents yet, this function returns False
1139 for the data.
1140
1141 Returns:
1142 Tuple:
Simon Glass43a98cc2022-03-05 20:18:58 -07001143 bytes: Concatenated data from all the entries (or None)
1144 str: Filename of file written (or None if no data)
1145 str: Unique portion of filename (or None if no data)
Simon Glass6fba35c2022-02-08 11:50:00 -07001146 """
1147 data = b''
1148 for entry in entries:
1149 # First get the input data and put it in a file. If not available,
1150 # try later.
1151 if not entry.ObtainContents():
Simon Glass43a98cc2022-03-05 20:18:58 -07001152 return None, None, None
Simon Glass6fba35c2022-02-08 11:50:00 -07001153 data += entry.GetData()
1154 uniq = self.GetUniqueName()
1155 fname = tools.get_output_filename(f'{prefix}.{uniq}')
1156 tools.write_file(fname, data)
1157 return data, fname, uniq