blob: ee63d183532a7696a3192215a6e827201a4bc13e [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
26
27modules = {}
28
Simon Glassbadf0ec2018-06-01 09:38:15 -060029our_path = os.path.dirname(os.path.realpath(__file__))
30
Simon Glass53af22a2018-07-17 13:25:32 -060031
32# An argument which can be passed to entries on the command line, in lieu of
33# device-tree properties.
34EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
35
Simon Glass41b8ba02019-07-08 14:25:43 -060036# Information about an entry for use when displaying summaries
37EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
38 'image_pos', 'uncomp_size', 'offset',
39 'entry'])
Simon Glass53af22a2018-07-17 13:25:32 -060040
Simon Glassbf7fd502016-11-25 20:15:51 -070041class Entry(object):
Simon Glass25ac0e62018-06-01 09:38:14 -060042 """An Entry in the section
Simon Glassbf7fd502016-11-25 20:15:51 -070043
44 An entry corresponds to a single node in the device-tree description
Simon Glass25ac0e62018-06-01 09:38:14 -060045 of the section. Each entry ends up being a part of the final section.
Simon Glassbf7fd502016-11-25 20:15:51 -070046 Entries can be placed either right next to each other, or with padding
47 between them. The type of the entry determines the data that is in it.
48
49 This class is not used by itself. All entry objects are subclasses of
50 Entry.
51
52 Attributes:
Simon Glass8122f392018-07-17 13:25:28 -060053 section: Section object containing this entry
Simon Glassbf7fd502016-11-25 20:15:51 -070054 node: The node that created this entry
Simon Glass3ab95982018-08-01 15:22:37 -060055 offset: Offset of entry within the section, None if not known yet (in
56 which case it will be calculated by Pack())
Simon Glassbf7fd502016-11-25 20:15:51 -070057 size: Entry size in bytes, None if not known
Simon Glass8287ee82019-07-08 14:25:30 -060058 uncomp_size: Size of uncompressed data in bytes, if the entry is
59 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070060 contents_size: Size of contents in bytes, 0 by default
Simon Glass3ab95982018-08-01 15:22:37 -060061 align: Entry start offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070062 align_size: Entry size alignment, or None
Simon Glass3ab95982018-08-01 15:22:37 -060063 align_end: Entry end offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070064 pad_before: Number of pad bytes before the contents, 0 if none
65 pad_after: Number of pad bytes after the contents, 0 if none
66 data: Contents of entry (string of bytes)
Simon Glass8287ee82019-07-08 14:25:30 -060067 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassc52c9e72019-07-08 14:25:37 -060068 orig_offset: Original offset value read from node
69 orig_size: Original size value read from node
Simon Glassbf7fd502016-11-25 20:15:51 -070070 """
Simon Glassc8d48ef2018-06-01 09:38:21 -060071 def __init__(self, section, etype, node, read_node=True, name_prefix=''):
Simon Glass25ac0e62018-06-01 09:38:14 -060072 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -070073 self.etype = etype
74 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -060075 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -060076 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -070077 self.size = None
Simon Glass8287ee82019-07-08 14:25:30 -060078 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -060079 self.data = None
Simon Glassbf7fd502016-11-25 20:15:51 -070080 self.contents_size = 0
81 self.align = None
82 self.align_size = None
83 self.align_end = None
84 self.pad_before = 0
85 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -060086 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -060087 self.image_pos = None
Simon Glassba64a0b2018-09-14 04:57:29 -060088 self._expand_size = False
Simon Glass8287ee82019-07-08 14:25:30 -060089 self.compress = 'none'
Simon Glassbf7fd502016-11-25 20:15:51 -070090 if read_node:
91 self.ReadNode()
92
93 @staticmethod
Simon Glassc073ced2019-07-08 14:25:31 -060094 def Lookup(node_path, etype):
Simon Glassfd8d1f72018-07-17 13:25:36 -060095 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -070096
97 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -060098 node_node: Path name of Node object containing information about
99 the entry to create (used for errors)
100 etype: Entry type to use
Simon Glassbf7fd502016-11-25 20:15:51 -0700101
102 Returns:
Simon Glassfd8d1f72018-07-17 13:25:36 -0600103 The entry class object if found, else None
Simon Glassbf7fd502016-11-25 20:15:51 -0700104 """
Simon Glassdd57c132018-06-01 09:38:11 -0600105 # Convert something like 'u-boot@0' to 'u_boot' since we are only
106 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700107 module_name = etype.replace('-', '_')
Simon Glassdd57c132018-06-01 09:38:11 -0600108 if '@' in module_name:
109 module_name = module_name.split('@')[0]
Simon Glassbf7fd502016-11-25 20:15:51 -0700110 module = modules.get(module_name)
111
Simon Glassbadf0ec2018-06-01 09:38:15 -0600112 # Also allow entry-type modules to be brought in from the etype directory.
113
Simon Glassbf7fd502016-11-25 20:15:51 -0700114 # Import the module if we have not already done so.
115 if not module:
Simon Glassbadf0ec2018-06-01 09:38:15 -0600116 old_path = sys.path
117 sys.path.insert(0, os.path.join(our_path, 'etype'))
Simon Glassbf7fd502016-11-25 20:15:51 -0700118 try:
119 if have_importlib:
120 module = importlib.import_module(module_name)
121 else:
122 module = __import__(module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600123 except ImportError as e:
124 raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
125 (etype, node_path, module_name, e))
Simon Glassbadf0ec2018-06-01 09:38:15 -0600126 finally:
127 sys.path = old_path
Simon Glassbf7fd502016-11-25 20:15:51 -0700128 modules[module_name] = module
129
Simon Glassfd8d1f72018-07-17 13:25:36 -0600130 # Look up the expected class name
131 return getattr(module, 'Entry_%s' % module_name)
132
133 @staticmethod
134 def Create(section, node, etype=None):
135 """Create a new entry for a node.
136
137 Args:
138 section: Section object containing this node
139 node: Node object containing information about the entry to
140 create
141 etype: Entry type to use, or None to work it out (used for tests)
142
143 Returns:
144 A new Entry object of the correct type (a subclass of Entry)
145 """
146 if not etype:
147 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glassc073ced2019-07-08 14:25:31 -0600148 obj = Entry.Lookup(node.path, etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600149
Simon Glassbf7fd502016-11-25 20:15:51 -0700150 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600151 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700152
153 def ReadNode(self):
154 """Read entry information from the node
155
156 This reads all the fields we recognise from the node, ready for use.
157 """
Simon Glass15a587c2018-07-17 13:25:51 -0600158 if 'pos' in self._node.props:
159 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass3ab95982018-08-01 15:22:37 -0600160 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700161 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glassc52c9e72019-07-08 14:25:37 -0600162 self.orig_offset = self.offset
163 self.orig_size = self.size
164
Simon Glassbf7fd502016-11-25 20:15:51 -0700165 self.align = fdt_util.GetInt(self._node, 'align')
166 if tools.NotPowerOfTwo(self.align):
167 raise ValueError("Node '%s': Alignment %s must be a power of two" %
168 (self._node.path, self.align))
169 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
170 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
171 self.align_size = fdt_util.GetInt(self._node, 'align-size')
172 if tools.NotPowerOfTwo(self.align_size):
173 raise ValueError("Node '%s': Alignment size %s must be a power "
174 "of two" % (self._node.path, self.align_size))
175 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600176 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassba64a0b2018-09-14 04:57:29 -0600177 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
Simon Glassbf7fd502016-11-25 20:15:51 -0700178
Simon Glass6c234bf2018-09-14 04:57:18 -0600179 def GetDefaultFilename(self):
180 return None
181
Simon Glass539aece2018-09-14 04:57:22 -0600182 def GetFdtSet(self):
183 """Get the set of device trees used by this entry
184
185 Returns:
186 Set containing the filename from this entry, if it is a .dtb, else
187 an empty set
188 """
189 fname = self.GetDefaultFilename()
190 # It would be better to use isinstance(self, Entry_blob_dtb) here but
191 # we cannot access Entry_blob_dtb
192 if fname and fname.endswith('.dtb'):
Simon Glassd141f6c2019-05-14 15:53:39 -0600193 return set([fname])
194 return set()
Simon Glass539aece2018-09-14 04:57:22 -0600195
Simon Glass0a98b282018-09-14 04:57:28 -0600196 def ExpandEntries(self):
197 pass
198
Simon Glass078ab1a2018-07-06 10:27:41 -0600199 def AddMissingProperties(self):
200 """Add new properties to the device tree as needed for this entry"""
Simon Glassdbf6be92018-08-01 15:22:42 -0600201 for prop in ['offset', 'size', 'image-pos']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600202 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600203 state.AddZeroProp(self._node, prop)
Simon Glass8287ee82019-07-08 14:25:30 -0600204 if self.compress != 'none':
205 state.AddZeroProp(self._node, 'uncomp-size')
Simon Glasse0e5df92018-09-14 04:57:31 -0600206 err = state.CheckAddHashProp(self._node)
207 if err:
208 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600209
210 def SetCalculatedProperties(self):
211 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600212 state.SetInt(self._node, 'offset', self.offset)
213 state.SetInt(self._node, 'size', self.size)
Simon Glassf8f8df62018-09-14 04:57:34 -0600214 state.SetInt(self._node, 'image-pos',
215 self.image_pos - self.section.GetRootSkipAtStart())
Simon Glass8287ee82019-07-08 14:25:30 -0600216 if self.uncomp_size is not None:
217 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Simon Glasse0e5df92018-09-14 04:57:31 -0600218 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600219
Simon Glassecab8972018-07-06 10:27:40 -0600220 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600221 """Allow entries to adjust the device tree
222
223 Some entries need to adjust the device tree for their purposes. This
224 may involve adding or deleting properties.
225
226 Returns:
227 True if processing is complete
228 False if processing could not be completed due to a dependency.
229 This will cause the entry to be retried after others have been
230 called
231 """
Simon Glassecab8972018-07-06 10:27:40 -0600232 return True
233
Simon Glassc8d48ef2018-06-01 09:38:21 -0600234 def SetPrefix(self, prefix):
235 """Set the name prefix for a node
236
237 Args:
238 prefix: Prefix to set, or '' to not use a prefix
239 """
240 if prefix:
241 self.name = prefix + self.name
242
Simon Glass5c890232018-07-06 10:27:19 -0600243 def SetContents(self, data):
244 """Set the contents of an entry
245
246 This sets both the data and content_size properties
247
248 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600249 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600250 """
251 self.data = data
252 self.contents_size = len(self.data)
253
254 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600255 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600256
Simon Glassa0dcaf22019-07-08 14:25:35 -0600257 This checks that the new data is the same size as the old. If the size
258 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600259
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 Raises:
264 ValueError if the new data size is not the same as the old
265 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600266 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600267 new_size = len(data)
268 if state.AllowEntryExpansion():
269 if new_size > self.contents_size:
270 print("Entry '%s' size change from %#x to %#x" % (
271 self._node.path, self.contents_size, new_size))
272 # self.data will indicate the new size needed
273 size_ok = False
274 elif new_size != self.contents_size:
Simon Glass5c890232018-07-06 10:27:19 -0600275 self.Raise('Cannot update entry size from %d to %d' %
Simon Glassc52c9e72019-07-08 14:25:37 -0600276 (self.contents_size, new_size))
Simon Glass5c890232018-07-06 10:27:19 -0600277 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600278 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600279
Simon Glassbf7fd502016-11-25 20:15:51 -0700280 def ObtainContents(self):
281 """Figure out the contents of an entry.
282
283 Returns:
284 True if the contents were found, False if another call is needed
285 after the other entries are processed.
286 """
287 # No contents by default: subclasses can implement this
288 return True
289
Simon Glassc52c9e72019-07-08 14:25:37 -0600290 def ResetForPack(self):
291 """Reset offset/size fields so that packing can be done again"""
292 self.offset = self.orig_offset
293 self.size = self.orig_size
294
Simon Glass3ab95982018-08-01 15:22:37 -0600295 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600296 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700297
298 Most of the time the entries are not fully specified. There may be
299 an alignment but no size. In that case we take the size from the
300 contents of the entry.
301
Simon Glass3ab95982018-08-01 15:22:37 -0600302 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700303
Simon Glass3ab95982018-08-01 15:22:37 -0600304 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700305 entry will be know.
306
307 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600308 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700309
310 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600311 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700312 """
Simon Glass3ab95982018-08-01 15:22:37 -0600313 if self.offset is None:
314 if self.offset_unset:
315 self.Raise('No offset set with offset-unset: should another '
316 'entry provide this correct offset?')
317 self.offset = tools.Align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700318 needed = self.pad_before + self.contents_size + self.pad_after
319 needed = tools.Align(needed, self.align_size)
320 size = self.size
321 if not size:
322 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600323 new_offset = self.offset + size
324 aligned_offset = tools.Align(new_offset, self.align_end)
325 if aligned_offset != new_offset:
326 size = aligned_offset - self.offset
327 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700328
329 if not self.size:
330 self.size = size
331
332 if self.size < needed:
333 self.Raise("Entry contents size is %#x (%d) but entry size is "
334 "%#x (%d)" % (needed, needed, self.size, self.size))
335 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600336 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700337 # conflict with the provided alignment values
338 if self.size != tools.Align(self.size, self.align_size):
339 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
340 (self.size, self.size, self.align_size, self.align_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600341 if self.offset != tools.Align(self.offset, self.align):
342 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
343 (self.offset, self.offset, self.align, self.align))
Simon Glassbf7fd502016-11-25 20:15:51 -0700344
Simon Glass3ab95982018-08-01 15:22:37 -0600345 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700346
347 def Raise(self, msg):
348 """Convenience function to raise an error referencing a node"""
349 raise ValueError("Node '%s': %s" % (self._node.path, msg))
350
Simon Glass53af22a2018-07-17 13:25:32 -0600351 def GetEntryArgsOrProps(self, props, required=False):
352 """Return the values of a set of properties
353
354 Args:
355 props: List of EntryArg objects
356
357 Raises:
358 ValueError if a property is not found
359 """
360 values = []
361 missing = []
362 for prop in props:
363 python_prop = prop.name.replace('-', '_')
364 if hasattr(self, python_prop):
365 value = getattr(self, python_prop)
366 else:
367 value = None
368 if value is None:
369 value = self.GetArg(prop.name, prop.datatype)
370 if value is None and required:
371 missing.append(prop.name)
372 values.append(value)
373 if missing:
374 self.Raise('Missing required properties/entry args: %s' %
375 (', '.join(missing)))
376 return values
377
Simon Glassbf7fd502016-11-25 20:15:51 -0700378 def GetPath(self):
379 """Get the path of a node
380
381 Returns:
382 Full path of the node for this entry
383 """
384 return self._node.path
385
386 def GetData(self):
387 return self.data
388
Simon Glass3ab95982018-08-01 15:22:37 -0600389 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600390 """Get the offsets for siblings
391
392 Some entry types can contain information about the position or size of
393 other entries. An example of this is the Intel Flash Descriptor, which
394 knows where the Intel Management Engine section should go.
395
396 If this entry knows about the position of other entries, it can specify
397 this by returning values here
398
399 Returns:
400 Dict:
401 key: Entry type
402 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600403 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600404 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700405 return {}
406
Simon Glasscf549042019-07-08 13:18:39 -0600407 def SetOffsetSize(self, offset, size):
408 """Set the offset and/or size of an entry
409
410 Args:
411 offset: New offset, or None to leave alone
412 size: New size, or None to leave alone
413 """
414 if offset is not None:
415 self.offset = offset
416 if size is not None:
417 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700418
Simon Glassdbf6be92018-08-01 15:22:42 -0600419 def SetImagePos(self, image_pos):
420 """Set the position in the image
421
422 Args:
423 image_pos: Position of this entry in the image
424 """
425 self.image_pos = image_pos + self.offset
426
Simon Glassbf7fd502016-11-25 20:15:51 -0700427 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600428 """Do any post-packing updates of entry contents
429
430 This function should call ProcessContentsUpdate() to update the entry
431 contents, if necessary, returning its return value here.
432
433 Args:
434 data: Data to set to the contents (bytes)
435
436 Returns:
437 True if the new data size is OK, False if expansion is needed
438
439 Raises:
440 ValueError if the new data size is not the same as the old and
441 state.AllowEntryExpansion() is False
442 """
443 return True
Simon Glass19790632017-11-13 18:55:01 -0700444
Simon Glassf55382b2018-06-01 09:38:13 -0600445 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700446 """Write symbol values into binary files for access at run time
447
448 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600449 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700450 """
451 pass
Simon Glass18546952018-06-01 09:38:16 -0600452
Simon Glass3ab95982018-08-01 15:22:37 -0600453 def CheckOffset(self):
454 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600455
Simon Glass3ab95982018-08-01 15:22:37 -0600456 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600457 than having to be fully inside their section). Sub-classes can implement
458 this function and raise if there is a problem.
459 """
460 pass
Simon Glass3b0c3822018-06-01 09:38:20 -0600461
Simon Glass8122f392018-07-17 13:25:28 -0600462 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600463 def GetStr(value):
464 if value is None:
465 return '<none> '
466 return '%08x' % value
467
468 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600469 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600470 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
471 Entry.GetStr(offset), Entry.GetStr(size),
472 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600473
Simon Glass3b0c3822018-06-01 09:38:20 -0600474 def WriteMap(self, fd, indent):
475 """Write a map of the entry to a .map file
476
477 Args:
478 fd: File to write the map to
479 indent: Curent indent level of map (0=none, 1=one level, etc.)
480 """
Simon Glass1be70d22018-07-17 13:25:49 -0600481 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
482 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600483
Simon Glass11e36cc2018-07-17 13:25:38 -0600484 def GetEntries(self):
485 """Return a list of entries contained by this entry
486
487 Returns:
488 List of entries, or None if none. A normal entry has no entries
489 within it so will return None
490 """
491 return None
492
Simon Glass53af22a2018-07-17 13:25:32 -0600493 def GetArg(self, name, datatype=str):
494 """Get the value of an entry argument or device-tree-node property
495
496 Some node properties can be provided as arguments to binman. First check
497 the entry arguments, and fall back to the device tree if not found
498
499 Args:
500 name: Argument name
501 datatype: Data type (str or int)
502
503 Returns:
504 Value of argument as a string or int, or None if no value
505
506 Raises:
507 ValueError if the argument cannot be converted to in
508 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600509 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600510 if value is not None:
511 if datatype == int:
512 try:
513 value = int(value)
514 except ValueError:
515 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
516 (name, value))
517 elif datatype == str:
518 pass
519 else:
520 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
521 datatype)
522 else:
523 value = fdt_util.GetDatatype(self._node, name, datatype)
524 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600525
526 @staticmethod
527 def WriteDocs(modules, test_missing=None):
528 """Write out documentation about the various entry types to stdout
529
530 Args:
531 modules: List of modules to include
532 test_missing: Used for testing. This is a module to report
533 as missing
534 """
535 print('''Binman Entry Documentation
536===========================
537
538This file describes the entry types supported by binman. These entry types can
539be placed in an image one by one to build up a final firmware image. It is
540fairly easy to create new entry types. Just add a new file to the 'etype'
541directory. You can use the existing entries as examples.
542
543Note that some entries are subclasses of others, using and extending their
544features to produce new behaviours.
545
546
547''')
548 modules = sorted(modules)
549
550 # Don't show the test entry
551 if '_testing' in modules:
552 modules.remove('_testing')
553 missing = []
554 for name in modules:
Simon Glasse4304402019-07-08 14:25:32 -0600555 if name.startswith('__'):
556 continue
Simon Glassc073ced2019-07-08 14:25:31 -0600557 module = Entry.Lookup(name, name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600558 docs = getattr(module, '__doc__')
559 if test_missing == name:
560 docs = None
561 if docs:
562 lines = docs.splitlines()
563 first_line = lines[0]
564 rest = [line[4:] for line in lines[1:]]
565 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
566 print(hdr)
567 print('-' * len(hdr))
568 print('\n'.join(rest))
569 print()
570 print()
571 else:
572 missing.append(name)
573
574 if missing:
575 raise ValueError('Documentation is missing for modules: %s' %
576 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600577
578 def GetUniqueName(self):
579 """Get a unique name for a node
580
581 Returns:
582 String containing a unique name for a node, consisting of the name
583 of all ancestors (starting from within the 'binman' node) separated
584 by a dot ('.'). This can be useful for generating unique filesnames
585 in the output directory.
586 """
587 name = self.name
588 node = self._node
589 while node.parent:
590 node = node.parent
591 if node.name == 'binman':
592 break
593 name = '%s.%s' % (node.name, name)
594 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600595
596 def ExpandToLimit(self, limit):
597 """Expand an entry so that it ends at the given offset limit"""
598 if self.offset + self.size < limit:
599 self.size = limit - self.offset
600 # Request the contents again, since changing the size requires that
601 # the data grows. This should not fail, but check it to be sure.
602 if not self.ObtainContents():
603 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600604
605 def HasSibling(self, name):
606 """Check if there is a sibling of a given name
607
608 Returns:
609 True if there is an entry with this name in the the same section,
610 else False
611 """
612 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600613
614 def GetSiblingImagePos(self, name):
615 """Return the image position of the given sibling
616
617 Returns:
618 Image position of sibling, or None if the sibling has no position,
619 or False if there is no such sibling
620 """
621 if not self.HasSibling(name):
622 return False
623 return self.section.GetEntries()[name].image_pos
Simon Glass41b8ba02019-07-08 14:25:43 -0600624
625 @staticmethod
626 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
627 uncomp_size, offset, entry):
628 """Add a new entry to the entries list
629
630 Args:
631 entries: List (of EntryInfo objects) to add to
632 indent: Current indent level to add to list
633 name: Entry name (string)
634 etype: Entry type (string)
635 size: Entry size in bytes (int)
636 image_pos: Position within image in bytes (int)
637 uncomp_size: Uncompressed size if the entry uses compression, else
638 None
639 offset: Entry offset within parent in bytes (int)
640 entry: Entry object
641 """
642 entries.append(EntryInfo(indent, name, etype, size, image_pos,
643 uncomp_size, offset, entry))
644
645 def ListEntries(self, entries, indent):
646 """Add files in this entry to the list of entries
647
648 This can be overridden by subclasses which need different behaviour.
649
650 Args:
651 entries: List (of EntryInfo objects) to add to
652 indent: Current indent level to add to list
653 """
654 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
655 self.image_pos, self.uncomp_size, self.offset, self)