summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/binman/binman.rst7
-rw-r--r--tools/binman/control.py30
-rw-r--r--tools/binman/entry.py17
-rw-r--r--tools/binman/etype/blob.py9
-rw-r--r--tools/binman/etype/blob_ext_list.py4
-rw-r--r--tools/binman/etype/cbfs.py3
-rw-r--r--tools/binman/etype/mkimage.py2
-rw-r--r--tools/binman/etype/section.py16
-rw-r--r--tools/binman/ftest.py54
-rw-r--r--tools/binman/image.py2
10 files changed, 102 insertions, 42 deletions
diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst
index 84b1331df5c..392e507d449 100644
--- a/tools/binman/binman.rst
+++ b/tools/binman/binman.rst
@@ -1143,6 +1143,13 @@ Optional entries
Some entries need to exist only if certain conditions are met. For example, an
entry may want to appear in the image only if a file has a particular format.
+Also, the ``optional`` property may be used to mark entries as optional::
+
+ tee-os {
+ filename = "tee.bin";
+ optional;
+ };
+
Obviously the entry must exist in the image description for it to be processed
at all, so a way needs to be found to have the entry remove itself.
diff --git a/tools/binman/control.py b/tools/binman/control.py
index e5bd7889806..3e0108d9635 100644
--- a/tools/binman/control.py
+++ b/tools/binman/control.py
@@ -645,14 +645,27 @@ def CheckForProblems(image):
_ShowHelpForMissingBlobs(tout.ERROR, missing_list)
faked_list = []
+ faked_optional_list = []
+ faked_required_list = []
image.CheckFakedBlobs(faked_list)
- if faked_list:
+ for e in faked_list:
+ if e.optional:
+ faked_optional_list.append(e)
+ else:
+ faked_required_list.append(e)
+ if faked_required_list:
tout.warning(
"Image '%s' has faked external blobs and is non-functional: %s\n" %
(image.name, ' '.join([os.path.basename(e.GetDefaultFilename())
- for e in faked_list])))
+ for e in faked_required_list])))
optional_list = []
+ # For optional blobs, we should inform the user when the blob is not present. This will come as
+ # a warning since it may not be immediately apparent that something is missing otherwise.
+ # E.g. user thinks they supplied a blob, but there is no info of the contrary if they made an
+ # error.
+ # Faked optional blobs are not relevant for final images (as they are dropped anyway) so we
+ # will omit the message with default verbosity.
image.CheckOptional(optional_list)
if optional_list:
tout.warning(
@@ -660,6 +673,12 @@ def CheckForProblems(image):
(image.name, ' '.join([e.name for e in optional_list])))
_ShowHelpForMissingBlobs(tout.WARNING, optional_list)
+ if faked_optional_list:
+ tout.info(
+ "Image '%s' has faked optional external blobs but is still functional: %s\n" %
+ (image.name, ' '.join([os.path.basename(e.GetDefaultFilename())
+ for e in faked_optional_list])))
+
missing_bintool_list = []
image.check_missing_bintools(missing_bintool_list)
if missing_bintool_list:
@@ -667,7 +686,7 @@ def CheckForProblems(image):
"Image '%s' has missing bintools and is non-functional: %s\n" %
(image.name, ' '.join([os.path.basename(bintool.name)
for bintool in missing_bintool_list])))
- return any([missing_list, faked_list, missing_bintool_list])
+ return any([missing_list, faked_required_list, missing_bintool_list])
def ProcessImage(image, update_fdt, write_map, get_contents=True,
allow_resize=True, allow_missing=False,
@@ -697,7 +716,6 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True,
image.SetAllowMissing(allow_missing)
image.SetAllowFakeBlob(allow_fake_blobs)
image.GetEntryContents()
- image.drop_absent()
image.GetEntryOffsets()
# We need to pack the entries to figure out where everything
@@ -736,12 +754,12 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True,
image.Raise('Entries changed size after packing (tried %s passes)' %
passes)
+ has_problems = CheckForProblems(image)
+
image.BuildImage()
if write_map:
image.WriteMap()
- has_problems = CheckForProblems(image)
-
image.WriteAlternates()
return has_problems
diff --git a/tools/binman/entry.py b/tools/binman/entry.py
index bdc60e47fca..ce7ef28e94b 100644
--- a/tools/binman/entry.py
+++ b/tools/binman/entry.py
@@ -88,6 +88,7 @@ class Entry(object):
updated with a hash of the entry contents
comp_bintool: Bintools used for compress and decompress data
fake_fname: Fake filename, if one was created, else None
+ faked (bool): True if the entry is absent and faked
required_props (dict of str): Properties which must be present. This can
be added to by subclasses
elf_fname (str): Filename of the ELF file, if this entry holds an ELF
@@ -759,7 +760,7 @@ class Entry(object):
self.image_pos)
# pylint: disable=assignment-from-none
- def GetEntries(self):
+ def GetEntries(self) -> None:
"""Return a list of entries contained by this entry
Returns:
@@ -1120,7 +1121,7 @@ features to produce new behaviours.
if self.missing and not self.optional:
missing_list.append(self)
- def check_fake_fname(self, fname, size=0):
+ def check_fake_fname(self, fname: str, size: int = 0) -> str:
"""If the file is missing and the entry allows fake blobs, fake it
Sets self.faked to True if faked
@@ -1130,9 +1131,7 @@ features to produce new behaviours.
size (int): Size of fake file to create
Returns:
- tuple:
- fname (str): Filename of faked file
- bool: True if the blob was faked, False if not
+ fname (str): Filename of faked file
"""
if self.allow_fake and not pathlib.Path(fname).is_file():
if not self.fake_fname:
@@ -1142,8 +1141,8 @@ features to produce new behaviours.
tout.info(f"Entry '{self._node.path}': Faked blob '{outfname}'")
self.fake_fname = outfname
self.faked = True
- return self.fake_fname, True
- return fname, False
+ return self.fake_fname
+ return fname
def CheckFakedBlobs(self, faked_blobs_list):
"""Check if any entries in this section have faked external blobs
@@ -1352,6 +1351,10 @@ features to produce new behaviours.
os.mkdir(cls.fake_dir)
tout.notice(f"Fake-blob dir is '{cls.fake_dir}'")
+ def drop_absent_optional(self) -> None:
+ """Entries don't have any entries, do nothing"""
+ pass
+
def ensure_props(self):
"""Raise an exception if properties are missing
diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py
index 041e1122953..acd9ae34074 100644
--- a/tools/binman/etype/blob.py
+++ b/tools/binman/etype/blob.py
@@ -42,7 +42,7 @@ class Entry_blob(Entry):
if fdt_util.GetBool(self._node, 'write-symbols'):
self.auto_write_symbols = True
- def ObtainContents(self, fake_size=0):
+ def ObtainContents(self, fake_size: int = 0) -> bool:
self._filename = self.GetDefaultFilename()
self._pathname = tools.get_input_filename(self._filename,
self.external and (self.optional or self.section.GetAllowMissing()))
@@ -50,10 +50,11 @@ class Entry_blob(Entry):
if not self._pathname:
if not fake_size and self.assume_size:
fake_size = self.assume_size
- self._pathname, faked = self.check_fake_fname(self._filename,
- fake_size)
+ self._pathname = self.check_fake_fname(self._filename, fake_size)
self.missing = True
- if not faked:
+ if self.optional:
+ self.mark_absent("missing but optional")
+ if not self.faked:
content_size = 0
if self.assume_size: # Ensure we get test coverage on next line
content_size = self.assume_size
diff --git a/tools/binman/etype/blob_ext_list.py b/tools/binman/etype/blob_ext_list.py
index 1bfcf6733a7..a8b5a24c3a1 100644
--- a/tools/binman/etype/blob_ext_list.py
+++ b/tools/binman/etype/blob_ext_list.py
@@ -33,11 +33,11 @@ class Entry_blob_ext_list(Entry_blob):
self._filenames = fdt_util.GetStringList(self._node, 'filenames')
self._pathnames = []
- def ObtainContents(self):
+ def ObtainContents(self) -> bool:
missing = False
pathnames = []
for fname in self._filenames:
- fname, _ = self.check_fake_fname(fname)
+ fname = self.check_fake_fname(fname)
pathname = tools.get_input_filename(
fname, self.external and self.section.GetAllowMissing())
# Allow the file to be missing
diff --git a/tools/binman/etype/cbfs.py b/tools/binman/etype/cbfs.py
index 124fa1e4ffc..5879f377231 100644
--- a/tools/binman/etype/cbfs.py
+++ b/tools/binman/etype/cbfs.py
@@ -276,7 +276,8 @@ class Entry_cbfs(Entry):
for entry in self._entries.values():
entry.ListEntries(entries, indent + 1)
- def GetEntries(self):
+ def GetEntries(self) -> dict[str, Entry]:
+ """Returns the entries (tree children) of this section"""
return self._entries
def ReadData(self, decomp=True, alt_format=None):
diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py
index 6ae5d0c8a4f..75e59c3d3a3 100644
--- a/tools/binman/etype/mkimage.py
+++ b/tools/binman/etype/mkimage.py
@@ -205,7 +205,7 @@ class Entry_mkimage(Entry_section):
self.record_missing_bintool(self.mkimage)
return data
- def GetEntries(self):
+ def GetEntries(self) -> dict[str, Entry]:
# Make a copy so we don't change the original
entries = OrderedDict(self._entries)
if self._imagename:
diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py
index 1d50bb47753..03c4f7c6ec7 100644
--- a/tools/binman/etype/section.py
+++ b/tools/binman/etype/section.py
@@ -537,7 +537,7 @@ class Entry_section(Entry):
for entry in self._entries.values():
entry.WriteMap(fd, indent + 1)
- def GetEntries(self):
+ def GetEntries(self) -> dict[str, Entry]:
return self._entries
def GetContentsByPhandle(self, phandle, source_entry, required):
@@ -772,9 +772,17 @@ class Entry_section(Entry):
todo)
return True
- def drop_absent(self):
- """Drop entries which are absent"""
- self._entries = {n: e for n, e in self._entries.items() if not e.absent}
+ def drop_absent_optional(self) -> None:
+ """Drop entries which are absent.
+ Call for all nodes in the tree. Leaf nodes will do nothing per
+ definition. Sections however have _entries and should drop all children
+ which are absent.
+ """
+ self._entries = {n: e for n, e in self._entries.items() if not (e.absent and e.optional)}
+ # Drop nodes first before traversing children to avoid superfluous calls
+ # to children of absent nodes.
+ for e in self.GetEntries().values():
+ e.drop_absent_optional()
def _SetEntryOffsetSize(self, name, offset, size):
"""Set the offset and size of an entry
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 4cf7dfc8216..bedfcb53c48 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -84,6 +84,7 @@ FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
b"sorry you're alive\n")
COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
COMPRESS_DATA_BIG = COMPRESS_DATA * 2
+MISSING_DATA = b'missing'
REFCODE_DATA = b'refcode'
FSP_M_DATA = b'fsp_m'
FSP_S_DATA = b'fsp_s'
@@ -250,7 +251,7 @@ class TestFunctional(unittest.TestCase):
# ATF and OP_TEE
TestFunctional._MakeInputFile('bl31.elf',
tools.read_file(cls.ElfTestFile('elf_sections')))
- TestFunctional._MakeInputFile('tee.elf',
+ TestFunctional.tee_elf_path = TestFunctional._MakeInputFile('tee.elf',
tools.read_file(cls.ElfTestFile('elf_sections')))
# Newer OP_TEE file in v1 binary format
@@ -514,9 +515,9 @@ class TestFunctional(unittest.TestCase):
return dtb.GetContents()
def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
- verbosity=None, map=False, update_dtb=False,
- entry_args=None, reset_dtbs=True, extra_indirs=None,
- threads=None):
+ verbosity=None, allow_fake_blobs=True, map=False,
+ update_dtb=False, entry_args=None, reset_dtbs=True,
+ extra_indirs=None, threads=None):
"""Run binman and return the resulting image
This runs binman with a given test file and then reads the resulting
@@ -534,6 +535,7 @@ class TestFunctional(unittest.TestCase):
use_expanded: True to use expanded entries where available, e.g.
'u-boot-expanded' instead of 'u-boot'
verbosity: Verbosity level to use (0-3, None=don't set it)
+ allow_fake_blobs: whether binman should fake missing ext blobs
map: True to output map files for the images
update_dtb: Update the offset and size of each entry in the device
tree before packing it into the image
@@ -571,7 +573,7 @@ class TestFunctional(unittest.TestCase):
retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
entry_args=entry_args, use_real_dtb=use_real_dtb,
use_expanded=use_expanded, verbosity=verbosity,
- extra_indirs=extra_indirs,
+ allow_fake_blobs=allow_fake_blobs, extra_indirs=extra_indirs,
threads=threads)
self.assertEqual(0, retcode)
out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
@@ -5210,13 +5212,15 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
def testExtblobList(self):
"""Test an image with an external blob list"""
- data = self._DoReadFile('215_blob_ext_list.dts')
- self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
+ data = self._DoReadFileDtb('215_blob_ext_list.dts',
+ allow_fake_blobs=False)
+ self.assertEqual(REFCODE_DATA + FSP_M_DATA, data[0])
def testExtblobListMissing(self):
"""Test an image with a missing external blob"""
with self.assertRaises(ValueError) as e:
- self._DoReadFile('216_blob_ext_list_missing.dts')
+ self._DoReadFileDtb('216_blob_ext_list_missing.dts',
+ allow_fake_blobs=False)
self.assertIn("Filename 'missing-file' not found in input path",
str(e.exception))
@@ -5224,7 +5228,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
"""Test an image with an missing external blob that is allowed"""
with terminal.capture() as (stdout, stderr):
self._DoTestFile('216_blob_ext_list_missing.dts',
- allow_missing=True)
+ allow_missing=True, allow_fake_blobs=False)
err = stderr.getvalue()
self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
@@ -5766,10 +5770,10 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
def testFitSplitElfMissing(self):
- """Test an split-elf FIT with a missing ELF file"""
+ """Test an split-elf FIT with a missing ELF file. Don't fake the file."""
if not elf.ELF_TOOLS:
self.skipTest('Python elftools not available')
- out, err = self.checkFitSplitElf(allow_missing=True)
+ out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=False)
self.assertRegex(
err,
"Image '.*' is missing external blobs and is non-functional: .*")
@@ -6458,16 +6462,18 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
def testAbsent(self):
"""Check handling of absent entries"""
data = self._DoReadFile('262_absent.dts')
- self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
+ self.assertEqual(U_BOOT_DATA + b'aa' + U_BOOT_IMG_DATA, data)
- def testPackTeeOsOptional(self):
- """Test that an image with an optional TEE binary can be created"""
+ def testPackTeeOsElf(self):
+ """Test that an image with a TEE elf binary can be created"""
entry_args = {
'tee-os-path': 'tee.elf',
}
+ tee_path = self.tee_elf_path
data = self._DoReadFileDtb('263_tee_os_opt.dts',
entry_args=entry_args)[0]
- self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
+ self.assertEqual(U_BOOT_DATA + tools.read_file(tee_path) +
+ U_BOOT_IMG_DATA, data)
def checkFitTee(self, dts, tee_fname):
"""Check that a tee-os entry works and returns data
@@ -6512,6 +6518,9 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertRegex(
err,
"Image '.*' is missing optional external blobs but is still functional: tee-os")
+ self.assertNotRegex(
+ err,
+ "Image '.*' has faked external blobs and is non-functional: tee-os")
def testFitTeeOsOptionalFitBad(self):
"""Test an image with a FIT with an optional OP-TEE binary"""
@@ -6537,7 +6546,15 @@ 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):
+ def testExtblobMissingOptional(self):
+ """Test an image with an external blob that is optional"""
+ with terminal.capture() as (stdout, stderr):
+ data = self._DoReadFileDtb('266_blob_ext_opt.dts',
+ allow_fake_blobs=False)[0]
+ self.assertEqual(REFCODE_DATA, data)
+ self.assertNotIn(MISSING_DATA, data)
+
+ def testExtblobFakedOptional(self):
"""Test an image with an external blob that is optional"""
with terminal.capture() as (stdout, stderr):
data = self._DoReadFile('266_blob_ext_opt.dts')
@@ -6546,6 +6563,9 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertRegex(
err,
"Image '.*' is missing optional external blobs but is still functional: missing")
+ self.assertNotRegex(
+ err,
+ "Image '.*' has faked external blobs and is non-functional: missing")
def testSectionInner(self):
"""Test an inner section with a size"""
@@ -6726,7 +6746,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
node = dtb.GetNode('/configurations/conf-missing-tee-1')
self.assertEqual('atf-1', node.props['firmware'].value)
- self.assertEqual(['u-boot', 'atf-2'],
+ self.assertEqual(['u-boot', 'tee', 'atf-2'],
fdt_util.GetStringList(node, 'loadables'))
def testTooldir(self):
diff --git a/tools/binman/image.py b/tools/binman/image.py
index 24ce0af7c72..698cfa4148e 100644
--- a/tools/binman/image.py
+++ b/tools/binman/image.py
@@ -183,6 +183,8 @@ class Image(section.Entry_section):
fname = tools.get_output_filename(self._filename)
tout.info("Writing image to '%s'" % fname)
with open(fname, 'wb') as fd:
+ # For final image, don't write absent blobs to file
+ self.drop_absent_optional()
data = self.GetPaddedData()
fd.write(data)
tout.info("Wrote %#x bytes" % len(data))