diff options
-rw-r--r-- | doc/develop/spl.rst | 2 | ||||
-rw-r--r-- | tools/binman/entries.rst | 29 | ||||
-rw-r--r-- | tools/binman/etype/fit.py | 69 | ||||
-rw-r--r-- | tools/binman/ftest.py | 35 | ||||
-rwxr-xr-x | tools/binman/main.py | 2 | ||||
-rw-r--r-- | tools/binman/test/335_fit_fdt_phase.dts | 61 |
6 files changed, 193 insertions, 5 deletions
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 { + }; + }; +}; |