[PATCH 00/20] binman: Enhance FIT support for firmware

So far Binman's FIT support is designed for booting the OS. But FIT is also used to hold firmware images.
One missing feature is the ability to produce the smaller FDTs used by SPL builds.
This series adds this, running fdtgrep on each devicetree to create the correct one for each model, for a particular boot phase. This allows, for example, a particular SPL image + devicetree combination to be selected during boot.
Simon Glass (20): fixdep: Support VPL binman: Correct an error in the FIT-template example binman: Tidy up bintool docs binman: Tidy up comment for Bintoolfdt_add_pubkey.run() binman: Move problem-checking code into a function binman: elf: Add more debugging to LookupAndWriteSymbols() binman: Write the compressed output to a file binman: Correct comment in blob_dtb GetFdtEtype() binman: Mention expanded entries in u-boot-vpl binman: Fix a comment typo in _DoReadFileDtb() binman: Correct indentation in testSplPubkeyDtb binman: Add a bintool for fdtgrep binman: Remove dependency on pylibfdt for entry-docs binman: Allow entry types to override FDT contents binman: Add support for alternative FDTs binman: fit: Allow providing FDT filenames in a directory binman: fit: Write the compatible string to configuration binman: fit: Allow running fdtgrep on devicetree blobs Makefile: Provide VPL devicetree and padding to binman Makefile: Pass OF_SPL_REMOVE_PROPS to binman
Makefile | 3 + doc/develop/spl.rst | 2 + scripts/basic/fixdep.c | 2 + tools/binman/binman.rst | 4 +- tools/binman/bintools.rst | 69 +++-- tools/binman/btool/fdt_add_pubkey.py | 4 + tools/binman/btool/fdtgrep.py | 137 ++++++++++ tools/binman/control.py | 77 +++--- tools/binman/elf.py | 2 +- tools/binman/entries.rst | 94 ++++++- tools/binman/entry.py | 17 ++ tools/binman/etype/alternates_fdt.py | 132 ++++++++++ tools/binman/etype/blob_dtb.py | 8 +- tools/binman/etype/fit.py | 93 ++++++- tools/binman/etype/u_boot_spl_nodtb.py | 2 - tools/binman/etype/u_boot_tpl_nodtb.py | 2 - tools/binman/etype/u_boot_vpl.py | 3 + tools/binman/etype/u_boot_vpl_nodtb.py | 4 +- tools/binman/ftest.py | 255 +++++++++++++++++-- tools/binman/image.py | 24 ++ tools/binman/main.py | 2 + 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/333_fit_fdt_dir.dts | 58 +++++ tools/binman/test/334_fit_fdt_compat.dts | 60 +++++ tools/binman/test/335_fit_fdt_phase.dts | 61 +++++ tools/binman/test/alt_dts/model1.dts | 24 ++ tools/binman/test/alt_dts/model2.dts | 24 ++ 31 files changed, 1208 insertions(+), 99 deletions(-) create mode 100644 tools/binman/btool/fdtgrep.py create mode 100644 tools/binman/etype/alternates_fdt.py 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/333_fit_fdt_dir.dts create mode 100644 tools/binman/test/334_fit_fdt_compat.dts create mode 100644 tools/binman/test/335_fit_fdt_phase.dts create mode 100644 tools/binman/test/alt_dts/model1.dts create mode 100644 tools/binman/test/alt_dts/model2.dts

Add VPL support in this tool.
Signed-off-by: Simon Glass sjg@chromium.org Fixes: f86ca5ad8f7 ("Introduce Verifying Program Loader (VPL)") ---
scripts/basic/fixdep.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c index 5ced0f6b069..3d40bd7ee25 100644 --- a/scripts/basic/fixdep.c +++ b/scripts/basic/fixdep.c @@ -421,6 +421,8 @@ int main(int argc, char *argv[]) strcpy(tmp_buf, "SPL_"); else if (!strncmp(target, "tpl/", 4)) strcpy(tmp_buf, "TPL_"); + else if (!strncmp(target, "vpl/", 4)) + strcpy(tmp_buf, "VPL_"); /* end U-Boot hack */
xprintf("cmd_%s := %s\n\n", target, cmdline);

Add VPL support in this tool.
Signed-off-by: Simon Glass sjg@chromium.org Fixes: f86ca5ad8f7 ("Introduce Verifying Program Loader (VPL)") ---
scripts/basic/fixdep.c | 2 ++ 1 file changed, 2 insertions(+)
Applied to u-boot-dm, thanks!

The example contains references to an non-existent node. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/binman.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 872e9746c8c..0cafc36bdcb 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -1211,7 +1211,7 @@ Templates provide a simple way to handle this::
spi-image { filename = "image-spi.bin"; - insert-template = <&fit>; + insert-template = <&common_part>;
/* things specific to SPI follow */ footer { @@ -1224,7 +1224,7 @@ Templates provide a simple way to handle this::
mmc-image { filename = "image-mmc.bin"; - insert-template = <&fit>; + insert-template = <&common_part>;
/* things specific to MMC follow */ footer {

The example contains references to an non-existent node. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/binman.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
Applied to u-boot-dm, thanks!

Hi Simon
On 20/07/24 16:19, Simon Glass wrote:
The example contains references to an non-existent node. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org
tools/binman/binman.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 872e9746c8c..0cafc36bdcb 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -1211,7 +1211,7 @@ Templates provide a simple way to handle this::
spi-image { filename = "image-spi.bin";
insert-template = <&fit>;
insert-template = <&common_part>; /* things specific to SPI follow */ footer {
@@ -1224,7 +1224,7 @@ Templates provide a simple way to handle this::
mmc-image { filename = "image-mmc.bin";
insert-template = <&fit>;
insert-template = <&common_part>; /* things specific to MMC follow */ footer {
Reviewed-by: Neha Malcom Francis n-francis@ti.com

The docs have somehow got out of sync. Regenerate the file to fix the problem.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/bintools.rst | 61 ++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 23 deletions(-)
diff --git a/tools/binman/bintools.rst b/tools/binman/bintools.rst index 1336f4d0115..fa44d9c6872 100644 --- a/tools/binman/bintools.rst +++ b/tools/binman/bintools.rst @@ -10,6 +10,15 @@ binaries. It is fairly easy to create new bintools. Just add a new file to the
+Bintool: bootgen: Generate bootable fsbl image for zynq/zynqmp +-------------------------------------------------------------- + +This bintools supports running Xilinx "bootgen" in order +to generate a bootable, authenticated image form an SPL. + + + + Bintool: bzip2: Compression/decompression using the bzip2 algorithm -------------------------------------------------------------------
@@ -37,6 +46,25 @@ Documentation about CBFS is at https://www.coreboot.org/CBFS
+Bintool: cst: Image generation for U-Boot +----------------------------------------- + +This bintool supports running `cst` with some basic parameters as +needed by binman. + + + +Bintool: fdt_add_pubkey: Add public key to control dtb (spl or u-boot proper) +----------------------------------------------------------------------------- + +This bintool supports running `fdt_add_pubkey`. + +Normally mkimage adds signature information to the control dtb. However +binman images are built independent from each other. Thus it is required +to add the public key separately from mkimage. + + + Bintool: fiptool: Image generation for ARM Trusted Firmware -----------------------------------------------------------
@@ -143,11 +171,20 @@ Documentation is available via::
+Bintool: mkeficapsule: Handles the 'mkeficapsule' tool +------------------------------------------------------ + +This bintool is used for generating the EFI capsules. The +capsule generation parameters can either be specified through +commandline, or through a config file. + + + Bintool: mkimage: Image generation for U-Boot ---------------------------------------------
This bintool supports running `mkimage` with some basic parameters as -neeed by binman. +needed by binman.
Normally binman uses the mkimage built by U-Boot. But when run outside the U-Boot build system, binman can use the version installed in your system. @@ -194,25 +231,3 @@ Documentation is available via::
-Bintool: fdt_add_pubkey: Add public key to device tree ------------------------------------------------------- - -This bintool supports running `fdt_add_pubkey` in order to add a public -key coming from a certificate to a device-tree. - -Normally signing is done using `mkimage` in context of `binman sign`. However, -in this process the public key is not added to the stage before u-boot proper. -Using `fdt_add_pubkey` the key can be injected to the SPL independent of -`mkimage` - - - -Bintool: bootgen: Sign ZynqMP FSBL image ----------------------------------------- - -This bintool supports running `bootgen` in order to sign a SPL for ZynqMP -devices. - -The bintool automatically creates an appropriate input image file (.bif) for -bootgen based on the passed arguments. The output is a bootable, -authenticated `boot.bin` file.

The docs have somehow got out of sync. Regenerate the file to fix the problem.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/bintools.rst | 61 ++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 23 deletions(-)
Applied to u-boot-dm, thanks!

On 20/07/24 16:19, Simon Glass wrote:
The docs have somehow got out of sync. Regenerate the file to fix the problem.
Signed-off-by: Simon Glass sjg@chromium.org
tools/binman/bintools.rst | 61 ++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 23 deletions(-)
diff --git a/tools/binman/bintools.rst b/tools/binman/bintools.rst index 1336f4d0115..fa44d9c6872 100644 --- a/tools/binman/bintools.rst +++ b/tools/binman/bintools.rst @@ -10,6 +10,15 @@ binaries. It is fairly easy to create new bintools. Just add a new file to the
+Bintool: bootgen: Generate bootable fsbl image for zynq/zynqmp +--------------------------------------------------------------
+This bintools supports running Xilinx "bootgen" in order +to generate a bootable, authenticated image form an SPL.
Bintool: bzip2: Compression/decompression using the bzip2 algorithm
@@ -37,6 +46,25 @@ Documentation about CBFS is at https://www.coreboot.org/CBFS
+Bintool: cst: Image generation for U-Boot +-----------------------------------------
+This bintool supports running `cst` with some basic parameters as +needed by binman.
+Bintool: fdt_add_pubkey: Add public key to control dtb (spl or u-boot proper) +-----------------------------------------------------------------------------
+This bintool supports running `fdt_add_pubkey`.
+Normally mkimage adds signature information to the control dtb. However +binman images are built independent from each other. Thus it is required +to add the public key separately from mkimage.
Bintool: fiptool: Image generation for ARM Trusted Firmware
@@ -143,11 +171,20 @@ Documentation is available via::
+Bintool: mkeficapsule: Handles the 'mkeficapsule' tool +------------------------------------------------------
+This bintool is used for generating the EFI capsules. The +capsule generation parameters can either be specified through +commandline, or through a config file.
Bintool: mkimage: Image generation for U-Boot
This bintool supports running `mkimage` with some basic parameters as
-neeed by binman. +needed by binman.
Normally binman uses the mkimage built by U-Boot. But when run outside the U-Boot build system, binman can use the version installed in your system. @@ -194,25 +231,3 @@ Documentation is available via::
-Bintool: fdt_add_pubkey: Add public key to device tree
-This bintool supports running `fdt_add_pubkey` in order to add a public -key coming from a certificate to a device-tree.
-Normally signing is done using `mkimage` in context of `binman sign`. However, -in this process the public key is not added to the stage before u-boot proper. -Using `fdt_add_pubkey` the key can be injected to the SPL independent of -`mkimage`
-Bintool: bootgen: Sign ZynqMP FSBL image
-This bintool supports running `bootgen` in order to sign a SPL for ZynqMP -devices.
-The bintool automatically creates an appropriate input image file (.bif) for -bootgen based on the passed arguments. The output is a bootable, -authenticated `boot.bin` file.
Reviewed-by: Neha Malcom Francis n-francis@ti.com

This function returns a value, so add documentation for it.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/btool/fdt_add_pubkey.py | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/tools/binman/btool/fdt_add_pubkey.py b/tools/binman/btool/fdt_add_pubkey.py index a50774200c9..75a9716ad72 100644 --- a/tools/binman/btool/fdt_add_pubkey.py +++ b/tools/binman/btool/fdt_add_pubkey.py @@ -32,6 +32,10 @@ class Bintoolfdt_add_pubkey(bintool.Bintool): verified for the image / configuration to be considered valid. algo (str): Cryptographic algorithm. Optional parameter, default value: sha1,rsa2048 + + Returns: + CommandResult: Resulting output from the bintool, or None if the + tool is not present """ args = [] if algo:

This function returns a value, so add documentation for it.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/btool/fdt_add_pubkey.py | 4 ++++ 1 file changed, 4 insertions(+)
Applied to u-boot-dm, thanks!

This has become quite long, so move it into a separate function.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/control.py | 78 ++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 32 deletions(-)
diff --git a/tools/binman/control.py b/tools/binman/control.py index 2f00279232b..a233c778d5e 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -617,6 +617,50 @@ def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded): dtb_item.Flush() return images
+def CheckForProblems(image): + """Check for problems with image generation + + Shows warning about missing, faked or optional external blobs, as well as + missing bintools. + + Args: + image (Image): Image to process + + Returns: + bool: True if there are any problems which result in a non-functional + image + """ + missing_list = [] + image.CheckMissing(missing_list) + if missing_list: + tout.error("Image '%s' is missing external blobs and is non-functional: %s\n" % + (image.name, ' '.join([e.name for e in missing_list]))) + _ShowHelpForMissingBlobs(tout.ERROR, missing_list) + + faked_list = [] + image.CheckFakedBlobs(faked_list) + if faked_list: + tout.warning( + "Image '%s' has faked external blobs and is non-functional: %s\n" % + (image.name, ' '.join([os.path.basename(e.GetDefaultFilename()) + for e in faked_list]))) + + optional_list = [] + image.CheckOptional(optional_list) + if optional_list: + tout.warning( + "Image '%s' is missing optional external blobs but is still functional: %s\n" % + (image.name, ' '.join([e.name for e in optional_list]))) + _ShowHelpForMissingBlobs(tout.WARNING, optional_list) + + missing_bintool_list = [] + image.check_missing_bintools(missing_bintool_list) + if missing_bintool_list: + tout.warning( + "Image '%s' has missing bintools and is non-functional: %s\n" % + (image.name, ' '.join([os.path.basename(bintool.name) + for bintool in missing_bintool_list]))) + return any([missing_list, faked_list, missing_bintool_list])
def ProcessImage(image, update_fdt, write_map, get_contents=True, allow_resize=True, allow_missing=False, @@ -689,38 +733,8 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True, if write_map: image.WriteMap()
- missing_list = [] - image.CheckMissing(missing_list) - if missing_list: - tout.error("Image '%s' is missing external blobs and is non-functional: %s\n" % - (image.name, ' '.join([e.name for e in missing_list]))) - _ShowHelpForMissingBlobs(tout.ERROR, missing_list) - - faked_list = [] - image.CheckFakedBlobs(faked_list) - if faked_list: - tout.warning( - "Image '%s' has faked external blobs and is non-functional: %s\n" % - (image.name, ' '.join([os.path.basename(e.GetDefaultFilename()) - for e in faked_list]))) - - optional_list = [] - image.CheckOptional(optional_list) - if optional_list: - tout.warning( - "Image '%s' is missing optional external blobs but is still functional: %s\n" % - (image.name, ' '.join([e.name for e in optional_list]))) - _ShowHelpForMissingBlobs(tout.WARNING, optional_list) - - missing_bintool_list = [] - image.check_missing_bintools(missing_bintool_list) - if missing_bintool_list: - tout.warning( - "Image '%s' has missing bintools and is non-functional: %s\n" % - (image.name, ' '.join([os.path.basename(bintool.name) - for bintool in missing_bintool_list]))) - return any([missing_list, faked_list, missing_bintool_list]) - + has_problems = CheckForProblems(image) + return has_problems
def Binman(args): """The main control code for binman

This has become quite long, so move it into a separate function.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/control.py | 78 ++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 32 deletions(-)
Applied to u-boot-dm, thanks!

When symbol-writing does not appear to work, it can sometimes be hard to figure out what is going on. Add some more debugging to help.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/elf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/binman/elf.py b/tools/binman/elf.py index 2ecc95f7eb8..a4694056391 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -275,7 +275,7 @@ def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False, return 0 base = syms.get(base_sym) if not base and not is_elf: - tout.debug('LookupAndWriteSymbols: no base') + tout.debug(f'LookupAndWriteSymbols: no base: elf_fname={elf_fname}, base_sym={base_sym}, is_elf={is_elf}') return 0 base_addr = 0 if is_elf else base.address count = 0

When symbol-writing does not appear to work, it can sometimes be hard to figure out what is going on. Add some more debugging to help.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/elf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Applied to u-boot-dm, thanks!

On 20/07/24 16:19, Simon Glass wrote:
When symbol-writing does not appear to work, it can sometimes be hard to figure out what is going on. Add some more debugging to help.
Signed-off-by: Simon Glass sjg@chromium.org
tools/binman/elf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/binman/elf.py b/tools/binman/elf.py index 2ecc95f7eb8..a4694056391 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -275,7 +275,7 @@ def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False, return 0 base = syms.get(base_sym) if not base and not is_elf:
tout.debug('LookupAndWriteSymbols: no base')
tout.debug(f'LookupAndWriteSymbols: no base: elf_fname={elf_fname}, base_sym={base_sym}, is_elf={is_elf}') return 0 base_addr = 0 if is_elf else base.address count = 0
Reviewed-by: Neha Malcom Francis n-francis@ti.com

When an entry is compressed, write the compressed contents to a file so that it is possible to see what was produced. This aids debugging with new images.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/entry.py | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 219d5dcecab..752c584c9a3 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -1199,6 +1199,9 @@ features to produce new behaviours. self.uncomp_size = len(indata) if self.comp_bintool.is_present(): data = self.comp_bintool.compress(indata) + uniq = self.GetUniqueName() + fname = tools.get_output_filename(f'comp.{uniq}') + tools.write_file(fname, data) else: self.record_missing_bintool(self.comp_bintool) data = tools.get_bytes(0, 1024)

When an entry is compressed, write the compressed contents to a file so that it is possible to see what was produced. This aids debugging with new images.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/entry.py | 3 +++ 1 file changed, 3 insertions(+)
Applied to u-boot-dm, thanks!

On 20/07/24 16:19, Simon Glass wrote:
When an entry is compressed, write the compressed contents to a file so that it is possible to see what was produced. This aids debugging with new images.
Signed-off-by: Simon Glass sjg@chromium.org
tools/binman/entry.py | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 219d5dcecab..752c584c9a3 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -1199,6 +1199,9 @@ features to produce new behaviours. self.uncomp_size = len(indata) if self.comp_bintool.is_present(): data = self.comp_bintool.compress(indata)
uniq = self.GetUniqueName()
fname = tools.get_output_filename(f'comp.{uniq}')
tools.write_file(fname, data) else: self.record_missing_bintool(self.comp_bintool) data = tools.get_bytes(0, 1024)
Reviewed-by: Neha Malcom Francis n-francis@ti.com

The filenames are a bit confused. Fix them.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/etype/blob_dtb.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/tools/binman/etype/blob_dtb.py b/tools/binman/etype/blob_dtb.py index d543de9f759..1471ded2211 100644 --- a/tools/binman/etype/blob_dtb.py +++ b/tools/binman/etype/blob_dtb.py @@ -57,7 +57,9 @@ class Entry_blob_dtb(Entry_blob): def GetFdtEtype(self): """Get the entry type of this device tree
- This can be 'u-boot-dtb', 'u-boot-spl-dtb' or 'u-boot-tpl-dtb' + This can be 'u-boot-dtb', 'u-boot-spl-dtb', 'u-boot-tpl-dtb' or + 'u-boot-vpl-dtb' + Returns: Entry type if any, e.g. 'u-boot-dtb' """

The filenames are a bit confused. Fix them.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/etype/blob_dtb.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
Applied to u-boot-dm, thanks!

Add a comment about this entry type being expanded, to match the comment for SPL and TPL. Drop an unwanted line in the SPL and TPL docs while here.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/entries.rst | 11 +++++------ tools/binman/etype/u_boot_spl_nodtb.py | 2 -- tools/binman/etype/u_boot_tpl_nodtb.py | 2 -- tools/binman/etype/u_boot_vpl.py | 3 +++ tools/binman/etype/u_boot_vpl_nodtb.py | 4 ++-- 5 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index bdda1ef2855..38dfe2c7db9 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -2290,8 +2290,6 @@ u-boot-spl-dtb
SPL can access binman symbols at runtime. See :ref:`binman_fdt`.
-in the binman README for more information. - The ELF file 'spl/u-boot-spl' must also be available for this to work, since binman uses that to look up symbols to write into the SPL binary.
@@ -2480,8 +2478,6 @@ u-boot-tpl-dtb
TPL can access binman symbols at runtime. See :ref:`binman_fdt`.
-in the binman README for more information. - The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since binman uses that to look up symbols to write into the TPL binary.
@@ -2571,6 +2567,9 @@ in the binman README for more information. The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since binman uses that to look up symbols to write into the VPL binary.
+Note that this entry is automatically replaced with u-boot-vpl-expanded +unless --no-expanded is used or the node has a 'no-expanded' property. +
.. _etype_u_boot_vpl_bss_pad: @@ -2659,8 +2658,8 @@ Properties / Entry arguments:
This is the U-Boot VPL binary, It does not include a device tree blob at the end of it so may not be able to work without it, assuming VPL needs -a device tree to operate on your platform. You can add a u_boot_vpl_dtb -entry after this one, or use a u_boot_vpl entry instead, which normally +a device tree to operate on your platform. You can add a u-boot-vpl-dtb +entry after this one, or use a u-boot-vpl entry instead, which normally expands to a section containing u-boot-vpl-dtb, u-boot-vpl-bss-pad and u-boot-vpl-dtb
diff --git a/tools/binman/etype/u_boot_spl_nodtb.py b/tools/binman/etype/u_boot_spl_nodtb.py index e7ec329c902..0e172aec1b0 100644 --- a/tools/binman/etype/u_boot_spl_nodtb.py +++ b/tools/binman/etype/u_boot_spl_nodtb.py @@ -23,8 +23,6 @@ class Entry_u_boot_spl_nodtb(Entry_blob):
SPL can access binman symbols at runtime. See :ref:`binman_fdt`.
- in the binman README for more information. - The ELF file 'spl/u-boot-spl' must also be available for this to work, since binman uses that to look up symbols to write into the SPL binary. """ diff --git a/tools/binman/etype/u_boot_tpl_nodtb.py b/tools/binman/etype/u_boot_tpl_nodtb.py index 9bb2b5dda30..e0c8a557d07 100644 --- a/tools/binman/etype/u_boot_tpl_nodtb.py +++ b/tools/binman/etype/u_boot_tpl_nodtb.py @@ -23,8 +23,6 @@ class Entry_u_boot_tpl_nodtb(Entry_blob):
TPL can access binman symbols at runtime. See :ref:`binman_fdt`.
- in the binman README for more information. - The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since binman uses that to look up symbols to write into the TPL binary. """ diff --git a/tools/binman/etype/u_boot_vpl.py b/tools/binman/etype/u_boot_vpl.py index 31d7e8374e2..0797831688f 100644 --- a/tools/binman/etype/u_boot_vpl.py +++ b/tools/binman/etype/u_boot_vpl.py @@ -27,6 +27,9 @@ class Entry_u_boot_vpl(Entry_blob):
The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since binman uses that to look up symbols to write into the VPL binary. + + Note that this entry is automatically replaced with u-boot-vpl-expanded + unless --no-expanded is used or the node has a 'no-expanded' property. """ def __init__(self, section, etype, node): super().__init__(section, etype, node, auto_write_symbols=True) diff --git a/tools/binman/etype/u_boot_vpl_nodtb.py b/tools/binman/etype/u_boot_vpl_nodtb.py index 64c2767488d..765cf53d164 100644 --- a/tools/binman/etype/u_boot_vpl_nodtb.py +++ b/tools/binman/etype/u_boot_vpl_nodtb.py @@ -16,8 +16,8 @@ class Entry_u_boot_vpl_nodtb(Entry_blob):
This is the U-Boot VPL binary, It does not include a device tree blob at the end of it so may not be able to work without it, assuming VPL needs - a device tree to operate on your platform. You can add a u_boot_vpl_dtb - entry after this one, or use a u_boot_vpl entry instead, which normally + a device tree to operate on your platform. You can add a u-boot-vpl-dtb + entry after this one, or use a u-boot-vpl entry instead, which normally expands to a section containing u-boot-vpl-dtb, u-boot-vpl-bss-pad and u-boot-vpl-dtb

Add a comment about this entry type being expanded, to match the comment for SPL and TPL. Drop an unwanted line in the SPL and TPL docs while here.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/entries.rst | 11 +++++------ tools/binman/etype/u_boot_spl_nodtb.py | 2 -- tools/binman/etype/u_boot_tpl_nodtb.py | 2 -- tools/binman/etype/u_boot_vpl.py | 3 +++ tools/binman/etype/u_boot_vpl_nodtb.py | 4 ++-- 5 files changed, 10 insertions(+), 12 deletions(-)
Applied to u-boot-dm, thanks!

Fix a minor typo in this function.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/ftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index e4da04030a5..bb691cb3ff2 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -546,7 +546,7 @@ class TestFunctional(unittest.TestCase): dtb_data = self._SetupDtb(fname)
# For testing purposes, make a copy of the DT for SPL and TPL. Add - # a node indicating which it is, so aid verification. + # a node indicating which it is, to aid verification. for name in ['spl', 'tpl', 'vpl']: dtb_fname = '%s/u-boot-%s.dtb' % (name, name) outfile = os.path.join(self._indir, dtb_fname)

Fix a minor typo in this function.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/ftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Applied to u-boot-dm, thanks!

This function has strange indentation. Fix it.
Fixes: 8c1fbd1f607 ("binman: ftest: Add test for u_boot_spl_pubkey_dtb")
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/ftest.py | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-)
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index bb691cb3ff2..d091855b8e3 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -7175,27 +7175,24 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
def testSplPubkeyDtb(self): - """Test u_boot_spl_pubkey_dtb etype""" - data = tools.read_file(self.TestFile("key.pem")) - self._MakeInputFile("key.crt", data) - self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts') - image = control.images['image'] - entries = image.GetEntries() - dtb_entry = entries['u-boot-spl-pubkey-dtb'] - dtb_data = dtb_entry.GetData() - dtb = fdt.Fdt.FromData(dtb_data) - dtb.Scan() - - signature_node = dtb.GetNode('/signature') - self.assertIsNotNone(signature_node) - key_node = signature_node.FindNode("key-key") - self.assertIsNotNone(key_node) - self.assertEqual(fdt_util.GetString(key_node, "required"), - "conf") - self.assertEqual(fdt_util.GetString(key_node, "algo"), - "sha384,rsa4096") - self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), - "key") + """Test u_boot_spl_pubkey_dtb etype""" + data = tools.read_file(self.TestFile("key.pem")) + self._MakeInputFile("key.crt", data) + self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts') + image = control.images['image'] + entries = image.GetEntries() + dtb_entry = entries['u-boot-spl-pubkey-dtb'] + dtb_data = dtb_entry.GetData() + dtb = fdt.Fdt.FromData(dtb_data) + dtb.Scan() + + signature_node = dtb.GetNode('/signature') + self.assertIsNotNone(signature_node) + key_node = signature_node.FindNode("key-key") + self.assertIsNotNone(key_node) + self.assertEqual(fdt_util.GetString(key_node, "required"), "conf") + self.assertEqual(fdt_util.GetString(key_node, "algo"), "sha384,rsa4096") + self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"), "key")
def testXilinxBootgenSigning(self): """Test xilinx-bootgen etype"""

This function has strange indentation. Fix it.
Fixes: 8c1fbd1f607 ("binman: ftest: Add test for u_boot_spl_pubkey_dtb")
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/ftest.py | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-)
Applied to u-boot-dm, thanks!

Binman needs the ability to run fdtgrep to prepare devicetree subsets for use by SPL and TPL. Add a new bintool in preparation for this.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/bintools.rst | 8 ++ tools/binman/btool/fdtgrep.py | 137 ++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 tools/binman/btool/fdtgrep.py
diff --git a/tools/binman/bintools.rst b/tools/binman/bintools.rst index fa44d9c6872..cd05ad8cb26 100644 --- a/tools/binman/bintools.rst +++ b/tools/binman/bintools.rst @@ -65,6 +65,14 @@ to add the public key separately from mkimage.
+Bintool: fdtgrep: Handles the 'fdtgrep' tool +-------------------------------------------- + +This bintool supports running `fdtgrep` with parameters suitable for +producing SPL devicetrees from the main one. + + + Bintool: fiptool: Image generation for ARM Trusted Firmware -----------------------------------------------------------
diff --git a/tools/binman/btool/fdtgrep.py b/tools/binman/btool/fdtgrep.py new file mode 100644 index 00000000000..c34d8d8943b --- /dev/null +++ b/tools/binman/btool/fdtgrep.py @@ -0,0 +1,137 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Bintool implementation for fdtgrep + +fdtgrepprovides a way to grep devicetree-binary files to extract or remove +certain elements. + +Usage: fdtgrep - extract portions from device tree + +Usage: + fdtgrep <options> <dt file>|- + +Output formats are: + dts - device tree soure text + dtb - device tree blob (sets -Hmt automatically) + bin - device tree fragment (may not be a valid .dtb) + +Options: -[haAc:b:C:defg:G:HIlLmn:N:o:O:p:P:rRsStTvhV] + -a, --show-address Display address + -A, --colour Show all nodes/tags, colour those that match + -b, --include-node-with-prop <arg> Include contains containing property + -c, --include-compat <arg> Compatible nodes to include in grep + -C, --exclude-compat <arg> Compatible nodes to exclude in grep + -d, --diff Diff: Mark matching nodes with +, others with - + -e, --enter-node Enter direct subnode names of matching nodes + -f, --show-offset Display offset + -g, --include-match <arg> Node/property/compatible string to include in grep + -G, --exclude-match <arg> Node/property/compatible string to exclude in grep + -H, --show-header Output a header + -I, --show-version Put "/dts-v1/;" on first line of dts output + -l, --list-regions Output a region list + -L, --list-strings List strings in string table + -m, --include-mem Include mem_rsvmap section in binary output + -n, --include-node <arg> Node to include in grep + -N, --exclude-node <arg> Node to exclude in grep + -p, --include-prop <arg> Property to include in grep + -P, --exclude-prop <arg> Property to exclude in grep + -r, --remove-strings Remove unused strings from string table + -R, --include-root Include root node and all properties + -s, --show-subnodes Show all subnodes matching nodes + -S, --skip-supernodes Don't include supernodes of matching nodes + -t, --show-stringtab Include string table in binary output + -T, --show-aliases Include matching aliases in output + -o, --out <arg> -o <output file> + -O, --out-format <arg> -O <output format> + -v, --invert-match Invert the sense of matching (select non-matching lines) + -h, --help Print this help and exit + -V, --version Print version and exit +""" + +import tempfile + +from u_boot_pylib import tools +from binman import bintool + +class Bintoolfdtgrep(bintool.Bintool): + """Handles the 'fdtgrep' tool + + This bintool supports running `fdtgrep` with parameters suitable for + producing SPL devicetrees from the main one. + """ + def __init__(self, name): + super().__init__(name, 'Grep devicetree files') + + def create_for_phase(self, infile, phase, outfile, remove_props): + """Create the FDT for a particular 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) + + Returns: + CommandResult: Resulting output from the bintool, or None if the + tool is not present + """ + if phase == 'tpl': + tag = 'bootph-pre-sram' + elif phase == 'vpl': + tag = 'bootph-verify' + elif phase == 'spl': + tag = 'bootph-pre-ram' + else: + raise(f"Invalid U-Boot phase '{phase}': Use tpl/vpl/spl") + + # These args mirror those in cmd_fdtgrep in scripts/Makefile.lib + # First do the first stage + with tempfile.NamedTemporaryFile(prefix='fdtgrep.tmp', + dir=tools.get_output_dir()) as tmp: + args = [ + infile, + '-o', tmp.name, + '-b', 'bootph-all', + '-b', tag, + '-u', + '-RT', + '-n', '/chosen', + '-n', '/config', + '-O', 'dtb', + ] + self.run_cmd(*args) + args = [ + tmp.name, + '-o', outfile, + '-r', + '-O', 'dtb', + '-P', 'bootph-all', + '-P', 'bootph-pre-ram', + '-P', 'bootph-pre-sram', + '-P', 'bootph-verify', + ] + for prop_name in remove_props: + args += ['-P', prop_name] + return self.run_cmd(*args) + + def fetch(self, method): + """Fetch handler for fdtgrep + + This installs fdtgrep using the apt utility, which assumes that it is + packaged in u-boot tools, which it is not. + + Args: + method (FETCH_...): Method to use + + Returns: + True if the file was fetched and now installed, None if a method + other than FETCH_BIN was requested + + Raises: + Valuerror: Fetching could not be completed + """ + if method != bintool.FETCH_BIN: + return None + return self.apt_install('u-boot-tools')

Binman needs the ability to run fdtgrep to prepare devicetree subsets for use by SPL and TPL. Add a new bintool in preparation for this.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/bintools.rst | 8 ++ tools/binman/btool/fdtgrep.py | 137 ++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 tools/binman/btool/fdtgrep.py
Applied to u-boot-dm, thanks!

Allow the entry-docs command to operate even if pylibfdt is not present in the systemn.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/image.py | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/tools/binman/image.py b/tools/binman/image.py index e77b5d0d97c..97443d3c0a7 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -21,6 +21,9 @@ from dtoc import fdt_util from u_boot_pylib import tools from u_boot_pylib import tout
+# This is imported if needed +state = None + class Image(section.Entry_section): """A Image, representing an output from binman
@@ -75,6 +78,10 @@ class Image(section.Entry_section): def __init__(self, name, node, copy_to_orig=True, test=False, ignore_missing=False, use_expanded=False, missing_etype=False, generate=True): + # Put this here to allow entry-docs and help to work without libfdt + global state + from binman import state + super().__init__(None, 'section', node, test=test) self.copy_to_orig = copy_to_orig self.name = name

Allow the entry-docs command to operate even if pylibfdt is not present in the systemn.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/image.py | 7 +++++++ 1 file changed, 7 insertions(+)
Applied to u-boot-dm, thanks!

At present the contents of an FDT (for each phase) are fixed, determined by the build and provided to Binman as input files.
Provide a means for entry types to provide their own FDT, so that it can be processed, if needed.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/entry.py | 12 ++++++++++++ tools/binman/etype/blob_dtb.py | 4 ++-- tools/binman/image.py | 4 ++++ 3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 752c584c9a3..494b1b1278d 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -1386,3 +1386,15 @@ features to produce new behaviours.
def UpdateSignatures(self, privatekey_fname, algo, input_fname): self.Raise('Updating signatures is not supported with this entry type') + + def FdtContents(self, fdt_etype): + """Get the contents of an FDT for a particular phase + + Args: + fdt_etype (str): Filename of the phase of the FDT to return, e.g. + 'u-boot-tpl-dtb' + + Returns: + bytes: Contents of requested FDT + """ + return self.section.FdtContents(fdt_etype) diff --git a/tools/binman/etype/blob_dtb.py b/tools/binman/etype/blob_dtb.py index 1471ded2211..b234323d7cf 100644 --- a/tools/binman/etype/blob_dtb.py +++ b/tools/binman/etype/blob_dtb.py @@ -41,12 +41,12 @@ class Entry_blob_dtb(Entry_blob): def ObtainContents(self, fake_size=0): """Get the device-tree from the list held by the 'state' module""" self._filename = self.GetDefaultFilename() - self._pathname, _ = state.GetFdtContents(self.GetFdtEtype()) + self._pathname, _ = self.FdtContents(self.GetFdtEtype()) return super().ReadBlobContents()
def ProcessContents(self): """Re-read the DTB contents so that we get any calculated properties""" - _, indata = state.GetFdtContents(self.GetFdtEtype()) + _, indata = self.FdtContents(self.GetFdtEtype())
if self.compress == 'zstd' and self.prepend != 'length': self.Raise('The zstd compression requires a length header') diff --git a/tools/binman/image.py b/tools/binman/image.py index 97443d3c0a7..c1be5cc23a2 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -425,3 +425,7 @@ class Image(section.Entry_section): super().AddBintools(bintools) self.bintools = bintools return bintools + + def FdtContents(self, fdt_etype): + """This base-class implementation simply calls the state function""" + return state.GetFdtContents(fdt_etype)

At present the contents of an FDT (for each phase) are fixed, determined by the build and provided to Binman as input files.
Provide a means for entry types to provide their own FDT, so that it can be processed, if needed.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/entry.py | 12 ++++++++++++ tools/binman/etype/blob_dtb.py | 4 ++-- tools/binman/image.py | 4 ++++ 3 files changed, 18 insertions(+), 2 deletions(-)
Applied to u-boot-dm, thanks!

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 ---
tools/binman/btool/fdtgrep.py | 2 +- tools/binman/control.py | 3 + tools/binman/entries.rst | 42 ++++++ tools/binman/entry.py | 4 +- tools/binman/etype/alternates_fdt.py | 132 +++++++++++++++++++ tools/binman/ftest.py | 121 +++++++++++++++++ tools/binman/image.py | 13 ++ 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 ++++ 14 files changed, 507 insertions(+), 2 deletions(-) create mode 100644 tools/binman/etype/alternates_fdt.py 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
diff --git a/tools/binman/btool/fdtgrep.py b/tools/binman/btool/fdtgrep.py index c34d8d8943b..da1f8c7bf4e 100644 --- a/tools/binman/btool/fdtgrep.py +++ b/tools/binman/btool/fdtgrep.py @@ -84,7 +84,7 @@ class Bintoolfdtgrep(bintool.Bintool): elif phase == 'spl': tag = 'bootph-pre-ram' else: - raise(f"Invalid U-Boot phase '{phase}': Use tpl/vpl/spl") + raise ValueError(f"Invalid U-Boot phase '{phase}': Use tpl/vpl/spl")
# These args mirror those in cmd_fdtgrep in scripts/Makefile.lib # First do the first stage diff --git a/tools/binman/control.py b/tools/binman/control.py index a233c778d5e..542c2b45644 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -734,6 +734,9 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True, image.WriteMap()
has_problems = CheckForProblems(image) + + image.WriteAlternates() + return has_problems
def Binman(args): diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index 38dfe2c7db9..8bfec8b434e 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -11,6 +11,48 @@ features to produce new behaviours.
+.. _etype_alternates_fdt: + +Entry: alternates-fdt: Entry that generates alternative sections for each devicetree provided +--------------------------------------------------------------------------------------------- + +When creating an image designed to boot on multiple models, each model +requires its own devicetree. This entry deals with selecting the correct +devicetree from a directory containing them. Each one is read in turn, then +used to produce section contents which are written to a file. This results +in a number of images, one for each model. + +For example this produces images for each .dtb file in the 'dtb' directory:: + + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + fdt-phase = "tpl"; + + section { + u-boot-tpl { + }; + }; + }; + +Each output file is named based on its input file, so an input file of +`model1.dtb` results in an output file of `model1.bin` (i.e. the `NAME` in +the `filename-pattern` property is replaced with the .dtb basename). + +Note that this entry type still produces contents for the 'main' image, in +that case using the normal dtb provided to Binman, e.g. `u-boot-tpl.dtb`. +But that image is unlikely to be useful, since it relates to whatever dtb +happened to be the default when U-Boot builds +(i.e. `CONFIG_DEFAULT_DEVICE_TREE`). However, Binman ensures that the size +of each of the alternates is the same as the 'default' one, so they can in +principle be 'slotted in' to the appropriate place in the main image. + +The optional `fdt-phase` property indicates the phase to build. In this +case, it etype runs fdtgrep to obtain the devicetree subset for that phase, +respecting the `bootph-xxx` tags in the devicetree. + + + .. _etype_atf_bl31:
Entry: atf-bl31: ARM Trusted Firmware (ATF) BL31 blob diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 494b1b1278d..6d2f3789940 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -1395,6 +1395,8 @@ features to produce new behaviours. 'u-boot-tpl-dtb'
Returns: - bytes: Contents of requested FDT + tuple: + fname (str): Filename of .dtb + bytes: Contents of FDT (possibly run through fdtgrep) """ return self.section.FdtContents(fdt_etype) diff --git a/tools/binman/etype/alternates_fdt.py b/tools/binman/etype/alternates_fdt.py new file mode 100644 index 00000000000..808f535aa1b --- /dev/null +++ b/tools/binman/etype/alternates_fdt.py @@ -0,0 +1,132 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2024 Google LLC +# Written by Simon Glass sjg@chromium.org + +"""Entry-type module for producing multiple alternate sections""" + +import glob +import os + +from binman.entry import EntryArg +from binman.etype.section import Entry_section +from dtoc import fdt_util +from u_boot_pylib import tools + +class Entry_alternates_fdt(Entry_section): + """Entry that generates alternative sections for each devicetree provided + + When creating an image designed to boot on multiple models, each model + requires its own devicetree. This entry deals with selecting the correct + devicetree from a directory containing them. Each one is read in turn, then + used to produce section contents which are written to a file. This results + in a number of images, one for each model. + + For example this produces images for each .dtb file in the 'dtb' directory:: + + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + fdt-phase = "tpl"; + + section { + u-boot-tpl { + }; + }; + }; + + Each output file is named based on its input file, so an input file of + `model1.dtb` results in an output file of `model1.bin` (i.e. the `NAME` in + the `filename-pattern` property is replaced with the .dtb basename). + + Note that this entry type still produces contents for the 'main' image, in + that case using the normal dtb provided to Binman, e.g. `u-boot-tpl.dtb`. + But that image is unlikely to be useful, since it relates to whatever dtb + happened to be the default when U-Boot builds + (i.e. `CONFIG_DEFAULT_DEVICE_TREE`). However, Binman ensures that the size + of each of the alternates is the same as the 'default' one, so they can in + principle be 'slotted in' to the appropriate place in the main image. + + The optional `fdt-phase` property indicates the phase to build. In this + case, it etype runs fdtgrep to obtain the devicetree subset for that phase, + respecting the `bootph-xxx` tags in the devicetree. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.fdt_list_dir = None + self.filename_pattern = None + self.required_props = ['fdt-list-dir'] + self._cur_fdt = None + self._fdt_phase = None + self.fdtgrep = None + self._fdt_dir = None + self._fdts = None + self._fname_pattern = None + self._remove_props = None + self.alternates = None + + def ReadNode(self): + """Read properties from the node""" + super().ReadNode() + self._fdt_dir = fdt_util.GetString(self._node, 'fdt-list-dir') + fname = tools.get_input_filename(self._fdt_dir) + fdts = glob.glob('*.dtb', root_dir=fname) + self._fdts = [os.path.splitext(f)[0] for f in fdts] + + self._fdt_phase = fdt_util.GetString(self._node, 'fdt-phase') + + # This is used by Image.WriteAlternates() + self.alternates = self._fdts + + self._fname_pattern = fdt_util.GetString(self._node, 'filename-pattern') + + self._remove_props = [] + props, = self.GetEntryArgsOrProps( + [EntryArg('of-spl-remove-props', str)], required=False) + if props: + self._remove_props = props.split() + + def FdtContents(self, fdt_etype): + # If there is no current FDT, just use the normal one + if not self._cur_fdt: + return self.section.FdtContents(fdt_etype) + + # Find the file to use + fname = os.path.join(self._fdt_dir, f'{self._cur_fdt}.dtb') + infile = tools.get_input_filename(fname) + + # Run fdtgrep if needed, to remove unwanted nodes and properties + if self._fdt_phase: + uniq = self.GetUniqueName() + outfile = tools.get_output_filename( + f'{uniq}.{self._cur_fdt}-{self._fdt_phase}.dtb') + self.fdtgrep.create_for_phase(infile, self._fdt_phase, outfile, + self._remove_props) + return outfile, tools.read_file(outfile) + return fname, tools.read_file(infile) + + def ProcessWithFdt(self, alt): + """Produce the contents of this entry, using a particular FDT blob + + Args: + alt (str): Name of the alternate + + Returns: + tuple: + str: Filename to use for the alternate's .bin file + bytes: Contents of this entry's section, using the selected FDT + """ + pattern = self._fname_pattern or 'NAME.bin' + fname = pattern.replace('NAME', alt) + + data = b'' + try: + self._cur_fdt = alt + self.ProcessContents() + data = self.GetPaddedData() + finally: + self._cur_fdt = None + return fname, data + + def AddBintools(self, btools): + super().AddBintools(btools) + self.fdtgrep = self.AddBintool(btools, 'fdtgrep') 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() diff --git a/tools/binman/image.py b/tools/binman/image.py index c1be5cc23a2..702c9055585 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -193,6 +193,19 @@ class Image(section.Entry_section): os.remove(sname) os.symlink(fname, sname)
+ def WriteAlternates(self): + """Write out alternative devicetree blobs, each in its own file""" + alt_entry = self.FindEntryType('alternates-fdt') + if not alt_entry: + return + + for alt in alt_entry.alternates: + fname, data = alt_entry.ProcessWithFdt(alt) + pathname = tools.get_output_filename(fname) + tout.info(f"Writing alternate '{alt}' to '{pathname}'") + tools.write_file(pathname, data) + tout.info("Wrote %#x bytes" % len(data)) + def WriteMap(self): """Write a map of the image to a .map file
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 sjg@chromium.org + +/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 sjg@chromium.org + +/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 sjg@chromium.org + +/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 sjg@chromium.org + +/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 sjg@chromium.org + +/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 sjg@chromium.org + +/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 sjg@chromium.org + +/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; + }; + }; +};

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 ---
tools/binman/btool/fdtgrep.py | 2 +- tools/binman/control.py | 3 + tools/binman/entries.rst | 42 ++++++ tools/binman/entry.py | 4 +- tools/binman/etype/alternates_fdt.py | 132 +++++++++++++++++++ tools/binman/ftest.py | 121 +++++++++++++++++ tools/binman/image.py | 13 ++ 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 ++++ 14 files changed, 507 insertions(+), 2 deletions(-) create mode 100644 tools/binman/etype/alternates_fdt.py 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
Applied to u-boot-dm, thanks!

Hi Simon
On 20/07/24 16:19, Simon Glass wrote:
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).
Realized I caught up too late to this thread, and it's already pulled in, anyways thanks for the effort this was needed.
Signed-off-by: Simon Glass sjg@chromium.org
tools/binman/btool/fdtgrep.py | 2 +- tools/binman/control.py | 3 + tools/binman/entries.rst | 42 ++++++ tools/binman/entry.py | 4 +- tools/binman/etype/alternates_fdt.py | 132 +++++++++++++++++++ tools/binman/ftest.py | 121 +++++++++++++++++ tools/binman/image.py | 13 ++ 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 ++++ 14 files changed, 507 insertions(+), 2 deletions(-) create mode 100644 tools/binman/etype/alternates_fdt.py 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
diff --git a/tools/binman/btool/fdtgrep.py b/tools/binman/btool/fdtgrep.py index c34d8d8943b..da1f8c7bf4e 100644 --- a/tools/binman/btool/fdtgrep.py +++ b/tools/binman/btool/fdtgrep.py @@ -84,7 +84,7 @@ class Bintoolfdtgrep(bintool.Bintool): elif phase == 'spl': tag = 'bootph-pre-ram' else:
raise(f"Invalid U-Boot phase '{phase}': Use tpl/vpl/spl")
raise ValueError(f"Invalid U-Boot phase '{phase}': Use tpl/vpl/spl") # These args mirror those in cmd_fdtgrep in scripts/Makefile.lib # First do the first stage
diff --git a/tools/binman/control.py b/tools/binman/control.py index a233c778d5e..542c2b45644 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -734,6 +734,9 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True, image.WriteMap()
has_problems = CheckForProblems(image)
image.WriteAlternates()
return has_problems
def Binman(args):
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index 38dfe2c7db9..8bfec8b434e 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -11,6 +11,48 @@ features to produce new behaviours.
+.. _etype_alternates_fdt:
+Entry: alternates-fdt: Entry that generates alternative sections for each devicetree provided +---------------------------------------------------------------------------------------------
+When creating an image designed to boot on multiple models, each model +requires its own devicetree. This entry deals with selecting the correct +devicetree from a directory containing them. Each one is read in turn, then +used to produce section contents which are written to a file. This results +in a number of images, one for each model.
+For example this produces images for each .dtb file in the 'dtb' directory::
- alternates-fdt {
fdt-list-dir = "dtb";
filename-pattern = "NAME.bin";
fdt-phase = "tpl";
section {
u-boot-tpl {
};
};
- };
+Each output file is named based on its input file, so an input file of +`model1.dtb` results in an output file of `model1.bin` (i.e. the `NAME` in +the `filename-pattern` property is replaced with the .dtb basename).
+Note that this entry type still produces contents for the 'main' image, in +that case using the normal dtb provided to Binman, e.g. `u-boot-tpl.dtb`. +But that image is unlikely to be useful, since it relates to whatever dtb +happened to be the default when U-Boot builds +(i.e. `CONFIG_DEFAULT_DEVICE_TREE`). However, Binman ensures that the size +of each of the alternates is the same as the 'default' one, so they can in +principle be 'slotted in' to the appropriate place in the main image.
+The optional `fdt-phase` property indicates the phase to build. In this +case, it etype runs fdtgrep to obtain the devicetree subset for that phase, +respecting the `bootph-xxx` tags in the devicetree.
.. _etype_atf_bl31:
Entry: atf-bl31: ARM Trusted Firmware (ATF) BL31 blob
diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 494b1b1278d..6d2f3789940 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -1395,6 +1395,8 @@ features to produce new behaviours. 'u-boot-tpl-dtb'
Returns:
bytes: Contents of requested FDT
tuple:
fname (str): Filename of .dtb
bytes: Contents of FDT (possibly run through fdtgrep) """ return self.section.FdtContents(fdt_etype)
diff --git a/tools/binman/etype/alternates_fdt.py b/tools/binman/etype/alternates_fdt.py new file mode 100644 index 00000000000..808f535aa1b --- /dev/null +++ b/tools/binman/etype/alternates_fdt.py @@ -0,0 +1,132 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2024 Google LLC +# Written by Simon Glass sjg@chromium.org
+"""Entry-type module for producing multiple alternate sections"""
+import glob +import os
+from binman.entry import EntryArg +from binman.etype.section import Entry_section +from dtoc import fdt_util +from u_boot_pylib import tools
+class Entry_alternates_fdt(Entry_section):
- """Entry that generates alternative sections for each devicetree provided
- When creating an image designed to boot on multiple models, each model
- requires its own devicetree. This entry deals with selecting the correct
- devicetree from a directory containing them. Each one is read in turn, then
- used to produce section contents which are written to a file. This results
- in a number of images, one for each model.
- For example this produces images for each .dtb file in the 'dtb' directory::
alternates-fdt {
fdt-list-dir = "dtb";
filename-pattern = "NAME.bin";
fdt-phase = "tpl";
section {
u-boot-tpl {
};
};
};
- Each output file is named based on its input file, so an input file of
- `model1.dtb` results in an output file of `model1.bin` (i.e. the `NAME` in
- the `filename-pattern` property is replaced with the .dtb basename).
- Note that this entry type still produces contents for the 'main' image, in
- that case using the normal dtb provided to Binman, e.g. `u-boot-tpl.dtb`.
- But that image is unlikely to be useful, since it relates to whatever dtb
- happened to be the default when U-Boot builds
- (i.e. `CONFIG_DEFAULT_DEVICE_TREE`). However, Binman ensures that the size
- of each of the alternates is the same as the 'default' one, so they can in
- principle be 'slotted in' to the appropriate place in the main image.
- The optional `fdt-phase` property indicates the phase to build. In this
- case, it etype runs fdtgrep to obtain the devicetree subset for that phase,
- respecting the `bootph-xxx` tags in the devicetree.
- """
- def __init__(self, section, etype, node):
super().__init__(section, etype, node)
self.fdt_list_dir = None
self.filename_pattern = None
self.required_props = ['fdt-list-dir']
self._cur_fdt = None
self._fdt_phase = None
self.fdtgrep = None
self._fdt_dir = None
self._fdts = None
self._fname_pattern = None
self._remove_props = None
self.alternates = None
- def ReadNode(self):
"""Read properties from the node"""
super().ReadNode()
self._fdt_dir = fdt_util.GetString(self._node, 'fdt-list-dir')
fname = tools.get_input_filename(self._fdt_dir)
fdts = glob.glob('*.dtb', root_dir=fname)
self._fdts = [os.path.splitext(f)[0] for f in fdts]
self._fdt_phase = fdt_util.GetString(self._node, 'fdt-phase')
# This is used by Image.WriteAlternates()
self.alternates = self._fdts
self._fname_pattern = fdt_util.GetString(self._node, 'filename-pattern')
self._remove_props = []
props, = self.GetEntryArgsOrProps(
[EntryArg('of-spl-remove-props', str)], required=False)
if props:
self._remove_props = props.split()
- def FdtContents(self, fdt_etype):
# If there is no current FDT, just use the normal one
if not self._cur_fdt:
return self.section.FdtContents(fdt_etype)
# Find the file to use
fname = os.path.join(self._fdt_dir, f'{self._cur_fdt}.dtb')
infile = tools.get_input_filename(fname)
# Run fdtgrep if needed, to remove unwanted nodes and properties
if self._fdt_phase:
uniq = self.GetUniqueName()
outfile = tools.get_output_filename(
f'{uniq}.{self._cur_fdt}-{self._fdt_phase}.dtb')
self.fdtgrep.create_for_phase(infile, self._fdt_phase, outfile,
self._remove_props)
return outfile, tools.read_file(outfile)
return fname, tools.read_file(infile)
- def ProcessWithFdt(self, alt):
"""Produce the contents of this entry, using a particular FDT blob
Args:
alt (str): Name of the alternate
Returns:
tuple:
str: Filename to use for the alternate's .bin file
bytes: Contents of this entry's section, using the selected FDT
"""
pattern = self._fname_pattern or 'NAME.bin'
fname = pattern.replace('NAME', alt)
data = b''
try:
self._cur_fdt = alt
self.ProcessContents()
data = self.GetPaddedData()
finally:
self._cur_fdt = None
return fname, data
- def AddBintools(self, btools):
super().AddBintools(btools)
self.fdtgrep = self.AddBintool(btools, 'fdtgrep')
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()
diff --git a/tools/binman/image.py b/tools/binman/image.py index c1be5cc23a2..702c9055585 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -193,6 +193,19 @@ class Image(section.Entry_section): os.remove(sname) os.symlink(fname, sname)
- def WriteAlternates(self):
"""Write out alternative devicetree blobs, each in its own file"""
alt_entry = self.FindEntryType('alternates-fdt')
if not alt_entry:
return
for alt in alt_entry.alternates:
fname, data = alt_entry.ProcessWithFdt(alt)
pathname = tools.get_output_filename(fname)
tout.info(f"Writing alternate '{alt}' to '{pathname}'")
tools.write_file(pathname, data)
tout.info("Wrote %#x bytes" % len(data))
def WriteMap(self): """Write a map of the image to a .map file
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 sjg@chromium.org
+/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 sjg@chromium.org
+/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 sjg@chromium.org
+/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 sjg@chromium.org
+/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 sjg@chromium.org
+/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 sjg@chromium.org
+/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 sjg@chromium.org
+/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;
};
- };
+};

Hi Neha,
On Mon, 29 Jul 2024 at 22:44, Neha Malcom Francis n-francis@ti.com wrote:
Hi Simon
On 20/07/24 16:19, Simon Glass wrote:
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).
Realized I caught up too late to this thread, and it's already pulled in, anyways thanks for the effort this was needed.
Thanks for looking through it...we'll see how it goes and what adjustments are needed.
Regards, Simon

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 sjg@chromium.org ---
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
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 { + }; + }; +};

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 sjg@chromium.org ---
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
Applied to u-boot-dm, thanks!

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 sjg@chromium.org ---
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
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 { + }; + }; +};

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 sjg@chromium.org ---
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
Applied to u-boot-dm, thanks!

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 sjg@chromium.org ---
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
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 { + }; + }; +};

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 sjg@chromium.org ---
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
Applied to u-boot-dm, thanks!

Provide these parameters to binman so that it can produce images targeted at VPL.
Signed-off-by: Simon Glass sjg@chromium.org ---
Makefile | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/Makefile b/Makefile index f5b2512f369..d8e2e6a1b4b 100644 --- a/Makefile +++ b/Makefile @@ -1388,8 +1388,10 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ -a rockchip-tpl-path=$(ROCKCHIP_TPL) \ -a spl-bss-pad=$(if $(CONFIG_SPL_SEPARATE_BSS),,1) \ -a tpl-bss-pad=$(if $(CONFIG_TPL_SEPARATE_BSS),,1) \ + -a vpl-bss-pad=$(if $(CONFIG_VPL_SEPARATE_BSS),,1) \ -a spl-dtb=$(CONFIG_SPL_OF_REAL) \ -a tpl-dtb=$(CONFIG_TPL_OF_REAL) \ + -a vpl-dtb=$(CONFIG_VPL_OF_REAL) \ -a pre-load-key-path=${PRE_LOAD_KEY_PATH} \ $(BINMAN_$(@F))

Provide these parameters to binman so that it can produce images targeted at VPL.
Signed-off-by: Simon Glass sjg@chromium.org ---
Makefile | 2 ++ 1 file changed, 2 insertions(+)
Applied to u-boot-dm, thanks!

Pass CONFIG_OF_SPL_REMOVE_PROPS to binman so that it can remove properties correctly when producing FITs for SPL phases.
Signed-off-by: Simon Glass sjg@chromium.org ---
Makefile | 1 + tools/binman/etype/fit.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile index d8e2e6a1b4b..4b61dac8428 100644 --- a/Makefile +++ b/Makefile @@ -1393,6 +1393,7 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ -a tpl-dtb=$(CONFIG_TPL_OF_REAL) \ -a vpl-dtb=$(CONFIG_VPL_OF_REAL) \ -a pre-load-key-path=${PRE_LOAD_KEY_PATH} \ + -a of-spl-remove-props=$(CONFIG_OF_SPL_REMOVE_PROPS) \ $(BINMAN_$(@F))
OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 38358292ece..ee44e5a1cd6 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -210,7 +210,8 @@ class Entry_fit(Entry_section):
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. + `clock-names` and `pinctrl-names` which are not needed in SPL builds. This + value is automatically passed to binman by the U-Boot build.
See :ref:`fdtgrep_filter` for more information.

Pass CONFIG_OF_SPL_REMOVE_PROPS to binman so that it can remove properties correctly when producing FITs for SPL phases.
Signed-off-by: Simon Glass sjg@chromium.org ---
Makefile | 1 + tools/binman/etype/fit.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
Applied to u-boot-dm, thanks!
participants (2)
-
Neha Malcom Francis
-
Simon Glass