summaryrefslogtreecommitdiff
path: root/tools/binman/ftest.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/binman/ftest.py')
-rw-r--r--tools/binman/ftest.py306
1 files changed, 289 insertions, 17 deletions
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index d74aa90a620..43b4f850a69 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -34,10 +34,10 @@ from dtoc import fdt_util
from binman.etype import fdtmap
from binman.etype import image_header
from binman.image import Image
-from patman import command
-from patman import test_util
-from patman import tools
-from patman import tout
+from u_boot_pylib import command
+from u_boot_pylib import test_util
+from u_boot_pylib import tools
+from u_boot_pylib import tout
# Contents of test files, corresponding to different entry types
U_BOOT_DATA = b'1234'
@@ -709,6 +709,14 @@ class TestFunctional(unittest.TestCase):
AddNode(dtb.GetRoot(), '')
return tree
+ def _CheckSign(self, fit, key):
+ try:
+ tools.run('fit_check_sign', '-k', key, '-f', fit)
+ except:
+ self.fail('Expected signed FIT container')
+ return False
+ return True
+
def testRun(self):
"""Test a basic run with valid args"""
result = self._RunBinman('-h')
@@ -1704,7 +1712,7 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(b'SECTION0', fentry.name)
self.assertEqual(0, fentry.offset)
self.assertEqual(16, fentry.size)
- self.assertEqual(0, fentry.flags)
+ self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags)
fentry = next(fiter)
self.assertEqual(b'RO_U_BOOT', fentry.name)
@@ -1752,7 +1760,7 @@ class TestFunctional(unittest.TestCase):
def _HandleGbbCommand(self, pipe_list):
"""Fake calls to the futility utility"""
- if pipe_list[0][0] == 'futility':
+ if 'futility' in pipe_list[0][0]:
fname = pipe_list[0][-1]
# Append our GBB data to the file, which will happen every time the
# futility command is called.
@@ -1814,7 +1822,7 @@ class TestFunctional(unittest.TestCase):
self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
of the input data (here, 'input.vblock').
"""
- if pipe_list[0][0] == 'futility':
+ if 'futility' in pipe_list[0][0]:
fname = pipe_list[0][3]
with open(fname, 'wb') as fd:
if self._hash_data:
@@ -4001,9 +4009,17 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(expected, data[image_pos:image_pos+size])
def testFitMissing(self):
+ """Test that binman complains if mkimage is missing"""
+ with self.assertRaises(ValueError) as e:
+ self._DoTestFile('162_fit_external.dts',
+ force_missing_bintools='mkimage')
+ self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'",
+ str(e.exception))
+
+ def testFitMissingOK(self):
"""Test that binman still produces a FIT image if mkimage is missing"""
with test_util.capture_sys_output() as (_, stderr):
- self._DoTestFile('162_fit_external.dts',
+ self._DoTestFile('162_fit_external.dts', allow_missing=True,
force_missing_bintools='mkimage')
err = stderr.getvalue()
self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
@@ -5813,13 +5829,61 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertEqual(expected_fdtmap, fdtmap)
def testReplaceSectionSimple(self):
- """Test replacing a simple section with arbitrary data"""
+ """Test replacing a simple section with same-sized data"""
new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA)
- with self.assertRaises(ValueError) as exc:
- self._RunReplaceCmd('section', new_data,
- dts='241_replace_section_simple.dts')
+ data, expected_fdtmap, image = self._RunReplaceCmd('section',
+ new_data, dts='241_replace_section_simple.dts')
+ self.assertEqual(new_data, data)
+
+ entries = image.GetEntries()
+ self.assertIn('section', entries)
+ entry = entries['section']
+ self.assertEqual(len(new_data), entry.size)
+
+ def testReplaceSectionLarger(self):
+ """Test replacing a simple section with larger data"""
+ new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
+ data, expected_fdtmap, image = self._RunReplaceCmd('section',
+ new_data, dts='241_replace_section_simple.dts')
+ self.assertEqual(new_data, data)
+
+ entries = image.GetEntries()
+ self.assertIn('section', entries)
+ entry = entries['section']
+ self.assertEqual(len(new_data), entry.size)
+ fentry = entries['fdtmap']
+ self.assertEqual(entry.offset + entry.size, fentry.offset)
+
+ def testReplaceSectionSmaller(self):
+ """Test replacing a simple section with smaller data"""
+ new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0'
+ data, expected_fdtmap, image = self._RunReplaceCmd('section',
+ new_data, dts='241_replace_section_simple.dts')
+ self.assertEqual(new_data, data)
+
+ # The new size is the same as the old, just with a pad byte at the end
+ entries = image.GetEntries()
+ self.assertIn('section', entries)
+ entry = entries['section']
+ self.assertEqual(len(new_data), entry.size)
+
+ def testReplaceSectionSmallerAllow(self):
+ """Test failing to replace a simple section with smaller data"""
+ new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1)
+ try:
+ state.SetAllowEntryContraction(True)
+ with self.assertRaises(ValueError) as exc:
+ self._RunReplaceCmd('section', new_data,
+ dts='241_replace_section_simple.dts')
+ finally:
+ state.SetAllowEntryContraction(False)
+
+ # Since we have no information about the position of things within the
+ # section, we cannot adjust the position of /section-u-boot so it ends
+ # up outside the section
self.assertIn(
- "Node '/section': Replacing sections is not implemented yet",
+ "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside "
+ "the section '/section' starting at 0x0 (0) of size 0x27 (39)",
str(exc.exception))
def testMkimageImagename(self):
@@ -6355,10 +6419,11 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
'tee-os-path': 'missing.bin',
}
test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
- data = self._DoReadFileDtb(
- '276_fit_firmware_loadables.dts',
- entry_args=entry_args,
- extra_indirs=[test_subdir])[0]
+ with test_util.capture_sys_output() as (stdout, stderr):
+ data = self._DoReadFileDtb(
+ '276_fit_firmware_loadables.dts',
+ entry_args=entry_args,
+ extra_indirs=[test_subdir])[0]
dtb = fdt.Fdt.FromData(data)
dtb.Scan()
@@ -6388,6 +6453,128 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertEqual(['u-boot', 'atf-2'],
fdt_util.GetStringList(node, 'loadables'))
+ def testTooldir(self):
+ """Test that we can specify the tooldir"""
+ with test_util.capture_sys_output() as (stdout, stderr):
+ self.assertEqual(0, self._DoBinman('--tooldir', 'fred',
+ 'tool', '-l'))
+ self.assertEqual('fred', bintool.Bintool.tooldir)
+
+ # Check that the toolpath is updated correctly
+ self.assertEqual(['fred'], tools.tool_search_paths)
+
+ # Try with a few toolpaths; the tooldir should be at the end
+ with test_util.capture_sys_output() as (stdout, stderr):
+ self.assertEqual(0, self._DoBinman(
+ '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred',
+ 'tool', '-l'))
+ self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths)
+
+ def testReplaceSectionEntry(self):
+ """Test replacing an entry in a section"""
+ expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
+ entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob',
+ expect_data, dts='241_replace_section_simple.dts')
+ self.assertEqual(expect_data, entry_data)
+
+ entries = image.GetEntries()
+ self.assertIn('section', entries)
+ section = entries['section']
+
+ sect_entries = section.GetEntries()
+ self.assertIn('blob', sect_entries)
+ entry = sect_entries['blob']
+ self.assertEqual(len(expect_data), entry.size)
+
+ fname = tools.get_output_filename('image-updated.bin')
+ data = tools.read_file(fname)
+
+ new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
+ self.assertEqual(expect_data, new_blob_data)
+
+ self.assertEqual(U_BOOT_DATA,
+ data[entry.image_pos + len(expect_data):]
+ [:len(U_BOOT_DATA)])
+
+ def testReplaceSectionDeep(self):
+ """Test replacing an entry in two levels of sections"""
+ expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
+ entry_data, expected_fdtmap, image = self._RunReplaceCmd(
+ 'section/section/blob', expect_data,
+ dts='278_replace_section_deep.dts')
+ self.assertEqual(expect_data, entry_data)
+
+ entries = image.GetEntries()
+ self.assertIn('section', entries)
+ section = entries['section']
+
+ subentries = section.GetEntries()
+ self.assertIn('section', subentries)
+ section = subentries['section']
+
+ sect_entries = section.GetEntries()
+ self.assertIn('blob', sect_entries)
+ entry = sect_entries['blob']
+ self.assertEqual(len(expect_data), entry.size)
+
+ fname = tools.get_output_filename('image-updated.bin')
+ data = tools.read_file(fname)
+
+ new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
+ self.assertEqual(expect_data, new_blob_data)
+
+ self.assertEqual(U_BOOT_DATA,
+ data[entry.image_pos + len(expect_data):]
+ [:len(U_BOOT_DATA)])
+
+ def testReplaceFitSibling(self):
+ """Test an image with a FIT inside where we replace its sibling"""
+ fname = TestFunctional._MakeInputFile('once', b'available once')
+ self._DoReadFileRealDtb('277_replace_fit_sibling.dts')
+ os.remove(fname)
+
+ try:
+ tmpdir, updated_fname = self._SetupImageInTmpdir()
+
+ fname = os.path.join(tmpdir, 'update-blob')
+ expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
+ tools.write_file(fname, expected)
+
+ self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname)
+ data = tools.read_file(updated_fname)
+ start = len(U_BOOT_DTB_DATA)
+ self.assertEqual(expected, data[start:start + len(expected)])
+ map_fname = os.path.join(tmpdir, 'image-updated.map')
+ self.assertFalse(os.path.exists(map_fname))
+ finally:
+ shutil.rmtree(tmpdir)
+
+ def testX509Cert(self):
+ """Test creating an X509 certificate"""
+ keyfile = self.TestFile('key.key')
+ entry_args = {
+ 'keyfile': keyfile,
+ }
+ data = self._DoReadFileDtb('279_x509_cert.dts',
+ entry_args=entry_args)[0]
+ cert = data[:-4]
+ self.assertEqual(U_BOOT_DATA, data[-4:])
+
+ # TODO: verify the signature
+
+ def testX509CertMissing(self):
+ """Test that binman still produces an image if openssl is missing"""
+ keyfile = self.TestFile('key.key')
+ entry_args = {
+ 'keyfile': 'keyfile',
+ }
+ with test_util.capture_sys_output() as (_, stderr):
+ self._DoTestFile('279_x509_cert.dts',
+ force_missing_bintools='openssl',
+ entry_args=entry_args)
+ err = stderr.getvalue()
+ self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
+
def testPackRockchipTpl(self):
"""Test that an image with a Rockchip TPL binary can be created"""
data = self._DoReadFile('277_rockchip_tpl.dts')
@@ -6404,6 +6591,91 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self._DoTestFile('278_mkimage_missing_multiple.dts', allow_missing=False)
self.assertIn("not found in input path", str(e.exception))
+ def _PrepareSignEnv(self, dts='280_fit_sign.dts'):
+ """Prepare sign environment
+
+ Create private and public keys, add pubkey into dtb.
+
+ Returns:
+ Tuple:
+ FIT container
+ Image name
+ Private key
+ DTB
+ """
+
+ data = self._DoReadFileRealDtb(dts)
+ updated_fname = tools.get_output_filename('image-updated.bin')
+ tools.write_file(updated_fname, data)
+ dtb = tools.get_output_filename('source.dtb')
+ private_key = tools.get_output_filename('test_key.key')
+ public_key = tools.get_output_filename('test_key.crt')
+ fit = tools.get_output_filename('fit.fit')
+ key_dir = tools.get_output_dir()
+
+ tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096',
+ '-sha256', '-new', '-nodes', '-x509', '-keyout',
+ private_key, '-out', public_key)
+ tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir,
+ '-n', 'test_key', '-r', 'conf', dtb)
+
+ return fit, updated_fname, private_key, dtb
+
+ def testSignSimple(self):
+ """Test that a FIT container can be signed in image"""
+ is_signed = False
+ fit, fname, private_key, dtb = self._PrepareSignEnv()
+
+ # do sign with private key
+ control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
+ ['fit'])
+ is_signed = self._CheckSign(fit, dtb)
+
+ self.assertEqual(is_signed, True)
+
+ def testSignExactFIT(self):
+ """Test that a FIT container can be signed and replaced in image"""
+ is_signed = False
+ fit, fname, private_key, dtb = self._PrepareSignEnv()
+
+ # Make sure we propagate the toolpath, since mkimage may not be on PATH
+ args = []
+ if self.toolpath:
+ for path in self.toolpath:
+ args += ['--toolpath', path]
+
+ # do sign with private key
+ self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a',
+ 'sha256,rsa4096', '-f', fit, 'fit')
+ is_signed = self._CheckSign(fit, dtb)
+
+ self.assertEqual(is_signed, True)
+
+ def testSignNonFit(self):
+ """Test a non-FIT entry cannot be signed"""
+ is_signed = False
+ fit, fname, private_key, _ = self._PrepareSignEnv(
+ '281_sign_non_fit.dts')
+
+ # do sign with private key
+ with self.assertRaises(ValueError) as e:
+ self._DoBinman('sign', '-i', fname, '-k', private_key, '-a',
+ 'sha256,rsa4096', '-f', fit, 'u-boot')
+ self.assertIn(
+ "Node '/u-boot': Updating signatures is not supported with this entry type",
+ str(e.exception))
+
+ def testSignMissingMkimage(self):
+ """Test that FIT signing handles a missing mkimage tool"""
+ fit, fname, private_key, _ = self._PrepareSignEnv()
+
+ # try to sign with a missing mkimage tool
+ bintool.Bintool.set_missing_list(['mkimage'])
+ with self.assertRaises(ValueError) as e:
+ control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
+ ['fit'])
+ self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
+
if __name__ == "__main__":
unittest.main()