binman: Support optional external blobs

Some blobs are actually not necessary for the board to work correctly. Add
a property to allow this to be indicated. Missing optional blobs do not
cause a build failure.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2023-01-07 14:07:15 -07:00
parent 2f80c5ef13
commit 67a0501708
8 changed files with 92 additions and 11 deletions

View file

@ -690,6 +690,15 @@ no-expanded:
`no-expanded` property disables this just for a single entry. Put the
`no-expanded` boolean property in the node to select this behaviour.
optional:
External blobs are normally required to be present for the image to be
built (but see `External blobs`_). This properly allows an entry to be
optional, so that when it is cannot be found, this problem is ignored and
an empty file is used for this blob. This should be used only when the blob
is entirely optional and is not needed for correct operation of the image.
Note that missing, optional blobs do not produce a non-zero exit code from
binman, although it does show a warning about the missing external blob.
The attributes supported for images and sections are described below. Several
are similar to those for entries.

View file

@ -594,12 +594,14 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True,
image.BuildImage()
if write_map:
image.WriteMap()
missing_list = []
image.CheckMissing(missing_list)
if missing_list:
tout.warning("Image '%s' is missing external blobs and is non-functional: %s" %
(image.name, ' '.join([e.name for e in missing_list])))
_ShowHelpForMissingBlobs(missing_list)
faked_list = []
image.CheckFakedBlobs(faked_list)
if faked_list:
@ -607,6 +609,15 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True,
"Image '%s' has faked external blobs and is non-functional: %s" %
(image.name, ' '.join([os.path.basename(e.GetDefaultFilename())
for e in faked_list])))
optional_list = []
image.CheckOptional(optional_list)
if optional_list:
tout.warning(
"Image '%s' is missing external blobs but is still functional: %s" %
(image.name, ' '.join([e.name for e in optional_list])))
_ShowHelpForMissingBlobs(optional_list)
missing_bintool_list = []
image.check_missing_bintools(missing_bintool_list)
if missing_bintool_list:

View file

@ -73,7 +73,9 @@ class Entry(object):
compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
orig_offset: Original offset value read from node
orig_size: Original size value read from node
missing: True if this entry is missing its contents
missing: True if this entry is missing its contents. Note that if it is
optional, this entry will not appear in the list generated by
entry.CheckMissing() since it is considered OK for it to be missing.
allow_missing: Allow children of this entry to be missing (used by
subclasses such as Entry_section)
allow_fake: Allow creating a dummy fake file if the blob file is not
@ -95,6 +97,7 @@ class Entry(object):
the entry itself, allowing it to vanish in certain circumstances.
An absent entry is removed during processing so that it does not
appear in the map
optional (bool): True if this entry contains an optional external blob
"""
fake_dir = None
@ -138,6 +141,7 @@ class Entry(object):
self.elf_fname = None
self.auto_write_symbols = auto_write_symbols
self.absent = False
self.optional = False
@staticmethod
def FindEntryClass(etype, expanded):
@ -289,6 +293,7 @@ class Entry(object):
self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
self.optional = fdt_util.GetBool(self._node, 'optional')
# This is only supported by blobs and sections at present
self.compress = fdt_util.GetString(self._node, 'compress', 'none')
@ -1039,14 +1044,15 @@ features to produce new behaviours.
self.allow_fake = allow_fake
def CheckMissing(self, missing_list):
"""Check if any entries in this section have missing external blobs
"""Check if the entry has missing external blobs
If there are missing blobs, the entries are added to the list
If there are missing (non-optional) blobs, the entries are added to the
list
Args:
missing_list: List of Entry objects to be added to
"""
if self.missing:
if self.missing and not self.optional:
missing_list.append(self)
def check_fake_fname(self, fname, size=0):
@ -1085,6 +1091,17 @@ features to produce new behaviours.
# This is meaningless for anything other than blobs
pass
def CheckOptional(self, optional_list):
"""Check if the entry has missing but optional external blobs
If there are missing (optional) blobs, the entries are added to the list
Args:
optional_list (list): List of Entry objects to be added to
"""
if self.missing and self.optional:
optional_list.append(self)
def GetAllowMissing(self):
"""Get whether a section allows missing external blobs

View file

@ -39,7 +39,7 @@ class Entry_blob(Entry):
def ObtainContents(self, fake_size=0):
self._filename = self.GetDefaultFilename()
self._pathname = tools.get_input_filename(self._filename,
self.external and self.section.GetAllowMissing())
self.external and (self.optional or self.section.GetAllowMissing()))
# Allow the file to be missing
if not self._pathname:
self._pathname, faked = self.check_fake_fname(self._filename,

View file

@ -392,8 +392,8 @@ class Entry_fit(Entry_section):
_add_entries(self._node, 0, self._node)
# Keep a copy of all entries, including generator entries, since these
# removed from self._entries later.
# Keep a copy of all entries, including generator entries, since those
# are removed from self._entries later.
self._priv_entries = dict(self._entries)
def BuildSectionData(self, required):
@ -602,14 +602,15 @@ class Entry_fit(Entry_section):
# Entry_section.ObtainContents() either returns True or
# raises an exception.
data = None
missing_list = []
missing_opt_list = []
entry.ObtainContents()
entry.Pack(0)
entry.CheckMissing(missing_list)
entry.CheckMissing(missing_opt_list)
entry.CheckOptional(missing_opt_list)
# If any pieces are missing, skip this. The missing entries will
# show an error
if not missing_list:
if not missing_opt_list:
segs = entry.read_elf_segments()
if segs:
segments, entry_addr = segs

View file

@ -863,7 +863,8 @@ class Entry_section(Entry):
def CheckMissing(self, missing_list):
"""Check if any entries in this section have missing external blobs
If there are missing blobs, the entries are added to the list
If there are missing (non-optional) blobs, the entries are added to the
list
Args:
missing_list: List of Entry objects to be added to
@ -882,6 +883,17 @@ class Entry_section(Entry):
for entry in self._entries.values():
entry.CheckFakedBlobs(faked_blobs_list)
def CheckOptional(self, optional_list):
"""Check the section for missing but optional external blobs
If there are missing (optional) blobs, the entries are added to the list
Args:
optional_list (list): List of Entry objects to be added to
"""
for entry in self._entries.values():
entry.CheckOptional(optional_list)
def check_missing_bintools(self, missing_list):
"""Check if any entries in this section have missing bintools

View file

@ -6178,6 +6178,16 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
"Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
str(exc.exception))
def testExtblobOptional(self):
"""Test an image with an external blob that is optional"""
with test_util.capture_sys_output() as (stdout, stderr):
data = self._DoReadFile('266_blob_ext_opt.dts')
self.assertEqual(REFCODE_DATA, data)
err = stderr.getvalue()
self.assertRegex(
err,
"Image '.*' is missing external blobs but is still functional: missing")
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
ok {
type = "blob-ext";
filename = "refcode.bin";
};
missing {
type = "blob-ext";
filename = "missing.bin";
optional;
};
};
};