From 982fae207e04a798e2045970ffc1880b0661f5b3 Mon Sep 17 00:00:00 2001 From: Tomasz Chyrowicz Date: Fri, 19 Sep 2025 14:08:23 +0200 Subject: [PATCH 1/2] boot: zephyr: Add multi-image support in FW loader Allow to use two images for both the application as well as the firmware loader. This confiugration alows to build the following configurations: - application: 1 image, FW loader: 1 image - application: 1 image, FW loader: 2 images - application: 2 images, FW loader: 1 image - application: 2 images, FW loader: 2 images Signed-off-by: Tomasz Chyrowicz --- boot/zephyr/firmware_loader.c | 127 ++++++++++++++++++++++++++-------- 1 file changed, 98 insertions(+), 29 deletions(-) diff --git a/boot/zephyr/firmware_loader.c b/boot/zephyr/firmware_loader.c index 217336755..b39c0d333 100644 --- a/boot/zephyr/firmware_loader.c +++ b/boot/zephyr/firmware_loader.c @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * * Copyright (c) 2020 Arm Limited - * Copyright (c) 2020-2023 Nordic Semiconductor ASA + * Copyright (c) 2020-2025 Nordic Semiconductor ASA */ #include @@ -27,7 +27,30 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); static const struct flash_area *_fa_p; static struct image_header _hdr = { 0 }; -#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) +#if DT_NODE_EXISTS(DT_NODELABEL(slot0_partition)) +#define SLOT0_PARTITION_ID DT_FIXED_PARTITION_ID(DT_NODELABEL(slot0_partition)) +#else +#error "No slot0_partition found in DTS" +#endif + +#if DT_NODE_EXISTS(DT_NODELABEL(slot2_partition)) +#define SLOT2_PARTITION_ID DT_FIXED_PARTITION_ID(DT_NODELABEL(slot2_partition)) +#endif + +#if DT_NODE_EXISTS(DT_NODELABEL(fw_loader_partition)) +#define FW_LOADER_PARTITION_ID DT_FIXED_PARTITION_ID(DT_NODELABEL(fw_loader_partition)) +#elif DT_NODE_EXISTS(DT_NODELABEL(slot1_partition)) +#define FW_LOADER_PARTITION_ID DT_FIXED_PARTITION_ID(DT_NODELABEL(slot1_partition)) +#else +#error "No firmware loader partition found in DTS" +#endif + +#if DT_NODE_EXISTS(DT_NODELABEL(fw_loader_aux_partition)) +#define FW_LOADER_AUX_PARTITION_ID DT_FIXED_PARTITION_ID(DT_NODELABEL(fw_loader_aux_partition)) +#elif DT_NODE_EXISTS(DT_NODELABEL(slot3_partition)) +#define FW_LOADER_AUX_PARTITION_ID DT_FIXED_PARTITION_ID(DT_NODELABEL(slot3_partition)) +#endif + /** * Validate hash of a primary boot image. * @@ -65,7 +88,6 @@ boot_image_validate(const struct flash_area *fa_p, FIH_RET(fih_rc); } -#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/ #if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) inline static fih_ret @@ -103,21 +125,21 @@ boot_image_validate_once(const struct flash_area *fa_p, #endif /** - * Validates that an image in a slot is OK to boot. + * Validates that an image in a partition is OK to boot. * - * @param[in] slot Slot number to check + * @param[in] id Fixed partition ID to check * @param[out] rsp Parameters for booting image, on success * * @return FIH_SUCCESS on success; non-zero on failure. */ -static fih_ret validate_image_slot(int slot, struct boot_rsp *rsp) +static fih_ret validate_image_id(int id, struct boot_rsp *rsp) { int rc = -1; FIH_DECLARE(fih_rc, FIH_FAILURE); - BOOT_LOG_DBG("validate_image_slot: slot %d", slot); + BOOT_LOG_DBG("validate_image_id: id %d", id); - rc = flash_area_open(slot, &_fa_p); + rc = flash_area_open(id, &_fa_p); assert(rc == 0); rc = boot_image_load_header(_fa_p, &_hdr); @@ -125,20 +147,35 @@ static fih_ret validate_image_slot(int slot, struct boot_rsp *rsp) goto other; } + switch (id) { + case SLOT0_PARTITION_ID: +#ifdef SLOT2_PARTITION_ID + case SLOT2_PARTITION_ID: +#endif /* SLOT2_PARTITION_ID */ #ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT - FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr); - if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { - goto other; - } + FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto other; + } #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)) { + goto other; + } + break; +#else + fih_rc = FIH_SUCCESS; goto other; +#endif /* !MCUBOOT_VALIDATE_PRIMARY_SLOT */ + default: + FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto other; + } + break; } -#else - fih_rc = FIH_SUCCESS; -#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ + BOOT_LOG_INF("validate_image_id: id %d is valid.", id); 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; @@ -168,46 +205,78 @@ boot_go(struct boot_rsp *rsp) BOOT_LOG_DBG("boot_go: firmware loader"); #ifdef CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO - if (io_detect_pin() && - !io_boot_skip_serial_recovery()) { + if (io_detect_pin() && !io_boot_skip_serial_recovery()) { + BOOT_LOG_INF("Button press detected - enter firmware loader."); boot_firmware_loader = true; } #endif #ifdef CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET if (io_detect_pin_reset()) { + BOOT_LOG_INF("Pin reset detected - enter firmware loader."); boot_firmware_loader = true; } #endif #ifdef CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE if (io_detect_boot_mode()) { + BOOT_LOG_INF("Boot mode detected - enter firmware loader."); boot_firmware_loader = true; } #endif #ifdef CONFIG_NRF_BOOT_FIRMWARE_LOADER_BOOT_REQ if (boot_request_detect_firmware_loader()) { + BOOT_LOG_INF("Boot request detected - enter firmware loader."); boot_firmware_loader = true; } #endif - /* Check if firmware loader button is pressed. TODO: check all entrance methods */ - if (boot_firmware_loader == true) { - FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_SECONDARY(0), rsp); - - if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + while (boot_firmware_loader == false) { + BOOT_LOG_DBG("Validating main image(s)..."); +#ifdef SLOT2_PARTITION_ID + FIH_CALL(validate_image_id, fih_rc, SLOT2_PARTITION_ID, rsp); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_NO_APPLICATION + BOOT_LOG_WRN("Failed to validate slot2_partition. Enter firmware loader."); + boot_firmware_loader = true; + break; +#else + BOOT_LOG_ERR("Failed to validate slot2_partition."); FIH_RET(fih_rc); +#endif } - } - - FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_PRIMARY(0), rsp); +#endif /* slot2_partition */ + FIH_CALL(validate_image_id, fih_rc, SLOT0_PARTITION_ID, rsp); #ifdef CONFIG_BOOT_FIRMWARE_LOADER_NO_APPLICATION - if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { - FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_SECONDARY(0), rsp); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + BOOT_LOG_WRN("Failed to validate slot0_partition. Enter firmware loader."); + boot_firmware_loader = true; + break; + } +#endif + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + BOOT_LOG_ERR("Failed to validate slot0_partition."); + } + FIH_RET(fih_rc); } + + /* Check if firmware loader button is pressed. TODO: check all entrance methods */ + if (boot_firmware_loader == true) { + BOOT_LOG_DBG("Validating firmware loader image(s)..."); +#ifdef FW_LOADER_AUX_PARTITION_ID + FIH_CALL(validate_image_id, fih_rc, FW_LOADER_AUX_PARTITION_ID, rsp); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + BOOT_LOG_ERR("Failed to validate auxiliary firmware loader image."); + FIH_RET(fih_rc); + } #endif + FIH_CALL(validate_image_id, fih_rc, FW_LOADER_PARTITION_ID, rsp); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + BOOT_LOG_ERR("Failed to validate firmware loader image."); + } + } FIH_RET(fih_rc); } From 70b4462c164ef05c06dd23540e9ef77df3c87dc1 Mon Sep 17 00:00:00 2001 From: Tomasz Chyrowicz Date: Fri, 19 Sep 2025 15:21:51 +0200 Subject: [PATCH 2/2] boot: zephyr: Fix IO-based entrance method If a device uses RESETINFO, than there are some bits set in the resetinfo, even for reboots that should allow to interpret and enter the device recovery. That way it is possible to recover a device through serial recovery if the main application resets due to i.e. watchdog. Signed-off-by: Tomasz Chyrowicz --- boot/zephyr/include/io/io.h | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/boot/zephyr/include/io/io.h b/boot/zephyr/include/io/io.h index 563281fa2..f0a9ba6eb 100644 --- a/boot/zephyr/include/io/io.h +++ b/boot/zephyr/include/io/io.h @@ -67,6 +67,45 @@ static inline bool io_boot_skip_serial_recovery() { uint32_t rr = nrfx_reset_reason_get(); +#ifdef NRF_RESETINFO + /* The following reset reasons should allow to enter firmware recovery or + * loader through an IO state. + */ + const uint32_t allowed_reset_reasons = ( + /* Reset from power on reset (reset reason POR or BOR). */ + NRFX_RESET_REASON_POR_MASK +#ifdef RESETINFO_RESETREAS_GLOBAL_RESETPOR_Msk + /* Reset from power on reset (reset reason other than POR or BOR). */ + | RESETINFO_RESETREAS_GLOBAL_RESETPOR_Msk +#endif + /* Reset from pin reset is not masked: it always enables the io-based recovery. */ + /* Reset from the watchdog timer. */ + | NRFX_RESET_REASON_DOG_MASK + /* Reset from CTRL-AP. */ + | NRFX_RESET_REASON_CTRLAP_MASK + /* Reset due to secure domain system reset request. */ + | NRFX_RESET_REASON_SREQ_MASK + /* Reset due to secure domain watchdog 0 timer. */ + | NRFX_RESET_REASON_SECWDT0_MASK + /* Reset due to secure domain watchdog 1 timer. */ + | NRFX_RESET_REASON_SECWDT1_MASK + /* Reset due to secure domain lockup. */ + | NRFX_RESET_REASON_LOCKUP_MASK +#if NRF_RESETINFO_HAS_LOCAL_WDT + /* Reset from the local watchdog timer 0. */ + | NRFX_RESET_REASON_LOCAL_DOG0_MASK + /* Reset from the local watchdog timer 1. */ + | NRFX_RESET_REASON_LOCAL_DOG1_MASK +#endif + /* Reset from the local soft reset request. */ + | NRFX_RESET_REASON_LOCAL_SREQ_MASK + /* Reset from local CPU lockup. */ + | NRFX_RESET_REASON_LOCAL_LOCKUP_MASK + ); + + rr &= ~allowed_reset_reasons; +#endif /* NRF_RESETINFO */ + return !(rr == 0 || (rr & NRFX_RESET_REASON_RESETPIN_MASK)); } #else