2026-08-15 17:38:02 -04:00
2026-05-16 23:17:22 -04:00
2026-05-14 17:25:17 -04:00
2026-07-04 16:52:55 -04:00
2026-05-14 21:23:34 +00:00

UTAT Device Firmware Upgrade with MCUboot

Documentation of the steps taken to implement MCUBoot for Device Firmware Upgrades.

Environnment config

Use the environment written in flake.nix by running nix develop

Set up Zephyr

Zephyr is the RTOS that uses the MCUboot bootloader being set up.

Getting started with Zephyr

west init zephyrproject
cd zepyhrproject
west update

west zephyr-export

cd zepyhr
west sdk install

MCUBoot POC Implementation

MCUBoot is a bootloader for Zephyr, which allows multiple firmwares to exist on a board. MCUBoot can dynamically switch which firmware is active. This is useful in the event of one firmware failing.

Abstract MCUBoot Guide

Configure partition sizes

The devicetree specification file declares the hardware configuration. The flash storage needs three partitions, one for boot, and two for the two firmwares it will hold.

DeviceTree Specification documentation

Partitioning examples

The board I'm using has its DeviceTree Specification in zephyr/boards/st/nucleo_g431rb/nucleo_g431rb.dts

I created a partition for MCUBoot, two firmware slots, and additional storage in the flash storage.

&flash0 {
	partitions {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;

		boot_partition: partition@0 {
			compatible = "zephyr,mapped-partition";
			label = "mcuboot";
			reg = <0x00000000 0x0000C000>;
		};

		slot0_partition: partition@C000 {
			compatible = "zephyr,mapped-partition";
			label = "image-0";
			reg = <0x0000C000 0x0000A000>;
		};

		slot1_partition: partition@14000 {
			compatible = "zephyr,mapped-partition";
			label = "image-1";
			reg = <0x00016000 0x0000A000>;
		};
	};
};

(Originally my partitions were 8000B each but MCUBoot would not build as its boot partition was too small. By using direct-xip there is no need for additional storage for copying images either.)

Enable building for MCUboot with direct-xip

Add these lines to bootloader/mcuboot/boot/zephyr/prj.conf

### Must be enabled to build applications for MCUboot
CONFIG_BOOTLOADER_MCUBOOT=y
### Allows in-place execution
CONFIG_BOOT_DIRECT_XIP=y
### Allows switching the image
CONFIG_BOOT_DIRECT_XIP_REVERT=y

Then in zephyr/samples/basic/blinky/prj.conf and zephyr/samples/basic/blinkyslow/prj.conf add the following:

CONFIG_BOOTLOADER_MCUBOOT=y
CONFIG_IMG_MANAGER=y
CONFIG_FLASH=y
CONFIG_REBOOT=y

Enable switching firmware

Each time a peice of firmware is run, set the other one as the upgrade so it runs next time. Add this to the code early in main:

#include <zephyr/dfu/mcuboot.h>
#include <zephyr/sys/reboot.h>

int main() {
	/* Confirm ourselves so MCUboot doesn't revert us on a later reset */
	if (!boot_is_img_confirmed()) {
		boot_write_img_confirmed();
	}

	/* Arm a permanent swap so the other image runs after the next reset */
	boot_request_upgrade(BOOT_UPGRADE_PERMANENT);
	...
}

Build the blinkys

The west build -DSB_CONFIG_BOOTLOADER_MCUBOOT=y flag enables MCUboot on the blinkys.

west build -p always -b nucleo_g431rb zephyr/samples/basic/blinky -d build/blinky --sysbuild -- -DSB_CONFIG_BOOTLOADER_MCUBOOT=y #-CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y

west build -p always -b nucleo_g431rb zephyr/samples/basic/blinkyslow -d build/blinkyslow --sysbuild -- -DSB_CONFIG_BOOTLOADER_MCUBOOT=y

Flash the blinkys

pyocd flash -t stm32g431rbtx build
/blinky/mcuboot/zephyr/zephyr.bin

pyocd flash -t stm32g431rbtx --base-address 0x0000C000 build/blinky/blinky/zephyr/zephyr.bin

(blinkyslow is a clone of blinky with a different frequency)

Set up crypto

python bootloader/mcuboot/scripts/imgtool.py keygen -k private.pem -t rsa-2048

openssl rsa -in private.pem -pubout > public.pem

Sign the blinkys

python bootloader/mcuboot/scripts/imgtool.py sign \
    --key private.pem \
    --header-size 0x200 \
    --pad-header \
    --align 8 \
    --version 1.2 \
    --slot-size 0x8000 \
    build/blinky/zephyr/zephyr.bin \
    blinky-signed.bin

python bootloader/mcuboot/scripts/imgtool.py sign \
    --key private.pem \
    --header-size 0x200 \
    --pad-header \
    --align 8 \
    --version 1.2 \
    --slot-size 0x8000 \
    build/blinkyslow/zephyr/zephyr.bin \
    blinkyslow-signed.bin

Build the firmware

west build -p always -b nucleo_g431rb -d build_mcuboot bootloader/mcuboot/boot/zephyr --

west build -p always -b nucleo_g431rb -d build_fast zephyr/samples/basic/blinky

west build -p always -b nucleo_g431rb -d build_slow zephyr/samples/basic/blinkyslow

Flash the firmware

su root allows flashing to usb devices, run nix develop again if using it.

flash0@0x08000000 in zephyr/dts/arm/st/g4/stm32g4.dtsi

pyocd flash -t stm32g431rbtx build_mcuboot/zephyr/zephyr.hex

pyocd flash -t stm32g431rbtx --base-address 0x0800C000 build_fast/zephyr/zephyr.bin

pyocd flash -t stm32g431rbtx --base-address 0x08014000 build_slow/zephyr/zephyr.bin
S
Description
Implementation guide for the MCUboot bootloader for the University of Toronto Aerospace Team
Readme Apache-2.0
3.7 MiB
0 Stars 1 Watchers 0 Forks
Languages
CMake 97.1%
Makefile 2.2%
Nix 0.7%