A board may keep its devicetree and overlays in a FIT while booting a kernel or EFI application from elsewhere. Boot scripts need a way to verify and assemble that devicetree without asking bootm to boot the FIT.
Add fdt fit to prepare the devicetree described by one FIT configuration and copy it to an address chosen by the script. The result can then be passed to bootefi, booti or another boot command. Always check image hashes and enforce any required configuration signatures, even when verify=no. Reject chained configurations: signing each configuration separately does not authenticate the combination. Only copy the completed result after verification, overlay application and destination checks succeed. The script remains responsible for loading the FIT and stopping the boot on failure. Reviewed-by: Simon Glass <[email protected]> Signed-off-by: Carlo Caione <[email protected]> --- cmd/Kconfig | 9 ++++++ cmd/fdt.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/usage/cmd/fdt.rst | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) diff --git a/cmd/Kconfig b/cmd/Kconfig index 7124b961dc2..c81534810c5 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -577,6 +577,15 @@ config CMD_FDT help Do FDT related setup before booting into the Operating System. +config CMD_FDT_FIT + bool "fdt fit - load the devicetree of a FIT configuration" + depends on CMD_FDT && FIT + imply OF_LIBFDT_OVERLAY + help + Add the 'fdt fit' subcommand, which loads the devicetree of a FIT + configuration, applies the overlays the configuration lists and + copies the result to a given address. + config CMD_EXTENSION bool "Extension board management command" select CMD_FDT diff --git a/cmd/fdt.c b/cmd/fdt.c index 7e2abf96103..94b2376d072 100644 --- a/cmd/fdt.c +++ b/cmd/fdt.c @@ -11,6 +11,7 @@ #include <env.h> #include <fdt_support.h> #include <image.h> +#include <lmb.h> #include <malloc.h> #include <mapmem.h> #include <asm/global_data.h> @@ -145,6 +146,79 @@ static int fdt_get_header_value(int argc, char *const argv[]) return CMD_RET_FAILURE; } +/* + * fdt fit <addr>[#<conf>] <dest> [<length>] + * + * Load the devicetree of a FIT configuration, with its overlays applied, and + * copy it to <dest>. Images are always verified, so the configuration must + * also satisfy any required signature in the control devicetree. Unlike bootm, + * image load addresses are ignored and configurations cannot be chained. + */ +static int fdt_fit(int argc, char *const argv[]) +{ + struct bootm_headers images = { .verify = 1 }; + const char *conf = NULL; + ulong addr, dest, data, len; + bool owned; + void *fdt; + int ret; + + if (argc < 2 || argc > 3) + return CMD_RET_USAGE; + + if (!fit_parse_conf(argv[0], image_load_addr, &addr, &conf)) + addr = hextoul(argv[0], NULL); + dest = hextoul(argv[1], NULL); + + /* Only a single configuration authenticates the whole combination */ + if (conf && strchr(conf, '#')) { + puts("Chained configurations are not supported\n"); + return CMD_RET_FAILURE; + } + + if (boot_get_fdt_fit(&images, addr, NULL, &conf, IH_ARCH_DEFAULT, + FIT_LOAD_PREPARE, &data, &len, &owned) < 0) + return CMD_RET_FAILURE; + + fdt = map_sysmem(data, len); + ret = CMD_RET_FAILURE; + if (fdt_check_full(fdt, len)) { + puts("Invalid FDT in FIT\n"); + goto out; + } + + len = fdt_totalsize(fdt); + if (argc > 2) { + ulong newlen = hextoul(argv[2], NULL); + + if (newlen < len) { + printf("New length 0x%lX < existing length 0x%lX, aborting.\n", + newlen, len); + goto out; + } + len = newlen; + } + + if (CONFIG_IS_ENABLED(LMB) && lmb_read_check(dest, len)) { + puts("ERROR: trying to overwrite reserved memory...\n"); + goto out; + } + + ret = fdt_open_into(fdt, map_sysmem(dest, len), len); + if (ret) { + printf("libfdt fdt_open_into(): %s\n", fdt_strerror(ret)); + ret = CMD_RET_FAILURE; + goto out; + } + ret = CMD_RET_SUCCESS; + +out: + if (owned) + free(fdt); + + return ret; +} + /* * Flattened Device Tree command, see the help for parameter definitions. */ @@ -281,6 +355,10 @@ static int do_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) return CMD_RET_SUCCESS; } + /* Load the devicetree of a FIT configuration */ + if (IS_ENABLED(CONFIG_CMD_FDT_FIT) && strncmp(argv[1], "fi", 2) == 0) + return fdt_fit(argc - 2, argv + 2); + if (!working_fdt) { puts("No FDT memory address configured. Please configure\n" "the FDT address via \"fdt addr <address>\" command.\n" @@ -1124,6 +1202,11 @@ U_BOOT_LONGHELP(fdt, #endif #ifdef CONFIG_OF_SYSTEM_SETUP "fdt systemsetup - Do system-specific set up\n" +#endif +#ifdef CONFIG_CMD_FDT_FIT + "fdt fit <addr>[#<conf>] <dest> [<length>] - Load the fdt of a FIT\n" + " configuration, apply its overlays and\n" + " copy it to <dest>\n" #endif "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n" "fdt resize [<extrasize>] - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n" diff --git a/doc/usage/cmd/fdt.rst b/doc/usage/cmd/fdt.rst index 493ed895bc7..88222a9939d 100644 --- a/doc/usage/cmd/fdt.rst +++ b/doc/usage/cmd/fdt.rst @@ -12,6 +12,7 @@ Synopsis :: fdt addr [-cq] [addr [len]] + fdt fit <addr>[#<conf>] <dest> [<len>] Description ----------- @@ -48,6 +49,46 @@ If the `len` argument is provided, then the device tree is expanded to that size. This can be used to make space for more nodes and properties. It is assumed that there is enough space in memory for this expansion. +fdt fit +~~~~~~~ + +This loads the device tree of a FIT configuration and copies it to `dest`. The +FIT must already be in memory at `addr`. + +The configuration `conf` is selected as for the bootm command. If it is +omitted, the configuration matching the control FDT is used when +CONFIG_FIT_BEST_MATCH is enabled, otherwise the default configuration of the +FIT. The `fdt` property of the configuration lists a base device tree followed +by any overlays: the base device tree is loaded and the overlays are applied to +it in order. + +Each image is checked against its hashes, whatever the value of the `verify` +environment variable. With CONFIG_FIT_SIGNATURE, the configuration must also +satisfy the signature policy of the control FDT, so an unsigned configuration +is rejected when a key has `required = "conf"`. A configuration signature +covers the hashes of all the images listed by the configuration, so it +authenticates the base device tree, the overlays and their order as one unit. + +Unlike bootm, the command does not accept further configurations appended to +`conf` with `#`. Each of them would be verified on its own, so a chain could +combine device trees in a way which no configuration lists and nobody signed. +List each valid combination of a base device tree and overlays in its own +configuration instead. + +The command fails if any image cannot be loaded, verified or applied, and +nothing is written to `dest` in that case. Unlike bootm, the command ignores +FIT image `load` addresses: only the completed device tree is copied to +`dest`, after checking that the destination does not overlap reserved memory +when CONFIG_LMB is enabled. Alignment and decompression of the base device +tree are still handled as needed. + +If the `len` argument is provided, then the device tree is expanded to that +size, leaving space for later changes. The command fails if `len` is smaller +than the device tree. Without `len`, the device tree is copied at its own size. +The working FDT is not changed; use `fdt addr` to select the result. + +This subcommand requires CONFIG_CMD_FDT_FIT. + Example ------- @@ -66,3 +107,37 @@ address and expand it to 0xf000 in size:: Working FDT set to 10000 => md 10000 4 00010000: edfe0dd0 00f00000 78000000 7c270000 ...........x..'| + +Load a FIT holding a device tree and its overlays, then copy the device tree of +the `conf-a` configuration, a base device tree with one overlay, to 2000000 and +inspect it:: + + => load mmc 0#firmware 1000000 fdt.itb + 1771 bytes read in 1 ms (1.7 MiB/s) + => fdt fit 1000000#conf-a 2000000 + ## Loading fdt (any) from FIT Image at 01000000 ... + Using 'conf-a' configuration + Verifying Hash Integrity ... OK + Trying 'base' fdt subimage + ... + Verifying Hash Integrity ... sha256+ OK + ## Loading fdt (any) from FIT Image at 01000000 ... + Trying 'ov-a' fdt subimage + ... + Verifying Hash Integrity ... sha256+ OK + => fdt addr 2000000 + Working FDT set to 2000000 + => fdt print /test-node + test-node { + ov-a-prop = "ov-a-value"; + base-prop = "base-value"; + order = "ov-a"; + }; + +A boot script can prepare a device tree kept with the firmware in this way and +pass it to the boot command, stopping if any step fails:: + + if load mmc 0#firmware ${loadaddr} fdt.itb && + fdt fit ${loadaddr}#conf-a ${fdt_addr_r}; then + bootefi bootmgr ${fdt_addr_r} + fi -- 2.56.0
