1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2018 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
5 # Base class for sections (collections of entries)
8 from __future__ import print_function
10 from collections import OrderedDict
19 class Section(object):
20 """A section which contains multiple entries
22 A section represents a collection of entries. There must be one or more
23 sections in an image. Sections are used to group entries together.
26 _node: Node object that contains the section definition in device tree
27 _parent_section: Parent Section object which created this Section
28 _size: Section size in bytes, or None if not known yet
29 _align_size: Section size alignment, or None
30 _pad_before: Number of bytes before the first entry starts. This
31 effectively changes the place where entry offset 0 starts
32 _pad_after: Number of bytes after the last entry ends. The last
33 entry will finish on or before this boundary
34 _pad_byte: Byte to use to pad the section where there is no entry
35 _sort: True if entries should be sorted by offset, False if they
36 must be in-order in the device tree description
37 _skip_at_start: Number of bytes before the first entry starts. These
38 effectively adjust the starting offset of entries. For example,
39 if _pad_before is 16, then the first entry would start at 16.
40 An entry with offset = 20 would in fact be written at offset 4
42 _end_4gb: Indicates that the section ends at the 4GB boundary. This is
43 used for x86 images, which want to use offsets such that a memory
44 address (like 0xff800000) is the first entry offset. This causes
45 _skip_at_start to be set to the starting memory address.
46 _name_prefix: Prefix to add to the name of all entries within this
48 _entries: OrderedDict() of entries
50 def __init__(self, name, parent_section, node, image, test=False):
54 from entry import Entry
56 self._parent_section = parent_section
62 self._align_size = None
67 self._skip_at_start = None
69 self._name_prefix = ''
70 self._entries = OrderedDict()
71 self._image_pos = None
77 """Read properties from the section node"""
78 self._size = fdt_util.GetInt(self._node, 'size')
79 self._align_size = fdt_util.GetInt(self._node, 'align-size')
80 if tools.NotPowerOfTwo(self._align_size):
81 self._Raise("Alignment size %s must be a power of two" %
83 self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
84 self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
85 self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
86 self._sort = fdt_util.GetBool(self._node, 'sort-by-offset')
87 self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
88 self._skip_at_start = fdt_util.GetInt(self._node, 'skip-at-start')
91 self._Raise("Section size must be provided when using end-at-4gb")
92 if self._skip_at_start is not None:
93 self._Raise("Provide either 'end-at-4gb' or 'skip-at-start'")
95 self._skip_at_start = 0x100000000 - self._size
97 if self._skip_at_start is None:
98 self._skip_at_start = 0
99 self._name_prefix = fdt_util.GetString(self._node, 'name-prefix')
101 def _ReadEntries(self):
102 for node in self._node.subnodes:
103 if node.name == 'hash':
105 entry = Entry.Create(self, node)
106 entry.SetPrefix(self._name_prefix)
107 self._entries[node.name] = entry
110 """Get the set of device tree files used by this image"""
112 for entry in self._entries.values():
113 fdt_set.update(entry.GetFdtSet())
116 def SetOffset(self, offset):
117 self._offset = offset
119 def ExpandEntries(self):
120 for entry in self._entries.values():
121 entry.ExpandEntries()
123 def AddMissingProperties(self):
124 """Add new properties to the device tree as needed for this entry"""
125 for prop in ['offset', 'size', 'image-pos']:
126 if not prop in self._node.props:
127 state.AddZeroProp(self._node, prop)
128 state.CheckAddHashProp(self._node)
129 for entry in self._entries.values():
130 entry.AddMissingProperties()
132 def SetCalculatedProperties(self):
133 state.SetInt(self._node, 'offset', self._offset)
134 state.SetInt(self._node, 'size', self._size)
135 image_pos = self._image_pos
136 if self._parent_section:
137 image_pos -= self._parent_section.GetRootSkipAtStart()
138 state.SetInt(self._node, 'image-pos', image_pos)
139 for entry in self._entries.values():
140 entry.SetCalculatedProperties()
142 def ProcessFdt(self, fdt):
143 todo = self._entries.values()
144 for passnum in range(3):
147 if not entry.ProcessFdt(fdt):
148 next_todo.append(entry)
153 self._Raise('Internal error: Could not complete processing of Fdt: '
154 'remaining %s' % todo)
158 """Check that the section contents does not exceed its size, etc."""
160 for entry in self._entries.values():
161 contents_size = max(contents_size, entry.offset + entry.size)
163 contents_size -= self._skip_at_start
167 size = self._pad_before + contents_size + self._pad_after
168 size = tools.Align(size, self._align_size)
170 if self._size and contents_size > self._size:
171 self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" %
172 (contents_size, contents_size, self._size, self._size))
175 if self._size != tools.Align(self._size, self._align_size):
176 self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
177 (self._size, self._size, self._align_size, self._align_size))
180 def _Raise(self, msg):
181 """Raises an error for this section
184 msg: Error message to use in the raise string
188 raise ValueError("Section '%s': %s" % (self._node.path, msg))
191 """Get the path of an image (in the FDT)
194 Full path of the node for this image
196 return self._node.path
198 def FindEntryType(self, etype):
199 """Find an entry type in the section
202 etype: Entry type to find
204 entry matching that type, or None if not found
206 for entry in self._entries.values():
207 if entry.etype == etype:
211 def GetEntryContents(self):
212 """Call ObtainContents() for each entry
214 This calls each entry's ObtainContents() a few times until they all
215 return True. We stop calling an entry's function once it returns
216 True. This allows the contents of one entry to depend on another.
218 After 3 rounds we give up since it's likely an error.
220 todo = self._entries.values()
221 for passnum in range(3):
224 if not entry.ObtainContents():
225 next_todo.append(entry)
230 self._Raise('Internal error: Could not complete processing of '
231 'contents: remaining %s' % todo)
234 def _SetEntryOffsetSize(self, name, offset, size):
235 """Set the offset and size of an entry
238 name: Entry name to update
242 entry = self._entries.get(name)
244 self._Raise("Unable to set offset/size for unknown entry '%s'" %
246 entry.SetOffsetSize(self._skip_at_start + offset, size)
248 def GetEntryOffsets(self):
249 """Handle entries that want to set the offset/size of other entries
251 This calls each entry's GetOffsets() method. If it returns a list
252 of entries to update, it updates them.
254 for entry in self._entries.values():
255 offset_dict = entry.GetOffsets()
256 for name, info in offset_dict.iteritems():
257 self._SetEntryOffsetSize(name, *info)
259 def PackEntries(self):
260 """Pack all entries into the section"""
261 offset = self._skip_at_start
262 for entry in self._entries.values():
263 offset = entry.Pack(offset)
264 self._size = self.CheckSize()
266 def _SortEntries(self):
267 """Sort entries by offset"""
268 entries = sorted(self._entries.values(), key=lambda entry: entry.offset)
269 self._entries.clear()
270 for entry in entries:
271 self._entries[entry._node.name] = entry
273 def _ExpandEntries(self):
274 """Expand any entries that are permitted to"""
276 for entry in self._entries.values():
278 exp_entry.ExpandToLimit(entry.offset)
280 if entry.expand_size:
283 exp_entry.ExpandToLimit(self._size)
285 def CheckEntries(self):
286 """Check that entries do not overlap or extend outside the section
288 This also sorts entries, if needed and expands
292 self._ExpandEntries()
295 for entry in self._entries.values():
297 if (entry.offset < self._skip_at_start or
298 entry.offset + entry.size > self._skip_at_start + self._size):
299 entry.Raise("Offset %#x (%d) is outside the section starting "
301 (entry.offset, entry.offset, self._skip_at_start,
302 self._skip_at_start))
303 if entry.offset < offset:
304 entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' "
305 "ending at %#x (%d)" %
306 (entry.offset, entry.offset, prev_name, offset, offset))
307 offset = entry.offset + entry.size
308 prev_name = entry.GetPath()
310 def SetImagePos(self, image_pos):
311 self._image_pos = image_pos
312 for entry in self._entries.values():
313 entry.SetImagePos(image_pos)
315 def ProcessEntryContents(self):
316 """Call the ProcessContents() method for each entry
318 This is intended to adjust the contents as needed by the entry type.
320 for entry in self._entries.values():
321 entry.ProcessContents()
323 def WriteSymbols(self):
324 """Write symbol values into binary files for access at run time"""
325 for entry in self._entries.values():
326 entry.WriteSymbols(self)
328 def BuildSection(self, fd, base_offset):
329 """Write the section to a file"""
331 fd.write(self.GetData())
334 """Get the contents of the section"""
335 section_data = chr(self._pad_byte) * self._size
337 for entry in self._entries.values():
338 data = entry.GetData()
339 base = self._pad_before + entry.offset - self._skip_at_start
340 section_data = (section_data[:base] + data +
341 section_data[base + len(data):])
344 def LookupSymbol(self, sym_name, optional, msg):
345 """Look up a symbol in an ELF file
347 Looks up a symbol in an ELF file. Only entry types which come from an
348 ELF image can be used by this function.
350 At present the only entry property supported is offset.
353 sym_name: Symbol name in the ELF file to look up in the format
354 _binman_<entry>_prop_<property> where <entry> is the name of
355 the entry and <property> is the property to find (e.g.
356 _binman_u_boot_prop_offset). As a special case, you can append
357 _any to <entry> to have it search for any matching entry. E.g.
358 _binman_u_boot_any_prop_offset will match entries called u-boot,
359 u-boot-img and u-boot-nodtb)
360 optional: True if the symbol is optional. If False this function
361 will raise if the symbol is not found
362 msg: Message to display if an error occurs
365 Value that should be assigned to that symbol, or None if it was
366 optional and not found
369 ValueError if the symbol is invalid or not found, or references a
370 property which is not supported
372 m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name)
374 raise ValueError("%s: Symbol '%s' has invalid format" %
376 entry_name, prop_name = m.groups()
377 entry_name = entry_name.replace('_', '-')
378 entry = self._entries.get(entry_name)
380 if entry_name.endswith('-any'):
381 root = entry_name[:-4]
382 for name in self._entries:
383 if name.startswith(root):
384 rest = name[len(root):]
385 if rest in ['', '-img', '-nodtb']:
386 entry = self._entries[name]
388 err = ("%s: Entry '%s' not found in list (%s)" %
389 (msg, entry_name, ','.join(self._entries.keys())))
391 print('Warning: %s' % err, file=sys.stderr)
393 raise ValueError(err)
394 if prop_name == 'offset':
396 elif prop_name == 'image_pos':
397 return entry.image_pos
399 raise ValueError("%s: No such property '%s'" % (msg, prop_name))
401 def GetEntries(self):
402 """Get the number of entries in a section
405 Number of entries in a section
410 """Get the size of a section in bytes
412 This is only meaningful if the section has a pre-defined size, or the
413 entries within it have been packed, so that the size has been
421 def WriteMap(self, fd, indent):
422 """Write a map of the section to a .map file
425 fd: File to write the map to
427 Entry.WriteMapLine(fd, indent, self._name, self._offset, self._size,
429 for entry in self._entries.values():
430 entry.WriteMap(fd, indent + 1)
432 def GetContentsByPhandle(self, phandle, source_entry):
433 """Get the data contents of an entry specified by a phandle
435 This uses a phandle to look up a node and and find the entry
436 associated with it. Then it returnst he contents of that entry.
439 phandle: Phandle to look up (integer)
440 source_entry: Entry containing that phandle (used for error
444 data from associated entry (as a string), or None if not found
446 node = self._node.GetFdt().LookupPhandle(phandle)
448 source_entry.Raise("Cannot find node for phandle %d" % phandle)
449 for entry in self._entries.values():
450 if entry._node == node:
451 return entry.GetData()
452 source_entry.Raise("Cannot find entry for node '%s'" % node.name)
454 def ExpandSize(self, size):
455 if size != self._size:
458 def GetRootSkipAtStart(self):
459 if self._parent_section:
460 return self._parent_section.GetRootSkipAtStart()
461 return self._skip_at_start
463 def GetImageSize(self):
464 return self._image._size