Skip to content

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

  1. Upgrade MCUBoot as well
    upgrade via Debugger or 2-step DFU update required

  2. 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

  3. Downgrade MCUBoot (new app with old MCUBoot)
    no longer "vanilla" zephyr -> version is fixed there

MCUBoot upgrade

Process:

  1. BMS start, access via Cyphal for DFU
  2. DFU to app: bms_mcuboot_upgrade (knowledge of new memory layout, itself a small application, cannot be verified by old MCUBoot)
  3. bms_mcuboot_upgrade routine starts. DFU possible.
  4. DFU with MCUBoot image (gets saved in RAM, no reboot after DFU).
  5. DFU with New app image (gets saved in Flash secondary, no reboot after DFU).
  6. Check of CRC (MCUBoot image) by Cyphal register readout: cyphal.dfu.crc with sent image (Makefile make ref prints local calculated CRC).
  7. Confirmation of image by setting cyphal.dfu.write_to_flash to 1.
  8. MCUBoot image is written from RAM to Flash.
  9. bms_mcuboot_upgrade reboots
  10. New MCUBoot starts
  11. 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