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:
parent
2f80c5ef13
commit
67a0501708
8 changed files with 92 additions and 11 deletions
|
@ -690,6 +690,15 @@ no-expanded:
|
||||||
`no-expanded` property disables this just for a single entry. Put the
|
`no-expanded` property disables this just for a single entry. Put the
|
||||||
`no-expanded` boolean property in the node to select this behaviour.
|
`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
|
The attributes supported for images and sections are described below. Several
|
||||||
are similar to those for entries.
|
are similar to those for entries.
|
||||||
|
|
||||||
|
|
|
@ -594,12 +594,14 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True,
|
||||||
image.BuildImage()
|
image.BuildImage()
|
||||||
if write_map:
|
if write_map:
|
||||||
image.WriteMap()
|
image.WriteMap()
|
||||||
|
|
||||||
missing_list = []
|
missing_list = []
|
||||||
image.CheckMissing(missing_list)
|
image.CheckMissing(missing_list)
|
||||||
if missing_list:
|
if missing_list:
|
||||||
tout.warning("Image '%s' is missing external blobs and is non-functional: %s" %
|
tout.warning("Image '%s' is missing external blobs and is non-functional: %s" %
|
||||||
(image.name, ' '.join([e.name for e in missing_list])))
|
(image.name, ' '.join([e.name for e in missing_list])))
|
||||||
_ShowHelpForMissingBlobs(missing_list)
|
_ShowHelpForMissingBlobs(missing_list)
|
||||||
|
|
||||||
faked_list = []
|
faked_list = []
|
||||||
image.CheckFakedBlobs(faked_list)
|
image.CheckFakedBlobs(faked_list)
|
||||||
if 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 '%s' has faked external blobs and is non-functional: %s" %
|
||||||
(image.name, ' '.join([os.path.basename(e.GetDefaultFilename())
|
(image.name, ' '.join([os.path.basename(e.GetDefaultFilename())
|
||||||
for e in faked_list])))
|
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 = []
|
missing_bintool_list = []
|
||||||
image.check_missing_bintools(missing_bintool_list)
|
image.check_missing_bintools(missing_bintool_list)
|
||||||
if missing_bintool_list:
|
if missing_bintool_list:
|
||||||
|
|
|
@ -73,7 +73,9 @@ class Entry(object):
|
||||||
compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
|
compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
|
||||||
orig_offset: Original offset value read from node
|
orig_offset: Original offset value read from node
|
||||||
orig_size: Original size 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
|
allow_missing: Allow children of this entry to be missing (used by
|
||||||
subclasses such as Entry_section)
|
subclasses such as Entry_section)
|
||||||
allow_fake: Allow creating a dummy fake file if the blob file is not
|
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.
|
the entry itself, allowing it to vanish in certain circumstances.
|
||||||
An absent entry is removed during processing so that it does not
|
An absent entry is removed during processing so that it does not
|
||||||
appear in the map
|
appear in the map
|
||||||
|
optional (bool): True if this entry contains an optional external blob
|
||||||
"""
|
"""
|
||||||
fake_dir = None
|
fake_dir = None
|
||||||
|
|
||||||
|
@ -138,6 +141,7 @@ class Entry(object):
|
||||||
self.elf_fname = None
|
self.elf_fname = None
|
||||||
self.auto_write_symbols = auto_write_symbols
|
self.auto_write_symbols = auto_write_symbols
|
||||||
self.absent = False
|
self.absent = False
|
||||||
|
self.optional = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def FindEntryClass(etype, expanded):
|
def FindEntryClass(etype, expanded):
|
||||||
|
@ -289,6 +293,7 @@ class Entry(object):
|
||||||
self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
|
self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
|
||||||
self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
|
self.extend_size = fdt_util.GetBool(self._node, 'extend-size')
|
||||||
self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
|
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
|
# This is only supported by blobs and sections at present
|
||||||
self.compress = fdt_util.GetString(self._node, 'compress', 'none')
|
self.compress = fdt_util.GetString(self._node, 'compress', 'none')
|
||||||
|
@ -1039,14 +1044,15 @@ features to produce new behaviours.
|
||||||
self.allow_fake = allow_fake
|
self.allow_fake = allow_fake
|
||||||
|
|
||||||
def CheckMissing(self, missing_list):
|
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:
|
Args:
|
||||||
missing_list: List of Entry objects to be added to
|
missing_list: List of Entry objects to be added to
|
||||||
"""
|
"""
|
||||||
if self.missing:
|
if self.missing and not self.optional:
|
||||||
missing_list.append(self)
|
missing_list.append(self)
|
||||||
|
|
||||||
def check_fake_fname(self, fname, size=0):
|
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
|
# This is meaningless for anything other than blobs
|
||||||
pass
|
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):
|
def GetAllowMissing(self):
|
||||||
"""Get whether a section allows missing external blobs
|
"""Get whether a section allows missing external blobs
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ class Entry_blob(Entry):
|
||||||
def ObtainContents(self, fake_size=0):
|
def ObtainContents(self, fake_size=0):
|
||||||
self._filename = self.GetDefaultFilename()
|
self._filename = self.GetDefaultFilename()
|
||||||
self._pathname = tools.get_input_filename(self._filename,
|
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
|
# Allow the file to be missing
|
||||||
if not self._pathname:
|
if not self._pathname:
|
||||||
self._pathname, faked = self.check_fake_fname(self._filename,
|
self._pathname, faked = self.check_fake_fname(self._filename,
|
||||||
|
|
|
@ -392,8 +392,8 @@ class Entry_fit(Entry_section):
|
||||||
|
|
||||||
_add_entries(self._node, 0, self._node)
|
_add_entries(self._node, 0, self._node)
|
||||||
|
|
||||||
# Keep a copy of all entries, including generator entries, since these
|
# Keep a copy of all entries, including generator entries, since those
|
||||||
# removed from self._entries later.
|
# are removed from self._entries later.
|
||||||
self._priv_entries = dict(self._entries)
|
self._priv_entries = dict(self._entries)
|
||||||
|
|
||||||
def BuildSectionData(self, required):
|
def BuildSectionData(self, required):
|
||||||
|
@ -602,14 +602,15 @@ class Entry_fit(Entry_section):
|
||||||
# Entry_section.ObtainContents() either returns True or
|
# Entry_section.ObtainContents() either returns True or
|
||||||
# raises an exception.
|
# raises an exception.
|
||||||
data = None
|
data = None
|
||||||
missing_list = []
|
missing_opt_list = []
|
||||||
entry.ObtainContents()
|
entry.ObtainContents()
|
||||||
entry.Pack(0)
|
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
|
# If any pieces are missing, skip this. The missing entries will
|
||||||
# show an error
|
# show an error
|
||||||
if not missing_list:
|
if not missing_opt_list:
|
||||||
segs = entry.read_elf_segments()
|
segs = entry.read_elf_segments()
|
||||||
if segs:
|
if segs:
|
||||||
segments, entry_addr = segs
|
segments, entry_addr = segs
|
||||||
|
|
|
@ -863,7 +863,8 @@ class Entry_section(Entry):
|
||||||
def CheckMissing(self, missing_list):
|
def CheckMissing(self, missing_list):
|
||||||
"""Check if any entries in this section have missing external blobs
|
"""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:
|
Args:
|
||||||
missing_list: List of Entry objects to be added to
|
missing_list: List of Entry objects to be added to
|
||||||
|
@ -882,6 +883,17 @@ class Entry_section(Entry):
|
||||||
for entry in self._entries.values():
|
for entry in self._entries.values():
|
||||||
entry.CheckFakedBlobs(faked_blobs_list)
|
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):
|
def check_missing_bintools(self, missing_list):
|
||||||
"""Check if any entries in this section have missing bintools
|
"""Check if any entries in this section have missing bintools
|
||||||
|
|
||||||
|
|
|
@ -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)",
|
"Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
|
||||||
str(exc.exception))
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
21
tools/binman/test/266_blob_ext_opt.dts
Normal file
21
tools/binman/test/266_blob_ext_opt.dts
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in a new issue