stm32f446 Nucleo

The stm32f446 example uses a STM32F446 Nucleo Board. The board has 1 user LED which will be blinking at different frequencies depending on whether we're executing boot-firmware or update-firmware.

Note

  • If you're using a different version of the board, you'll probably need to edit your firmware's partition-addresses to accommodate for differences.
  • Just make sure you don't change the names of files or the folder structure, as cargo xtask looks for these file/folder names.

Partitioning

The first step in integrating rustBoot is flash-memory partitioning i.e. we divide the stm32f446's flash-memory into 4 partitions, taking into account the geometry of the flash memory.

You can read more about mcu partitioning here

In this example, we'll be using the following partitioning scheme. You can locate these constants in the constants module

#![allow(unused)]
fn main() {
#[cfg(feature = "stm32f446")]
pub const SECTOR_SIZE: usize = 0x20000;
#[cfg(feature = "stm32f446")]
pub const PARTITION_SIZE: usize = 0x20000;
#[cfg(feature = "stm32f446")]
pub const BOOT_PARTITION_ADDRESS: usize = 0x08020000;
#[cfg(feature = "stm32f446")]
pub const SWAP_PARTITION_ADDRESS: usize = 0x08060000;
#[cfg(feature = "stm32f446")]
pub const UPDATE_PARTITION_ADDRESS: usize = 0x08040000;
}

RUSTBOOT partition: contains the bootloader (its code and data) and a (test) public-key embedded as part of the bootloader image, starts at address 0x0800_0000.

  • BOOT partition: contains boot firmware, starts at address PARTITION_BOOT_ADDRESS.
  • UPDATE partition: contains update firmware, starts at address UPDATE_PARTITION_ADDRESS. The boot firmware is responsible for downloading and installing the update firmware into this partition via a secure channel.
  • SWAP partition: is the temporary swap space, starts at address SWAP_PARTITION_ADDRESS.

Compiling, Signing and Programming:

Now that we have properly partitioned the stm32f446 Nucleo's on-board flash-memory, the next step is - compiling, signing and programming

We will compile the following

  • bootloader
  • boot and update firmware

sign both pieces of firmware with a (test) private-key and finally create valid rustBoot mcu-images i.e. signed boot and update firmware images.

Note:

  • the ecc256.der file contains a public-key and a private-key, the first 64 bytes being the public-key and remaining 32 bytes make up the private-key.
  • This is a test key file and is to be used for testing purposes only.

Compiling, signing and programming can be performed via a single command

cargo stm32f446 build-sign-flash rustBoot [boot-ver] [updt-ver]

Note:

  • The updt-ver number should be greater than boot-ver.

This will build, sign and flash all 3 packages (i.e. bootloader + boot-fw + update-fw) onto the board. With the respective firmware versions.

Note:

  • The corresponding public-key is embedded in the bootloader's source.
  • In order to test this example, you'll have to install a couple of pre-requisites as it uses probe-run to flash the binary.
cargo install probe-rs-cli 
cargo install cargo-flash
cargo install cargo-binutils

Here's the command line output that should be produced.

anand@anand-VirtualBox:~/Desktop/dev_space/Prod/rustBoot_mcusigner$ cargo stm32f446 build-sign-flash rustBoot 1234 1235
Compiling version_check v0.9.4
   Compiling typenum v1.15.0
   Compiling subtle v2.4.1
   Compiling rand_core v0.6.3
   Compiling const-oid v0.7.1
   Compiling zeroize v1.4.3
   Compiling memchr v2.5.0
   Compiling cfg-if v1.0.0
   Compiling base16ct v0.1.1
   Compiling log v0.4.17
   Compiling opaque-debug v0.3.0
   Compiling cpufeatures v0.2.2
   Compiling anyhow v1.0.58
   Compiling minimal-lexical v0.2.1
   Compiling stable_deref_trait v1.2.0
   Compiling byteorder v1.4.3
   Compiling xshell-macros v0.1.17
   Compiling der v0.5.1
   Compiling ff v0.11.1
   Compiling as-slice v0.2.1
   Compiling group v0.11.0
...
...
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/xtask stm32f446 build-sign-flash rustBoot 1234 1235`
$ cargo build --release
    Finished release [optimized] target(s) in 0.03s
$ cargo build --release
    Finished release [optimized] target(s) in 0.03s
$ cargo build --release
    Finished release [optimized] target(s) in 0.03s
$ rust-objcopy -I elf32-littlearm ../../target/thumbv7em-none-eabihf/release/stm32f446_bootfw -O binary stm32f446_bootfw.bin
$ rust-objcopy -I elf32-littlearm ../../target/thumbv7em-none-eabihf/release/stm32f446_updtfw -O binary stm32f446_updtfw.bin
$ cargo run mcu-image ../boards/sign_images/signed_images/stm32f446_bootfw.bin nistp256 ../boards/sign_images/keygen/ecc256.der 1234
   Compiling libc v0.2.126
   Compiling rustBoot v0.1.0 (/home/anand/Desktop/dev_space/Prod/rustBoot_mcusigner/rustBoot)
   Compiling filetime v0.2.17
   Compiling rbsigner v0.1.0 (/home/anand/Desktop/dev_space/Prod/rustBoot_mcusigner/rbsigner)
    Finished dev [unoptimized + debuginfo] target(s) in 1.18s
     Running `/home/anand/Desktop/dev_space/Prod/rustBoot_mcusigner/target/debug/rbsigner mcu-image ../boards/sign_images/signed_images/stm32f446_bootfw.bin nistp256 ../boards/sign_images/keygen/ecc256.der 1234`

Update type:    Firmware
Curve type:       nistp256
Input image:      stm32f446_bootfw.bin
Public key:       ecc256.der
Image version:    1234
Output image:     stm32f446_bootfw_v1234_signed.bin
Calculating sha256 digest...
Signing the firmware...
Done.
Output image successfully created with 1948 bytes.

$ cargo run mcu-image ../boards/sign_images/signed_images/stm32f446_updtfw.bin nistp256 ../boards/sign_images/keygen/ecc256.der 1235
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `/home/anand/Desktop/dev_space/Prod/rustBoot_mcusigner/target/debug/rbsigner mcu-image ../boards/sign_images/signed_images/stm32f446_updtfw.bin nistp256 ../boards/sign_images/keygen/ecc256.der 1235`

Update type:    Firmware
Curve type:       nistp256
Input image:      stm32f446_updtfw.bin
Public key:       ecc256.der
Image version:    1235
Output image:     stm32f446_updtfw_v1235_signed.bin
Calculating sha256 digest...
Signing the firmware...
Done.
Output image successfully created with 2092 bytes.

$ probe-rs-cli erase --chip stm32f446retx
$ probe-rs-cli download --format Bin --base-address 0x8020000 --chip stm32f446retx stm32f446_bootfw_v1234_signed.bin
     Erasing sectors ✔ [00:00:03] [##############################################################################################################] 128.00KiB/128.00KiB @ 37.61KiB/s (eta 0s )
 Programming pages   ✔ [00:00:01] [################################################################################################################]  2.00KiB/ 2.00KiB @     296B/s (eta 0s )
    Finished in 5.804s
$ probe-rs-cli download --format Bin --base-address 0x8040000 --chip stm32f446retx stm32f446_updtfw_v1235_signed.bin
     Erasing sectors ✔ [00:00:03] [##############################################################################################################] 128.00KiB/128.00KiB @ 37.75KiB/s (eta 0s )
 Programming pages   ✔ [00:00:02] [################################################################################################################]  3.00KiB/ 3.00KiB @     353B/s (eta 0s )
    Finished in 6.273s
$ cargo flash --chip stm32f446vetx --release
    Finished release [optimized] target(s) in 0.03s
    Flashing /home/anand/Desktop/dev_space/Prod/rustBoot_mcusigner/boards/target/thumbv7em-none-eabihf/release/stm32f446
     Erasing sectors ✔ [00:00:03] [################################################################################################################] 48.00KiB/48.00KiB @ 11.88KiB/s (eta 0s )
 Programming pages   ✔ [00:00:21] [################################################################################################################] 43.00KiB/43.00KiB @  1.69KiB/s (eta 0s )
    Finished in 25.449s

Verifying:

user led is used to confirm that rustBoot works as expected. Here's the flow

  • Upon supplying power to the board, rustBoot takes over
    • validates the firmware image stored in the BOOT partition
    • verifies the signature attached against a known public key stored in the rustBoot image.
  • If the signature checks out, rustBoot boots into the bootfw and blinks a user led for a few seconds, at an interval of 0.5 seconds.
    • post which, the boot firmware triggers the update and performs a system reset.
  • Upon reset, the rustBoot again takes over
    • validates the firmware image stored in the UPDATE partition
    • swaps the contents of the BOOT and the UPDATE partitions
    • marks the new firmware in the BOOT partition as in state STATE_TESTING
    • boots into the UPDATE'd firmware
  • Now that execution-control has been transferred to the UPDATE'd firmware
    • it will attempt to blink a user led at an interval of 1 second.
    • and set a confirmation flag to indicate that the update was successful.
    • post which, it continuously blinks a user led at an interval of 1 second.