summaryrefslogtreecommitdiff
path: root/tools/binman/ftest.py
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2024-07-20 11:49:45 +0100
committerSimon Glass <sjg@chromium.org>2024-07-29 08:42:18 -0600
commit7081a94ea46df42dcef77c902d16c38685739719 (patch)
tree2fe448b329d47f66d2c5db6b0020dca0b75ebc29 /tools/binman/ftest.py
parentdaed9b42b42cee1d0ea33a568707fb9eef423f9f (diff)
binman: Add support for alternative FDTs
FIT provides a way to select between different devicetree blobs depending on the model. This works fine for U-Boot proper and allows SPL to select the correct blob for the current board at runtime. The boot sequence (SPL->U-Boot proper) is therefore covered by the existing feature set. The first boot phase (typically TPL) cannot use FIT since SoC boot ROMs don't currently support it. Therefore the TPL image must be specific to each model it boots on. To support booting on mulitple models, binman must therefore produce a separate TPL image for each model, even if the images for the rest of the phases are identical. TPL needs to be packaged as an executable binary along with a reduced devicetree. When multiple models are supported, a reduced devicetree must be provided for each model. U-Boot's build system is designed to build a single devicetree for SPL builds, so does not support this requirement. Add a new 'alternatives' feature to Binman, allowing it to automatically subset a devicetree to produce the reduced devicetree for a particular phase for each supported model. With this it is possible to produce a separate TPL image for each of the models. The correct one can then be loaded onto a board, along with the common FIT image(s). Signed-off-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'tools/binman/ftest.py')
-rw-r--r--tools/binman/ftest.py121
1 files changed, 121 insertions, 0 deletions
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index d091855b8e3..684e960b582 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -7,6 +7,7 @@
# python -m unittest func_test.TestFunctional.testHelp
import collections
+import glob
import gzip
import hashlib
from optparse import OptionParser
@@ -7484,6 +7485,126 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
err,
"Image '.*' is missing external blobs and is non-functional: .*")
+ def CheckAlternates(self, dts, phase, xpl_data):
+ """Run the test for the alterative-fdt etype
+
+ Args:
+ dts (str): Devicetree file to process
+ phase (str): Phase to process ('spl', 'tpl' or 'vpl')
+ xpl_data (bytes): Expected data for the phase's binary
+
+ Returns:
+ dict of .dtb files produced
+ key: str filename
+ value: Fdt object
+ """
+ testdir = TestFunctional._MakeInputDir('dtb')
+ dtb_list = []
+ for fname in glob.glob(f'{self.TestFile("alt_dts")}/*.dts'):
+ tmp_fname = fdt_util.EnsureCompiled(fname, testdir)
+ base = os.path.splitext(os.path.basename(fname))[0]
+ dtb_list.append(base + '.bin')
+ shutil.move(tmp_fname, os.path.join(testdir, base + '.dtb'))
+
+ entry_args = {
+ f'{phase}-dtb': '1',
+ f'{phase}-bss-pad': 'y',
+ 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of',
+ }
+ data = self._DoReadFileDtb(dts, use_real_dtb=True, update_dtb=True,
+ use_expanded=True, entry_args=entry_args)[0]
+ self.assertEqual(xpl_data, data[:len(xpl_data)])
+ rest = data[len(xpl_data):]
+ pad_len = 10
+ self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
+
+ # Check the dtb is using the test file
+ dtb_data = rest[pad_len:]
+ dtb = fdt.Fdt.FromData(dtb_data)
+ dtb.Scan()
+ fdt_size = dtb.GetFdtObj().totalsize()
+ self.assertEqual('model-not-set',
+ fdt_util.GetString(dtb.GetRoot(), 'compatible'))
+
+ pad_len = 10
+
+ # Check the other output files
+ dtbs = {}
+ for fname in dtb_list:
+ pathname = tools.get_output_filename(fname)
+ self.assertTrue(os.path.exists(pathname))
+
+ data = tools.read_file(pathname)
+ self.assertEqual(xpl_data, data[:len(xpl_data)])
+ rest = data[len(xpl_data):]
+
+ self.assertEqual(tools.get_bytes(0, pad_len), rest[:pad_len])
+ rest = rest[pad_len:]
+
+ dtb = fdt.Fdt.FromData(rest)
+ dtb.Scan()
+ dtbs[fname] = dtb
+
+ expected = 'one' if '1' in fname else 'two'
+ self.assertEqual(f'u-boot,model-{expected}',
+ fdt_util.GetString(dtb.GetRoot(), 'compatible'))
+
+ # Make sure the FDT is the same size as the 'main' one
+ rest = rest[fdt_size:]
+
+ self.assertEqual(b'', rest)
+ return dtbs
+
+ def testAlternatesFdt(self):
+ """Test handling of alternates-fdt etype"""
+ self._SetupTplElf()
+ dtbs = self.CheckAlternates('328_alternates_fdt.dts', 'tpl',
+ U_BOOT_TPL_NODTB_DATA)
+ for dtb in dtbs.values():
+ # Check for the node with the tag
+ node = dtb.GetNode('/node')
+ self.assertIsNotNone(node)
+ self.assertEqual(5, len(node.props.keys()))
+
+ # Make sure the other node is still there
+ self.assertIsNotNone(dtb.GetNode('/node/other-node'))
+
+ def testAlternatesFdtgrep(self):
+ """Test handling of alternates-fdt etype using fdtgrep"""
+ self._SetupTplElf()
+ dtbs = self.CheckAlternates('329_alternates_fdtgrep.dts', 'tpl',
+ U_BOOT_TPL_NODTB_DATA)
+ for dtb in dtbs.values():
+ # Check for the node with the tag
+ node = dtb.GetNode('/node')
+ self.assertIsNotNone(node)
+ self.assertEqual({'some-prop', 'not-a-prop-to-remove'},
+ node.props.keys())
+
+ # Make sure the other node is gone
+ self.assertIsNone(dtb.GetNode('/node/other-node'))
+
+ def testAlternatesFdtgrepVpl(self):
+ """Test handling of alternates-fdt etype using fdtgrep with vpl"""
+ self._SetupVplElf()
+ dtbs = self.CheckAlternates('330_alternates_vpl.dts', 'vpl',
+ U_BOOT_VPL_NODTB_DATA)
+
+ def testAlternatesFdtgrepSpl(self):
+ """Test handling of alternates-fdt etype using fdtgrep with spl"""
+ self._SetupSplElf()
+ dtbs = self.CheckAlternates('331_alternates_spl.dts', 'spl',
+ U_BOOT_SPL_NODTB_DATA)
+
+ def testAlternatesFdtgrepInval(self):
+ """Test alternates-fdt etype using fdtgrep with invalid phase"""
+ self._SetupSplElf()
+ with self.assertRaises(ValueError) as e:
+ dtbs = self.CheckAlternates('332_alternates_inval.dts', 'spl',
+ U_BOOT_SPL_NODTB_DATA)
+ self.assertIn("Invalid U-Boot phase 'bad-phase': Use tpl/vpl/spl",
+ str(e.exception))
+
if __name__ == "__main__":
unittest.main()