187 lines
4.7 KiB
Markdown
187 lines
4.7 KiB
Markdown
# 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 <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
|
|
```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
|
|
```
|
|
|
|
|
|
|