# 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](https://docs.zephyrproject.org/latest/develop/getting_started/index.html) ```sh 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](https://docs.mcuboot.com/readme-zephyr.html) ## 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](https://docs.zephyrproject.org/latest/build/dts/intro-scope-purpose.html) [Partitioning examples](https://docs.zephyrproject.org/latest/build/dts/api/bindings/mtd/fixed-partitions.html) 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. ```c &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` ```sh ### 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: ```sh 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: ```c #include #include 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 ```sh python bootloader/mcuboot/scripts/imgtool.py keygen -k private.pem -t rsa-2048 openssl rsa -in private.pem -pubout > public.pem ``` ## Build the firmware ```sh 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 ```sh 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` ```sh 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 ```