]>
git.ipfire.org Git - thirdparty/u-boot.git/blob - tools/binman/etype/section.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2018 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
5 """Entry-type module for sections (groups of entries)
7 Sections are entries which can contain other entries. This allows hierarchical
11 from collections
import OrderedDict
15 from binman
.entry
import Entry
16 from dtoc
import fdt_util
17 from patman
import tools
18 from patman
import tout
19 from patman
.tools
import ToHexSize
22 class Entry_section(Entry
):
23 """Entry that contains other entries
25 Properties / Entry arguments: (see binman README for more information):
26 pad-byte: Pad byte to use when padding
27 sort-by-offset: True if entries should be sorted by offset, False if
28 they must be in-order in the device tree description
30 end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
32 skip-at-start: Number of bytes before the first entry starts. These
33 effectively adjust the starting offset of entries. For example,
34 if this is 16, then the first entry would start at 16. An entry
35 with offset = 20 would in fact be written at offset 4 in the image
36 file, since the first 16 bytes are skipped when writing.
37 name-prefix: Adds a prefix to the name of every entry in the section
38 when writing out the map
39 align_default: Default alignment for this section, if no alignment is
43 allow_missing: True if this section permits external blobs to be
44 missing their contents. The second will produce an image but of
45 course it will not work.
47 Since a section is also an entry, it inherits all the properies of entries
50 A section is an entry which can contain other entries, thus allowing
51 hierarchical images to be created. See 'Sections and hierarchical images'
52 in the binman README for more information.
54 def __init__(self
, section
, etype
, node
, test
=False):
56 super().__init
__(section
, etype
, node
)
57 self
._entries
= OrderedDict()
60 self
._skip
_at
_start
= None
64 """Read properties from the section node"""
66 self
._pad
_byte
= fdt_util
.GetInt(self
._node
, 'pad-byte', 0)
67 self
._sort
= fdt_util
.GetBool(self
._node
, 'sort-by-offset')
68 self
._end
_4gb
= fdt_util
.GetBool(self
._node
, 'end-at-4gb')
69 self
._skip
_at
_start
= fdt_util
.GetInt(self
._node
, 'skip-at-start')
72 self
.Raise("Section size must be provided when using end-at-4gb")
73 if self
._skip
_at
_start
is not None:
74 self
.Raise("Provide either 'end-at-4gb' or 'skip-at-start'")
76 self
._skip
_at
_start
= 0x100000000 - self
.size
78 if self
._skip
_at
_start
is None:
79 self
._skip
_at
_start
= 0
80 self
._name
_prefix
= fdt_util
.GetString(self
._node
, 'name-prefix')
81 self
.align_default
= fdt_util
.GetInt(self
._node
, 'align-default', 0)
82 filename
= fdt_util
.GetString(self
._node
, 'filename')
84 self
._filename
= filename
88 def _ReadEntries(self
):
89 for node
in self
._node
.subnodes
:
90 if node
.name
.startswith('hash') or node
.name
.startswith('signature'):
92 entry
= Entry
.Create(self
, node
,
93 expanded
=self
.GetImage().use_expanded
)
95 entry
.SetPrefix(self
._name
_prefix
)
96 self
._entries
[node
.name
] = entry
98 def _Raise(self
, msg
):
99 """Raises an error for this section
102 msg: Error message to use in the raise string
106 raise ValueError("Section '%s': %s" % (self
._node
.path
, msg
))
110 for entry
in self
._entries
.values():
111 fdts
.update(entry
.GetFdts())
114 def ProcessFdt(self
, fdt
):
115 """Allow entries to adjust the device tree
117 Some entries need to adjust the device tree for their purposes. This
118 may involve adding or deleting properties.
120 todo
= self
._entries
.values()
121 for passnum
in range(3):
124 if not entry
.ProcessFdt(fdt
):
125 next_todo
.append(entry
)
130 self
.Raise('Internal error: Could not complete processing of Fdt: remaining %s' %
134 def ExpandEntries(self
):
135 super().ExpandEntries()
136 for entry
in self
._entries
.values():
137 entry
.ExpandEntries()
139 def AddMissingProperties(self
, have_image_pos
):
140 """Add new properties to the device tree as needed for this entry"""
141 super().AddMissingProperties(have_image_pos
)
142 if self
.compress
!= 'none':
143 have_image_pos
= False
144 for entry
in self
._entries
.values():
145 entry
.AddMissingProperties(have_image_pos
)
147 def ObtainContents(self
):
148 return self
.GetEntryContents()
150 def GetPaddedDataForEntry(self
, entry
, entry_data
):
151 """Get the data for an entry including any padding
153 Gets the entry data and uses the section pad-byte value to add padding
154 before and after as defined by the pad-before and pad-after properties.
155 This does not consider alignment.
158 entry: Entry to check
161 Contents of the entry along with any pad bytes before and
164 pad_byte
= (entry
._pad
_byte
if isinstance(entry
, Entry_section
)
168 # Handle padding before the entry
170 data
+= tools
.GetBytes(self
._pad
_byte
, entry
.pad_before
)
172 # Add in the actual entry data
175 # Handle padding after the entry
177 data
+= tools
.GetBytes(self
._pad
_byte
, entry
.pad_after
)
180 data
+= tools
.GetBytes(pad_byte
, entry
.size
- len(data
))
182 self
.Detail('GetPaddedDataForEntry: size %s' % ToHexSize(self
.data
))
186 def _BuildSectionData(self
, required
):
187 """Build the contents of a section
189 This places all entries at the right place, dealing with padding before
190 and after entries. It does not do padding for the section itself (the
191 pad-before and pad-after properties in the section items) since that is
192 handled by the parent section.
195 required: True if the data must be present, False if it is OK to
199 Contents of the section (bytes)
203 for entry
in self
._entries
.values():
204 entry_data
= entry
.GetData(required
)
205 if not required
and entry_data
is None:
207 data
= self
.GetPaddedDataForEntry(entry
, entry_data
)
208 # Handle empty space before the entry
209 pad
= (entry
.offset
or 0) - self
._skip
_at
_start
- len(section_data
)
211 section_data
+= tools
.GetBytes(self
._pad
_byte
, pad
)
213 # Add in the actual entry data
216 self
.Detail('GetData: %d entries, total size %#x' %
217 (len(self
._entries
), len(section_data
)))
218 return self
.CompressData(section_data
)
220 def GetPaddedData(self
, data
=None):
221 """Get the data for a section including any padding
223 Gets the section data and uses the parent section's pad-byte value to
224 add padding before and after as defined by the pad-before and pad-after
225 properties. If this is a top-level section (i.e. an image), this is the
226 same as GetData(), since padding is not supported.
228 This does not consider alignment.
231 Contents of the section along with any pad bytes before and
234 section
= self
.section
or self
236 data
= self
.GetData()
237 return section
.GetPaddedDataForEntry(self
, data
)
239 def GetData(self
, required
=True):
240 """Get the contents of an entry
242 This builds the contents of the section, stores this as the contents of
243 the section and returns it
246 required: True if the data must be present, False if it is OK to
250 bytes content of the section, made up for all all of its subentries.
251 This excludes any padding. If the section is compressed, the
252 compressed data is returned
254 data
= self
._BuildSectionData
(required
)
257 self
.SetContents(data
)
260 def GetOffsets(self
):
261 """Handle entries that want to set the offset/size of other entries
263 This calls each entry's GetOffsets() method. If it returns a list
264 of entries to update, it updates them.
266 self
.GetEntryOffsets()
269 def ResetForPack(self
):
270 """Reset offset/size fields so that packing can be done again"""
271 super().ResetForPack()
272 for entry
in self
._entries
.values():
275 def Pack(self
, offset
):
276 """Pack all entries into the section"""
280 self
._ExpandEntries
()
282 data
= self
._BuildSectionData
(True)
283 self
.SetContents(data
)
287 offset
= super().Pack(offset
)
291 def _PackEntries(self
):
292 """Pack all entries into the section"""
293 offset
= self
._skip
_at
_start
294 for entry
in self
._entries
.values():
295 offset
= entry
.Pack(offset
)
298 def _ExpandEntries(self
):
299 """Expand any entries that are permitted to"""
301 for entry
in self
._entries
.values():
303 exp_entry
.ExpandToLimit(entry
.offset
)
305 if entry
.expand_size
:
308 exp_entry
.ExpandToLimit(self
.size
)
310 def _SortEntries(self
):
311 """Sort entries by offset"""
312 entries
= sorted(self
._entries
.values(), key
=lambda entry
: entry
.offset
)
313 self
._entries
.clear()
314 for entry
in entries
:
315 self
._entries
[entry
._node
.name
] = entry
317 def CheckEntries(self
):
318 """Check that entries do not overlap or extend outside the section"""
319 max_size
= self
.size
if self
.uncomp_size
is None else self
.uncomp_size
323 for entry
in self
._entries
.values():
325 if (entry
.offset
< self
._skip
_at
_start
or
326 entry
.offset
+ entry
.size
> self
._skip
_at
_start
+
328 entry
.Raise('Offset %#x (%d) size %#x (%d) is outside the '
329 "section '%s' starting at %#x (%d) "
331 (entry
.offset
, entry
.offset
, entry
.size
, entry
.size
,
332 self
._node
.path
, self
._skip
_at
_start
,
333 self
._skip
_at
_start
, max_size
, max_size
))
334 if entry
.offset
< offset
and entry
.size
:
335 entry
.Raise("Offset %#x (%d) overlaps with previous entry '%s' "
336 "ending at %#x (%d)" %
337 (entry
.offset
, entry
.offset
, prev_name
, offset
, offset
))
338 offset
= entry
.offset
+ entry
.size
339 prev_name
= entry
.GetPath()
341 def WriteSymbols(self
, section
):
342 """Write symbol values into binary files for access at run time"""
343 for entry
in self
._entries
.values():
344 entry
.WriteSymbols(self
)
346 def SetCalculatedProperties(self
):
347 super().SetCalculatedProperties()
348 for entry
in self
._entries
.values():
349 entry
.SetCalculatedProperties()
351 def SetImagePos(self
, image_pos
):
352 super().SetImagePos(image_pos
)
353 if self
.compress
== 'none':
354 for entry
in self
._entries
.values():
355 entry
.SetImagePos(image_pos
+ self
.offset
)
357 def ProcessContents(self
):
358 sizes_ok_base
= super(Entry_section
, self
).ProcessContents()
360 for entry
in self
._entries
.values():
361 if not entry
.ProcessContents():
363 return sizes_ok
and sizes_ok_base
365 def WriteMap(self
, fd
, indent
):
366 """Write a map of the section to a .map file
369 fd: File to write the map to
371 Entry
.WriteMapLine(fd
, indent
, self
.name
, self
.offset
or 0,
372 self
.size
, self
.image_pos
)
373 for entry
in self
._entries
.values():
374 entry
.WriteMap(fd
, indent
+ 1)
376 def GetEntries(self
):
379 def GetContentsByPhandle(self
, phandle
, source_entry
, required
):
380 """Get the data contents of an entry specified by a phandle
382 This uses a phandle to look up a node and and find the entry
383 associated with it. Then it returns the contents of that entry.
385 The node must be a direct subnode of this section.
388 phandle: Phandle to look up (integer)
389 source_entry: Entry containing that phandle (used for error
391 required: True if the data must be present, False if it is OK to
395 data from associated entry (as a string), or None if not found
397 node
= self
._node
.GetFdt().LookupPhandle(phandle
)
399 source_entry
.Raise("Cannot find node for phandle %d" % phandle
)
400 for entry
in self
._entries
.values():
401 if entry
._node
== node
:
402 return entry
.GetData(required
)
403 source_entry
.Raise("Cannot find entry for node '%s'" % node
.name
)
405 def LookupSymbol(self
, sym_name
, optional
, msg
, base_addr
, entries
=None):
406 """Look up a symbol in an ELF file
408 Looks up a symbol in an ELF file. Only entry types which come from an
409 ELF image can be used by this function.
411 At present the only entry properties supported are:
413 image_pos - 'base_addr' is added if this is not an end-at-4gb image
417 sym_name: Symbol name in the ELF file to look up in the format
418 _binman_<entry>_prop_<property> where <entry> is the name of
419 the entry and <property> is the property to find (e.g.
420 _binman_u_boot_prop_offset). As a special case, you can append
421 _any to <entry> to have it search for any matching entry. E.g.
422 _binman_u_boot_any_prop_offset will match entries called u-boot,
423 u-boot-img and u-boot-nodtb)
424 optional: True if the symbol is optional. If False this function
425 will raise if the symbol is not found
426 msg: Message to display if an error occurs
427 base_addr: Base address of image. This is added to the returned
428 image_pos in most cases so that the returned position indicates
429 where the targetted entry/binary has actually been loaded. But
430 if end-at-4gb is used, this is not done, since the binary is
431 already assumed to be linked to the ROM position and using
432 execute-in-place (XIP).
435 Value that should be assigned to that symbol, or None if it was
436 optional and not found
439 ValueError if the symbol is invalid or not found, or references a
440 property which is not supported
442 m
= re
.match(r
'^_binman_(\w+)_prop_(\w+)$', sym_name
)
444 raise ValueError("%s: Symbol '%s' has invalid format" %
446 entry_name
, prop_name
= m
.groups()
447 entry_name
= entry_name
.replace('_', '-')
449 entries
= self
._entries
450 entry
= entries
.get(entry_name
)
452 if entry_name
.endswith('-any'):
453 root
= entry_name
[:-4]
455 if name
.startswith(root
):
456 rest
= name
[len(root
):]
457 if rest
in ['', '-img', '-nodtb']:
458 entry
= entries
[name
]
460 err
= ("%s: Entry '%s' not found in list (%s)" %
461 (msg
, entry_name
, ','.join(entries
.keys())))
463 print('Warning: %s' % err
, file=sys
.stderr
)
465 raise ValueError(err
)
466 if prop_name
== 'offset':
468 elif prop_name
== 'image_pos':
469 value
= entry
.image_pos
470 if not self
.GetImage()._end
_4gb
:
473 if prop_name
== 'size':
476 raise ValueError("%s: No such property '%s'" % (msg
, prop_name
))
478 def GetRootSkipAtStart(self
):
479 """Get the skip-at-start value for the top-level section
481 This is used to find out the starting offset for root section that
482 contains this section. If this is a top-level section then it returns
483 the skip-at-start offset for this section.
485 This is used to get the absolute position of section within the image.
488 Integer skip-at-start value for the root section containing this
492 return self
.section
.GetRootSkipAtStart()
493 return self
._skip
_at
_start
495 def GetStartOffset(self
):
496 """Get the start offset for this section
499 The first available offset in this section (typically 0)
501 return self
._skip
_at
_start
503 def GetImageSize(self
):
504 """Get the size of the image containing this section
507 Image size as an integer number of bytes, which may be None if the
508 image size is dynamic and its sections have not yet been packed
510 return self
.GetImage().size
512 def FindEntryType(self
, etype
):
513 """Find an entry type in the section
516 etype: Entry type to find
518 entry matching that type, or None if not found
520 for entry
in self
._entries
.values():
521 if entry
.etype
== etype
:
525 def GetEntryContents(self
):
526 """Call ObtainContents() for each entry in the section
528 todo
= self
._entries
.values()
529 for passnum
in range(3):
532 if not entry
.ObtainContents():
533 next_todo
.append(entry
)
538 self
.Raise('Internal error: Could not complete processing of contents: remaining %s' %
542 def _SetEntryOffsetSize(self
, name
, offset
, size
):
543 """Set the offset and size of an entry
546 name: Entry name to update
547 offset: New offset, or None to leave alone
548 size: New size, or None to leave alone
550 entry
= self
._entries
.get(name
)
552 self
._Raise
("Unable to set offset/size for unknown entry '%s'" %
554 entry
.SetOffsetSize(self
._skip
_at
_start
+ offset
if offset
is not None
557 def GetEntryOffsets(self
):
558 """Handle entries that want to set the offset/size of other entries
560 This calls each entry's GetOffsets() method. If it returns a list
561 of entries to update, it updates them.
563 for entry
in self
._entries
.values():
564 offset_dict
= entry
.GetOffsets()
565 for name
, info
in offset_dict
.items():
566 self
._SetEntryOffsetSize
(name
, *info
)
569 contents_size
= len(self
.data
)
573 data
= self
.GetPaddedData(self
.data
)
575 size
= tools
.Align(size
, self
.align_size
)
577 if self
.size
and contents_size
> self
.size
:
578 self
._Raise
("contents size %#x (%d) exceeds section size %#x (%d)" %
579 (contents_size
, contents_size
, self
.size
, self
.size
))
582 if self
.size
!= tools
.Align(self
.size
, self
.align_size
):
583 self
._Raise
("Size %#x (%d) does not match align-size %#x (%d)" %
584 (self
.size
, self
.size
, self
.align_size
,
588 def ListEntries(self
, entries
, indent
):
589 """List the files in the section"""
590 Entry
.AddEntryInfo(entries
, indent
, self
.name
, 'section', self
.size
,
591 self
.image_pos
, None, self
.offset
, self
)
592 for entry
in self
._entries
.values():
593 entry
.ListEntries(entries
, indent
+ 1)
595 def LoadData(self
, decomp
=True):
596 for entry
in self
._entries
.values():
597 entry
.LoadData(decomp
)
598 self
.Detail('Loaded data')
601 """Get the image containing this section
603 Note that a top-level section is actually an Image, so this function may
607 Image object containing this section
611 return self
.section
.GetImage()
614 """Check if the entries in this section will be sorted
617 True if to be sorted, False if entries will be left in the order
618 they appear in the device tree
622 def ReadData(self
, decomp
=True):
623 tout
.Info("ReadData path='%s'" % self
.GetPath())
624 parent_data
= self
.section
.ReadData(True)
625 offset
= self
.offset
- self
.section
._skip
_at
_start
626 data
= parent_data
[offset
:offset
+ self
.size
]
628 '%s: Reading data from offset %#x-%#x (real %#x), size %#x, got %#x' %
629 (self
.GetPath(), self
.offset
, self
.offset
+ self
.size
, offset
,
630 self
.size
, len(data
)))
633 def ReadChildData(self
, child
, decomp
=True):
634 tout
.Debug("ReadChildData for child '%s'" % child
.GetPath())
635 parent_data
= self
.ReadData(True)
636 offset
= child
.offset
- self
._skip
_at
_start
637 tout
.Debug("Extract for child '%s': offset %#x, skip_at_start %#x, result %#x" %
638 (child
.GetPath(), child
.offset
, self
._skip
_at
_start
, offset
))
639 data
= parent_data
[offset
:offset
+ child
.size
]
642 data
= tools
.Decompress(indata
, child
.compress
)
643 if child
.uncomp_size
:
644 tout
.Info("%s: Decompressing data size %#x with algo '%s' to data size %#x" %
645 (child
.GetPath(), len(indata
), child
.compress
,
649 def WriteChildData(self
, child
):
652 def SetAllowMissing(self
, allow_missing
):
653 """Set whether a section allows missing external blobs
656 allow_missing: True if allowed, False if not allowed
658 self
.allow_missing
= allow_missing
659 for entry
in self
._entries
.values():
660 entry
.SetAllowMissing(allow_missing
)
662 def CheckMissing(self
, missing_list
):
663 """Check if any entries in this section have missing external blobs
665 If there are missing blobs, the entries are added to the list
668 missing_list: List of Entry objects to be added to
670 for entry
in self
._entries
.values():
671 entry
.CheckMissing(missing_list
)
673 def _CollectEntries(self
, entries
, entries_by_name
, add_entry
):
674 """Collect all the entries in an section
676 This builds up a dict of entries in this section and all subsections.
677 Entries are indexed by path and by name.
679 Since all paths are unique, entries will not have any conflicts. However
680 entries_by_name make have conflicts if two entries have the same name
681 (e.g. with different parent sections). In this case, an entry at a
682 higher level in the hierarchy will win over a lower-level entry.
685 entries: dict to put entries:
688 entries_by_name: dict to put entries
691 add_entry: Entry to add
693 entries
[add_entry
.GetPath()] = add_entry
694 to_add
= add_entry
.GetEntries()
696 for entry
in to_add
.values():
697 entries
[entry
.GetPath()] = entry
698 for entry
in to_add
.values():
699 self
._CollectEntries
(entries
, entries_by_name
, entry
)
700 entries_by_name
[add_entry
.name
] = add_entry
702 def MissingArgs(self
, entry
, missing
):
703 """Report a missing argument, if enabled
705 For entries which require arguments, this reports an error if some are
706 missing. If missing entries are being ignored (e.g. because we read the
707 entry from an image rather than creating it), this function does
711 missing: List of missing properties / entry args, each a string
713 if not self
._ignore
_missing
:
714 entry
.Raise('Missing required properties/entry args: %s' %
715 (', '.join(missing
)))