How do I store updated images for both MCUboot and the main App in external flash on the nRF5340 such that the existing MCUboot will apply the updates to the internal flash and execute them? Does the existing MCUboot codebase support this?
I have a nRF5340 with a MX25R6435F flash chip connected via QSPI. It is also connected to an LTE modem (nRF9160) via another SPI bus. The app on the 5340 will download any needed update data from the internet with custom code interfacing to the modem and write the needed update data to the external flash (this functionality is not part of my question here, assume the download and write to external flash of arbitrary data is working).
My main questions are:
- In what format do the updated MCUboot and App images need to be stored in external flash? (address offsets, etc.)
- Are any changes required to be made to the MCUboot codebase to support this?
- Are any additional configuration changes needed other than I what I have listed below?
I have configured my build system to use the B0 Immutable bootloader as the first-stage bootloader and MCUboot as an upgradeable second-stage bootloader. For the flash layout, I believe I have correctly configured the build for a single app image in internal flash, dual MCUboot image slots in internal flash, and the "mcuboot_secondary" slot in external flash. That all appears to be configured, build, and run correctly.
My goal for functionality is:
- The old App runs, downloads the updated image(s) to external flash, and triggers a reboot.
- The old MCUboot checks the external flash, detects that an update is available, and begins to copy them to internal flash...
- The updated MCUboot code is overwritten to the "other" MCUboot slot (the one not currently running).
- The updated App code is overwritten to the single App slot which had the old App code.
- The old MCUboot triggers a reboot.
- B0 detects the updated MCUboot in the "other" slot and executes it.
- The updated MCUboot runs and executes the updated App code.
I have read over some of the guides for DFU and bootloaders, but they seem to only apply to BLE updates not using external flash, or to LTE modem updates, or update MCUboot only but not the app; and aren't clear to me for how to achieve my goals. For example:
Device Firmware Upgrade module
Adding an upgradable bootloader
I have tried to debug/follow the code of context_boot_go() in workspace/bootloader/mcuboot/boot/bootutil/src/loader.c:1780 to better understand how MCUboot works and its upgrade process, but it's not clear to me how it needs to read from the external flash.
My configuration is as follows
Using NCS 1.6.0
Flash layout from west tool:
external_flash (0x800000 - 8192kB): +------------------------------------------------+ | 0x0: mcuboot_secondary (0x100000 - 1024kB) | | 0x100000: littlefs_storage (0x700000 - 7168kB) | | 0x800000: external_flash (0x0 - 0B) | +------------------------------------------------+ flash_primary (0x100000 - 1024kB): +--------------------------------------------------+ +---0x0: b0_container (0x8000 - 32kB)--------------+ | 0x0: b0 (0x8000 - 32kB) | +---0x8000: s0 (0x10000 - 64kB)--------------------+ | 0x8000: s0_pad (0x200 - 512B) | +---0x8200: s0_image (0xfe00 - 63kB)---------------+ | 0x8200: mcuboot (0xfe00 - 63kB) | +---0x18000: s1 (0x10000 - 64kB)-------------------+ | 0x18000: s1_pad (0x200 - 512B) | | 0x18200: s1_image (0xfe00 - 63kB) | +---0x28000: mcuboot_primary (0xd2000 - 840kB)-----+ | 0x28000: mcuboot_pad (0x200 - 512B) | +---0x28200: mcuboot_primary_app (0xd1e00 - 839kB)-+ +---0x28200: spm_app (0xd1e00 - 839kB)-------------+ | 0x28200: app (0xd1e00 - 839kB) | +--------------------------------------------------+ | 0xfa000: factory_cfg_1 (0x1000 - 4kB) | | 0xfb000: user_cfg_1 (0x1000 - 4kB) | | 0xfc000: developer_cfg_1 (0x1000 - 4kB) | | 0xfd000: factory_cfg_2 (0x1000 - 4kB) | | 0xfe000: user_cfg_2 (0x1000 - 4kB) | | 0xff000: developer_cfg_2 (0x1000 - 4kB) | +--------------------------------------------------+ otp (0x2fc - 764B): +------------------------------------+ | 0xff8100: provision (0x280 - 640B) | | 0xff8380: otp (0x7c - 124B) | +------------------------------------+ sram_primary (0x80000 - 512kB): +--------------------------------------------+ | 0x20000000: sram_primary (0x7e000 - 504kB) | | 0x2007e000: pcd_sram (0x2000 - 8kB) | +--------------------------------------------+
prj.conf (relevant sections)
# FLASH CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y CONFIG_FLASH_MAP=y CONFIG_FLASH_SHELL=y CONFIG_SETTINGS=y CONFIG_MPU_ALLOW_FLASH_WRITE=y # Secure Immutable Bootloader (B0) CONFIG_SECURE_BOOT=y # MCUBOOT CONFIG_BOOTLOADER_MCUBOOT=y CONFIG_IMG_MANAGER=y CONFIG_MCUBOOT_IMG_MANAGER=y CONFIG_IMG_ERASE_PROGRESSIVELY=y CONFIG_BUILD_S1_VARIANT=
mcuboot.conf (applied to the MCUboot image in CMake system)
CONFIG_NORDIC_QSPI_NOR=y CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=4 CONFIG_MULTITHREADING=y CONFIG_BOOT_MAX_IMG_SECTORS=256 CONFIG_PM_EXTERNAL_FLASH=y CONFIG_PM_EXTERNAL_FLASH_DEV_NAME="MX25R64" CONFIG_PM_EXTERNAL_FLASH_SIZE=0x800000 CONFIG_PM_EXTERNAL_FLASH_BASE=0x000000 CONFIG_BUILD_S1_VARIANT=y
pm_static.yml
# Config/Settings Partition: factory_cfg_1: address: 0xfa000 end_address: 0xfb000 placement: before: - end region: flash_primary size: 0x1000 user_cfg_1: address: 0xfb000 end_address: 0xfc000 placement: before: - end region: flash_primary size: 0x1000 developer_cfg_1: address: 0xfc000 end_address: 0xfd000 placement: before: - end region: flash_primary size: 0x1000 #Backup Config Partitions factory_cfg_2: address: 0xfd000 end_address: 0xfe000 placement: before: - end region: flash_primary size: 0x1000 user_cfg_2: address: 0xfe000 end_address: 0xff000 placement: before: - end region: flash_primary size: 0x1000 developer_cfg_2: address: 0xff000 end_address: 0x100000 placement: before: - end region: flash_primary size: 0x1000 # External Flash Partitions mcuboot_secondary: address: 0x0 device: MX25R64 end_address: 0x100000 region: external_flash size: 0x100000 littlefs_storage: address: 0x100000 device: MX25R64 end_address: 0x800000 region: external_flash size: 0x700000
normal boot log
*** Booting Zephyr OS build v2.6.0-rc1-ncs1 *** Attempting to boot slot 0. Attempting to boot from address 0x8200. Verifying signature against key 0. Hash: 0xfa...2e Firmware signature verified. Firmware version 1 *** Booting Zephyr OS build v2.6.0-rc1-ncs1 *** I: Starting bootloader I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Boot source: none I: Swap type: none I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Boot source: none I: Swap type: none I: Bootloader chainload address offset: 0x28000 �: Jumping to the first image slot [00:00:00.020,080] <dbg> fs.fs_register: fs register 1: 0 *** Booting Zephyr OS build v2.6.0-rc1-ncs1 *** nrf5340:~$
With some debug prints in MCUboot, I was able to see that MCUboot does at least open the external flash partition for access
*** Booting Zephyr OS build v2.6.0-rc1-ncs1 *** Attempting to boot slot 0. Attempting to boot from address 0x8200. Verifying signature against key 0. Hash: 0xfa...2e Firmware signature verified. Firmware version 1 Setting monotonic counter (version: 1, slot: 0) *** Booting Zephyr OS build v2.6.0-rc1-ncs1 *** I: Starting bootloader Flash open 11 -NRF_FLASH_DRV_NAME Flash open 2 -MX25R64 Flash open 11 -NRF_FLASH_DRV_NAME Flash open 2 -MX25R64 Flash open 11 -NRF_FLASH_DRV_NAME I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 Flash open 2 -MX25R64 I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Boot source: none Flash open 11 -NRF_FLASH_DRV_NAME Flash open 2 -MX25R64 Flash open 11 -NRF_FLASH_DRV_NAME Flash open 2 -MX25R64 I: Swap type: none Flash open 7 -NRF_FLASH_DRV_NAME Flash open 2 -MX25R64 Flash open 7 -NRF_FLASH_DRV_NAME Flash open 2 -MX25R64 Flash open 7 -NRF_FLASH_DRV_NAME I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 Flash open 2 -MX25R64 I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Boot source: none Flash open 7 -NRF_FLASH_DRV_NAME Flash open 2 -MX25R64 Flash open 7 -NRF_FLASH_DRV_NAME Flash open 2 -MX25R64 I: Swap type: none Flash open 11 -NRF_FLASH_DRV_NAME Flash open 11 -NRF_FLASH_DRV_NAME I: Bootloader chainload address offset: 0x28000 �: Jumping to the first image slot