From a34331ae5fbf76a61bb2f65dbb13af4d34fcb176 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 3 Aug 2023 20:56:04 +0200 Subject: [PATCH] Refactor firmware updater * Allow manipulating state without accessing DFU partition. * Provide aligned buffer when creating updater to reduce potential wrong parameters passed. --- .../boot/src/firmware_updater/asynch.rs | 194 +++++++++-------- .../boot/src/firmware_updater/blocking.rs | 200 ++++++++++-------- embassy-boot/boot/src/firmware_updater/mod.rs | 4 +- embassy-boot/boot/src/lib.rs | 79 ++++--- embassy-boot/nrf/src/lib.rs | 6 +- embassy-boot/rp/src/lib.rs | 6 +- embassy-boot/stm32/src/lib.rs | 6 +- examples/boot/application/nrf/src/bin/a.rs | 8 +- examples/boot/application/rp/src/bin/a.rs | 7 +- .../boot/application/stm32f3/src/bin/a.rs | 8 +- .../boot/application/stm32f7/src/bin/a.rs | 6 +- .../boot/application/stm32h7/src/bin/a.rs | 6 +- .../boot/application/stm32l0/src/bin/a.rs | 8 +- .../boot/application/stm32l1/src/bin/a.rs | 8 +- .../boot/application/stm32l4/src/bin/a.rs | 8 +- .../boot/application/stm32wl/src/bin/a.rs | 8 +- 16 files changed, 307 insertions(+), 255 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index 20731ee0a..ae713bb6f 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs @@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to /// 'mess up' the internal bootloader state -pub struct FirmwareUpdater { +pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { dfu: DFU, - state: STATE, + state: FirmwareState<'d, STATE>, } #[cfg(target_os = "none")] @@ -47,22 +47,12 @@ impl<'a, FLASH: NorFlash> } } -impl FirmwareUpdater { +impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub fn new(config: FirmwareUpdaterConfig) -> Self { + pub fn new(config: FirmwareUpdaterConfig, aligned: &'d mut [u8]) -> Self { Self { dfu: config.dfu, - state: config.state, - } - } - - // Make sure we are running a booted firmware to avoid reverting to a bad state. - async fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - if self.get_state(aligned).await? == State::Boot { - Ok(()) - } else { - Err(FirmwareUpdaterError::BadState) + state: FirmwareState::new(config.state, aligned), } } @@ -71,14 +61,8 @@ impl FirmwareUpdater { /// 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, aligned: &mut [u8]) -> Result { - self.state.read(0, aligned).await?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } + pub async fn get_state(&mut self) -> Result { + self.state.get_state().await } /// Verify the DFU given a public key. If there is an error then DO NOT @@ -92,23 +76,16 @@ impl FirmwareUpdater { /// /// If no signature feature is set then this method will always return a /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. #[cfg(feature = "_verify")] pub async fn verify_and_mark_updated( &mut self, _public_key: &[u8], _signature: &[u8], _update_len: u32, - _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), STATE::WRITE_SIZE); assert!(_update_len <= self.dfu.capacity() as u32); - self.verify_booted(_aligned).await?; + self.state.verify_booted().await?; #[cfg(feature = "ed25519-dalek")] { @@ -121,8 +98,9 @@ impl FirmwareUpdater { let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + let mut chunk_buf = [0; 2]; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message).await?; + self.hash::(_update_len, &mut chunk_buf, &mut message).await?; public_key.verify(&message, &signature).map_err(into_signature_error)? } @@ -143,7 +121,8 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message).await?; + let mut chunk_buf = [0; 2]; + self.hash::(_update_len, &mut chunk_buf, &mut message).await?; let r = public_key.verify(&message, &signature); trace!( @@ -156,7 +135,7 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic(_aligned, SWAP_MAGIC).await + self.state.mark_updated().await } /// Verify the update in DFU with any digest. @@ -177,49 +156,14 @@ impl FirmwareUpdater { } /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// 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 + pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_updated().await } /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// 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) -> Result<(), FirmwareUpdaterError> { - self.state.read(0, aligned).await?; - - if aligned.iter().any(|&b| b != magic) { - // Read progress validity - 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::WRITE_SIZE as u32, aligned).await?; - } - - // Clear magic and progress - self.state.erase(0, self.state.capacity() as u32).await?; - - // Set magic - aligned.fill(magic); - self.state.write(0, aligned).await?; - } - Ok(()) + pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_booted().await } /// Write data to a flash page. @@ -229,16 +173,10 @@ impl FirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( - &mut self, - aligned: &mut [u8], - offset: usize, - data: &[u8], - ) -> Result<(), FirmwareUpdaterError> { + pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= DFU::ERASE_SIZE); - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned).await?; + self.state.verify_booted().await?; self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; @@ -252,20 +190,94 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - /// - /// # Safety - /// - /// 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 prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned).await?; - + pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + self.state.verify_booted().await?; self.dfu.erase(0, self.dfu.capacity() as u32).await?; Ok(&mut self.dfu) } } +/// Manages the state partition of the firmware update. +/// +/// Can be used standalone for more fine grained control, or as part of the updater. +pub struct FirmwareState<'d, STATE> { + state: STATE, + aligned: &'d mut [u8], +} + +impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { + /// Create a firmware state instance with a buffer for magic content and state partition. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + Self { state, aligned } + } + + // Make sure we are running a booted firmware to avoid reverting to a bad state. + async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + if self.get_state().await? == State::Boot { + Ok(()) + } else { + Err(FirmwareUpdaterError::BadState) + } + } + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub async fn get_state(&mut self) -> Result { + self.state.read(0, &mut self.aligned).await?; + + if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Mark to trigger firmware swap on next boot. + pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(SWAP_MAGIC).await + } + + /// Mark firmware boot successful and stop rollback on reset. + pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(BOOT_MAGIC).await + } + + async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> { + self.state.read(0, &mut self.aligned).await?; + + if self.aligned.iter().any(|&b| b != magic) { + // Read progress validity + self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?; + + if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + self.aligned.fill(!STATE_ERASE_VALUE); + self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?; + } + + // Clear magic and progress + self.state.erase(0, self.state.capacity() as u32).await?; + + // Set magic + self.aligned.fill(magic); + self.state.write(0, &self.aligned).await?; + } + Ok(()) + } +} + #[cfg(test)] mod tests { use embassy_embedded_hal::flash::partition::Partition; @@ -288,8 +300,8 @@ mod tests { let mut to_write = [0; 4096]; to_write[..7].copy_from_slice(update.as_slice()); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); - block_on(updater.write_firmware(&mut aligned, 0, to_write.as_slice())).unwrap(); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + block_on(updater.write_firmware(0, to_write.as_slice())).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; block_on(updater.hash::(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs index f03f53e4d..76e4264a0 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs @@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG /// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to /// 'mess up' the internal bootloader state -pub struct BlockingFirmwareUpdater { +pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { dfu: DFU, - state: STATE, + state: BlockingFirmwareState<'d, STATE>, } #[cfg(target_os = "none")] @@ -49,22 +49,17 @@ impl<'a, FLASH: NorFlash> } } -impl BlockingFirmwareUpdater { +impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> { /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub fn new(config: FirmwareUpdaterConfig) -> Self { + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + pub fn new(config: FirmwareUpdaterConfig, aligned: &'d mut [u8]) -> Self { Self { dfu: config.dfu, - state: config.state, - } - } - - // Make sure we are running a booted firmware to avoid reverting to a bad state. - fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - if self.get_state(aligned)? == State::Boot { - Ok(()) - } else { - Err(FirmwareUpdaterError::BadState) + state: BlockingFirmwareState::new(config.state, aligned), } } @@ -73,14 +68,8 @@ impl BlockingFirmwareUpdater { /// This is useful to check if the bootloader has just done a swap, in order /// to do verifications and self-tests of the new image before calling /// `mark_booted`. - pub fn get_state(&mut self, aligned: &mut [u8]) -> Result { - self.state.read(0, aligned)?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } + pub fn get_state(&mut self) -> Result { + self.state.get_state() } /// Verify the DFU given a public key. If there is an error then DO NOT @@ -94,23 +83,16 @@ impl BlockingFirmwareUpdater { /// /// If no signature feature is set then this method will always return a /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. #[cfg(feature = "_verify")] pub fn verify_and_mark_updated( &mut self, _public_key: &[u8], _signature: &[u8], _update_len: u32, - _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), STATE::WRITE_SIZE); assert!(_update_len <= self.dfu.capacity() as u32); - self.verify_booted(_aligned)?; + self.state.verify_booted()?; #[cfg(feature = "ed25519-dalek")] { @@ -124,7 +106,8 @@ impl BlockingFirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message)?; + let mut chunk_buf = [0; 2]; + self.hash::(_update_len, &mut chunk_buf, &mut message)?; public_key.verify(&message, &signature).map_err(into_signature_error)? } @@ -145,7 +128,8 @@ impl BlockingFirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message)?; + let mut chunk_buf = [0; 2]; + self.hash::(_update_len, &mut chunk_buf, &mut message)?; let r = public_key.verify(&message, &signature); trace!( @@ -158,7 +142,7 @@ impl BlockingFirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic(_aligned, SWAP_MAGIC) + self.state.mark_updated() } /// Verify the update in DFU with any digest. @@ -179,49 +163,14 @@ impl BlockingFirmwareUpdater { } /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// 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(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC) + pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_updated() } /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// 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(&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(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(STATE::WRITE_SIZE as u32, aligned)?; - } - - // Clear magic and progress - self.state.erase(0, self.state.capacity() as u32)?; - - // Set magic - aligned.fill(magic); - self.state.write(0, aligned)?; - } - Ok(()) + pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_booted() } /// Write data to a flash page. @@ -231,15 +180,9 @@ impl BlockingFirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware( - &mut self, - aligned: &mut [u8], - offset: usize, - data: &[u8], - ) -> Result<(), FirmwareUpdaterError> { + pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= DFU::ERASE_SIZE); - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned)?; + self.state.verify_booted()?; self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; @@ -253,19 +196,94 @@ impl BlockingFirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned)?; + pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + self.state.verify_booted()?; self.dfu.erase(0, self.dfu.capacity() as u32)?; Ok(&mut self.dfu) } } +/// Manages the state partition of the firmware update. +/// +/// Can be used standalone for more fine grained control, or as part of the updater. +pub struct BlockingFirmwareState<'d, STATE> { + state: STATE, + aligned: &'d mut [u8], +} + +impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { + /// Create a firmware state instance with a buffer for magic content and state partition. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + Self { state, aligned } + } + + // Make sure we are running a booted firmware to avoid reverting to a bad state. + fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + if self.get_state()? == State::Boot { + Ok(()) + } else { + Err(FirmwareUpdaterError::BadState) + } + } + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub fn get_state(&mut self) -> Result { + self.state.read(0, &mut self.aligned)?; + + if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Mark to trigger firmware swap on next boot. + pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(SWAP_MAGIC) + } + + /// Mark firmware boot successful and stop rollback on reset. + pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(BOOT_MAGIC) + } + + fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> { + self.state.read(0, &mut self.aligned)?; + + if self.aligned.iter().any(|&b| b != magic) { + // Read progress validity + self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned)?; + + if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + self.aligned.fill(!STATE_ERASE_VALUE); + self.state.write(STATE::WRITE_SIZE as u32, &self.aligned)?; + } + + // Clear magic and progress + self.state.erase(0, self.state.capacity() as u32)?; + + // Set magic + self.aligned.fill(magic); + self.state.write(0, &self.aligned)?; + } + Ok(()) + } +} + #[cfg(test)] mod tests { use core::cell::RefCell; @@ -283,14 +301,14 @@ mod tests { let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 4096, 8>::default())); let state = BlockingPartition::new(&flash, 0, 4096); let dfu = BlockingPartition::new(&flash, 65536, 65536); + let mut aligned = [0; 8]; let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; let mut to_write = [0; 4096]; to_write[..7].copy_from_slice(update.as_slice()); - let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); - let mut aligned = [0; 8]; - updater.write_firmware(&mut aligned, 0, to_write.as_slice()).unwrap(); + let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + updater.write_firmware(0, to_write.as_slice()).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; updater diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs index 55ce8f363..937ddcc69 100644 --- a/embassy-boot/boot/src/firmware_updater/mod.rs +++ b/embassy-boot/boot/src/firmware_updater/mod.rs @@ -3,8 +3,8 @@ mod asynch; mod blocking; #[cfg(feature = "nightly")] -pub use asynch::FirmwareUpdater; -pub use blocking::BlockingFirmwareUpdater; +pub use asynch::{FirmwareState, FirmwareUpdater}; +pub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater}; use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; /// Firmware updater flash configuration holding the two flashes used by the updater diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 016362b86..47f7c1797 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -16,9 +16,11 @@ mod test_flash; // TODO: Use the value provided by NorFlash when available pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF; pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; +pub use firmware_updater::{ + BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError, +}; #[cfg(feature = "nightly")] -pub use firmware_updater::FirmwareUpdater; -pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError}; +pub use firmware_updater::{FirmwareState, FirmwareUpdater}; pub(crate) const BOOT_MAGIC: u8 = 0xD0; pub(crate) const SWAP_MAGIC: u8 = 0xF0; @@ -118,15 +120,18 @@ mod tests { block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); - block_on(updater.mark_updated(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated()).unwrap(); // Writing after marking updated is not allowed until marked as booted. - let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)); + let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(0, &UPDATE)); assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState))); let flash = flash.into_blocking(); @@ -158,11 +163,14 @@ mod tests { // Mark as booted 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 mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.mark_booted()).unwrap(); let flash = flash.into_blocking(); let mut bootloader = BootLoader::new(BootLoaderConfig { @@ -190,12 +198,15 @@ mod tests { block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); - block_on(updater.mark_updated(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated()).unwrap(); let flash = flash.into_blocking(); let mut bootloader = BootLoader::new(BootLoaderConfig { @@ -232,12 +243,15 @@ mod tests { block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); - block_on(updater.mark_updated(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated()).unwrap(); let flash = flash.into_blocking(); let mut bootloader = BootLoader::new(BootLoaderConfig { @@ -293,18 +307,19 @@ mod tests { // On with the test let flash = flash.into_async(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - let mut aligned = [0; 4]; + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); assert!(block_on(updater.verify_and_mark_updated( &public_key.to_bytes(), &signature.to_bytes(), firmware_len as u32, - &mut aligned, )) .is_ok()); } diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 65f57fcd1..df94819fc 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -3,9 +3,11 @@ #![doc = include_str!("../README.md")] mod fmt; +pub use embassy_boot::{ + AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, +}; #[cfg(feature = "nightly")] -pub use embassy_boot::FirmwareUpdater; -pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig}; +pub use embassy_boot::{FirmwareState, FirmwareUpdater}; use embassy_nrf::nvmc::PAGE_SIZE; use embassy_nrf::peripherals::WDT; use embassy_nrf::wdt; diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 35fc104ec..f5aefa416 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -3,9 +3,11 @@ #![doc = include_str!("../README.md")] mod fmt; +pub use embassy_boot::{ + AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State, +}; #[cfg(feature = "nightly")] -pub use embassy_boot::FirmwareUpdater; -pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; +pub use embassy_boot::{FirmwareState, FirmwareUpdater}; use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE}; use embassy_rp::peripherals::{FLASH, WATCHDOG}; use embassy_rp::watchdog::Watchdog; diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 069de0d1a..25f029423 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -3,9 +3,11 @@ #![doc = include_str!("../README.md")] mod fmt; +pub use embassy_boot::{ + AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State, +}; #[cfg(feature = "nightly")] -pub use embassy_boot::FirmwareUpdater; -pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; +pub use embassy_boot::{FirmwareState, FirmwareUpdater}; use embedded_storage::nor_flash::NorFlash; /// A bootloader for STM32 devices. diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 021d77f3b..8b510ed35 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -52,20 +52,20 @@ async fn main(_spawner: Spawner) { let nvmc = Mutex::new(BlockingAsync::new(nvmc)); let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc); - let mut updater = FirmwareUpdater::new(config); + let mut magic = [0; 4]; + let mut updater = FirmwareUpdater::new(config, &mut magic); loop { led.set_low(); button.wait_for_any_edge().await; if button.is_low() { let mut offset = 0; - let mut magic = [0; 4]; for chunk in APP_B.chunks(4096) { let mut buf: [u8; 4096] = [0; 4096]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(&mut magic, offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(&mut magic).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_high(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index b5e1950cc..f0dda39d0 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -38,7 +38,8 @@ async fn main(_s: Spawner) { let flash = Mutex::new(RefCell::new(flash)); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); - let mut updater = BlockingFirmwareUpdater::new(config); + let mut aligned = AlignedBuffer([0; 4]); + let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0); Timer::after(Duration::from_secs(5)).await; watchdog.feed(); @@ -47,7 +48,7 @@ async fn main(_s: Spawner) { let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); defmt::info!("preparing update"); let writer = updater - .prepare_update(&mut buf.0[..1]) + .prepare_update() .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) .unwrap(); defmt::info!("writer created, starting write"); @@ -59,7 +60,7 @@ async fn main(_s: Spawner) { } watchdog.feed(); defmt::info!("firmware written, marking update"); - updater.mark_updated(&mut buf.0[..1]).unwrap(); + updater.mark_updated().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 c0a11d699..8be39bfb7 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -31,17 +31,17 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; let mut offset = 0; - let mut magic = AlignedBuffer([0; WRITE_SIZE]); for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().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 dea682a96..0c3819bed 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -33,9 +33,9 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); - let mut updater = BlockingFirmwareUpdater::new(config); let mut magic = AlignedBuffer([0; WRITE_SIZE]); - let writer = updater.prepare_update(magic.as_mut()).unwrap(); + let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0); + let writer = updater.prepare_update().unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { writer.write(offset, buf.as_ref()).unwrap(); offset += chunk.len() as u32; } - updater.mark_updated(magic.as_mut()).unwrap(); + updater.mark_updated().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 719176692..f239e3732 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -34,8 +34,8 @@ async fn main(_spawner: Spawner) { let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); - let mut updater = BlockingFirmwareUpdater::new(config); - let writer = updater.prepare_update(magic.as_mut()).unwrap(); + let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0); + let writer = updater.prepare_update().unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { writer.write(offset, buf.as_ref()).unwrap(); offset += chunk.len() as u32; } - updater.mark_updated(magic.as_mut()).unwrap(); + updater.mark_updated().unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index ce80056e6..b4cdcd44d 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -33,18 +33,18 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; let mut offset = 0; - let mut magic = AlignedBuffer([0; WRITE_SIZE]); for chunk in APP_B.chunks(128) { let mut buf: [u8; 128] = [0; 128]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; 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 1e9bf3cb9..b4cdcd44d 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -33,18 +33,18 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); - button.wait_for_falling_edge().await; let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); + 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(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().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 a514ab5be..eefa25f75 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -31,17 +31,17 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); - button.wait_for_falling_edge().await; let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); + 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(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 52a197a5c..c837e47b5 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -31,19 +31,19 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; //defmt::info!("Starting update"); - let mut magic = AlignedBuffer([0; WRITE_SIZE]); 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); // defmt::info!("Writing chunk at 0x{:x}", offset); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); //defmt::info!("Marked as updated"); led.set_low(); cortex_m::peripheral::SCB::sys_reset();