blob: 173c9131cbbaa291744ae5d9e84bf484875aaddf [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
10import sys
Simon Glass29aa7362018-09-14 04:57:19 -060011
Simon Glassc585dd42020-04-17 18:09:03 -060012from dtoc import fdt_util
Simon Glassa997ea52020-04-17 18:09:04 -060013from patman import tools
Simon Glassc585dd42020-04-17 18:09:03 -060014from patman.tools import ToHex, ToHexSize
Simon Glassa997ea52020-04-17 18:09:04 -060015from patman import tout
Simon Glass2574ef62016-11-25 20:15:51 -070016
17modules = {}
18
Simon Glass91710b32018-07-17 13:25:32 -060019
20# An argument which can be passed to entries on the command line, in lieu of
21# device-tree properties.
22EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
23
Simon Glass6b156f82019-07-08 14:25:43 -060024# Information about an entry for use when displaying summaries
25EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
26 'image_pos', 'uncomp_size', 'offset',
27 'entry'])
Simon Glass91710b32018-07-17 13:25:32 -060028
Simon Glass2574ef62016-11-25 20:15:51 -070029class Entry(object):
Simon Glassad5a7712018-06-01 09:38:14 -060030 """An Entry in the section
Simon Glass2574ef62016-11-25 20:15:51 -070031
32 An entry corresponds to a single node in the device-tree description
Simon Glassad5a7712018-06-01 09:38:14 -060033 of the section. Each entry ends up being a part of the final section.
Simon Glass2574ef62016-11-25 20:15:51 -070034 Entries can be placed either right next to each other, or with padding
35 between them. The type of the entry determines the data that is in it.
36
37 This class is not used by itself. All entry objects are subclasses of
38 Entry.
39
40 Attributes:
Simon Glass3a9a2b82018-07-17 13:25:28 -060041 section: Section object containing this entry
Simon Glass2574ef62016-11-25 20:15:51 -070042 node: The node that created this entry
Simon Glasse8561af2018-08-01 15:22:37 -060043 offset: Offset of entry within the section, None if not known yet (in
44 which case it will be calculated by Pack())
Simon Glass2574ef62016-11-25 20:15:51 -070045 size: Entry size in bytes, None if not known
Simon Glass1fdb4872019-10-31 07:43:02 -060046 pre_reset_size: size as it was before ResetForPack(). This allows us to
47 keep track of the size we started with and detect size changes
Simon Glassaa2fcf92019-07-08 14:25:30 -060048 uncomp_size: Size of uncompressed data in bytes, if the entry is
49 compressed, else None
Simon Glass2574ef62016-11-25 20:15:51 -070050 contents_size: Size of contents in bytes, 0 by default
Simon Glasse8561af2018-08-01 15:22:37 -060051 align: Entry start offset alignment, or None
Simon Glass2574ef62016-11-25 20:15:51 -070052 align_size: Entry size alignment, or None
Simon Glasse8561af2018-08-01 15:22:37 -060053 align_end: Entry end offset alignment, or None
Simon Glass2574ef62016-11-25 20:15:51 -070054 pad_before: Number of pad bytes before the contents, 0 if none
55 pad_after: Number of pad bytes after the contents, 0 if none
56 data: Contents of entry (string of bytes)
Simon Glassaa2fcf92019-07-08 14:25:30 -060057 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glasse61b6f62019-07-08 14:25:37 -060058 orig_offset: Original offset value read from node
59 orig_size: Original size value read from node
Simon Glassb8f90372020-09-01 05:13:57 -060060 missing: True if this entry is missing its contents
61 allow_missing: Allow children of this entry to be missing (used by
62 subclasses such as Entry_section)
63 external: True if this entry contains an external binary blob
Simon Glass2574ef62016-11-25 20:15:51 -070064 """
Simon Glass2c360cf2019-07-20 12:23:45 -060065 def __init__(self, section, etype, node, name_prefix=''):
Simon Glassb9ba4e02019-08-24 07:22:44 -060066 # Put this here to allow entry-docs and help to work without libfdt
67 global state
Simon Glassc585dd42020-04-17 18:09:03 -060068 from binman import state
Simon Glassb9ba4e02019-08-24 07:22:44 -060069
Simon Glassad5a7712018-06-01 09:38:14 -060070 self.section = section
Simon Glass2574ef62016-11-25 20:15:51 -070071 self.etype = etype
72 self._node = node
Simon Glass3b78d532018-06-01 09:38:21 -060073 self.name = node and (name_prefix + node.name) or 'none'
Simon Glasse8561af2018-08-01 15:22:37 -060074 self.offset = None
Simon Glass2574ef62016-11-25 20:15:51 -070075 self.size = None
Simon Glass1fdb4872019-10-31 07:43:02 -060076 self.pre_reset_size = None
Simon Glassaa2fcf92019-07-08 14:25:30 -060077 self.uncomp_size = None
Simon Glass5c350162018-07-17 13:25:47 -060078 self.data = None
Simon Glass2574ef62016-11-25 20:15:51 -070079 self.contents_size = 0
80 self.align = None
81 self.align_size = None
82 self.align_end = None
83 self.pad_before = 0
84 self.pad_after = 0
Simon Glasse8561af2018-08-01 15:22:37 -060085 self.offset_unset = False
Simon Glass9dcc8612018-08-01 15:22:42 -060086 self.image_pos = None
Simon Glassfa79a812018-09-14 04:57:29 -060087 self._expand_size = False
Simon Glassaa2fcf92019-07-08 14:25:30 -060088 self.compress = 'none'
Simon Glassa003cd32020-07-09 18:39:40 -060089 self.missing = False
Simon Glassb8f90372020-09-01 05:13:57 -060090 self.external = False
91 self.allow_missing = False
Simon Glass2574ef62016-11-25 20:15:51 -070092
93 @staticmethod
Simon Glass75502932019-07-08 14:25:31 -060094 def Lookup(node_path, etype):
Simon Glass969616c2018-07-17 13:25:36 -060095 """Look up the entry class for a node.
Simon Glass2574ef62016-11-25 20:15:51 -070096
97 Args:
Simon Glass969616c2018-07-17 13:25:36 -060098 node_node: Path name of Node object containing information about
99 the entry to create (used for errors)
100 etype: Entry type to use
Simon Glass2574ef62016-11-25 20:15:51 -0700101
102 Returns:
Simon Glass969616c2018-07-17 13:25:36 -0600103 The entry class object if found, else None
Simon Glass2574ef62016-11-25 20:15:51 -0700104 """
Simon Glasse76a3e62018-06-01 09:38:11 -0600105 # Convert something like 'u-boot@0' to 'u_boot' since we are only
106 # interested in the type.
Simon Glass2574ef62016-11-25 20:15:51 -0700107 module_name = etype.replace('-', '_')
Simon Glasse76a3e62018-06-01 09:38:11 -0600108 if '@' in module_name:
109 module_name = module_name.split('@')[0]
Simon Glass2574ef62016-11-25 20:15:51 -0700110 module = modules.get(module_name)
111
Simon Glass691198c2018-06-01 09:38:15 -0600112 # Also allow entry-type modules to be brought in from the etype directory.
113
Simon Glass2574ef62016-11-25 20:15:51 -0700114 # Import the module if we have not already done so.
115 if not module:
116 try:
Simon Glassc585dd42020-04-17 18:09:03 -0600117 module = importlib.import_module('binman.etype.' + module_name)
Simon Glass969616c2018-07-17 13:25:36 -0600118 except ImportError as e:
119 raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
120 (etype, node_path, module_name, e))
Simon Glass2574ef62016-11-25 20:15:51 -0700121 modules[module_name] = module
122
Simon Glass969616c2018-07-17 13:25:36 -0600123 # Look up the expected class name
124 return getattr(module, 'Entry_%s' % module_name)
125
126 @staticmethod
127 def Create(section, node, etype=None):
128 """Create a new entry for a node.
129
130 Args:
131 section: Section object containing this node
132 node: Node object containing information about the entry to
133 create
134 etype: Entry type to use, or None to work it out (used for tests)
135
136 Returns:
137 A new Entry object of the correct type (a subclass of Entry)
138 """
139 if not etype:
140 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glass75502932019-07-08 14:25:31 -0600141 obj = Entry.Lookup(node.path, etype)
Simon Glass969616c2018-07-17 13:25:36 -0600142
Simon Glass2574ef62016-11-25 20:15:51 -0700143 # Call its constructor to get the object we want.
Simon Glassad5a7712018-06-01 09:38:14 -0600144 return obj(section, etype, node)
Simon Glass2574ef62016-11-25 20:15:51 -0700145
146 def ReadNode(self):
147 """Read entry information from the node
148
Simon Glass2c360cf2019-07-20 12:23:45 -0600149 This must be called as the first thing after the Entry is created.
150
Simon Glass2574ef62016-11-25 20:15:51 -0700151 This reads all the fields we recognise from the node, ready for use.
152 """
Simon Glass24b97442018-07-17 13:25:51 -0600153 if 'pos' in self._node.props:
154 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glasse8561af2018-08-01 15:22:37 -0600155 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glass2574ef62016-11-25 20:15:51 -0700156 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glassfb30e292019-07-20 12:23:51 -0600157 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
158 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
159 if self.GetImage().copy_to_orig:
160 self.orig_offset = self.offset
161 self.orig_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600162
Simon Glassb8424fa2019-07-08 14:25:46 -0600163 # These should not be set in input files, but are set in an FDT map,
164 # which is also read by this code.
165 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
166 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
167
Simon Glass2574ef62016-11-25 20:15:51 -0700168 self.align = fdt_util.GetInt(self._node, 'align')
169 if tools.NotPowerOfTwo(self.align):
170 raise ValueError("Node '%s': Alignment %s must be a power of two" %
171 (self._node.path, self.align))
172 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
173 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
174 self.align_size = fdt_util.GetInt(self._node, 'align-size')
175 if tools.NotPowerOfTwo(self.align_size):
Simon Glass39dd2152019-07-08 14:25:47 -0600176 self.Raise("Alignment size %s must be a power of two" %
177 self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700178 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glasse8561af2018-08-01 15:22:37 -0600179 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassfa79a812018-09-14 04:57:29 -0600180 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
Simon Glassa820af72020-09-06 10:39:09 -0600181 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
Simon Glass2574ef62016-11-25 20:15:51 -0700182
Simon Glassa1301a22020-10-26 17:40:06 -0600183 # This is only supported by blobs and sections at present
184 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
185
Simon Glass3732ec32018-09-14 04:57:18 -0600186 def GetDefaultFilename(self):
187 return None
188
Simon Glass267112e2019-07-20 12:23:28 -0600189 def GetFdts(self):
190 """Get the device trees used by this entry
Simon Glass0c9d5b52018-09-14 04:57:22 -0600191
192 Returns:
Simon Glass267112e2019-07-20 12:23:28 -0600193 Empty dict, if this entry is not a .dtb, otherwise:
194 Dict:
195 key: Filename from this entry (without the path)
Simon Glass684a4f12019-07-20 12:23:31 -0600196 value: Tuple:
197 Fdt object for this dtb, or None if not available
198 Filename of file containing this dtb
Simon Glass0c9d5b52018-09-14 04:57:22 -0600199 """
Simon Glass267112e2019-07-20 12:23:28 -0600200 return {}
Simon Glass0c9d5b52018-09-14 04:57:22 -0600201
Simon Glassac6328c2018-09-14 04:57:28 -0600202 def ExpandEntries(self):
203 pass
204
Simon Glasse22f8fa2018-07-06 10:27:41 -0600205 def AddMissingProperties(self):
206 """Add new properties to the device tree as needed for this entry"""
Simon Glass9dcc8612018-08-01 15:22:42 -0600207 for prop in ['offset', 'size', 'image-pos']:
Simon Glasse22f8fa2018-07-06 10:27:41 -0600208 if not prop in self._node.props:
Simon Glassc8135dc2018-09-14 04:57:21 -0600209 state.AddZeroProp(self._node, prop)
Simon Glassfb30e292019-07-20 12:23:51 -0600210 if self.GetImage().allow_repack:
211 if self.orig_offset is not None:
212 state.AddZeroProp(self._node, 'orig-offset', True)
213 if self.orig_size is not None:
214 state.AddZeroProp(self._node, 'orig-size', True)
215
Simon Glassaa2fcf92019-07-08 14:25:30 -0600216 if self.compress != 'none':
217 state.AddZeroProp(self._node, 'uncomp-size')
Simon Glassae7cf032018-09-14 04:57:31 -0600218 err = state.CheckAddHashProp(self._node)
219 if err:
220 self.Raise(err)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600221
222 def SetCalculatedProperties(self):
223 """Set the value of device-tree properties calculated by binman"""
Simon Glassc8135dc2018-09-14 04:57:21 -0600224 state.SetInt(self._node, 'offset', self.offset)
225 state.SetInt(self._node, 'size', self.size)
Simon Glass39dd2152019-07-08 14:25:47 -0600226 base = self.section.GetRootSkipAtStart() if self.section else 0
227 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glassfb30e292019-07-20 12:23:51 -0600228 if self.GetImage().allow_repack:
229 if self.orig_offset is not None:
230 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
231 if self.orig_size is not None:
232 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glassaa2fcf92019-07-08 14:25:30 -0600233 if self.uncomp_size is not None:
234 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Simon Glassae7cf032018-09-14 04:57:31 -0600235 state.CheckSetHashValue(self._node, self.GetData)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600236
Simon Glass92307732018-07-06 10:27:40 -0600237 def ProcessFdt(self, fdt):
Simon Glasse219aa42018-09-14 04:57:24 -0600238 """Allow entries to adjust the device tree
239
240 Some entries need to adjust the device tree for their purposes. This
241 may involve adding or deleting properties.
242
243 Returns:
244 True if processing is complete
245 False if processing could not be completed due to a dependency.
246 This will cause the entry to be retried after others have been
247 called
248 """
Simon Glass92307732018-07-06 10:27:40 -0600249 return True
250
Simon Glass3b78d532018-06-01 09:38:21 -0600251 def SetPrefix(self, prefix):
252 """Set the name prefix for a node
253
254 Args:
255 prefix: Prefix to set, or '' to not use a prefix
256 """
257 if prefix:
258 self.name = prefix + self.name
259
Simon Glass2e1169f2018-07-06 10:27:19 -0600260 def SetContents(self, data):
261 """Set the contents of an entry
262
263 This sets both the data and content_size properties
264
265 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600266 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600267 """
268 self.data = data
269 self.contents_size = len(self.data)
270
271 def ProcessContentsUpdate(self, data):
Simon Glassd17dfea2019-07-08 14:25:33 -0600272 """Update the contents of an entry, after the size is fixed
Simon Glass2e1169f2018-07-06 10:27:19 -0600273
Simon Glassec849852019-07-08 14:25:35 -0600274 This checks that the new data is the same size as the old. If the size
275 has changed, this triggers a re-run of the packing algorithm.
Simon Glass2e1169f2018-07-06 10:27:19 -0600276
277 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600278 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600279
280 Raises:
281 ValueError if the new data size is not the same as the old
282 """
Simon Glassec849852019-07-08 14:25:35 -0600283 size_ok = True
Simon Glasse61b6f62019-07-08 14:25:37 -0600284 new_size = len(data)
Simon Glass9d8ee322019-07-20 12:23:58 -0600285 if state.AllowEntryExpansion() and new_size > self.contents_size:
286 # self.data will indicate the new size needed
287 size_ok = False
288 elif state.AllowEntryContraction() and new_size < self.contents_size:
289 size_ok = False
290
291 # If not allowed to change, try to deal with it or give up
292 if size_ok:
Simon Glasse61b6f62019-07-08 14:25:37 -0600293 if new_size > self.contents_size:
Simon Glass9d8ee322019-07-20 12:23:58 -0600294 self.Raise('Cannot update entry size from %d to %d' %
295 (self.contents_size, new_size))
296
297 # Don't let the data shrink. Pad it if necessary
298 if size_ok and new_size < self.contents_size:
299 data += tools.GetBytes(0, self.contents_size - new_size)
300
301 if not size_ok:
302 tout.Debug("Entry '%s' size change from %s to %s" % (
303 self._node.path, ToHex(self.contents_size),
304 ToHex(new_size)))
Simon Glass2e1169f2018-07-06 10:27:19 -0600305 self.SetContents(data)
Simon Glassec849852019-07-08 14:25:35 -0600306 return size_ok
Simon Glass2e1169f2018-07-06 10:27:19 -0600307
Simon Glass2574ef62016-11-25 20:15:51 -0700308 def ObtainContents(self):
309 """Figure out the contents of an entry.
310
311 Returns:
312 True if the contents were found, False if another call is needed
313 after the other entries are processed.
314 """
315 # No contents by default: subclasses can implement this
316 return True
317
Simon Glasse61b6f62019-07-08 14:25:37 -0600318 def ResetForPack(self):
319 """Reset offset/size fields so that packing can be done again"""
Simon Glassb6dff4c2019-07-20 12:23:36 -0600320 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
321 (ToHex(self.offset), ToHex(self.orig_offset),
322 ToHex(self.size), ToHex(self.orig_size)))
Simon Glass1fdb4872019-10-31 07:43:02 -0600323 self.pre_reset_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600324 self.offset = self.orig_offset
325 self.size = self.orig_size
326
Simon Glasse8561af2018-08-01 15:22:37 -0600327 def Pack(self, offset):
Simon Glassad5a7712018-06-01 09:38:14 -0600328 """Figure out how to pack the entry into the section
Simon Glass2574ef62016-11-25 20:15:51 -0700329
330 Most of the time the entries are not fully specified. There may be
331 an alignment but no size. In that case we take the size from the
332 contents of the entry.
333
Simon Glasse8561af2018-08-01 15:22:37 -0600334 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glass2574ef62016-11-25 20:15:51 -0700335
Simon Glasse8561af2018-08-01 15:22:37 -0600336 Once this function is complete, both the offset and size of the
Simon Glass2574ef62016-11-25 20:15:51 -0700337 entry will be know.
338
339 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600340 Current section offset pointer
Simon Glass2574ef62016-11-25 20:15:51 -0700341
342 Returns:
Simon Glasse8561af2018-08-01 15:22:37 -0600343 New section offset pointer (after this entry)
Simon Glass2574ef62016-11-25 20:15:51 -0700344 """
Simon Glassb6dff4c2019-07-20 12:23:36 -0600345 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
346 (ToHex(self.offset), ToHex(self.size),
347 self.contents_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600348 if self.offset is None:
349 if self.offset_unset:
350 self.Raise('No offset set with offset-unset: should another '
351 'entry provide this correct offset?')
352 self.offset = tools.Align(offset, self.align)
Simon Glass2574ef62016-11-25 20:15:51 -0700353 needed = self.pad_before + self.contents_size + self.pad_after
354 needed = tools.Align(needed, self.align_size)
355 size = self.size
356 if not size:
357 size = needed
Simon Glasse8561af2018-08-01 15:22:37 -0600358 new_offset = self.offset + size
359 aligned_offset = tools.Align(new_offset, self.align_end)
360 if aligned_offset != new_offset:
361 size = aligned_offset - self.offset
362 new_offset = aligned_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700363
364 if not self.size:
365 self.size = size
366
367 if self.size < needed:
368 self.Raise("Entry contents size is %#x (%d) but entry size is "
369 "%#x (%d)" % (needed, needed, self.size, self.size))
370 # Check that the alignment is correct. It could be wrong if the
Simon Glasse8561af2018-08-01 15:22:37 -0600371 # and offset or size values were provided (i.e. not calculated), but
Simon Glass2574ef62016-11-25 20:15:51 -0700372 # conflict with the provided alignment values
373 if self.size != tools.Align(self.size, self.align_size):
374 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
375 (self.size, self.size, self.align_size, self.align_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600376 if self.offset != tools.Align(self.offset, self.align):
377 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
378 (self.offset, self.offset, self.align, self.align))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600379 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
380 (self.offset, self.size, self.contents_size, new_offset))
Simon Glass2574ef62016-11-25 20:15:51 -0700381
Simon Glasse8561af2018-08-01 15:22:37 -0600382 return new_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700383
384 def Raise(self, msg):
385 """Convenience function to raise an error referencing a node"""
386 raise ValueError("Node '%s': %s" % (self._node.path, msg))
387
Simon Glassb6dff4c2019-07-20 12:23:36 -0600388 def Detail(self, msg):
389 """Convenience function to log detail referencing a node"""
390 tag = "Node '%s'" % self._node.path
391 tout.Detail('%30s: %s' % (tag, msg))
392
Simon Glass91710b32018-07-17 13:25:32 -0600393 def GetEntryArgsOrProps(self, props, required=False):
394 """Return the values of a set of properties
395
396 Args:
397 props: List of EntryArg objects
398
399 Raises:
400 ValueError if a property is not found
401 """
402 values = []
403 missing = []
404 for prop in props:
405 python_prop = prop.name.replace('-', '_')
406 if hasattr(self, python_prop):
407 value = getattr(self, python_prop)
408 else:
409 value = None
410 if value is None:
411 value = self.GetArg(prop.name, prop.datatype)
412 if value is None and required:
413 missing.append(prop.name)
414 values.append(value)
415 if missing:
416 self.Raise('Missing required properties/entry args: %s' %
417 (', '.join(missing)))
418 return values
419
Simon Glass2574ef62016-11-25 20:15:51 -0700420 def GetPath(self):
421 """Get the path of a node
422
423 Returns:
424 Full path of the node for this entry
425 """
426 return self._node.path
427
428 def GetData(self):
Simon Glassb6dff4c2019-07-20 12:23:36 -0600429 self.Detail('GetData: size %s' % ToHexSize(self.data))
Simon Glass2574ef62016-11-25 20:15:51 -0700430 return self.data
431
Simon Glasse8561af2018-08-01 15:22:37 -0600432 def GetOffsets(self):
Simon Glass224bc662019-07-08 13:18:30 -0600433 """Get the offsets for siblings
434
435 Some entry types can contain information about the position or size of
436 other entries. An example of this is the Intel Flash Descriptor, which
437 knows where the Intel Management Engine section should go.
438
439 If this entry knows about the position of other entries, it can specify
440 this by returning values here
441
442 Returns:
443 Dict:
444 key: Entry type
445 value: List containing position and size of the given entry
Simon Glassed365eb2019-07-08 13:18:39 -0600446 type. Either can be None if not known
Simon Glass224bc662019-07-08 13:18:30 -0600447 """
Simon Glass2574ef62016-11-25 20:15:51 -0700448 return {}
449
Simon Glassed365eb2019-07-08 13:18:39 -0600450 def SetOffsetSize(self, offset, size):
451 """Set the offset and/or size of an entry
452
453 Args:
454 offset: New offset, or None to leave alone
455 size: New size, or None to leave alone
456 """
457 if offset is not None:
458 self.offset = offset
459 if size is not None:
460 self.size = size
Simon Glass2574ef62016-11-25 20:15:51 -0700461
Simon Glass9dcc8612018-08-01 15:22:42 -0600462 def SetImagePos(self, image_pos):
463 """Set the position in the image
464
465 Args:
466 image_pos: Position of this entry in the image
467 """
468 self.image_pos = image_pos + self.offset
469
Simon Glass2574ef62016-11-25 20:15:51 -0700470 def ProcessContents(self):
Simon Glassec849852019-07-08 14:25:35 -0600471 """Do any post-packing updates of entry contents
472
473 This function should call ProcessContentsUpdate() to update the entry
474 contents, if necessary, returning its return value here.
475
476 Args:
477 data: Data to set to the contents (bytes)
478
479 Returns:
480 True if the new data size is OK, False if expansion is needed
481
482 Raises:
483 ValueError if the new data size is not the same as the old and
484 state.AllowEntryExpansion() is False
485 """
486 return True
Simon Glass4ca8e042017-11-13 18:55:01 -0700487
Simon Glass8a6f56e2018-06-01 09:38:13 -0600488 def WriteSymbols(self, section):
Simon Glass4ca8e042017-11-13 18:55:01 -0700489 """Write symbol values into binary files for access at run time
490
491 Args:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600492 section: Section containing the entry
Simon Glass4ca8e042017-11-13 18:55:01 -0700493 """
494 pass
Simon Glassa91e1152018-06-01 09:38:16 -0600495
Simon Glasse8561af2018-08-01 15:22:37 -0600496 def CheckOffset(self):
497 """Check that the entry offsets are correct
Simon Glassa91e1152018-06-01 09:38:16 -0600498
Simon Glasse8561af2018-08-01 15:22:37 -0600499 This is used for entries which have extra offset requirements (other
Simon Glassa91e1152018-06-01 09:38:16 -0600500 than having to be fully inside their section). Sub-classes can implement
501 this function and raise if there is a problem.
502 """
503 pass
Simon Glass30732662018-06-01 09:38:20 -0600504
Simon Glass3a9a2b82018-07-17 13:25:28 -0600505 @staticmethod
Simon Glasscd817d52018-09-14 04:57:36 -0600506 def GetStr(value):
507 if value is None:
508 return '<none> '
509 return '%08x' % value
510
511 @staticmethod
Simon Glass7eca7922018-07-17 13:25:49 -0600512 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glasscd817d52018-09-14 04:57:36 -0600513 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
514 Entry.GetStr(offset), Entry.GetStr(size),
515 name), file=fd)
Simon Glass3a9a2b82018-07-17 13:25:28 -0600516
Simon Glass30732662018-06-01 09:38:20 -0600517 def WriteMap(self, fd, indent):
518 """Write a map of the entry to a .map file
519
520 Args:
521 fd: File to write the map to
522 indent: Curent indent level of map (0=none, 1=one level, etc.)
523 """
Simon Glass7eca7922018-07-17 13:25:49 -0600524 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
525 self.image_pos)
Simon Glass91710b32018-07-17 13:25:32 -0600526
Simon Glass704784b2018-07-17 13:25:38 -0600527 def GetEntries(self):
528 """Return a list of entries contained by this entry
529
530 Returns:
531 List of entries, or None if none. A normal entry has no entries
532 within it so will return None
533 """
534 return None
535
Simon Glass91710b32018-07-17 13:25:32 -0600536 def GetArg(self, name, datatype=str):
537 """Get the value of an entry argument or device-tree-node property
538
539 Some node properties can be provided as arguments to binman. First check
540 the entry arguments, and fall back to the device tree if not found
541
542 Args:
543 name: Argument name
544 datatype: Data type (str or int)
545
546 Returns:
547 Value of argument as a string or int, or None if no value
548
549 Raises:
550 ValueError if the argument cannot be converted to in
551 """
Simon Glass29aa7362018-09-14 04:57:19 -0600552 value = state.GetEntryArg(name)
Simon Glass91710b32018-07-17 13:25:32 -0600553 if value is not None:
554 if datatype == int:
555 try:
556 value = int(value)
557 except ValueError:
558 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
559 (name, value))
560 elif datatype == str:
561 pass
562 else:
563 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
564 datatype)
565 else:
566 value = fdt_util.GetDatatype(self._node, name, datatype)
567 return value
Simon Glass969616c2018-07-17 13:25:36 -0600568
569 @staticmethod
570 def WriteDocs(modules, test_missing=None):
571 """Write out documentation about the various entry types to stdout
572
573 Args:
574 modules: List of modules to include
575 test_missing: Used for testing. This is a module to report
576 as missing
577 """
578 print('''Binman Entry Documentation
579===========================
580
581This file describes the entry types supported by binman. These entry types can
582be placed in an image one by one to build up a final firmware image. It is
583fairly easy to create new entry types. Just add a new file to the 'etype'
584directory. You can use the existing entries as examples.
585
586Note that some entries are subclasses of others, using and extending their
587features to produce new behaviours.
588
589
590''')
591 modules = sorted(modules)
592
593 # Don't show the test entry
594 if '_testing' in modules:
595 modules.remove('_testing')
596 missing = []
597 for name in modules:
Simon Glassc585dd42020-04-17 18:09:03 -0600598 module = Entry.Lookup('WriteDocs', name)
Simon Glass969616c2018-07-17 13:25:36 -0600599 docs = getattr(module, '__doc__')
600 if test_missing == name:
601 docs = None
602 if docs:
603 lines = docs.splitlines()
604 first_line = lines[0]
605 rest = [line[4:] for line in lines[1:]]
606 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
607 print(hdr)
608 print('-' * len(hdr))
609 print('\n'.join(rest))
610 print()
611 print()
612 else:
613 missing.append(name)
614
615 if missing:
616 raise ValueError('Documentation is missing for modules: %s' %
617 ', '.join(missing))
Simon Glass639505b2018-09-14 04:57:11 -0600618
619 def GetUniqueName(self):
620 """Get a unique name for a node
621
622 Returns:
623 String containing a unique name for a node, consisting of the name
624 of all ancestors (starting from within the 'binman' node) separated
625 by a dot ('.'). This can be useful for generating unique filesnames
626 in the output directory.
627 """
628 name = self.name
629 node = self._node
630 while node.parent:
631 node = node.parent
632 if node.name == 'binman':
633 break
634 name = '%s.%s' % (node.name, name)
635 return name
Simon Glassfa79a812018-09-14 04:57:29 -0600636
637 def ExpandToLimit(self, limit):
638 """Expand an entry so that it ends at the given offset limit"""
639 if self.offset + self.size < limit:
640 self.size = limit - self.offset
641 # Request the contents again, since changing the size requires that
642 # the data grows. This should not fail, but check it to be sure.
643 if not self.ObtainContents():
644 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassc4056b82019-07-08 13:18:38 -0600645
646 def HasSibling(self, name):
647 """Check if there is a sibling of a given name
648
649 Returns:
650 True if there is an entry with this name in the the same section,
651 else False
652 """
653 return name in self.section.GetEntries()
Simon Glasscec34ba2019-07-08 14:25:28 -0600654
655 def GetSiblingImagePos(self, name):
656 """Return the image position of the given sibling
657
658 Returns:
659 Image position of sibling, or None if the sibling has no position,
660 or False if there is no such sibling
661 """
662 if not self.HasSibling(name):
663 return False
664 return self.section.GetEntries()[name].image_pos
Simon Glass6b156f82019-07-08 14:25:43 -0600665
666 @staticmethod
667 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
668 uncomp_size, offset, entry):
669 """Add a new entry to the entries list
670
671 Args:
672 entries: List (of EntryInfo objects) to add to
673 indent: Current indent level to add to list
674 name: Entry name (string)
675 etype: Entry type (string)
676 size: Entry size in bytes (int)
677 image_pos: Position within image in bytes (int)
678 uncomp_size: Uncompressed size if the entry uses compression, else
679 None
680 offset: Entry offset within parent in bytes (int)
681 entry: Entry object
682 """
683 entries.append(EntryInfo(indent, name, etype, size, image_pos,
684 uncomp_size, offset, entry))
685
686 def ListEntries(self, entries, indent):
687 """Add files in this entry to the list of entries
688
689 This can be overridden by subclasses which need different behaviour.
690
691 Args:
692 entries: List (of EntryInfo objects) to add to
693 indent: Current indent level to add to list
694 """
695 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
696 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glass4c613bf2019-07-08 14:25:50 -0600697
698 def ReadData(self, decomp=True):
699 """Read the data for an entry from the image
700
701 This is used when the image has been read in and we want to extract the
702 data for a particular entry from that image.
703
704 Args:
705 decomp: True to decompress any compressed data before returning it;
706 False to return the raw, uncompressed data
707
708 Returns:
709 Entry data (bytes)
710 """
711 # Use True here so that we get an uncompressed section to work from,
712 # although compressed sections are currently not supported
Simon Glass4d8151f2019-09-25 08:56:21 -0600713 tout.Debug("ReadChildData section '%s', entry '%s'" %
714 (self.section.GetPath(), self.GetPath()))
Simon Glass0cd8ace2019-07-20 12:24:04 -0600715 data = self.section.ReadChildData(self, decomp)
716 return data
Simon Glassaf8c45c2019-07-20 12:23:41 -0600717
Simon Glass23f00472019-09-25 08:56:20 -0600718 def ReadChildData(self, child, decomp=True):
Simon Glass4d8151f2019-09-25 08:56:21 -0600719 """Read the data for a particular child entry
Simon Glass23f00472019-09-25 08:56:20 -0600720
721 This reads data from the parent and extracts the piece that relates to
722 the given child.
723
724 Args:
Simon Glass4d8151f2019-09-25 08:56:21 -0600725 child: Child entry to read data for (must be valid)
Simon Glass23f00472019-09-25 08:56:20 -0600726 decomp: True to decompress any compressed data before returning it;
727 False to return the raw, uncompressed data
728
729 Returns:
730 Data for the child (bytes)
731 """
732 pass
733
Simon Glassaf8c45c2019-07-20 12:23:41 -0600734 def LoadData(self, decomp=True):
735 data = self.ReadData(decomp)
Simon Glass072959a2019-07-20 12:23:50 -0600736 self.contents_size = len(data)
Simon Glassaf8c45c2019-07-20 12:23:41 -0600737 self.ProcessContentsUpdate(data)
738 self.Detail('Loaded data size %x' % len(data))
Simon Glass990b1742019-07-20 12:23:46 -0600739
740 def GetImage(self):
741 """Get the image containing this entry
742
743 Returns:
744 Image object containing this entry
745 """
746 return self.section.GetImage()
Simon Glass072959a2019-07-20 12:23:50 -0600747
748 def WriteData(self, data, decomp=True):
749 """Write the data to an entry in the image
750
751 This is used when the image has been read in and we want to replace the
752 data for a particular entry in that image.
753
754 The image must be re-packed and written out afterwards.
755
756 Args:
757 data: Data to replace it with
758 decomp: True to compress the data if needed, False if data is
759 already compressed so should be used as is
760
761 Returns:
762 True if the data did not result in a resize of this entry, False if
763 the entry must be resized
764 """
Simon Glass1fdb4872019-10-31 07:43:02 -0600765 if self.size is not None:
766 self.contents_size = self.size
767 else:
768 self.contents_size = self.pre_reset_size
Simon Glass072959a2019-07-20 12:23:50 -0600769 ok = self.ProcessContentsUpdate(data)
770 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glassd34af7a2019-07-20 12:24:05 -0600771 section_ok = self.section.WriteChildData(self)
772 return ok and section_ok
773
774 def WriteChildData(self, child):
775 """Handle writing the data in a child entry
776
777 This should be called on the child's parent section after the child's
778 data has been updated. It
779
780 This base-class implementation does nothing, since the base Entry object
781 does not have any children.
782
783 Args:
784 child: Child Entry that was written
785
786 Returns:
787 True if the section could be updated successfully, False if the
788 data is such that the section could not updat
789 """
790 return True
Simon Glass11453762019-07-20 12:23:55 -0600791
792 def GetSiblingOrder(self):
793 """Get the relative order of an entry amoung its siblings
794
795 Returns:
796 'start' if this entry is first among siblings, 'end' if last,
797 otherwise None
798 """
799 entries = list(self.section.GetEntries().values())
800 if entries:
801 if self == entries[0]:
802 return 'start'
803 elif self == entries[-1]:
804 return 'end'
805 return 'middle'
Simon Glass5d94cc62020-07-09 18:39:38 -0600806
807 def SetAllowMissing(self, allow_missing):
808 """Set whether a section allows missing external blobs
809
810 Args:
811 allow_missing: True if allowed, False if not allowed
812 """
813 # This is meaningless for anything other than sections
814 pass
Simon Glassa003cd32020-07-09 18:39:40 -0600815
816 def CheckMissing(self, missing_list):
817 """Check if any entries in this section have missing external blobs
818
819 If there are missing blobs, the entries are added to the list
820
821 Args:
822 missing_list: List of Entry objects to be added to
823 """
824 if self.missing:
825 missing_list.append(self)
Simon Glassb8f90372020-09-01 05:13:57 -0600826
827 def GetAllowMissing(self):
828 """Get whether a section allows missing external blobs
829
830 Returns:
831 True if allowed, False if not allowed
832 """
833 return self.allow_missing
Simon Glassa820af72020-09-06 10:39:09 -0600834
835 def GetHelpTags(self):
836 """Get the tags use for missing-blob help
837
838 Returns:
839 list of possible tags, most desirable first
840 """
841 return list(filter(None, [self.missing_msg, self.name, self.etype]))
Simon Glassa1301a22020-10-26 17:40:06 -0600842
843 def CompressData(self, indata):
844 """Compress data according to the entry's compression method
845
846 Args:
847 indata: Data to compress
848
849 Returns:
850 Compressed data (first word is the compressed size)
851 """
852 if self.compress != 'none':
853 self.uncomp_size = len(indata)
854 data = tools.Compress(indata, self.compress)
855 return data