blob: 2ed9dc0d6f4b698871243744c5eb716c82c34359 [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 Glass3b0c3822018-06-01 09:38:20 -06007from __future__ import print_function
8
Simon Glass53af22a2018-07-17 13:25:32 -06009from collections import namedtuple
10
Simon Glassbf7fd502016-11-25 20:15:51 -070011# importlib was introduced in Python 2.7 but there was a report of it not
12# working in 2.7.12, so we work around this:
13# http://lists.denx.de/pipermail/u-boot/2016-October/269729.html
14try:
15 import importlib
16 have_importlib = True
17except:
18 have_importlib = False
19
Simon Glassbadf0ec2018-06-01 09:38:15 -060020import os
21import sys
Simon Glassc55a50f2018-09-14 04:57:19 -060022
23import fdt_util
24import state
Simon Glassbf7fd502016-11-25 20:15:51 -070025import tools
Simon Glasseea264e2019-07-08 14:25:49 -060026import tout
Simon Glassbf7fd502016-11-25 20:15:51 -070027
28modules = {}
29
Simon Glassbadf0ec2018-06-01 09:38:15 -060030our_path = os.path.dirname(os.path.realpath(__file__))
31
Simon Glass53af22a2018-07-17 13:25:32 -060032
33# An argument which can be passed to entries on the command line, in lieu of
34# device-tree properties.
35EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
36
Simon Glass41b8ba02019-07-08 14:25:43 -060037# Information about an entry for use when displaying summaries
38EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
39 'image_pos', 'uncomp_size', 'offset',
40 'entry'])
Simon Glass53af22a2018-07-17 13:25:32 -060041
Simon Glassbf7fd502016-11-25 20:15:51 -070042class Entry(object):
Simon Glass25ac0e62018-06-01 09:38:14 -060043 """An Entry in the section
Simon Glassbf7fd502016-11-25 20:15:51 -070044
45 An entry corresponds to a single node in the device-tree description
Simon Glass25ac0e62018-06-01 09:38:14 -060046 of the section. Each entry ends up being a part of the final section.
Simon Glassbf7fd502016-11-25 20:15:51 -070047 Entries can be placed either right next to each other, or with padding
48 between them. The type of the entry determines the data that is in it.
49
50 This class is not used by itself. All entry objects are subclasses of
51 Entry.
52
53 Attributes:
Simon Glass8122f392018-07-17 13:25:28 -060054 section: Section object containing this entry
Simon Glassbf7fd502016-11-25 20:15:51 -070055 node: The node that created this entry
Simon Glass3ab95982018-08-01 15:22:37 -060056 offset: Offset of entry within the section, None if not known yet (in
57 which case it will be calculated by Pack())
Simon Glassbf7fd502016-11-25 20:15:51 -070058 size: Entry size in bytes, None if not known
Simon Glass8287ee82019-07-08 14:25:30 -060059 uncomp_size: Size of uncompressed data in bytes, if the entry is
60 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070061 contents_size: Size of contents in bytes, 0 by default
Simon Glass3ab95982018-08-01 15:22:37 -060062 align: Entry start offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070063 align_size: Entry size alignment, or None
Simon Glass3ab95982018-08-01 15:22:37 -060064 align_end: Entry end offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070065 pad_before: Number of pad bytes before the contents, 0 if none
66 pad_after: Number of pad bytes after the contents, 0 if none
67 data: Contents of entry (string of bytes)
Simon Glass8287ee82019-07-08 14:25:30 -060068 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassc52c9e72019-07-08 14:25:37 -060069 orig_offset: Original offset value read from node
70 orig_size: Original size value read from node
Simon Glassbf7fd502016-11-25 20:15:51 -070071 """
Simon Glassc8d48ef2018-06-01 09:38:21 -060072 def __init__(self, section, etype, node, read_node=True, name_prefix=''):
Simon Glass25ac0e62018-06-01 09:38:14 -060073 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -070074 self.etype = etype
75 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -060076 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -060077 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -070078 self.size = None
Simon Glass8287ee82019-07-08 14:25:30 -060079 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -060080 self.data = None
Simon Glassbf7fd502016-11-25 20:15:51 -070081 self.contents_size = 0
82 self.align = None
83 self.align_size = None
84 self.align_end = None
85 self.pad_before = 0
86 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -060087 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -060088 self.image_pos = None
Simon Glassba64a0b2018-09-14 04:57:29 -060089 self._expand_size = False
Simon Glass8287ee82019-07-08 14:25:30 -060090 self.compress = 'none'
Simon Glassbf7fd502016-11-25 20:15:51 -070091 if read_node:
92 self.ReadNode()
93
94 @staticmethod
Simon Glassc073ced2019-07-08 14:25:31 -060095 def Lookup(node_path, etype):
Simon Glassfd8d1f72018-07-17 13:25:36 -060096 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -070097
98 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -060099 node_node: Path name of Node object containing information about
100 the entry to create (used for errors)
101 etype: Entry type to use
Simon Glassbf7fd502016-11-25 20:15:51 -0700102
103 Returns:
Simon Glassfd8d1f72018-07-17 13:25:36 -0600104 The entry class object if found, else None
Simon Glassbf7fd502016-11-25 20:15:51 -0700105 """
Simon Glassdd57c132018-06-01 09:38:11 -0600106 # Convert something like 'u-boot@0' to 'u_boot' since we are only
107 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700108 module_name = etype.replace('-', '_')
Simon Glassdd57c132018-06-01 09:38:11 -0600109 if '@' in module_name:
110 module_name = module_name.split('@')[0]
Simon Glassbf7fd502016-11-25 20:15:51 -0700111 module = modules.get(module_name)
112
Simon Glassbadf0ec2018-06-01 09:38:15 -0600113 # Also allow entry-type modules to be brought in from the etype directory.
114
Simon Glassbf7fd502016-11-25 20:15:51 -0700115 # Import the module if we have not already done so.
116 if not module:
Simon Glassbadf0ec2018-06-01 09:38:15 -0600117 old_path = sys.path
118 sys.path.insert(0, os.path.join(our_path, 'etype'))
Simon Glassbf7fd502016-11-25 20:15:51 -0700119 try:
120 if have_importlib:
121 module = importlib.import_module(module_name)
122 else:
123 module = __import__(module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600124 except ImportError as e:
125 raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
126 (etype, node_path, module_name, e))
Simon Glassbadf0ec2018-06-01 09:38:15 -0600127 finally:
128 sys.path = old_path
Simon Glassbf7fd502016-11-25 20:15:51 -0700129 modules[module_name] = module
130
Simon Glassfd8d1f72018-07-17 13:25:36 -0600131 # Look up the expected class name
132 return getattr(module, 'Entry_%s' % module_name)
133
134 @staticmethod
135 def Create(section, node, etype=None):
136 """Create a new entry for a node.
137
138 Args:
139 section: Section object containing this node
140 node: Node object containing information about the entry to
141 create
142 etype: Entry type to use, or None to work it out (used for tests)
143
144 Returns:
145 A new Entry object of the correct type (a subclass of Entry)
146 """
147 if not etype:
148 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glassc073ced2019-07-08 14:25:31 -0600149 obj = Entry.Lookup(node.path, etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600150
Simon Glassbf7fd502016-11-25 20:15:51 -0700151 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600152 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700153
154 def ReadNode(self):
155 """Read entry information from the node
156
157 This reads all the fields we recognise from the node, ready for use.
158 """
Simon Glass15a587c2018-07-17 13:25:51 -0600159 if 'pos' in self._node.props:
160 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass3ab95982018-08-01 15:22:37 -0600161 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700162 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glassc52c9e72019-07-08 14:25:37 -0600163 self.orig_offset = self.offset
164 self.orig_size = self.size
165
Simon Glassffded752019-07-08 14:25:46 -0600166 # These should not be set in input files, but are set in an FDT map,
167 # which is also read by this code.
168 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
169 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
170
Simon Glassbf7fd502016-11-25 20:15:51 -0700171 self.align = fdt_util.GetInt(self._node, 'align')
172 if tools.NotPowerOfTwo(self.align):
173 raise ValueError("Node '%s': Alignment %s must be a power of two" %
174 (self._node.path, self.align))
175 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
176 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
177 self.align_size = fdt_util.GetInt(self._node, 'align-size')
178 if tools.NotPowerOfTwo(self.align_size):
Simon Glass8beb11e2019-07-08 14:25:47 -0600179 self.Raise("Alignment size %s must be a power of two" %
180 self.align_size)
Simon Glassbf7fd502016-11-25 20:15:51 -0700181 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600182 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassba64a0b2018-09-14 04:57:29 -0600183 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
Simon Glassbf7fd502016-11-25 20:15:51 -0700184
Simon Glass6c234bf2018-09-14 04:57:18 -0600185 def GetDefaultFilename(self):
186 return None
187
Simon Glassa8adb6d2019-07-20 12:23:28 -0600188 def GetFdts(self):
189 """Get the device trees used by this entry
Simon Glass539aece2018-09-14 04:57:22 -0600190
191 Returns:
Simon Glassa8adb6d2019-07-20 12:23:28 -0600192 Empty dict, if this entry is not a .dtb, otherwise:
193 Dict:
194 key: Filename from this entry (without the path)
195 value: Fdt object for this dtb, or None if not available
Simon Glass539aece2018-09-14 04:57:22 -0600196 """
Simon Glassa8adb6d2019-07-20 12:23:28 -0600197 return {}
Simon Glass539aece2018-09-14 04:57:22 -0600198
Simon Glass0a98b282018-09-14 04:57:28 -0600199 def ExpandEntries(self):
200 pass
201
Simon Glass078ab1a2018-07-06 10:27:41 -0600202 def AddMissingProperties(self):
203 """Add new properties to the device tree as needed for this entry"""
Simon Glassdbf6be92018-08-01 15:22:42 -0600204 for prop in ['offset', 'size', 'image-pos']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600205 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600206 state.AddZeroProp(self._node, prop)
Simon Glass8287ee82019-07-08 14:25:30 -0600207 if self.compress != 'none':
208 state.AddZeroProp(self._node, 'uncomp-size')
Simon Glasse0e5df92018-09-14 04:57:31 -0600209 err = state.CheckAddHashProp(self._node)
210 if err:
211 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600212
213 def SetCalculatedProperties(self):
214 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600215 state.SetInt(self._node, 'offset', self.offset)
216 state.SetInt(self._node, 'size', self.size)
Simon Glass8beb11e2019-07-08 14:25:47 -0600217 base = self.section.GetRootSkipAtStart() if self.section else 0
218 state.SetInt(self._node, 'image-pos', self.image_pos - base)
Simon Glass8287ee82019-07-08 14:25:30 -0600219 if self.uncomp_size is not None:
220 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Simon Glasse0e5df92018-09-14 04:57:31 -0600221 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600222
Simon Glassecab8972018-07-06 10:27:40 -0600223 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600224 """Allow entries to adjust the device tree
225
226 Some entries need to adjust the device tree for their purposes. This
227 may involve adding or deleting properties.
228
229 Returns:
230 True if processing is complete
231 False if processing could not be completed due to a dependency.
232 This will cause the entry to be retried after others have been
233 called
234 """
Simon Glassecab8972018-07-06 10:27:40 -0600235 return True
236
Simon Glassc8d48ef2018-06-01 09:38:21 -0600237 def SetPrefix(self, prefix):
238 """Set the name prefix for a node
239
240 Args:
241 prefix: Prefix to set, or '' to not use a prefix
242 """
243 if prefix:
244 self.name = prefix + self.name
245
Simon Glass5c890232018-07-06 10:27:19 -0600246 def SetContents(self, data):
247 """Set the contents of an entry
248
249 This sets both the data and content_size properties
250
251 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600252 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600253 """
254 self.data = data
255 self.contents_size = len(self.data)
256
257 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600258 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600259
Simon Glassa0dcaf22019-07-08 14:25:35 -0600260 This checks that the new data is the same size as the old. If the size
261 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600262
263 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600264 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600265
266 Raises:
267 ValueError if the new data size is not the same as the old
268 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600269 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600270 new_size = len(data)
271 if state.AllowEntryExpansion():
272 if new_size > self.contents_size:
Simon Glasseea264e2019-07-08 14:25:49 -0600273 tout.Debug("Entry '%s' size change from %#x to %#x" % (
Simon Glassc52c9e72019-07-08 14:25:37 -0600274 self._node.path, self.contents_size, new_size))
275 # self.data will indicate the new size needed
276 size_ok = False
277 elif new_size != self.contents_size:
Simon Glass5c890232018-07-06 10:27:19 -0600278 self.Raise('Cannot update entry size from %d to %d' %
Simon Glassc52c9e72019-07-08 14:25:37 -0600279 (self.contents_size, new_size))
Simon Glass5c890232018-07-06 10:27:19 -0600280 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600281 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600282
Simon Glassbf7fd502016-11-25 20:15:51 -0700283 def ObtainContents(self):
284 """Figure out the contents of an entry.
285
286 Returns:
287 True if the contents were found, False if another call is needed
288 after the other entries are processed.
289 """
290 # No contents by default: subclasses can implement this
291 return True
292
Simon Glassc52c9e72019-07-08 14:25:37 -0600293 def ResetForPack(self):
294 """Reset offset/size fields so that packing can be done again"""
295 self.offset = self.orig_offset
296 self.size = self.orig_size
297
Simon Glass3ab95982018-08-01 15:22:37 -0600298 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600299 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700300
301 Most of the time the entries are not fully specified. There may be
302 an alignment but no size. In that case we take the size from the
303 contents of the entry.
304
Simon Glass3ab95982018-08-01 15:22:37 -0600305 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700306
Simon Glass3ab95982018-08-01 15:22:37 -0600307 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700308 entry will be know.
309
310 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600311 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700312
313 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600314 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700315 """
Simon Glass3ab95982018-08-01 15:22:37 -0600316 if self.offset is None:
317 if self.offset_unset:
318 self.Raise('No offset set with offset-unset: should another '
319 'entry provide this correct offset?')
320 self.offset = tools.Align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700321 needed = self.pad_before + self.contents_size + self.pad_after
322 needed = tools.Align(needed, self.align_size)
323 size = self.size
324 if not size:
325 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600326 new_offset = self.offset + size
327 aligned_offset = tools.Align(new_offset, self.align_end)
328 if aligned_offset != new_offset:
329 size = aligned_offset - self.offset
330 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700331
332 if not self.size:
333 self.size = size
334
335 if self.size < needed:
336 self.Raise("Entry contents size is %#x (%d) but entry size is "
337 "%#x (%d)" % (needed, needed, self.size, self.size))
338 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600339 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700340 # conflict with the provided alignment values
341 if self.size != tools.Align(self.size, self.align_size):
342 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
343 (self.size, self.size, self.align_size, self.align_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600344 if self.offset != tools.Align(self.offset, self.align):
345 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
346 (self.offset, self.offset, self.align, self.align))
Simon Glassbf7fd502016-11-25 20:15:51 -0700347
Simon Glass3ab95982018-08-01 15:22:37 -0600348 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700349
350 def Raise(self, msg):
351 """Convenience function to raise an error referencing a node"""
352 raise ValueError("Node '%s': %s" % (self._node.path, msg))
353
Simon Glass53af22a2018-07-17 13:25:32 -0600354 def GetEntryArgsOrProps(self, props, required=False):
355 """Return the values of a set of properties
356
357 Args:
358 props: List of EntryArg objects
359
360 Raises:
361 ValueError if a property is not found
362 """
363 values = []
364 missing = []
365 for prop in props:
366 python_prop = prop.name.replace('-', '_')
367 if hasattr(self, python_prop):
368 value = getattr(self, python_prop)
369 else:
370 value = None
371 if value is None:
372 value = self.GetArg(prop.name, prop.datatype)
373 if value is None and required:
374 missing.append(prop.name)
375 values.append(value)
376 if missing:
377 self.Raise('Missing required properties/entry args: %s' %
378 (', '.join(missing)))
379 return values
380
Simon Glassbf7fd502016-11-25 20:15:51 -0700381 def GetPath(self):
382 """Get the path of a node
383
384 Returns:
385 Full path of the node for this entry
386 """
387 return self._node.path
388
389 def GetData(self):
390 return self.data
391
Simon Glass3ab95982018-08-01 15:22:37 -0600392 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600393 """Get the offsets for siblings
394
395 Some entry types can contain information about the position or size of
396 other entries. An example of this is the Intel Flash Descriptor, which
397 knows where the Intel Management Engine section should go.
398
399 If this entry knows about the position of other entries, it can specify
400 this by returning values here
401
402 Returns:
403 Dict:
404 key: Entry type
405 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600406 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600407 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700408 return {}
409
Simon Glasscf549042019-07-08 13:18:39 -0600410 def SetOffsetSize(self, offset, size):
411 """Set the offset and/or size of an entry
412
413 Args:
414 offset: New offset, or None to leave alone
415 size: New size, or None to leave alone
416 """
417 if offset is not None:
418 self.offset = offset
419 if size is not None:
420 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700421
Simon Glassdbf6be92018-08-01 15:22:42 -0600422 def SetImagePos(self, image_pos):
423 """Set the position in the image
424
425 Args:
426 image_pos: Position of this entry in the image
427 """
428 self.image_pos = image_pos + self.offset
429
Simon Glassbf7fd502016-11-25 20:15:51 -0700430 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600431 """Do any post-packing updates of entry contents
432
433 This function should call ProcessContentsUpdate() to update the entry
434 contents, if necessary, returning its return value here.
435
436 Args:
437 data: Data to set to the contents (bytes)
438
439 Returns:
440 True if the new data size is OK, False if expansion is needed
441
442 Raises:
443 ValueError if the new data size is not the same as the old and
444 state.AllowEntryExpansion() is False
445 """
446 return True
Simon Glass19790632017-11-13 18:55:01 -0700447
Simon Glassf55382b2018-06-01 09:38:13 -0600448 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700449 """Write symbol values into binary files for access at run time
450
451 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600452 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700453 """
454 pass
Simon Glass18546952018-06-01 09:38:16 -0600455
Simon Glass3ab95982018-08-01 15:22:37 -0600456 def CheckOffset(self):
457 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600458
Simon Glass3ab95982018-08-01 15:22:37 -0600459 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600460 than having to be fully inside their section). Sub-classes can implement
461 this function and raise if there is a problem.
462 """
463 pass
Simon Glass3b0c3822018-06-01 09:38:20 -0600464
Simon Glass8122f392018-07-17 13:25:28 -0600465 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600466 def GetStr(value):
467 if value is None:
468 return '<none> '
469 return '%08x' % value
470
471 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600472 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600473 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
474 Entry.GetStr(offset), Entry.GetStr(size),
475 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600476
Simon Glass3b0c3822018-06-01 09:38:20 -0600477 def WriteMap(self, fd, indent):
478 """Write a map of the entry to a .map file
479
480 Args:
481 fd: File to write the map to
482 indent: Curent indent level of map (0=none, 1=one level, etc.)
483 """
Simon Glass1be70d22018-07-17 13:25:49 -0600484 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
485 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600486
Simon Glass11e36cc2018-07-17 13:25:38 -0600487 def GetEntries(self):
488 """Return a list of entries contained by this entry
489
490 Returns:
491 List of entries, or None if none. A normal entry has no entries
492 within it so will return None
493 """
494 return None
495
Simon Glass53af22a2018-07-17 13:25:32 -0600496 def GetArg(self, name, datatype=str):
497 """Get the value of an entry argument or device-tree-node property
498
499 Some node properties can be provided as arguments to binman. First check
500 the entry arguments, and fall back to the device tree if not found
501
502 Args:
503 name: Argument name
504 datatype: Data type (str or int)
505
506 Returns:
507 Value of argument as a string or int, or None if no value
508
509 Raises:
510 ValueError if the argument cannot be converted to in
511 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600512 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600513 if value is not None:
514 if datatype == int:
515 try:
516 value = int(value)
517 except ValueError:
518 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
519 (name, value))
520 elif datatype == str:
521 pass
522 else:
523 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
524 datatype)
525 else:
526 value = fdt_util.GetDatatype(self._node, name, datatype)
527 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600528
529 @staticmethod
530 def WriteDocs(modules, test_missing=None):
531 """Write out documentation about the various entry types to stdout
532
533 Args:
534 modules: List of modules to include
535 test_missing: Used for testing. This is a module to report
536 as missing
537 """
538 print('''Binman Entry Documentation
539===========================
540
541This file describes the entry types supported by binman. These entry types can
542be placed in an image one by one to build up a final firmware image. It is
543fairly easy to create new entry types. Just add a new file to the 'etype'
544directory. You can use the existing entries as examples.
545
546Note that some entries are subclasses of others, using and extending their
547features to produce new behaviours.
548
549
550''')
551 modules = sorted(modules)
552
553 # Don't show the test entry
554 if '_testing' in modules:
555 modules.remove('_testing')
556 missing = []
557 for name in modules:
Simon Glasse4304402019-07-08 14:25:32 -0600558 if name.startswith('__'):
559 continue
Simon Glassc073ced2019-07-08 14:25:31 -0600560 module = Entry.Lookup(name, name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600561 docs = getattr(module, '__doc__')
562 if test_missing == name:
563 docs = None
564 if docs:
565 lines = docs.splitlines()
566 first_line = lines[0]
567 rest = [line[4:] for line in lines[1:]]
568 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
569 print(hdr)
570 print('-' * len(hdr))
571 print('\n'.join(rest))
572 print()
573 print()
574 else:
575 missing.append(name)
576
577 if missing:
578 raise ValueError('Documentation is missing for modules: %s' %
579 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600580
581 def GetUniqueName(self):
582 """Get a unique name for a node
583
584 Returns:
585 String containing a unique name for a node, consisting of the name
586 of all ancestors (starting from within the 'binman' node) separated
587 by a dot ('.'). This can be useful for generating unique filesnames
588 in the output directory.
589 """
590 name = self.name
591 node = self._node
592 while node.parent:
593 node = node.parent
594 if node.name == 'binman':
595 break
596 name = '%s.%s' % (node.name, name)
597 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600598
599 def ExpandToLimit(self, limit):
600 """Expand an entry so that it ends at the given offset limit"""
601 if self.offset + self.size < limit:
602 self.size = limit - self.offset
603 # Request the contents again, since changing the size requires that
604 # the data grows. This should not fail, but check it to be sure.
605 if not self.ObtainContents():
606 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600607
608 def HasSibling(self, name):
609 """Check if there is a sibling of a given name
610
611 Returns:
612 True if there is an entry with this name in the the same section,
613 else False
614 """
615 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600616
617 def GetSiblingImagePos(self, name):
618 """Return the image position of the given sibling
619
620 Returns:
621 Image position of sibling, or None if the sibling has no position,
622 or False if there is no such sibling
623 """
624 if not self.HasSibling(name):
625 return False
626 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600627
628 @staticmethod
629 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
630 uncomp_size, offset, entry):
631 """Add a new entry to the entries list
632
633 Args:
634 entries: List (of EntryInfo objects) to add to
635 indent: Current indent level to add to list
636 name: Entry name (string)
637 etype: Entry type (string)
638 size: Entry size in bytes (int)
639 image_pos: Position within image in bytes (int)
640 uncomp_size: Uncompressed size if the entry uses compression, else
641 None
642 offset: Entry offset within parent in bytes (int)
643 entry: Entry object
644 """
645 entries.append(EntryInfo(indent, name, etype, size, image_pos,
646 uncomp_size, offset, entry))
647
648 def ListEntries(self, entries, indent):
649 """Add files in this entry to the list of entries
650
651 This can be overridden by subclasses which need different behaviour.
652
653 Args:
654 entries: List (of EntryInfo objects) to add to
655 indent: Current indent level to add to list
656 """
657 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
658 self.image_pos, self.uncomp_size, self.offset, self)
Simon Glassf667e452019-07-08 14:25:50 -0600659
660 def ReadData(self, decomp=True):
661 """Read the data for an entry from the image
662
663 This is used when the image has been read in and we want to extract the
664 data for a particular entry from that image.
665
666 Args:
667 decomp: True to decompress any compressed data before returning it;
668 False to return the raw, uncompressed data
669
670 Returns:
671 Entry data (bytes)
672 """
673 # Use True here so that we get an uncompressed section to work from,
674 # although compressed sections are currently not supported
675 data = self.section.ReadData(True)
676 tout.Info('%s: Reading data from offset %#x-%#x, size %#x (avail %#x)' %
677 (self.GetPath(), self.offset, self.offset + self.size,
678 self.size, len(data)))
679 return data[self.offset:self.offset + self.size]