From 7ff80ece2159c1e73f11e1e1b9484e00d1dfeb76 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Jul 2024 10:11:23 +0100 Subject: qconfig: Allow searching for CONFIG values Add basic support for searching for matching of non-matching values. Signed-off-by: Simon Glass Fixes: https://source.denx.de/u-boot/custodians/u-boot-dm/-/issues/20 --- tools/qconfig.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/qconfig.py b/tools/qconfig.py index 408807931ff..71fe6fff29c 100755 --- a/tools/qconfig.py +++ b/tools/qconfig.py @@ -1079,7 +1079,7 @@ def do_imply_config(config_list, add_imply, imply_flags, skip_added, for linenum in sorted(linenums, reverse=True): add_imply_rule(config[CONFIG_LEN:], fname, linenum) -def defconfig_matches(configs, re_match): +def defconfig_matches(configs, re_match, re_val): """Check if any CONFIG option matches a regex The match must be complete, i.e. from the start to end of the CONFIG option. @@ -1089,13 +1089,15 @@ def defconfig_matches(configs, re_match): key: CONFIG option value: Value of option re_match (re.Pattern): Match to check + re_val (re.Pattern): Regular expression to check against value (or None) Returns: bool: True if any CONFIG matches the regex """ - for cfg in configs: + for cfg, val in configs.items(): if re_match.fullmatch(cfg): - return True + if not re_val or re_val.fullmatch(val): + return True return False def do_find_config(config_list): @@ -1123,6 +1125,11 @@ def do_find_config(config_list): if cfg[0] == '~': want = False cfg = cfg[1:] + val = None + re_val = None + if '=' in cfg: + cfg, val = cfg.split('=', maxsplit=1) + re_val = re.compile(val) # Search everything that is still in the running. If it has a config # that we want, or doesn't have one that we don't, add it into the @@ -1131,7 +1138,7 @@ def do_find_config(config_list): out = set() re_match = re.compile(cfg) for defc in in_list: - has_cfg = defconfig_matches(config_db[defc], re_match) + has_cfg = defconfig_matches(config_db[defc], re_match, re_val) if has_cfg == want: out.add(defc) print(f'{len(out)} matches') -- cgit v1.2.3 From 6bf44b48f9c4be65cbd0142e28558227ea4ae063 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Jul 2024 10:11:24 +0100 Subject: qconfig: Sort the boards by name when finding There is no particular ordering of the board list at present, since it is generated by a multi-threaded process. Sort them by name to make it easier to see if a particular board is present. Signed-off-by: Simon Glass --- tools/qconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/qconfig.py b/tools/qconfig.py index 71fe6fff29c..241bd9efe33 100755 --- a/tools/qconfig.py +++ b/tools/qconfig.py @@ -1142,7 +1142,7 @@ def do_find_config(config_list): if has_cfg == want: out.add(defc) print(f'{len(out)} matches') - print(' '.join(item.split('_defconfig')[0] for item in out)) + print(' '.join(item.split('_defconfig')[0] for item in sorted(list(out)))) return 0 -- cgit v1.2.3 From 00b0855aef3c6d14a435d33a799a0649cda34e5c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Jul 2024 10:11:25 +0100 Subject: qconfig: Support a 'list' format Add a flag to output the found list in a more user-friendly format, with one board per line. Omit the board count. This can be useful with grep, for example. Signed-off-by: Simon Glass --- tools/qconfig.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/qconfig.py b/tools/qconfig.py index 241bd9efe33..7b868c7d72c 100755 --- a/tools/qconfig.py +++ b/tools/qconfig.py @@ -1100,7 +1100,7 @@ def defconfig_matches(configs, re_match, re_val): return True return False -def do_find_config(config_list): +def do_find_config(config_list, list_format): """Find boards with a given combination of CONFIGs Args: @@ -1108,6 +1108,8 @@ def do_find_config(config_list): consisting of a config option, with or without a CONFIG_ prefix. If an option is preceded by a tilde (~) then it must be false, otherwise it must be true) + list_format (bool): True to write in 'list' format, one board name per + line Returns: int: exit code (0 for success) @@ -1141,8 +1143,10 @@ def do_find_config(config_list): has_cfg = defconfig_matches(config_db[defc], re_match, re_val) if has_cfg == want: out.add(defc) - print(f'{len(out)} matches') - print(' '.join(item.split('_defconfig')[0] for item in sorted(list(out)))) + if not list_format: + print(f'{len(out)} matches') + sep = '\n' if list_format else ' ' + print(sep.join(item.split('_defconfig')[0] for item in sorted(list(out)))) return 0 @@ -1535,6 +1539,8 @@ doc/develop/moveconfig.rst for documentation.''' help='Find boards with a given config combination') parser.add_argument('-i', '--imply', action='store_true', default=False, help='find options which imply others') + parser.add_argument('-l', '--list', action='store_true', default=False, + help='Show a sorted list of board names, one per line') parser.add_argument('-I', '--imply-flags', type=str, default='', help="control the -i option ('help' for help") parser.add_argument('-j', '--jobs', type=int, default=cpu_count, @@ -1688,7 +1694,7 @@ def main(): sys.exit(1) return 0 if args.find: - return do_find_config(args.configs) + return do_find_config(args.configs, args.list) config_db, progress = move_config(args) -- cgit v1.2.3 From 68516827c84524d47be09b95fd635ded040c8a54 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:32 +0100 Subject: binman: Correct an error in the FIT-template example The example contains references to an non-existent node. Fix it. Signed-off-by: Simon Glass --- tools/binman/binman.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') 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 { -- cgit v1.2.3 From 018646cddd5f6e1d44a1d1d382d85fd1360f96cd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:33 +0100 Subject: binman: Tidy up bintool docs The docs have somehow got out of sync. Regenerate the file to fix the problem. Signed-off-by: Simon Glass --- tools/binman/bintools.rst | 61 +++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 23 deletions(-) (limited to 'tools') 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. -- cgit v1.2.3 From 82873aab47c092a16c4f8743749215700f151e39 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:34 +0100 Subject: binman: Tidy up comment for Bintoolfdt_add_pubkey.run() This function returns a value, so add documentation for it. Signed-off-by: Simon Glass --- tools/binman/btool/fdt_add_pubkey.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools') 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: -- cgit v1.2.3 From bcc8f0853ba8f3b42157b6d05702716234219f4e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:35 +0100 Subject: binman: Move problem-checking code into a function This has become quite long, so move it into a separate function. Signed-off-by: Simon Glass --- tools/binman/control.py | 78 +++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 32 deletions(-) (limited to 'tools') 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 -- cgit v1.2.3 From 9b37db6b770fa0e001d9526edd43f23b8b4c6368 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:36 +0100 Subject: binman: elf: Add more debugging to LookupAndWriteSymbols() 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 --- tools/binman/elf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') 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 -- cgit v1.2.3 From d720be4766e91459e92197d9b112958e943d66c3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:37 +0100 Subject: binman: Write the compressed output to a file 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 --- tools/binman/entry.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools') 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) -- cgit v1.2.3 From 32fdd92e36d4fdc9beae7a405b90b61301e52bed Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:38 +0100 Subject: binman: Correct comment in blob_dtb GetFdtEtype() The filenames are a bit confused. Fix them. Signed-off-by: Simon Glass --- tools/binman/etype/blob_dtb.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools') 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' """ -- cgit v1.2.3 From 19009864466d5fe7639befe948028b4a6f96fb5b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:39 +0100 Subject: binman: Mention expanded entries in u-boot-vpl 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 --- 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(-) (limited to 'tools') 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 -- cgit v1.2.3 From 57208ddc8ca8dbc9b96b223d568b8dcef4254479 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:40 +0100 Subject: binman: Fix a comment typo in _DoReadFileDtb() Fix a minor typo in this function. Signed-off-by: Simon Glass --- tools/binman/ftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') 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) -- cgit v1.2.3 From a56ea60627b5672ba792fe461752f8a348538387 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:41 +0100 Subject: binman: Correct indentation in testSplPubkeyDtb This function has strange indentation. Fix it. Fixes: 8c1fbd1f607 ("binman: ftest: Add test for u_boot_spl_pubkey_dtb") Signed-off-by: Simon Glass --- tools/binman/ftest.py | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'tools') 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""" -- cgit v1.2.3 From 76cbda76e7e9d90eaaa1dc1931943a1a2b62c92c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:42 +0100 Subject: binman: Add a bintool for fdtgrep 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 --- tools/binman/bintools.rst | 8 +++ tools/binman/btool/fdtgrep.py | 137 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 tools/binman/btool/fdtgrep.py (limited to 'tools') 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
|- + +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 Include contains containing property + -c, --include-compat Compatible nodes to include in grep + -C, --exclude-compat 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 Node/property/compatible string to include in grep + -G, --exclude-match 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 Node to include in grep + -N, --exclude-node Node to exclude in grep + -p, --include-prop Property to include in grep + -P, --exclude-prop 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 -o + -O, --out-format -O + -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') -- cgit v1.2.3 From 2e3697b954c09bd731e8b351f561f25480238aca Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:43 +0100 Subject: binman: Remove dependency on pylibfdt for entry-docs Allow the entry-docs command to operate even if pylibfdt is not present in the systemn. Signed-off-by: Simon Glass --- tools/binman/image.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'tools') 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 -- cgit v1.2.3 From daed9b42b42cee1d0ea33a568707fb9eef423f9f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:44 +0100 Subject: binman: Allow entry types to override FDT contents 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 --- tools/binman/entry.py | 12 ++++++++++++ tools/binman/etype/blob_dtb.py | 4 ++-- tools/binman/image.py | 4 ++++ 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'tools') 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) -- cgit v1.2.3 From 7081a94ea46df42dcef77c902d16c38685739719 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:45 +0100 Subject: binman: Add support for alternative FDTs FIT provides a way to select between different devicetree blobs depending on the model. This works fine for U-Boot proper and allows SPL to select the correct blob for the current board at runtime. The boot sequence (SPL->U-Boot proper) is therefore covered by the existing feature set. The first boot phase (typically TPL) cannot use FIT since SoC boot ROMs don't currently support it. Therefore the TPL image must be specific to each model it boots on. To support booting on mulitple models, binman must therefore produce a separate TPL image for each model, even if the images for the rest of the phases are identical. TPL needs to be packaged as an executable binary along with a reduced devicetree. When multiple models are supported, a reduced devicetree must be provided for each model. U-Boot's build system is designed to build a single devicetree for SPL builds, so does not support this requirement. Add a new 'alternatives' feature to Binman, allowing it to automatically subset a devicetree to produce the reduced devicetree for a particular phase for each supported model. With this it is possible to produce a separate TPL image for each of the models. The correct one can then be loaded onto a board, along with the common FIT image(s). Signed-off-by: Simon Glass --- tools/binman/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 (limited to 'tools') 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 + +"""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 + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "model-not-set"; + + binman { + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + + section { + u-boot-tpl { + }; + }; + }; + + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/binman/test/329_alternates_fdtgrep.dts b/tools/binman/test/329_alternates_fdtgrep.dts new file mode 100644 index 00000000000..41695281456 --- /dev/null +++ b/tools/binman/test/329_alternates_fdtgrep.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "model-not-set"; + + binman { + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + fdt-phase = "tpl"; + + section { + u-boot-tpl { + }; + }; + }; + + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/binman/test/330_alternates_vpl.dts b/tools/binman/test/330_alternates_vpl.dts new file mode 100644 index 00000000000..5b57069e2ab --- /dev/null +++ b/tools/binman/test/330_alternates_vpl.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "model-not-set"; + + binman { + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + fdt-phase = "vpl"; + + section { + u-boot-vpl { + }; + }; + }; + + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/binman/test/331_alternates_spl.dts b/tools/binman/test/331_alternates_spl.dts new file mode 100644 index 00000000000..882fefce34a --- /dev/null +++ b/tools/binman/test/331_alternates_spl.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "model-not-set"; + + binman { + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + fdt-phase = "spl"; + + section { + u-boot-spl { + }; + }; + }; + + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/binman/test/332_alternates_inval.dts b/tools/binman/test/332_alternates_inval.dts new file mode 100644 index 00000000000..8c145dd2449 --- /dev/null +++ b/tools/binman/test/332_alternates_inval.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "model-not-set"; + + binman { + alternates-fdt { + fdt-list-dir = "dtb"; + filename-pattern = "NAME.bin"; + fdt-phase = "bad-phase"; + + section { + u-boot-spl { + }; + }; + }; + + blob { + filename = "blobfile"; + }; + }; +}; diff --git a/tools/binman/test/alt_dts/model1.dts b/tools/binman/test/alt_dts/model1.dts new file mode 100644 index 00000000000..01e95e8fabe --- /dev/null +++ b/tools/binman/test/alt_dts/model1.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + model = "Model One"; + compatible = "u-boot,model-one"; + + /* this node remains due to bootph-pre-sram tag */ + node { + some-prop; + prop-to-remove; + another-prop-to-get-rid-of; + not-a-prop-to-remove; + bootph-pre-sram; + + /* this node get removed by fdtgrep */ + other-node { + another-prop; + }; + }; +}; diff --git a/tools/binman/test/alt_dts/model2.dts b/tools/binman/test/alt_dts/model2.dts new file mode 100644 index 00000000000..7829c519772 --- /dev/null +++ b/tools/binman/test/alt_dts/model2.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2024 Google LLC +// Written by Simon Glass + +/dts-v1/; + +/ { + model = "Model Two"; + compatible = "u-boot,model-two"; + + /* this node remains due to bootph-pre-sram tag */ + node { + some-prop; + prop-to-remove; + another-prop-to-get-rid-of; + not-a-prop-to-remove; + bootph-pre-sram; + + /* this node get removed by fdtgrep */ + other-node { + another-prop; + }; + }; +}; -- cgit v1.2.3 From 9db7a3a4325d1b67287cdc2db2d80c5fc1e7cb76 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:46 +0100 Subject: binman: fit: Allow providing FDT filenames in a directory In some cases the list of available FDT files is not available in an entryarg. Provide an option to point to a directory containing them instead. Signed-off-by: Simon Glass --- tools/binman/entries.rst | 7 +++++ tools/binman/etype/fit.py | 19 +++++++++++- tools/binman/ftest.py | 17 +++++++--- tools/binman/test/333_fit_fdt_dir.dts | 58 +++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 tools/binman/test/333_fit_fdt_dir.dts (limited to 'tools') diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index 8bfec8b434e..dcacd298c6d 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -857,6 +857,13 @@ The top-level 'fit' node supports the following special properties: fit,fdt-list-val = "dtb1", "dtb2"; + fit,fdt-list-dir + As an alternative to fit,fdt-list the list of device tree files + can be provided as a directory. Each .dtb file in the directory is + processed, , e.g.:: + + fit,fdt-list-dir = "arch/arm/dts + Substitutions ~~~~~~~~~~~~~ diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 2c14b15b03c..8a25c784ef6 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -5,7 +5,9 @@ """Entry-type module for producing a FIT""" +import glob import libfdt +import os from binman.entry import Entry, EntryArg from binman.etype.section import Entry_section @@ -87,6 +89,13 @@ class Entry_fit(Entry_section): fit,fdt-list-val = "dtb1", "dtb2"; + fit,fdt-list-dir + As an alternative to fit,fdt-list the list of device tree files + can be provided as a directory. Each .dtb file in the directory is + processed, , e.g.:: + + fit,fdt-list-dir = "arch/arm/dts + Substitutions ~~~~~~~~~~~~~ @@ -352,6 +361,7 @@ class Entry_fit(Entry_section): self._fit = None self._fit_props = {} self._fdts = None + self._fdt_dir = None self.mkimage = None self._priv_entries = {} self._loadables = [] @@ -368,7 +378,14 @@ class Entry_fit(Entry_section): if fdts is not None: self._fdts = fdts.split() else: - self._fdts = fdt_util.GetStringList(self._node, 'fit,fdt-list-val') + self._fdt_dir = fdt_util.GetString(self._node, 'fit,fdt-list-dir') + if self._fdt_dir: + indir = tools.get_input_filename(self._fdt_dir) + fdts = glob.glob('*.dtb', root_dir=indir) + self._fdts = [os.path.splitext(f)[0] for f in sorted(fdts)] + else: + self._fdts = fdt_util.GetStringList(self._node, + 'fit,fdt-list-val') self._fit_default_dt = self.GetEntryArgsOrProps([EntryArg('default-dt', str)])[0] diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 684e960b582..6e1e1e9b50b 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -4181,8 +4181,8 @@ class TestFunctional(unittest.TestCase): data = self._DoReadFile('172_scp.dts') self.assertEqual(SCP_DATA, data[:len(SCP_DATA)]) - def testFitFdt(self): - """Test an image with an FIT with multiple FDT images""" + def CheckFitFdt(self, dts='170_fit_fdt.dts', use_fdt_list=True): + """Check an image with an FIT with multiple FDT images""" def _CheckFdt(seq, expected_data): """Check the FDT nodes @@ -4221,11 +4221,12 @@ class TestFunctional(unittest.TestCase): self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value) entry_args = { - 'of-list': 'test-fdt1 test-fdt2', 'default-dt': 'test-fdt2', } + if use_fdt_list: + entry_args['of-list'] = 'test-fdt1 test-fdt2' data = self._DoReadFileDtb( - '170_fit_fdt.dts', + dts, entry_args=entry_args, extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) @@ -4244,6 +4245,10 @@ class TestFunctional(unittest.TestCase): _CheckConfig(1, TEST_FDT1_DATA) _CheckConfig(2, TEST_FDT2_DATA) + def testFitFdt(self): + """Test an image with an FIT with multiple FDT images""" + self.CheckFitFdt() + def testFitFdtMissingList(self): """Test handling of a missing 'of-list' entry arg""" with self.assertRaises(ValueError) as e: @@ -7605,6 +7610,10 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self.assertIn("Invalid U-Boot phase 'bad-phase': Use tpl/vpl/spl", str(e.exception)) + def testFitFdtListDir(self): + """Test an image with an FIT with FDT images using fit,fdt-list-dir""" + self.CheckFitFdt('333_fit_fdt_dir.dts', False) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/333_fit_fdt_dir.dts b/tools/binman/test/333_fit_fdt_dir.dts new file mode 100644 index 00000000000..aa778451a4b --- /dev/null +++ b/tools/binman/test/333_fit_fdt_dir.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + description = "test-desc"; + #address-cells = <1>; + fit,fdt-list-dir = "fdts"; + + images { + kernel { + description = "Vanilla Linux kernel"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot { + }; + }; + @fdt-SEQ { + description = "fdt-NAME.dtb"; + type = "flat_dt"; + compression = "none"; + hash { + algo = "sha256"; + }; + }; + }; + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "conf-NAME.dtb"; + firmware = "uboot"; + loadables = "atf"; + fdt = "fdt-SEQ"; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; -- cgit v1.2.3 From 57902e6941a0e914313afe50104fd0660ab47839 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:47 +0100 Subject: binman: fit: Write the compatible string to configuration FIT allows the FDT's root-node compatible string to be placed in a configuration node to simplify and speed up finding the best match for booting. Add a new property to support this. Signed-off-by: Simon Glass --- tools/binman/entries.rst | 5 +++ tools/binman/etype/fit.py | 12 +++++++ tools/binman/ftest.py | 55 +++++++++++++++++++++++++---- tools/binman/test/334_fit_fdt_compat.dts | 60 ++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 tools/binman/test/334_fit_fdt_compat.dts (limited to 'tools') diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index dcacd298c6d..fd8edc64fa2 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -939,6 +939,7 @@ You can create config nodes in a similar way:: firmware = "atf"; loadables = "uboot"; fdt = "fdt-SEQ"; + fit,compatible; // optional }; }; @@ -948,6 +949,10 @@ for each of your two files. Note that if no devicetree files are provided (with '-a of-list' as above) then no nodes will be generated. +The 'fit,compatible' property (if present) is replaced with the compatible +string from the root node of the devicetree, so that things work correctly +with FIT's configuration-matching algortihm. + Generating nodes from an ELF file (split-elf) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 8a25c784ef6..ab827d52066 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -171,6 +171,7 @@ class Entry_fit(Entry_section): firmware = "atf"; loadables = "uboot"; fdt = "fdt-SEQ"; + fit,compatible; }; }; @@ -180,6 +181,10 @@ class Entry_fit(Entry_section): Note that if no devicetree files are provided (with '-a of-list' as above) then no nodes will be generated. + The 'fit,compatible' property is replaced with the compatible string from + the root node of the devicetree, so that things work correctly with FIT's + configuration-matching algortihm. + Generating nodes from an ELF file (split-elf) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -611,6 +616,13 @@ class Entry_fit(Entry_section): fsw.property('loadables', val.encode('utf-8')) elif pname == 'fit,operation': pass + elif pname == 'fit,compatible': + fdt_phase = fdt_util.GetString(node, pname) + data = tools.read_file(fname) + fdt = Fdt.FromData(data) + fdt.Scan() + prop = fdt.GetRoot().props['compatible'] + fsw.property('compatible', prop.bytes) elif pname.startswith('fit,'): self._raise_subnode( node, f"Unknown directive '{pname}'") diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 6e1e1e9b50b..d930e353faf 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -7490,6 +7490,24 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err, "Image '.*' is missing external blobs and is non-functional: .*") + def SetupAlternateDts(self): + """Compile the .dts test files for alternative-fdt + + Returns: + tuple: + str: Test directory created + list of str: '.bin' files which we expect Binman to create + """ + testdir = TestFunctional._MakeInputDir('dtb') + dtb_list = [] + for fname in glob.glob(f'{self.TestFile("alt_dts")}/*.dts'): + tmp_fname = fdt_util.EnsureCompiled(fname, testdir) + base = os.path.splitext(os.path.basename(fname))[0] + dtb_list.append(base + '.bin') + shutil.move(tmp_fname, os.path.join(testdir, base + '.dtb')) + + return testdir, dtb_list + def CheckAlternates(self, dts, phase, xpl_data): """Run the test for the alterative-fdt etype @@ -7503,13 +7521,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap key: str filename value: Fdt object """ - testdir = TestFunctional._MakeInputDir('dtb') - dtb_list = [] - for fname in glob.glob(f'{self.TestFile("alt_dts")}/*.dts'): - tmp_fname = fdt_util.EnsureCompiled(fname, testdir) - base = os.path.splitext(os.path.basename(fname))[0] - dtb_list.append(base + '.bin') - shutil.move(tmp_fname, os.path.join(testdir, base + '.dtb')) + dtb_list = self.SetupAlternateDts()[1] entry_args = { f'{phase}-dtb': '1', @@ -7614,6 +7626,35 @@ fdt fdtmap Extract the devicetree blob from the fdtmap """Test an image with an FIT with FDT images using fit,fdt-list-dir""" self.CheckFitFdt('333_fit_fdt_dir.dts', False) + def testFitFdtCompat(self): + """Test an image with an FIT with compatible in the config nodes""" + entry_args = { + 'of-list': 'model1 model2', + 'default-dt': 'model2', + } + testdir, dtb_list = self.SetupAlternateDts() + data = self._DoReadFileDtb( + '334_fit_fdt_compat.dts', use_real_dtb=True, update_dtb=True, + entry_args=entry_args, extra_indirs=[testdir])[0] + + fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] + + fit = fdt.Fdt.FromData(fit_data) + fit.Scan() + + cnode = fit.GetNode('/configurations') + self.assertIn('default', cnode.props) + self.assertEqual('config-2', cnode.props['default'].value) + + for seq in range(1, 2): + name = f'config-{seq}' + fnode = fit.GetNode('/configurations/%s' % name) + self.assertIsNotNone(fnode) + self.assertIn('compatible', fnode.props.keys()) + expected = 'one' if seq == 1 else 'two' + self.assertEqual(f'u-boot,model-{expected}', + fnode.props['compatible'].value) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/334_fit_fdt_compat.dts b/tools/binman/test/334_fit_fdt_compat.dts new file mode 100644 index 00000000000..3bf45c710db --- /dev/null +++ b/tools/binman/test/334_fit_fdt_compat.dts @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + description = "test-desc"; + #address-cells = <1>; + fit,fdt-list = "of-list"; + + images { + kernel { + description = "Vanilla Linux kernel"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot { + }; + }; + @fdt-SEQ { + description = "fdt-NAME.dtb"; + type = "flat_dt"; + compression = "none"; + hash { + algo = "sha256"; + }; + }; + }; + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "conf-NAME.dtb"; + firmware = "uboot"; + loadables = "atf"; + fdt = "fdt-SEQ"; + fit,firmware = "vpl"; + fit,compatible; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; -- cgit v1.2.3 From 34d4a5df1be3708971d80d368446595af377a03f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:48 +0100 Subject: binman: fit: Allow running fdtgrep on devicetree blobs When using FIT to load firmware builds for multiple models, the FIT must include a common binary along with a number of devicetree blobs, one for each model. This is the same mechanism as is used for loading an OS. However, SPL builds do not normally use the full devicetree, but instead a cut-down version which various nodes and properties removed. Add a new fit,fdt-phase property to allow binman to produce these devicetree blobs. Signed-off-by: Simon Glass --- 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 +++++++++++++++++++++++++++++ 5 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 tools/binman/test/335_fit_fdt_phase.dts (limited to 'tools') diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index fd8edc64fa2..12482703782 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -953,6 +953,35 @@ The 'fit,compatible' property (if present) is replaced with the compatible string from the root node of the devicetree, so that things work correctly with FIT's configuration-matching algortihm. +Dealing with phases +~~~~~~~~~~~~~~~~~~~ + +FIT can be used to load firmware. In this case it may be necessary to run +the devicetree for each model through fdtgrep to remove unwanted properties. +The 'fit,fdt-phase' property can be provided to indicate the phase for which +the devicetree is intended. + +For example this indicates that the FDT should be processed for VPL:: + + images { + @fdt-SEQ { + description = "fdt-NAME"; + type = "flat_dt"; + compression = "none"; + fit,fdt-phase = "vpl"; + }; + }; + +Using this mechanism, it is possible to generate a FIT which can provide VPL +images for multiple models, with TPL selecting the correct model to use. The +same approach can of course be used for SPL images. + +Note that the `of-spl-remove-props` entryarg can be used to indicate +additional properties to remove. It is often used to remove properties like +`clock-names` and `pinctrl-names` which are not needed in SPL builds. + +See :ref:`fdtgrep_filter` for more information. + Generating nodes from an ELF file (split-elf) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index ab827d52066..38358292ece 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -171,7 +171,7 @@ class Entry_fit(Entry_section): firmware = "atf"; loadables = "uboot"; fdt = "fdt-SEQ"; - fit,compatible; + fit,compatible; // optional }; }; @@ -181,9 +181,38 @@ class Entry_fit(Entry_section): Note that if no devicetree files are provided (with '-a of-list' as above) then no nodes will be generated. - The 'fit,compatible' property is replaced with the compatible string from - the root node of the devicetree, so that things work correctly with FIT's - configuration-matching algortihm. + The 'fit,compatible' property (if present) is replaced with the compatible + string from the root node of the devicetree, so that things work correctly + with FIT's configuration-matching algortihm. + + Dealing with phases + ~~~~~~~~~~~~~~~~~~~ + + FIT can be used to load firmware. In this case it may be necessary to run + the devicetree for each model through fdtgrep to remove unwanted properties. + The 'fit,fdt-phase' property can be provided to indicate the phase for which + the devicetree is intended. + + For example this indicates that the FDT should be processed for VPL:: + + images { + @fdt-SEQ { + description = "fdt-NAME"; + type = "flat_dt"; + compression = "none"; + fit,fdt-phase = "vpl"; + }; + }; + + Using this mechanism, it is possible to generate a FIT which can provide VPL + images for multiple models, with TPL selecting the correct model to use. The + same approach can of course be used for SPL images. + + Note that the `of-spl-remove-props` entryarg can be used to indicate + additional properties to remove. It is often used to remove properties like + `clock-names` and `pinctrl-names` which are not needed in SPL builds. + + See :ref:`fdtgrep_filter` for more information. Generating nodes from an ELF file (split-elf) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -368,8 +397,14 @@ class Entry_fit(Entry_section): self._fdts = None self._fdt_dir = None self.mkimage = None + self.fdtgrep = None self._priv_entries = {} self._loadables = [] + self._remove_props = [] + props, = self.GetEntryArgsOrProps( + [EntryArg('of-spl-remove-props', str)], required=False) + if props: + self._remove_props = props.split() def ReadNode(self): super().ReadNode() @@ -505,6 +540,19 @@ class Entry_fit(Entry_section): rel_path = node.path[len(self._node.path) + 1:] self.Raise(f"subnode '{rel_path}': {msg}") + def _run_fdtgrep(self, infile, phase, outfile): + """Run fdtgrep to create the dtb for a phase + + Args: + infile (str): Input filename containing the full FDT contents (with + all nodes and properties) + phase (str): Phase to generate for ('tpl', 'vpl', 'spl') + outfile (str): Output filename to write the grepped FDT contents to + (with only neceesary nodes and properties) + """ + return self.fdtgrep.create_for_phase(infile, phase, outfile, + self._remove_props) + def _build_input(self): """Finish the FIT by adding the 'data' properties to it @@ -606,6 +654,7 @@ class Entry_fit(Entry_section): for seq, fdt_fname in enumerate(self._fdts): node_name = node.name[1:].replace('SEQ', str(seq + 1)) fname = tools.get_input_filename(fdt_fname + '.dtb') + fdt_phase = None with fsw.add_node(node_name): for pname, prop in node.props.items(): if pname == 'fit,firmware': @@ -623,6 +672,8 @@ class Entry_fit(Entry_section): fdt.Scan() prop = fdt.GetRoot().props['compatible'] fsw.property('compatible', prop.bytes) + elif pname == 'fit,fdt-phase': + fdt_phase = fdt_util.GetString(node, pname) elif pname.startswith('fit,'): self._raise_subnode( node, f"Unknown directive '{pname}'") @@ -635,7 +686,14 @@ class Entry_fit(Entry_section): # Add data for 'images' nodes (but not 'config') if depth == 1 and in_images: - fsw.property('data', tools.read_file(fname)) + if fdt_phase: + phase_fname = tools.get_output_filename( + f'{fdt_fname}-{fdt_phase}.dtb') + self._run_fdtgrep(fname, fdt_phase, phase_fname) + data = tools.read_file(phase_fname) + else: + data = tools.read_file(fname) + fsw.property('data', data) for subnode in node.subnodes: with fsw.add_node(subnode.name): @@ -863,6 +921,7 @@ class Entry_fit(Entry_section): def AddBintools(self, btools): super().AddBintools(btools) self.mkimage = self.AddBintool(btools, 'mkimage') + self.fdtgrep = self.AddBintool(btools, 'fdtgrep') def CheckMissing(self, missing_list): # We must use our private entry list for this since generator nodes diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index d930e353faf..93f3d22cf57 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -7655,6 +7655,41 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self.assertEqual(f'u-boot,model-{expected}', fnode.props['compatible'].value) + def testFitFdtPhase(self): + """Test an image with an FIT with fdt-phase in the fdt nodes""" + phase = 'tpl' + entry_args = { + f'{phase}-dtb': '1', + f'{phase}-bss-pad': 'y', + 'of-spl-remove-props': 'prop-to-remove another-prop-to-get-rid-of', + 'of-list': 'model1 model2', + 'default-dt': 'model2', + } + testdir, dtb_list = self.SetupAlternateDts() + data = self._DoReadFileDtb( + '335_fit_fdt_phase.dts', use_real_dtb=True, update_dtb=True, + entry_args=entry_args, extra_indirs=[testdir])[0] + fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] + fit = fdt.Fdt.FromData(fit_data) + fit.Scan() + + # Check that each FDT has only the expected properties for the phase + for seq in range(1, 2): + fnode = fit.GetNode(f'/images/fdt-{seq}') + self.assertIsNotNone(fnode) + dtb = fdt.Fdt.FromData(fnode.props['data'].bytes) + dtb.Scan() + + # Make sure that the 'bootph-pre-sram' tag in /node protects it from + # removal + node = dtb.GetNode('/node') + self.assertIsNotNone(node) + self.assertEqual({'some-prop', 'not-a-prop-to-remove'}, + node.props.keys()) + + # Make sure the other node is gone + self.assertIsNone(dtb.GetNode('/node/other-node')) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/main.py b/tools/binman/main.py index 92d2431aea7..dc817ddcd42 100755 --- a/tools/binman/main.py +++ b/tools/binman/main.py @@ -122,6 +122,8 @@ def RunBinman(args): ret_code = RunTests(args.debug, args.verbosity, args.processes, args.test_preserve_dirs, args.tests, args.toolpath) + if args.debug and not test_util.use_concurrent: + print('Tests can run in parallel: pip install concurrencytest') elif args.cmd == 'bintool-docs': control.write_bintool_docs(bintool.Bintool.get_tool_list()) diff --git a/tools/binman/test/335_fit_fdt_phase.dts b/tools/binman/test/335_fit_fdt_phase.dts new file mode 100644 index 00000000000..f8d0740a394 --- /dev/null +++ b/tools/binman/test/335_fit_fdt_phase.dts @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + description = "test-desc"; + #address-cells = <1>; + fit,fdt-list = "of-list"; + + images { + kernel { + description = "Vanilla Linux kernel"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot { + }; + }; + @fdt-SEQ { + description = "fdt-NAME.dtb"; + type = "flat_dt"; + compression = "none"; + fit,fdt-phase = "tpl"; + hash { + algo = "sha256"; + }; + }; + }; + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "conf-NAME.dtb"; + firmware = "uboot"; + loadables = "atf"; + fdt = "fdt-SEQ"; + fit,firmware = "tpl"; + fit,compatible; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; -- cgit v1.2.3 From 20e5399a311eb3aca5cebfd3e494047ed1ceacb8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 20 Jul 2024 11:49:50 +0100 Subject: Makefile: Pass OF_SPL_REMOVE_PROPS to binman 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 --- tools/binman/etype/fit.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools') 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. -- cgit v1.2.3