From 57f882c8db202172dfe9ba5ac9a9e0c36eead616 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Tue, 4 Feb 2025 10:14:31 -0800 Subject: [PATCH 1/7] boot/zephyr: Fix SINGLE_APPLICATION_SLOT_RAM_LOAD file inclusion Tweak if/else chain so that: - single_loader.c and ram_load.c are included if CONFIG_SINGLE_APPLICATION_SLOT_RAM_LOAD=y. - single_loader.c is included if CONFIG_SINGLE_APPLICATION_SLOT=y - ram_load.c is included if CONFIG_BOOT_RAM_LOAD=y and CONFIG_SINGLE_APPLICATION_SLOT_RAM_LOAD=n and CONFIG_SINGLE_APPLICATION_SLOT=n. Without this patch, having both CONFIG_SINGLE_APPLICATION_SLOT_RAM_LOAD and CONFIG_SINGLE_APPLICATION_SLOT would not include ram_load.c. Signed-off-by: Ederson de Souza --- boot/zephyr/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index d8bef2f21f..b4d4860a95 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -125,15 +125,15 @@ zephyr_library_sources( ) endif() -if(CONFIG_SINGLE_APPLICATION_SLOT) +if(CONFIG_SINGLE_APPLICATION_SLOT_RAM_LOAD) zephyr_library_sources( ${BOOT_DIR}/zephyr/single_loader.c + ${BOOT_DIR}/bootutil/src/ram_load.c ) zephyr_library_include_directories(${BOOT_DIR}/bootutil/src) -elseif(CONFIG_SINGLE_APPLICATION_SLOT OR CONFIG_SINGLE_APPLICATION_SLOT_RAM_LOAD) +elseif(CONFIG_SINGLE_APPLICATION_SLOT) zephyr_library_sources( ${BOOT_DIR}/zephyr/single_loader.c - ${BOOT_DIR}/bootutil/src/ram_load.c ) zephyr_library_include_directories(${BOOT_DIR}/bootutil/src) elseif(CONFIG_BOOT_FIRMWARE_LOADER) @@ -161,7 +161,7 @@ else() ${BOOT_DIR}/bootutil/src/swap_scratch.c ) - if(CONFIG_BOOT_RAM_LOAD OR CONFIG_SINGLE_APPLICATION_SLOT_RAM_LOAD) + if(CONFIG_BOOT_RAM_LOAD) zephyr_library_sources( ${BOOT_DIR}/bootutil/src/ram_load.c ) From a6f22e8eb487067027458e2af8164043f927b4f8 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Fri, 20 Dec 2024 12:34:19 -0800 Subject: [PATCH 2/7] boot/bootutil: Get bootloader state Add `boot_get_loader_state()` to allow one have a reference to current bootloader state. While this state is internal - and struct is opaque - it will be important to be able to "pass it around" when using hooks that will be introduced in future patches. While at it, make Zephyr single loader also expose it. Signed-off-by: Ederson de Souza --- boot/bootutil/include/bootutil/bootutil.h | 2 +- boot/bootutil/include/bootutil/bootutil_public.h | 2 ++ boot/bootutil/src/loader.c | 5 +++++ boot/zephyr/single_loader.c | 6 ++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/boot/bootutil/include/bootutil/bootutil.h b/boot/bootutil/include/bootutil/bootutil.h index cea5689b9b..babb62fdc8 100644 --- a/boot/bootutil/include/bootutil/bootutil.h +++ b/boot/bootutil/include/bootutil/bootutil.h @@ -86,9 +86,9 @@ struct image_max_size { fih_ret boot_go(struct boot_rsp *rsp); fih_ret boot_go_for_image_id(struct boot_rsp *rsp, uint32_t image_id); -struct boot_loader_state; void boot_state_clear(struct boot_loader_state *state); fih_ret context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp); +struct boot_loader_state *boot_get_loader_state(void); const struct image_max_size *boot_get_max_app_size(void); uint32_t boot_get_state_secondary_offset(struct boot_loader_state *state, const struct flash_area *fap); diff --git a/boot/bootutil/include/bootutil/bootutil_public.h b/boot/bootutil/include/bootutil/bootutil_public.h index ffd263591a..49c4725a71 100644 --- a/boot/bootutil/include/bootutil/bootutil_public.h +++ b/boot/bootutil/include/bootutil/bootutil_public.h @@ -145,6 +145,8 @@ _Static_assert(MCUBOOT_BOOT_MAX_ALIGN >= 8 && MCUBOOT_BOOT_MAX_ALIGN <= 32, #endif #endif +struct boot_loader_state; + struct boot_swap_state { uint8_t magic; /* One of the BOOT_MAGIC_[...] values. */ uint8_t swap_type; /* One of the BOOT_SWAP_TYPE_[...] values. */ diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index 43dcd3f22f..4efbe719af 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -118,6 +118,11 @@ static struct sector_buffer_t sector_buffers; boot_copy_region(state, fap_pri, fap_sec, pri_off, sec_off, sz) #endif +struct boot_loader_state *boot_get_loader_state(void) +{ + return &boot_data; +} + static int boot_read_image_headers(struct boot_loader_state *state, bool require_all, struct boot_status *bs) diff --git a/boot/zephyr/single_loader.c b/boot/zephyr/single_loader.c index f4122783ca..382f81eaa4 100644 --- a/boot/zephyr/single_loader.c +++ b/boot/zephyr/single_loader.c @@ -21,6 +21,12 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); /* Variables passed outside of unit via poiters. */ static const struct flash_area *_fa_p; static struct image_header _hdr = { 0 }; +static struct boot_loader_state boot_data; + +struct boot_loader_state *boot_get_loader_state(void) +{ + return &boot_data; +} #if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) /** From b04a37ca95d06eb5d144bd3fa9ee395d7dc97c1a Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Fri, 20 Dec 2024 12:26:51 -0800 Subject: [PATCH 3/7] boot/bootutil: Make RAM loading methods public RAM loading methods are currently private, which prevents external modules with hooks from using them. This patch makes them public. An interesting note is that a new method, `boot_load_image_from_flash_to_sram()`, is added so that code can actually set the image to be loaded - currently, that is inferred from current boot loader state, but external code can't directly access members of relevant `struct boot_loader_state`. Signed-off-by: Ederson de Souza --- .../include/bootutil/bootutil_public.h | 35 +++++++++++++++++++ boot/bootutil/src/bootutil_priv.h | 3 -- boot/bootutil/src/ram_load.c | 14 ++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/boot/bootutil/include/bootutil/bootutil_public.h b/boot/bootutil/include/bootutil/bootutil_public.h index 49c4725a71..bbfb729a3c 100644 --- a/boot/bootutil/include/bootutil/bootutil_public.h +++ b/boot/bootutil/include/bootutil/bootutil_public.h @@ -311,6 +311,41 @@ int boot_image_load_header(const struct flash_area *fa_p, struct image_header *hdr); +#ifdef MCUBOOT_RAM_LOAD +/** + * Loads image with given header to RAM. + * + * Destination on RAM and size is described on image header. + * + * @param[in] state boot loader state + * @param[in] hdr image header + * + * @return 0 on success, error code otherwise + */ +int boot_load_image_from_flash_to_sram(struct boot_loader_state *state, + struct image_header *hdr); + +/** + * Removes an image from SRAM, by overwriting it with zeros. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +int boot_remove_image_from_sram(struct boot_loader_state *state); + +/** + * Removes an image from flash by erasing the corresponding flash area + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be erased. + * + * @return 0 on success; nonzero on failure. + */ +int boot_remove_image_from_flash(struct boot_loader_state *state, + uint32_t slot); +#endif + #ifdef __cplusplus } #endif diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h index bb8a79cc41..01ec23fc12 100644 --- a/boot/bootutil/src/bootutil_priv.h +++ b/boot/bootutil/src/bootutil_priv.h @@ -503,9 +503,6 @@ struct bootsim_ram_info *bootsim_get_ram_info(void); (size)), 0) int boot_load_image_to_sram(struct boot_loader_state *state); -int boot_remove_image_from_sram(struct boot_loader_state *state); -int boot_remove_image_from_flash(struct boot_loader_state *state, - uint32_t slot); #else #define IMAGE_RAM_BASE ((uintptr_t)0) diff --git a/boot/bootutil/src/ram_load.c b/boot/bootutil/src/ram_load.c index dd74052a36..630af11709 100644 --- a/boot/bootutil/src/ram_load.c +++ b/boot/bootutil/src/ram_load.c @@ -438,3 +438,17 @@ boot_remove_image_from_flash(struct boot_loader_state *state, uint32_t slot) return rc; } + +int boot_load_image_from_flash_to_sram(struct boot_loader_state *state, + struct image_header *hdr) +{ + int active_slot; + + /* boot_load_image_to_sram will load the image from state active_slot, + * so force it before loading the image. + */ + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + BOOT_IMG(state, active_slot).hdr = *hdr; + + return boot_load_image_to_sram(state); +} From ae5963bf449efaf7f993d6eb7957f24fe4428cea Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Mon, 23 Dec 2024 11:45:57 -0800 Subject: [PATCH 4/7] boot: New boot_go hook This new hook allows external modules to override the choice of which image to boot - it can be used, for instance, to load an image based on some GPIO state. If the hook returns FIH_BOOT_HOOK_REGULAR, then normal platform boot_go() will be run to choose image to boot. This hook is shielded by a different configuration than other hooks - MCUBOOT_BOOT_GO_HOOKS, so that it can be enabled independently from other available hooks. Support for this new hook was added to Zephyr port. Signed-off-by: Ederson de Souza --- boot/bootutil/include/bootutil/boot_hooks.h | 58 ++++++++++++++++--- boot/zephyr/Kconfig | 7 +++ .../include/mcuboot_config/mcuboot_config.h | 4 ++ boot/zephyr/main.c | 6 +- 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/boot/bootutil/include/bootutil/boot_hooks.h b/boot/bootutil/include/bootutil/boot_hooks.h index 6d4a34e87a..4cbc7d5f5c 100644 --- a/boot/bootutil/include/bootutil/boot_hooks.h +++ b/boot/bootutil/include/bootutil/boot_hooks.h @@ -34,25 +34,54 @@ #ifndef H_BOOTUTIL_HOOKS #define H_BOOTUTIL_HOOKS -#ifdef MCUBOOT_IMAGE_ACCESS_HOOKS +#include "bootutil/bootutil.h" +#include "bootutil/fault_injection_hardening.h" -#define BOOT_HOOK_CALL(f, ret_default, ...) f(__VA_ARGS__) +#define DO_HOOK_CALL(f, ret_default, ...) \ + f(__VA_ARGS__) -#define BOOT_HOOK_CALL_FIH(f, fih_ret_default, fih_rc, ...) \ +#define DO_HOOK_CALL_FIH(f, fih_ret_default, fih_rc, ...) \ do { \ FIH_CALL(f, fih_rc, __VA_ARGS__); \ } while(0); -#else - -#define BOOT_HOOK_CALL(f, ret_default, ...) ret_default +#define HOOK_CALL_NOP(f, ret_default, ...) ret_default -#define BOOT_HOOK_CALL_FIH(f, fih_ret_default, fih_rc, ...) \ +#define HOOK_CALL_FIH_NOP(f, fih_ret_default, fih_rc, ...) \ do { \ fih_rc = fih_ret_default; \ } while(0); -#endif +#ifdef MCUBOOT_IMAGE_ACCESS_HOOKS + +#define BOOT_HOOK_CALL(f, ret_default, ...) \ + DO_HOOK_CALL(f, ret_default, __VA_ARGS__) + +#define BOOT_HOOK_CALL_FIH(f, fih_ret_default, fih_rc, ...) \ + DO_HOOK_CALL_FIH(f, fih_ret_default, fih_rc, __VA_ARGS__) + +#else + +#define BOOT_HOOK_CALL(f, ret_default, ...) \ + HOOK_CALL_NOP(f, ret_default, __VA_ARGS__) + +#define BOOT_HOOK_CALL_FIH(f, fih_ret_default, fih_rc, ...) \ + HOOK_CALL_FIH_NOP(f, fih_ret_default, fih_rc, __VA_ARGS__) + +#endif /* MCUBOOT_IMAGE_ACCESS_HOOKS */ + +#ifdef MCUBOOT_BOOT_GO_HOOKS + +#define BOOT_HOOK_GO_CALL_FIH(f, fih_ret_default, fih_rc, ...) \ + DO_HOOK_CALL_FIH(f, fih_ret_default, fih_rc, __VA_ARGS__); + +#else + +#define BOOT_HOOK_GO_CALL_FIH(f, fih_ret_default, fih_rc, ...) \ + HOOK_CALL_FIH_NOP(f, fih_ret_default, fih_rc, __VA_ARGS__) + +#endif /* MCUBOOT_BOOT_GO_HOOKS */ + /** Hook for provide image header data. * @@ -173,6 +202,19 @@ int boot_img_install_stat_hook(int image_index, int slot, */ int boot_reset_request_hook(bool force); +/** + * Hook to implement custom action before boot_go() function. + * + * @param rsp boot response structure. + * + * @retval FIH_SUCCESS: boot_go() should be skipped, boot response is already + * filled. + * FIH_FAILURE: boot_go() should be skipped, boot response is already + * filled with error. + * FIH_BOOT_HOOK_REGULAR: follow the normal execution path. + */ +fih_ret boot_go_hook(struct boot_rsp *rsp); + #define BOOT_RESET_REQUEST_HOOK_BUSY 1 #define BOOT_RESET_REQUEST_HOOK_TIMEOUT 2 #define BOOT_RESET_REQUEST_HOOK_CHECK_FAILED 3 diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index f4b9a55f78..1bb27786ff 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -855,6 +855,13 @@ config BOOT_IMAGE_ACCESS_HOOKS update. It is up to the project customization to add required source files to the build. +config BOOT_GO_HOOKS + bool "Enable hooks for overriding MCUBOOT's boot_go routine" + help + Allow to provide procedures for override or extend native + MCUboot's boot_go routine. It is up to the project customization to + add required source files to the build. + config MCUBOOT_ACTION_HOOKS bool "Enable hooks for responding to MCUboot status changes" help diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index dc49c66880..e568dee7b6 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -242,6 +242,10 @@ #define MCUBOOT_IMAGE_ACCESS_HOOKS #endif +#ifdef CONFIG_BOOT_GO_HOOKS +#define MCUBOOT_BOOT_GO_HOOKS +#endif + #ifdef CONFIG_MCUBOOT_VERIFY_IMG_ADDRESS #define MCUBOOT_VERIFY_IMG_ADDRESS #endif diff --git a/boot/zephyr/main.c b/boot/zephyr/main.c index c84f18a1db..683b2f7f5f 100644 --- a/boot/zephyr/main.c +++ b/boot/zephyr/main.c @@ -41,6 +41,7 @@ #include "bootutil/bootutil_log.h" #include "bootutil/image.h" #include "bootutil/bootutil.h" +#include "bootutil/boot_hooks.h" #include "bootutil/fault_injection_hardening.h" #include "bootutil/mcuboot_status.h" #include "flash_map_backend/flash_map_backend.h" @@ -525,7 +526,10 @@ int main(void) #endif #endif - FIH_CALL(boot_go, fih_rc, &rsp); + BOOT_HOOK_GO_CALL_FIH(boot_go_hook, FIH_BOOT_HOOK_REGULAR, fih_rc, &rsp); + if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR)) { + FIH_CALL(boot_go, fih_rc, &rsp); + } #ifdef CONFIG_BOOT_SERIAL_BOOT_MODE if (io_detect_boot_mode()) { From 6d59eaa0aadc54eec88eb3116cbd8a0e8a071546 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Fri, 20 Dec 2024 13:00:13 -0800 Subject: [PATCH 5/7] boot: Add flash area ID/device ID retrieval hooks For users that customise (for instance, via boot_go() hook) from where images are being loaded from, having those hardcoded to `slot_partitionX` (when using Zephyr) can be problematic. New hooks, `flash_area_id_from_multi_image_slot_hook` and `flash_area_get_device_id_hook` allow some customisation around these processes. Support for these hooks is shielded by a different configuration than other hooks - MCUBOOT_FLASH_AREA_HOOKS, so that it can be enabled independently from other available hooks. Support for this new hook was added to the Zephyr port. Signed-off-by: Ederson de Souza --- boot/bootutil/include/bootutil/boot_hooks.h | 40 +++++++++++++++++++ boot/zephyr/Kconfig | 18 +++++++++ boot/zephyr/flash_map_extended.c | 19 +++++++++ .../include/mcuboot_config/mcuboot_config.h | 4 ++ 4 files changed, 81 insertions(+) diff --git a/boot/bootutil/include/bootutil/boot_hooks.h b/boot/bootutil/include/bootutil/boot_hooks.h index 4cbc7d5f5c..ef7a89b289 100644 --- a/boot/bootutil/include/bootutil/boot_hooks.h +++ b/boot/bootutil/include/bootutil/boot_hooks.h @@ -82,6 +82,17 @@ #endif /* MCUBOOT_BOOT_GO_HOOKS */ +#ifdef MCUBOOT_FLASH_AREA_HOOKS + +#define BOOT_HOOK_FLASH_AREA_CALL(f, ret_default, ...) \ + DO_HOOK_CALL(f, ret_default, __VA_ARGS__) + +#else + +#define BOOT_HOOK_FLASH_AREA_CALL(f, ret_default, ...) \ + HOOK_CALL_NOP(f, ret_default, __VA_ARGS__) + +#endif /* MCUBOOT_FLASH_AREA_ID_HOOKS */ /** Hook for provide image header data. * @@ -215,6 +226,35 @@ int boot_reset_request_hook(bool force); */ fih_ret boot_go_hook(struct boot_rsp *rsp); +/** + * Hook to implement custom action before retrieving flash area ID. + * + * @param image_index the index of the image pair + * @param slot slot number + * @param area_id the flash area ID to be populated + * + * @retval 0 the flash area ID was fetched successfully; + * BOOT_HOOK_REGULAR follow the normal execution path to get the flash + * area ID; + * otherwise an error-code value. + */ +int flash_area_id_from_multi_image_slot_hook(int image_index, int slot, + int *area_id); + +/** + * Hook to implement custom action before retrieving flash area device ID. + * + * @param fa the flash area structure + * @param device_id the device ID to be populated + * + * @retval 0 the device ID was fetched successfully; + * BOOT_HOOK_REGULAR follow the normal execution path to get the device + * ID; + * otherwise an error-code value. + */ +int flash_area_get_device_id_hook(const struct flash_area *fa, + uint8_t *device_id); + #define BOOT_RESET_REQUEST_HOOK_BUSY 1 #define BOOT_RESET_REQUEST_HOOK_TIMEOUT 2 #define BOOT_RESET_REQUEST_HOOK_CHECK_FAILED 3 diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index 1bb27786ff..9afd292b01 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -472,6 +472,17 @@ 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" + depends on 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. Usually, application will provide a boot_go_hook() to + decide which image to load. + config BOOT_ENCRYPTION_SUPPORT bool help @@ -862,6 +873,13 @@ config BOOT_GO_HOOKS MCUboot's boot_go routine. It is up to the project customization to add required source files to the build. +config BOOT_FLASH_AREA_HOOKS + bool "Enable hooks for overriding MCUboot's flash area routines" + help + Allow to provide procedures for override or extend native + MCUboot's flash area routines. It is up to the project customization to + add required source files to the build. + config MCUBOOT_ACTION_HOOKS bool "Enable hooks for responding to MCUboot status changes" help diff --git a/boot/zephyr/flash_map_extended.c b/boot/zephyr/flash_map_extended.c index bf0d8f191c..4a29750f75 100644 --- a/boot/zephyr/flash_map_extended.c +++ b/boot/zephyr/flash_map_extended.c @@ -14,7 +14,9 @@ #include #include +#include "bootutil/boot_hooks.h" #include "bootutil/bootutil_log.h" +#include "bootutil/bootutil_public.h" BOOT_LOG_MODULE_DECLARE(mcuboot); @@ -58,6 +60,14 @@ int flash_device_base(uint8_t fd_id, uintptr_t *ret) */ int flash_area_id_from_multi_image_slot(int image_index, int slot) { + int rc, id; + + rc = BOOT_HOOK_FLASH_AREA_CALL(flash_area_id_from_multi_image_slot_hook, + BOOT_HOOK_REGULAR, image_index, slot, &id); + if (rc != BOOT_HOOK_REGULAR) { + return id; + } + switch (slot) { case 0: return FLASH_AREA_IMAGE_PRIMARY(image_index); #if !defined(CONFIG_SINGLE_APPLICATION_SLOT) @@ -138,6 +148,15 @@ 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) { + uint8_t device_id; + int rc; + + rc = BOOT_HOOK_FLASH_AREA_CALL(flash_area_get_device_id_hook, + BOOT_HOOK_REGULAR, fa, &device_id); + if (rc != BOOT_HOOK_REGULAR) { + return device_id; + } + #if defined(CONFIG_ARM) return fa->fa_id; #else diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index e568dee7b6..b5fcf82b42 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -246,6 +246,10 @@ #define MCUBOOT_BOOT_GO_HOOKS #endif +#ifdef CONFIG_BOOT_FLASH_AREA_HOOKS +#define MCUBOOT_FLASH_AREA_HOOKS +#endif + #ifdef CONFIG_MCUBOOT_VERIFY_IMG_ADDRESS #define MCUBOOT_VERIFY_IMG_ADDRESS #endif From b9877276be2074a0bb67d3795a453a2846473cbd Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Wed, 7 Aug 2024 08:34:26 -0700 Subject: [PATCH 6/7] samples: Add a sample for runtime chosen image A sample for runtime chose image on FRDM K64F. It provides implementation for boot_go and flash area id related hooks, showing how an external module implementing hooks can influence which images end up being booted on the platform. 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 --- samples/runtime-source/zephyr/README.md | 55 +++++++++ .../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/hooks/CMakeLists.txt | 3 + samples/runtime-source/zephyr/hooks/hooks.c | 114 ++++++++++++++++++ .../zephyr/hooks/zephyr/module.yml | 3 + samples/runtime-source/zephyr/sample.conf | 8 ++ 9 files changed, 220 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/hooks/CMakeLists.txt create mode 100644 samples/runtime-source/zephyr/hooks/hooks.c create mode 100644 samples/runtime-source/zephyr/hooks/zephyr/module.yml create mode 100644 samples/runtime-source/zephyr/sample.conf diff --git a/samples/runtime-source/zephyr/README.md b/samples/runtime-source/zephyr/README.md new file mode 100644 index 0000000000..e30c0b9064 --- /dev/null +++ b/samples/runtime-source/zephyr/README.md @@ -0,0 +1,55 @@ +# 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 +sample directory, run the following commands: + +``` + source /zephyr-env.sh + + west build -p -b frdm_k64f ../../../boot/zephyr/ -- \ + -DEXTRA_ZEPHYR_MODULES=$PWD/hooks -DEXTRA_CONF_FILE="$PWD/sample.conf" + + 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 boot the one on slot1. diff --git a/samples/runtime-source/zephyr/app/CMakeLists.txt b/samples/runtime-source/zephyr/app/CMakeLists.txt new file mode 100644 index 0000000000..63d893340c --- /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 0000000000..364d7e35e3 --- /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 0000000000..bf0ea6a28e --- /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 0000000000..03e16e2cf3 --- /dev/null +++ b/samples/runtime-source/zephyr/app/src/main.c @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * 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/hooks/CMakeLists.txt b/samples/runtime-source/zephyr/hooks/CMakeLists.txt new file mode 100644 index 0000000000..d7d05669b6 --- /dev/null +++ b/samples/runtime-source/zephyr/hooks/CMakeLists.txt @@ -0,0 +1,3 @@ +zephyr_library() +zephyr_library_sources(hooks.c) +zephyr_library_link_libraries(MCUBOOT_BOOTUTIL) diff --git a/samples/runtime-source/zephyr/hooks/hooks.c b/samples/runtime-source/zephyr/hooks/hooks.c new file mode 100644 index 0000000000..849ce80141 --- /dev/null +++ b/samples/runtime-source/zephyr/hooks/hooks.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "bootutil/bootutil.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/fault_injection_hardening.h" + +#define BOOT_TMPBUF_SZ 256 + +static struct image_header _hdr; +static uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + +static uint8_t known_ids[] = { + FIXED_PARTITION_ID(slot0_partition), + FIXED_PARTITION_ID(slot1_partition), +}; + +static int current_id; + +#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 + +fih_ret boot_go_hook(struct boot_rsp *rsp) +{ + int rc; +#ifdef MCUBOOT_RAM_LOAD + struct boot_loader_state *state; +#endif + FIH_DECLARE(fih_rc, FIH_FAILURE); + const struct flash_area *_fa_p; + + current_id = 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) { + current_id = ARRAY_SIZE(known_ids) - 1; + printk("%s pressed, forcing boot from partition %u\n", + sw1_spec.port->name, known_ids[current_id]); + } else { + printk("%s not pressed, looping partitions to boot\n", + sw1_spec.port->name); + } + } +#else + printk("SW1 not defined, looping partitions to boot\n"); +#endif + + for ( ; current_id < ARRAY_SIZE(known_ids); current_id++) { + printk("Trying to boot from fixed partition %u\n", + known_ids[current_id]); + + rc = flash_area_open(known_ids[current_id], &_fa_p); + if (rc != 0) { + continue; + } + + rc = boot_image_load_header(_fa_p, &_hdr); + if (rc != 0) { + flash_area_close(_fa_p); + continue; + } + +#ifdef MCUBOOT_RAM_LOAD + state = boot_get_loader_state(); + + rc = boot_load_image_from_flash_to_sram(state, &_hdr); + if (rc != 0) { + flash_area_close(_fa_p); + continue; + } +#endif + + FIH_CALL(bootutil_img_validate, fih_rc, NULL, &_hdr, _fa_p, tmpbuf, + BOOT_TMPBUF_SZ, NULL, 0, NULL); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + flash_area_close(_fa_p); +#ifdef MCUBOOT_RAM_LOAD + boot_remove_image_from_sram(state); +#endif + continue; + } + + 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; + } + + FIH_RET(fih_rc); +} + +int flash_area_id_from_multi_image_slot_hook(int image_index, int slot, + int *area_id) +{ + *area_id = known_ids[current_id]; + + return 0; +} + +int flash_area_get_device_id_hook(const struct flash_area *fa, uint8_t *dev_id) +{ + return BOOT_HOOK_REGULAR; +} diff --git a/samples/runtime-source/zephyr/hooks/zephyr/module.yml b/samples/runtime-source/zephyr/hooks/zephyr/module.yml new file mode 100644 index 0000000000..2beee397ca --- /dev/null +++ b/samples/runtime-source/zephyr/hooks/zephyr/module.yml @@ -0,0 +1,3 @@ +name: testmod +build: + cmake: . diff --git a/samples/runtime-source/zephyr/sample.conf b/samples/runtime-source/zephyr/sample.conf new file mode 100644 index 0000000000..fc9764ae06 --- /dev/null +++ b/samples/runtime-source/zephyr/sample.conf @@ -0,0 +1,8 @@ +CONFIG_FLASH_RUNTIME_SOURCES=y +CONFIG_SINGLE_APPLICATION_SLOT=y +CONFIG_BOOT_SIGNATURE_TYPE_RSA=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_ENTROPY_GENERATOR=y + +CONFIG_BOOT_GO_HOOKS=y +CONFIG_BOOT_FLASH_AREA_HOOKS=y From 27c2202a862ab3fb649884ceb3cddcba3ebc5a0e Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Mon, 6 Jan 2025 15:57:18 -0800 Subject: [PATCH 7/7] samples/runtime-source: Make it buildable on twister This sample has two parts: hooks for an mcuboot instance and a target application. For the first, a new entry at boot/zephyr/sample.yaml is added as well as a corresponding change at boot/zephyr/CMakeLists.txt to include the hook as a module. For the second, added a sample.yaml as well as invoke it from the github workflow. Signed-off-by: Ederson de Souza --- .github/workflows/zephyr_build.yaml | 1 + boot/zephyr/CMakeLists.txt | 6 ++++++ boot/zephyr/sample.yaml | 6 ++++++ samples/runtime-source/zephyr/app/sample.yaml | 10 ++++++++++ 4 files changed, 23 insertions(+) create mode 100644 samples/runtime-source/zephyr/app/sample.yaml diff --git a/.github/workflows/zephyr_build.yaml b/.github/workflows/zephyr_build.yaml index e1ef4631da..a3826fb31a 100644 --- a/.github/workflows/zephyr_build.yaml +++ b/.github/workflows/zephyr_build.yaml @@ -87,6 +87,7 @@ jobs: -T ../bootloader/mcuboot/boot/zephyr -T ./tests/subsys/dfu -T ./samples/subsys/mgmt/mcumgr/smp_svr + -T ../bootloader/mcuboot/samples/runtime-source/zephyr/app run: | export ZEPHYR_BASE=${PWD} export ZEPHYR_TOOLCHAIN_VARIANT=zephyr diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index b4d4860a95..b332000dac 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -7,6 +7,12 @@ cmake_minimum_required(VERSION 3.13.1) +# This sample shows usage of an external module and we need to set the +# set the extra module path before calling find_package(Zephyr). +if(TEST_RUNTIME_SOURCE_HOOKS) + set(EXTRA_ZEPHYR_MODULES "${CMAKE_SOURCE_DIR}/../../samples/runtime-source/zephyr/hooks") +endif() + # find_package(Zephyr) in order to load application boilerplate: # http://docs.zephyrproject.org/application/application.html find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) diff --git a/boot/zephyr/sample.yaml b/boot/zephyr/sample.yaml index 924f950cab..bf5bc93eaa 100644 --- a/boot/zephyr/sample.yaml +++ b/boot/zephyr/sample.yaml @@ -96,3 +96,9 @@ tests: integration_platforms: - nrf52840dk/nrf52840 tags: bootloader_mcuboot + sample.bootloader.mcuboot.runtime_source.hooks: + extra_args: EXTRA_CONF_FILE=../../samples/runtime-source/zephyr/sample.conf + TEST_RUNTIME_SOURCE_HOOKS=y + tags: bootloader_mcuboot runtime_source + platform_allow: frdm_k64f + build_only: true diff --git a/samples/runtime-source/zephyr/app/sample.yaml b/samples/runtime-source/zephyr/app/sample.yaml new file mode 100644 index 0000000000..ed90d7d698 --- /dev/null +++ b/samples/runtime-source/zephyr/app/sample.yaml @@ -0,0 +1,10 @@ +sample: + name: Runtime source target + description: Application loaded from mcuboot using runtime source hooks +common: + build_only: true +tests: + sample.zephyr.runtime_source.app: + tags: samples tests runtime_source + platform_allow: + - frdm_k64f