From 7081a94ea46df42dcef77c902d16c38685739719 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:45 +0100 Subject: 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 --- tools/binman/test/328_alternates_fdt.dts | 28 +++++++++++++++++++++++++++ tools/binman/test/329_alternates_fdtgrep.dts | 29 ++++++++++++++++++++++++++++ tools/binman/test/330_alternates_vpl.dts | 29 ++++++++++++++++++++++++++++ tools/binman/test/331_alternates_spl.dts | 29 ++++++++++++++++++++++++++++ tools/binman/test/332_alternates_inval.dts | 29 ++++++++++++++++++++++++++++ tools/binman/test/alt_dts/model1.dts | 24 +++++++++++++++++++++++ tools/binman/test/alt_dts/model2.dts | 24 +++++++++++++++++++++++ 7 files changed, 192 insertions(+) create mode 100644 tools/binman/test/328_alternates_fdt.dts create mode 100644 tools/binman/test/329_alternates_fdtgrep.dts create mode 100644 tools/binman/test/330_alternates_vpl.dts create mode 100644 tools/binman/test/331_alternates_spl.dts create mode 100644 tools/binman/test/332_alternates_inval.dts create mode 100644 tools/binman/test/alt_dts/model1.dts create mode 100644 tools/binman/test/alt_dts/model2.dts (limited to 'tools/binman/test') diff --git a/tools/binman/test/328_alternates_fdt.dts b/tools/binman/test/328_alternates_fdt.dts new file mode 100644 index 00000000000..c913c8e4745 --- /dev/null +++ b/tools/binman/test/328_alternates_fdt.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "model-not-set"; + + binman { + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + + section { + u-boot-tpl { + }; + }; + }; + + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/binman/test/329_alternates_fdtgrep.dts b/tools/binman/test/329_alternates_fdtgrep.dts new file mode 100644 index 00000000000..41695281456 --- /dev/null +++ b/tools/binman/test/329_alternates_fdtgrep.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "model-not-set"; + + binman { + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + fdt-phase = "tpl"; + + section { + u-boot-tpl { + }; + }; + }; + + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/binman/test/330_alternates_vpl.dts b/tools/binman/test/330_alternates_vpl.dts new file mode 100644 index 00000000000..5b57069e2ab --- /dev/null +++ b/tools/binman/test/330_alternates_vpl.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "model-not-set"; + + binman { + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + fdt-phase = "vpl"; + + section { + u-boot-vpl { + }; + }; + }; + + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/binman/test/331_alternates_spl.dts b/tools/binman/test/331_alternates_spl.dts new file mode 100644 index 00000000000..882fefce34a --- /dev/null +++ b/tools/binman/test/331_alternates_spl.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "model-not-set"; + + binman { + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + fdt-phase = "spl"; + + section { + u-boot-spl { + }; + }; + }; + + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/binman/test/332_alternates_inval.dts b/tools/binman/test/332_alternates_inval.dts new file mode 100644 index 00000000000..8c145dd2449 --- /dev/null +++ b/tools/binman/test/332_alternates_inval.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "model-not-set"; + + binman { + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + fdt-phase = "bad-phase"; + + section { + u-boot-spl { + }; + }; + }; + + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/binman/test/alt_dts/model1.dts b/tools/binman/test/alt_dts/model1.dts new file mode 100644 index 00000000000..01e95e8fabe --- /dev/null +++ b/tools/binman/test/alt_dts/model1.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + model = "Model One"; + compatible = "u-boot,model-one"; + + /* this node remains due to bootph-pre-sram tag */ + node { + some-prop; + prop-to-remove; + another-prop-to-get-rid-of; + not-a-prop-to-remove; + bootph-pre-sram; + + /* this node get removed by fdtgrep */ + other-node { + another-prop; + }; + }; +}; diff --git a/tools/binman/test/alt_dts/model2.dts b/tools/binman/test/alt_dts/model2.dts new file mode 100644 index 00000000000..7829c519772 --- /dev/null +++ b/tools/binman/test/alt_dts/model2.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + model = "Model Two"; + compatible = "u-boot,model-two"; + + /* this node remains due to bootph-pre-sram tag */ + node { + some-prop; + prop-to-remove; + another-prop-to-get-rid-of; + not-a-prop-to-remove; + bootph-pre-sram; + + /* this node get removed by fdtgrep */ + other-node { + another-prop; + }; + }; +}; -- cgit v1.3.1 From 9db7a3a4325d1b67287cdc2db2d80c5fc1e7cb76 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:46 +0100 Subject: binman: fit: Allow providing FDT filenames in a directory In some cases the list of available FDT files is not available in an entryarg. Provide an option to point to a directory containing them instead. Signed-off-by: Simon Glass --- tools/binman/entries.rst | 7 +++++ tools/binman/etype/fit.py | 19 +++++++++++- tools/binman/ftest.py | 17 +++++++--- tools/binman/test/333_fit_fdt_dir.dts | 58 +++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 tools/binman/test/333_fit_fdt_dir.dts (limited to 'tools/binman/test') diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index 8bfec8b434e..dcacd298c6d 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -857,6 +857,13 @@ The top-level 'fit' node supports the following special properties: fit,fdt-list-val = "dtb1", "dtb2"; + fit,fdt-list-dir + As an alternative to fit,fdt-list the list of device tree files + can be provided as a directory. Each .dtb file in the directory is + processed, , e.g.:: + + fit,fdt-list-dir = "arch/arm/dts + Substitutions ~~~~~~~~~~~~~ diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 2c14b15b03c..8a25c784ef6 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -5,7 +5,9 @@ """Entry-type module for producing a FIT""" +import glob import libfdt +import os from binman.entry import Entry, EntryArg from binman.etype.section import Entry_section @@ -87,6 +89,13 @@ class Entry_fit(Entry_section): fit,fdt-list-val = "dtb1", "dtb2"; + fit,fdt-list-dir + As an alternative to fit,fdt-list the list of device tree files + can be provided as a directory. Each .dtb file in the directory is + processed, , e.g.:: + + fit,fdt-list-dir = "arch/arm/dts + Substitutions ~~~~~~~~~~~~~ @@ -352,6 +361,7 @@ class Entry_fit(Entry_section): self._fit = None self._fit_props = {} self._fdts = None + self._fdt_dir = None self.mkimage = None self._priv_entries = {} self._loadables = [] @@ -368,7 +378,14 @@ class Entry_fit(Entry_section): if fdts is not None: self._fdts = fdts.split() else: - self._fdts = fdt_util.GetStringList(self._node, 'fit,fdt-list-val') + self._fdt_dir = fdt_util.GetString(self._node, 'fit,fdt-list-dir') + if self._fdt_dir: + indir = tools.get_input_filename(self._fdt_dir) + fdts = glob.glob('*.dtb', root_dir=indir) + self._fdts = [os.path.splitext(f)[0] for f in sorted(fdts)] + else: + self._fdts = fdt_util.GetStringList(self._node, + 'fit,fdt-list-val') self._fit_default_dt = self.GetEntryArgsOrProps([EntryArg('default-dt', str)])[0] diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 684e960b582..6e1e1e9b50b 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -4181,8 +4181,8 @@ class TestFunctional(unittest.TestCase): data = self._DoReadFile('172_scp.dts') self.assertEqual(SCP_DATA, data[:len(SCP_DATA)]) - def testFitFdt(self): - """Test an image with an FIT with multiple FDT images""" + def CheckFitFdt(self, dts='170_fit_fdt.dts', use_fdt_list=True): + """Check an image with an FIT with multiple FDT images""" def _CheckFdt(seq, expected_data): """Check the FDT nodes @@ -4221,11 +4221,12 @@ class TestFunctional(unittest.TestCase): self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value) entry_args = { - 'of-list': 'test-fdt1 test-fdt2', 'default-dt': 'test-fdt2', } + if use_fdt_list: + entry_args['of-list'] = 'test-fdt1 test-fdt2' data = self._DoReadFileDtb( - '170_fit_fdt.dts', + dts, entry_args=entry_args, extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) @@ -4244,6 +4245,10 @@ class TestFunctional(unittest.TestCase): _CheckConfig(1, TEST_FDT1_DATA) _CheckConfig(2, TEST_FDT2_DATA) + def testFitFdt(self): + """Test an image with an FIT with multiple FDT images""" + self.CheckFitFdt() + def testFitFdtMissingList(self): """Test handling of a missing 'of-list' entry arg""" with self.assertRaises(ValueError) as e: @@ -7605,6 +7610,10 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self.assertIn("Invalid U-Boot phase 'bad-phase': Use tpl/vpl/spl", str(e.exception)) + def testFitFdtListDir(self): + """Test an image with an FIT with FDT images using fit,fdt-list-dir""" + self.CheckFitFdt('333_fit_fdt_dir.dts', False) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/333_fit_fdt_dir.dts b/tools/binman/test/333_fit_fdt_dir.dts new file mode 100644 index 00000000000..aa778451a4b --- /dev/null +++ b/tools/binman/test/333_fit_fdt_dir.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + description = "test-desc"; + #address-cells = <1>; + fit,fdt-list-dir = "fdts"; + + images { + kernel { + description = "Vanilla Linux kernel"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot { + }; + }; + @fdt-SEQ { + description = "fdt-NAME.dtb"; + type = "flat_dt"; + compression = "none"; + hash { + algo = "sha256"; + }; + }; + }; + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "conf-NAME.dtb"; + firmware = "uboot"; + loadables = "atf"; + fdt = "fdt-SEQ"; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; -- cgit v1.3.1 From 57902e6941a0e914313afe50104fd0660ab47839 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:47 +0100 Subject: binman: fit: Write the compatible string to configuration FIT allows the FDT's root-node compatible string to be placed in a configuration node to simplify and speed up finding the best match for booting. Add a new property to support this. Signed-off-by: Simon Glass --- tools/binman/entries.rst | 5 +++ tools/binman/etype/fit.py | 12 +++++++ tools/binman/ftest.py | 55 +++++++++++++++++++++++++---- tools/binman/test/334_fit_fdt_compat.dts | 60 ++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 tools/binman/test/334_fit_fdt_compat.dts (limited to 'tools/binman/test') diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index dcacd298c6d..fd8edc64fa2 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -939,6 +939,7 @@ You can create config nodes in a similar way:: firmware = "atf"; loadables = "uboot"; fdt = "fdt-SEQ"; + fit,compatible; // optional }; }; @@ -948,6 +949,10 @@ for each of your two files. Note that if no devicetree files are provided (with '-a of-list' as above) then no nodes will be generated. +The 'fit,compatible' property (if present) is replaced with the compatible +string from the root node of the devicetree, so that things work correctly +with FIT's configuration-matching algortihm. + Generating nodes from an ELF file (split-elf) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 8a25c784ef6..ab827d52066 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -171,6 +171,7 @@ class Entry_fit(Entry_section): firmware = "atf"; loadables = "uboot"; fdt = "fdt-SEQ"; + fit,compatible; }; }; @@ -180,6 +181,10 @@ class Entry_fit(Entry_section): Note that if no devicetree files are provided (with '-a of-list' as above) then no nodes will be generated. + The 'fit,compatible' property is replaced with the compatible string from + the root node of the devicetree, so that things work correctly with FIT's + configuration-matching algortihm. + Generating nodes from an ELF file (split-elf) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -611,6 +616,13 @@ class Entry_fit(Entry_section): fsw.property('loadables', val.encode('utf-8')) elif pname == 'fit,operation': pass + elif pname == 'fit,compatible': + fdt_phase = fdt_util.GetString(node, pname) + data = tools.read_file(fname) + fdt = Fdt.FromData(data) + fdt.Scan() + prop = fdt.GetRoot().props['compatible'] + fsw.property('compatible', prop.bytes) elif pname.startswith('fit,'): self._raise_subnode( node, f"Unknown directive '{pname}'") diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 6e1e1e9b50b..d930e353faf 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -7490,6 +7490,24 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err, "Image '.*' is missing external blobs and is non-functional: .*") + def SetupAlternateDts(self): + """Compile the .dts test files for alternative-fdt + + Returns: + tuple: + str: Test directory created + list of str: '.bin' files which we expect Binman to create + """ + 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')) + + return testdir, dtb_list + def CheckAlternates(self, dts, phase, xpl_data): """Run the test for the alterative-fdt etype @@ -7503,13 +7521,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap 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')) + dtb_list = self.SetupAlternateDts()[1] entry_args = { f'{phase}-dtb': '1', @@ -7614,6 +7626,35 @@ fdt fdtmap Extract the devicetree blob from the fdtmap """Test an image with an FIT with FDT images using fit,fdt-list-dir""" self.CheckFitFdt('333_fit_fdt_dir.dts', False) + def testFitFdtCompat(self): + """Test an image with an FIT with compatible in the config nodes""" + entry_args = { + 'of-list': 'model1 model2', + 'default-dt': 'model2', + } + testdir, dtb_list = self.SetupAlternateDts() + data = self._DoReadFileDtb( + '334_fit_fdt_compat.dts', use_real_dtb=True, update_dtb=True, + entry_args=entry_args, extra_indirs=[testdir])[0] + + fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] + + fit = fdt.Fdt.FromData(fit_data) + fit.Scan() + + cnode = fit.GetNode('/configurations') + self.assertIn('default', cnode.props) + self.assertEqual('config-2', cnode.props['default'].value) + + for seq in range(1, 2): + name = f'config-{seq}' + fnode = fit.GetNode('/configurations/%s' % name) + self.assertIsNotNone(fnode) + self.assertIn('compatible', fnode.props.keys()) + expected = 'one' if seq == 1 else 'two' + self.assertEqual(f'u-boot,model-{expected}', + fnode.props['compatible'].value) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/334_fit_fdt_compat.dts b/tools/binman/test/334_fit_fdt_compat.dts new file mode 100644 index 00000000000..3bf45c710db --- /dev/null +++ b/tools/binman/test/334_fit_fdt_compat.dts @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + description = "test-desc"; + #address-cells = <1>; + fit,fdt-list = "of-list"; + + images { + kernel { + description = "Vanilla Linux kernel"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot { + }; + }; + @fdt-SEQ { + description = "fdt-NAME.dtb"; + type = "flat_dt"; + compression = "none"; + hash { + algo = "sha256"; + }; + }; + }; + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "conf-NAME.dtb"; + firmware = "uboot"; + loadables = "atf"; + fdt = "fdt-SEQ"; + fit,firmware = "vpl"; + fit,compatible; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; -- cgit v1.3.1 From 34d4a5df1be3708971d80d368446595af377a03f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:48 +0100 Subject: binman: fit: Allow running fdtgrep on devicetree blobs When using FIT to load firmware builds for multiple models, the FIT must include a common binary along with a number of devicetree blobs, one for each model. This is the same mechanism as is used for loading an OS. However, SPL builds do not normally use the full devicetree, but instead a cut-down version which various nodes and properties removed. Add a new fit,fdt-phase property to allow binman to produce these devicetree blobs. Signed-off-by: Simon Glass --- doc/develop/spl.rst | 2 + tools/binman/entries.rst | 29 ++++++++++++++ tools/binman/etype/fit.py | 69 ++++++++++++++++++++++++++++++--- tools/binman/ftest.py | 35 +++++++++++++++++ tools/binman/main.py | 2 + tools/binman/test/335_fit_fdt_phase.dts | 61 +++++++++++++++++++++++++++++ 6 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 tools/binman/test/335_fit_fdt_phase.dts (limited to 'tools/binman/test') diff --git a/doc/develop/spl.rst b/doc/develop/spl.rst index 0a3e572310a..4bb48e6b7b3 100644 --- a/doc/develop/spl.rst +++ b/doc/develop/spl.rst @@ -121,6 +121,8 @@ Use `spl_phase()` to find the current U-Boot phase, e.g. `PHASE_SPL`. You can also find the previous and next phase and get the phase name. +.. _fdtgrep_filter: + Device tree ----------- The U-Boot device tree is filtered by the fdtgrep tools during the build diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index fd8edc64fa2..12482703782 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -953,6 +953,35 @@ The 'fit,compatible' property (if present) is replaced with the compatible string from the root node of the devicetree, so that things work correctly with FIT's configuration-matching algortihm. +Dealing with phases +~~~~~~~~~~~~~~~~~~~ + +FIT can be used to load firmware. In this case it may be necessary to run +the devicetree for each model through fdtgrep to remove unwanted properties. +The 'fit,fdt-phase' property can be provided to indicate the phase for which +the devicetree is intended. + +For example this indicates that the FDT should be processed for VPL:: + + images { + @fdt-SEQ { + description = "fdt-NAME"; + type = "flat_dt"; + compression = "none"; + fit,fdt-phase = "vpl"; + }; + }; + +Using this mechanism, it is possible to generate a FIT which can provide VPL +images for multiple models, with TPL selecting the correct model to use. The +same approach can of course be used for SPL images. + +Note that the `of-spl-remove-props` entryarg can be used to indicate +additional properties to remove. It is often used to remove properties like +`clock-names` and `pinctrl-names` which are not needed in SPL builds. + +See :ref:`fdtgrep_filter` for more information. + Generating nodes from an ELF file (split-elf) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index ab827d52066..38358292ece 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -171,7 +171,7 @@ class Entry_fit(Entry_section): firmware = "atf"; loadables = "uboot"; fdt = "fdt-SEQ"; - fit,compatible; + fit,compatible; // optional }; }; @@ -181,9 +181,38 @@ class Entry_fit(Entry_section): Note that if no devicetree files are provided (with '-a of-list' as above) then no nodes will be generated. - The 'fit,compatible' property is replaced with the compatible string from - the root node of the devicetree, so that things work correctly with FIT's - configuration-matching algortihm. + The 'fit,compatible' property (if present) is replaced with the compatible + string from the root node of the devicetree, so that things work correctly + with FIT's configuration-matching algortihm. + + Dealing with phases + ~~~~~~~~~~~~~~~~~~~ + + FIT can be used to load firmware. In this case it may be necessary to run + the devicetree for each model through fdtgrep to remove unwanted properties. + The 'fit,fdt-phase' property can be provided to indicate the phase for which + the devicetree is intended. + + For example this indicates that the FDT should be processed for VPL:: + + images { + @fdt-SEQ { + description = "fdt-NAME"; + type = "flat_dt"; + compression = "none"; + fit,fdt-phase = "vpl"; + }; + }; + + Using this mechanism, it is possible to generate a FIT which can provide VPL + images for multiple models, with TPL selecting the correct model to use. The + same approach can of course be used for SPL images. + + Note that the `of-spl-remove-props` entryarg can be used to indicate + additional properties to remove. It is often used to remove properties like + `clock-names` and `pinctrl-names` which are not needed in SPL builds. + + See :ref:`fdtgrep_filter` for more information. Generating nodes from an ELF file (split-elf) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -368,8 +397,14 @@ class Entry_fit(Entry_section): self._fdts = None self._fdt_dir = None self.mkimage = None + self.fdtgrep = None self._priv_entries = {} self._loadables = [] + self._remove_props = [] + props, = self.GetEntryArgsOrProps( + [EntryArg('of-spl-remove-props', str)], required=False) + if props: + self._remove_props = props.split() def ReadNode(self): super().ReadNode() @@ -505,6 +540,19 @@ class Entry_fit(Entry_section): rel_path = node.path[len(self._node.path) + 1:] self.Raise(f"subnode '{rel_path}': {msg}") + def _run_fdtgrep(self, infile, phase, outfile): + """Run fdtgrep to create the dtb for a phase + + Args: + infile (str): Input filename containing the full FDT contents (with + all nodes and properties) + phase (str): Phase to generate for ('tpl', 'vpl', 'spl') + outfile (str): Output filename to write the grepped FDT contents to + (with only neceesary nodes and properties) + """ + return self.fdtgrep.create_for_phase(infile, phase, outfile, + self._remove_props) + def _build_input(self): """Finish the FIT by adding the 'data' properties to it @@ -606,6 +654,7 @@ class Entry_fit(Entry_section): for seq, fdt_fname in enumerate(self._fdts): node_name = node.name[1:].replace('SEQ', str(seq + 1)) fname = tools.get_input_filename(fdt_fname + '.dtb') + fdt_phase = None with fsw.add_node(node_name): for pname, prop in node.props.items(): if pname == 'fit,firmware': @@ -623,6 +672,8 @@ class Entry_fit(Entry_section): fdt.Scan() prop = fdt.GetRoot().props['compatible'] fsw.property('compatible', prop.bytes) + elif pname == 'fit,fdt-phase': + fdt_phase = fdt_util.GetString(node, pname) elif pname.startswith('fit,'): self._raise_subnode( node, f"Unknown directive '{pname}'") @@ -635,7 +686,14 @@ class Entry_fit(Entry_section): # Add data for 'images' nodes (but not 'config') if depth == 1 and in_images: - fsw.property('data', tools.read_file(fname)) + if fdt_phase: + phase_fname = tools.get_output_filename( + f'{fdt_fname}-{fdt_phase}.dtb') + self._run_fdtgrep(fname, fdt_phase, phase_fname) + data = tools.read_file(phase_fname) + else: + data = tools.read_file(fname) + fsw.property('data', data) for subnode in node.subnodes: with fsw.add_node(subnode.name): @@ -863,6 +921,7 @@ class Entry_fit(Entry_section): def AddBintools(self, btools): super().AddBintools(btools) self.mkimage = self.AddBintool(btools, 'mkimage') + self.fdtgrep = self.AddBintool(btools, 'fdtgrep') def CheckMissing(self, missing_list): # We must use our private entry list for this since generator nodes diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index d930e353faf..93f3d22cf57 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -7655,6 +7655,41 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self.assertEqual(f'u-boot,model-{expected}', fnode.props['compatible'].value) + def testFitFdtPhase(self): + """Test an image with an FIT with fdt-phase in the fdt nodes""" + phase = 'tpl' + entry_args = { + f'{phase}-dtb': '1', + f'{phase}-bss-pad': 'y', + 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of', + 'of-list': 'model1 model2', + 'default-dt': 'model2', + } + testdir, dtb_list = self.SetupAlternateDts() + data = self._DoReadFileDtb( + '335_fit_fdt_phase.dts', use_real_dtb=True, update_dtb=True, + entry_args=entry_args, extra_indirs=[testdir])[0] + fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] + fit = fdt.Fdt.FromData(fit_data) + fit.Scan() + + # Check that each FDT has only the expected properties for the phase + for seq in range(1, 2): + fnode = fit.GetNode(f'/images/fdt-{seq}') + self.assertIsNotNone(fnode) + dtb = fdt.Fdt.FromData(fnode.props['data'].bytes) + dtb.Scan() + + # Make sure that the 'bootph-pre-sram' tag in /node protects it from + # removal + 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')) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/main.py b/tools/binman/main.py index 92d2431aea7..dc817ddcd42 100755 --- a/tools/binman/main.py +++ b/tools/binman/main.py @@ -122,6 +122,8 @@ def RunBinman(args): ret_code = RunTests(args.debug, args.verbosity, args.processes, args.test_preserve_dirs, args.tests, args.toolpath) + if args.debug and not test_util.use_concurrent: + print('Tests can run in parallel: pip install concurrencytest') elif args.cmd == 'bintool-docs': control.write_bintool_docs(bintool.Bintool.get_tool_list()) diff --git a/tools/binman/test/335_fit_fdt_phase.dts b/tools/binman/test/335_fit_fdt_phase.dts new file mode 100644 index 00000000000..f8d0740a394 --- /dev/null +++ b/tools/binman/test/335_fit_fdt_phase.dts @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + description = "test-desc"; + #address-cells = <1>; + fit,fdt-list = "of-list"; + + images { + kernel { + description = "Vanilla Linux kernel"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot { + }; + }; + @fdt-SEQ { + description = "fdt-NAME.dtb"; + type = "flat_dt"; + compression = "none"; + fit,fdt-phase = "tpl"; + hash { + algo = "sha256"; + }; + }; + }; + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "conf-NAME.dtb"; + firmware = "uboot"; + loadables = "atf"; + fdt = "fdt-SEQ"; + fit,firmware = "tpl"; + fit,compatible; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; -- cgit v1.3.1