MCUBoot upgrade¶
Version 0.11 Flash Layout with MCUBoot. MCUBoot uses one slot0 sector (size = FLASH_PAGE = 2048 Byte) as scratch memory for image move/switch process. Additional memory for status data is occupied for each available image (2 images = 2 pages = 4096 Byte).
boot: 32 KiB
slot0: 206 KiB
slot1: 200 KiB
storage: 74 KiB
Zephyr update (3.6.0 -> 4.3.0) updated MCUBoot as well. MCUBoot uses the status memory of each slot image (header that is appended to each image). Scratch memory sector is still needed (single page).
boot: 32 KiB
slot0: 204 KiB
slot1: 202 KiB
storage: 74 KiB
Problem: As the partition memory layout changes, MCUBoot messes up status flags for image verification...
e.g.:
- New MCUBoot; New -> New: Confirming
*** Booting MCUboot 96576b341ee1 ***
*** Using Zephyr OS build cffdb1d8cf83 ***
I: Starting bootloader
I: Primary image: magic=bad, swap_type=0x2, copy_done=0x1, image_ok=0x2
I: Secondary image: magic=good, swap_type=0x2, copy_done=0x3, image_ok=0x3
I: Boot source: none
I: Image index: 0, Swap type: test
I: Starting swap using move algorithm.
I: Bootloader chainload address offset: 0x8000
I: Image version: v0.12.0
I: Jumping to the first image slot
*** Booting Zephyr OS build cffdb1d8cf83 ***
I: RtcSync initialized: Local clock 10 kHz, Reference clock 10 kHz
Firmware image is unconfirmed. Confirming in 10s if no rollback occurs.
...
*** Booting MCUboot 96576b341ee1 ***
*** Using Zephyr OS build cffdb1d8cf83 ***
I: Starting bootloader
I: Primary image: magic=good, swap_type=0x2, copy_done=0x1, image_ok=0x1
I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Boot source: none
I: Image index: 0, Swap type: none
I: Bootloader chainload address offset: 0x8000
I: Image version: v0.12.0
I: Jumping to the first image slot
*** Booting Zephyr OS build cffdb1d8cf83 ***
I: RtcSync initialized: Local clock 10 kHz, Reference clock 10 kHz
Firmware image is confirmed.
- New MCUBoot; New -> Old Image via DFU: image unconfirmed -> unable to confirm -> reverting always on reboot
*** Booting MCUboot 96576b341ee1 ***
*** Using Zephyr OS build cffdb1d8cf83 ***
I: Starting bootloader
I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Secondary image: magic=good, swap_type=0x2, copy_done=0x3, image_ok=0x3
I: Boot source: none
I: Image index: 0, Swap type: test
I: Starting swap using move algorithm.
I: Bootloader chainload address offset: 0x8000
I: Image version: v0.11.0
I: Jumping to the first image slot
*** Booting Zephyr OS build 7b64ec536fea ***
Firmware image is unconfirmed. Confirming in 10s if no rollback occurs.
...
*** Booting MCUboot 96576b341ee1 ***
*** Using Zephyr OS build cffdb1d8cf83 ***
I: Starting bootloader
I: Primary image: magic=good, swap_type=0x2, copy_done=0x1, image_ok=0x3
I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Boot source: none
I: Image index: 0, Swap type: revert
I: Starting swap using move algorithm.
I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Bootloader chainload address offset: 0x8000
I: Image version: v0.12.0
- Old MCUBoot; Old to Old: confirming
*** Booting Zephyr OS build 7b64ec536fea ***
I: Starting bootloader
I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Secondary image: magic=good, swap_type=0x2, copy_done=0x3, image_ok=0x3
I: Boot source: none
I: Image index: 0, Swap type: test
I: Starting swap using move algorithm.
I: Bootloader chainload address offset: 0x8000
I: Jumping to the first image slot
*** Booting Zephyr OS build 7b64ec536fea ***
Firmware image is unconfirmed. Confirming in 10s if no rollback occurs.
...
*** Booting Zephyr OS build 7b64ec536fea ***
I: Starting bootloader
I: Primary image: magic=good, swap_type=0x2, copy_done=0x1, image_ok=0x1
I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Boot source: none
I: Image index: 0, Swap type: none
I: Bootloader chainload address offset: 0x8000
I: Jumping to the first image slot
*** Booting Zephyr OS build 7b64ec536fea ***
Firmware image is confirmed.
- Old MCUBoot; Old to New: Reverting image
*** *** Booting Zephyr OS build 7b64ec536fea ***
I: Starting bootloader
I: Primary image: magic=good, swap_type=0x2, copy_done=0x1, image_ok=0x1
I: Secondary image: magic=good, swap_type=0x2, copy_done=0x3, image_ok=0x3
I: Boot source: none
I: Image index: 0, Swap type: test
I: Starting swap using move algorithm.
I: Bootloader chainload address offset: 0x8000
I: Jumping to the first image slot
*** Booting Zephyr OS build cffdb1d8cf83 ***
I: RtcSync initialized: Local clock 10 kHz, Reference clock 10 kHz
Firmware image is unconfirmed. Confirming in 10s if no rollback occurs.
...
*** Booting Zephyr OS build 7b64ec536fea ***
I: Starting bootloader
I: Primary image: magic=good, swap_type=0x2, copy_done=0x1, image_ok=0x3
I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Boot source: none
I: Image index: 0, Swap type: revert
I: Starting swap using move algorithm.
I: Secondary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
Solution Ideas¶
-
Upgrade MCUBoot as well
upgrade via Debugger or 2-step DFU update required -
Fix confirm routine in application (check MCUBoot version)
layout change may lead to unexpected / unsafe behavior (may be wrong memory regions)
different software versions / systems increase development / test scope -
Downgrade MCUBoot (new app with old MCUBoot)
no longer "vanilla" zephyr -> version is fixed there
MCUBoot upgrade¶
Process:
- BMS start, access via Cyphal for DFU
- DFU to app: bms_mcuboot_upgrade (knowledge of new memory layout, itself a small application, cannot be verified by old MCUBoot)
- bms_mcuboot_upgrade routine starts. DFU possible.
- DFU with MCUBoot image (gets saved in RAM, no reboot after DFU).
- DFU with New app image (gets saved in Flash secondary, no reboot after DFU).
- Check of CRC (MCUBoot image) by Cyphal register readout:
cyphal.dfu.crcwith sent image (Makefilemake refprints local calculated CRC). - Confirmation of image by setting
cyphal.dfu.write_to_flashto 1. - MCUBoot image is written from RAM to Flash.
- bms_mcuboot_upgrade reboots
- New MCUBoot starts
- New application image from secondary partition starts as marked to be permanent (bms_mcuboot_upgrade no longer needed)
Optimization:
- Integration in DFUThread -> regular app can upgrade MCUBoot
- No confirm / node id enter in makefile process / cyphal lib changes?
Flash access example¶
#include <zephyr/drivers/flash.h>
// STM32 flash controller device (Zephyr)
const device *flash_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller));
if (!device_is_ready(flash_dev)) {
LOG_ERR("Flash device not ready!");
return -1;
}
uint32_t data[FLASH_PAGE_SIZE];
uint32_t data2[FLASH_PAGE_SIZE];
uint32_t address = 0x00000000; // + CONFIG_FLASH_BASE_ADDRESS; -> base is added by driver
uint64_t flash_size = 0;
flash_get_size(flash_dev, &flash_size);
LOG_INF("--- Flash Information ---");
LOG_INF("Size: %llu bytes", flash_size);
LOG_INF("Page size: %u bytes, count: %u", FLASH_PAGE_SIZE, flash_get_page_count(flash_dev));
LOG_INF("Write Block size: %u, no explicit erase: %u",
flash_get_write_block_size(flash_dev),
flash_get_parameters(flash_dev)->caps.no_explicit_erase);
LOG_INF("Erase value: %x", flash_get_parameters(flash_dev)->erase_value);
LOG_INF("---");
flash_pages_info page_info;
flash_get_page_info_by_offs(flash_dev, address, &page_info);
LOG_INF("Page info by offs: start_offset=0x%08X, size=%u, index=%u",
(unsigned int)page_info.start_offset, (unsigned int)page_info.size,
(unsigned int)page_info.index);
// print first 16 bytes
flash_read(flash_dev, address, data, FLASH_PAGE_SIZE);
LOG_INF("Flash data at address 0x%08X:", (unsigned int)address);
LOG_INF(" 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X "
"0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
((uint8_t *)data)[0], ((uint8_t *)data)[1], ((uint8_t *)data)[2],
((uint8_t *)data)[3], ((uint8_t *)data)[4], ((uint8_t *)data)[5],
((uint8_t *)data)[6], ((uint8_t *)data)[7], ((uint8_t *)data)[8],
((uint8_t *)data)[9], ((uint8_t *)data)[10], ((uint8_t *)data)[11],
((uint8_t *)data)[12], ((uint8_t *)data)[13], ((uint8_t *)data)[14],
((uint8_t *)data)[15]);
LOG_INF("Erasing flash page at address 0x%08X...", (unsigned int)address);
flash_flatten(flash_dev, address, FLASH_PAGE_SIZE); // instead flash_erase
// print first 16 bytes
flash_read(flash_dev, address, data2, FLASH_PAGE_SIZE);
LOG_INF("Flash data at address 0x%08X:", (unsigned int)address);
LOG_INF(" 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X "
"0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
((uint8_t *)data2)[0], ((uint8_t *)data2)[1], ((uint8_t *)data2)[2],
((uint8_t *)data2)[3], ((uint8_t *)data2)[4], ((uint8_t *)data2)[5],
((uint8_t *)data2)[6], ((uint8_t *)data2)[7], ((uint8_t *)data2)[8],
((uint8_t *)data2)[9], ((uint8_t *)data2)[10], ((uint8_t *)data2)[11],
((uint8_t *)data2)[12], ((uint8_t *)data2)[13], ((uint8_t *)data2)[14],
((uint8_t *)data2)[15]);
LOG_INF("Writing to flash page at address 0x%08X...", (unsigned int)address);
flash_write(flash_dev, address, data, FLASH_PAGE_SIZE);
// print first 16 bytes
flash_read(flash_dev, address, data, FLASH_PAGE_SIZE);
LOG_INF("Flash data at address 0x%08X:", (unsigned int)address);
LOG_INF(" 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X "
"0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
((uint8_t *)data)[0], ((uint8_t *)data)[1], ((uint8_t *)data)[2],
((uint8_t *)data)[3], ((uint8_t *)data)[4], ((uint8_t *)data)[5],
((uint8_t *)data)[6], ((uint8_t *)data)[7], ((uint8_t *)data)[8],
((uint8_t *)data)[9], ((uint8_t *)data)[10], ((uint8_t *)data)[11],
((uint8_t *)data)[12], ((uint8_t *)data)[13], ((uint8_t *)data)[14],
((uint8_t *)data)[15]);
// flash_page_foreach
// flash_copy
// flash_ex_op