From f584d44c2371b9d0027ac30fe4af5475926caecc Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Sat, 21 Jan 2023 19:02:12 +0000 Subject: [PATCH] binman: Add support for selecting firmware to use with split-elf In some cases it is desired for SPL to start TF-A instead of U-Boot proper. Add support for a new property fit,firmware that picks a valid entry and prepends the remaining valid entries to the loadables list generated by the split-elf generator. Signed-off-by: Jonas Karlman Reviewed-by: Simon Glass Reviewed-by: Simon Glass --- tools/binman/entries.rst | 16 +++- tools/binman/etype/fit.py | 62 ++++++++++-- tools/binman/ftest.py | 44 +++++++++ .../test/276_fit_firmware_loadables.dts | 96 +++++++++++++++++++ 4 files changed, 205 insertions(+), 13 deletions(-) create mode 100644 tools/binman/test/276_fit_firmware_loadables.dts diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index 78f95dae1a..7a04a61399 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -721,6 +721,12 @@ split-elf fit,data Generates a `data = <...>` property with the contents of the segment + fit,firmware + Generates a `firmware = <...>` property. Provides a list of possible + nodes to be used as the `firmware` property value. The first valid + node is picked as the firmware. Any remaining valid nodes is + prepended to the `loadable` property generated by `fit,loadables` + fit,loadables Generates a `loadable = <...>` property with a list of the generated nodes (including all nodes if this operation is used multiple times) @@ -791,7 +797,7 @@ Here is an example showing ATF, TEE and a device tree all combined:: @config-SEQ { description = "conf-NAME.dtb"; fdt = "fdt-SEQ"; - firmware = "u-boot"; + fit,firmware = "atf-1", "u-boot"; fit,loadables; }; }; @@ -846,15 +852,15 @@ is:: configurations { default = "config-1"; config-1 { - loadables = "atf-1", "atf-2", "atf-3", "tee-1", "tee-2"; + loadables = "u-boot", "atf-2", "atf-3", "tee-1", "tee-2"; description = "rk3399-firefly.dtb"; fdt = "fdt-1"; - firmware = "u-boot"; + firmware = "atf-1"; }; }; -U-Boot SPL can then load the firmware (U-Boot proper) and all the loadables -(ATF and TEE), then proceed with the boot. +U-Boot SPL can then load the firmware (ATF) and all the loadables (U-Boot +proper, ATF and TEE), then proceed with the boot. diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index bcb606f3f9..cd2943533c 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -187,6 +187,12 @@ class Entry_fit(Entry_section): fit,data Generates a `data = <...>` property with the contents of the segment + fit,firmware + Generates a `firmware = <...>` property. Provides a list of possible + nodes to be used as the `firmware` property value. The first valid + node is picked as the firmware. Any remaining valid nodes is + prepended to the `loadable` property generated by `fit,loadables` + fit,loadables Generates a `loadable = <...>` property with a list of the generated nodes (including all nodes if this operation is used multiple times) @@ -257,7 +263,7 @@ class Entry_fit(Entry_section): @config-SEQ { description = "conf-NAME.dtb"; fdt = "fdt-SEQ"; - firmware = "u-boot"; + fit,firmware = "atf-1", "u-boot"; fit,loadables; }; }; @@ -312,15 +318,15 @@ class Entry_fit(Entry_section): configurations { default = "config-1"; config-1 { - loadables = "atf-1", "atf-2", "atf-3", "tee-1", "tee-2"; + loadables = "u-boot", "atf-2", "atf-3", "tee-1", "tee-2"; description = "rk3399-firefly.dtb"; fdt = "fdt-1"; - firmware = "u-boot"; + firmware = "atf-1"; }; }; - U-Boot SPL can then load the firmware (U-Boot proper) and all the loadables - (ATF and TEE), then proceed with the boot. + U-Boot SPL can then load the firmware (ATF) and all the loadables (U-Boot + proper, ATF and TEE), then proceed with the boot. """ def __init__(self, section, etype, node): """ @@ -510,6 +516,42 @@ class Entry_fit(Entry_section): return fsw.property(pname, prop.bytes) + def _process_firmware_prop(node): + """Process optional fit,firmware property + + Picks the first valid entry for use as the firmware, remaining valid + entries is prepended to loadables + + Args: + node (Node): Generator node to process + + Returns: + firmware (str): Firmware or None + result (list): List of remaining loadables + """ + val = fdt_util.GetStringList(node, 'fit,firmware') + if val is None: + return None, self._loadables + valid_entries = list(self._loadables) + for name, entry in self.GetEntries().items(): + missing = [] + entry.CheckMissing(missing) + entry.CheckOptional(missing) + if not missing: + valid_entries.append(name) + firmware = None + result = [] + for name in val: + if name in valid_entries: + if not firmware: + firmware = name + elif name not in result: + result.append(name) + for name in self._loadables: + if name != firmware and name not in result: + result.append(name) + return firmware, result + def _gen_fdt_nodes(base_node, node, depth, in_images): """Generate FDT nodes @@ -520,20 +562,24 @@ class Entry_fit(Entry_section): first. Args: - node (None): Generator node to process + node (Node): Generator node to process depth: Current node depth (0 is the base 'fit' node) in_images: True if this is inside the 'images' node, so that 'data' properties should be generated """ if self._fdts: + firmware, fit_loadables = _process_firmware_prop(node) # Generate nodes for each FDT 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') with fsw.add_node(node_name): for pname, prop in node.props.items(): - if pname == 'fit,loadables': - val = '\0'.join(self._loadables) + '\0' + if pname == 'fit,firmware': + if firmware: + fsw.property_string('firmware', firmware) + elif pname == 'fit,loadables': + val = '\0'.join(fit_loadables) + '\0' fsw.property('loadables', val.encode('utf-8')) elif pname == 'fit,operation': pass diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index cd27572571..0c4d34dfe4 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6337,6 +6337,50 @@ fdt fdtmap Extract the devicetree blob from the fdtmap } self.assertEqual(expected, props) + def testFitFirmwareLoadables(self): + """Test an image with an FIT that use fit,firmware""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') + entry_args = { + 'of-list': 'test-fdt1', + 'default-dt': 'test-fdt1', + 'atf-bl31-path': 'bl31.elf', + '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] + + dtb = fdt.Fdt.FromData(data) + dtb.Scan() + + node = dtb.GetNode('/configurations/conf-uboot-1') + self.assertEqual('u-boot', node.props['firmware'].value) + self.assertEqual(['atf-1', 'atf-2'], + fdt_util.GetStringList(node, 'loadables')) + + node = dtb.GetNode('/configurations/conf-atf-1') + self.assertEqual('atf-1', node.props['firmware'].value) + self.assertEqual(['u-boot', 'atf-2'], + fdt_util.GetStringList(node, 'loadables')) + + node = dtb.GetNode('/configurations/conf-missing-uboot-1') + self.assertEqual('u-boot', node.props['firmware'].value) + self.assertEqual(['atf-1', 'atf-2'], + fdt_util.GetStringList(node, 'loadables')) + + node = dtb.GetNode('/configurations/conf-missing-atf-1') + self.assertEqual('atf-1', node.props['firmware'].value) + self.assertEqual(['u-boot', 'atf-2'], + fdt_util.GetStringList(node, 'loadables')) + + node = dtb.GetNode('/configurations/conf-missing-tee-1') + self.assertEqual('atf-1', node.props['firmware'].value) + self.assertEqual(['u-boot', 'atf-2'], + fdt_util.GetStringList(node, 'loadables')) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/276_fit_firmware_loadables.dts b/tools/binman/test/276_fit_firmware_loadables.dts new file mode 100644 index 0000000000..2f79cdc9bb --- /dev/null +++ b/tools/binman/test/276_fit_firmware_loadables.dts @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + fit { + description = "test desc"; + #address-cells = <1>; + fit,fdt-list = "of-list"; + + images { + u-boot { + description = "test u-boot"; + type = "standalone"; + arch = "arm64"; + os = "u-boot"; + compression = "none"; + load = <0x00000000>; + entry = <0x00000000>; + + u-boot-nodtb { + }; + }; + tee { + description = "test tee"; + type = "tee"; + arch = "arm64"; + os = "tee"; + compression = "none"; + load = <0x00200000>; + + tee-os { + optional; + }; + }; + @atf-SEQ { + fit,operation = "split-elf"; + description = "test tf-a"; + type = "firmware"; + arch = "arm64"; + os = "arm-trusted-firmware"; + compression = "none"; + fit,load; + fit,entry; + fit,data; + + atf-bl31 { + }; + }; + @fdt-SEQ { + description = "test fdt"; + type = "flat_dt"; + compression = "none"; + }; + }; + + configurations { + default = "@conf-uboot-DEFAULT-SEQ"; + @conf-uboot-SEQ { + description = "uboot config"; + fdt = "fdt-SEQ"; + fit,firmware = "u-boot"; + fit,loadables; + }; + @conf-atf-SEQ { + description = "atf config"; + fdt = "fdt-SEQ"; + fit,firmware = "atf-1", "u-boot"; + fit,loadables; + }; + @conf-missing-uboot-SEQ { + description = "missing uboot config"; + fdt = "fdt-SEQ"; + fit,firmware = "missing-1", "u-boot"; + fit,loadables; + }; + @conf-missing-atf-SEQ { + description = "missing atf config"; + fdt = "fdt-SEQ"; + fit,firmware = "missing-1", "atf-1", "u-boot"; + fit,loadables; + }; + @conf-missing-tee-SEQ { + description = "missing tee config"; + fdt = "fdt-SEQ"; + fit,firmware = "atf-1", "u-boot", "tee"; + fit,loadables; + }; + }; + }; + }; +}; -- 2.39.5