For use on the STM32MP1, add a bootm handler for chainloading FIP images. This removes the last remaining use case for SSBL .stm32 images, after TF-A had dropped support late 2022.
Signed-off-by: Ahmad Fatoum <a.fat...@pengutronix.de> --- arch/arm/Kconfig | 11 ++ arch/arm/cpu/Makefile | 1 + arch/arm/cpu/bootm-fip.c | 276 +++++++++++++++++++++++++++++++++++ arch/arm/mach-stm32mp/init.c | 10 ++ include/fiptool.h | 13 ++ 5 files changed, 311 insertions(+) create mode 100644 arch/arm/cpu/bootm-fip.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ab7ff5369b20..84b1660d1ed9 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -473,6 +473,17 @@ config ARM_BOOTM_ELF help Add support to load elf file with bootm. +config ARM_BOOTM_FIP + bool + depends on BOOTM + select FIP + prompt "FIP loading support" + default ARCH_STM32MP + help + Add support to load FIP file with bootm. + This allows barebox to chainload barebox on platforms where it + is located inside a FIP. This can be useful during development. + config ARM_ATF bool diff --git a/arch/arm/cpu/Makefile b/arch/arm/cpu/Makefile index d59aae1ee55c..39e59c2a2f73 100644 --- a/arch/arm/cpu/Makefile +++ b/arch/arm/cpu/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_CMD_ARM_CPUINFO) += cpuinfo.o obj-$(CONFIG_MMUINFO) += mmuinfo.o mmuinfo_$(S64_32).o obj-$(CONFIG_OFDEVICE) += dtb.o obj-$(CONFIG_ARM_BOOTM_ELF) += bootm-elf.o +obj-$(CONFIG_ARM_BOOTM_FIP) += bootm-fip.o ifeq ($(CONFIG_MMU),) obj-$(CONFIG_CPU_32v7) += no-mmu.o diff --git a/arch/arm/cpu/bootm-fip.c b/arch/arm/cpu/bootm-fip.c new file mode 100644 index 000000000000..89201ade5f12 --- /dev/null +++ b/arch/arm/cpu/bootm-fip.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) "bootm-fip: " fmt + +#include <bootm.h> +#include <common.h> +#include <init.h> +#include <memory.h> +#include <linux/sizes.h> +#include <asm/boot.h> +#include <fip.h> +#include <libfile.h> +#include <bootm.h> +#include <fiptool.h> + +const struct fip_binding *fip_bindings; + +#define for_each_fip_binding(bindings, binding) \ + for (binding = bindings; !uuid_is_null(&binding->uuid); binding++) + +static inline struct resource *desc_get_res(struct fip_image_desc *desc) +{ + return desc->private_data; +} + +static inline void desc_set_res(struct fip_image_desc *desc, + struct resource *res) +{ + desc->private_data = res; +} + +static int desc_to_sdram(struct fip_image_desc *loadable, ulong load_address) +{ + struct resource *res; + + if (desc_get_res(loadable)) + return 0; + + res = request_sdram_region("fip", load_address, + loadable->image->toc_e.size); + if (!res) + return -EBUSY; + + memcpy((void *)res->start, + loadable->image->buffer, loadable->image->toc_e.size); + + desc_set_res(loadable, res); + + return 0; +} + +static void desc_release_sdram(struct fip_image_desc *loadable) +{ + struct resource *res = loadable ? desc_get_res(loadable) : NULL; + if (res) + release_sdram_region(res); +} + +enum { IMAGE_BL33, IMAGE_HW_CONFIG, IMAGE_COUNT }; + +static int parse_dtb_registry(struct fip_image_desc *fw_config_desc, + struct fip_image_desc *loadables[], + int verbose) +{ + struct fip_toc_entry *toc_entry = &fw_config_desc->image->toc_e; + struct device_node *fw_config, *fconf = NULL, *image, *tmp; + const struct fip_binding *binding; + void *buf; + int ret = 0; + + if (!fip_bindings) { + if (verbose) + printf("Platform registered no FIP bindings. Skipping firmware config\n"); + return 0; + } + + /* We have no alignment guarantees for the buffer, so we need to copy it */ + buf = xmemdup(fw_config_desc->image->buffer, toc_entry->size); + + fw_config = of_unflatten_dtb(buf, toc_entry->size); + if (!IS_ERR(fw_config)) + fconf = of_find_compatible_node(fw_config, NULL, + "fconf,dyn_cfg-dtb_registry"); + if (!fconf) { + pr_warn("error parsing fw_config devicetree\n"); + goto out; + } + + for_each_fip_binding(fip_bindings, binding) { + for_each_child_of_node_safe(fconf, tmp, image) { + u64 load_addr; + u32 id; + int ret; + + ret = of_property_read_u32(image, "id", &id); + if (ret) + continue; + + ret = of_property_read_u64(image, "load-address", &load_addr); + if (ret || load_addr > ULONG_MAX) + continue; + + if (id != binding->id) + continue; + + for (int i = 0; i < IMAGE_COUNT; i++) { + struct fip_image_desc *loadable = loadables[i]; + + if (!loadable) + continue; + + if (!uuid_equal(&binding->uuid, + &loadable->image->toc_e.uuid)) + continue; + + ret = desc_to_sdram(loadable, load_addr); + if (ret) { + pr_warn("load address 0x%08llx for image ID %u" + " conflicts with reservation\n", + load_addr, binding->id); + goto out; + } + + if (verbose > 1) + printf("loaded image ID %u to address 0x%08llx\n", + binding->id, load_addr); + break; + } + + /* No need to iterate over this node again, so drop it */ + of_delete_node(image); + } + } + +out: + if (!IS_ERR(fw_config)) + of_delete_node(fw_config); + free(buf); + return ret; +} + +static int fip_load(struct image_data *data, + struct fip_image_desc *fw_config_desc, + struct fip_image_desc *loadables[]) +{ + resource_size_t start, end; + int ret; + + if (UIMAGE_IS_ADDRESS_VALID(data->os_address)) { + ret = desc_to_sdram(loadables[IMAGE_BL33], data->os_address); + if (ret) + return ret; + } + + if (fw_config_desc) { + ret = parse_dtb_registry(fw_config_desc, loadables, data->verbose); + if (ret) + return ret; + } + + ret = memory_bank_first_find_space(&start, &end); + if (ret) + return ret; + + for (int i = 0; i < IMAGE_COUNT; i++) { + struct fip_image_desc *loadable = loadables[i]; + + if (!loadable || desc_get_res(loadable)) + continue; + + start = ALIGN(start, SZ_4K); + + ret = desc_to_sdram(loadable, start); + if (ret) + return ret; + + /* Leave a stack's worth of space after each artifact; + * The STM32MP1 barebox entry point depends on it. + */ + start += resource_size(desc_get_res(loadable)); + start += CONFIG_STACK_SIZE; + start = ALIGN(start, 16); + } + + return 0; +} + +static const uuid_t uuid_bl33 = UUID_NON_TRUSTED_FIRMWARE_BL33; +static const uuid_t uuid_hwconfig = UUID_HW_CONFIG; +static const uuid_t uuid_fwconfig = UUID_FW_CONFIG; + +static int do_bootm_fip(struct image_data *data) +{ + struct fip_image_desc *fwconfig = NULL; + struct fip_image_desc *loadables[IMAGE_COUNT] = {}; + struct fip_image_desc *desc; + struct fip_state *fip; + int ret; + + if (!data->os_file) + return -EINVAL; + + fip = fip_new(); + ret = fip_parse(fip, data->os_file, NULL); + if (ret) + goto out; + + fip_for_each_desc(fip, desc) { + struct fip_toc_entry *toc_entry = &desc->image->toc_e; + + if (uuid_equal(&toc_entry->uuid, &uuid_bl33)) + loadables[IMAGE_BL33] = desc; + else if (uuid_equal(&toc_entry->uuid, &uuid_hwconfig)) + loadables[IMAGE_HW_CONFIG] = desc; + else if (uuid_equal(&toc_entry->uuid, &uuid_fwconfig)) + fwconfig = desc; + } + + if (!loadables[IMAGE_BL33]) { + pr_err("FIP is missing BL33 image to chainload\n"); + ret = -ENOENT; + goto out; + } + + ret = fip_load(data, fwconfig, loadables); + if (ret) + goto out; + + if (data->verbose) { + printf("Loaded non-trusted firmware to 0x%08lx", + (ulong)desc_get_res(loadables[IMAGE_BL33])->start); + if (loadables[IMAGE_HW_CONFIG]) + printf(", hardware config to 0x%08lx", + (ulong)desc_get_res(loadables[IMAGE_HW_CONFIG])->start); + printf("\n"); + } + + if (data->dryrun) { + ret = 0; + goto out; + } + + shutdown_barebox(); + + /* We don't actually expect NT FW to be a Linux image, but + * we want to use the Linux boot calling convention + */ + jump_to_linux((void *)desc_get_res(loadables[IMAGE_BL33])->start, + desc_get_res(loadables[IMAGE_HW_CONFIG])->start); + + ret = -EIO; +out: + for (int i = 0; i < IMAGE_COUNT; i++) + desc_release_sdram(loadables[i]); + + if (fip) + fip_free(fip); + return ret; +} + +void plat_set_fip_bindings(const struct fip_binding *bindings) +{ + fip_bindings = bindings; +} + +struct image_handler fip_image_handler = { + .name = "FIP", + .bootm = do_bootm_fip, + .filetype = filetype_fip, +}; + +static int stm32mp_register_fip_bootm_handler(void) +{ + return register_image_handler(&fip_image_handler); +} +late_initcall(stm32mp_register_fip_bootm_handler); diff --git a/arch/arm/mach-stm32mp/init.c b/arch/arm/mach-stm32mp/init.c index 1c7f62dbb033..422218e18e5a 100644 --- a/arch/arm/mach-stm32mp/init.c +++ b/arch/arm/mach-stm32mp/init.c @@ -13,6 +13,7 @@ #include <mach/stm32mp/revision.h> #include <mach/stm32mp/bootsource.h> #include <bootsource.h> +#include <fiptool.h> #include <dt-bindings/pinctrl/stm32-pinfunc.h> /* Package = bit 27:29 of OTP16 @@ -236,6 +237,13 @@ unsigned stm32mp_soc_code(void) return __st32mp_soc; } +static const struct fip_binding stm32mp_fip_handler[] = { + { 1, UUID_FW_CONFIG }, + { 2, UUID_HW_CONFIG }, + { 5, UUID_NON_TRUSTED_FIRMWARE_BL33 }, + { /* sentinel */ } +}; + static int stm32mp_init(void) { u32 boot_ctx; @@ -264,6 +272,8 @@ static int stm32mp_init(void) setup_boot_mode(boot_ctx); + plat_set_fip_bindings(stm32mp_fip_handler); + return 0; } postcore_initcall(stm32mp_init); diff --git a/include/fiptool.h b/include/fiptool.h index 1a0dbe0e6975..8cb1f7d3959d 100644 --- a/include/fiptool.h +++ b/include/fiptool.h @@ -104,4 +104,17 @@ struct fip_state *fip_image_open(const char *filename, size_t offset); int fip_sha256(struct fip_state *fip, char *hash); +struct fip_binding { + unsigned id; + uuid_t uuid; +}; + +#ifdef CONFIG_ARM_BOOTM_FIP +void plat_set_fip_bindings(const struct fip_binding *bindings); +#else +static inline void plat_set_fip_bindings(const struct fip_binding *bindings) +{ +} +#endif + #endif /* FIPTOOL_H */ -- 2.39.5