From e0775fbc8ab1ecc83bce42fe6e11accf481bc9e1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 11 Jan 2024 18:55:59 +0100 Subject: Flatten embassy-boot dir tree --- embassy-boot/boot/src/boot_loader.rs | 411 --------------------- .../boot/src/digest_adapters/ed25519_dalek.rs | 30 -- embassy-boot/boot/src/digest_adapters/mod.rs | 5 - embassy-boot/boot/src/digest_adapters/salty.rs | 29 -- embassy-boot/boot/src/firmware_updater/asynch.rs | 329 ----------------- embassy-boot/boot/src/firmware_updater/blocking.rs | 340 ----------------- embassy-boot/boot/src/firmware_updater/mod.rs | 49 --- embassy-boot/boot/src/fmt.rs | 258 ------------- embassy-boot/boot/src/lib.rs | 323 ---------------- embassy-boot/boot/src/mem_flash.rs | 170 --------- embassy-boot/boot/src/test_flash/asynch.rs | 64 ---- embassy-boot/boot/src/test_flash/blocking.rs | 68 ---- embassy-boot/boot/src/test_flash/mod.rs | 5 - 13 files changed, 2081 deletions(-) delete mode 100644 embassy-boot/boot/src/boot_loader.rs delete mode 100644 embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs delete mode 100644 embassy-boot/boot/src/digest_adapters/mod.rs delete mode 100644 embassy-boot/boot/src/digest_adapters/salty.rs delete mode 100644 embassy-boot/boot/src/firmware_updater/asynch.rs delete mode 100644 embassy-boot/boot/src/firmware_updater/blocking.rs delete mode 100644 embassy-boot/boot/src/firmware_updater/mod.rs delete mode 100644 embassy-boot/boot/src/fmt.rs delete mode 100644 embassy-boot/boot/src/lib.rs delete mode 100644 embassy-boot/boot/src/mem_flash.rs delete mode 100644 embassy-boot/boot/src/test_flash/asynch.rs delete mode 100644 embassy-boot/boot/src/test_flash/blocking.rs delete mode 100644 embassy-boot/boot/src/test_flash/mod.rs (limited to 'embassy-boot/boot/src') diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs deleted file mode 100644 index e568001bc..000000000 --- a/embassy-boot/boot/src/boot_loader.rs +++ /dev/null @@ -1,411 +0,0 @@ -use core::cell::RefCell; - -use embassy_embedded_hal::flash::partition::BlockingPartition; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::blocking_mutex::Mutex; -use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; - -use crate::{State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; - -/// Errors returned by bootloader -#[derive(PartialEq, Eq, Debug)] -pub enum BootError { - /// Error from flash. - Flash(NorFlashErrorKind), - /// Invalid bootloader magic - BadMagic, -} - -#[cfg(feature = "defmt")] -impl defmt::Format for BootError { - fn format(&self, fmt: defmt::Formatter) { - match self { - BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"), - BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"), - } - } -} - -impl From for BootError -where - E: NorFlashError, -{ - fn from(error: E) -> Self { - BootError::Flash(error.kind()) - } -} - -/// Bootloader flash configuration holding the three flashes used by the bootloader -/// -/// If only a single flash is actually used, then that flash should be partitioned into three partitions before use. -/// The easiest way to do this is to use [`BootLoaderConfig::from_linkerfile_blocking`] which will partition -/// the provided flash according to symbols defined in the linkerfile. -pub struct BootLoaderConfig { - /// Flash type used for the active partition - the partition which will be booted from. - pub active: ACTIVE, - /// Flash type used for the dfu partition - the partition which will be swapped in when requested. - pub dfu: DFU, - /// Flash type used for the state partition. - pub state: STATE, -} - -impl<'a, FLASH: NorFlash> - BootLoaderConfig< - BlockingPartition<'a, NoopRawMutex, FLASH>, - BlockingPartition<'a, NoopRawMutex, FLASH>, - BlockingPartition<'a, NoopRawMutex, FLASH>, - > -{ - /// Create a bootloader config from the flash and address symbols defined in the linkerfile - // #[cfg(target_os = "none")] - pub fn from_linkerfile_blocking(flash: &'a Mutex>) -> Self { - extern "C" { - static __bootloader_state_start: u32; - static __bootloader_state_end: u32; - static __bootloader_active_start: u32; - static __bootloader_active_end: u32; - static __bootloader_dfu_start: u32; - static __bootloader_dfu_end: u32; - } - - let active = unsafe { - let start = &__bootloader_active_start as *const u32 as u32; - let end = &__bootloader_active_end as *const u32 as u32; - trace!("ACTIVE: 0x{:x} - 0x{:x}", start, end); - - BlockingPartition::new(flash, start, end - start) - }; - let dfu = unsafe { - let start = &__bootloader_dfu_start as *const u32 as u32; - let end = &__bootloader_dfu_end as *const u32 as u32; - trace!("DFU: 0x{:x} - 0x{:x}", start, end); - - BlockingPartition::new(flash, start, end - start) - }; - let state = unsafe { - let start = &__bootloader_state_start as *const u32 as u32; - let end = &__bootloader_state_end as *const u32 as u32; - trace!("STATE: 0x{:x} - 0x{:x}", start, end); - - BlockingPartition::new(flash, start, end - start) - }; - - Self { active, dfu, state } - } -} - -/// BootLoader works with any flash implementing embedded_storage. -pub struct BootLoader { - active: ACTIVE, - dfu: DFU, - /// The state partition has the following format: - /// All ranges are in multiples of WRITE_SIZE bytes. - /// | Range | Description | - /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | - /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | - /// | 2..2 + N | Progress index used while swapping or reverting - state: STATE, -} - -impl BootLoader { - /// Get the page size which is the "unit of operation" within the bootloader. - const PAGE_SIZE: u32 = if ACTIVE::ERASE_SIZE > DFU::ERASE_SIZE { - ACTIVE::ERASE_SIZE as u32 - } else { - DFU::ERASE_SIZE as u32 - }; - - /// Create a new instance of a bootloader with the flash partitions. - /// - /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. - /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. - pub fn new(config: BootLoaderConfig) -> Self { - Self { - active: config.active, - dfu: config.dfu, - state: config.state, - } - } - - /// Perform necessary boot preparations like swapping images. - /// - /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap - /// algorithm to work correctly. - /// - /// The provided aligned_buf argument must satisfy any alignment requirements - /// given by the partition flashes. All flash operations will use this buffer. - /// - /// ## SWAPPING - /// - /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition. - /// The swap index contains the copy progress, as to allow continuation of the copy process on - /// power failure. The index counter is represented within 1 or more pages (depending on total - /// flash size), where a page X is considered swapped if index at location (`X + WRITE_SIZE`) - /// contains a zero value. This ensures that index updates can be performed atomically and - /// avoid a situation where the wrong index value is set (page write size is "atomic"). - /// - /// - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// |-----------|------------|--------|--------|--------|--------| - /// | Active | 0 | 1 | 2 | 3 | - | - /// | DFU | 0 | 3 | 2 | 1 | X | - /// - /// The algorithm starts by copying 'backwards', and after the first step, the layout is - /// as follows: - /// - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// |-----------|------------|--------|--------|--------|--------| - /// | Active | 1 | 1 | 2 | 1 | - | - /// | DFU | 1 | 3 | 2 | 1 | 3 | - /// - /// The next iteration performs the same steps - /// - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// |-----------|------------|--------|--------|--------|--------| - /// | Active | 2 | 1 | 2 | 1 | - | - /// | DFU | 2 | 3 | 2 | 2 | 3 | - /// - /// And again until we're done - /// - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// |-----------|------------|--------|--------|--------|--------| - /// | Active | 3 | 3 | 2 | 1 | - | - /// | DFU | 3 | 3 | 1 | 2 | 3 | - /// - /// ## REVERTING - /// - /// The reverting algorithm uses the swap index to discover that images were swapped, but that - /// the application failed to mark the boot successful. In this case, the revert algorithm will - /// run. - /// - /// The revert index is located separately from the swap index, to ensure that revert can continue - /// on power failure. - /// - /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start. - /// - /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// |-----------|--------------|--------|--------|--------|--------| - /// | Active | 3 | 1 | 2 | 1 | - | - /// | DFU | 3 | 3 | 1 | 2 | 3 | - /// - /// - /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// |-----------|--------------|--------|--------|--------|--------| - /// | Active | 3 | 1 | 2 | 1 | - | - /// | DFU | 3 | 3 | 2 | 2 | 3 | - /// - /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// |-----------|--------------|--------|--------|--------|--------| - /// | Active | 3 | 1 | 2 | 3 | - | - /// | DFU | 3 | 3 | 2 | 1 | 3 | - /// - pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result { - // Ensure we have enough progress pages to store copy progress - assert_eq!(0, Self::PAGE_SIZE % aligned_buf.len() as u32); - assert_eq!(0, Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32); - assert_eq!(0, Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32); - assert_eq!(0, Self::PAGE_SIZE % DFU::WRITE_SIZE as u32); - assert_eq!(0, Self::PAGE_SIZE % DFU::ERASE_SIZE as u32); - assert!(aligned_buf.len() >= STATE::WRITE_SIZE); - assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE); - assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE); - - // Ensure our partitions are able to handle boot operations - assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE); - - // Copy contents from partition N to active - let state = self.read_state(aligned_buf)?; - if state == State::Swap { - // - // Check if we already swapped. If we're in the swap state, this means we should revert - // since the app has failed to mark boot as successful - // - if !self.is_swapped(aligned_buf)? { - trace!("Swapping"); - self.swap(aligned_buf)?; - trace!("Swapping done"); - } else { - trace!("Reverting"); - self.revert(aligned_buf)?; - - let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; - - // Invalidate progress - state_word.fill(!STATE_ERASE_VALUE); - self.state.write(STATE::WRITE_SIZE as u32, state_word)?; - - // Clear magic and progress - self.state.erase(0, self.state.capacity() as u32)?; - - // Set magic - state_word.fill(BOOT_MAGIC); - self.state.write(0, state_word)?; - } - } - Ok(state) - } - - fn is_swapped(&mut self, aligned_buf: &mut [u8]) -> Result { - let page_count = self.active.capacity() / Self::PAGE_SIZE as usize; - let progress = self.current_progress(aligned_buf)?; - - Ok(progress >= page_count * 2) - } - - fn current_progress(&mut self, aligned_buf: &mut [u8]) -> Result { - let write_size = STATE::WRITE_SIZE as u32; - let max_index = ((self.state.capacity() - STATE::WRITE_SIZE) / STATE::WRITE_SIZE) - 2; - let state_word = &mut aligned_buf[..write_size as usize]; - - self.state.read(write_size, state_word)?; - if state_word.iter().any(|&b| b != STATE_ERASE_VALUE) { - // Progress is invalid - return Ok(max_index); - } - - for index in 0..max_index { - self.state.read((2 + index) as u32 * write_size, state_word)?; - - if state_word.iter().any(|&b| b == STATE_ERASE_VALUE) { - return Ok(index); - } - } - Ok(max_index) - } - - fn update_progress(&mut self, progress_index: usize, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; - state_word.fill(!STATE_ERASE_VALUE); - self.state - .write((2 + progress_index) as u32 * STATE::WRITE_SIZE as u32, state_word)?; - Ok(()) - } - - fn copy_page_once_to_active( - &mut self, - progress_index: usize, - from_offset: u32, - to_offset: u32, - aligned_buf: &mut [u8], - ) -> Result<(), BootError> { - if self.current_progress(aligned_buf)? <= progress_index { - let page_size = Self::PAGE_SIZE as u32; - - self.active.erase(to_offset, to_offset + page_size)?; - - for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { - self.dfu.read(from_offset + offset_in_page as u32, aligned_buf)?; - self.active.write(to_offset + offset_in_page as u32, aligned_buf)?; - } - - self.update_progress(progress_index, aligned_buf)?; - } - Ok(()) - } - - fn copy_page_once_to_dfu( - &mut self, - progress_index: usize, - from_offset: u32, - to_offset: u32, - aligned_buf: &mut [u8], - ) -> Result<(), BootError> { - if self.current_progress(aligned_buf)? <= progress_index { - let page_size = Self::PAGE_SIZE as u32; - - self.dfu.erase(to_offset as u32, to_offset + page_size)?; - - for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { - self.active.read(from_offset + offset_in_page as u32, aligned_buf)?; - self.dfu.write(to_offset + offset_in_page as u32, aligned_buf)?; - } - - self.update_progress(progress_index, aligned_buf)?; - } - Ok(()) - } - - fn swap(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE; - for page_num in 0..page_count { - let progress_index = (page_num * 2) as usize; - - // Copy active page to the 'next' DFU page. - let active_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; - let dfu_to_offset = (page_count - page_num) * Self::PAGE_SIZE; - //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); - self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?; - - // Copy DFU page to the active page - let active_to_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; - let dfu_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; - //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); - self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?; - } - - Ok(()) - } - - fn revert(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE; - for page_num in 0..page_count { - let progress_index = (page_count * 2 + page_num * 2) as usize; - - // Copy the bad active page to the DFU page - let active_from_offset = page_num * Self::PAGE_SIZE; - let dfu_to_offset = page_num * Self::PAGE_SIZE; - self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?; - - // Copy the DFU page back to the active page - let active_to_offset = page_num * Self::PAGE_SIZE; - let dfu_from_offset = (page_num + 1) * Self::PAGE_SIZE; - self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?; - } - - Ok(()) - } - - fn read_state(&mut self, aligned_buf: &mut [u8]) -> Result { - let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; - self.state.read(0, state_word)?; - - if !state_word.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) { - Ok(State::DfuDetach) - } else { - Ok(State::Boot) - } - } -} - -fn assert_partitions( - active: &ACTIVE, - dfu: &DFU, - state: &STATE, - page_size: u32, -) { - assert_eq!(active.capacity() as u32 % page_size, 0); - assert_eq!(dfu.capacity() as u32 % page_size, 0); - // DFU partition has to be bigger than ACTIVE partition to handle swap algorithm - assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); - assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mem_flash::MemFlash; - - #[test] - #[should_panic] - fn test_range_asserts() { - const ACTIVE_SIZE: usize = 4194304 - 4096; - const DFU_SIZE: usize = 4194304; - const STATE_SIZE: usize = 4096; - static ACTIVE: MemFlash = MemFlash::new(0xFF); - static DFU: MemFlash = MemFlash::new(0xFF); - static STATE: MemFlash = MemFlash::new(0xFF); - assert_partitions(&ACTIVE, &DFU, &STATE, 4096); - } -} diff --git a/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs deleted file mode 100644 index 2e4e03da3..000000000 --- a/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs +++ /dev/null @@ -1,30 +0,0 @@ -use digest::typenum::U64; -use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; -use ed25519_dalek::Digest; - -pub struct Sha512(ed25519_dalek::Sha512); - -impl Default for Sha512 { - fn default() -> Self { - Self(ed25519_dalek::Sha512::new()) - } -} - -impl Update for Sha512 { - fn update(&mut self, data: &[u8]) { - Digest::update(&mut self.0, data) - } -} - -impl FixedOutput for Sha512 { - fn finalize_into(self, out: &mut digest::Output) { - let result = self.0.finalize(); - out.as_mut_slice().copy_from_slice(result.as_slice()) - } -} - -impl OutputSizeUser for Sha512 { - type OutputSize = U64; -} - -impl HashMarker for Sha512 {} diff --git a/embassy-boot/boot/src/digest_adapters/mod.rs b/embassy-boot/boot/src/digest_adapters/mod.rs deleted file mode 100644 index 9b4b4b60c..000000000 --- a/embassy-boot/boot/src/digest_adapters/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[cfg(feature = "ed25519-dalek")] -pub(crate) mod ed25519_dalek; - -#[cfg(feature = "ed25519-salty")] -pub(crate) mod salty; diff --git a/embassy-boot/boot/src/digest_adapters/salty.rs b/embassy-boot/boot/src/digest_adapters/salty.rs deleted file mode 100644 index 2b5dcf3af..000000000 --- a/embassy-boot/boot/src/digest_adapters/salty.rs +++ /dev/null @@ -1,29 +0,0 @@ -use digest::typenum::U64; -use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; - -pub struct Sha512(salty::Sha512); - -impl Default for Sha512 { - fn default() -> Self { - Self(salty::Sha512::new()) - } -} - -impl Update for Sha512 { - fn update(&mut self, data: &[u8]) { - self.0.update(data) - } -} - -impl FixedOutput for Sha512 { - fn finalize_into(self, out: &mut digest::Output) { - let result = self.0.finalize(); - out.as_mut_slice().copy_from_slice(result.as_slice()) - } -} - -impl OutputSizeUser for Sha512 { - type OutputSize = U64; -} - -impl HashMarker for Sha512 {} diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs deleted file mode 100644 index 2e43e1cc1..000000000 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ /dev/null @@ -1,329 +0,0 @@ -use digest::Digest; -#[cfg(target_os = "none")] -use embassy_embedded_hal::flash::partition::Partition; -#[cfg(target_os = "none")] -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embedded_storage_async::nor_flash::NorFlash; - -use super::FirmwareUpdaterConfig; -use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; - -/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to -/// 'mess up' the internal bootloader state -pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { - dfu: DFU, - state: FirmwareState<'d, STATE>, -} - -#[cfg(target_os = "none")] -impl<'a, FLASH: NorFlash> - FirmwareUpdaterConfig, Partition<'a, NoopRawMutex, FLASH>> -{ - /// Create a firmware updater config from the flash and address symbols defined in the linkerfile - pub fn from_linkerfile(flash: &'a embassy_sync::mutex::Mutex) -> Self { - extern "C" { - static __bootloader_state_start: u32; - static __bootloader_state_end: u32; - static __bootloader_dfu_start: u32; - static __bootloader_dfu_end: u32; - } - - let dfu = unsafe { - let start = &__bootloader_dfu_start as *const u32 as u32; - let end = &__bootloader_dfu_end as *const u32 as u32; - trace!("DFU: 0x{:x} - 0x{:x}", start, end); - - Partition::new(flash, start, end - start) - }; - let state = unsafe { - let start = &__bootloader_state_start as *const u32 as u32; - let end = &__bootloader_state_end as *const u32 as u32; - trace!("STATE: 0x{:x} - 0x{:x}", start, end); - - Partition::new(flash, start, end - start) - }; - - Self { dfu, state } - } -} - -impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { - /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub fn new(config: FirmwareUpdaterConfig, aligned: &'d mut [u8]) -> Self { - Self { - dfu: config.dfu, - state: FirmwareState::new(config.state, aligned), - } - } - - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub async fn get_state(&mut self) -> Result { - self.state.get_state().await - } - - /// Verify the DFU given a public key. If there is an error then DO NOT - /// proceed with updating the firmware as it must be signed with a - /// corresponding private key (otherwise it could be malicious firmware). - /// - /// Mark to trigger firmware swap on next boot if verify suceeds. - /// - /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have - /// been generated from a SHA-512 digest of the firmware bytes. - /// - /// If no signature feature is set then this method will always return a - /// signature error. - #[cfg(feature = "_verify")] - pub async fn verify_and_mark_updated( - &mut self, - _public_key: &[u8; 32], - _signature: &[u8; 64], - _update_len: u32, - ) -> Result<(), FirmwareUpdaterError> { - assert!(_update_len <= self.dfu.capacity() as u32); - - self.state.verify_booted().await?; - - #[cfg(feature = "ed25519-dalek")] - { - use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey}; - - use crate::digest_adapters::ed25519_dalek::Sha512; - - let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); - - let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?; - let signature = Signature::from_bytes(_signature); - - let mut chunk_buf = [0; 2]; - let mut message = [0; 64]; - self.hash::(_update_len, &mut chunk_buf, &mut message).await?; - - public_key.verify(&message, &signature).map_err(into_signature_error)? - } - #[cfg(feature = "ed25519-salty")] - { - use salty::{PublicKey, Signature}; - - use crate::digest_adapters::salty::Sha512; - - fn into_signature_error(_: E) -> FirmwareUpdaterError { - FirmwareUpdaterError::Signature(signature::Error::default()) - } - - let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?; - let signature = Signature::try_from(_signature).map_err(into_signature_error)?; - - let mut message = [0; 64]; - let mut chunk_buf = [0; 2]; - self.hash::(_update_len, &mut chunk_buf, &mut message).await?; - - let r = public_key.verify(&message, &signature); - trace!( - "Verifying with public key {}, signature {} and message {} yields ok: {}", - public_key.to_bytes(), - signature.to_bytes(), - message, - r.is_ok() - ); - r.map_err(into_signature_error)? - } - - self.state.mark_updated().await - } - - /// Verify the update in DFU with any digest. - pub async fn hash( - &mut self, - update_len: u32, - chunk_buf: &mut [u8], - output: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - let mut digest = D::new(); - for offset in (0..update_len).step_by(chunk_buf.len()) { - self.dfu.read(offset, chunk_buf).await?; - let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); - digest.update(&chunk_buf[..len]); - } - output.copy_from_slice(digest.finalize().as_slice()); - Ok(()) - } - - /// Mark to trigger firmware swap on next boot. - #[cfg(not(feature = "_verify"))] - pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { - self.state.mark_updated().await - } - - /// Mark to trigger USB DFU on next boot. - pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { - self.state.verify_booted().await?; - self.state.mark_dfu().await - } - - /// Mark firmware boot successful and stop rollback on reset. - pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { - self.state.mark_booted().await - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= DFU::ERASE_SIZE); - - self.state.verify_booted().await?; - - self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; - - self.dfu.write(offset as u32, data).await?; - - Ok(()) - } - - /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning its `Partition`. - /// - /// Using this instead of `write_firmware` allows for an optimized API in - /// exchange for added complexity. - pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { - self.state.verify_booted().await?; - self.dfu.erase(0, self.dfu.capacity() as u32).await?; - - Ok(&mut self.dfu) - } -} - -/// Manages the state partition of the firmware update. -/// -/// Can be used standalone for more fine grained control, or as part of the updater. -pub struct FirmwareState<'d, STATE> { - state: STATE, - aligned: &'d mut [u8], -} - -impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { - /// Create a firmware state instance from a FirmwareUpdaterConfig with a buffer for magic content and state partition. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - pub fn from_config(config: FirmwareUpdaterConfig, aligned: &'d mut [u8]) -> Self { - Self::new(config.state, aligned) - } - - /// Create a firmware state instance with a buffer for magic content and state partition. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of maximum of STATE::WRITE_SIZE and STATE::READ_SIZE, - /// and follow the alignment rules for the flash being read from and written to. - pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { - assert_eq!(aligned.len(), STATE::WRITE_SIZE.max(STATE::READ_SIZE)); - Self { state, aligned } - } - - // Make sure we are running a booted firmware to avoid reverting to a bad state. - async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { - if self.get_state().await? == State::Boot { - Ok(()) - } else { - Err(FirmwareUpdaterError::BadState) - } - } - - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub async fn get_state(&mut self) -> Result { - self.state.read(0, &mut self.aligned).await?; - - if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } - - /// Mark to trigger firmware swap on next boot. - pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { - self.set_magic(SWAP_MAGIC).await - } - - /// Mark to trigger USB DFU on next boot. - pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { - self.set_magic(DFU_DETACH_MAGIC).await - } - - /// Mark firmware boot successful and stop rollback on reset. - pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { - self.set_magic(BOOT_MAGIC).await - } - - async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> { - self.state.read(0, &mut self.aligned).await?; - - if self.aligned.iter().any(|&b| b != magic) { - // Read progress validity - self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?; - - if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { - // The current progress validity marker is invalid - } else { - // Invalidate progress - self.aligned.fill(!STATE_ERASE_VALUE); - self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?; - } - - // Clear magic and progress - self.state.erase(0, self.state.capacity() as u32).await?; - - // Set magic - self.aligned.fill(magic); - self.state.write(0, &self.aligned).await?; - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use embassy_embedded_hal::flash::partition::Partition; - use embassy_sync::blocking_mutex::raw::NoopRawMutex; - use embassy_sync::mutex::Mutex; - use futures::executor::block_on; - use sha1::{Digest, Sha1}; - - use super::*; - use crate::mem_flash::MemFlash; - - #[test] - fn can_verify_sha1() { - let flash = Mutex::::new(MemFlash::<131072, 4096, 8>::default()); - let state = Partition::new(&flash, 0, 4096); - let dfu = Partition::new(&flash, 65536, 65536); - let mut aligned = [0; 8]; - - let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; - let mut to_write = [0; 4096]; - to_write[..7].copy_from_slice(update.as_slice()); - - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); - block_on(updater.write_firmware(0, to_write.as_slice())).unwrap(); - let mut chunk_buf = [0; 2]; - let mut hash = [0; 20]; - block_on(updater.hash::(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); - - assert_eq!(Sha1::digest(update).as_slice(), hash); - } -} diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs deleted file mode 100644 index f1368540d..000000000 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ /dev/null @@ -1,340 +0,0 @@ -use digest::Digest; -#[cfg(target_os = "none")] -use embassy_embedded_hal::flash::partition::BlockingPartition; -#[cfg(target_os = "none")] -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embedded_storage::nor_flash::NorFlash; - -use super::FirmwareUpdaterConfig; -use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; - -/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to -/// 'mess up' the internal bootloader state -pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { - dfu: DFU, - state: BlockingFirmwareState<'d, STATE>, -} - -#[cfg(target_os = "none")] -impl<'a, FLASH: NorFlash> - FirmwareUpdaterConfig, BlockingPartition<'a, NoopRawMutex, FLASH>> -{ - /// Create a firmware updater config from the flash and address symbols defined in the linkerfile - pub fn from_linkerfile_blocking( - flash: &'a embassy_sync::blocking_mutex::Mutex>, - ) -> Self { - extern "C" { - static __bootloader_state_start: u32; - static __bootloader_state_end: u32; - static __bootloader_dfu_start: u32; - static __bootloader_dfu_end: u32; - } - - let dfu = unsafe { - let start = &__bootloader_dfu_start as *const u32 as u32; - let end = &__bootloader_dfu_end as *const u32 as u32; - trace!("DFU: 0x{:x} - 0x{:x}", start, end); - - BlockingPartition::new(flash, start, end - start) - }; - let state = unsafe { - let start = &__bootloader_state_start as *const u32 as u32; - let end = &__bootloader_state_end as *const u32 as u32; - trace!("STATE: 0x{:x} - 0x{:x}", start, end); - - BlockingPartition::new(flash, start, end - start) - }; - - Self { dfu, state } - } -} - -impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> { - /// Create a firmware updater instance with partition ranges for the update and state partitions. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - pub fn new(config: FirmwareUpdaterConfig, aligned: &'d mut [u8]) -> Self { - Self { - dfu: config.dfu, - state: BlockingFirmwareState::new(config.state, aligned), - } - } - - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub fn get_state(&mut self) -> Result { - self.state.get_state() - } - - /// Verify the DFU given a public key. If there is an error then DO NOT - /// proceed with updating the firmware as it must be signed with a - /// corresponding private key (otherwise it could be malicious firmware). - /// - /// Mark to trigger firmware swap on next boot if verify suceeds. - /// - /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have - /// been generated from a SHA-512 digest of the firmware bytes. - /// - /// If no signature feature is set then this method will always return a - /// signature error. - #[cfg(feature = "_verify")] - pub fn verify_and_mark_updated( - &mut self, - _public_key: &[u8; 32], - _signature: &[u8; 64], - _update_len: u32, - ) -> Result<(), FirmwareUpdaterError> { - assert!(_update_len <= self.dfu.capacity() as u32); - - self.state.verify_booted()?; - - #[cfg(feature = "ed25519-dalek")] - { - use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey}; - - use crate::digest_adapters::ed25519_dalek::Sha512; - - let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); - - let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?; - let signature = Signature::from_bytes(_signature); - - let mut message = [0; 64]; - let mut chunk_buf = [0; 2]; - self.hash::(_update_len, &mut chunk_buf, &mut message)?; - - public_key.verify(&message, &signature).map_err(into_signature_error)? - } - #[cfg(feature = "ed25519-salty")] - { - use salty::{PublicKey, Signature}; - - use crate::digest_adapters::salty::Sha512; - - fn into_signature_error(_: E) -> FirmwareUpdaterError { - FirmwareUpdaterError::Signature(signature::Error::default()) - } - - let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?; - let signature = Signature::try_from(_signature).map_err(into_signature_error)?; - - let mut message = [0; 64]; - let mut chunk_buf = [0; 2]; - self.hash::(_update_len, &mut chunk_buf, &mut message)?; - - let r = public_key.verify(&message, &signature); - trace!( - "Verifying with public key {}, signature {} and message {} yields ok: {}", - public_key.to_bytes(), - signature.to_bytes(), - message, - r.is_ok() - ); - r.map_err(into_signature_error)? - } - - self.state.mark_updated() - } - - /// Verify the update in DFU with any digest. - pub fn hash( - &mut self, - update_len: u32, - chunk_buf: &mut [u8], - output: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - let mut digest = D::new(); - for offset in (0..update_len).step_by(chunk_buf.len()) { - self.dfu.read(offset, chunk_buf)?; - let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); - digest.update(&chunk_buf[..len]); - } - output.copy_from_slice(digest.finalize().as_slice()); - Ok(()) - } - - /// Mark to trigger firmware swap on next boot. - #[cfg(not(feature = "_verify"))] - pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { - self.state.mark_updated() - } - - /// Mark to trigger USB DFU device on next boot. - pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { - self.state.verify_booted()?; - self.state.mark_dfu() - } - - /// Mark firmware boot successful and stop rollback on reset. - pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { - self.state.mark_booted() - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= DFU::ERASE_SIZE); - self.state.verify_booted()?; - - self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; - - self.dfu.write(offset as u32, data)?; - - Ok(()) - } - - /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning its `Partition`. - /// - /// Using this instead of `write_firmware` allows for an optimized API in - /// exchange for added complexity. - pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { - self.state.verify_booted()?; - self.dfu.erase(0, self.dfu.capacity() as u32)?; - - Ok(&mut self.dfu) - } -} - -/// Manages the state partition of the firmware update. -/// -/// Can be used standalone for more fine grained control, or as part of the updater. -pub struct BlockingFirmwareState<'d, STATE> { - state: STATE, - aligned: &'d mut [u8], -} - -impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { - /// Creates a firmware state instance from a FirmwareUpdaterConfig, with a buffer for magic content and state partition. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - pub fn from_config(config: FirmwareUpdaterConfig, aligned: &'d mut [u8]) -> Self { - Self::new(config.state, aligned) - } - - /// Create a firmware state instance with a buffer for magic content and state partition. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - Self { state, aligned } - } - - // Make sure we are running a booted firmware to avoid reverting to a bad state. - fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { - if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach { - Ok(()) - } else { - Err(FirmwareUpdaterError::BadState) - } - } - - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub fn get_state(&mut self) -> Result { - self.state.read(0, &mut self.aligned)?; - - if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) { - Ok(State::DfuDetach) - } else { - Ok(State::Boot) - } - } - - /// Mark to trigger firmware swap on next boot. - pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { - self.set_magic(SWAP_MAGIC) - } - - /// Mark to trigger USB DFU on next boot. - pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { - self.set_magic(DFU_DETACH_MAGIC) - } - - /// Mark firmware boot successful and stop rollback on reset. - pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { - self.set_magic(BOOT_MAGIC) - } - - fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> { - self.state.read(0, &mut self.aligned)?; - - if self.aligned.iter().any(|&b| b != magic) { - // Read progress validity - self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned)?; - - if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { - // The current progress validity marker is invalid - } else { - // Invalidate progress - self.aligned.fill(!STATE_ERASE_VALUE); - self.state.write(STATE::WRITE_SIZE as u32, &self.aligned)?; - } - - // Clear magic and progress - self.state.erase(0, self.state.capacity() as u32)?; - - // Set magic - self.aligned.fill(magic); - self.state.write(0, &self.aligned)?; - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use core::cell::RefCell; - - use embassy_embedded_hal::flash::partition::BlockingPartition; - use embassy_sync::blocking_mutex::raw::NoopRawMutex; - use embassy_sync::blocking_mutex::Mutex; - use sha1::{Digest, Sha1}; - - use super::*; - use crate::mem_flash::MemFlash; - - #[test] - fn can_verify_sha1() { - let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 4096, 8>::default())); - let state = BlockingPartition::new(&flash, 0, 4096); - let dfu = BlockingPartition::new(&flash, 65536, 65536); - let mut aligned = [0; 8]; - - let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; - let mut to_write = [0; 4096]; - to_write[..7].copy_from_slice(update.as_slice()); - - let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); - updater.write_firmware(0, to_write.as_slice()).unwrap(); - let mut chunk_buf = [0; 2]; - let mut hash = [0; 20]; - updater - .hash::(update.len() as u32, &mut chunk_buf, &mut hash) - .unwrap(); - - assert_eq!(Sha1::digest(update).as_slice(), hash); - } -} diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs deleted file mode 100644 index 4814786bf..000000000 --- a/embassy-boot/boot/src/firmware_updater/mod.rs +++ /dev/null @@ -1,49 +0,0 @@ -mod asynch; -mod blocking; - -pub use asynch::{FirmwareState, FirmwareUpdater}; -pub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater}; -use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; - -/// Firmware updater flash configuration holding the two flashes used by the updater -/// -/// If only a single flash is actually used, then that flash should be partitioned into two partitions before use. -/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition -/// the provided flash according to symbols defined in the linkerfile. -pub struct FirmwareUpdaterConfig { - /// The dfu flash partition - pub dfu: DFU, - /// The state flash partition - pub state: STATE, -} - -/// Errors returned by FirmwareUpdater -#[derive(Debug)] -pub enum FirmwareUpdaterError { - /// Error from flash. - Flash(NorFlashErrorKind), - /// Signature errors. - Signature(signature::Error), - /// Bad state. - BadState, -} - -#[cfg(feature = "defmt")] -impl defmt::Format for FirmwareUpdaterError { - fn format(&self, fmt: defmt::Formatter) { - match self { - FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), - FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), - FirmwareUpdaterError::BadState => defmt::write!(fmt, "FirmwareUpdaterError::BadState"), - } - } -} - -impl From for FirmwareUpdaterError -where - E: NorFlashError, -{ - fn from(error: E) -> Self { - FirmwareUpdaterError::Flash(error.kind()) - } -} diff --git a/embassy-boot/boot/src/fmt.rs b/embassy-boot/boot/src/fmt.rs deleted file mode 100644 index 78e583c1c..000000000 --- a/embassy-boot/boot/src/fmt.rs +++ /dev/null @@ -1,258 +0,0 @@ -#![macro_use] -#![allow(unused_macros)] - -use core::fmt::{Debug, Display, LowerHex}; - -#[cfg(all(feature = "defmt", feature = "log"))] -compile_error!("You may not enable both `defmt` and `log` features."); - -macro_rules! assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert!($($x)*); - } - }; -} - -macro_rules! assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_eq!($($x)*); - } - }; -} - -macro_rules! assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_ne!($($x)*); - } - }; -} - -macro_rules! debug_assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert!($($x)*); - } - }; -} - -macro_rules! debug_assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_eq!($($x)*); - } - }; -} - -macro_rules! debug_assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_ne!($($x)*); - } - }; -} - -macro_rules! todo { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::todo!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::todo!($($x)*); - } - }; -} - -#[cfg(not(feature = "defmt"))] -macro_rules! unreachable { - ($($x:tt)*) => { - ::core::unreachable!($($x)*) - }; -} - -#[cfg(feature = "defmt")] -macro_rules! unreachable { - ($($x:tt)*) => { - ::defmt::unreachable!($($x)*) - }; -} - -macro_rules! panic { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::panic!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::panic!($($x)*); - } - }; -} - -macro_rules! trace { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::trace!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::trace!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! debug { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::debug!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::debug!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! info { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::info!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::info!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! warn { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::warn!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::warn!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! error { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::error!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::error!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -#[cfg(feature = "defmt")] -macro_rules! unwrap { - ($($x:tt)*) => { - ::defmt::unwrap!($($x)*) - }; -} - -#[cfg(not(feature = "defmt"))] -macro_rules! unwrap { - ($arg:expr) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); - } - } - }; - ($arg:expr, $($msg:expr),+ $(,)? ) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); - } - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct NoneError; - -pub trait Try { - type Ok; - type Error; - fn into_result(self) -> Result; -} - -impl Try for Option { - type Ok = T; - type Error = NoneError; - - #[inline] - fn into_result(self) -> Result { - self.ok_or(NoneError) - } -} - -impl Try for Result { - type Ok = T; - type Error = E; - - #[inline] - fn into_result(self) -> Self { - self - } -} - -#[allow(unused)] -pub(crate) struct Bytes<'a>(pub &'a [u8]); - -impl<'a> Debug for Bytes<'a> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{:#02x?}", self.0) - } -} - -impl<'a> Display for Bytes<'a> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{:#02x?}", self.0) - } -} - -impl<'a> LowerHex for Bytes<'a> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{:#02x?}", self.0) - } -} - -#[cfg(feature = "defmt")] -impl<'a> defmt::Format for Bytes<'a> { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!(fmt, "{:02x}", self.0) - } -} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs deleted file mode 100644 index b4f03e01e..000000000 --- a/embassy-boot/boot/src/lib.rs +++ /dev/null @@ -1,323 +0,0 @@ -#![no_std] -#![allow(async_fn_in_trait)] -#![warn(missing_docs)] -#![doc = include_str!("../README.md")] -mod fmt; - -mod boot_loader; -mod digest_adapters; -mod firmware_updater; -#[cfg(test)] -mod mem_flash; -#[cfg(test)] -mod test_flash; - -// 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; -pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; -pub use firmware_updater::{ - BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareState, FirmwareUpdater, FirmwareUpdaterConfig, - FirmwareUpdaterError, -}; - -pub(crate) const BOOT_MAGIC: u8 = 0xD0; -pub(crate) const SWAP_MAGIC: u8 = 0xF0; -pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0; - -/// The state of the bootloader after running prepare. -#[derive(PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum State { - /// Bootloader is ready to boot the active partition. - Boot, - /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. - Swap, - /// Application has received a request to reboot into DFU mode to apply an update. - DfuDetach, -} - -/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. -#[repr(align(32))] -pub struct AlignedBuffer(pub [u8; N]); - -impl AsRef<[u8]> for AlignedBuffer { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for AlignedBuffer { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -#[cfg(test)] -mod tests { - #![allow(unused_imports)] - - use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; - use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; - use futures::executor::block_on; - - use super::*; - use crate::boot_loader::BootLoaderConfig; - use crate::firmware_updater::FirmwareUpdaterConfig; - use crate::mem_flash::MemFlash; - use crate::test_flash::{AsyncTestFlash, BlockingTestFlash}; - - /* - #[test] - fn test_bad_magic() { - let mut flash = MemFlash([0xff; 131072]); - let mut flash = SingleFlashConfig::new(&mut flash); - - let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); - - assert_eq!( - bootloader.prepare_boot(&mut flash), - Err(BootError::BadMagic) - ); - } - */ - - #[test] - fn test_boot_state() { - let flash = BlockingTestFlash::new(BootLoaderConfig { - active: MemFlash::<57344, 4096, 4>::default(), - dfu: MemFlash::<61440, 4096, 4>::default(), - state: MemFlash::<4096, 4096, 4>::default(), - }); - - flash.state().write(0, &[BOOT_MAGIC; 4]).unwrap(); - - let mut bootloader = BootLoader::new(BootLoaderConfig { - active: flash.active(), - dfu: flash.dfu(), - state: flash.state(), - }); - - let mut page = [0; 4096]; - assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap()); - } - - #[test] - #[cfg(not(feature = "_verify"))] - fn test_swap_state() { - const FIRMWARE_SIZE: usize = 57344; - let flash = AsyncTestFlash::new(BootLoaderConfig { - active: MemFlash::::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]; - let mut aligned = [0; 4]; - - 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(), - }, - &mut aligned, - ); - block_on(updater.write_firmware(0, &UPDATE)).unwrap(); - block_on(updater.mark_updated()).unwrap(); - - // Writing after marking updated is not allowed until marked as booted. - let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(0, &UPDATE)); - assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState))); - - let flash = flash.into_blocking(); - let mut bootloader = BootLoader::new(BootLoaderConfig { - active: flash.active(), - dfu: flash.dfu(), - state: flash.state(), - }); - - let mut page = [0; 1024]; - assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - - let mut read_buf = [0; FIRMWARE_SIZE]; - flash.active().read(0, &mut read_buf).unwrap(); - assert_eq!(UPDATE, read_buf); - // First DFU page is untouched - flash.dfu().read(4096, &mut read_buf).unwrap(); - assert_eq!(ORIGINAL, read_buf); - - // Running again should cause a revert - assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - - 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); - - // Mark as booted - let flash = flash.into_async(); - let mut updater = FirmwareUpdater::new( - FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }, - &mut aligned, - ); - block_on(updater.mark_booted()).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()); - } - - #[test] - #[cfg(not(feature = "_verify"))] - 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]; - let mut aligned = [0; 4]; - - 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(), - }, - &mut aligned, - ); - block_on(updater.write_firmware(0, &UPDATE)).unwrap(); - block_on(updater.mark_updated()).unwrap(); - - let flash = flash.into_blocking(); - let mut bootloader = BootLoader::new(BootLoaderConfig { - active: flash.active(), - dfu: flash.dfu(), - state: flash.state(), - }); - - let mut page = [0; 4096]; - assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - - let mut read_buf = [0; FIRMWARE_SIZE]; - flash.active().read(0, &mut read_buf).unwrap(); - assert_eq!(UPDATE, read_buf); - // First DFU page is untouched - flash.dfu().read(4096, &mut read_buf).unwrap(); - assert_eq!(ORIGINAL, read_buf); - } - - #[test] - #[cfg(not(feature = "_verify"))] - fn test_swap_state_dfu_page_biggest() { - const FIRMWARE_SIZE: usize = 12288; - let flash = AsyncTestFlash::new(BootLoaderConfig { - active: MemFlash::::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]; - let mut aligned = [0; 4]; - - 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(), - }, - &mut aligned, - ); - block_on(updater.write_firmware(0, &UPDATE)).unwrap(); - block_on(updater.mark_updated()).unwrap(); - - let flash = flash.into_blocking(); - let mut bootloader = BootLoader::new(BootLoaderConfig { - active: flash.active(), - dfu: flash.dfu(), - state: flash.state(), - }); - let mut page = [0; 4096]; - assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - - let mut read_buf = [0; FIRMWARE_SIZE]; - flash.active().read(0, &mut read_buf).unwrap(); - assert_eq!(UPDATE, read_buf); - // First DFU page is untouched - flash.dfu().read(4096, &mut read_buf).unwrap(); - assert_eq!(ORIGINAL, read_buf); - } - - #[test] - #[cfg(feature = "_verify")] - fn test_verify() { - // The following key setup is based on: - // https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example - - use ed25519_dalek::{Digest, Sha512, Signature, Signer, SigningKey, VerifyingKey}; - use rand::rngs::OsRng; - - let mut csprng = OsRng {}; - let keypair = SigningKey::generate(&mut csprng); - - 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); - - let public_key = keypair.verifying_key(); - - // Setup flash - let flash = BlockingTestFlash::new(BootLoaderConfig { - active: MemFlash::<0, 0, 0>::default(), - dfu: MemFlash::<4096, 4096, 4>::default(), - state: MemFlash::<4096, 4096, 4>::default(), - }); - - let firmware_len = firmware.len(); - - let mut write_buf = [0; 4096]; - write_buf[0..firmware_len].copy_from_slice(firmware); - flash.dfu().write(0, &write_buf).unwrap(); - - // On with the test - let flash = flash.into_async(); - let mut aligned = [0; 4]; - let mut updater = FirmwareUpdater::new( - FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }, - &mut aligned, - ); - - assert!(block_on(updater.verify_and_mark_updated( - &public_key.to_bytes(), - &signature.to_bytes(), - firmware_len as u32, - )) - .is_ok()); - } -} diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs deleted file mode 100644 index 40f352c8d..000000000 --- a/embassy-boot/boot/src/mem_flash.rs +++ /dev/null @@ -1,170 +0,0 @@ -#![allow(unused)] - -use core::ops::{Bound, Range, RangeBounds}; - -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; - -pub struct MemFlash { - pub mem: [u8; SIZE], - pub pending_write_successes: Option, -} - -#[derive(Debug)] -pub struct MemFlashError; - -impl MemFlash { - pub const fn new(fill: u8) -> Self { - Self { - mem: [fill; SIZE], - pending_write_successes: None, - } - } - - #[cfg(test)] - pub fn random() -> Self { - let mut mem = [0; SIZE]; - for byte in mem.iter_mut() { - *byte = rand::random::(); - } - Self { - mem, - pending_write_successes: None, - } - } - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), MemFlashError> { - let len = bytes.len(); - bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); - Ok(()) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> { - let offset = offset as usize; - assert!(bytes.len() % WRITE_SIZE == 0); - assert!(offset % WRITE_SIZE == 0); - assert!(offset + bytes.len() <= SIZE); - - if let Some(pending_successes) = self.pending_write_successes { - if pending_successes > 0 { - self.pending_write_successes = Some(pending_successes - 1); - } else { - return Err(MemFlashError); - } - } - - for ((offset, mem_byte), new_byte) in self - .mem - .iter_mut() - .enumerate() - .skip(offset) - .take(bytes.len()) - .zip(bytes) - { - assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); - *mem_byte = *new_byte; - } - - Ok(()) - } - - fn erase(&mut self, from: u32, to: u32) -> Result<(), MemFlashError> { - let from = from as usize; - let to = to as usize; - assert!(from % ERASE_SIZE == 0); - assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); - for i in from..to { - self.mem[i] = 0xFF; - } - Ok(()) - } - - pub fn program(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> { - let offset = offset as usize; - assert!(bytes.len() % WRITE_SIZE == 0); - assert!(offset % WRITE_SIZE == 0); - assert!(offset + bytes.len() <= SIZE); - - self.mem[offset..offset + bytes.len()].copy_from_slice(bytes); - - Ok(()) - } -} - -impl Default - for MemFlash -{ - fn default() -> Self { - Self::new(0xFF) - } -} - -impl ErrorType - for MemFlash -{ - type Error = MemFlashError; -} - -impl NorFlashError for MemFlashError { - fn kind(&self) -> NorFlashErrorKind { - NorFlashErrorKind::Other - } -} - -impl ReadNorFlash - for MemFlash -{ - const READ_SIZE: usize = 1; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) - } - - fn capacity(&self) -> usize { - SIZE - } -} - -impl NorFlash - for MemFlash -{ - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(offset, bytes) - } - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase(from, to) - } -} - -impl AsyncReadNorFlash - for MemFlash -{ - const READ_SIZE: usize = 1; - - async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) - } - - fn capacity(&self) -> usize { - SIZE - } -} - -impl AsyncNorFlash - for MemFlash -{ - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(offset, bytes) - } - - async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase(from, to) - } -} diff --git a/embassy-boot/boot/src/test_flash/asynch.rs b/embassy-boot/boot/src/test_flash/asynch.rs deleted file mode 100644 index 3ac9e71ab..000000000 --- a/embassy-boot/boot/src/test_flash/asynch.rs +++ /dev/null @@ -1,64 +0,0 @@ -use embassy_embedded_hal::flash::partition::Partition; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::mutex::Mutex; -use embedded_storage_async::nor_flash::NorFlash; - -use crate::BootLoaderConfig; - -pub struct AsyncTestFlash -where - ACTIVE: NorFlash, - DFU: NorFlash, - STATE: NorFlash, -{ - active: Mutex, - dfu: Mutex, - state: Mutex, -} - -impl AsyncTestFlash -where - ACTIVE: NorFlash, - DFU: NorFlash, - STATE: NorFlash, -{ - pub fn new(config: BootLoaderConfig) -> Self { - Self { - active: Mutex::new(config.active), - dfu: Mutex::new(config.dfu), - state: Mutex::new(config.state), - } - } - - pub fn active(&self) -> Partition { - Self::create_partition(&self.active) - } - - pub fn dfu(&self) -> Partition { - Self::create_partition(&self.dfu) - } - - pub fn state(&self) -> Partition { - Self::create_partition(&self.state) - } - - fn create_partition(mutex: &Mutex) -> Partition { - Partition::new(mutex, 0, mutex.try_lock().unwrap().capacity() as u32) - } -} - -impl AsyncTestFlash -where - ACTIVE: NorFlash + embedded_storage::nor_flash::NorFlash, - DFU: NorFlash + embedded_storage::nor_flash::NorFlash, - STATE: NorFlash + embedded_storage::nor_flash::NorFlash, -{ - pub fn into_blocking(self) -> super::BlockingTestFlash { - let config = BootLoaderConfig { - active: self.active.into_inner(), - dfu: self.dfu.into_inner(), - state: self.state.into_inner(), - }; - super::BlockingTestFlash::new(config) - } -} diff --git a/embassy-boot/boot/src/test_flash/blocking.rs b/embassy-boot/boot/src/test_flash/blocking.rs deleted file mode 100644 index 5ec476c65..000000000 --- a/embassy-boot/boot/src/test_flash/blocking.rs +++ /dev/null @@ -1,68 +0,0 @@ -use core::cell::RefCell; - -use embassy_embedded_hal::flash::partition::BlockingPartition; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::blocking_mutex::Mutex; -use embedded_storage::nor_flash::NorFlash; - -use crate::BootLoaderConfig; - -pub struct BlockingTestFlash -where - ACTIVE: NorFlash, - DFU: NorFlash, - STATE: NorFlash, -{ - active: Mutex>, - dfu: Mutex>, - state: Mutex>, -} - -impl BlockingTestFlash -where - ACTIVE: NorFlash, - DFU: NorFlash, - STATE: NorFlash, -{ - pub fn new(config: BootLoaderConfig) -> Self { - Self { - active: Mutex::new(RefCell::new(config.active)), - dfu: Mutex::new(RefCell::new(config.dfu)), - state: Mutex::new(RefCell::new(config.state)), - } - } - - pub fn active(&self) -> BlockingPartition { - Self::create_partition(&self.active) - } - - pub fn dfu(&self) -> BlockingPartition { - Self::create_partition(&self.dfu) - } - - pub fn state(&self) -> BlockingPartition { - Self::create_partition(&self.state) - } - - pub fn create_partition( - mutex: &Mutex>, - ) -> BlockingPartition { - BlockingPartition::new(mutex, 0, mutex.lock(|f| f.borrow().capacity()) as u32) - } -} - -impl BlockingTestFlash -where - ACTIVE: NorFlash + embedded_storage_async::nor_flash::NorFlash, - DFU: NorFlash + embedded_storage_async::nor_flash::NorFlash, - STATE: NorFlash + embedded_storage_async::nor_flash::NorFlash, -{ - pub fn into_async(self) -> super::AsyncTestFlash { - let config = BootLoaderConfig { - active: self.active.into_inner().into_inner(), - dfu: self.dfu.into_inner().into_inner(), - state: self.state.into_inner().into_inner(), - }; - super::AsyncTestFlash::new(config) - } -} diff --git a/embassy-boot/boot/src/test_flash/mod.rs b/embassy-boot/boot/src/test_flash/mod.rs deleted file mode 100644 index 79b15a081..000000000 --- a/embassy-boot/boot/src/test_flash/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod asynch; -mod blocking; - -pub(crate) use asynch::AsyncTestFlash; -pub(crate) use blocking::BlockingTestFlash; -- cgit