2023-03-22 16:49:49 +01:00
|
|
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
2022-01-24 12:54:09 +01:00
|
|
|
#![no_std]
|
2022-08-30 13:07:35 +02:00
|
|
|
#![warn(missing_docs)]
|
2022-11-23 14:48:51 +01:00
|
|
|
#![doc = include_str!("../README.md")]
|
2022-01-24 12:54:09 +01:00
|
|
|
mod fmt;
|
|
|
|
|
2023-03-31 08:05:37 +02:00
|
|
|
mod boot_loader;
|
2023-04-04 12:24:30 +02:00
|
|
|
mod digest_adapters;
|
2023-03-31 08:05:37 +02:00
|
|
|
mod firmware_updater;
|
2023-05-30 13:41:10 +02:00
|
|
|
#[cfg(test)]
|
2023-04-03 15:33:20 +02:00
|
|
|
mod mem_flash;
|
2023-05-30 13:41:10 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test_flash;
|
2023-03-22 16:49:49 +01:00
|
|
|
|
2023-05-30 13:41:10 +02:00
|
|
|
// The expected value of the flash after an erase
|
|
|
|
// TODO: Use the value provided by NorFlash when available
|
|
|
|
pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
|
2023-05-30 13:38:00 +02:00
|
|
|
pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
|
2023-05-30 13:36:42 +02:00
|
|
|
#[cfg(feature = "nightly")]
|
|
|
|
pub use firmware_updater::FirmwareUpdater;
|
|
|
|
pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError};
|
2022-01-24 12:54:09 +01:00
|
|
|
|
2023-03-31 08:05:37 +02:00
|
|
|
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
|
|
|
|
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
|
2022-01-24 12:54:09 +01:00
|
|
|
|
2022-08-30 13:07:35 +02:00
|
|
|
/// The state of the bootloader after running prepare.
|
|
|
|
#[derive(PartialEq, Eq, Debug)]
|
2022-01-24 12:54:09 +01:00
|
|
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
|
|
pub enum State {
|
2022-08-30 13:07:35 +02:00
|
|
|
/// Bootloader is ready to boot the active partition.
|
2022-01-24 12:54:09 +01:00
|
|
|
Boot,
|
2022-08-30 13:07:35 +02:00
|
|
|
/// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
|
2022-01-24 12:54:09 +01:00
|
|
|
Swap,
|
|
|
|
}
|
|
|
|
|
2022-08-30 13:07:35 +02:00
|
|
|
/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
|
|
|
|
#[repr(align(32))]
|
|
|
|
pub struct AlignedBuffer<const N: usize>(pub [u8; N]);
|
|
|
|
|
|
|
|
impl<const N: usize> AsRef<[u8]> for AlignedBuffer<N> {
|
|
|
|
fn as_ref(&self) -> &[u8] {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
|
|
|
|
fn as_mut(&mut self) -> &mut [u8] {
|
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
}
|
2022-04-19 14:42:38 +02:00
|
|
|
|
2022-01-24 12:54:09 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-05-30 13:55:49 +02:00
|
|
|
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
|
|
|
|
#[cfg(feature = "nightly")]
|
|
|
|
use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
|
2022-01-24 12:54:09 +01:00
|
|
|
use futures::executor::block_on;
|
|
|
|
|
2022-06-12 22:15:44 +02:00
|
|
|
use super::*;
|
2023-05-30 13:55:49 +02:00
|
|
|
use crate::boot_loader::BootLoaderConfig;
|
|
|
|
use crate::firmware_updater::FirmwareUpdaterConfig;
|
2023-04-03 15:33:20 +02:00
|
|
|
use crate::mem_flash::MemFlash;
|
2023-05-30 13:55:49 +02:00
|
|
|
#[cfg(feature = "nightly")]
|
|
|
|
use crate::test_flash::AsyncTestFlash;
|
|
|
|
use crate::test_flash::BlockingTestFlash;
|
2022-06-12 22:15:44 +02:00
|
|
|
|
2022-04-20 13:49:59 +02:00
|
|
|
/*
|
2022-01-24 12:54:09 +01:00
|
|
|
#[test]
|
|
|
|
fn test_bad_magic() {
|
|
|
|
let mut flash = MemFlash([0xff; 131072]);
|
2022-08-30 13:07:35 +02:00
|
|
|
let mut flash = SingleFlashConfig::new(&mut flash);
|
2022-01-24 12:54:09 +01:00
|
|
|
|
|
|
|
let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
bootloader.prepare_boot(&mut flash),
|
|
|
|
Err(BootError::BadMagic)
|
|
|
|
);
|
|
|
|
}
|
2022-04-20 13:49:59 +02:00
|
|
|
*/
|
2022-01-24 12:54:09 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_boot_state() {
|
2023-05-30 13:55:49 +02:00
|
|
|
let flash = BlockingTestFlash::new(BootLoaderConfig {
|
|
|
|
active: MemFlash::<57344, 4096, 4>::default(),
|
|
|
|
dfu: MemFlash::<61440, 4096, 4>::default(),
|
|
|
|
state: MemFlash::<4096, 4096, 4>::default(),
|
|
|
|
});
|
2022-04-28 10:38:25 +02:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
flash.state().write(0, &[BOOT_MAGIC; 4]).unwrap();
|
2022-01-24 12:54:09 +01:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
|
|
|
active: flash.active(),
|
|
|
|
dfu: flash.dfu(),
|
|
|
|
state: flash.state(),
|
|
|
|
});
|
2022-01-24 12:54:09 +01:00
|
|
|
|
2022-08-30 13:07:35 +02:00
|
|
|
let mut page = [0; 4096];
|
2023-05-30 13:55:49 +02:00
|
|
|
assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap());
|
2022-01-24 12:54:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2023-04-20 10:56:59 +02:00
|
|
|
#[cfg(all(feature = "nightly", not(feature = "_verify")))]
|
2022-01-24 12:54:09 +01:00
|
|
|
fn test_swap_state() {
|
2023-05-30 13:55:49 +02:00
|
|
|
const FIRMWARE_SIZE: usize = 57344;
|
|
|
|
let flash = AsyncTestFlash::new(BootLoaderConfig {
|
|
|
|
active: MemFlash::<FIRMWARE_SIZE, 4096, 4>::default(),
|
|
|
|
dfu: MemFlash::<61440, 4096, 4>::default(),
|
|
|
|
state: MemFlash::<4096, 4096, 4>::default(),
|
|
|
|
});
|
|
|
|
|
|
|
|
const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];
|
|
|
|
const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];
|
2022-08-30 13:07:35 +02:00
|
|
|
let mut aligned = [0; 4];
|
2022-01-24 12:54:09 +01:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
|
|
|
|
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
|
|
|
|
|
|
|
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
|
|
|
dfu: flash.dfu(),
|
|
|
|
state: flash.state(),
|
|
|
|
});
|
|
|
|
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
|
|
|
|
block_on(updater.mark_updated(&mut aligned)).unwrap();
|
2022-01-24 12:54:09 +01:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
let flash = flash.into_blocking();
|
|
|
|
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
|
|
|
active: flash.active(),
|
|
|
|
dfu: flash.dfu(),
|
|
|
|
state: flash.state(),
|
|
|
|
});
|
2022-01-24 12:54:09 +01:00
|
|
|
|
2023-04-04 21:09:30 +02:00
|
|
|
let mut page = [0; 1024];
|
2023-05-30 13:55:49 +02:00
|
|
|
assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
|
2022-01-24 12:54:09 +01:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
let mut read_buf = [0; FIRMWARE_SIZE];
|
|
|
|
flash.active().read(0, &mut read_buf).unwrap();
|
|
|
|
assert_eq!(UPDATE, read_buf);
|
2022-01-24 12:54:09 +01:00
|
|
|
// First DFU page is untouched
|
2023-05-30 13:55:49 +02:00
|
|
|
flash.dfu().read(4096, &mut read_buf).unwrap();
|
|
|
|
assert_eq!(ORIGINAL, read_buf);
|
2022-01-24 12:54:09 +01:00
|
|
|
|
|
|
|
// Running again should cause a revert
|
2023-05-30 13:55:49 +02:00
|
|
|
assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
|
2022-01-24 12:54:09 +01:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
let mut read_buf = [0; FIRMWARE_SIZE];
|
|
|
|
flash.active().read(0, &mut read_buf).unwrap();
|
|
|
|
assert_eq!(ORIGINAL, read_buf);
|
|
|
|
// Last DFU page is untouched
|
|
|
|
flash.dfu().read(0, &mut read_buf).unwrap();
|
|
|
|
assert_eq!(UPDATE, read_buf);
|
2022-01-24 12:54:09 +01:00
|
|
|
|
|
|
|
// Mark as booted
|
2023-05-30 13:55:49 +02:00
|
|
|
let flash = flash.into_async();
|
|
|
|
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
|
|
|
dfu: flash.dfu(),
|
|
|
|
state: flash.state(),
|
|
|
|
});
|
|
|
|
block_on(updater.mark_booted(&mut aligned)).unwrap();
|
|
|
|
|
|
|
|
let flash = flash.into_blocking();
|
|
|
|
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
|
|
|
active: flash.active(),
|
|
|
|
dfu: flash.dfu(),
|
|
|
|
state: flash.state(),
|
|
|
|
});
|
|
|
|
assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap());
|
2022-01-24 12:54:09 +01:00
|
|
|
}
|
|
|
|
|
2022-04-28 10:38:25 +02:00
|
|
|
#[test]
|
2023-04-20 10:56:59 +02:00
|
|
|
#[cfg(all(feature = "nightly", not(feature = "_verify")))]
|
2023-05-30 13:55:49 +02:00
|
|
|
fn test_swap_state_active_page_biggest() {
|
|
|
|
const FIRMWARE_SIZE: usize = 12288;
|
|
|
|
let flash = AsyncTestFlash::new(BootLoaderConfig {
|
|
|
|
active: MemFlash::<12288, 4096, 8>::random(),
|
|
|
|
dfu: MemFlash::<16384, 2048, 8>::random(),
|
|
|
|
state: MemFlash::<2048, 128, 4>::random(),
|
|
|
|
});
|
|
|
|
|
|
|
|
const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];
|
|
|
|
const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];
|
2022-08-30 13:07:35 +02:00
|
|
|
let mut aligned = [0; 4];
|
2022-04-28 10:38:25 +02:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
|
|
|
|
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
2022-04-28 10:38:25 +02:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
|
|
|
dfu: flash.dfu(),
|
|
|
|
state: flash.state(),
|
|
|
|
});
|
|
|
|
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
|
|
|
|
block_on(updater.mark_updated(&mut aligned)).unwrap();
|
2022-04-28 10:38:25 +02:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
let flash = flash.into_blocking();
|
|
|
|
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
|
|
|
active: flash.active(),
|
|
|
|
dfu: flash.dfu(),
|
|
|
|
state: flash.state(),
|
|
|
|
});
|
2022-04-28 10:38:25 +02:00
|
|
|
|
2022-08-30 13:07:35 +02:00
|
|
|
let mut page = [0; 4096];
|
2023-05-30 13:55:49 +02:00
|
|
|
assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
|
2022-04-28 10:38:25 +02:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
let mut read_buf = [0; FIRMWARE_SIZE];
|
|
|
|
flash.active().read(0, &mut read_buf).unwrap();
|
|
|
|
assert_eq!(UPDATE, read_buf);
|
2022-04-28 10:38:25 +02:00
|
|
|
// First DFU page is untouched
|
2023-05-30 13:55:49 +02:00
|
|
|
flash.dfu().read(4096, &mut read_buf).unwrap();
|
|
|
|
assert_eq!(ORIGINAL, read_buf);
|
2022-04-28 10:38:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2023-04-20 10:56:59 +02:00
|
|
|
#[cfg(all(feature = "nightly", not(feature = "_verify")))]
|
2023-05-30 13:55:49 +02:00
|
|
|
fn test_swap_state_dfu_page_biggest() {
|
|
|
|
const FIRMWARE_SIZE: usize = 12288;
|
|
|
|
let flash = AsyncTestFlash::new(BootLoaderConfig {
|
|
|
|
active: MemFlash::<FIRMWARE_SIZE, 2048, 4>::random(),
|
|
|
|
dfu: MemFlash::<16384, 4096, 8>::random(),
|
|
|
|
state: MemFlash::<2048, 128, 4>::random(),
|
|
|
|
});
|
|
|
|
|
|
|
|
const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];
|
|
|
|
const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];
|
2022-08-30 13:07:35 +02:00
|
|
|
let mut aligned = [0; 4];
|
2022-04-28 10:38:25 +02:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
|
|
|
|
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
|
|
|
|
|
|
|
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
|
|
|
dfu: flash.dfu(),
|
|
|
|
state: flash.state(),
|
|
|
|
});
|
|
|
|
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
|
|
|
|
block_on(updater.mark_updated(&mut aligned)).unwrap();
|
|
|
|
|
|
|
|
let flash = flash.into_blocking();
|
|
|
|
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
|
|
|
active: flash.active(),
|
|
|
|
dfu: flash.dfu(),
|
|
|
|
state: flash.state(),
|
|
|
|
});
|
2022-08-30 13:07:35 +02:00
|
|
|
let mut page = [0; 4096];
|
2023-05-30 13:55:49 +02:00
|
|
|
assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
|
2022-04-28 10:38:25 +02:00
|
|
|
|
2023-05-30 13:55:49 +02:00
|
|
|
let mut read_buf = [0; FIRMWARE_SIZE];
|
|
|
|
flash.active().read(0, &mut read_buf).unwrap();
|
|
|
|
assert_eq!(UPDATE, read_buf);
|
2022-04-28 10:38:25 +02:00
|
|
|
// First DFU page is untouched
|
2023-05-30 13:55:49 +02:00
|
|
|
flash.dfu().read(4096, &mut read_buf).unwrap();
|
|
|
|
assert_eq!(ORIGINAL, read_buf);
|
2022-09-20 14:03:04 +02:00
|
|
|
}
|
|
|
|
|
2023-01-06 12:21:39 +01:00
|
|
|
#[test]
|
2023-04-20 10:56:59 +02:00
|
|
|
#[cfg(all(feature = "nightly", feature = "_verify"))]
|
2023-01-06 12:21:39 +01:00
|
|
|
fn test_verify() {
|
|
|
|
// The following key setup is based on:
|
|
|
|
// https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example
|
|
|
|
|
|
|
|
use ed25519_dalek::Keypair;
|
|
|
|
use rand::rngs::OsRng;
|
|
|
|
|
|
|
|
let mut csprng = OsRng {};
|
|
|
|
let keypair: Keypair = Keypair::generate(&mut csprng);
|
|
|
|
|
|
|
|
use ed25519_dalek::{Digest, Sha512, Signature, Signer};
|
|
|
|
let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU.";
|
|
|
|
let mut digest = Sha512::new();
|
|
|
|
digest.update(&firmware);
|
|
|
|
let message = digest.finalize();
|
|
|
|
let signature: Signature = keypair.sign(&message);
|
|
|
|
|
|
|
|
use ed25519_dalek::PublicKey;
|
|
|
|
let public_key: PublicKey = keypair.public;
|
|
|
|
|
|
|
|
// Setup flash
|
2023-05-30 13:55:49 +02:00
|
|
|
let flash = BlockingTestFlash::new(BootLoaderConfig {
|
|
|
|
active: MemFlash::<0, 0, 0>::default(),
|
|
|
|
dfu: MemFlash::<4096, 4096, 4>::default(),
|
|
|
|
state: MemFlash::<4096, 4096, 4>::default(),
|
|
|
|
});
|
2023-01-06 12:21:39 +01:00
|
|
|
|
|
|
|
let firmware_len = firmware.len();
|
|
|
|
|
|
|
|
let mut write_buf = [0; 4096];
|
|
|
|
write_buf[0..firmware_len].copy_from_slice(firmware);
|
2023-05-30 13:55:49 +02:00
|
|
|
flash.dfu().write(0, &write_buf).unwrap();
|
2023-01-06 12:21:39 +01:00
|
|
|
|
|
|
|
// On with the test
|
2023-05-30 13:55:49 +02:00
|
|
|
let flash = flash.into_async();
|
|
|
|
let mut updater = FirmwareUpdater::new(flash.dfu(), flash.state());
|
2023-01-06 12:21:39 +01:00
|
|
|
|
|
|
|
let mut aligned = [0; 4];
|
|
|
|
|
|
|
|
assert!(block_on(updater.verify_and_mark_updated(
|
|
|
|
&public_key.to_bytes(),
|
|
|
|
&signature.to_bytes(),
|
2023-04-11 07:46:05 +02:00
|
|
|
firmware_len as u32,
|
2023-01-06 12:21:39 +01:00
|
|
|
&mut aligned,
|
|
|
|
))
|
|
|
|
.is_ok());
|
|
|
|
}
|
2022-01-24 12:54:09 +01:00
|
|
|
}
|