blob: e38cb71c5966e6d8fe23163a8211246fdbcae3e6 [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
36
Simon Glassbf7fd502016-11-25 20:15:51 -070037class Entry(object):
Simon Glass25ac0e62018-06-01 09:38:14 -060038 """An Entry in the section
Simon Glassbf7fd502016-11-25 20:15:51 -070039
40 An entry corresponds to a single node in the device-tree description
Simon Glass25ac0e62018-06-01 09:38:14 -060041 of the section. Each entry ends up being a part of the final section.
Simon Glassbf7fd502016-11-25 20:15:51 -070042 Entries can be placed either right next to each other, or with padding
43 between them. The type of the entry determines the data that is in it.
44
45 This class is not used by itself. All entry objects are subclasses of
46 Entry.
47
48 Attributes:
Simon Glass8122f392018-07-17 13:25:28 -060049 section: Section object containing this entry
Simon Glassbf7fd502016-11-25 20:15:51 -070050 node: The node that created this entry
Simon Glass3ab95982018-08-01 15:22:37 -060051 offset: Offset of entry within the section, None if not known yet (in
52 which case it will be calculated by Pack())
Simon Glassbf7fd502016-11-25 20:15:51 -070053 size: Entry size in bytes, None if not known
Simon Glass8287ee82019-07-08 14:25:30 -060054 uncomp_size: Size of uncompressed data in bytes, if the entry is
55 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070056 contents_size: Size of contents in bytes, 0 by default
Simon Glass3ab95982018-08-01 15:22:37 -060057 align: Entry start offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070058 align_size: Entry size alignment, or None
Simon Glass3ab95982018-08-01 15:22:37 -060059 align_end: Entry end offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070060 pad_before: Number of pad bytes before the contents, 0 if none
61 pad_after: Number of pad bytes after the contents, 0 if none
62 data: Contents of entry (string of bytes)
Simon Glass8287ee82019-07-08 14:25:30 -060063 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassc52c9e72019-07-08 14:25:37 -060064 orig_offset: Original offset value read from node
65 orig_size: Original size value read from node
Simon Glassbf7fd502016-11-25 20:15:51 -070066 """
Simon Glassc8d48ef2018-06-01 09:38:21 -060067 def __init__(self, section, etype, node, read_node=True, name_prefix=''):
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 Glass8287ee82019-07-08 14:25:30 -060074 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -060075 self.data = None
Simon Glassbf7fd502016-11-25 20:15:51 -070076 self.contents_size = 0
77 self.align = None
78 self.align_size = None
79 self.align_end = None
80 self.pad_before = 0
81 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -060082 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -060083 self.image_pos = None
Simon Glassba64a0b2018-09-14 04:57:29 -060084 self._expand_size = False
Simon Glass8287ee82019-07-08 14:25:30 -060085 self.compress = 'none'
Simon Glassbf7fd502016-11-25 20:15:51 -070086 if read_node:
87 self.ReadNode()
88
89 @staticmethod
Simon Glassc073ced2019-07-08 14:25:31 -060090 def Lookup(node_path, etype):
Simon Glassfd8d1f72018-07-17 13:25:36 -060091 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -070092
93 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -060094 node_node: Path name of Node object containing information about
95 the entry to create (used for errors)
96 etype: Entry type to use
Simon Glassbf7fd502016-11-25 20:15:51 -070097
98 Returns:
Simon Glassfd8d1f72018-07-17 13:25:36 -060099 The entry class object if found, else None
Simon Glassbf7fd502016-11-25 20:15:51 -0700100 """
Simon Glassdd57c132018-06-01 09:38:11 -0600101 # Convert something like 'u-boot@0' to 'u_boot' since we are only
102 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700103 module_name = etype.replace('-', '_')
Simon Glassdd57c132018-06-01 09:38:11 -0600104 if '@' in module_name:
105 module_name = module_name.split('@')[0]
Simon Glassbf7fd502016-11-25 20:15:51 -0700106 module = modules.get(module_name)
107
Simon Glassbadf0ec2018-06-01 09:38:15 -0600108 # Also allow entry-type modules to be brought in from the etype directory.
109
Simon Glassbf7fd502016-11-25 20:15:51 -0700110 # Import the module if we have not already done so.
111 if not module:
Simon Glassbadf0ec2018-06-01 09:38:15 -0600112 old_path = sys.path
113 sys.path.insert(0, os.path.join(our_path, 'etype'))
Simon Glassbf7fd502016-11-25 20:15:51 -0700114 try:
115 if have_importlib:
116 module = importlib.import_module(module_name)
117 else:
118 module = __import__(module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600119 except ImportError as e:
120 raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
121 (etype, node_path, module_name, e))
Simon Glassbadf0ec2018-06-01 09:38:15 -0600122 finally:
123 sys.path = old_path
Simon Glassbf7fd502016-11-25 20:15:51 -0700124 modules[module_name] = module
125
Simon Glassfd8d1f72018-07-17 13:25:36 -0600126 # Look up the expected class name
127 return getattr(module, 'Entry_%s' % module_name)
128
129 @staticmethod
130 def Create(section, node, etype=None):
131 """Create a new entry for a node.
132
133 Args:
134 section: Section object containing this node
135 node: Node object containing information about the entry to
136 create
137 etype: Entry type to use, or None to work it out (used for tests)
138
139 Returns:
140 A new Entry object of the correct type (a subclass of Entry)
141 """
142 if not etype:
143 etype = fdt_util.GetString(node, 'type', node.name)
Simon Glassc073ced2019-07-08 14:25:31 -0600144 obj = Entry.Lookup(node.path, etype)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600145
Simon Glassbf7fd502016-11-25 20:15:51 -0700146 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600147 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700148
149 def ReadNode(self):
150 """Read entry information from the node
151
152 This reads all the fields we recognise from the node, ready for use.
153 """
Simon Glass15a587c2018-07-17 13:25:51 -0600154 if 'pos' in self._node.props:
155 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass3ab95982018-08-01 15:22:37 -0600156 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700157 self.size = fdt_util.GetInt(self._node, 'size')
Simon Glassc52c9e72019-07-08 14:25:37 -0600158 self.orig_offset = self.offset
159 self.orig_size = self.size
160
Simon Glassbf7fd502016-11-25 20:15:51 -0700161 self.align = fdt_util.GetInt(self._node, 'align')
162 if tools.NotPowerOfTwo(self.align):
163 raise ValueError("Node '%s': Alignment %s must be a power of two" %
164 (self._node.path, self.align))
165 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
166 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
167 self.align_size = fdt_util.GetInt(self._node, 'align-size')
168 if tools.NotPowerOfTwo(self.align_size):
169 raise ValueError("Node '%s': Alignment size %s must be a power "
170 "of two" % (self._node.path, self.align_size))
171 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600172 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassba64a0b2018-09-14 04:57:29 -0600173 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
Simon Glassbf7fd502016-11-25 20:15:51 -0700174
Simon Glass6c234bf2018-09-14 04:57:18 -0600175 def GetDefaultFilename(self):
176 return None
177
Simon Glass539aece2018-09-14 04:57:22 -0600178 def GetFdtSet(self):
179 """Get the set of device trees used by this entry
180
181 Returns:
182 Set containing the filename from this entry, if it is a .dtb, else
183 an empty set
184 """
185 fname = self.GetDefaultFilename()
186 # It would be better to use isinstance(self, Entry_blob_dtb) here but
187 # we cannot access Entry_blob_dtb
188 if fname and fname.endswith('.dtb'):
Simon Glassd141f6c2019-05-14 15:53:39 -0600189 return set([fname])
190 return set()
Simon Glass539aece2018-09-14 04:57:22 -0600191
Simon Glass0a98b282018-09-14 04:57:28 -0600192 def ExpandEntries(self):
193 pass
194
Simon Glass078ab1a2018-07-06 10:27:41 -0600195 def AddMissingProperties(self):
196 """Add new properties to the device tree as needed for this entry"""
Simon Glassdbf6be92018-08-01 15:22:42 -0600197 for prop in ['offset', 'size', 'image-pos']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600198 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600199 state.AddZeroProp(self._node, prop)
Simon Glass8287ee82019-07-08 14:25:30 -0600200 if self.compress != 'none':
201 state.AddZeroProp(self._node, 'uncomp-size')
Simon Glasse0e5df92018-09-14 04:57:31 -0600202 err = state.CheckAddHashProp(self._node)
203 if err:
204 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600205
206 def SetCalculatedProperties(self):
207 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600208 state.SetInt(self._node, 'offset', self.offset)
209 state.SetInt(self._node, 'size', self.size)
Simon Glassf8f8df62018-09-14 04:57:34 -0600210 state.SetInt(self._node, 'image-pos',
211 self.image_pos - self.section.GetRootSkipAtStart())
Simon Glass8287ee82019-07-08 14:25:30 -0600212 if self.uncomp_size is not None:
213 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Simon Glasse0e5df92018-09-14 04:57:31 -0600214 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600215
Simon Glassecab8972018-07-06 10:27:40 -0600216 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600217 """Allow entries to adjust the device tree
218
219 Some entries need to adjust the device tree for their purposes. This
220 may involve adding or deleting properties.
221
222 Returns:
223 True if processing is complete
224 False if processing could not be completed due to a dependency.
225 This will cause the entry to be retried after others have been
226 called
227 """
Simon Glassecab8972018-07-06 10:27:40 -0600228 return True
229
Simon Glassc8d48ef2018-06-01 09:38:21 -0600230 def SetPrefix(self, prefix):
231 """Set the name prefix for a node
232
233 Args:
234 prefix: Prefix to set, or '' to not use a prefix
235 """
236 if prefix:
237 self.name = prefix + self.name
238
Simon Glass5c890232018-07-06 10:27:19 -0600239 def SetContents(self, data):
240 """Set the contents of an entry
241
242 This sets both the data and content_size properties
243
244 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600245 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600246 """
247 self.data = data
248 self.contents_size = len(self.data)
249
250 def ProcessContentsUpdate(self, data):
Simon Glass5b463fc2019-07-08 14:25:33 -0600251 """Update the contents of an entry, after the size is fixed
Simon Glass5c890232018-07-06 10:27:19 -0600252
Simon Glassa0dcaf22019-07-08 14:25:35 -0600253 This checks that the new data is the same size as the old. If the size
254 has changed, this triggers a re-run of the packing algorithm.
Simon Glass5c890232018-07-06 10:27:19 -0600255
256 Args:
Simon Glass5b463fc2019-07-08 14:25:33 -0600257 data: Data to set to the contents (bytes)
Simon Glass5c890232018-07-06 10:27:19 -0600258
259 Raises:
260 ValueError if the new data size is not the same as the old
261 """
Simon Glassa0dcaf22019-07-08 14:25:35 -0600262 size_ok = True
Simon Glassc52c9e72019-07-08 14:25:37 -0600263 new_size = len(data)
264 if state.AllowEntryExpansion():
265 if new_size > self.contents_size:
266 print("Entry '%s' size change from %#x to %#x" % (
267 self._node.path, self.contents_size, new_size))
268 # self.data will indicate the new size needed
269 size_ok = False
270 elif new_size != self.contents_size:
Simon Glass5c890232018-07-06 10:27:19 -0600271 self.Raise('Cannot update entry size from %d to %d' %
Simon Glassc52c9e72019-07-08 14:25:37 -0600272 (self.contents_size, new_size))
Simon Glass5c890232018-07-06 10:27:19 -0600273 self.SetContents(data)
Simon Glassa0dcaf22019-07-08 14:25:35 -0600274 return size_ok
Simon Glass5c890232018-07-06 10:27:19 -0600275
Simon Glassbf7fd502016-11-25 20:15:51 -0700276 def ObtainContents(self):
277 """Figure out the contents of an entry.
278
279 Returns:
280 True if the contents were found, False if another call is needed
281 after the other entries are processed.
282 """
283 # No contents by default: subclasses can implement this
284 return True
285
Simon Glassc52c9e72019-07-08 14:25:37 -0600286 def ResetForPack(self):
287 """Reset offset/size fields so that packing can be done again"""
288 self.offset = self.orig_offset
289 self.size = self.orig_size
290
Simon Glass3ab95982018-08-01 15:22:37 -0600291 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600292 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700293
294 Most of the time the entries are not fully specified. There may be
295 an alignment but no size. In that case we take the size from the
296 contents of the entry.
297
Simon Glass3ab95982018-08-01 15:22:37 -0600298 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700299
Simon Glass3ab95982018-08-01 15:22:37 -0600300 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700301 entry will be know.
302
303 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600304 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700305
306 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600307 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700308 """
Simon Glass3ab95982018-08-01 15:22:37 -0600309 if self.offset is None:
310 if self.offset_unset:
311 self.Raise('No offset set with offset-unset: should another '
312 'entry provide this correct offset?')
313 self.offset = tools.Align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700314 needed = self.pad_before + self.contents_size + self.pad_after
315 needed = tools.Align(needed, self.align_size)
316 size = self.size
317 if not size:
318 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600319 new_offset = self.offset + size
320 aligned_offset = tools.Align(new_offset, self.align_end)
321 if aligned_offset != new_offset:
322 size = aligned_offset - self.offset
323 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700324
325 if not self.size:
326 self.size = size
327
328 if self.size < needed:
329 self.Raise("Entry contents size is %#x (%d) but entry size is "
330 "%#x (%d)" % (needed, needed, self.size, self.size))
331 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600332 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700333 # conflict with the provided alignment values
334 if self.size != tools.Align(self.size, self.align_size):
335 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
336 (self.size, self.size, self.align_size, self.align_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600337 if self.offset != tools.Align(self.offset, self.align):
338 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
339 (self.offset, self.offset, self.align, self.align))
Simon Glassbf7fd502016-11-25 20:15:51 -0700340
Simon Glass3ab95982018-08-01 15:22:37 -0600341 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700342
343 def Raise(self, msg):
344 """Convenience function to raise an error referencing a node"""
345 raise ValueError("Node '%s': %s" % (self._node.path, msg))
346
Simon Glass53af22a2018-07-17 13:25:32 -0600347 def GetEntryArgsOrProps(self, props, required=False):
348 """Return the values of a set of properties
349
350 Args:
351 props: List of EntryArg objects
352
353 Raises:
354 ValueError if a property is not found
355 """
356 values = []
357 missing = []
358 for prop in props:
359 python_prop = prop.name.replace('-', '_')
360 if hasattr(self, python_prop):
361 value = getattr(self, python_prop)
362 else:
363 value = None
364 if value is None:
365 value = self.GetArg(prop.name, prop.datatype)
366 if value is None and required:
367 missing.append(prop.name)
368 values.append(value)
369 if missing:
370 self.Raise('Missing required properties/entry args: %s' %
371 (', '.join(missing)))
372 return values
373
Simon Glassbf7fd502016-11-25 20:15:51 -0700374 def GetPath(self):
375 """Get the path of a node
376
377 Returns:
378 Full path of the node for this entry
379 """
380 return self._node.path
381
382 def GetData(self):
383 return self.data
384
Simon Glass3ab95982018-08-01 15:22:37 -0600385 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600386 """Get the offsets for siblings
387
388 Some entry types can contain information about the position or size of
389 other entries. An example of this is the Intel Flash Descriptor, which
390 knows where the Intel Management Engine section should go.
391
392 If this entry knows about the position of other entries, it can specify
393 this by returning values here
394
395 Returns:
396 Dict:
397 key: Entry type
398 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600399 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600400 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700401 return {}
402
Simon Glasscf549042019-07-08 13:18:39 -0600403 def SetOffsetSize(self, offset, size):
404 """Set the offset and/or size of an entry
405
406 Args:
407 offset: New offset, or None to leave alone
408 size: New size, or None to leave alone
409 """
410 if offset is not None:
411 self.offset = offset
412 if size is not None:
413 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700414
Simon Glassdbf6be92018-08-01 15:22:42 -0600415 def SetImagePos(self, image_pos):
416 """Set the position in the image
417
418 Args:
419 image_pos: Position of this entry in the image
420 """
421 self.image_pos = image_pos + self.offset
422
Simon Glassbf7fd502016-11-25 20:15:51 -0700423 def ProcessContents(self):
Simon Glassa0dcaf22019-07-08 14:25:35 -0600424 """Do any post-packing updates of entry contents
425
426 This function should call ProcessContentsUpdate() to update the entry
427 contents, if necessary, returning its return value here.
428
429 Args:
430 data: Data to set to the contents (bytes)
431
432 Returns:
433 True if the new data size is OK, False if expansion is needed
434
435 Raises:
436 ValueError if the new data size is not the same as the old and
437 state.AllowEntryExpansion() is False
438 """
439 return True
Simon Glass19790632017-11-13 18:55:01 -0700440
Simon Glassf55382b2018-06-01 09:38:13 -0600441 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700442 """Write symbol values into binary files for access at run time
443
444 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600445 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700446 """
447 pass
Simon Glass18546952018-06-01 09:38:16 -0600448
Simon Glass3ab95982018-08-01 15:22:37 -0600449 def CheckOffset(self):
450 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600451
Simon Glass3ab95982018-08-01 15:22:37 -0600452 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600453 than having to be fully inside their section). Sub-classes can implement
454 this function and raise if there is a problem.
455 """
456 pass
Simon Glass3b0c3822018-06-01 09:38:20 -0600457
Simon Glass8122f392018-07-17 13:25:28 -0600458 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600459 def GetStr(value):
460 if value is None:
461 return '<none> '
462 return '%08x' % value
463
464 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600465 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600466 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
467 Entry.GetStr(offset), Entry.GetStr(size),
468 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600469
Simon Glass3b0c3822018-06-01 09:38:20 -0600470 def WriteMap(self, fd, indent):
471 """Write a map of the entry to a .map file
472
473 Args:
474 fd: File to write the map to
475 indent: Curent indent level of map (0=none, 1=one level, etc.)
476 """
Simon Glass1be70d22018-07-17 13:25:49 -0600477 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
478 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600479
Simon Glass11e36cc2018-07-17 13:25:38 -0600480 def GetEntries(self):
481 """Return a list of entries contained by this entry
482
483 Returns:
484 List of entries, or None if none. A normal entry has no entries
485 within it so will return None
486 """
487 return None
488
Simon Glass53af22a2018-07-17 13:25:32 -0600489 def GetArg(self, name, datatype=str):
490 """Get the value of an entry argument or device-tree-node property
491
492 Some node properties can be provided as arguments to binman. First check
493 the entry arguments, and fall back to the device tree if not found
494
495 Args:
496 name: Argument name
497 datatype: Data type (str or int)
498
499 Returns:
500 Value of argument as a string or int, or None if no value
501
502 Raises:
503 ValueError if the argument cannot be converted to in
504 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600505 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600506 if value is not None:
507 if datatype == int:
508 try:
509 value = int(value)
510 except ValueError:
511 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
512 (name, value))
513 elif datatype == str:
514 pass
515 else:
516 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
517 datatype)
518 else:
519 value = fdt_util.GetDatatype(self._node, name, datatype)
520 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600521
522 @staticmethod
523 def WriteDocs(modules, test_missing=None):
524 """Write out documentation about the various entry types to stdout
525
526 Args:
527 modules: List of modules to include
528 test_missing: Used for testing. This is a module to report
529 as missing
530 """
531 print('''Binman Entry Documentation
532===========================
533
534This file describes the entry types supported by binman. These entry types can
535be placed in an image one by one to build up a final firmware image. It is
536fairly easy to create new entry types. Just add a new file to the 'etype'
537directory. You can use the existing entries as examples.
538
539Note that some entries are subclasses of others, using and extending their
540features to produce new behaviours.
541
542
543''')
544 modules = sorted(modules)
545
546 # Don't show the test entry
547 if '_testing' in modules:
548 modules.remove('_testing')
549 missing = []
550 for name in modules:
Simon Glasse4304402019-07-08 14:25:32 -0600551 if name.startswith('__'):
552 continue
Simon Glassc073ced2019-07-08 14:25:31 -0600553 module = Entry.Lookup(name, name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600554 docs = getattr(module, '__doc__')
555 if test_missing == name:
556 docs = None
557 if docs:
558 lines = docs.splitlines()
559 first_line = lines[0]
560 rest = [line[4:] for line in lines[1:]]
561 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
562 print(hdr)
563 print('-' * len(hdr))
564 print('\n'.join(rest))
565 print()
566 print()
567 else:
568 missing.append(name)
569
570 if missing:
571 raise ValueError('Documentation is missing for modules: %s' %
572 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600573
574 def GetUniqueName(self):
575 """Get a unique name for a node
576
577 Returns:
578 String containing a unique name for a node, consisting of the name
579 of all ancestors (starting from within the 'binman' node) separated
580 by a dot ('.'). This can be useful for generating unique filesnames
581 in the output directory.
582 """
583 name = self.name
584 node = self._node
585 while node.parent:
586 node = node.parent
587 if node.name == 'binman':
588 break
589 name = '%s.%s' % (node.name, name)
590 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600591
592 def ExpandToLimit(self, limit):
593 """Expand an entry so that it ends at the given offset limit"""
594 if self.offset + self.size < limit:
595 self.size = limit - self.offset
596 # Request the contents again, since changing the size requires that
597 # the data grows. This should not fail, but check it to be sure.
598 if not self.ObtainContents():
599 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600600
601 def HasSibling(self, name):
602 """Check if there is a sibling of a given name
603
604 Returns:
605 True if there is an entry with this name in the the same section,
606 else False
607 """
608 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600609
610 def GetSiblingImagePos(self, name):
611 """Return the image position of the given sibling
612
613 Returns:
614 Image position of sibling, or None if the sibling has no position,
615 or False if there is no such sibling
616 """
617 if not self.HasSibling(name):
618 return False
619 return self.section.GetEntries()[name].image_pos