blob: 5bf5be4794b4c92f87ff492df2dba0b29d963d70 [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 Glass30732662018-06-01 09:38:20 -06007from __future__ import print_function
8
Simon Glass91710b32018-07-17 13:25:32 -06009from collections import namedtuple
Simon Glass7ccca832019-10-31 07:42:59 -060010import importlib
Simon Glass691198c2018-06-01 09:38:15 -060011import os
12import sys
Simon Glass29aa7362018-09-14 04:57:19 -060013
14import fdt_util
Simon Glass2574ef62016-11-25 20:15:51 -070015import tools
Simon Glassb6dff4c2019-07-20 12:23:36 -060016from tools import ToHex, ToHexSize
Simon Glass233a26a92019-07-08 14:25:49 -060017import tout
Simon Glass2574ef62016-11-25 20:15:51 -070018
19modules = {}
20
Simon Glass691198c2018-06-01 09:38:15 -060021our_path = os.path.dirname(os.path.realpath(__file__))
22
Simon Glass91710b32018-07-17 13:25:32 -060023
24# An argument which can be passed to entries on the command line, in lieu of
25# device-tree properties.
26EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
27
Simon Glass6b156f82019-07-08 14:25:43 -060028# Information about an entry for use when displaying summaries
29EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
30 'image_pos', 'uncomp_size', 'offset',
31 'entry'])
Simon Glass91710b32018-07-17 13:25:32 -060032
Simon Glass2574ef62016-11-25 20:15:51 -070033class Entry(object):
Simon Glassad5a7712018-06-01 09:38:14 -060034 """An Entry in the section
Simon Glass2574ef62016-11-25 20:15:51 -070035
36 An entry corresponds to a single node in the device-tree description
Simon Glassad5a7712018-06-01 09:38:14 -060037 of the section. Each entry ends up being a part of the final section.
Simon Glass2574ef62016-11-25 20:15:51 -070038 Entries can be placed either right next to each other, or with padding
39 between them. The type of the entry determines the data that is in it.
40
41 This class is not used by itself. All entry objects are subclasses of
42 Entry.
43
44 Attributes:
Simon Glass3a9a2b82018-07-17 13:25:28 -060045 section: Section object containing this entry
Simon Glass2574ef62016-11-25 20:15:51 -070046 node: The node that created this entry
Simon Glasse8561af2018-08-01 15:22:37 -060047 offset: Offset of entry within the section, None if not known yet (in
48 which case it will be calculated by Pack())
Simon Glass2574ef62016-11-25 20:15:51 -070049 size: Entry size in bytes, None if not known
Simon Glassaa2fcf92019-07-08 14:25:30 -060050 uncomp_size: Size of uncompressed data in bytes, if the entry is
51 compressed, else None
Simon Glass2574ef62016-11-25 20:15:51 -070052 contents_size: Size of contents in bytes, 0 by default
Simon Glasse8561af2018-08-01 15:22:37 -060053 align: Entry start offset alignment, or None
Simon Glass2574ef62016-11-25 20:15:51 -070054 align_size: Entry size alignment, or None
Simon Glasse8561af2018-08-01 15:22:37 -060055 align_end: Entry end offset alignment, or None
Simon Glass2574ef62016-11-25 20:15:51 -070056 pad_before: Number of pad bytes before the contents, 0 if none
57 pad_after: Number of pad bytes after the contents, 0 if none
58 data: Contents of entry (string of bytes)
Simon Glassaa2fcf92019-07-08 14:25:30 -060059 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glasse61b6f62019-07-08 14:25:37 -060060 orig_offset: Original offset value read from node
61 orig_size: Original size value read from node
Simon Glass2574ef62016-11-25 20:15:51 -070062 """
Simon Glass2c360cf2019-07-20 12:23:45 -060063 def __init__(self, section, etype, node, name_prefix=''):
Simon Glassb9ba4e02019-08-24 07:22:44 -060064 # Put this here to allow entry-docs and help to work without libfdt
65 global state
66 import state
67
Simon Glassad5a7712018-06-01 09:38:14 -060068 self.section = section
Simon Glass2574ef62016-11-25 20:15:51 -070069 self.etype = etype
70 self._node = node
Simon Glass3b78d532018-06-01 09:38:21 -060071 self.name = node and (name_prefix + node.name) or 'none'
Simon Glasse8561af2018-08-01 15:22:37 -060072 self.offset = None
Simon Glass2574ef62016-11-25 20:15:51 -070073 self.size = None
Simon Glassaa2fcf92019-07-08 14:25:30 -060074 self.uncomp_size = None
Simon Glass5c350162018-07-17 13:25:47 -060075 self.data = None
Simon Glass2574ef62016-11-25 20:15:51 -070076 self.contents_size = 0
77 self.align = None
78 self.align_size = None
79 self.align_end = None
80 self.pad_before = 0
81 self.pad_after = 0
Simon Glasse8561af2018-08-01 15:22:37 -060082 self.offset_unset = False
Simon Glass9dcc8612018-08-01 15:22:42 -060083 self.image_pos = None
Simon Glassfa79a812018-09-14 04:57:29 -060084 self._expand_size = False
Simon Glassaa2fcf92019-07-08 14:25:30 -060085 self.compress = 'none'
Simon Glass2574ef62016-11-25 20:15:51 -070086
87 @staticmethod
Simon Glass75502932019-07-08 14:25:31 -060088 def Lookup(node_path, etype):
Simon Glass969616c2018-07-17 13:25:36 -060089 """Look up the entry class for a node.
Simon Glass2574ef62016-11-25 20:15:51 -070090
91 Args:
Simon Glass969616c2018-07-17 13:25:36 -060092 node_node: Path name of Node object containing information about
93 the entry to create (used for errors)
94 etype: Entry type to use
Simon Glass2574ef62016-11-25 20:15:51 -070095
96 Returns:
Simon Glass969616c2018-07-17 13:25:36 -060097 The entry class object if found, else None
Simon Glass2574ef62016-11-25 20:15:51 -070098 """
Simon Glasse76a3e62018-06-01 09:38:11 -060099 # Convert something like 'u-boot@0' to 'u_boot' since we are only
100 # interested in the type.
Simon Glass2574ef62016-11-25 20:15:51 -0700101 module_name = etype.replace('-', '_')
Simon Glasse76a3e62018-06-01 09:38:11 -0600102 if '@' in module_name:
103 module_name = module_name.split('@')[0]
Simon Glass2574ef62016-11-25 20:15:51 -0700104 module = modules.get(module_name)
105
Simon Glass691198c2018-06-01 09:38:15 -0600106 # Also allow entry-type modules to be brought in from the etype directory.
107
Simon Glass2574ef62016-11-25 20:15:51 -0700108 # Import the module if we have not already done so.
109 if not module:
Simon Glass691198c2018-06-01 09:38:15 -0600110 old_path = sys.path
111 sys.path.insert(0, os.path.join(our_path, 'etype'))
Simon Glass2574ef62016-11-25 20:15:51 -0700112 try:
Simon Glass7ccca832019-10-31 07:42:59 -0600113 module = importlib.import_module(module_name)
Simon Glass969616c2018-07-17 13:25:36 -0600114 except ImportError as e:
115 raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
116 (etype, node_path, module_name, e))
Simon Glass691198c2018-06-01 09:38:15 -0600117 finally:
118 sys.path = old_path
Simon Glass2574ef62016-11-25 20:15:51 -0700119 modules[module_name] = module
120
Simon Glass969616c2018-07-17 13:25:36 -0600121 # Look up the expected class name
122 return getattr(module, 'Entry_%s' % module_name)
123
124 @staticmethod
125 def Create(section, node, etype=None):
126 """Create a new entry for a node.
127
128 Args:
129 section: Section object containing this node
130 node: Node object containing information about the entry to
131 create
132 etype: Entry type to use, or None to work it out (used for tests)
133
134 Returns:
135 A new Entry object of the correct type (a subclass of Entry)
136 """
137 if not etype:
138 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glass75502932019-07-08 14:25:31 -0600139 obj = Entry.Lookup(node.path, etype)
Simon Glass969616c2018-07-17 13:25:36 -0600140
Simon Glass2574ef62016-11-25 20:15:51 -0700141 # Call its constructor to get the object we want.
Simon Glassad5a7712018-06-01 09:38:14 -0600142 return obj(section, etype, node)
Simon Glass2574ef62016-11-25 20:15:51 -0700143
144 def ReadNode(self):
145 """Read entry information from the node
146
Simon Glass2c360cf2019-07-20 12:23:45 -0600147 This must be called as the first thing after the Entry is created.
148
Simon Glass2574ef62016-11-25 20:15:51 -0700149 This reads all the fields we recognise from the node, ready for use.
150 """
Simon Glass24b97442018-07-17 13:25:51 -0600151 if 'pos' in self._node.props:
152 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glasse8561af2018-08-01 15:22:37 -0600153 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glass2574ef62016-11-25 20:15:51 -0700154 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glassfb30e292019-07-20 12:23:51 -0600155 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
156 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
157 if self.GetImage().copy_to_orig:
158 self.orig_offset = self.offset
159 self.orig_size = self.size
Simon Glasse61b6f62019-07-08 14:25:37 -0600160
Simon Glassb8424fa2019-07-08 14:25:46 -0600161 # These should not be set in input files, but are set in an FDT map,
162 # which is also read by this code.
163 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
164 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
165
Simon Glass2574ef62016-11-25 20:15:51 -0700166 self.align = fdt_util.GetInt(self._node, 'align')
167 if tools.NotPowerOfTwo(self.align):
168 raise ValueError("Node '%s': Alignment %s must be a power of two" %
169 (self._node.path, self.align))
170 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
171 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
172 self.align_size = fdt_util.GetInt(self._node, 'align-size')
173 if tools.NotPowerOfTwo(self.align_size):
Simon Glass39dd2152019-07-08 14:25:47 -0600174 self.Raise("Alignment size %s must be a power of two" %
175 self.align_size)
Simon Glass2574ef62016-11-25 20:15:51 -0700176 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glasse8561af2018-08-01 15:22:37 -0600177 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassfa79a812018-09-14 04:57:29 -0600178 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
Simon Glass2574ef62016-11-25 20:15:51 -0700179
Simon Glass3732ec32018-09-14 04:57:18 -0600180 def GetDefaultFilename(self):
181 return None
182
Simon Glass267112e2019-07-20 12:23:28 -0600183 def GetFdts(self):
184 """Get the device trees used by this entry
Simon Glass0c9d5b52018-09-14 04:57:22 -0600185
186 Returns:
Simon Glass267112e2019-07-20 12:23:28 -0600187 Empty dict, if this entry is not a .dtb, otherwise:
188 Dict:
189 key: Filename from this entry (without the path)
Simon Glass684a4f12019-07-20 12:23:31 -0600190 value: Tuple:
191 Fdt object for this dtb, or None if not available
192 Filename of file containing this dtb
Simon Glass0c9d5b52018-09-14 04:57:22 -0600193 """
Simon Glass267112e2019-07-20 12:23:28 -0600194 return {}
Simon Glass0c9d5b52018-09-14 04:57:22 -0600195
Simon Glassac6328c2018-09-14 04:57:28 -0600196 def ExpandEntries(self):
197 pass
198
Simon Glasse22f8fa2018-07-06 10:27:41 -0600199 def AddMissingProperties(self):
200 """Add new properties to the device tree as needed for this entry"""
Simon Glass9dcc8612018-08-01 15:22:42 -0600201 for prop in ['offset', 'size', 'image-pos']:
Simon Glasse22f8fa2018-07-06 10:27:41 -0600202 if not prop in self._node.props:
Simon Glassc8135dc2018-09-14 04:57:21 -0600203 state.AddZeroProp(self._node, prop)
Simon Glassfb30e292019-07-20 12:23:51 -0600204 if self.GetImage().allow_repack:
205 if self.orig_offset is not None:
206 state.AddZeroProp(self._node, 'orig-offset', True)
207 if self.orig_size is not None:
208 state.AddZeroProp(self._node, 'orig-size', True)
209
Simon Glassaa2fcf92019-07-08 14:25:30 -0600210 if self.compress != 'none':
211 state.AddZeroProp(self._node, 'uncomp-size')
Simon Glassae7cf032018-09-14 04:57:31 -0600212 err = state.CheckAddHashProp(self._node)
213 if err:
214 self.Raise(err)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600215
216 def SetCalculatedProperties(self):
217 """Set the value of device-tree properties calculated by binman"""
Simon Glassc8135dc2018-09-14 04:57:21 -0600218 state.SetInt(self._node, 'offset', self.offset)
219 state.SetInt(self._node, 'size', self.size)
Simon Glass39dd2152019-07-08 14:25:47 -0600220 base = self.section.GetRootSkipAtStart() if self.section else 0
221 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glassfb30e292019-07-20 12:23:51 -0600222 if self.GetImage().allow_repack:
223 if self.orig_offset is not None:
224 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
225 if self.orig_size is not None:
226 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glassaa2fcf92019-07-08 14:25:30 -0600227 if self.uncomp_size is not None:
228 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Simon Glassae7cf032018-09-14 04:57:31 -0600229 state.CheckSetHashValue(self._node, self.GetData)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600230
Simon Glass92307732018-07-06 10:27:40 -0600231 def ProcessFdt(self, fdt):
Simon Glasse219aa42018-09-14 04:57:24 -0600232 """Allow entries to adjust the device tree
233
234 Some entries need to adjust the device tree for their purposes. This
235 may involve adding or deleting properties.
236
237 Returns:
238 True if processing is complete
239 False if processing could not be completed due to a dependency.
240 This will cause the entry to be retried after others have been
241 called
242 """
Simon Glass92307732018-07-06 10:27:40 -0600243 return True
244
Simon Glass3b78d532018-06-01 09:38:21 -0600245 def SetPrefix(self, prefix):
246 """Set the name prefix for a node
247
248 Args:
249 prefix: Prefix to set, or '' to not use a prefix
250 """
251 if prefix:
252 self.name = prefix + self.name
253
Simon Glass2e1169f2018-07-06 10:27:19 -0600254 def SetContents(self, data):
255 """Set the contents of an entry
256
257 This sets both the data and content_size properties
258
259 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600260 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600261 """
262 self.data = data
263 self.contents_size = len(self.data)
264
265 def ProcessContentsUpdate(self, data):
Simon Glassd17dfea2019-07-08 14:25:33 -0600266 """Update the contents of an entry, after the size is fixed
Simon Glass2e1169f2018-07-06 10:27:19 -0600267
Simon Glassec849852019-07-08 14:25:35 -0600268 This checks that the new data is the same size as the old. If the size
269 has changed, this triggers a re-run of the packing algorithm.
Simon Glass2e1169f2018-07-06 10:27:19 -0600270
271 Args:
Simon Glassd17dfea2019-07-08 14:25:33 -0600272 data: Data to set to the contents (bytes)
Simon Glass2e1169f2018-07-06 10:27:19 -0600273
274 Raises:
275 ValueError if the new data size is not the same as the old
276 """
Simon Glassec849852019-07-08 14:25:35 -0600277 size_ok = True
Simon Glasse61b6f62019-07-08 14:25:37 -0600278 new_size = len(data)
Simon Glass9d8ee322019-07-20 12:23:58 -0600279 if state.AllowEntryExpansion() and new_size > self.contents_size:
280 # self.data will indicate the new size needed
281 size_ok = False
282 elif state.AllowEntryContraction() and new_size < self.contents_size:
283 size_ok = False
284
285 # If not allowed to change, try to deal with it or give up
286 if size_ok:
Simon Glasse61b6f62019-07-08 14:25:37 -0600287 if new_size > self.contents_size:
Simon Glass9d8ee322019-07-20 12:23:58 -0600288 self.Raise('Cannot update entry size from %d to %d' %
289 (self.contents_size, new_size))
290
291 # Don't let the data shrink. Pad it if necessary
292 if size_ok and new_size < self.contents_size:
293 data += tools.GetBytes(0, self.contents_size - new_size)
294
295 if not size_ok:
296 tout.Debug("Entry '%s' size change from %s to %s" % (
297 self._node.path, ToHex(self.contents_size),
298 ToHex(new_size)))
Simon Glass2e1169f2018-07-06 10:27:19 -0600299 self.SetContents(data)
Simon Glassec849852019-07-08 14:25:35 -0600300 return size_ok
Simon Glass2e1169f2018-07-06 10:27:19 -0600301
Simon Glass2574ef62016-11-25 20:15:51 -0700302 def ObtainContents(self):
303 """Figure out the contents of an entry.
304
305 Returns:
306 True if the contents were found, False if another call is needed
307 after the other entries are processed.
308 """
309 # No contents by default: subclasses can implement this
310 return True
311
Simon Glasse61b6f62019-07-08 14:25:37 -0600312 def ResetForPack(self):
313 """Reset offset/size fields so that packing can be done again"""
Simon Glassb6dff4c2019-07-20 12:23:36 -0600314 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
315 (ToHex(self.offset), ToHex(self.orig_offset),
316 ToHex(self.size), ToHex(self.orig_size)))
Simon Glasse61b6f62019-07-08 14:25:37 -0600317 self.offset = self.orig_offset
318 self.size = self.orig_size
319
Simon Glasse8561af2018-08-01 15:22:37 -0600320 def Pack(self, offset):
Simon Glassad5a7712018-06-01 09:38:14 -0600321 """Figure out how to pack the entry into the section
Simon Glass2574ef62016-11-25 20:15:51 -0700322
323 Most of the time the entries are not fully specified. There may be
324 an alignment but no size. In that case we take the size from the
325 contents of the entry.
326
Simon Glasse8561af2018-08-01 15:22:37 -0600327 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glass2574ef62016-11-25 20:15:51 -0700328
Simon Glasse8561af2018-08-01 15:22:37 -0600329 Once this function is complete, both the offset and size of the
Simon Glass2574ef62016-11-25 20:15:51 -0700330 entry will be know.
331
332 Args:
Simon Glasse8561af2018-08-01 15:22:37 -0600333 Current section offset pointer
Simon Glass2574ef62016-11-25 20:15:51 -0700334
335 Returns:
Simon Glasse8561af2018-08-01 15:22:37 -0600336 New section offset pointer (after this entry)
Simon Glass2574ef62016-11-25 20:15:51 -0700337 """
Simon Glassb6dff4c2019-07-20 12:23:36 -0600338 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
339 (ToHex(self.offset), ToHex(self.size),
340 self.contents_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600341 if self.offset is None:
342 if self.offset_unset:
343 self.Raise('No offset set with offset-unset: should another '
344 'entry provide this correct offset?')
345 self.offset = tools.Align(offset, self.align)
Simon Glass2574ef62016-11-25 20:15:51 -0700346 needed = self.pad_before + self.contents_size + self.pad_after
347 needed = tools.Align(needed, self.align_size)
348 size = self.size
349 if not size:
350 size = needed
Simon Glasse8561af2018-08-01 15:22:37 -0600351 new_offset = self.offset + size
352 aligned_offset = tools.Align(new_offset, self.align_end)
353 if aligned_offset != new_offset:
354 size = aligned_offset - self.offset
355 new_offset = aligned_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700356
357 if not self.size:
358 self.size = size
359
360 if self.size < needed:
361 self.Raise("Entry contents size is %#x (%d) but entry size is "
362 "%#x (%d)" % (needed, needed, self.size, self.size))
363 # Check that the alignment is correct. It could be wrong if the
Simon Glasse8561af2018-08-01 15:22:37 -0600364 # and offset or size values were provided (i.e. not calculated), but
Simon Glass2574ef62016-11-25 20:15:51 -0700365 # conflict with the provided alignment values
366 if self.size != tools.Align(self.size, self.align_size):
367 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
368 (self.size, self.size, self.align_size, self.align_size))
Simon Glasse8561af2018-08-01 15:22:37 -0600369 if self.offset != tools.Align(self.offset, self.align):
370 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
371 (self.offset, self.offset, self.align, self.align))
Simon Glassb6dff4c2019-07-20 12:23:36 -0600372 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
373 (self.offset, self.size, self.contents_size, new_offset))
Simon Glass2574ef62016-11-25 20:15:51 -0700374
Simon Glasse8561af2018-08-01 15:22:37 -0600375 return new_offset
Simon Glass2574ef62016-11-25 20:15:51 -0700376
377 def Raise(self, msg):
378 """Convenience function to raise an error referencing a node"""
379 raise ValueError("Node '%s': %s" % (self._node.path, msg))
380
Simon Glassb6dff4c2019-07-20 12:23:36 -0600381 def Detail(self, msg):
382 """Convenience function to log detail referencing a node"""
383 tag = "Node '%s'" % self._node.path
384 tout.Detail('%30s: %s' % (tag, msg))
385
Simon Glass91710b32018-07-17 13:25:32 -0600386 def GetEntryArgsOrProps(self, props, required=False):
387 """Return the values of a set of properties
388
389 Args:
390 props: List of EntryArg objects
391
392 Raises:
393 ValueError if a property is not found
394 """
395 values = []
396 missing = []
397 for prop in props:
398 python_prop = prop.name.replace('-', '_')
399 if hasattr(self, python_prop):
400 value = getattr(self, python_prop)
401 else:
402 value = None
403 if value is None:
404 value = self.GetArg(prop.name, prop.datatype)
405 if value is None and required:
406 missing.append(prop.name)
407 values.append(value)
408 if missing:
409 self.Raise('Missing required properties/entry args: %s' %
410 (', '.join(missing)))
411 return values
412
Simon Glass2574ef62016-11-25 20:15:51 -0700413 def GetPath(self):
414 """Get the path of a node
415
416 Returns:
417 Full path of the node for this entry
418 """
419 return self._node.path
420
421 def GetData(self):
Simon Glassb6dff4c2019-07-20 12:23:36 -0600422 self.Detail('GetData: size %s' % ToHexSize(self.data))
Simon Glass2574ef62016-11-25 20:15:51 -0700423 return self.data
424
Simon Glasse8561af2018-08-01 15:22:37 -0600425 def GetOffsets(self):
Simon Glass224bc662019-07-08 13:18:30 -0600426 """Get the offsets for siblings
427
428 Some entry types can contain information about the position or size of
429 other entries. An example of this is the Intel Flash Descriptor, which
430 knows where the Intel Management Engine section should go.
431
432 If this entry knows about the position of other entries, it can specify
433 this by returning values here
434
435 Returns:
436 Dict:
437 key: Entry type
438 value: List containing position and size of the given entry
Simon Glassed365eb2019-07-08 13:18:39 -0600439 type. Either can be None if not known
Simon Glass224bc662019-07-08 13:18:30 -0600440 """
Simon Glass2574ef62016-11-25 20:15:51 -0700441 return {}
442
Simon Glassed365eb2019-07-08 13:18:39 -0600443 def SetOffsetSize(self, offset, size):
444 """Set the offset and/or size of an entry
445
446 Args:
447 offset: New offset, or None to leave alone
448 size: New size, or None to leave alone
449 """
450 if offset is not None:
451 self.offset = offset
452 if size is not None:
453 self.size = size
Simon Glass2574ef62016-11-25 20:15:51 -0700454
Simon Glass9dcc8612018-08-01 15:22:42 -0600455 def SetImagePos(self, image_pos):
456 """Set the position in the image
457
458 Args:
459 image_pos: Position of this entry in the image
460 """
461 self.image_pos = image_pos + self.offset
462
Simon Glass2574ef62016-11-25 20:15:51 -0700463 def ProcessContents(self):
Simon Glassec849852019-07-08 14:25:35 -0600464 """Do any post-packing updates of entry contents
465
466 This function should call ProcessContentsUpdate() to update the entry
467 contents, if necessary, returning its return value here.
468
469 Args:
470 data: Data to set to the contents (bytes)
471
472 Returns:
473 True if the new data size is OK, False if expansion is needed
474
475 Raises:
476 ValueError if the new data size is not the same as the old and
477 state.AllowEntryExpansion() is False
478 """
479 return True
Simon Glass4ca8e042017-11-13 18:55:01 -0700480
Simon Glass8a6f56e2018-06-01 09:38:13 -0600481 def WriteSymbols(self, section):
Simon Glass4ca8e042017-11-13 18:55:01 -0700482 """Write symbol values into binary files for access at run time
483
484 Args:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600485 section: Section containing the entry
Simon Glass4ca8e042017-11-13 18:55:01 -0700486 """
487 pass
Simon Glassa91e1152018-06-01 09:38:16 -0600488
Simon Glasse8561af2018-08-01 15:22:37 -0600489 def CheckOffset(self):
490 """Check that the entry offsets are correct
Simon Glassa91e1152018-06-01 09:38:16 -0600491
Simon Glasse8561af2018-08-01 15:22:37 -0600492 This is used for entries which have extra offset requirements (other
Simon Glassa91e1152018-06-01 09:38:16 -0600493 than having to be fully inside their section). Sub-classes can implement
494 this function and raise if there is a problem.
495 """
496 pass
Simon Glass30732662018-06-01 09:38:20 -0600497
Simon Glass3a9a2b82018-07-17 13:25:28 -0600498 @staticmethod
Simon Glasscd817d52018-09-14 04:57:36 -0600499 def GetStr(value):
500 if value is None:
501 return '<none> '
502 return '%08x' % value
503
504 @staticmethod
Simon Glass7eca7922018-07-17 13:25:49 -0600505 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glasscd817d52018-09-14 04:57:36 -0600506 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
507 Entry.GetStr(offset), Entry.GetStr(size),
508 name), file=fd)
Simon Glass3a9a2b82018-07-17 13:25:28 -0600509
Simon Glass30732662018-06-01 09:38:20 -0600510 def WriteMap(self, fd, indent):
511 """Write a map of the entry to a .map file
512
513 Args:
514 fd: File to write the map to
515 indent: Curent indent level of map (0=none, 1=one level, etc.)
516 """
Simon Glass7eca7922018-07-17 13:25:49 -0600517 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
518 self.image_pos)
Simon Glass91710b32018-07-17 13:25:32 -0600519
Simon Glass704784b2018-07-17 13:25:38 -0600520 def GetEntries(self):
521 """Return a list of entries contained by this entry
522
523 Returns:
524 List of entries, or None if none. A normal entry has no entries
525 within it so will return None
526 """
527 return None
528
Simon Glass91710b32018-07-17 13:25:32 -0600529 def GetArg(self, name, datatype=str):
530 """Get the value of an entry argument or device-tree-node property
531
532 Some node properties can be provided as arguments to binman. First check
533 the entry arguments, and fall back to the device tree if not found
534
535 Args:
536 name: Argument name
537 datatype: Data type (str or int)
538
539 Returns:
540 Value of argument as a string or int, or None if no value
541
542 Raises:
543 ValueError if the argument cannot be converted to in
544 """
Simon Glass29aa7362018-09-14 04:57:19 -0600545 value = state.GetEntryArg(name)
Simon Glass91710b32018-07-17 13:25:32 -0600546 if value is not None:
547 if datatype == int:
548 try:
549 value = int(value)
550 except ValueError:
551 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
552 (name, value))
553 elif datatype == str:
554 pass
555 else:
556 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
557 datatype)
558 else:
559 value = fdt_util.GetDatatype(self._node, name, datatype)
560 return value
Simon Glass969616c2018-07-17 13:25:36 -0600561
562 @staticmethod
563 def WriteDocs(modules, test_missing=None):
564 """Write out documentation about the various entry types to stdout
565
566 Args:
567 modules: List of modules to include
568 test_missing: Used for testing. This is a module to report
569 as missing
570 """
571 print('''Binman Entry Documentation
572===========================
573
574This file describes the entry types supported by binman. These entry types can
575be placed in an image one by one to build up a final firmware image. It is
576fairly easy to create new entry types. Just add a new file to the 'etype'
577directory. You can use the existing entries as examples.
578
579Note that some entries are subclasses of others, using and extending their
580features to produce new behaviours.
581
582
583''')
584 modules = sorted(modules)
585
586 # Don't show the test entry
587 if '_testing' in modules:
588 modules.remove('_testing')
589 missing = []
590 for name in modules:
Simon Glass0baeab72019-07-08 14:25:32 -0600591 if name.startswith('__'):
592 continue
Simon Glass75502932019-07-08 14:25:31 -0600593 module = Entry.Lookup(name, name)
Simon Glass969616c2018-07-17 13:25:36 -0600594 docs = getattr(module, '__doc__')
595 if test_missing == name:
596 docs = None
597 if docs:
598 lines = docs.splitlines()
599 first_line = lines[0]
600 rest = [line[4:] for line in lines[1:]]
601 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
602 print(hdr)
603 print('-' * len(hdr))
604 print('\n'.join(rest))
605 print()
606 print()
607 else:
608 missing.append(name)
609
610 if missing:
611 raise ValueError('Documentation is missing for modules: %s' %
612 ', '.join(missing))
Simon Glass639505b2018-09-14 04:57:11 -0600613
614 def GetUniqueName(self):
615 """Get a unique name for a node
616
617 Returns:
618 String containing a unique name for a node, consisting of the name
619 of all ancestors (starting from within the 'binman' node) separated
620 by a dot ('.'). This can be useful for generating unique filesnames
621 in the output directory.
622 """
623 name = self.name
624 node = self._node
625 while node.parent:
626 node = node.parent
627 if node.name == 'binman':
628 break
629 name = '%s.%s' % (node.name, name)
630 return name
Simon Glassfa79a812018-09-14 04:57:29 -0600631
632 def ExpandToLimit(self, limit):
633 """Expand an entry so that it ends at the given offset limit"""
634 if self.offset + self.size < limit:
635 self.size = limit - self.offset
636 # Request the contents again, since changing the size requires that
637 # the data grows. This should not fail, but check it to be sure.
638 if not self.ObtainContents():
639 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassc4056b82019-07-08 13:18:38 -0600640
641 def HasSibling(self, name):
642 """Check if there is a sibling of a given name
643
644 Returns:
645 True if there is an entry with this name in the the same section,
646 else False
647 """
648 return name in self.section.GetEntries()
Simon Glasscec34ba2019-07-08 14:25:28 -0600649
650 def GetSiblingImagePos(self, name):
651 """Return the image position of the given sibling
652
653 Returns:
654 Image position of sibling, or None if the sibling has no position,
655 or False if there is no such sibling
656 """
657 if not self.HasSibling(name):
658 return False
659 return self.section.GetEntries()[name].image_pos
Simon Glass6b156f82019-07-08 14:25:43 -0600660
661 @staticmethod
662 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
663 uncomp_size, offset, entry):
664 """Add a new entry to the entries list
665
666 Args:
667 entries: List (of EntryInfo objects) to add to
668 indent: Current indent level to add to list
669 name: Entry name (string)
670 etype: Entry type (string)
671 size: Entry size in bytes (int)
672 image_pos: Position within image in bytes (int)
673 uncomp_size: Uncompressed size if the entry uses compression, else
674 None
675 offset: Entry offset within parent in bytes (int)
676 entry: Entry object
677 """
678 entries.append(EntryInfo(indent, name, etype, size, image_pos,
679 uncomp_size, offset, entry))
680
681 def ListEntries(self, entries, indent):
682 """Add files in this entry to the list of entries
683
684 This can be overridden by subclasses which need different behaviour.
685
686 Args:
687 entries: List (of EntryInfo objects) to add to
688 indent: Current indent level to add to list
689 """
690 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
691 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glass4c613bf2019-07-08 14:25:50 -0600692
693 def ReadData(self, decomp=True):
694 """Read the data for an entry from the image
695
696 This is used when the image has been read in and we want to extract the
697 data for a particular entry from that image.
698
699 Args:
700 decomp: True to decompress any compressed data before returning it;
701 False to return the raw, uncompressed data
702
703 Returns:
704 Entry data (bytes)
705 """
706 # Use True here so that we get an uncompressed section to work from,
707 # although compressed sections are currently not supported
Simon Glass4d8151f2019-09-25 08:56:21 -0600708 tout.Debug("ReadChildData section '%s', entry '%s'" %
709 (self.section.GetPath(), self.GetPath()))
Simon Glass0cd8ace2019-07-20 12:24:04 -0600710 data = self.section.ReadChildData(self, decomp)
711 return data
Simon Glassaf8c45c2019-07-20 12:23:41 -0600712
Simon Glass23f00472019-09-25 08:56:20 -0600713 def ReadChildData(self, child, decomp=True):
Simon Glass4d8151f2019-09-25 08:56:21 -0600714 """Read the data for a particular child entry
Simon Glass23f00472019-09-25 08:56:20 -0600715
716 This reads data from the parent and extracts the piece that relates to
717 the given child.
718
719 Args:
Simon Glass4d8151f2019-09-25 08:56:21 -0600720 child: Child entry to read data for (must be valid)
Simon Glass23f00472019-09-25 08:56:20 -0600721 decomp: True to decompress any compressed data before returning it;
722 False to return the raw, uncompressed data
723
724 Returns:
725 Data for the child (bytes)
726 """
727 pass
728
Simon Glassaf8c45c2019-07-20 12:23:41 -0600729 def LoadData(self, decomp=True):
730 data = self.ReadData(decomp)
Simon Glass072959a2019-07-20 12:23:50 -0600731 self.contents_size = len(data)
Simon Glassaf8c45c2019-07-20 12:23:41 -0600732 self.ProcessContentsUpdate(data)
733 self.Detail('Loaded data size %x' % len(data))
Simon Glass990b1742019-07-20 12:23:46 -0600734
735 def GetImage(self):
736 """Get the image containing this entry
737
738 Returns:
739 Image object containing this entry
740 """
741 return self.section.GetImage()
Simon Glass072959a2019-07-20 12:23:50 -0600742
743 def WriteData(self, data, decomp=True):
744 """Write the data to an entry in the image
745
746 This is used when the image has been read in and we want to replace the
747 data for a particular entry in that image.
748
749 The image must be re-packed and written out afterwards.
750
751 Args:
752 data: Data to replace it with
753 decomp: True to compress the data if needed, False if data is
754 already compressed so should be used as is
755
756 Returns:
757 True if the data did not result in a resize of this entry, False if
758 the entry must be resized
759 """
760 self.contents_size = self.size
761 ok = self.ProcessContentsUpdate(data)
762 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glassd34af7a2019-07-20 12:24:05 -0600763 section_ok = self.section.WriteChildData(self)
764 return ok and section_ok
765
766 def WriteChildData(self, child):
767 """Handle writing the data in a child entry
768
769 This should be called on the child's parent section after the child's
770 data has been updated. It
771
772 This base-class implementation does nothing, since the base Entry object
773 does not have any children.
774
775 Args:
776 child: Child Entry that was written
777
778 Returns:
779 True if the section could be updated successfully, False if the
780 data is such that the section could not updat
781 """
782 return True
Simon Glass11453762019-07-20 12:23:55 -0600783
784 def GetSiblingOrder(self):
785 """Get the relative order of an entry amoung its siblings
786
787 Returns:
788 'start' if this entry is first among siblings, 'end' if last,
789 otherwise None
790 """
791 entries = list(self.section.GetEntries().values())
792 if entries:
793 if self == entries[0]:
794 return 'start'
795 elif self == entries[-1]:
796 return 'end'
797 return 'middle'