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