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/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..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); @@ -58,6 +62,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 +154,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); } 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