blob: 1244d36c20fcfa7be0b1600bd311e65287d2eed5 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassbf7fd502016-11-25 20:15:51 -07002# Copyright (c) 2016 Google, Inc
3#
Simon Glassbf7fd502016-11-25 20:15:51 -07004# Base class for all entries
5#
6
Simon Glass53af22a2018-07-17 13:25:32 -06007from collections import namedtuple
Simon Glassb4cf5f12019-10-31 07:42:59 -06008import importlib
Simon Glassbadf0ec2018-06-01 09:38:15 -06009import os
10import sys
Simon Glassc55a50f2018-09-14 04:57:19 -060011
12import fdt_util
Simon Glassbf7fd502016-11-25 20:15:51 -070013import tools
Simon Glass9f297b02019-07-20 12:23:36 -060014from tools import ToHex, ToHexSize
Simon Glasseea264e2019-07-08 14:25:49 -060015import tout
Simon Glassbf7fd502016-11-25 20:15:51 -070016
17modules = {}
18
Simon Glassbadf0ec2018-06-01 09:38:15 -060019our_path = os.path.dirname(os.path.realpath(__file__))
20
Simon Glass53af22a2018-07-17 13:25:32 -060021
22# An argument which can be passed to entries on the command line, in lieu of
23# device-tree properties.
24EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
25
Simon Glass41b8ba02019-07-08 14:25:43 -060026# Information about an entry for use when displaying summaries
27EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
28 'image_pos', 'uncomp_size', 'offset',
29 'entry'])
Simon Glass53af22a2018-07-17 13:25:32 -060030
Simon Glassbf7fd502016-11-25 20:15:51 -070031class Entry(object):
Simon Glass25ac0e62018-06-01 09:38:14 -060032 """An Entry in the section
Simon Glassbf7fd502016-11-25 20:15:51 -070033
34 An entry corresponds to a single node in the device-tree description
Simon Glass25ac0e62018-06-01 09:38:14 -060035 of the section. Each entry ends up being a part of the final section.
Simon Glassbf7fd502016-11-25 20:15:51 -070036 Entries can be placed either right next to each other, or with padding
37 between them. The type of the entry determines the data that is in it.
38
39 This class is not used by itself. All entry objects are subclasses of
40 Entry.
41
42 Attributes:
Simon Glass8122f392018-07-17 13:25:28 -060043 section: Section object containing this entry
Simon Glassbf7fd502016-11-25 20:15:51 -070044 node: The node that created this entry
Simon Glass3ab95982018-08-01 15:22:37 -060045 offset: Offset of entry within the section, None if not known yet (in
46 which case it will be calculated by Pack())
Simon Glassbf7fd502016-11-25 20:15:51 -070047 size: Entry size in bytes, None if not known
Simon Glass9a5d3dc2019-10-31 07:43:02 -060048 pre_reset_size: size as it was before ResetForPack(). This allows us to
49 keep track of the size we started with and detect size changes
Simon Glass8287ee82019-07-08 14:25:30 -060050 uncomp_size: Size of uncompressed data in bytes, if the entry is
51 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070052 contents_size: Size of contents in bytes, 0 by default
Simon Glass3ab95982018-08-01 15:22:37 -060053 align: Entry start offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070054 align_size: Entry size alignment, or None
Simon Glass3ab95982018-08-01 15:22:37 -060055 align_end: Entry end offset alignment, or None
Simon Glassbf7fd502016-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 Glass8287ee82019-07-08 14:25:30 -060059 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassc52c9e72019-07-08 14:25:37 -060060 orig_offset: Original offset value read from node
61 orig_size: Original size value read from node
Simon Glassbf7fd502016-11-25 20:15:51 -070062 """
Simon Glassc6bd6e22019-07-20 12:23:45 -060063 def __init__(self, section, etype, node, name_prefix=''):
Simon Glass8dbb7442019-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 Glass25ac0e62018-06-01 09:38:14 -060068 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -070069 self.etype = etype
70 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -060071 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -060072 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -070073 self.size = None
Simon Glass9a5d3dc2019-10-31 07:43:02 -060074 self.pre_reset_size = None
Simon Glass8287ee82019-07-08 14:25:30 -060075 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -060076 self.data = None
Simon Glassbf7fd502016-11-25 20:15:51 -070077 self.contents_size = 0
78 self.align = None
79 self.align_size = None
80 self.align_end = None
81 self.pad_before = 0
82 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -060083 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -060084 self.image_pos = None
Simon Glassba64a0b2018-09-14 04:57:29 -060085 self._expand_size = False
Simon Glass8287ee82019-07-08 14:25:30 -060086 self.compress = 'none'
Simon Glassbf7fd502016-11-25 20:15:51 -070087
88 @staticmethod
Simon Glassc073ced2019-07-08 14:25:31 -060089 def Lookup(node_path, etype):
Simon Glassfd8d1f72018-07-17 13:25:36 -060090 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -070091
92 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -060093 node_node: Path name of Node object containing information about
94 the entry to create (used for errors)
95 etype: Entry type to use
Simon Glassbf7fd502016-11-25 20:15:51 -070096
97 Returns:
Simon Glassfd8d1f72018-07-17 13:25:36 -060098 The entry class object if found, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070099 """
Simon Glassdd57c132018-06-01 09:38:11 -0600100 # Convert something like 'u-boot@0' to 'u_boot' since we are only
101 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700102 module_name = etype.replace('-', '_')
Simon Glassdd57c132018-06-01 09:38:11 -0600103 if '@' in module_name:
104 module_name = module_name.split('@')[0]
Simon Glassbf7fd502016-11-25 20:15:51 -0700105 module = modules.get(module_name)
106
Simon Glassbadf0ec2018-06-01 09:38:15 -0600107 # Also allow entry-type modules to be brought in from the etype directory.
108
Simon Glassbf7fd502016-11-25 20:15:51 -0700109 # Import the module if we have not already done so.
110 if not module:
Simon Glassbadf0ec2018-06-01 09:38:15 -0600111 old_path = sys.path
112 sys.path.insert(0, os.path.join(our_path, 'etype'))
Simon Glassbf7fd502016-11-25 20:15:51 -0700113 try:
Simon Glassb4cf5f12019-10-31 07:42:59 -0600114 module = importlib.import_module(module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600115 except ImportError as e:
116 raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
117 (etype, node_path, module_name, e))
Simon Glassbadf0ec2018-06-01 09:38:15 -0600118 finally:
119 sys.path = old_path
Simon Glassbf7fd502016-11-25 20:15:51 -0700120 modules[module_name] = module
121
Simon Glassfd8d1f72018-07-17 13:25:36 -0600122 # Look up the expected class name
123 return getattr(module, 'Entry_%s' % module_name)
124
125 @staticmethod
126 def Create(section, node, etype=None):
127 """Create a new entry for a node.
128
129 Args:
130 section: Section object containing this node
131 node: Node object containing information about the entry to
132 create
133 etype: Entry type to use, or None to work it out (used for tests)
134
135 Returns:
136 A new Entry object of the correct type (a subclass of Entry)
137 """
138 if not etype:
139 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glassc073ced2019-07-08 14:25:31 -0600140 obj = Entry.Lookup(node.path, etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600141
Simon Glassbf7fd502016-11-25 20:15:51 -0700142 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600143 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700144
145 def ReadNode(self):
146 """Read entry information from the node
147
Simon Glassc6bd6e22019-07-20 12:23:45 -0600148 This must be called as the first thing after the Entry is created.
149
Simon Glassbf7fd502016-11-25 20:15:51 -0700150 This reads all the fields we recognise from the node, ready for use.
151 """
Simon Glass15a587c2018-07-17 13:25:51 -0600152 if 'pos' in self._node.props:
153 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass3ab95982018-08-01 15:22:37 -0600154 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700155 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glass12bb1a92019-07-20 12:23:51 -0600156 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
157 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
158 if self.GetImage().copy_to_orig:
159 self.orig_offset = self.offset
160 self.orig_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600161
Simon Glassffded752019-07-08 14:25:46 -0600162 # These should not be set in input files, but are set in an FDT map,
163 # which is also read by this code.
164 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
165 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
166
Simon Glassbf7fd502016-11-25 20:15:51 -0700167 self.align = fdt_util.GetInt(self._node, 'align')
168 if tools.NotPowerOfTwo(self.align):
169 raise ValueError("Node '%s': Alignment %s must be a power of two" %
170 (self._node.path, self.align))
171 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
172 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
173 self.align_size = fdt_util.GetInt(self._node, 'align-size')
174 if tools.NotPowerOfTwo(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600175 self.Raise("Alignment size %s must be a power of two" %
176 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700177 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600178 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassba64a0b2018-09-14 04:57:29 -0600179 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
Simon Glassbf7fd502016-11-25 20:15:51 -0700180
Simon Glass6c234bf2018-09-14 04:57:18 -0600181 def GetDefaultFilename(self):
182 return None
183
Simon Glassa8adb6d2019-07-20 12:23:28 -0600184 def GetFdts(self):
185 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600186
187 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600188 Empty dict, if this entry is not a .dtb, otherwise:
189 Dict:
190 key: Filename from this entry (without the path)
Simon Glass4bdd3002019-07-20 12:23:31 -0600191 value: Tuple:
192 Fdt object for this dtb, or None if not available
193 Filename of file containing this dtb
Simon Glass539aece2018-09-14 04:57:22 -0600194 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600195 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600196
Simon Glass0a98b282018-09-14 04:57:28 -0600197 def ExpandEntries(self):
198 pass
199
Simon Glass078ab1a2018-07-06 10:27:41 -0600200 def AddMissingProperties(self):
201 """Add new properties to the device tree as needed for this entry"""
Simon Glassdbf6be92018-08-01 15:22:42 -0600202 for prop in ['offset', 'size', 'image-pos']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600203 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600204 state.AddZeroProp(self._node, prop)
Simon Glass12bb1a92019-07-20 12:23:51 -0600205 if self.GetImage().allow_repack:
206 if self.orig_offset is not None:
207 state.AddZeroProp(self._node, 'orig-offset', True)
208 if self.orig_size is not None:
209 state.AddZeroProp(self._node, 'orig-size', True)
210
Simon Glass8287ee82019-07-08 14:25:30 -0600211 if self.compress != 'none':
212 state.AddZeroProp(self._node, 'uncomp-size')
Simon Glasse0e5df92018-09-14 04:57:31 -0600213 err = state.CheckAddHashProp(self._node)
214 if err:
215 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600216
217 def SetCalculatedProperties(self):
218 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600219 state.SetInt(self._node, 'offset', self.offset)
220 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600221 base = self.section.GetRootSkipAtStart() if self.section else 0
222 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass12bb1a92019-07-20 12:23:51 -0600223 if self.GetImage().allow_repack:
224 if self.orig_offset is not None:
225 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
226 if self.orig_size is not None:
227 state.SetInt(self._node, 'orig-size', self.orig_size, True)
Simon Glass8287ee82019-07-08 14:25:30 -0600228 if self.uncomp_size is not None:
229 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Simon Glasse0e5df92018-09-14 04:57:31 -0600230 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600231
Simon Glassecab8972018-07-06 10:27:40 -0600232 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600233 """Allow entries to adjust the device tree
234
235 Some entries need to adjust the device tree for their purposes. This
236 may involve adding or deleting properties.
237
238 Returns:
239 True if processing is complete
240 False if processing could not be completed due to a dependency.
241 This will cause the entry to be retried after others have been
242 called
243 """
Simon Glassecab8972018-07-06 10:27:40 -0600244 return True
245
Simon Glassc8d48ef2018-06-01 09:38:21 -0600246 def SetPrefix(self, prefix):
247 """Set the name prefix for a node
248
249 Args:
250 prefix: Prefix to set, or '' to not use a prefix
251 """
252 if prefix:
253 self.name = prefix + self.name
254
Simon Glass5c890232018-07-06 10:27:19 -0600255 def SetContents(self, data):
256 """Set the contents of an entry
257
258 This sets both the data and content_size properties
259
260 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600261 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600262 """
263 self.data = data
264 self.contents_size = len(self.data)
265
266 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600267 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600268
Simon Glassa0dcaf22019-07-08 14:25:35 -0600269 This checks that the new data is the same size as the old. If the size
270 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600271
272 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600273 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600274
275 Raises:
276 ValueError if the new data size is not the same as the old
277 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600278 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600279 new_size = len(data)
Simon Glass61ec04f2019-07-20 12:23:58 -0600280 if state.AllowEntryExpansion() and new_size > self.contents_size:
281 # self.data will indicate the new size needed
282 size_ok = False
283 elif state.AllowEntryContraction() and new_size < self.contents_size:
284 size_ok = False
285
286 # If not allowed to change, try to deal with it or give up
287 if size_ok:
Simon Glassc52c9e72019-07-08 14:25:37 -0600288 if new_size > self.contents_size:
Simon Glass61ec04f2019-07-20 12:23:58 -0600289 self.Raise('Cannot update entry size from %d to %d' %
290 (self.contents_size, new_size))
291
292 # Don't let the data shrink. Pad it if necessary
293 if size_ok and new_size < self.contents_size:
294 data += tools.GetBytes(0, self.contents_size - new_size)
295
296 if not size_ok:
297 tout.Debug("Entry '%s' size change from %s to %s" % (
298 self._node.path, ToHex(self.contents_size),
299 ToHex(new_size)))
Simon Glass5c890232018-07-06 10:27:19 -0600300 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600301 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600302
Simon Glassbf7fd502016-11-25 20:15:51 -0700303 def ObtainContents(self):
304 """Figure out the contents of an entry.
305
306 Returns:
307 True if the contents were found, False if another call is needed
308 after the other entries are processed.
309 """
310 # No contents by default: subclasses can implement this
311 return True
312
Simon Glassc52c9e72019-07-08 14:25:37 -0600313 def ResetForPack(self):
314 """Reset offset/size fields so that packing can be done again"""
Simon Glass9f297b02019-07-20 12:23:36 -0600315 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
316 (ToHex(self.offset), ToHex(self.orig_offset),
317 ToHex(self.size), ToHex(self.orig_size)))
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600318 self.pre_reset_size = self.size
Simon Glassc52c9e72019-07-08 14:25:37 -0600319 self.offset = self.orig_offset
320 self.size = self.orig_size
321
Simon Glass3ab95982018-08-01 15:22:37 -0600322 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600323 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700324
325 Most of the time the entries are not fully specified. There may be
326 an alignment but no size. In that case we take the size from the
327 contents of the entry.
328
Simon Glass3ab95982018-08-01 15:22:37 -0600329 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700330
Simon Glass3ab95982018-08-01 15:22:37 -0600331 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700332 entry will be know.
333
334 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600335 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700336
337 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600338 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700339 """
Simon Glass9f297b02019-07-20 12:23:36 -0600340 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
341 (ToHex(self.offset), ToHex(self.size),
342 self.contents_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600343 if self.offset is None:
344 if self.offset_unset:
345 self.Raise('No offset set with offset-unset: should another '
346 'entry provide this correct offset?')
347 self.offset = tools.Align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700348 needed = self.pad_before + self.contents_size + self.pad_after
349 needed = tools.Align(needed, self.align_size)
350 size = self.size
351 if not size:
352 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600353 new_offset = self.offset + size
354 aligned_offset = tools.Align(new_offset, self.align_end)
355 if aligned_offset != new_offset:
356 size = aligned_offset - self.offset
357 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700358
359 if not self.size:
360 self.size = size
361
362 if self.size < needed:
363 self.Raise("Entry contents size is %#x (%d) but entry size is "
364 "%#x (%d)" % (needed, needed, self.size, self.size))
365 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600366 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700367 # conflict with the provided alignment values
368 if self.size != tools.Align(self.size, self.align_size):
369 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
370 (self.size, self.size, self.align_size, self.align_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600371 if self.offset != tools.Align(self.offset, self.align):
372 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
373 (self.offset, self.offset, self.align, self.align))
Simon Glass9f297b02019-07-20 12:23:36 -0600374 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
375 (self.offset, self.size, self.contents_size, new_offset))
Simon Glassbf7fd502016-11-25 20:15:51 -0700376
Simon Glass3ab95982018-08-01 15:22:37 -0600377 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700378
379 def Raise(self, msg):
380 """Convenience function to raise an error referencing a node"""
381 raise ValueError("Node '%s': %s" % (self._node.path, msg))
382
Simon Glass9f297b02019-07-20 12:23:36 -0600383 def Detail(self, msg):
384 """Convenience function to log detail referencing a node"""
385 tag = "Node '%s'" % self._node.path
386 tout.Detail('%30s: %s' % (tag, msg))
387
Simon Glass53af22a2018-07-17 13:25:32 -0600388 def GetEntryArgsOrProps(self, props, required=False):
389 """Return the values of a set of properties
390
391 Args:
392 props: List of EntryArg objects
393
394 Raises:
395 ValueError if a property is not found
396 """
397 values = []
398 missing = []
399 for prop in props:
400 python_prop = prop.name.replace('-', '_')
401 if hasattr(self, python_prop):
402 value = getattr(self, python_prop)
403 else:
404 value = None
405 if value is None:
406 value = self.GetArg(prop.name, prop.datatype)
407 if value is None and required:
408 missing.append(prop.name)
409 values.append(value)
410 if missing:
411 self.Raise('Missing required properties/entry args: %s' %
412 (', '.join(missing)))
413 return values
414
Simon Glassbf7fd502016-11-25 20:15:51 -0700415 def GetPath(self):
416 """Get the path of a node
417
418 Returns:
419 Full path of the node for this entry
420 """
421 return self._node.path
422
423 def GetData(self):
Simon Glass9f297b02019-07-20 12:23:36 -0600424 self.Detail('GetData: size %s' % ToHexSize(self.data))
Simon Glassbf7fd502016-11-25 20:15:51 -0700425 return self.data
426
Simon Glass3ab95982018-08-01 15:22:37 -0600427 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600428 """Get the offsets for siblings
429
430 Some entry types can contain information about the position or size of
431 other entries. An example of this is the Intel Flash Descriptor, which
432 knows where the Intel Management Engine section should go.
433
434 If this entry knows about the position of other entries, it can specify
435 this by returning values here
436
437 Returns:
438 Dict:
439 key: Entry type
440 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600441 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600442 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700443 return {}
444
Simon Glasscf549042019-07-08 13:18:39 -0600445 def SetOffsetSize(self, offset, size):
446 """Set the offset and/or size of an entry
447
448 Args:
449 offset: New offset, or None to leave alone
450 size: New size, or None to leave alone
451 """
452 if offset is not None:
453 self.offset = offset
454 if size is not None:
455 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700456
Simon Glassdbf6be92018-08-01 15:22:42 -0600457 def SetImagePos(self, image_pos):
458 """Set the position in the image
459
460 Args:
461 image_pos: Position of this entry in the image
462 """
463 self.image_pos = image_pos + self.offset
464
Simon Glassbf7fd502016-11-25 20:15:51 -0700465 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600466 """Do any post-packing updates of entry contents
467
468 This function should call ProcessContentsUpdate() to update the entry
469 contents, if necessary, returning its return value here.
470
471 Args:
472 data: Data to set to the contents (bytes)
473
474 Returns:
475 True if the new data size is OK, False if expansion is needed
476
477 Raises:
478 ValueError if the new data size is not the same as the old and
479 state.AllowEntryExpansion() is False
480 """
481 return True
Simon Glass19790632017-11-13 18:55:01 -0700482
Simon Glassf55382b2018-06-01 09:38:13 -0600483 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700484 """Write symbol values into binary files for access at run time
485
486 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600487 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700488 """
489 pass
Simon Glass18546952018-06-01 09:38:16 -0600490
Simon Glass3ab95982018-08-01 15:22:37 -0600491 def CheckOffset(self):
492 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600493
Simon Glass3ab95982018-08-01 15:22:37 -0600494 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600495 than having to be fully inside their section). Sub-classes can implement
496 this function and raise if there is a problem.
497 """
498 pass
Simon Glass3b0c3822018-06-01 09:38:20 -0600499
Simon Glass8122f392018-07-17 13:25:28 -0600500 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600501 def GetStr(value):
502 if value is None:
503 return '<none> '
504 return '%08x' % value
505
506 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600507 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600508 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
509 Entry.GetStr(offset), Entry.GetStr(size),
510 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600511
Simon Glass3b0c3822018-06-01 09:38:20 -0600512 def WriteMap(self, fd, indent):
513 """Write a map of the entry to a .map file
514
515 Args:
516 fd: File to write the map to
517 indent: Curent indent level of map (0=none, 1=one level, etc.)
518 """
Simon Glass1be70d22018-07-17 13:25:49 -0600519 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
520 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600521
Simon Glass11e36cc2018-07-17 13:25:38 -0600522 def GetEntries(self):
523 """Return a list of entries contained by this entry
524
525 Returns:
526 List of entries, or None if none. A normal entry has no entries
527 within it so will return None
528 """
529 return None
530
Simon Glass53af22a2018-07-17 13:25:32 -0600531 def GetArg(self, name, datatype=str):
532 """Get the value of an entry argument or device-tree-node property
533
534 Some node properties can be provided as arguments to binman. First check
535 the entry arguments, and fall back to the device tree if not found
536
537 Args:
538 name: Argument name
539 datatype: Data type (str or int)
540
541 Returns:
542 Value of argument as a string or int, or None if no value
543
544 Raises:
545 ValueError if the argument cannot be converted to in
546 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600547 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600548 if value is not None:
549 if datatype == int:
550 try:
551 value = int(value)
552 except ValueError:
553 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
554 (name, value))
555 elif datatype == str:
556 pass
557 else:
558 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
559 datatype)
560 else:
561 value = fdt_util.GetDatatype(self._node, name, datatype)
562 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600563
564 @staticmethod
565 def WriteDocs(modules, test_missing=None):
566 """Write out documentation about the various entry types to stdout
567
568 Args:
569 modules: List of modules to include
570 test_missing: Used for testing. This is a module to report
571 as missing
572 """
573 print('''Binman Entry Documentation
574===========================
575
576This file describes the entry types supported by binman. These entry types can
577be placed in an image one by one to build up a final firmware image. It is
578fairly easy to create new entry types. Just add a new file to the 'etype'
579directory. You can use the existing entries as examples.
580
581Note that some entries are subclasses of others, using and extending their
582features to produce new behaviours.
583
584
585''')
586 modules = sorted(modules)
587
588 # Don't show the test entry
589 if '_testing' in modules:
590 modules.remove('_testing')
591 missing = []
592 for name in modules:
Simon Glasse4304402019-07-08 14:25:32 -0600593 if name.startswith('__'):
594 continue
Simon Glassc073ced2019-07-08 14:25:31 -0600595 module = Entry.Lookup(name, name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600596 docs = getattr(module, '__doc__')
597 if test_missing == name:
598 docs = None
599 if docs:
600 lines = docs.splitlines()
601 first_line = lines[0]
602 rest = [line[4:] for line in lines[1:]]
603 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
604 print(hdr)
605 print('-' * len(hdr))
606 print('\n'.join(rest))
607 print()
608 print()
609 else:
610 missing.append(name)
611
612 if missing:
613 raise ValueError('Documentation is missing for modules: %s' %
614 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600615
616 def GetUniqueName(self):
617 """Get a unique name for a node
618
619 Returns:
620 String containing a unique name for a node, consisting of the name
621 of all ancestors (starting from within the 'binman' node) separated
622 by a dot ('.'). This can be useful for generating unique filesnames
623 in the output directory.
624 """
625 name = self.name
626 node = self._node
627 while node.parent:
628 node = node.parent
629 if node.name == 'binman':
630 break
631 name = '%s.%s' % (node.name, name)
632 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600633
634 def ExpandToLimit(self, limit):
635 """Expand an entry so that it ends at the given offset limit"""
636 if self.offset + self.size < limit:
637 self.size = limit - self.offset
638 # Request the contents again, since changing the size requires that
639 # the data grows. This should not fail, but check it to be sure.
640 if not self.ObtainContents():
641 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600642
643 def HasSibling(self, name):
644 """Check if there is a sibling of a given name
645
646 Returns:
647 True if there is an entry with this name in the the same section,
648 else False
649 """
650 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600651
652 def GetSiblingImagePos(self, name):
653 """Return the image position of the given sibling
654
655 Returns:
656 Image position of sibling, or None if the sibling has no position,
657 or False if there is no such sibling
658 """
659 if not self.HasSibling(name):
660 return False
661 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600662
663 @staticmethod
664 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
665 uncomp_size, offset, entry):
666 """Add a new entry to the entries list
667
668 Args:
669 entries: List (of EntryInfo objects) to add to
670 indent: Current indent level to add to list
671 name: Entry name (string)
672 etype: Entry type (string)
673 size: Entry size in bytes (int)
674 image_pos: Position within image in bytes (int)
675 uncomp_size: Uncompressed size if the entry uses compression, else
676 None
677 offset: Entry offset within parent in bytes (int)
678 entry: Entry object
679 """
680 entries.append(EntryInfo(indent, name, etype, size, image_pos,
681 uncomp_size, offset, entry))
682
683 def ListEntries(self, entries, indent):
684 """Add files in this entry to the list of entries
685
686 This can be overridden by subclasses which need different behaviour.
687
688 Args:
689 entries: List (of EntryInfo objects) to add to
690 indent: Current indent level to add to list
691 """
692 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
693 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600694
695 def ReadData(self, decomp=True):
696 """Read the data for an entry from the image
697
698 This is used when the image has been read in and we want to extract the
699 data for a particular entry from that image.
700
701 Args:
702 decomp: True to decompress any compressed data before returning it;
703 False to return the raw, uncompressed data
704
705 Returns:
706 Entry data (bytes)
707 """
708 # Use True here so that we get an uncompressed section to work from,
709 # although compressed sections are currently not supported
Simon Glass2d553c02019-09-25 08:56:21 -0600710 tout.Debug("ReadChildData section '%s', entry '%s'" %
711 (self.section.GetPath(), self.GetPath()))
Simon Glassa9cd39e2019-07-20 12:24:04 -0600712 data = self.section.ReadChildData(self, decomp)
713 return data
Simon Glassd5079332019-07-20 12:23:41 -0600714
Simon Glass4e185e82019-09-25 08:56:20 -0600715 def ReadChildData(self, child, decomp=True):
Simon Glass2d553c02019-09-25 08:56:21 -0600716 """Read the data for a particular child entry
Simon Glass4e185e82019-09-25 08:56:20 -0600717
718 This reads data from the parent and extracts the piece that relates to
719 the given child.
720
721 Args:
Simon Glass2d553c02019-09-25 08:56:21 -0600722 child: Child entry to read data for (must be valid)
Simon Glass4e185e82019-09-25 08:56:20 -0600723 decomp: True to decompress any compressed data before returning it;
724 False to return the raw, uncompressed data
725
726 Returns:
727 Data for the child (bytes)
728 """
729 pass
730
Simon Glassd5079332019-07-20 12:23:41 -0600731 def LoadData(self, decomp=True):
732 data = self.ReadData(decomp)
Simon Glass10f9d002019-07-20 12:23:50 -0600733 self.contents_size = len(data)
Simon Glassd5079332019-07-20 12:23:41 -0600734 self.ProcessContentsUpdate(data)
735 self.Detail('Loaded data size %x' % len(data))
Simon Glassc5ad04b2019-07-20 12:23:46 -0600736
737 def GetImage(self):
738 """Get the image containing this entry
739
740 Returns:
741 Image object containing this entry
742 """
743 return self.section.GetImage()
Simon Glass10f9d002019-07-20 12:23:50 -0600744
745 def WriteData(self, data, decomp=True):
746 """Write the data to an entry in the image
747
748 This is used when the image has been read in and we want to replace the
749 data for a particular entry in that image.
750
751 The image must be re-packed and written out afterwards.
752
753 Args:
754 data: Data to replace it with
755 decomp: True to compress the data if needed, False if data is
756 already compressed so should be used as is
757
758 Returns:
759 True if the data did not result in a resize of this entry, False if
760 the entry must be resized
761 """
Simon Glass9a5d3dc2019-10-31 07:43:02 -0600762 if self.size is not None:
763 self.contents_size = self.size
764 else:
765 self.contents_size = self.pre_reset_size
Simon Glass10f9d002019-07-20 12:23:50 -0600766 ok = self.ProcessContentsUpdate(data)
767 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
Simon Glass7210c892019-07-20 12:24:05 -0600768 section_ok = self.section.WriteChildData(self)
769 return ok and section_ok
770
771 def WriteChildData(self, child):
772 """Handle writing the data in a child entry
773
774 This should be called on the child's parent section after the child's
775 data has been updated. It
776
777 This base-class implementation does nothing, since the base Entry object
778 does not have any children.
779
780 Args:
781 child: Child Entry that was written
782
783 Returns:
784 True if the section could be updated successfully, False if the
785 data is such that the section could not updat
786 """
787 return True
Simon Glasseba1f0c2019-07-20 12:23:55 -0600788
789 def GetSiblingOrder(self):
790 """Get the relative order of an entry amoung its siblings
791
792 Returns:
793 'start' if this entry is first among siblings, 'end' if last,
794 otherwise None
795 """
796 entries = list(self.section.GetEntries().values())
797 if entries:
798 if self == entries[0]:
799 return 'start'
800 elif self == entries[-1]:
801 return 'end'
802 return 'middle'