Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Update Firmware (Bootloader and Application) from external flash

I am using the nRF52840-dk with the nRF Connect SDK v2.0.0 and for the development itself I use Visual Studio Code.

My Goal: Both the bootloader and the actual application should be upgradeable, with firmware updates stored on an external flash. Supposedly this should be possible.
The firmware is stored in the external flash via specially developed methods. Afterwards the bootloader should detect these firmware updates in the external flash and copy/swap them to the internal flash.


According to the documentation I need the nRF Secure Immutable Bootloader and MCUboot as a second stage bootloader.
This actual bootloader chain works fine so far even with my own private keys.

Currently I am getting the following output with my configuration:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
*** Booting Zephyr OS build v3.0.99-ncs1 ***
Attempting to boot slot 0.
Attempting to boot from address 0x9200.
Verifying signature against key 0.
Hash: 0xea...6a
Firmware signature verified.
Firmware version 1
Setting monotonic counter (version: 1, slot: 0)
*** Booting Zephyr OS build v3.0.99-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=bad, swap_type=0x2, copy_done=0x2, image_ok=0x2
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: 0x23000
▒*** Booting Zephyr OS build v3.0.99-ncs1 ***
[00:0Bluetooth initialized
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

I do not understand the output of MCUboot at all. Why do two passes take place? Why is magic from the primary image suddenly bad on the second pass, but loads the firmware anyway?

I can change the version number of MCUboot. This is also shown correctly by the first bootloader.

But that's it. I can't get it to recognize the firmware updates.
MCUboot doesn't seem to make any attempt at all to read anything from the external flash. Well I don't know. The bootloader doesn't give any further relevant information, even if I adjust the log level.

For now, I'd be happy to at least have MCUboot upgradeable.

The following already works:
The firmware update is in the right position in the external flash. For testing purposes at offset 0x0.
As the firmware update for MCUboot I use the file "signed_by_b0_s0_image.bin" from the "build/zephyr" directory. If I open this binary
I see the first 0x200 bytes with some meta information. See here:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ head --bytes=$((16#200)) signed_by_b0_s0_image.bin | xxd
00000000: c05d 0020 5dbc 0000 1907 0100 31bc 0000 .]. ].......1...
00000010: 31bc 0000 31bc 0000 31bc 0000 0000 0000 1...1...1.......
00000020: 0000 0000 0000 0000 0000 0000 69b7 0000 ............i...
00000030: 31bc 0000 0000 0000 15b7 0000 31bc 0000 1...........1...
00000040: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
00000050: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
00000060: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
00000070: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
00000080: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
00000090: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
000000a0: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
000000b0: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
000000c0: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
000000d0: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
000000e0: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
000000f0: 19b8 0000 19b8 0000 19b8 0000 19b8 0000 ................
00000100: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000110: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000120: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000130: 0000 0000 0000 0000 0000 0000 0000 0000 ................
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

My expectation would be: Since it is signed_by_b0_s0_image.bin, which is (presumably) a valid MCUboot firmware, the bootloader would have to output some message and validate the update or do something else. But I don't see anything of that.

Below I list the most important configurations I use. These are mainly for the current development.
And yes, due to days of desperate attempts, the configuration files are now very messy.

pm_static.yml (originally taken from a sample project - but couldn't even be built in the first place)

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
external_flash:
address: 0x000000
region: external_flash
size: 0x0
device: MX25R64
mcuboot_secondary:
address: 0x00000
device: MX25R64
region: external_flash
size: 0xdb000
rest_storage:
address: 0xdb000
size: 0x725000
device: MX25R64
region: external_flash
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

nrf52840dk_nrf52840.overlay:

Fullscreen
1
2
3
4
5
/ {
chosen {
nordic,pm-ext-flash = &mx25r64;
};
};
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

The generated partitions.yml:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
EMPTY_0:
address: 0x15200
end_address: 0x16000
placement:
before:
- s1_pad
region: flash_primary
size: 0xe00
EMPTY_1:
address: 0x22200
end_address: 0x23000
placement:
before:
- mcuboot_pad
region: flash_primary
size: 0xe00
app:
address: 0x23200
end_address: 0xfe000
region: flash_primary
size: 0xdae00
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

prj.conf

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CONFIG_NEWLIB_LIBC=y
CONFIG_CPLUSPLUS=y
CONFIG_STD_CPP2A=y
CONFIG_LIB_CPLUSPLUS=y
CONFIG_NEWLIB_LIBC_NANO=y
CONFIG_EXCEPTIONS=y
CONFIG_SENSOR=y
CONFIG_TEMP_NRF5=y
CONFIG_NRFX_TEMP=y
CONFIG_STDOUT_CONSOLE=y
CONFIG_FLASH=y
CONFIG_I2C=n
CONFIG_SPI=y
CONFIG_NORDIC_QSPI_NOR=y
CONFIG_SPI_SLAVE=n
CONFIG_SPI_NRFX=y
CONFIG_SPI_ASYNC=y
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

child_image/b0.conf:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CONFIG_IS_SECURE_BOOTLOADER=y
CONFIG_MULTITHREADING=n
CONFIG_GPIO=n
CONFIG_ARM_MPU=n
CONFIG_PM=n
CONFIG_TICKLESS_KERNEL=n
CONFIG_ERRNO=n
CONFIG_FPROTECT=y
CONFIG_SECURE_BOOT_CRYPTO=y
CONFIG_SECURE_BOOT_DEBUG=y
CONFIG_SECURE_BOOT_VALIDATION=y
CONFIG_SECURE_BOOT_STORAGE=y
CONFIG_BL_ROT_VERIFY_EXT_API_ENABLED=y
CONFIG_BL_SHA256_EXT_API_ENABLED=y
CONFIG_BL_SECP256R1_EXT_API_ENABLED=y
CONFIG_BL_VALIDATE_FW_EXT_API_ENABLED=y
CONFIG_EXT_API_PROVIDE_EXT_API_ENABLED=y
CONFIG_NRFX_NVMC=y
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_TIMEOUT_64BIT=n
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

child_image/mcuboot.conf:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CONFIG_PM=n
CONFIG_MAIN_STACK_SIZE=10240
CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h"
CONFIG_BOOT_SWAP_SAVE_ENCTLV=n
CONFIG_BOOT_ENCRYPT_RSA=n
CONFIG_BOOT_ENCRYPT_EC256=n
CONFIG_BOOT_ENCRYPT_X25519=n
CONFIG_BOOT_UPGRADE_ONLY=n
CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=n
CONFIG_BOOT_BOOTSTRAP=n
CONFIG_FLASH=y
CONFIG_FPROTECT=y
CONFIG_LOG=y
CONFIG_LOG_MODE_MINIMAL=y # former CONFIG_MODE_MINIMAL
CONFIG_LOG_DEFAULT_LEVEL=0
CONFIG_CBPRINTF_NANO=y
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX