aboutsummaryrefslogtreecommitdiff
path: root/tools/binman/etype/section.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/binman/etype/section.py')
-rw-r--r--tools/binman/etype/section.py214
1 files changed, 165 insertions, 49 deletions
diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py
index e2949fc916..43436a11f2 100644
--- a/tools/binman/etype/section.py
+++ b/tools/binman/etype/section.py
@@ -24,34 +24,135 @@ from patman.tools import ToHexSize
class Entry_section(Entry):
"""Entry that contains other entries
- Properties / Entry arguments: (see binman README for more information):
- pad-byte: Pad byte to use when padding
- sort-by-offset: True if entries should be sorted by offset, False if
- they must be in-order in the device tree description
-
- end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
-
- skip-at-start: Number of bytes before the first entry starts. These
- effectively adjust the starting offset of entries. For example,
- if this is 16, then the first entry would start at 16. An entry
- with offset = 20 would in fact be written at offset 4 in the image
- file, since the first 16 bytes are skipped when writing.
- name-prefix: Adds a prefix to the name of every entry in the section
- when writing out the map
- align_default: Default alignment for this section, if no alignment is
- given in the entry
-
- Properties:
- allow_missing: True if this section permits external blobs to be
- missing their contents. The second will produce an image but of
- course it will not work.
+ A section is an entry which can contain other entries, thus allowing
+ hierarchical images to be created. See 'Sections and hierarchical images'
+ in the binman README for more information.
+
+ The base implementation simply joins the various entries together, using
+ various rules about alignment, etc.
+
+ Subclassing
+ ~~~~~~~~~~~
+
+ This class can be subclassed to support other file formats which hold
+ multiple entries, such as CBFS. To do this, override the following
+ functions. The documentation here describes what your function should do.
+ For example code, see etypes which subclass `Entry_section`, or `cbfs.py`
+ for a more involved example::
+
+ $ grep -l \(Entry_section tools/binman/etype/*.py
+
+ ReadNode()
+ Call `super().ReadNode()`, then read any special properties for the
+ section. Then call `self.ReadEntries()` to read the entries.
+
+ Binman calls this at the start when reading the image description.
+
+ ReadEntries()
+ Read in the subnodes of the section. This may involve creating entries
+ of a particular etype automatically, as well as reading any special
+ properties in the entries. For each entry, entry.ReadNode() should be
+ called, to read the basic entry properties. The properties should be
+ added to `self._entries[]`, in the correct order, with a suitable name.
+
+ Binman calls this at the start when reading the image description.
+
+ BuildSectionData(required)
+ Create the custom file format that you want and return it as bytes.
+ This likely sets up a file header, then loops through the entries,
+ adding them to the file. For each entry, call `entry.GetData()` to
+ obtain the data. If that returns None, and `required` is False, then
+ this method must give up and return None. But if `required` is True then
+ it should assume that all data is valid.
+
+ Binman calls this when packing the image, to find out the size of
+ everything. It is called again at the end when building the final image.
+
+ SetImagePos(image_pos):
+ Call `super().SetImagePos(image_pos)`, then set the `image_pos` values
+ for each of the entries. This should use the custom file format to find
+ the `start offset` (and `image_pos`) of each entry. If the file format
+ uses compression in such a way that there is no offset available (other
+ than reading the whole file and decompressing it), then the offsets for
+ affected entries can remain unset (`None`). The size should also be set
+ if possible.
+
+ Binman calls this after the image has been packed, to update the
+ location that all the entries ended up at.
+
+ ReadChildData(child, decomp, alt_format):
+ The default version of this may be good enough, if you are able to
+ implement SetImagePos() correctly. But that is a bit of a bypass, so
+ you can override this method to read from your custom file format. It
+ should read the entire entry containing the custom file using
+ `super().ReadData(True)`, then parse the file to get the data for the
+ given child, then return that data.
+
+ If your file format supports compression, the `decomp` argument tells
+ you whether to return the compressed data (`decomp` is False) or to
+ uncompress it first, then return the uncompressed data (`decomp` is
+ True). This is used by the `binman extract -U` option.
+
+ If your entry supports alternative formats, the alt_format provides the
+ alternative format that the user has selected. Your function should
+ return data in that format. This is used by the 'binman extract -l'
+ option.
+
+ Binman calls this when reading in an image, in order to populate all the
+ entries with the data from that image (`binman ls`).
+
+ WriteChildData(child):
+ Binman calls this after `child.data` is updated, to inform the custom
+ file format about this, in case it needs to do updates.
+
+ The default version of this does nothing and probably needs to be
+ overridden for the 'binman replace' command to work. Your version should
+ use `child.data` to update the data for that child in the custom file
+ format.
+
+ Binman calls this when updating an image that has been read in and in
+ particular to update the data for a particular entry (`binman replace`)
+
+ Properties / Entry arguments
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ See :ref:`develop/package/binman:Image description format` for more
+ information.
+
+ align-default
+ Default alignment for this section, if no alignment is given in the
+ entry
+
+ pad-byte
+ Pad byte to use when padding
+
+ sort-by-offset
+ True if entries should be sorted by offset, False if they must be
+ in-order in the device tree description
+
+ end-at-4gb
+ Used to build an x86 ROM which ends at 4GB (2^32)
+
+ name-prefix
+ Adds a prefix to the name of every entry in the section when writing out
+ the map
+
+ skip-at-start
+ Number of bytes before the first entry starts. These effectively adjust
+ the starting offset of entries. For example, if this is 16, then the
+ first entry would start at 16. An entry with offset = 20 would in fact
+ be written at offset 4 in the image file, since the first 16 bytes are
+ skipped when writing.
Since a section is also an entry, it inherits all the properies of entries
too.
- A section is an entry which can contain other entries, thus allowing
- hierarchical images to be created. See 'Sections and hierarchical images'
- in the binman README for more information.
+ Note that the `allow_missing` member controls whether this section permits
+ external blobs to be missing their contents. The option will produce an
+ image but of course it will not work. It is useful to make sure that
+ Continuous Integration systems can build without the binaries being
+ available. This is set by the `SetAllowMissing()` method, if
+ `--allow-missing` is passed to binman.
"""
def __init__(self, section, etype, node, test=False):
if not test:
@@ -81,18 +182,16 @@ class Entry_section(Entry):
self._skip_at_start = 0
self._name_prefix = fdt_util.GetString(self._node, 'name-prefix')
self.align_default = fdt_util.GetInt(self._node, 'align-default', 0)
- filename = fdt_util.GetString(self._node, 'filename')
- if filename:
- self._filename = filename
- self._ReadEntries()
+ self.ReadEntries()
- def _ReadEntries(self):
+ def ReadEntries(self):
for node in self._node.subnodes:
if node.name.startswith('hash') or node.name.startswith('signature'):
continue
entry = Entry.Create(self, node,
- expanded=self.GetImage().use_expanded)
+ expanded=self.GetImage().use_expanded,
+ missing_etype=self.GetImage().missing_etype)
entry.ReadNode()
entry.SetPrefix(self._name_prefix)
self._entries[node.name] = entry
@@ -101,9 +200,9 @@ class Entry_section(Entry):
"""Raises an error for this section
Args:
- msg: Error message to use in the raise string
+ msg (str): Error message to use in the raise string
Raises:
- ValueError()
+ ValueError: always
"""
raise ValueError("Section '%s': %s" % (self._node.path, msg))
@@ -146,8 +245,8 @@ class Entry_section(Entry):
for entry in self._entries.values():
entry.AddMissingProperties(have_image_pos)
- def ObtainContents(self):
- return self.GetEntryContents()
+ def ObtainContents(self, skip_entry=None):
+ return self.GetEntryContents(skip_entry=skip_entry)
def GetPaddedDataForEntry(self, entry, entry_data):
"""Get the data for an entry including any padding
@@ -185,7 +284,7 @@ class Entry_section(Entry):
return data
- def _BuildSectionData(self, required):
+ def BuildSectionData(self, required):
"""Build the contents of a section
This places all entries at the right place, dealing with padding before
@@ -193,6 +292,9 @@ class Entry_section(Entry):
pad-before and pad-after properties in the section items) since that is
handled by the parent section.
+ This should be overridden by subclasses which want to build their own
+ data structure for the section.
+
Args:
required: True if the data must be present, False if it is OK to
return None
@@ -204,6 +306,9 @@ class Entry_section(Entry):
for entry in self._entries.values():
entry_data = entry.GetData(required)
+
+ # This can happen when this section is referenced from a collection
+ # earlier in the image description. See testCollectionSection().
if not required and entry_data is None:
return None
data = self.GetPaddedDataForEntry(entry, entry_data)
@@ -253,7 +358,7 @@ class Entry_section(Entry):
This excludes any padding. If the section is compressed, the
compressed data is returned
"""
- data = self._BuildSectionData(required)
+ data = self.BuildSectionData(required)
if data is None:
return None
self.SetContents(data)
@@ -281,7 +386,7 @@ class Entry_section(Entry):
self._SortEntries()
self._ExpandEntries()
- data = self._BuildSectionData(True)
+ data = self.BuildSectionData(True)
self.SetContents(data)
self.CheckSize()
@@ -524,12 +629,13 @@ class Entry_section(Entry):
return entry
return None
- def GetEntryContents(self):
+ def GetEntryContents(self, skip_entry=None):
"""Call ObtainContents() for each entry in the section
"""
def _CheckDone(entry):
- if not entry.ObtainContents():
- next_todo.append(entry)
+ if entry != skip_entry:
+ if not entry.ObtainContents():
+ next_todo.append(entry)
return entry
todo = self._entries.values()
@@ -617,7 +723,7 @@ class Entry_section(Entry):
def ListEntries(self, entries, indent):
"""List the files in the section"""
- Entry.AddEntryInfo(entries, indent, self.name, 'section', self.size,
+ Entry.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
self.image_pos, None, self.offset, self)
for entry in self._entries.values():
entry.ListEntries(entries, indent + 1)
@@ -649,9 +755,9 @@ class Entry_section(Entry):
"""
return self._sort
- def ReadData(self, decomp=True):
+ def ReadData(self, decomp=True, alt_format=None):
tout.Info("ReadData path='%s'" % self.GetPath())
- parent_data = self.section.ReadData(True)
+ parent_data = self.section.ReadData(True, alt_format)
offset = self.offset - self.section._skip_at_start
data = parent_data[offset:offset + self.size]
tout.Info(
@@ -660,9 +766,9 @@ class Entry_section(Entry):
self.size, len(data)))
return data
- def ReadChildData(self, child, decomp=True):
- tout.Debug("ReadChildData for child '%s'" % child.GetPath())
- parent_data = self.ReadData(True)
+ def ReadChildData(self, child, decomp=True, alt_format=None):
+ tout.Debug(f"ReadChildData for child '{child.GetPath()}'")
+ parent_data = self.ReadData(True, alt_format)
offset = child.offset - self._skip_at_start
tout.Debug("Extract for child '%s': offset %#x, skip_at_start %#x, result %#x" %
(child.GetPath(), child.offset, self._skip_at_start, offset))
@@ -674,6 +780,10 @@ class Entry_section(Entry):
tout.Info("%s: Decompressing data size %#x with algo '%s' to data size %#x" %
(child.GetPath(), len(indata), child.compress,
len(data)))
+ if alt_format:
+ new_data = child.GetAltFormat(data, alt_format)
+ if new_data is not None:
+ data = new_data
return data
def WriteChildData(self, child):
@@ -738,8 +848,14 @@ class Entry_section(Entry):
nothing.
Args:
- missing: List of missing properties / entry args, each a string
+ entry (Entry): Entry to raise the error on
+ missing (list of str): List of missing properties / entry args, each
+ a string
"""
if not self._ignore_missing:
- entry.Raise('Missing required properties/entry args: %s' %
- (', '.join(missing)))
+ missing = ', '.join(missing)
+ entry.Raise(f'Missing required properties/entry args: {missing}')
+
+ def CheckAltFormats(self, alt_formats):
+ for entry in self._entries.values():
+ entry.CheckAltFormats(alt_formats)