4.7 KiB
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.
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.
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
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);
...
}
Also, create an overlay for the firmware being executed in place. This is so the code jumps to addresses in the right block. Create zephyr/samples/basic/blinkyslow/boards/nucleo_g431rb.overlay
/ {
chosen {
zephyr,code-partition = &slot1_partition;
};
};
Set up crypto
python bootloader/mcuboot/scripts/imgtool.py keygen -k private.pem -t rsa-2048
openssl rsa -in private.pem -pubout > public.pem
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
Sign the firmware
python bootloader/mcuboot/scripts/imgtool.py sign \
--key private.pem \
--header-size 0x200 \
--pad-header \
--align 8 \
--pad \
--version 1.2 \
--slot-size 0xA000 \
build_fast/zephyr/zephyr.bin \
blinky-signed.bin
python bootloader/mcuboot/scripts/imgtool.py sign \
--key private.pem \
--header-size 0x200 \
--pad-header \
--align 8 \
--pad \
--version 1.2 \
--slot-size 0xA000 \
build_slow/zephyr/zephyr.bin \
blinkyslow-signed.bin
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 blinky-signed.bin
pyocd flash -t stm32g431rbtx --base-address 0x08016000 blinkyslow-signed.bin