From a94e57ed620e506f9af1010372d3157dc4d94a83 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Fri, 16 Aug 2024 15:51:38 -0700 Subject: [PATCH 1/2] boot/zephyr: Support for loading images from arbitrary flash slots Instead of normal MCUboot flow, that checks flash slots to find update images, this patch enables an MCUboot application to chose from which available slots to load an image. This allows an application, for instance, to check hardware configuration at runtime (by checking hardware straps, state of GPIOs, etc) and decide from where to load an image to boot. MCUboot will basically loop through provided image sources (flash slots) and boot the first one that succed signature/validation. To provide the image sources, the application need to provide strong implementation for functions flash_map_id_get_next() and flash_map_id_get_current(). The default, weak, implementations just keep current behaviour for single loader, i.e. just load from FLASH_AREA_IMAGE_PRIMARY(0). Note that in this case, MCUboot won't try to record if image succeeded boot, it will only boot the image provided. Signed-off-by: Ederson de Souza Signed-off-by: Tom Burdick --- boot/zephyr/Kconfig | 12 +++ boot/zephyr/flash_map_extended.c | 11 ++- boot/zephyr/include/flash_runtime_sources.h | 46 +++++++++++ boot/zephyr/single_loader.c | 85 +++++++++++++++------ 4 files changed, 130 insertions(+), 24 deletions(-) create mode 100644 boot/zephyr/include/flash_runtime_sources.h diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index 7ff11ee90..64f8a1f61 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -383,6 +383,18 @@ config BOOT_IMAGE_EXECUTABLE_RAM_SIZE default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_SRAM),0) endif +config FLASH_RUNTIME_SOURCES + bool "Images are read from flash partitions defined at runtime" + select SINGLE_APPLICATION_SLOT + help + Instead of using information on the flash slots to decide which images + to load/update, the application provides the information from which + flash slot to load in runtime. This is useful when the application + reads the state for hardware straps or other sources to decide which + image to load. The application must provide `flash_map_id_get_next` + and `flash_map_id_get_next` functions to tell mcuboot where to find + the images. + config BOOT_ENCRYPTION_SUPPORT bool help diff --git a/boot/zephyr/flash_map_extended.c b/boot/zephyr/flash_map_extended.c index 4631da75b..c13d6adf0 100644 --- a/boot/zephyr/flash_map_extended.c +++ b/boot/zephyr/flash_map_extended.c @@ -58,6 +58,15 @@ int flash_device_base(uint8_t fd_id, uintptr_t *ret) */ int flash_area_id_from_multi_image_slot(int image_index, int slot) { +#if defined(CONFIG_FLASH_RUNTIME_SOURCES) + uint8_t id; + + if (flash_map_id_get_current(&id)) { + return id; + } + return -1; +#endif + switch (slot) { case 0: return FLASH_AREA_IMAGE_PRIMARY(image_index); #if !defined(CONFIG_SINGLE_APPLICATION_SLOT) @@ -141,7 +150,7 @@ int flash_area_sector_from_off(off_t off, struct flash_sector *sector) uint8_t flash_area_get_device_id(const struct flash_area *fa) { -#if defined(CONFIG_ARM) +#if defined(CONFIG_ARM) || defined(CONFIG_FLASH_RUNTIME_SOURCES) return fa->fa_id; #else (void)fa; diff --git a/boot/zephyr/include/flash_runtime_sources.h b/boot/zephyr/include/flash_runtime_sources.h new file mode 100644 index 000000000..44640b892 --- /dev/null +++ b/boot/zephyr/include/flash_runtime_sources.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FLASH_RUNTIME_SOURCES_H__ +#define __FLASH_RUNTIME_SOURCES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Get next flash map id. + * + * Implement this function to get the next flash map id. The function should + * return true if the flash map id was successfully updated. If the reset + * parameter is true, the function should reset the flash map id to the first + * one. + * + * @param id Pointer to the flash map id. + * @param reset If true, the function will reset the flash map id to the first + * one. + * @retval true If the flash map id was successfully updated. + */ +bool flash_map_id_get_next(uint8_t *id, bool reset); + +/* + * Get current flash map id. + * + * Implement this function to get the current flash map id. The function should + * return true if the flash map id was successfully read. + * + * @param id Pointer to the flash map id. + * @retval true If the flash map id was successfully read. + */ +bool flash_map_id_get_current(uint8_t *id); + +#ifdef __cplusplus +} +#endif + +#endif /* __FLASH_RUNTIME_SOURCES_H__ */ diff --git a/boot/zephyr/single_loader.c b/boot/zephyr/single_loader.c index 858f294ad..fb6223312 100644 --- a/boot/zephyr/single_loader.c +++ b/boot/zephyr/single_loader.c @@ -20,6 +20,30 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); static const struct flash_area *_fa_p; static struct image_header _hdr = { 0 }; +__weak bool +flash_map_id_get_next(uint8_t *id, bool reset) +{ + if (!reset || !id) { + return false; + } + + *id = FLASH_AREA_IMAGE_PRIMARY(0); + + return true; +} + +__weak bool +flash_map_id_get_current(uint8_t *id) +{ + if (!id) { + return false; + } + + *id = FLASH_AREA_IMAGE_PRIMARY(0); + + return true; +} + #if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) /** * Validate hash of a primary boot image. @@ -102,50 +126,65 @@ fih_ret boot_go(struct boot_rsp *rsp) { int rc = -1; + uint8_t flash_id; + bool reset = true; FIH_DECLARE(fih_rc, FIH_FAILURE); - rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p); - assert(rc == 0); + while (flash_map_id_get_next(&flash_id, reset)) { + reset = false; + rc = flash_area_open(flash_id, &_fa_p); + if (rc != 0) { + continue; + } - rc = boot_image_load_header(_fa_p, &_hdr); - if (rc != 0) - goto out; + rc = boot_image_load_header(_fa_p, &_hdr); + if (rc != 0) { + flash_area_close(_fa_p); + continue; + } #ifdef MCUBOOT_RAM_LOAD static struct boot_loader_state state; state.imgs[0][0].hdr = _hdr; rc = boot_load_image_to_sram(&state); - if (rc != 0) - goto out; + if (rc != 0) { + flash_area_close(_fa_p); + continue; + } #endif #ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT - FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr); - if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + flash_area_close(_fa_p); #ifdef MCUBOOT_RAM_LOAD - boot_remove_image_from_sram(&state); + boot_remove_image_from_sram(&state); #endif - goto out; - } + continue; + } #elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) - FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr); - if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + flash_area_close(_fa_p); #ifdef MCUBOOT_RAM_LOAD - boot_remove_image_from_sram(&state); + boot_remove_image_from_sram(&state); #endif - goto out; - } + continue; + } #else - fih_rc = FIH_SUCCESS; + fih_rc = FIH_SUCCESS; #endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ - rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p); - rsp->br_image_off = flash_area_get_off(_fa_p); - rsp->br_hdr = &_hdr; + rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p); + rsp->br_image_off = flash_area_get_off(_fa_p); + rsp->br_hdr = &_hdr; + + flash_area_close(_fa_p); + + break; + } -out: - flash_area_close(_fa_p); FIH_RET(fih_rc); } From ed0e9b9cc8c73834fe8edf1c1c1e9cd4e75adf63 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Wed, 7 Aug 2024 08:34:26 -0700 Subject: [PATCH 2/2] samples: Add a sample for runtime chosen image A sample for runtime chose image on FRDM K64F. It provides implementation for Zephyr flash_area_open_custom(), so the right flash map implementation is used, and MCUboot flash_map_id_get_next() and flash_map_id_get_current() to prioritize sources. It should show what is expected from an application to be able to use non-flash sources for images. In this sample, one can influence from which slot image will be loaded by pressing a button on the board. For more details on how to build and test the samples, check the provided README.md. Signed-off-by: Ederson de Souza Signed-off-by: Tom Burdick --- boot/zephyr/CMakeLists.txt | 6 ++ boot/zephyr/flash_map_extended.c | 4 ++ samples/runtime-source/zephyr/README.md | 56 +++++++++++++++++ .../runtime-source/zephyr/app/CMakeLists.txt | 14 +++++ .../zephyr/app/boards/frdm_k64f.overlay | 5 ++ samples/runtime-source/zephyr/app/prj.conf | 3 + samples/runtime-source/zephyr/app/src/main.c | 15 +++++ .../zephyr/flash_map_dispatcher.c | 60 +++++++++++++++++++ samples/runtime-source/zephyr/sample.conf | 4 ++ 9 files changed, 167 insertions(+) create mode 100644 samples/runtime-source/zephyr/README.md create mode 100644 samples/runtime-source/zephyr/app/CMakeLists.txt create mode 100644 samples/runtime-source/zephyr/app/boards/frdm_k64f.overlay create mode 100644 samples/runtime-source/zephyr/app/prj.conf create mode 100644 samples/runtime-source/zephyr/app/src/main.c create mode 100644 samples/runtime-source/zephyr/flash_map_dispatcher.c create mode 100644 samples/runtime-source/zephyr/sample.conf diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index ed490e6ee..a04dfcd80 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -481,6 +481,12 @@ if((CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE) AND (DEFINED zephyr_library_sources(flash_check.c) endif() +if(BUILD_RUNTIME_SOURCE_SAMPLE) + zephyr_library_sources( + ${MCUBOOT_DIR}/samples/runtime-source/zephyr/flash_map_dispatcher.c + ) +endif() + if(SYSBUILD) if(CONFIG_SINGLE_APPLICATION_SLOT OR CONFIG_BOOT_FIRMWARE_LOADER OR CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE OR CONFIG_BOOT_UPGRADE_ONLY OR CONFIG_BOOT_DIRECT_XIP OR CONFIG_BOOT_RAM_LOAD) # TODO: RAM LOAD support diff --git a/boot/zephyr/flash_map_extended.c b/boot/zephyr/flash_map_extended.c index c13d6adf0..ef11f34cd 100644 --- a/boot/zephyr/flash_map_extended.c +++ b/boot/zephyr/flash_map_extended.c @@ -14,6 +14,10 @@ #include #include +#if defined(CONFIG_FLASH_RUNTIME_SOURCES) +#include +#endif + #include "bootutil/bootutil_log.h" BOOT_LOG_MODULE_DECLARE(mcuboot); diff --git a/samples/runtime-source/zephyr/README.md b/samples/runtime-source/zephyr/README.md new file mode 100644 index 000000000..825858160 --- /dev/null +++ b/samples/runtime-source/zephyr/README.md @@ -0,0 +1,56 @@ +# Runtime chosen image sample application + +This sample demonstrates how to use a non flash storage to retrieve the image +being booted. It was tested on a FRDM K64F. Both slots are used to store two +different images. The image to be booted is selected based on a button press. + +## Build + +Build mcuboot. First, ensure ZEPHYR_SDK_INSTALL_DIR is defined. From the +mcuboot directory, run the following commands: + +``` + source /zephyr-env.sh + + west build -p -b frdm_k64f boot/zephyr/ -- -DBUILD_RUNTIME_SOURCE_SAMPLE=1 \ + -DEXTRA_CONF_FILE="../../samples/runtime-source/zephyr/sample.conf" + -DEXTRA_DTC_OVERLAY_FILE=../../samples/runtime-source/zephyr/boards/frdm_k64f.overlay + + west build -t flash +``` + +Then, build the sample application to be loaded. We need to build it twice, one +for each slot. From the sample +app directory (mcuboot/samples/non-flash-source/zephyr/app), run: + +``` + west build -p -b frdm_k64f . + west flash +``` + +Then change the overlay file to use the second slot. For instance, open +`boards/frdm_k64f.overlay` and change the line: + +``` + zephyr,code-partition = &slot0_partition; + +``` + +to: + +``` + zephyr,code-partition = &slot1_partition; +``` + +And build and flash again: + +``` + west build -b frdm_k64f . + west flash +``` + +## Run + +Open a serial terminal to see the output and reset the board. It shall boot the +image on slot0 by default. By keeping the SW2 button pressed during reset, the +bootloader will randomly select the image to be booted. diff --git a/samples/runtime-source/zephyr/app/CMakeLists.txt b/samples/runtime-source/zephyr/app/CMakeLists.txt new file mode 100644 index 000000000..bb60128f3 --- /dev/null +++ b/samples/runtime-source/zephyr/app/CMakeLists.txt @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(non_flash_backend_app) + +if (NOT DEFINED FROM_WHO) + set(FROM_WHO Zephyr) +endif() + +target_compile_definitions(app PRIVATE "-DMCUBOOT_HELLO_WORLD_FROM=\"${FROM_WHO}\"") + +target_sources(app PRIVATE src/main.c) diff --git a/samples/runtime-source/zephyr/app/boards/frdm_k64f.overlay b/samples/runtime-source/zephyr/app/boards/frdm_k64f.overlay new file mode 100644 index 000000000..8b642c6b6 --- /dev/null +++ b/samples/runtime-source/zephyr/app/boards/frdm_k64f.overlay @@ -0,0 +1,5 @@ +/ { + chosen { + zephyr,code-partition = &slot0_partition; + }; +}; diff --git a/samples/runtime-source/zephyr/app/prj.conf b/samples/runtime-source/zephyr/app/prj.conf new file mode 100644 index 000000000..bf0ea6a28 --- /dev/null +++ b/samples/runtime-source/zephyr/app/prj.conf @@ -0,0 +1,3 @@ +CONFIG_BOOTLOADER_MCUBOOT=y + +CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="./bootloader/mcuboot/root-rsa-2048.pem" diff --git a/samples/runtime-source/zephyr/app/src/main.c b/samples/runtime-source/zephyr/app/src/main.c new file mode 100644 index 000000000..a3ebbdca3 --- /dev/null +++ b/samples/runtime-source/zephyr/app/src/main.c @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2017 Linaro, Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +int main(void) +{ + printk("Hello World from %s on %s, slot %s!\n", + MCUBOOT_HELLO_WORLD_FROM, CONFIG_BOARD, + DT_PROP(DT_CHOSEN(zephyr_code_partition), label)); +} diff --git a/samples/runtime-source/zephyr/flash_map_dispatcher.c b/samples/runtime-source/zephyr/flash_map_dispatcher.c new file mode 100644 index 000000000..63e1feb88 --- /dev/null +++ b/samples/runtime-source/zephyr/flash_map_dispatcher.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define SW1_NODE DT_ALIAS(sw1) +#if DT_NODE_HAS_STATUS(SW1_NODE, okay) +static struct gpio_dt_spec sw1_spec = GPIO_DT_SPEC_GET(SW1_NODE, gpios); +#endif + +static int curr_idx = -1; + +static uint8_t known_ids[] = { + FIXED_PARTITION_ID(slot0_partition), + FIXED_PARTITION_ID(slot1_partition), +}; + +bool +flash_map_id_get_next(uint8_t *id, bool reset) +{ + if (reset) { + curr_idx = 0; +#if DT_NODE_HAS_STATUS(SW1_NODE, okay) + if (gpio_pin_configure_dt(&sw1_spec, GPIO_INPUT) == 0) { + if (gpio_pin_get_dt(&sw1_spec) == 1) { + curr_idx = sys_rand8_get() % ARRAY_SIZE(known_ids); + printk("Booting from curr_idx = %d\n", curr_idx); + } + } +#endif + } else { + curr_idx++; + } + + if (curr_idx >= ARRAY_SIZE(known_ids)) { + return false; + } + + *id = known_ids[curr_idx]; + + return true; +} + +bool +flash_map_id_get_current(uint8_t *id) +{ + if (curr_idx == -1 || curr_idx >= ARRAY_SIZE(known_ids)) { + return false; + } + + *id = known_ids[curr_idx]; + + return true; +} diff --git a/samples/runtime-source/zephyr/sample.conf b/samples/runtime-source/zephyr/sample.conf new file mode 100644 index 000000000..3a6b9819c --- /dev/null +++ b/samples/runtime-source/zephyr/sample.conf @@ -0,0 +1,4 @@ +CONFIG_FLASH_RUNTIME_SOURCES=y +CONFIG_BOOT_SIGNATURE_TYPE_RSA=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_ENTROPY_GENERATOR=y