From 1acbc5b1a90985df83195ad23f9665b3227b3805 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 27 May 2023 19:26:45 +0200 Subject: [PATCH 01/16] Remove the usage of the local Partition type in BootLoader --- embassy-boot/boot/Cargo.toml | 1 + embassy-boot/boot/src/boot_loader.rs | 344 +++++++++------------------ 2 files changed, 114 insertions(+), 231 deletions(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index f641d5e1c..3fdf69c5e 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -39,6 +39,7 @@ env_logger = "0.9" rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version futures = { version = "0.3", features = ["executor"] } sha1 = "0.10.5" +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } [dev-dependencies.ed25519-dalek] default_features = false diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index b959de2c4..50eb5e668 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -1,6 +1,8 @@ use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; +use crate::{State, BOOT_MAGIC, SWAP_MAGIC}; + +const STATE_ERASE_VALUE: u8 = 0xFF; /// Errors returned by bootloader #[derive(PartialEq, Eq, Debug)] @@ -30,65 +32,39 @@ where } } -/// Trait defining the flash handles used for active and DFU partition. -pub trait FlashConfig { - /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value. - const STATE_ERASE_VALUE: u8 = 0xFF; - /// Flash type used for the state partition. - type STATE: NorFlash; - /// Flash type used for the active partition. - type ACTIVE: NorFlash; - /// Flash type used for the dfu partition. - type DFU: NorFlash; - - /// Return flash instance used to write/read to/from active partition. - fn active(&mut self) -> &mut Self::ACTIVE; - /// Return flash instance used to write/read to/from dfu partition. - fn dfu(&mut self) -> &mut Self::DFU; - /// Return flash instance used to write/read to/from bootloader state. - fn state(&mut self) -> &mut Self::STATE; -} - -trait FlashConfigEx { - fn page_size() -> u32; -} - -impl FlashConfigEx for T { - /// Get the page size which is the "unit of operation" within the bootloader. - fn page_size() -> u32 { - core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) as u32 - } -} - /// BootLoader works with any flash implementing embedded_storage. -pub struct BootLoader { - // Page with current state of bootloader. 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: Partition, - // Location of the partition which will be booted from - active: Partition, - // Location of the partition which will be swapped in when requested - dfu: Partition, +pub struct BootLoader { + /// Flash type used for the active partition - the partition which will be booted from. + active: ACTIVE, + /// Flash type used for the dfu partition - he partition which will be swapped in when requested. + dfu: DFU, + /// Flash type used for the state partition. + /// + /// 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 { - /// Create a new instance of a bootloader with the given partitions. +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(active: Partition, dfu: Partition, state: Partition) -> Self { + pub fn new(active: ACTIVE, dfu: DFU, state: STATE) -> Self { Self { active, dfu, state } } - /// Return the offset of the active partition into the active flash. - pub fn boot_address(&self) -> usize { - self.active.from as usize - } - /// Perform necessary boot preparations like swapping images. /// /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap @@ -175,195 +151,174 @@ impl BootLoader { /// | DFU | 3 | 3 | 2 | 1 | 3 | /// +-----------+--------------+--------+--------+--------+--------+ /// - pub fn prepare_boot(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { + pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(0, P::page_size() % aligned_buf.len() as u32); - assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE as u32); - assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE as u32); - assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE as u32); - assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE as u32); - assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); - assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE); - assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE); - assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); + 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); + + assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE); // Copy contents from partition N to active - let state = self.read_state(p, aligned_buf)?; + 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(p, aligned_buf)? { + if !self.is_swapped(aligned_buf)? { trace!("Swapping"); - self.swap(p, aligned_buf)?; + self.swap(aligned_buf)?; trace!("Swapping done"); } else { trace!("Reverting"); - self.revert(p, aligned_buf)?; + self.revert(aligned_buf)?; - let state_flash = p.state(); - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; + let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; // Invalidate progress - state_word.fill(!P::STATE_ERASE_VALUE); - self.state - .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; + state_word.fill(!STATE_ERASE_VALUE); + self.state.write(STATE::WRITE_SIZE as u32, state_word)?; // Clear magic and progress - self.state.wipe_blocking(state_flash)?; + self.state.erase(0, self.state.capacity() as u32)?; // Set magic state_word.fill(BOOT_MAGIC); - self.state.write_blocking(state_flash, 0, state_word)?; + self.state.write(0, state_word)?; } } Ok(state) } - fn is_swapped(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { - let page_count = (self.active.size() / P::page_size()) as usize; - let progress = self.current_progress(p, aligned_buf)?; + 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, config: &mut P, aligned_buf: &mut [u8]) -> Result { - let write_size = P::STATE::WRITE_SIZE as u32; - let max_index = (((self.state.size() - write_size) / write_size) - 2) as usize; - let state_flash = config.state(); + 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_blocking(state_flash, write_size, state_word)?; - if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) { + 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_blocking(state_flash, (2 + index) as u32 * write_size, state_word)?; + self.state.read((2 + index) as u32 * write_size, state_word)?; - if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) { + if state_word.iter().any(|&b| b == STATE_ERASE_VALUE) { return Ok(index); } } Ok(max_index) } - fn update_progress( - &mut self, - progress_index: usize, - p: &mut P, - aligned_buf: &mut [u8], - ) -> Result<(), BootError> { - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; - state_word.fill(!P::STATE_ERASE_VALUE); - self.state.write_blocking( - p.state(), - (2 + progress_index) as u32 * P::STATE::WRITE_SIZE as u32, - state_word, - )?; + 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( + fn copy_page_once_to_active( &mut self, progress_index: usize, from_offset: u32, to_offset: u32, - p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= progress_index { - let page_size = P::page_size() as u32; + if self.current_progress(aligned_buf)? <= progress_index { + let page_size = Self::PAGE_SIZE as u32; - self.active - .erase_blocking(p.active(), to_offset, to_offset + page_size)?; + 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_blocking(p.dfu(), from_offset + offset_in_page as u32, aligned_buf)?; - self.active - .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?; + 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, p, aligned_buf)?; + self.update_progress(progress_index, aligned_buf)?; } Ok(()) } - fn copy_page_once_to_dfu( + fn copy_page_once_to_dfu( &mut self, progress_index: usize, from_offset: u32, to_offset: u32, - p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= progress_index { - let page_size = P::page_size() as u32; + if self.current_progress(aligned_buf)? <= progress_index { + let page_size = Self::PAGE_SIZE as u32; - self.dfu - .erase_blocking(p.dfu(), to_offset as u32, to_offset + page_size)?; + 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_blocking(p.active(), from_offset + offset_in_page as u32, aligned_buf)?; - self.dfu - .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?; + 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, p, aligned_buf)?; + self.update_progress(progress_index, aligned_buf)?; } Ok(()) } - fn swap(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let page_size = P::page_size(); - let page_count = self.active.size() / page_size; + 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) * page_size; - let dfu_to_offset = (page_count - page_num) * page_size; + 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, p, aligned_buf)?; + 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) * page_size; - let dfu_from_offset = (page_count - 1 - page_num) * page_size; + 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, p, aligned_buf)?; + self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?; } Ok(()) } - fn revert(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let page_size = P::page_size(); - let page_count = self.active.size() / page_size; + 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 * page_size; - let dfu_to_offset = page_num * page_size; - self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; + 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 * page_size; - let dfu_from_offset = (page_num + 1) * page_size; - self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; + 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, config: &mut P, aligned_buf: &mut [u8]) -> Result { - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; - self.state.read_blocking(config.state(), 0, state_word)?; + 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) @@ -373,11 +328,16 @@ impl BootLoader { } } -fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: u32, state_write_size: usize) { - assert_eq!(active.size() % page_size, 0); - assert_eq!(dfu.size() % page_size, 0); - assert!(dfu.size() - active.size() >= page_size); - assert!(2 + 2 * (active.size() / page_size) <= state.size() / state_write_size as u32); +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); + 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); } /// A flash wrapper implementing the Flash and embedded_storage traits. @@ -436,98 +396,20 @@ where } } -/// Convenience provider that uses a single flash for all partitions. -pub struct SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - flash: &'a mut F, -} - -impl<'a, F> SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - /// Create a provider for a single flash. - pub fn new(flash: &'a mut F) -> Self { - Self { flash } - } -} - -impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - type STATE = F; - type ACTIVE = F; - type DFU = F; - - fn active(&mut self) -> &mut Self::STATE { - self.flash - } - fn dfu(&mut self) -> &mut Self::ACTIVE { - self.flash - } - fn state(&mut self) -> &mut Self::DFU { - self.flash - } -} - -/// Convenience flash provider that uses separate flash instances for each partition. -pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - active: &'a mut ACTIVE, - state: &'a mut STATE, - dfu: &'a mut DFU, -} - -impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - /// Create a new flash provider with separate configuration for all three partitions. - pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { - Self { active, state, dfu } - } -} - -impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - type STATE = STATE; - type ACTIVE = ACTIVE; - type DFU = DFU; - - fn active(&mut self) -> &mut Self::ACTIVE { - self.active - } - fn dfu(&mut self) -> &mut Self::DFU { - self.dfu - } - fn state(&mut self) -> &mut Self::STATE { - self.state - } -} - #[cfg(test)] mod tests { use super::*; + use crate::mem_flash::MemFlash; #[test] #[should_panic] fn test_range_asserts() { - const ACTIVE: Partition = Partition::new(4096, 4194304); - const DFU: Partition = Partition::new(4194304, 2 * 4194304); - const STATE: Partition = Partition::new(0, 4096); - assert_partitions(ACTIVE, DFU, STATE, 4096, 4); + 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); } } From 94046f30ffefc96a7b110ed1d9bb60d64cb4780f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 27 May 2023 19:26:45 +0200 Subject: [PATCH 02/16] Remove the usage of the local Partition type in BootLoader --- embassy-boot/boot/Cargo.toml | 1 + embassy-boot/boot/src/boot_loader.rs | 344 +++++++++------------------ 2 files changed, 114 insertions(+), 231 deletions(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index f641d5e1c..3fdf69c5e 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -39,6 +39,7 @@ env_logger = "0.9" rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version futures = { version = "0.3", features = ["executor"] } sha1 = "0.10.5" +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } [dev-dependencies.ed25519-dalek] default_features = false diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index b959de2c4..50eb5e668 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -1,6 +1,8 @@ use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; +use crate::{State, BOOT_MAGIC, SWAP_MAGIC}; + +const STATE_ERASE_VALUE: u8 = 0xFF; /// Errors returned by bootloader #[derive(PartialEq, Eq, Debug)] @@ -30,65 +32,39 @@ where } } -/// Trait defining the flash handles used for active and DFU partition. -pub trait FlashConfig { - /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value. - const STATE_ERASE_VALUE: u8 = 0xFF; - /// Flash type used for the state partition. - type STATE: NorFlash; - /// Flash type used for the active partition. - type ACTIVE: NorFlash; - /// Flash type used for the dfu partition. - type DFU: NorFlash; - - /// Return flash instance used to write/read to/from active partition. - fn active(&mut self) -> &mut Self::ACTIVE; - /// Return flash instance used to write/read to/from dfu partition. - fn dfu(&mut self) -> &mut Self::DFU; - /// Return flash instance used to write/read to/from bootloader state. - fn state(&mut self) -> &mut Self::STATE; -} - -trait FlashConfigEx { - fn page_size() -> u32; -} - -impl FlashConfigEx for T { - /// Get the page size which is the "unit of operation" within the bootloader. - fn page_size() -> u32 { - core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) as u32 - } -} - /// BootLoader works with any flash implementing embedded_storage. -pub struct BootLoader { - // Page with current state of bootloader. 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: Partition, - // Location of the partition which will be booted from - active: Partition, - // Location of the partition which will be swapped in when requested - dfu: Partition, +pub struct BootLoader { + /// Flash type used for the active partition - the partition which will be booted from. + active: ACTIVE, + /// Flash type used for the dfu partition - he partition which will be swapped in when requested. + dfu: DFU, + /// Flash type used for the state partition. + /// + /// 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 { - /// Create a new instance of a bootloader with the given partitions. +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(active: Partition, dfu: Partition, state: Partition) -> Self { + pub fn new(active: ACTIVE, dfu: DFU, state: STATE) -> Self { Self { active, dfu, state } } - /// Return the offset of the active partition into the active flash. - pub fn boot_address(&self) -> usize { - self.active.from as usize - } - /// Perform necessary boot preparations like swapping images. /// /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap @@ -175,195 +151,174 @@ impl BootLoader { /// | DFU | 3 | 3 | 2 | 1 | 3 | /// +-----------+--------------+--------+--------+--------+--------+ /// - pub fn prepare_boot(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { + pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(0, P::page_size() % aligned_buf.len() as u32); - assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE as u32); - assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE as u32); - assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE as u32); - assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE as u32); - assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); - assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE); - assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE); - assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); + 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); + + assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE); // Copy contents from partition N to active - let state = self.read_state(p, aligned_buf)?; + 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(p, aligned_buf)? { + if !self.is_swapped(aligned_buf)? { trace!("Swapping"); - self.swap(p, aligned_buf)?; + self.swap(aligned_buf)?; trace!("Swapping done"); } else { trace!("Reverting"); - self.revert(p, aligned_buf)?; + self.revert(aligned_buf)?; - let state_flash = p.state(); - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; + let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; // Invalidate progress - state_word.fill(!P::STATE_ERASE_VALUE); - self.state - .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; + state_word.fill(!STATE_ERASE_VALUE); + self.state.write(STATE::WRITE_SIZE as u32, state_word)?; // Clear magic and progress - self.state.wipe_blocking(state_flash)?; + self.state.erase(0, self.state.capacity() as u32)?; // Set magic state_word.fill(BOOT_MAGIC); - self.state.write_blocking(state_flash, 0, state_word)?; + self.state.write(0, state_word)?; } } Ok(state) } - fn is_swapped(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { - let page_count = (self.active.size() / P::page_size()) as usize; - let progress = self.current_progress(p, aligned_buf)?; + 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, config: &mut P, aligned_buf: &mut [u8]) -> Result { - let write_size = P::STATE::WRITE_SIZE as u32; - let max_index = (((self.state.size() - write_size) / write_size) - 2) as usize; - let state_flash = config.state(); + 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_blocking(state_flash, write_size, state_word)?; - if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) { + 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_blocking(state_flash, (2 + index) as u32 * write_size, state_word)?; + self.state.read((2 + index) as u32 * write_size, state_word)?; - if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) { + if state_word.iter().any(|&b| b == STATE_ERASE_VALUE) { return Ok(index); } } Ok(max_index) } - fn update_progress( - &mut self, - progress_index: usize, - p: &mut P, - aligned_buf: &mut [u8], - ) -> Result<(), BootError> { - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; - state_word.fill(!P::STATE_ERASE_VALUE); - self.state.write_blocking( - p.state(), - (2 + progress_index) as u32 * P::STATE::WRITE_SIZE as u32, - state_word, - )?; + 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( + fn copy_page_once_to_active( &mut self, progress_index: usize, from_offset: u32, to_offset: u32, - p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= progress_index { - let page_size = P::page_size() as u32; + if self.current_progress(aligned_buf)? <= progress_index { + let page_size = Self::PAGE_SIZE as u32; - self.active - .erase_blocking(p.active(), to_offset, to_offset + page_size)?; + 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_blocking(p.dfu(), from_offset + offset_in_page as u32, aligned_buf)?; - self.active - .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?; + 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, p, aligned_buf)?; + self.update_progress(progress_index, aligned_buf)?; } Ok(()) } - fn copy_page_once_to_dfu( + fn copy_page_once_to_dfu( &mut self, progress_index: usize, from_offset: u32, to_offset: u32, - p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= progress_index { - let page_size = P::page_size() as u32; + if self.current_progress(aligned_buf)? <= progress_index { + let page_size = Self::PAGE_SIZE as u32; - self.dfu - .erase_blocking(p.dfu(), to_offset as u32, to_offset + page_size)?; + 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_blocking(p.active(), from_offset + offset_in_page as u32, aligned_buf)?; - self.dfu - .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?; + 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, p, aligned_buf)?; + self.update_progress(progress_index, aligned_buf)?; } Ok(()) } - fn swap(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let page_size = P::page_size(); - let page_count = self.active.size() / page_size; + 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) * page_size; - let dfu_to_offset = (page_count - page_num) * page_size; + 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, p, aligned_buf)?; + 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) * page_size; - let dfu_from_offset = (page_count - 1 - page_num) * page_size; + 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, p, aligned_buf)?; + self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?; } Ok(()) } - fn revert(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let page_size = P::page_size(); - let page_count = self.active.size() / page_size; + 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 * page_size; - let dfu_to_offset = page_num * page_size; - self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; + 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 * page_size; - let dfu_from_offset = (page_num + 1) * page_size; - self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; + 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, config: &mut P, aligned_buf: &mut [u8]) -> Result { - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; - self.state.read_blocking(config.state(), 0, state_word)?; + 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) @@ -373,11 +328,16 @@ impl BootLoader { } } -fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: u32, state_write_size: usize) { - assert_eq!(active.size() % page_size, 0); - assert_eq!(dfu.size() % page_size, 0); - assert!(dfu.size() - active.size() >= page_size); - assert!(2 + 2 * (active.size() / page_size) <= state.size() / state_write_size as u32); +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); + 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); } /// A flash wrapper implementing the Flash and embedded_storage traits. @@ -436,98 +396,20 @@ where } } -/// Convenience provider that uses a single flash for all partitions. -pub struct SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - flash: &'a mut F, -} - -impl<'a, F> SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - /// Create a provider for a single flash. - pub fn new(flash: &'a mut F) -> Self { - Self { flash } - } -} - -impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - type STATE = F; - type ACTIVE = F; - type DFU = F; - - fn active(&mut self) -> &mut Self::STATE { - self.flash - } - fn dfu(&mut self) -> &mut Self::ACTIVE { - self.flash - } - fn state(&mut self) -> &mut Self::DFU { - self.flash - } -} - -/// Convenience flash provider that uses separate flash instances for each partition. -pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - active: &'a mut ACTIVE, - state: &'a mut STATE, - dfu: &'a mut DFU, -} - -impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - /// Create a new flash provider with separate configuration for all three partitions. - pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { - Self { active, state, dfu } - } -} - -impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - type STATE = STATE; - type ACTIVE = ACTIVE; - type DFU = DFU; - - fn active(&mut self) -> &mut Self::ACTIVE { - self.active - } - fn dfu(&mut self) -> &mut Self::DFU { - self.dfu - } - fn state(&mut self) -> &mut Self::STATE { - self.state - } -} - #[cfg(test)] mod tests { use super::*; + use crate::mem_flash::MemFlash; #[test] #[should_panic] fn test_range_asserts() { - const ACTIVE: Partition = Partition::new(4096, 4194304); - const DFU: Partition = Partition::new(4194304, 2 * 4194304); - const STATE: Partition = Partition::new(0, 4096); - assert_partitions(ACTIVE, DFU, STATE, 4096, 4); + 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); } } From 5205b5b09588d6e0006e5479764ee94b113b03b9 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:36:42 +0200 Subject: [PATCH 03/16] Split FirmwareUpdater into async and blocking types --- .../boot/src/firmware_updater/asynch.rs | 176 +++++++++-------- .../boot/src/firmware_updater/blocking.rs | 187 +++++++++++------- embassy-boot/boot/src/firmware_updater/mod.rs | 51 ++--- embassy-boot/boot/src/lib.rs | 4 +- 4 files changed, 236 insertions(+), 182 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index bdd03bff4..d0780bdf1 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs @@ -1,20 +1,68 @@ use digest::Digest; -use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; +use embassy_embedded_hal::flash::partition::Partition; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embedded_storage_async::nor_flash::NorFlash; -use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; +use super::FirmwareUpdaterConfig; +use crate::{FirmwareUpdaterError, State, BOOT_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 { + dfu: DFU, + state: STATE, +} + +impl<'a, FLASH: NorFlash> + FirmwareUpdaterConfig, Partition<'a, NoopRawMutex, FLASH>> +{ + /// Create a firmware updater config from the flash and address symbols defined in the linkerfile + #[cfg(target_os = "none")] + pub fn from_linkerfile(flash: &'a Mutex) -> Self { + use embassy_sync::mutex::Mutex; + + 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 FirmwareUpdater { + /// Create a firmware updater instance with partition ranges for the update and state partitions. + pub fn new(config: FirmwareUpdaterConfig) -> Self { + Self { + dfu: config.dfu, + state: config.state, + } + } -impl FirmwareUpdater { /// 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, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result { - self.state.read(state_flash, 0, aligned).await?; + pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result { + self.state.read(0, aligned).await?; if !aligned.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) @@ -37,19 +85,18 @@ impl FirmwareUpdater { /// /// # Safety /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// 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. - #[cfg(all(feature = "_verify", feature = "nightly"))] - pub async fn verify_and_mark_updated( + #[cfg(feature = "_verify")] + pub async fn verify_and_mark_updated( &mut self, - _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.size()); + assert_eq!(_aligned.len(), STATE::WRITE_SIZE); + assert!(_update_len <= self.dfu.capacity() as u32); #[cfg(feature = "ed25519-dalek")] { @@ -63,8 +110,7 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) - .await?; + self.hash::(_update_len, _aligned, &mut message).await?; public_key.verify(&message, &signature).map_err(into_signature_error)? } @@ -85,8 +131,7 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) - .await?; + self.hash::(_update_len, _aligned, &mut message).await?; let r = public_key.verify(&message, &signature); trace!( @@ -99,20 +144,19 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await + self.set_magic(_aligned, SWAP_MAGIC).await } /// Verify the update in DFU with any digest. - pub async fn hash( + pub async fn hash( &mut self, - dfu_flash: &mut F, 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(dfu_flash, offset, chunk_buf).await?; + 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]); } @@ -124,60 +168,44 @@ impl FirmwareUpdater { /// /// # Safety /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(all(feature = "nightly", not(feature = "_verify")))] - pub async fn mark_updated( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC, state_flash).await + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(not(feature = "_verify"))] + pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC).await } /// Mark firmware boot successful and stop rollback on reset. /// /// # Safety /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_booted( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC, state_flash).await + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub async fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC).await } - async fn set_magic( - &mut self, - aligned: &mut [u8], - magic: u8, - state_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - self.state.read(state_flash, 0, aligned).await?; + async fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> { + self.state.read(0, aligned).await?; if aligned.iter().any(|&b| b != magic) { // Read progress validity - self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; - - // FIXME: Do not make this assumption. - const STATE_ERASE_VALUE: u8 = 0xFF; + self.state.read(STATE::WRITE_SIZE as u32, aligned).await?; if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { // The current progress validity marker is invalid } else { // Invalidate progress aligned.fill(!STATE_ERASE_VALUE); - self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; + self.state.write(STATE::WRITE_SIZE as u32, aligned).await?; } // Clear magic and progress - self.state.wipe(state_flash).await?; + self.state.erase(0, self.state.capacity() as u32).await?; // Set magic aligned.fill(magic); - self.state.write(state_flash, 0, aligned).await?; + self.state.write(0, aligned).await?; } Ok(()) } @@ -189,19 +217,12 @@ impl FirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( - &mut self, - offset: usize, - data: &[u8], - dfu_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); + pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= DFU::ERASE_SIZE); - self.dfu - .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) - .await?; + self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; - self.dfu.write(dfu_flash, offset as u32, data).await?; + self.dfu.write(offset as u32, data).await?; Ok(()) } @@ -211,18 +232,18 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - pub async fn prepare_update( - &mut self, - dfu_flash: &mut F, - ) -> Result { - self.dfu.wipe(dfu_flash).await?; + pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + self.dfu.erase(0, self.dfu.capacity() as u32).await?; - Ok(self.dfu) + Ok(&mut self.dfu) } } #[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}; @@ -231,20 +252,19 @@ mod tests { #[test] fn can_verify_sha1() { - const STATE: Partition = Partition::new(0, 4096); - const DFU: Partition = Partition::new(65536, 131072); - - let mut flash = MemFlash::<131072, 4096, 8>::default(); + let flash = Mutex::::new(MemFlash::<131072, 4096, 8>::default()); + let state = Partition::new(&flash, 0, 4096); + let dfu = Partition::new(&flash, 65536, 65536); 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(DFU, STATE); - block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); + 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::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); + 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 index 50caaf08c..c44126149 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs @@ -1,25 +1,70 @@ use digest::Digest; +use embassy_embedded_hal::flash::partition::BlockingPartition; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embedded_storage::nor_flash::NorFlash; -use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; +use super::FirmwareUpdaterConfig; +use crate::{FirmwareUpdaterError, State, BOOT_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 { + dfu: DFU, + state: STATE, +} + +impl<'a, FLASH: NorFlash> + FirmwareUpdaterConfig, BlockingPartition<'a, NoopRawMutex, FLASH>> +{ + /// Create a firmware updater config from the flash and address symbols defined in the linkerfile + #[cfg(target_os = "none")] + pub fn from_linkerfile_blocking(flash: &'a Mutex>) -> Self { + use core::cell::RefCell; + + use embassy_sync::blocking_mutex::Mutex; + + 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) + }; -impl FirmwareUpdater { - /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub const fn new(dfu: Partition, state: Partition) -> Self { Self { dfu, state } } +} + +impl BlockingFirmwareUpdater { + /// Create a firmware updater instance with partition ranges for the update and state partitions. + pub fn new(config: FirmwareUpdaterConfig) -> Self { + Self { + dfu: config.dfu, + state: config.state, + } + } /// 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_blocking( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result { - self.state.read_blocking(state_flash, 0, aligned)?; + pub fn get_state(&mut self, aligned: &mut [u8]) -> Result { + self.state.read(0, aligned)?; if !aligned.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) @@ -42,19 +87,18 @@ impl FirmwareUpdater { /// /// # Safety /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// 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. #[cfg(feature = "_verify")] - pub fn verify_and_mark_updated_blocking( + pub fn verify_and_mark_updated( &mut self, - _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.size()); + assert_eq!(_aligned.len(), STATE::WRITE_SIZE); + assert!(_update_len <= self.dfu.capacity() as u32); #[cfg(feature = "ed25519-dalek")] { @@ -68,7 +112,7 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; + self.hash::(_update_len, _aligned, &mut message)?; public_key.verify(&message, &signature).map_err(into_signature_error)? } @@ -89,7 +133,7 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; + self.hash::(_update_len, _aligned, &mut message)?; let r = public_key.verify(&message, &signature); trace!( @@ -102,20 +146,19 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) + self.set_magic(_aligned, SWAP_MAGIC) } /// Verify the update in DFU with any digest. - pub fn hash_blocking( + pub fn hash( &mut self, - dfu_flash: &mut F, 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_blocking(dfu_flash, offset, chunk_buf)?; + self.dfu.read(offset, chunk_buf)?; let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); digest.update(&chunk_buf[..len]); } @@ -127,60 +170,44 @@ impl FirmwareUpdater { /// /// # Safety /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. #[cfg(not(feature = "_verify"))] - pub fn mark_updated_blocking( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash) + pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC) } /// Mark firmware boot successful and stop rollback on reset. /// /// # Safety /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn mark_booted_blocking( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash) + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC) } - fn set_magic_blocking( - &mut self, - aligned: &mut [u8], - magic: u8, - state_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - self.state.read_blocking(state_flash, 0, aligned)?; + fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> { + self.state.read(0, aligned)?; if aligned.iter().any(|&b| b != magic) { // Read progress validity - self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; - - // FIXME: Do not make this assumption. - const STATE_ERASE_VALUE: u8 = 0xFF; + self.state.read(STATE::WRITE_SIZE as u32, aligned)?; if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { // The current progress validity marker is invalid } else { // Invalidate progress aligned.fill(!STATE_ERASE_VALUE); - self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; + self.state.write(STATE::WRITE_SIZE as u32, aligned)?; } // Clear magic and progress - self.state.wipe_blocking(state_flash)?; + self.state.erase(0, self.state.capacity() as u32)?; // Set magic aligned.fill(magic); - self.state.write_blocking(state_flash, 0, aligned)?; + self.state.write(0, aligned)?; } Ok(()) } @@ -192,18 +219,12 @@ impl FirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware_blocking( - &mut self, - offset: usize, - data: &[u8], - dfu_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); + pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= DFU::ERASE_SIZE); - self.dfu - .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; + self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; - self.dfu.write_blocking(dfu_flash, offset as u32, data)?; + self.dfu.write(offset as u32, data)?; Ok(()) } @@ -211,11 +232,45 @@ impl FirmwareUpdater { /// Prepare for an incoming DFU update by erasing the entire DFU area and /// returning its `Partition`. /// - /// Using this instead of `write_firmware_blocking` allows for an optimized - /// API in exchange for added complexity. - pub fn prepare_update_blocking(&mut self, flash: &mut F) -> Result { - self.dfu.wipe_blocking(flash)?; + /// 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.dfu.erase(0, self.dfu.capacity() as u32)?; - Ok(self.dfu) + Ok(&mut self.dfu) + } +} + +#[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 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 }); + 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 index e09f5eb6c..a37984a3a 100644 --- a/embassy-boot/boot/src/firmware_updater/mod.rs +++ b/embassy-boot/boot/src/firmware_updater/mod.rs @@ -2,9 +2,22 @@ mod asynch; mod blocking; +#[cfg(feature = "nightly")] +pub use asynch::FirmwareUpdater; +pub use blocking::BlockingFirmwareUpdater; use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; -use crate::Partition; +/// 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)] @@ -33,39 +46,3 @@ where FirmwareUpdaterError::Flash(error.kind()) } } - -/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to -/// 'mess up' the internal bootloader state -pub struct FirmwareUpdater { - state: Partition, - dfu: Partition, -} - -#[cfg(target_os = "none")] -impl Default for FirmwareUpdater { - fn default() -> 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 { - Partition::new( - &__bootloader_dfu_start as *const u32 as u32, - &__bootloader_dfu_end as *const u32 as u32, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as u32, - &__bootloader_state_end as *const u32 as u32, - ) - }; - - trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); - trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); - FirmwareUpdater::new(dfu, state) - } -} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4521fecb0..af2d3ce02 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -11,8 +11,10 @@ mod mem_flash; mod partition; pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig}; -pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; pub use partition::Partition; +#[cfg(feature = "nightly")] +pub use firmware_updater::FirmwareUpdater; +pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError}; pub(crate) const BOOT_MAGIC: u8 = 0xD0; pub(crate) const SWAP_MAGIC: u8 = 0xF0; From c5ec453ec1e87c2f5c5047bf357ae99298aa6d12 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:38:00 +0200 Subject: [PATCH 04/16] Add bootloader helper for creating config from linkerfile symbols --- embassy-boot/boot/src/boot_loader.rs | 136 ++++++++++++++------------- embassy-boot/boot/src/lib.rs | 2 +- 2 files changed, 72 insertions(+), 66 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 50eb5e668..bdf7bd7fd 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -1,8 +1,11 @@ -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; +use core::cell::RefCell; -use crate::{State, BOOT_MAGIC, SWAP_MAGIC}; +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}; -const STATE_ERASE_VALUE: u8 = 0xFF; +use crate::{State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; /// Errors returned by bootloader #[derive(PartialEq, Eq, Debug)] @@ -32,14 +35,69 @@ where } } +/// 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 { - /// Flash type used for the active partition - the partition which will be booted from. active: ACTIVE, - /// Flash type used for the dfu partition - he partition which will be swapped in when requested. dfu: DFU, - /// Flash type used for the state partition. - /// /// The state partition has the following format: /// All ranges are in multiples of WRITE_SIZE bytes. /// | Range | Description | @@ -61,8 +119,12 @@ impl BootLoader Self { - Self { active, dfu, state } + pub fn new(config: BootLoaderConfig) -> Self { + Self { + active: config.active, + dfu: config.dfu, + state: config.state, + } } /// Perform necessary boot preparations like swapping images. @@ -340,62 +402,6 @@ fn assert_partitions( assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); } -/// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash -where - F: NorFlash, -{ - flash: F, -} - -impl BootFlash -where - F: NorFlash, -{ - /// Create a new instance of a bootable flash - pub fn new(flash: F) -> Self { - Self { flash } - } -} - -impl ErrorType for BootFlash -where - F: NorFlash, -{ - type Error = F::Error; -} - -impl NorFlash for BootFlash -where - F: NorFlash, -{ - const WRITE_SIZE: usize = F::WRITE_SIZE; - const ERASE_SIZE: usize = F::ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - F::erase(&mut self.flash, from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - F::write(&mut self.flash, offset, bytes) - } -} - -impl ReadNorFlash for BootFlash -where - F: NorFlash, -{ - const READ_SIZE: usize = F::READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - F::read(&mut self.flash, offset, bytes) - } - - fn capacity(&self) -> usize { - F::capacity(&self.flash) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index af2d3ce02..f2034fa8a 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -10,8 +10,8 @@ mod firmware_updater; mod mem_flash; mod partition; -pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig}; pub use partition::Partition; +pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; #[cfg(feature = "nightly")] pub use firmware_updater::FirmwareUpdater; pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError}; From 1cd87f0028c8a0f9b87a09016a22179fb05ced33 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:40:04 +0200 Subject: [PATCH 05/16] Cleanup MemFlash --- embassy-boot/boot/src/mem_flash.rs | 108 +++++++++++++++-------------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index 3ba84a92c..2728e9720 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -34,6 +34,52 @@ impl MemFla } } + 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); @@ -44,12 +90,6 @@ impl MemFla Ok(()) } - - pub fn assert_eq(&self, offset: u32, expectation: &[u8]) { - for i in 0..expectation.len() { - assert_eq!(self.mem[offset as usize + i], expectation[i], "Index {}", i); - } - } } impl Default @@ -78,9 +118,7 @@ impl ReadNo const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - let len = bytes.len(); - bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); - Ok(()) + self.read(offset, bytes) } fn capacity(&self) -> usize { @@ -94,44 +132,12 @@ impl NorFla const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = ERASE_SIZE; - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - 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(()) + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) } - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - 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<(), Self::Error> { + self.erase(from, to) } } @@ -142,11 +148,11 @@ impl AsyncR const READ_SIZE: usize = 1; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - ::read(self, offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { - ::capacity(self) + SIZE } } @@ -157,11 +163,11 @@ impl AsyncN const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = ERASE_SIZE; - async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - ::erase(self, from, to) + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) } - async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - ::write(self, offset, bytes) + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to) } } From b23e40f72242a9f4759d8a6d4a924c8c9d041c67 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:41:10 +0200 Subject: [PATCH 06/16] Add TestFlash helper --- embassy-boot/boot/src/lib.rs | 6 ++ embassy-boot/boot/src/test_flash/asynch.rs | 57 +++++++++++++++++ embassy-boot/boot/src/test_flash/blocking.rs | 65 ++++++++++++++++++++ embassy-boot/boot/src/test_flash/mod.rs | 7 +++ 4 files changed, 135 insertions(+) create mode 100644 embassy-boot/boot/src/test_flash/asynch.rs create mode 100644 embassy-boot/boot/src/test_flash/blocking.rs create mode 100644 embassy-boot/boot/src/test_flash/mod.rs diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index f2034fa8a..c76087ff1 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -7,10 +7,16 @@ mod fmt; mod boot_loader; mod digest_adapters; mod firmware_updater; +#[cfg(test)] mod mem_flash; mod partition; +#[cfg(test)] +mod test_flash; pub use partition::Partition; +// 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}; #[cfg(feature = "nightly")] pub use firmware_updater::FirmwareUpdater; diff --git a/embassy-boot/boot/src/test_flash/asynch.rs b/embassy-boot/boot/src/test_flash/asynch.rs new file mode 100644 index 000000000..b5b3c2538 --- /dev/null +++ b/embassy-boot/boot/src/test_flash/asynch.rs @@ -0,0 +1,57 @@ +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; + +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(active: ACTIVE, dfu: DFU, state: STATE) -> Self { + Self { + active: Mutex::new(active), + dfu: Mutex::new(dfu), + state: Mutex::new(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 { + super::BlockingTestFlash::new(self.active.into_inner(), self.dfu.into_inner(), self.state.into_inner()) + } +} diff --git a/embassy-boot/boot/src/test_flash/blocking.rs b/embassy-boot/boot/src/test_flash/blocking.rs new file mode 100644 index 000000000..77876a218 --- /dev/null +++ b/embassy-boot/boot/src/test_flash/blocking.rs @@ -0,0 +1,65 @@ +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; + +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(active: ACTIVE, dfu: DFU, state: STATE) -> Self { + Self { + active: Mutex::new(RefCell::new(active)), + dfu: Mutex::new(RefCell::new(dfu)), + state: Mutex::new(RefCell::new(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 { + super::AsyncTestFlash::new( + self.active.into_inner().into_inner(), + self.dfu.into_inner().into_inner(), + self.state.into_inner().into_inner(), + ) + } +} diff --git a/embassy-boot/boot/src/test_flash/mod.rs b/embassy-boot/boot/src/test_flash/mod.rs new file mode 100644 index 000000000..a0672322e --- /dev/null +++ b/embassy-boot/boot/src/test_flash/mod.rs @@ -0,0 +1,7 @@ +#[cfg(feature = "nightly")] +mod asynch; +mod blocking; + +#[cfg(feature = "nightly")] +pub(crate) use asynch::AsyncTestFlash; +pub(crate) use blocking::BlockingTestFlash; From 551f76c70067bfa14b159a198cdb92f810f40607 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:44:12 +0200 Subject: [PATCH 07/16] Remove legacy Partition type and use the one from embedded-hal --- embassy-boot/boot/Cargo.toml | 7 +- embassy-boot/boot/src/lib.rs | 2 - embassy-boot/boot/src/partition.rs | 144 ----------------------------- 3 files changed, 4 insertions(+), 149 deletions(-) delete mode 100644 embassy-boot/boot/src/partition.rs diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 3fdf69c5e..415d7960f 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -27,9 +27,10 @@ defmt = { version = "0.3", optional = true } digest = "0.10" log = { version = "0.4", optional = true } ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } embedded-storage = "0.3.0" -embedded-storage-async = { version = "0.4.0", optional = true} +embedded-storage-async = { version = "0.4.0", optional = true } salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } signature = { version = "1.6.4", default-features = false } @@ -39,7 +40,7 @@ env_logger = "0.9" rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version futures = { version = "0.3", features = ["executor"] } sha1 = "0.10.5" -embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } +critical-section = { version = "1.1.1", features = ["std"] } [dev-dependencies.ed25519-dalek] default_features = false @@ -49,7 +50,7 @@ features = ["rand", "std", "u32_backend"] ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] -nightly = ["dep:embedded-storage-async"] +nightly = ["dep:embedded-storage-async", "embassy-embedded-hal/nightly"] #Internal features _verify = [] diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index c76087ff1..15d3a4f21 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -9,11 +9,9 @@ mod digest_adapters; mod firmware_updater; #[cfg(test)] mod mem_flash; -mod partition; #[cfg(test)] mod test_flash; -pub use partition::Partition; // 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; diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs deleted file mode 100644 index 7b56a8240..000000000 --- a/embassy-boot/boot/src/partition.rs +++ /dev/null @@ -1,144 +0,0 @@ -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; -#[cfg(feature = "nightly")] -use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; - -/// A region in flash used by the bootloader. -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Partition { - /// The offset into the flash where the partition starts. - pub from: u32, - /// The offset into the flash where the partition ends. - pub to: u32, -} - -impl Partition { - /// Create a new partition with the provided range - pub const fn new(from: u32, to: u32) -> Self { - Self { from, to } - } - - /// Return the size of the partition - pub const fn size(&self) -> u32 { - self.to - self.from - } - - /// Read from the partition on the provided flash - #[cfg(feature = "nightly")] - pub async fn read( - &self, - flash: &mut F, - offset: u32, - bytes: &mut [u8], - ) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.read(offset, bytes).await - } - - /// Write to the partition on the provided flash - #[cfg(feature = "nightly")] - pub async fn write(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.write(offset, bytes).await?; - trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); - Ok(()) - } - - /// Erase part of the partition on the provided flash - #[cfg(feature = "nightly")] - pub async fn erase(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { - let from = self.from as u32 + from; - let to = self.from as u32 + to; - flash.erase(from, to).await?; - trace!("Erased from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } - - /// Erase the entire partition - #[cfg(feature = "nightly")] - pub(crate) async fn wipe(&self, flash: &mut F) -> Result<(), F::Error> { - let from = self.from as u32; - let to = self.to as u32; - flash.erase(from, to).await?; - trace!("Wiped from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } - - /// Read from the partition on the provided flash - pub fn read_blocking(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.read(offset, bytes) - } - - /// Write to the partition on the provided flash - pub fn write_blocking(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.write(offset, bytes)?; - trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); - Ok(()) - } - - /// Erase part of the partition on the provided flash - pub fn erase_blocking(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { - let from = self.from as u32 + from; - let to = self.from as u32 + to; - flash.erase(from, to)?; - trace!("Erased from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } - - /// Erase the entire partition - pub(crate) fn wipe_blocking(&self, flash: &mut F) -> Result<(), F::Error> { - let from = self.from as u32; - let to = self.to as u32; - flash.erase(from, to)?; - trace!("Wiped from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use crate::mem_flash::MemFlash; - use crate::Partition; - - #[test] - fn can_erase() { - let mut flash = MemFlash::<1024, 64, 4>::new(0x00); - let partition = Partition::new(256, 512); - - partition.erase_blocking(&mut flash, 64, 192).unwrap(); - - for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) { - assert_eq!(0x00, byte, "Index {}", index); - } - - for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) { - assert_eq!(0xFF, byte, "Index {}", index); - } - - for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) { - assert_eq!(0x00, byte, "Index {}", index); - } - } - - #[test] - fn can_wipe() { - let mut flash = MemFlash::<1024, 64, 4>::new(0x00); - let partition = Partition::new(256, 512); - - partition.wipe_blocking(&mut flash).unwrap(); - - for (index, byte) in flash.mem.iter().copied().enumerate().take(256) { - assert_eq!(0x00, byte, "Index {}", index); - } - - for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) { - assert_eq!(0xFF, byte, "Index {}", index); - } - - for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) { - assert_eq!(0x00, byte, "Index {}", index); - } - } -} From c6a984f506530bc08464800abc332be9c49ac198 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:55:49 +0200 Subject: [PATCH 08/16] Align tests --- embassy-boot/boot/src/boot_loader.rs | 2 +- embassy-boot/boot/src/lib.rs | 238 +++++++++++-------- embassy-boot/boot/src/test_flash/asynch.rs | 17 +- embassy-boot/boot/src/test_flash/blocking.rs | 22 +- 4 files changed, 162 insertions(+), 117 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index bdf7bd7fd..a8c19197b 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -40,7 +40,7 @@ where /// 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 { +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. diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 15d3a4f21..d13eafdd0 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -51,10 +51,18 @@ impl AsMut<[u8]> for AlignedBuffer { #[cfg(test)] mod tests { + use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; + #[cfg(feature = "nightly")] + 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; + #[cfg(feature = "nightly")] + use crate::test_flash::AsyncTestFlash; + use crate::test_flash::BlockingTestFlash; /* #[test] @@ -73,147 +81,173 @@ mod tests { #[test] fn test_boot_state() { - const STATE: Partition = Partition::new(0, 4096); - const ACTIVE: Partition = Partition::new(4096, 61440); - const DFU: Partition = Partition::new(61440, 122880); + let flash = BlockingTestFlash::new(BootLoaderConfig { + active: MemFlash::<57344, 4096, 4>::default(), + dfu: MemFlash::<61440, 4096, 4>::default(), + state: MemFlash::<4096, 4096, 4>::default(), + }); - let mut flash = MemFlash::<131072, 4096, 4>::default(); - flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); - let mut flash = SingleFlashConfig::new(&mut flash); + flash.state().write(0, &[BOOT_MAGIC; 4]).unwrap(); - let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); + 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 flash, &mut page).unwrap()); + assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap()); } #[test] #[cfg(all(feature = "nightly", not(feature = "_verify")))] fn test_swap_state() { - const STATE: Partition = Partition::new(0, 4096); - const ACTIVE: Partition = Partition::new(4096, 61440); - const DFU: Partition = Partition::new(61440, 122880); - let mut flash = MemFlash::<131072, 4096, 4>::random(); + 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(), + }); - let original = [rand::random::(); ACTIVE.size() as usize]; - let update = [rand::random::(); ACTIVE.size() as usize]; + const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE]; + const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE]; let mut aligned = [0; 4]; - flash.program(ACTIVE.from, &original).unwrap(); + block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); + block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); - let mut updater = FirmwareUpdater::new(DFU, STATE); - block_on(updater.write_firmware(0, &update, &mut flash)).unwrap(); - block_on(updater.mark_updated(&mut flash, &mut aligned)).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(), + }); let mut page = [0; 1024]; - assert_eq!( - State::Swap, - bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) - .unwrap() - ); + assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - flash.assert_eq(ACTIVE.from, &update); + 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.assert_eq(DFU.from + 4096, &original); + 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 SingleFlashConfig::new(&mut flash), &mut page) - .unwrap() - ); + assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - flash.assert_eq(ACTIVE.from, &original); - // Last page is untouched - flash.assert_eq(DFU.from, &update); + 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 - block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap(); - assert_eq!( - State::Boot, - bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) - .unwrap() - ); + 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()); } #[test] #[cfg(all(feature = "nightly", not(feature = "_verify")))] - fn test_separate_flash_active_page_biggest() { - const STATE: Partition = Partition::new(2048, 4096); - const ACTIVE: Partition = Partition::new(4096, 16384); - const DFU: Partition = Partition::new(0, 16384); + 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(), + }); - let mut active = MemFlash::<16384, 4096, 8>::random(); - let mut dfu = MemFlash::<16384, 2048, 8>::random(); - let mut state = MemFlash::<4096, 128, 4>::random(); + const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE]; + const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE]; let mut aligned = [0; 4]; - let original = [rand::random::(); ACTIVE.size() as usize]; - let update = [rand::random::(); ACTIVE.size() as usize]; + block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); + block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - active.program(ACTIVE.from, &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 mut updater = FirmwareUpdater::new(DFU, STATE); + let flash = flash.into_blocking(); + let mut bootloader = BootLoader::new(BootLoaderConfig { + active: flash.active(), + dfu: flash.dfu(), + state: flash.state(), + }); - block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); - block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); - - let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); let mut page = [0; 4096]; + assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - assert_eq!( - State::Swap, - bootloader - .prepare_boot(&mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), &mut page) - .unwrap() - ); - - active.assert_eq(ACTIVE.from, &update); + 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 - dfu.assert_eq(DFU.from + 4096, &original); + flash.dfu().read(4096, &mut read_buf).unwrap(); + assert_eq!(ORIGINAL, read_buf); } #[test] #[cfg(all(feature = "nightly", not(feature = "_verify")))] - fn test_separate_flash_dfu_page_biggest() { - const STATE: Partition = Partition::new(2048, 4096); - const ACTIVE: Partition = Partition::new(4096, 16384); - const DFU: Partition = Partition::new(0, 16384); + 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]; - let mut active = MemFlash::<16384, 2048, 4>::random(); - let mut dfu = MemFlash::<16384, 4096, 8>::random(); - let mut state = MemFlash::<4096, 128, 4>::random(); - let original = [rand::random::(); ACTIVE.size() as usize]; - let update = [rand::random::(); ACTIVE.size() as usize]; + block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); + block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - active.program(ACTIVE.from, &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 mut updater = FirmwareUpdater::new(DFU, STATE); - - block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); - block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); - - let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); + 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 MultiFlashConfig::new(&mut active, &mut state, &mut dfu,), - &mut page - ) - .unwrap() - ); + assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - active.assert_eq(ACTIVE.from, &update); + 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 - dfu.assert_eq(DFU.from + 4096, &original); + flash.dfu().read(4096, &mut read_buf).unwrap(); + assert_eq!(ORIGINAL, read_buf); } #[test] @@ -239,25 +273,25 @@ mod tests { let public_key: PublicKey = keypair.public; // Setup flash - - const STATE: Partition = Partition::new(0, 4096); - const DFU: Partition = Partition::new(4096, 8192); - let mut flash = MemFlash::<8192, 4096, 4>::default(); + 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); - DFU.write_blocking(&mut flash, 0, &write_buf).unwrap(); + flash.dfu().write(0, &write_buf).unwrap(); // On with the test - - let mut updater = FirmwareUpdater::new(DFU, STATE); + let flash = flash.into_async(); + let mut updater = FirmwareUpdater::new(flash.dfu(), flash.state()); let mut aligned = [0; 4]; assert!(block_on(updater.verify_and_mark_updated( - &mut flash, &public_key.to_bytes(), &signature.to_bytes(), firmware_len as u32, diff --git a/embassy-boot/boot/src/test_flash/asynch.rs b/embassy-boot/boot/src/test_flash/asynch.rs index b5b3c2538..3ac9e71ab 100644 --- a/embassy-boot/boot/src/test_flash/asynch.rs +++ b/embassy-boot/boot/src/test_flash/asynch.rs @@ -3,6 +3,8 @@ 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, @@ -20,11 +22,11 @@ where DFU: NorFlash, STATE: NorFlash, { - pub fn new(active: ACTIVE, dfu: DFU, state: STATE) -> Self { + pub fn new(config: BootLoaderConfig) -> Self { Self { - active: Mutex::new(active), - dfu: Mutex::new(dfu), - state: Mutex::new(state), + active: Mutex::new(config.active), + dfu: Mutex::new(config.dfu), + state: Mutex::new(config.state), } } @@ -52,6 +54,11 @@ where STATE: NorFlash + embedded_storage::nor_flash::NorFlash, { pub fn into_blocking(self) -> super::BlockingTestFlash { - super::BlockingTestFlash::new(self.active.into_inner(), self.dfu.into_inner(), self.state.into_inner()) + 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 index 77876a218..ba33c9208 100644 --- a/embassy-boot/boot/src/test_flash/blocking.rs +++ b/embassy-boot/boot/src/test_flash/blocking.rs @@ -5,6 +5,8 @@ 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, @@ -22,11 +24,11 @@ where DFU: NorFlash, STATE: NorFlash, { - pub fn new(active: ACTIVE, dfu: DFU, state: STATE) -> Self { + pub fn new(config: BootLoaderConfig) -> Self { Self { - active: Mutex::new(RefCell::new(active)), - dfu: Mutex::new(RefCell::new(dfu)), - state: Mutex::new(RefCell::new(state)), + active: Mutex::new(RefCell::new(config.active)), + dfu: Mutex::new(RefCell::new(config.dfu)), + state: Mutex::new(RefCell::new(config.state)), } } @@ -49,6 +51,7 @@ where } } +#[cfg(feature = "nightly")] impl BlockingTestFlash where ACTIVE: NorFlash + embedded_storage_async::nor_flash::NorFlash, @@ -56,10 +59,11 @@ where STATE: NorFlash + embedded_storage_async::nor_flash::NorFlash, { pub fn into_async(self) -> super::AsyncTestFlash { - super::AsyncTestFlash::new( - self.active.into_inner().into_inner(), - self.dfu.into_inner().into_inner(), - self.state.into_inner().into_inner(), - ) + 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) } } From 54bbb4400d02e40d142d3f2cd9186c7892374d83 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:56:35 +0200 Subject: [PATCH 09/16] Align nrf --- embassy-boot/nrf/src/lib.rs | 71 +++++++++---------------------------- 1 file changed, 17 insertions(+), 54 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 710798bdb..e26b07c9f 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -3,74 +3,37 @@ #![doc = include_str!("../README.md")] mod fmt; -pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig}; +#[cfg(feature = "nightly")] +pub use embassy_boot::FirmwareUpdater; +pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig}; use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; use embassy_nrf::peripherals::WDT; use embassy_nrf::wdt; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for nRF devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, +pub struct BootLoader { + boot: embassy_boot::BootLoader, aligned_buf: AlignedBuffer, } -#[cfg(target_os = "none")] -impl Default for BootLoader { - /// Create a new bootloader instance using parameters from linker script - fn default() -> 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 { - Partition::new( - &__bootloader_active_start as *const u32 as u32, - &__bootloader_active_end as *const u32 as u32, - ) - }; - let dfu = unsafe { - Partition::new( - &__bootloader_dfu_start as *const u32 as u32, - &__bootloader_dfu_end as *const u32 as u32, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as u32, - &__bootloader_state_end as *const u32 as u32, - ) - }; - - trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to); - trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); - trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); - - Self::new(active, dfu, state) - } -} - -impl BootLoader { +impl + BootLoader +{ /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { + pub fn new(config: BootLoaderConfig) -> Self { Self { - boot: embassy_boot::BootLoader::new(active, dfu, state), + boot: embassy_boot::BootLoader::new(config), aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. - pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, &mut self.aligned_buf.0) { - Ok(_) => self.boot.boot_address(), - Err(_) => panic!("boot prepare error!"), - } + pub fn prepare(&mut self) { + self.boot + .prepare_boot(&mut self.aligned_buf.0) + .expect("Boot prepare error"); } /// Boots the application without softdevice mechanisms. @@ -79,10 +42,10 @@ impl BootLoader { /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(not(feature = "softdevice"))] - pub unsafe fn load(&mut self, start: usize) -> ! { + pub unsafe fn load(&mut self, start: u32) -> ! { let mut p = cortex_m::Peripherals::steal(); p.SCB.invalidate_icache(); - p.SCB.vtor.write(start as u32); + p.SCB.vtor.write(start); cortex_m::asm::bootload(start as *const u32) } @@ -92,7 +55,7 @@ impl BootLoader { /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(feature = "softdevice")] - pub unsafe fn load(&mut self, _app: usize) -> ! { + pub unsafe fn load(&mut self, _app: u32) -> ! { use nrf_softdevice_mbr as mbr; const NRF_SUCCESS: u32 = 0; From 24dee870a862d16fa2dc9be13d3f10dc3941b54b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:57:03 +0200 Subject: [PATCH 10/16] Align rp --- embassy-boot/rp/src/lib.rs | 69 +++++++++----------------------------- 1 file changed, 16 insertions(+), 53 deletions(-) diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index fb9bc3242..e825a7d13 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -3,7 +3,9 @@ #![doc = include_str!("../README.md")] mod fmt; -pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; +#[cfg(feature = "nightly")] +pub use embassy_boot::FirmwareUpdater; +pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; use embassy_rp::flash::{Flash, ERASE_SIZE}; use embassy_rp::peripherals::{FLASH, WATCHDOG}; use embassy_rp::watchdog::Watchdog; @@ -11,27 +13,28 @@ use embassy_time::Duration; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for RP2040 devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, +pub struct BootLoader { + boot: embassy_boot::BootLoader, aligned_buf: AlignedBuffer, } -impl BootLoader { +impl + BootLoader +{ /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { + pub fn new(config: BootLoaderConfig) -> Self { Self { - boot: embassy_boot::BootLoader::new(active, dfu, state), + boot: embassy_boot::BootLoader::new(config), aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. - pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { - Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(), - Err(_) => panic!("boot prepare error!"), - } + pub fn prepare(&mut self) { + self.boot + .prepare_boot(self.aligned_buf.as_mut()) + .expect("Boot prepare error"); } /// Boots the application. @@ -39,58 +42,18 @@ impl BootLoader { /// # Safety /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. - pub unsafe fn load(&mut self, start: usize) -> ! { + pub unsafe fn load(&mut self, start: u32) -> ! { trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); #[cfg(not(armv6m))] p.SCB.invalidate_icache(); - p.SCB.vtor.write(start as u32); + p.SCB.vtor.write(start); cortex_m::asm::bootload(start as *const u32) } } -#[cfg(target_os = "none")] -impl Default for BootLoader { - /// Create a new bootloader instance using parameters from linker script - fn default() -> 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 { - Partition::new( - &__bootloader_active_start as *const u32 as u32, - &__bootloader_active_end as *const u32 as u32, - ) - }; - let dfu = unsafe { - Partition::new( - &__bootloader_dfu_start as *const u32 as u32, - &__bootloader_dfu_end as *const u32 as u32, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as u32, - &__bootloader_state_end as *const u32 as u32, - ) - }; - - trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to); - trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); - trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); - - Self::new(active, dfu, state) - } -} - /// A flash implementation that will feed a watchdog when touching flash. pub struct WatchdogFlash<'d, const SIZE: usize> { flash: Flash<'d, FLASH, SIZE>, From 887ecef3690b55234e640ae16d33a2d79a822bf0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:57:19 +0200 Subject: [PATCH 11/16] Align stm32 --- embassy-boot/stm32/src/lib.rs | 70 +++++++++-------------------------- 1 file changed, 17 insertions(+), 53 deletions(-) diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index ccf136c74..b47b2db43 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -3,30 +3,34 @@ #![doc = include_str!("../README.md")] mod fmt; -pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; +#[cfg(feature = "nightly")] +pub use embassy_boot::FirmwareUpdater; +pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; +use embedded_storage::nor_flash::NorFlash; /// A bootloader for STM32 devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, +pub struct BootLoader { + boot: embassy_boot::BootLoader, aligned_buf: AlignedBuffer, } -impl BootLoader { +impl + BootLoader +{ /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { + pub fn new(config: BootLoaderConfig) -> Self { Self { - boot: embassy_boot::BootLoader::new(active, dfu, state), + boot: embassy_boot::BootLoader::new(config), aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. - pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { - Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(), - Err(_) => panic!("boot prepare error!"), - } + pub fn prepare(&mut self) { + self.boot + .prepare_boot(self.aligned_buf.as_mut()) + .expect("Boot prepare error"); } /// Boots the application. @@ -34,54 +38,14 @@ impl BootLoader { /// # Safety /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. - pub unsafe fn load(&mut self, start: usize) -> ! { + pub unsafe fn load(&mut self, start: u32) -> ! { trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); #[cfg(not(armv6m))] p.SCB.invalidate_icache(); - p.SCB.vtor.write(start as u32); + p.SCB.vtor.write(start); cortex_m::asm::bootload(start as *const u32) } } - -#[cfg(target_os = "none")] -impl Default for BootLoader { - /// Create a new bootloader instance using parameters from linker script - fn default() -> 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 { - Partition::new( - &__bootloader_active_start as *const u32 as u32, - &__bootloader_active_end as *const u32 as u32, - ) - }; - let dfu = unsafe { - Partition::new( - &__bootloader_dfu_start as *const u32 as u32, - &__bootloader_dfu_end as *const u32 as u32, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as u32, - &__bootloader_state_end as *const u32 as u32, - ) - }; - - trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to); - trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); - trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); - - Self::new(active, dfu, state) - } -} From c2aca45b8d3785007da20ce007d6a6e352fac1a0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:57:40 +0200 Subject: [PATCH 12/16] Add offset and size accessors to Partition --- embassy-embedded-hal/src/flash/partition/asynch.rs | 10 ++++++++++ embassy-embedded-hal/src/flash/partition/blocking.rs | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/embassy-embedded-hal/src/flash/partition/asynch.rs b/embassy-embedded-hal/src/flash/partition/asynch.rs index 141e0d9fc..5920436dd 100644 --- a/embassy-embedded-hal/src/flash/partition/asynch.rs +++ b/embassy-embedded-hal/src/flash/partition/asynch.rs @@ -30,6 +30,16 @@ impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> { } Self { flash, offset, size } } + + /// Get the partition offset within the flash + pub const fn offset(&self) -> u32 { + self.offset + } + + /// Get the partition size + pub const fn size(&self) -> u32 { + self.size + } } impl ErrorType for Partition<'_, M, T> { diff --git a/embassy-embedded-hal/src/flash/partition/blocking.rs b/embassy-embedded-hal/src/flash/partition/blocking.rs index dc52e292a..2ddbe3de0 100644 --- a/embassy-embedded-hal/src/flash/partition/blocking.rs +++ b/embassy-embedded-hal/src/flash/partition/blocking.rs @@ -31,6 +31,16 @@ impl<'a, M: RawMutex, T: NorFlash> BlockingPartition<'a, M, T> { } Self { flash, offset, size } } + + /// Get the partition offset within the flash + pub const fn offset(&self) -> u32 { + self.offset + } + + /// Get the partition size + pub const fn size(&self) -> u32 { + self.size + } } impl ErrorType for BlockingPartition<'_, M, T> { From 36e00caf4dc70905b735531c0d5634addd026954 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 14:03:31 +0200 Subject: [PATCH 13/16] Align examples --- embassy-boot/nrf/src/lib.rs | 4 +++- embassy-boot/rp/src/lib.rs | 4 +++- embassy-boot/stm32/src/lib.rs | 4 +++- examples/boot/application/nrf/src/bin/a.rs | 12 ++++++----- examples/boot/application/rp/Cargo.toml | 1 + examples/boot/application/rp/src/bin/a.rs | 19 ++++++++++------- .../boot/application/stm32f3/src/bin/a.rs | 12 ++++++----- .../boot/application/stm32f7/src/bin/a.rs | 14 +++++++------ .../boot/application/stm32h7/src/bin/a.rs | 15 ++++++------- .../boot/application/stm32l1/src/bin/a.rs | 11 +++++----- .../boot/application/stm32l4/src/bin/a.rs | 9 ++++---- .../boot/application/stm32wl/src/bin/a.rs | 9 ++++---- examples/boot/bootloader/nrf/Cargo.toml | 1 + examples/boot/bootloader/nrf/src/main.rs | 21 ++++++++++++------- examples/boot/bootloader/rp/Cargo.toml | 1 + examples/boot/bootloader/rp/src/main.rs | 16 +++++++++----- examples/boot/bootloader/stm32/Cargo.toml | 1 + examples/boot/bootloader/stm32/src/main.rs | 19 +++++++++++------ 18 files changed, 108 insertions(+), 65 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index e26b07c9f..bb702073c 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -42,7 +42,9 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(not(feature = "softdevice"))] - pub unsafe fn load(&mut self, start: u32) -> ! { + pub unsafe fn load(self, start: u32) -> ! { + core::mem::drop(self.boot); + let mut p = cortex_m::Peripherals::steal(); p.SCB.invalidate_icache(); p.SCB.vtor.write(start); diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index e825a7d13..25329f9e9 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -42,7 +42,9 @@ impl /// # Safety /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. - pub unsafe fn load(&mut self, start: u32) -> ! { + pub unsafe fn load(self, start: u32) -> ! { + core::mem::drop(self.boot); + trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index b47b2db43..069de0d1a 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -38,7 +38,9 @@ impl /// # Safety /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. - pub unsafe fn load(&mut self, start: u32) -> ! { + pub unsafe fn load(self, start: u32) -> ! { + core::mem::drop(self.boot); + trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 090a05b23..06c237781 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -3,12 +3,13 @@ #![macro_use] #![feature(type_alias_impl_trait)] -use embassy_boot_nrf::FirmwareUpdater; +use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; use embassy_nrf::nvmc::Nvmc; use embassy_nrf::wdt::{self, Watchdog}; +use embassy_sync::mutex::Mutex; use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); @@ -45,9 +46,10 @@ async fn main(_spawner: Spawner) { }; let nvmc = Nvmc::new(p.NVMC); - let mut nvmc = BlockingAsync::new(nvmc); + let nvmc = Mutex::new(BlockingAsync::new(nvmc)); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc); + let mut updater = FirmwareUpdater::new(config); loop { led.set_low(); button.wait_for_any_edge().await; @@ -56,11 +58,11 @@ async fn main(_spawner: Spawner) { for chunk in APP_B.chunks(4096) { let mut buf: [u8; 4096] = [0; 4096]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut nvmc, 4096).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } let mut magic = [0; 4]; - updater.mark_updated(&mut nvmc, &mut magic).await.unwrap(); + updater.mark_updated(&mut magic).await.unwrap(); led.set_high(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 64c2b8925..4a2c5dd8f 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -20,6 +20,7 @@ embedded-hal = { version = "0.2.6" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" +embedded-storage = "0.3.0" [features] default = ["panic-reset"] diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 47f1d16d8..3fa908b63 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -9,6 +9,9 @@ use embassy_rp::flash::Flash; use embassy_rp::gpio::{Level, Output}; use embassy_rp::watchdog::Watchdog; use embassy_time::{Duration, Timer}; +use embassy_sync::blocking_mutex::Mutex; +use core::cell::RefCell; +use embedded_storage::nor_flash::NorFlash; #[cfg(feature = "panic-probe")] use panic_probe as _; #[cfg(feature = "panic-reset")] @@ -26,9 +29,11 @@ async fn main(_s: Spawner) { let mut watchdog = Watchdog::new(p.WATCHDOG); watchdog.start(Duration::from_secs(8)); - let mut flash: Flash<_, FLASH_SIZE> = Flash::new_blocking(p.FLASH); + let flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); + let flash = Mutex::new(RefCell::new(flash)); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let mut updater = BlockingFirmwareUpdater::new(config); Timer::after(Duration::from_secs(5)).await; watchdog.feed(); @@ -36,8 +41,8 @@ async fn main(_s: Spawner) { let mut offset = 0; let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); defmt::info!("preparing update"); - let mut writer = updater - .prepare_update_blocking(&mut flash) + let writer = updater + .prepare_update() .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) .unwrap(); defmt::info!("writer created, starting write"); @@ -45,13 +50,13 @@ async fn main(_s: Spawner) { buf.0[..chunk.len()].copy_from_slice(chunk); defmt::info!("writing block at offset {}", offset); writer - .write_block_blocking(offset, &buf.0[..], &mut flash, 256) + .write(offset, &buf.0[..]) .unwrap(); - offset += chunk.len(); + offset += chunk.len() as u32; } watchdog.feed(); defmt::info!("firmware written, marking update"); - updater.mark_updated_blocking(&mut flash, &mut buf.0[..1]).unwrap(); + updater.mark_updated(&mut buf.0[..1]).unwrap(); Timer::after(Duration::from_secs(2)).await; led.set_low(); defmt::info!("update marked, resetting"); diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 5db1dbb57..6a5c276fd 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -4,7 +4,8 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; +use embassy_sync::mutex::Mutex; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; @@ -18,7 +19,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let flash = Mutex::new(BlockingAsync::new(flash)); let button = Input::new(p.PC13, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI13); @@ -26,17 +27,18 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PA5, Level::Low, Speed::Low); led.set_high(); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; let mut offset = 0; for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated(magic.as_mut()).await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 5d586445c..530683cac 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -4,7 +4,7 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; @@ -16,7 +16,8 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::new_blocking(p.FLASH); + let flash = Flash::new_blocking(p.FLASH); + let flash = Mutex::new(RefCell::new(flash)); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); @@ -24,20 +25,21 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB7, Level::Low, Speed::Low); led.set_high(); - let mut updater = FirmwareUpdater::default(); - let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let mut updater = BlockingFirmwareUpdater::new(config); + let mut writer = updater.prepare_update().unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); writer - .write_block_blocking(offset, buf.as_ref(), &mut flash, chunk.len()) + .write(offset, buf.as_ref()) .unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); + updater.mark_updated(magic.as_mut()).unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 202220223..e5f94310c 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -4,7 +4,7 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; @@ -16,7 +16,8 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::new_blocking(p.FLASH); + let flash = Flash::new_blocking(p.FLASH); + let flash = Mutex::new(RefCell::new(flash)); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); @@ -24,21 +25,21 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB14, Level::Low, Speed::Low); led.set_high(); - let mut updater = FirmwareUpdater::default(); - - let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let mut updater = BlockingFirmwareUpdater::new(config); + let mut writer = updater.prepare_update().unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); writer - .write_block_blocking(offset, buf.as_ref(), &mut flash, 4096) + .write(offset, buf.as_ref()) .unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); + updater.mark_updated(magic.as_mut()).unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index 4033ac590..00ddda636 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -4,7 +4,7 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; @@ -19,7 +19,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let flash = Mutex::new(BlockingAsync::new(flash)); let button = Input::new(p.PB2, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI2); @@ -28,18 +28,19 @@ async fn main(_spawner: Spawner) { led.set_high(); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; let mut offset = 0; for chunk in APP_B.chunks(128) { let mut buf: [u8; 128] = [0; 128]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 128).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated(magic.as_mut()).await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 141d82afd..54579e4ac 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -4,7 +4,7 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let flash = Mutex::new(BlockingAsync::new(flash)); let button = Input::new(p.PC13, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI13); @@ -26,13 +26,14 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB14, Level::Low, Speed::Low); led.set_high(); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; let mut offset = 0; for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 5f48dbe51..0c6fa05f9 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let mut flash = Mutex::new(BlockingAsync::new(flash)); let button = Input::new(p.PA0, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI0); @@ -26,7 +26,8 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB9, Level::Low, Speed::Low); led.set_high(); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; //defmt::info!("Starting update"); let mut offset = 0; @@ -34,11 +35,11 @@ async fn main(_spawner: Spawner) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); // defmt::info!("Writing chunk at 0x{:x}", offset); - updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated(magic.as_mut()).await.unwrap(); //defmt::info!("Marked as updated"); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 8c2fb4c5f..40656f359 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -12,6 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-nrf = { path = "../../../../embassy-nrf", features = ["nightly"] } embassy-boot-nrf = { path = "../../../../embassy-boot/nrf" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +embassy-sync = { path = "../../../../embassy-sync" } cortex-m-rt = { version = "0.7" } cfg-if = "1.0.0" diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 8818a23b8..72c95c02a 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -1,12 +1,15 @@ #![no_std] #![no_main] +use core::cell::RefCell; + use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_nrf::*; use embassy_nrf::nvmc::Nvmc; use embassy_nrf::wdt; +use embassy_sync::blocking_mutex::Mutex; #[entry] fn main() -> ! { @@ -20,19 +23,21 @@ fn main() -> ! { } */ - let mut bl = BootLoader::default(); - let mut wdt_config = wdt::Config::default(); wdt_config.timeout_ticks = 32768 * 5; // timeout seconds wdt_config.run_during_sleep = true; wdt_config.run_during_debug_halt = false; - let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::new(WatchdogFlash::start( - Nvmc::new(p.NVMC), - p.WDT, - wdt_config, - )))); - unsafe { bl.load(start) } + let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config); + let flash = Mutex::new(RefCell::new(flash)); + + let config = BootLoaderConfig::from_linkerfile_blocking(&flash); + let active_offset = config.active.offset(); + let mut bl: BootLoader<_, _, _> = BootLoader::new(config); + + bl.prepare(); + + unsafe { bl.load(active_offset) } } #[no_mangle] diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index bf9226993..8d60f18be 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -11,6 +11,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-rp = { path = "../../../../embassy-rp", features = ["nightly"] } embassy-boot-rp = { path = "../../../../embassy-boot/rp" } +embassy-sync = { path = "../../../../embassy-sync" } embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 8129591fa..6a81db804 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -1,10 +1,13 @@ #![no_std] #![no_main] +use core::cell::RefCell; + use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_rp::*; +use embassy_sync::blocking_mutex::Mutex; use embassy_time::Duration; const FLASH_SIZE: usize = 2 * 1024 * 1024; @@ -21,13 +24,16 @@ fn main() -> ! { } */ - let mut bl: BootLoader = BootLoader::default(); let flash = WatchdogFlash::::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); - let mut flash = BootFlash::new(flash); - let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); - core::mem::drop(flash); + let flash = Mutex::new(RefCell::new(flash)); - unsafe { bl.load(start) } + let config = BootLoaderConfig::from_linkerfile_blocking(&flash); + let active_offset = config.active.offset(); + let mut bl: BootLoader<_, _, _> = BootLoader::new(config); + + bl.prepare(); + + unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) } } #[no_mangle] diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index fbc80b34c..6436f2fee 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -12,6 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-stm32 = { path = "../../../../embassy-stm32", features = ["nightly"] } embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +embassy-sync = { path = "../../../../embassy-sync" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" embedded-storage-async = "0.4.0" diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index f81fdbc5f..262eed200 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -1,11 +1,14 @@ #![no_std] #![no_main] +use core::cell::RefCell; + use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, BANK1_REGION}; +use embassy_sync::blocking_mutex::Mutex; #[entry] fn main() -> ! { @@ -19,12 +22,16 @@ fn main() -> ! { } */ - let mut bl: BootLoader<2048> = BootLoader::default(); let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); - let mut flash = BootFlash::new(layout.bank1_region); - let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); - core::mem::drop(flash); - unsafe { bl.load(start) } + let flash = Mutex::new(RefCell::new(layout.bank1_region)); + + let config = BootLoaderConfig::from_linkerfile_blocking(&flash); + let active_offset = config.active.offset(); + let mut bl: BootLoader<_, _, _, 2048> = BootLoader::new(config); + + bl.prepare(); + + unsafe { bl.load(BANK1_REGION.base + active_offset) } } #[no_mangle] From b527cc98af97e43186a9676b78b4b7f7ece1fa86 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 14:05:38 +0200 Subject: [PATCH 14/16] Formatting --- examples/boot/application/rp/src/bin/a.rs | 9 ++++----- examples/boot/application/stm32f3/src/bin/a.rs | 2 +- examples/boot/application/stm32f7/src/bin/a.rs | 4 +--- examples/boot/application/stm32h7/src/bin/a.rs | 4 +--- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 3fa908b63..69850069b 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -2,15 +2,16 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::cell::RefCell; + use defmt_rtt as _; use embassy_boot_rp::*; use embassy_executor::Spawner; use embassy_rp::flash::Flash; use embassy_rp::gpio::{Level, Output}; use embassy_rp::watchdog::Watchdog; -use embassy_time::{Duration, Timer}; use embassy_sync::blocking_mutex::Mutex; -use core::cell::RefCell; +use embassy_time::{Duration, Timer}; use embedded_storage::nor_flash::NorFlash; #[cfg(feature = "panic-probe")] use panic_probe as _; @@ -49,9 +50,7 @@ async fn main(_s: Spawner) { for chunk in APP_B.chunks(4096) { buf.0[..chunk.len()].copy_from_slice(chunk); defmt::info!("writing block at offset {}", offset); - writer - .write(offset, &buf.0[..]) - .unwrap(); + writer.write(offset, &buf.0[..]).unwrap(); offset += chunk.len() as u32; } watchdog.feed(); diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 6a5c276fd..c94676f09 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -5,12 +5,12 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; -use embassy_sync::mutex::Mutex; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; +use embassy_sync::mutex::Mutex; use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 530683cac..fc2702c91 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -33,9 +33,7 @@ async fn main(_spawner: Spawner) { let mut buf = AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); - writer - .write(offset, buf.as_ref()) - .unwrap(); + writer.write(offset, buf.as_ref()).unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index e5f94310c..1a54464d0 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -33,9 +33,7 @@ async fn main(_spawner: Spawner) { let mut buf = AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); - writer - .write(offset, buf.as_ref()) - .unwrap(); + writer.write(offset, buf.as_ref()).unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); From b703db4c09d1d4f0c4296a7fd7b808d96f29e1a2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 14:07:35 +0200 Subject: [PATCH 15/16] Fix verify test --- embassy-boot/boot/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index d13eafdd0..45a87bd0e 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -287,7 +287,10 @@ mod tests { // On with the test let flash = flash.into_async(); - let mut updater = FirmwareUpdater::new(flash.dfu(), flash.state()); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }); let mut aligned = [0; 4]; From c22d2b5b5bbc5e3c7d3a039e90b50d39809a10f2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 14:13:53 +0200 Subject: [PATCH 16/16] Remove unused use's --- embassy-boot/boot/src/firmware_updater/asynch.rs | 8 ++++---- embassy-boot/boot/src/firmware_updater/blocking.rs | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index d0780bdf1..0b3f88313 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs @@ -1,5 +1,7 @@ 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; @@ -13,14 +15,12 @@ pub struct FirmwareUpdater { state: 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 - #[cfg(target_os = "none")] - pub fn from_linkerfile(flash: &'a Mutex) -> Self { - use embassy_sync::mutex::Mutex; - + pub fn from_linkerfile(flash: &'a embassy_sync::mutex::Mutex) -> Self { extern "C" { static __bootloader_state_start: u32; static __bootloader_state_end: u32; diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs index c44126149..551150c4f 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs @@ -1,5 +1,7 @@ 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; @@ -13,16 +15,14 @@ pub struct BlockingFirmwareUpdater { state: 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 - #[cfg(target_os = "none")] - pub fn from_linkerfile_blocking(flash: &'a Mutex>) -> Self { - use core::cell::RefCell; - - use embassy_sync::blocking_mutex::Mutex; - + 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;