I'm trying to download and flash an MCUboot image at runtime. All goes well, the image is flashed to the secondary slot, confirmed, and marked for upgrade. The secondary slot is in external spi flash.
On reboot MCUboot has no knowledge of the secondary image and boots back into the primary image. What could be going wrong here?
Stack trace BEFORE reset:
I: Image index: 0, Swap type: none D: mcuboot_swap_type: 1 D: writing magic; fa_id=1 off=0xd7ff0 (0xd7ff0) D: writing swap_info; fa_id=1 off=0xd7fd8 (0xd7fd8), swap_type=0x2 image_num=0x0 D: Image info: mcuboot_primary I: Image index: 0, Swap type: test I: MCUboot swap type: 2 I: Image Version 0.3.0-0 I: Image is confirmed OK D: Image info: mcuboot_secondary I: Image index: 0, Swap type: test I: MCUboot swap type: 2 I: Image Version 0.3.1-0 I: Image is confirmed OK
`mcumgr image list` BEFORE reset:
Images:
image=0 slot=0
version: 0.3.0
bootable: true
flags: active confirmed
hash: 649999195e98cbc94b74be313c406478d4932a42c2fc22e622e4f480b4e9806f
image=0 slot=1
version: 0.3.1
bootable: true
flags: pending
hash: 777ed754b4bac05b53a5763ecf0cb37c2a66320d7e0226fe8fa5c2c8a7291443
Split status: N/A (0)
Stack trace AFTER reset:
*** Booting nRF Connect SDK v2.5.2 *** I: Reset Reason 2, Flags: W: RESET_SOFTWARE D: Image info: mcuboot_primary I: Image index: 0, Swap type: none I: MCUboot swap type: 1 I: Image Version 0.3.0-0 I: Image is confirmed OK D: Image info: mcuboot_secondary I: Image index: 0, Swap type: none I: MCUboot swap type: 1 I: Image Version 76.146.8194-224105 I: Image is confirmed OK
`mcumgr image list` AFTER reset:
Images:
image=0 slot=0
version: 0.3.0
bootable: true
flags: active confirmed
hash: 649999195e98cbc94b74be313c406478d4932a42c2fc22e622e4f480b4e9806f
Split status: N/A (0)
My `mcuboot.conf`:
#core should be on a clean state when TF-M starts CONFIG_MCUBOOT_CLEANUP_ARM_CORE=y # Increase MCUboot stack size CONFIG_BOOT_MAX_IMG_SECTORS=256 CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x20000 CONFIG_BOOT_SIGNATURE_KEY_FILE="../keys/private/boot-ecdsa-p256.pem" CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y CONFIG_BOOT_ECDSA_CC310=y # CONFIG_BOOT_SWAP_USING_MOVE=y # CONFIG_BOOT_SWAP_SAVE_ENCTLV=y # SPI Flash CONFIG_SPI=y CONFIG_NVS=y CONFIG_PM_EXTERNAL_FLASH_BASE=0x0 CONFIG_PM_PARTITION_REGION_NVS_STORAGE_EXTERNAL=y CONFIG_SPI_NOR=y CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK=y CONFIG_UART_CONSOLE=n # Enable ech command CONFIG_BOOT_MGMT_ECHO=y # Enable flash operations CONFIG_FLASH=y # Enable serial recovery CONFIG_MCUBOOT_SERIAL=y CONFIG_BOOT_SERIAL_UART=y CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y CONFIG_BOOT_SERIAL_WAIT_FOR_DFU=y CONFIG_BOOT_SERIAL_WAIT_FOR_DFU_TIMEOUT=1000 CONFIG_BOOT_SERIAL_IMG_GRP_IMAGE_STATE=y CONFIG_BOOT_ERASE_PROGRESSIVELY=y
My overlay:
&uart0 {
status = "okay";
compatible = "nordic,nrf-uarte";
current-speed = <1000000>;
pinctrl-0 = <&uart0_default>;
pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default", "sleep";
hw-flow-control;
};
/delete-node/ &boot_partition;
/delete-node/ &slot0_partition;
/delete-node/ &slot1_partition;
// /delete-node/ &scratch_partition;
&flash0 {
partitions {
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 0x10000>;
};
slot0_partition: partition@10000 {
label = "image-0";
reg = <0x000010000 0x0000e8000>;
};
};
};
&w25q32jv {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
slot1_partition: partition@0 {
label = "image-1";
reg = <0x00000000 0x0000d8000>;
};
};
};
/ {
chosen {
nordic,pm-ext-flash = &w25q32jv;
zephyr,console = &uart0;
zephyr,shell-uart = &uart0;
zephyr,uart-mcumgr = &uart0;
};
};
My `partitions.yml`:
EMPTY_0:
address: 0xfa000
end_address: 0x100000
placement:
after:
- settings_storage
region: flash_primary
size: 0x6000
app:
address: 0x28000
end_address: 0xf8000
region: flash_primary
size: 0xd0000
external_flash:
address: 0xde000
end_address: 0x400000
region: external_flash
size: 0x322000
mcuboot:
address: 0x0
end_address: 0x20000
placement:
before:
- mcuboot_primary
region: flash_primary
size: 0x20000
mcuboot_pad:
address: 0x20000
end_address: 0x20200
placement:
align:
start: 0x8000
before:
- mcuboot_primary_app
region: flash_primary
size: 0x200
mcuboot_primary:
address: 0x20000
end_address: 0xf8000
orig_span: &id001
- mcuboot_pad
- app
- tfm
region: flash_primary
size: 0xd8000
span: *id001
mcuboot_primary_app:
address: 0x20200
end_address: 0xf8000
orig_span: &id002
- app
- tfm
region: flash_primary
size: 0xd7e00
span: *id002
mcuboot_secondary:
address: 0x0
device: DT_CHOSEN(nordic_pm_ext_flash)
end_address: 0xd8000
placement:
align:
start: 0x4
region: external_flash
share_size:
- mcuboot_primary
size: 0xd8000
mcuboot_sram:
address: 0x20000000
end_address: 0x20008000
orig_span: &id003
- tfm_sram
region: sram_primary
size: 0x8000
span: *id003
nonsecure_storage:
address: 0xf8000
end_address: 0xfa000
orig_span: &id004
- settings_storage
region: flash_primary
size: 0x2000
span: *id004
nrf_modem_lib_ctrl:
address: 0x20008000
end_address: 0x200084e8
inside:
- sram_nonsecure
placement:
after:
- tfm_sram
- start
region: sram_primary
size: 0x4e8
nrf_modem_lib_rx:
address: 0x2000a568
end_address: 0x2000c568
inside:
- sram_nonsecure
placement:
after:
- nrf_modem_lib_tx
region: sram_primary
size: 0x2000
nrf_modem_lib_sram:
address: 0x20008000
end_address: 0x2000c568
orig_span: &id005
- nrf_modem_lib_ctrl
- nrf_modem_lib_tx
- nrf_modem_lib_rx
region: sram_primary
size: 0x4568
span: *id005
nrf_modem_lib_tx:
address: 0x200084e8
end_address: 0x2000a568
inside:
- sram_nonsecure
placement:
after:
- nrf_modem_lib_ctrl
region: sram_primary
size: 0x2080
nvs_storage:
address: 0xd8000
device: DT_CHOSEN(nordic_pm_ext_flash)
end_address: 0xde000
placement:
before:
- tfm_storage
- end
region: external_flash
size: 0x6000
otp:
address: 0xff8108
end_address: 0xff83fc
region: otp
size: 0x2f4
settings_storage:
address: 0xf8000
end_address: 0xfa000
inside:
- nonsecure_storage
placement:
align:
start: 0x8000
before:
- end
region: flash_primary
size: 0x2000
sram_nonsecure:
address: 0x20008000
end_address: 0x20040000
orig_span: &id006
- sram_primary
- nrf_modem_lib_ctrl
- nrf_modem_lib_tx
- nrf_modem_lib_rx
region: sram_primary
size: 0x38000
span: *id006
sram_primary:
address: 0x2000c568
end_address: 0x20040000
region: sram_primary
size: 0x33a98
sram_secure:
address: 0x20000000
end_address: 0x20008000
orig_span: &id007
- tfm_sram
region: sram_primary
size: 0x8000
span: *id007
tfm:
address: 0x20200
end_address: 0x28000
inside:
- mcuboot_primary_app
placement:
before:
- app
region: flash_primary
size: 0x7e00
tfm_nonsecure:
address: 0x28000
end_address: 0xf8000
orig_span: &id008
- app
region: flash_primary
size: 0xd0000
span: *id008
tfm_secure:
address: 0x20000
end_address: 0x28000
orig_span: &id009
- mcuboot_pad
- tfm
region: flash_primary
size: 0x8000
span: *id009
tfm_sram:
address: 0x20000000
end_address: 0x20008000
inside:
- sram_secure
placement:
after:
- start
region: sram_primary
size: 0x8000
The code:
#include "fota.h"
#include <zephyr/device.h>
#include <zephyr/dfu/flash_img.h>
#include <zephyr/dfu/mcuboot.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/stats/stats.h>
#include <zephyr/storage/flash_map.h>
#include "custom_http_client.h"
#include "pm_config.h"
LOG_MODULE_REGISTER(fota, LOG_LEVEL_DBG);
#define STRINGIZE(arg) #arg
#define STRINGIZE_VALUE(arg) STRINGIZE(arg)
#define PM_MCUBOOT_PRIMARY_STRING STRINGIZE_VALUE(PM_MCUBOOT_PRIMARY_NAME)
#define PM_MCUBOOT_SECONDARY_STRING STRINGIZE_VALUE(PM_MCUBOOT_SECONDARY_NAME)
BUILD_ASSERT(
FIXED_PARTITION_EXISTS(PM_MCUBOOT_SECONDARY_NAME),
"Missing " PM_MCUBOOT_SECONDARY_STRING
" fixed partition. Secondary slot partition is required!"
);
struct flash_img_context ctx;
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/
void image_validation(uint8_t area_id) {
int rc;
char buf[BOOT_IMG_VER_STRLEN_MAX];
struct mcuboot_img_header header;
boot_read_bank_header(area_id, &header, sizeof(header));
snprintk(
buf, sizeof(buf), "%d.%d.%d-%d", header.h.v1.sem_ver.major,
header.h.v1.sem_ver.minor, header.h.v1.sem_ver.revision,
header.h.v1.sem_ver.build_num
);
LOG_INF("Booting image: build time: " __DATE__ " " __TIME__);
LOG_INF("Image Version: %s", buf);
LOG_INF("Image size: %d", header.h.v1.image_size);
rc = boot_is_img_confirmed();
LOG_INF("Image is%s confirmed", rc ? "" : " not");
if (!rc) {
if (boot_write_img_confirmed()) {
LOG_ERR("Failed to confirm image");
} else {
LOG_INF("Marked image as OK");
}
}
}
void image_info(uint8_t area_id) {
int rc;
char buf[BOOT_IMG_VER_STRLEN_MAX];
struct mcuboot_img_header header;
boot_read_bank_header(area_id, &header, sizeof(header));
snprintk(
buf, sizeof(buf), "%d.%d.%d-%d", header.h.v1.sem_ver.major,
header.h.v1.sem_ver.minor, header.h.v1.sem_ver.revision,
header.h.v1.sem_ver.build_num
);
LOG_INF("MCUboot swap type: %d", mcuboot_swap_type());
LOG_INF("Image Version %s", buf);
rc = boot_is_img_confirmed();
LOG_INF("Image is%s confirmed OK", rc ? "" : " not");
}
int write_buffer_to_flash(char *data, size_t len, _Bool flush) {
int rc;
if (flush) {
rc = flash_img_buffered_write(&ctx, data, len, true);
} else {
rc = flash_img_buffered_write(&ctx, data, len, false);
}
LOG_DBG("Flash img bytes written: %d", flash_img_bytes_written(&ctx));
return rc;
}
void download_update(void) {
int rc;
char headers_buf[1024];
char write_buf[CONFIG_IMG_BLOCK_BUF_SIZE];
// struct flash_img_check fic {
// const uint8_t *match; /** Match vector data */
// size_t clen; /** Content to be compared */
// };
rc = flash_img_init_id(&ctx, PM_MCUBOOT_SECONDARY_ID);
if (rc < 0) {
LOG_ERR("Failed to init stream flash");
}
(void)http_get_firmware(
write_buf, sizeof(write_buf), headers_buf, sizeof(headers_buf)
);
LOG_DBG("mcuboot_swap_type: %d", mcuboot_swap_type());
rc = boot_request_upgrade(BOOT_UPGRADE_TEST);
if (rc < 0) {
LOG_ERR("Failed to REQUEST FIRMWARE UPGRADE");
}
LOG_DBG("\nImage info: " PM_MCUBOOT_PRIMARY_STRING);
(void)image_info(PM_MCUBOOT_PRIMARY_ID);
LOG_DBG("\nImage info: " PM_MCUBOOT_SECONDARY_STRING);
(void)image_info(PM_MCUBOOT_SECONDARY_ID);
}