diff --git a/embassy-boot/README.md b/embassy-boot/README.md new file mode 100644 index 00000000..41440537 --- /dev/null +++ b/embassy-boot/README.md @@ -0,0 +1,30 @@ +# embassy-boot + +An [Embassy](https://embassy.dev) project. + +A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks. + +The bootloader can be used either as a library or be flashed directly with the default configuration derived from linker scripts. + +By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself. + +## Hardware support + +The bootloader supports different hardware in separate crates: + +* `embassy-boot-nrf` - for the nRF microcontrollers. +* `embassy-boot-stm32` - for the STM32 microcontrollers. + +## Minimum supported Rust version (MSRV) + +`embassy-boot` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 51e1056c..e8ebe628 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -1,53 +1,54 @@ #![feature(type_alias_impl_trait)] #![feature(generic_associated_types)] -#![feature(generic_const_exprs)] -#![allow(incomplete_features)] #![no_std] -///! embassy-boot is a bootloader and firmware updater for embedded devices with flash -///! storage implemented using embedded-storage -///! -///! The bootloader works in conjunction with the firmware application, and only has the -///! ability to manage two flash banks with an active and a updatable part. It implements -///! a swap algorithm that is power-failure safe, and allows reverting to the previous -///! version of the firmware, should the application crash and fail to mark itself as booted. -///! -///! This library is intended to be used by platform-specific bootloaders, such as embassy-boot-nrf, -///! which defines the limits and flash type for that particular platform. -///! +#![warn(missing_docs)] +#![doc = include_str!("../../README.md")] mod fmt; -use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use embedded_storage_async::nor_flash::AsyncNorFlash; const BOOT_MAGIC: u8 = 0xD0; const SWAP_MAGIC: u8 = 0xF0; +/// A region in flash used by the bootloader. #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Partition { + /// Start of the flash region. pub from: usize, + /// End of the flash region. pub to: usize, } impl Partition { + /// Create a new partition with the provided range pub const fn new(from: usize, to: usize) -> Self { Self { from, to } } + + /// Return the length of the partition pub const fn len(&self) -> usize { self.to - self.from } } -#[derive(PartialEq, Debug)] +/// The state of the bootloader after running prepare. +#[derive(PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum State { + /// Bootloader is ready to boot the active partition. Boot, + /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. Swap, } -#[derive(PartialEq, Debug)] +/// Errors returned by bootloader +#[derive(PartialEq, Eq, Debug)] pub enum BootError { + /// Error from flash. Flash(NorFlashErrorKind), + /// Invalid bootloader magic BadMagic, } @@ -60,19 +61,39 @@ where } } -pub trait FlashConfig { - const BLOCK_SIZE: usize; - const ERASE_VALUE: u8; - type FLASH: NorFlash + ReadNorFlash; +/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. +#[repr(align(32))] +pub struct AlignedBuffer(pub [u8; N]); - fn flash(&mut self) -> &mut Self::FLASH; +impl AsRef<[u8]> for AlignedBuffer { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for AlignedBuffer { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +/// Extension of the embedded-storage flash type information with block size and erase value. +pub trait Flash: NorFlash + ReadNorFlash { + /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase + /// size of the flash, but for external QSPI flash modules, this can be lower. + const BLOCK_SIZE: usize; + /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. + const ERASE_VALUE: u8 = 0xFF; } /// Trait defining the flash handles used for active and DFU partition -pub trait FlashProvider { - type STATE: FlashConfig; - type ACTIVE: FlashConfig; - type DFU: FlashConfig; +pub trait FlashConfig { + /// Flash type used for the state partition. + type STATE: Flash; + /// Flash type used for the active partition. + type ACTIVE: Flash; + /// Flash type used for the dfu partition. + type DFU: Flash; /// Return flash instance used to write/read to/from active partition. fn active(&mut self) -> &mut Self::ACTIVE; @@ -84,9 +105,7 @@ pub trait FlashProvider { /// BootLoader works with any flash implementing embedded_storage and can also work with /// different page sizes and flash write sizes. -/// -/// The PAGE_SIZE const parameter must be a multiple of the ACTIVE and DFU page sizes. -pub struct BootLoader { +pub struct BootLoader { // Page with current state of bootloader. The state partition has the following format: // | Range | Description | // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | @@ -98,15 +117,16 @@ pub struct BootLoader { dfu: Partition, } -impl BootLoader { +impl BootLoader { + /// Create a new instance of a bootloader with the given 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 { - assert_eq!(active.len() % PAGE_SIZE, 0); - assert_eq!(dfu.len() % PAGE_SIZE, 0); - // DFU partition must have an extra page - assert!(dfu.len() - active.len() >= PAGE_SIZE); Self { active, dfu, state } } + /// Return the boot address for the active partition. pub fn boot_address(&self) -> usize { self.active.from } @@ -194,44 +214,43 @@ impl BootLoader { /// | DFU | 3 | 3 | 2 | 1 | 3 | /// +-----------+--------------+--------+--------+--------+--------+ /// - pub fn prepare_boot(&mut self, p: &mut P) -> Result - where - [(); <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - [(); <

::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:, - { + pub fn prepare_boot( + &mut self, + p: &mut P, + magic: &mut [u8], + page: &mut [u8], + ) -> Result { // Ensure we have enough progress pages to store copy progress - assert!( - self.active.len() / PAGE_SIZE - <= (self.state.len() - <

::STATE as FlashConfig>::FLASH::WRITE_SIZE) - / <

::STATE as FlashConfig>::FLASH::WRITE_SIZE - ); + assert_eq!(self.active.len() % page.len(), 0); + assert_eq!(self.dfu.len() % page.len(), 0); + assert!(self.dfu.len() - self.active.len() >= page.len()); + assert!(self.active.len() / page.len() <= (self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE); + assert_eq!(magic.len(), P::STATE::WRITE_SIZE); // Copy contents from partition N to active - let state = self.read_state(p.state())?; + let state = self.read_state(p, magic)?; match 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.state())? { + if !self.is_swapped(p, magic, page)? { trace!("Swapping"); - self.swap(p)?; + self.swap(p, magic, page)?; trace!("Swapping done"); } else { trace!("Reverting"); - self.revert(p)?; + self.revert(p, magic, page)?; // Overwrite magic and reset progress - let fstate = p.state().flash(); - let aligned = Aligned( - [!P::STATE::ERASE_VALUE; <

::STATE as FlashConfig>::FLASH::WRITE_SIZE], - ); - fstate.write(self.state.from as u32, &aligned.0)?; + let fstate = p.state(); + magic.fill(!P::STATE::ERASE_VALUE); + fstate.write(self.state.from as u32, magic)?; fstate.erase(self.state.from as u32, self.state.to as u32)?; - let aligned = - Aligned([BOOT_MAGIC; <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]); - fstate.write(self.state.from as u32, &aligned.0)?; + + magic.fill(BOOT_MAGIC); + fstate.write(self.state.from as u32, magic)?; } } _ => {} @@ -239,166 +258,152 @@ impl BootLoader { Ok(state) } - fn is_swapped(&mut self, p: &mut P) -> Result - where - [(); P::FLASH::WRITE_SIZE]:, - { - let page_count = self.active.len() / P::FLASH::ERASE_SIZE; - let progress = self.current_progress(p)?; + fn is_swapped(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result { + let page_size = page.len(); + let page_count = self.active.len() / page_size; + let progress = self.current_progress(p, magic)?; Ok(progress >= page_count * 2) } - fn current_progress(&mut self, p: &mut P) -> Result - where - [(); P::FLASH::WRITE_SIZE]:, - { - let write_size = P::FLASH::WRITE_SIZE; + fn current_progress(&mut self, config: &mut P, aligned: &mut [u8]) -> Result { + let write_size = aligned.len(); let max_index = ((self.state.len() - write_size) / write_size) - 1; - let flash = p.flash(); - let mut aligned = Aligned([!P::ERASE_VALUE; P::FLASH::WRITE_SIZE]); + aligned.fill(!P::STATE::ERASE_VALUE); + + let flash = config.state(); for i in 0..max_index { - flash.read((self.state.from + write_size + i * write_size) as u32, &mut aligned.0)?; - if aligned.0 == [P::ERASE_VALUE; P::FLASH::WRITE_SIZE] { + flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; + + if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { return Ok(i); } } Ok(max_index) } - fn update_progress(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> - where - [(); P::FLASH::WRITE_SIZE]:, - { - let flash = p.flash(); - let write_size = P::FLASH::WRITE_SIZE; + fn update_progress(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { + let flash = p.state(); + let write_size = magic.len(); let w = self.state.from + write_size + idx * write_size; - let aligned = Aligned([!P::ERASE_VALUE; P::FLASH::WRITE_SIZE]); - flash.write(w as u32, &aligned.0)?; + + let aligned = magic; + aligned.fill(!P::STATE::ERASE_VALUE); + flash.write(w as u32, aligned)?; Ok(()) } - fn active_addr(&self, n: usize) -> usize { - self.active.from + n * PAGE_SIZE + fn active_addr(&self, n: usize, page_size: usize) -> usize { + self.active.from + n * page_size } - fn dfu_addr(&self, n: usize) -> usize { - self.dfu.from + n * PAGE_SIZE + fn dfu_addr(&self, n: usize, page_size: usize) -> usize { + self.dfu.from + n * page_size } - fn copy_page_once_to_active( + fn copy_page_once_to_active( &mut self, idx: usize, from_page: usize, to_page: usize, p: &mut P, - ) -> Result<(), BootError> - where - [(); <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - { - let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; - if self.current_progress(p.state())? <= idx { + magic: &mut [u8], + page: &mut [u8], + ) -> Result<(), BootError> { + let buf = page; + if self.current_progress(p, magic)? <= idx { let mut offset = from_page; for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { - p.dfu().flash().read(offset as u32, chunk)?; + p.dfu().read(offset as u32, chunk)?; offset += chunk.len(); } - p.active().flash().erase(to_page as u32, (to_page + PAGE_SIZE) as u32)?; + p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; let mut offset = to_page; for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { - p.active().flash().write(offset as u32, &chunk)?; + p.active().write(offset as u32, chunk)?; offset += chunk.len(); } - self.update_progress(idx, p.state())?; + self.update_progress(idx, p, magic)?; } Ok(()) } - fn copy_page_once_to_dfu( + fn copy_page_once_to_dfu( &mut self, idx: usize, from_page: usize, to_page: usize, p: &mut P, - ) -> Result<(), BootError> - where - [(); <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - { - let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; - if self.current_progress(p.state())? <= idx { + magic: &mut [u8], + page: &mut [u8], + ) -> Result<(), BootError> { + let buf = page; + if self.current_progress(p, magic)? <= idx { let mut offset = from_page; for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { - p.active().flash().read(offset as u32, chunk)?; + p.active().read(offset as u32, chunk)?; offset += chunk.len(); } - p.dfu().flash().erase(to_page as u32, (to_page + PAGE_SIZE) as u32)?; + p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; let mut offset = to_page; for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { - p.dfu().flash().write(offset as u32, chunk)?; + p.dfu().write(offset as u32, chunk)?; offset += chunk.len(); } - self.update_progress(idx, p.state())?; + self.update_progress(idx, p, magic)?; } Ok(()) } - fn swap(&mut self, p: &mut P) -> Result<(), BootError> - where - [(); <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - { - let page_count = self.active.len() / PAGE_SIZE; + fn swap(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { + let page_size = page.len(); + let page_count = self.active.len() / page_size; trace!("Page count: {}", page_count); - for page in 0..page_count { - trace!("COPY PAGE {}", page); + for page_num in 0..page_count { + trace!("COPY PAGE {}", page_num); // Copy active page to the 'next' DFU page. - let active_page = self.active_addr(page_count - 1 - page); - let dfu_page = self.dfu_addr(page_count - page); + let active_page = self.active_addr(page_count - 1 - page_num, page_size); + let dfu_page = self.dfu_addr(page_count - page_num, page_size); //trace!("Copy active {} to dfu {}", active_page, dfu_page); - self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?; + self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; // Copy DFU page to the active page - let active_page = self.active_addr(page_count - 1 - page); - let dfu_page = self.dfu_addr(page_count - 1 - page); + let active_page = self.active_addr(page_count - 1 - page_num, page_size); + let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); //trace!("Copy dfy {} to active {}", dfu_page, active_page); - self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?; + self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; } Ok(()) } - fn revert(&mut self, p: &mut P) -> Result<(), BootError> - where - [(); <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - { - let page_count = self.active.len() / PAGE_SIZE; - for page in 0..page_count { + fn revert(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { + let page_size = page.len(); + let page_count = self.active.len() / page_size; + for page_num in 0..page_count { // Copy the bad active page to the DFU page - let active_page = self.active_addr(page); - let dfu_page = self.dfu_addr(page); - self.copy_page_once_to_dfu(page_count * 2 + page * 2, active_page, dfu_page, p)?; + let active_page = self.active_addr(page_num, page_size); + let dfu_page = self.dfu_addr(page_num, page_size); + self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; // Copy the DFU page back to the active page - let active_page = self.active_addr(page); - let dfu_page = self.dfu_addr(page + 1); - self.copy_page_once_to_active(page_count * 2 + page * 2 + 1, dfu_page, active_page, p)?; + let active_page = self.active_addr(page_num, page_size); + let dfu_page = self.dfu_addr(page_num + 1, page_size); + self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; } Ok(()) } - fn read_state(&mut self, p: &mut P) -> Result - where - [(); P::FLASH::WRITE_SIZE]:, - { - let mut magic: [u8; P::FLASH::WRITE_SIZE] = [0; P::FLASH::WRITE_SIZE]; - let flash = p.flash(); - flash.read(self.state.from as u32, &mut magic)?; + fn read_state(&mut self, config: &mut P, magic: &mut [u8]) -> Result { + let flash = config.state(); + flash.read(self.state.from as u32, magic)?; - if magic == [SWAP_MAGIC; P::FLASH::WRITE_SIZE] { + if !magic.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) } else { Ok(State::Boot) @@ -406,108 +411,149 @@ impl BootLoader { } } -/// Convenience provider that uses a single flash for everything -pub struct SingleFlashProvider<'a, F, const ERASE_VALUE: u8 = 0xFF> +/// Convenience provider that uses a single flash for all partitions. +pub struct SingleFlashConfig<'a, F> where - F: NorFlash + ReadNorFlash, + F: Flash, { - config: SingleFlashConfig<'a, F, ERASE_VALUE>, + flash: &'a mut F, } -impl<'a, F, const ERASE_VALUE: u8> SingleFlashProvider<'a, F, ERASE_VALUE> +impl<'a, F> SingleFlashConfig<'a, F> where - F: NorFlash + ReadNorFlash, + F: Flash, { + /// Create a provider for a single flash. pub fn new(flash: &'a mut F) -> Self { - Self { - config: SingleFlashConfig { flash }, - } + Self { flash } } } -pub struct SingleFlashConfig<'a, F, const ERASE_VALUE: u8 = 0xFF> +impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> +where + F: Flash, +{ + 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 + } +} + +/// A flash wrapper implementing the Flash and embedded_storage traits. +pub struct BootFlash<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF> where F: NorFlash + ReadNorFlash, { flash: &'a mut F, } -impl<'a, F> FlashProvider for SingleFlashProvider<'a, F> +impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> where F: NorFlash + ReadNorFlash, { - type STATE = SingleFlashConfig<'a, F>; - type ACTIVE = SingleFlashConfig<'a, F>; - type DFU = SingleFlashConfig<'a, F>; - - fn active(&mut self) -> &mut Self::STATE { - &mut self.config - } - fn dfu(&mut self) -> &mut Self::ACTIVE { - &mut self.config - } - fn state(&mut self) -> &mut Self::DFU { - &mut self.config + /// Create a new instance of a bootable flash + pub fn new(flash: &'a mut F) -> Self { + Self { flash } } } -impl<'a, F, const ERASE_VALUE: u8> FlashConfig for SingleFlashConfig<'a, F, ERASE_VALUE> +impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> where F: NorFlash + ReadNorFlash, { - const BLOCK_SIZE: usize = F::ERASE_SIZE; + const BLOCK_SIZE: usize = BLOCK_SIZE; const ERASE_VALUE: u8 = ERASE_VALUE; - type FLASH = F; - fn flash(&mut self) -> &mut F { - self.flash +} + +impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +where + F: ReadNorFlash + NorFlash, +{ + type Error = F::Error; +} + +impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +where + F: ReadNorFlash + 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(self.flash, from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + F::write(self.flash, offset, bytes) } } -/// Convenience provider that uses a single flash for everything -pub struct MultiFlashProvider<'a, ACTIVE, STATE, DFU> +impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> where - ACTIVE: NorFlash + ReadNorFlash, - STATE: NorFlash + ReadNorFlash, - DFU: NorFlash + ReadNorFlash, + F: ReadNorFlash + NorFlash, { - active: SingleFlashConfig<'a, ACTIVE>, - state: SingleFlashConfig<'a, STATE>, - dfu: SingleFlashConfig<'a, DFU>, + const READ_SIZE: usize = F::READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + F::read(self.flash, offset, bytes) + } + + fn capacity(&self) -> usize { + F::capacity(self.flash) + } } -impl<'a, ACTIVE, STATE, DFU> MultiFlashProvider<'a, ACTIVE, STATE, DFU> +/// Convenience flash provider that uses separate flash instances for each partition. +pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> where - ACTIVE: NorFlash + ReadNorFlash, - STATE: NorFlash + ReadNorFlash, - DFU: NorFlash + ReadNorFlash, + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, { + active: &'a mut ACTIVE, + state: &'a mut STATE, + dfu: &'a mut DFU, +} + +impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> +where + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, +{ + /// 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: SingleFlashConfig { flash: active }, - state: SingleFlashConfig { flash: state }, - dfu: SingleFlashConfig { flash: dfu }, - } + Self { active, state, dfu } } } -impl<'a, ACTIVE, STATE, DFU> FlashProvider for MultiFlashProvider<'a, ACTIVE, STATE, DFU> +impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> where - ACTIVE: NorFlash + ReadNorFlash, - STATE: NorFlash + ReadNorFlash, - DFU: NorFlash + ReadNorFlash, + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, { - type STATE = SingleFlashConfig<'a, STATE>; - type ACTIVE = SingleFlashConfig<'a, ACTIVE>; - type DFU = SingleFlashConfig<'a, DFU>; + type STATE = STATE; + type ACTIVE = ACTIVE; + type DFU = DFU; fn active(&mut self) -> &mut Self::ACTIVE { - &mut self.active + self.active } fn dfu(&mut self) -> &mut Self::DFU { - &mut self.dfu + self.dfu } fn state(&mut self) -> &mut Self::STATE { - &mut self.state + self.state } } @@ -518,10 +564,6 @@ pub struct FirmwareUpdater { dfu: Partition, } -// NOTE: Aligned to the largest write size supported by flash -#[repr(align(32))] -pub struct Aligned([u8; N]); - impl Default for FirmwareUpdater { fn default() -> Self { extern "C" { @@ -551,6 +593,7 @@ impl Default for FirmwareUpdater { } 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 } } @@ -560,23 +603,24 @@ impl FirmwareUpdater { self.dfu.len() } - /// Instruct bootloader that DFU should commence at next boot. - /// Must be provided with an aligned buffer to use for reading and writing magic; - pub async fn update(&mut self, flash: &mut F) -> Result<(), F::Error> - where - [(); F::WRITE_SIZE]:, - { - let mut aligned = Aligned([0; { F::WRITE_SIZE }]); - self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await + /// Mark to trigger firmware swap on next boot. + /// + /// # 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_updated(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC, flash).await } - /// Mark firmware boot successfully - pub async fn mark_booted(&mut self, flash: &mut F) -> Result<(), F::Error> - where - [(); F::WRITE_SIZE]:, - { - let mut aligned = Aligned([0; { F::WRITE_SIZE }]); - self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).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, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC, flash).await } async fn set_magic( @@ -587,7 +631,7 @@ impl FirmwareUpdater { ) -> Result<(), F::Error> { flash.read(self.state.from as u32, aligned).await?; - if aligned.iter().find(|&&b| b != magic).is_some() { + if aligned.iter().any(|&b| b != magic) { aligned.fill(0); flash.write(self.state.from as u32, aligned).await?; @@ -599,7 +643,13 @@ impl FirmwareUpdater { Ok(()) } - // Write to a region of the DFU page + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. pub async fn write_firmware( &mut self, offset: usize, @@ -668,7 +718,7 @@ mod tests { #[test] fn test_bad_magic() { let mut flash = MemFlash([0xff; 131072]); - let mut flash = SingleFlashProvider::new(&mut flash); + let mut flash = SingleFlashConfig::new(&mut flash); let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); @@ -687,11 +737,16 @@ mod tests { let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); - let mut flash = SingleFlashProvider::new(&mut flash); + let mut flash = SingleFlashConfig::new(&mut flash); - let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); + let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); - assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); + let mut magic = [0; 4]; + let mut page = [0; 4096]; + assert_eq!( + State::Boot, + bootloader.prepare_boot(&mut flash, &mut magic, &mut page).unwrap() + ); } #[test] @@ -703,24 +758,27 @@ mod tests { let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; + let mut aligned = [0; 4]; for i in ACTIVE.from..ACTIVE.to { flash.0[i] = original[i - ACTIVE.from]; } - let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); + let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); let mut updater = FirmwareUpdater::new(DFU, STATE); let mut offset = 0; for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, &chunk, &mut flash, 4096)).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap(); offset += chunk.len(); } - block_on(updater.update(&mut flash)).unwrap(); + block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); + let mut magic = [0; 4]; + let mut page = [0; 4096]; assert_eq!( State::Swap, bootloader - .prepare_boot(&mut SingleFlashProvider::new(&mut flash)) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) .unwrap() ); @@ -737,7 +795,7 @@ mod tests { assert_eq!( State::Swap, bootloader - .prepare_boot(&mut SingleFlashProvider::new(&mut flash)) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) .unwrap() ); @@ -751,11 +809,11 @@ mod tests { } // Mark as booted - block_on(updater.mark_booted(&mut flash)).unwrap(); + block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap(); assert_eq!( State::Boot, bootloader - .prepare_boot(&mut SingleFlashProvider::new(&mut flash)) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) .unwrap() ); } @@ -769,6 +827,7 @@ mod tests { let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); + let mut aligned = [0; 4]; let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; @@ -781,16 +840,23 @@ mod tests { let mut offset = 0; for chunk in update.chunks(2048) { - block_on(updater.write_firmware(offset, &chunk, &mut dfu, chunk.len())).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); offset += chunk.len(); } - block_on(updater.update(&mut state)).unwrap(); + block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); + + let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); + let mut magic = [0; 4]; + let mut page = [0; 4096]; - let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); assert_eq!( State::Swap, bootloader - .prepare_boot(&mut MultiFlashProvider::new(&mut active, &mut state, &mut dfu,)) + .prepare_boot( + &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), + &mut magic, + &mut page + ) .unwrap() ); @@ -810,6 +876,7 @@ mod tests { const ACTIVE: Partition = Partition::new(4096, 16384); const DFU: Partition = Partition::new(0, 16384); + let mut aligned = [0; 4]; let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); @@ -825,16 +892,22 @@ mod tests { let mut offset = 0; for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, &chunk, &mut dfu, chunk.len())).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); offset += chunk.len(); } - block_on(updater.update(&mut state)).unwrap(); + block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); - let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); + let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); + let mut magic = [0; 4]; + let mut page = [0; 4096]; assert_eq!( State::Swap, bootloader - .prepare_boot(&mut MultiFlashProvider::new(&mut active, &mut state, &mut dfu,)) + .prepare_boot( + &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,), + &mut magic, + &mut page + ) .unwrap() ); @@ -899,6 +972,13 @@ mod tests { } } + impl super::Flash + for MemFlash + { + const BLOCK_SIZE: usize = ERASE_SIZE; + const ERASE_VALUE: u8 = 0xFF; + } + impl AsyncReadNorFlash for MemFlash { diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 2d6b837c..0c14781a 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -1,19 +1,21 @@ #![no_std] #![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] -#![allow(incomplete_features)] -#![feature(generic_const_exprs)] - +#![warn(missing_docs)] +#![doc = include_str!("../../README.md")] mod fmt; -pub use embassy_boot::{FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider}; +pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig}; 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, + boot: embassy_boot::BootLoader, + magic: AlignedBuffer<4>, + page: AlignedBuffer, } impl BootLoader { @@ -58,21 +60,25 @@ impl BootLoader { pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { Self { boot: embassy_boot::BootLoader::new(active, dfu, state), + magic: AlignedBuffer([0; 4]), + page: AlignedBuffer([0; PAGE_SIZE]), } } - /// Boots the application without softdevice mechanisms - pub fn prepare(&mut self, flash: &mut F) -> usize - where - [(); <::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - [(); <::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:, - { - match self.boot.prepare_boot(flash) { + /// 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.magic.0, &mut self.page.0) { Ok(_) => self.boot.boot_address(), Err(_) => panic!("boot prepare error!"), } } + /// Boots the application without softdevice mechanisms. + /// + /// # Safety + /// + /// 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) -> ! { let mut p = cortex_m::Peripherals::steal(); @@ -81,6 +87,11 @@ impl BootLoader { cortex_m::asm::bootload(start as *const u32) } + /// Boots the application assuming softdevice is present. + /// + /// # Safety + /// + /// 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) -> ! { use nrf_softdevice_mbr as mbr; diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 5a4f2d05..39f08051 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -1,19 +1,20 @@ #![no_std] #![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] -#![allow(incomplete_features)] -#![feature(generic_const_exprs)] - +#![warn(missing_docs)] +#![doc = include_str!("../../README.md")] mod fmt; -pub use embassy_boot::{FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider, State}; -use embedded_storage::nor_flash::NorFlash; +pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; -pub struct BootLoader { - boot: embassy_boot::BootLoader, +/// A bootloader for STM32 devices. +pub struct BootLoader { + boot: embassy_boot::BootLoader, + magic: AlignedBuffer, + page: AlignedBuffer, } -impl BootLoader { +impl BootLoader { /// Create a new bootloader instance using parameters from linker script pub fn default() -> Self { extern "C" { @@ -55,21 +56,25 @@ impl BootLoader { pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { Self { boot: embassy_boot::BootLoader::new(active, dfu, state), + magic: AlignedBuffer([0; WRITE_SIZE]), + page: AlignedBuffer([0; PAGE_SIZE]), } } - /// Boots the application - pub fn prepare(&mut self, flash: &mut F) -> usize - where - [(); <::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - [(); <::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:, - { - match self.boot.prepare_boot(flash) { + /// 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.magic.as_mut(), self.page.as_mut()) { Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(), Err(_) => panic!("boot prepare error!"), } } + /// Boots the application. + /// + /// # 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) -> ! { trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index bd8fa324..133a3e67 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -36,7 +36,8 @@ async fn main(_spawner: Spawner) { updater.write_firmware(offset, &buf, &mut nvmc, 4096).await.unwrap(); offset += chunk.len(); } - updater.update(&mut nvmc).await.unwrap(); + let mut magic = [0; 4]; + updater.mark_updated(&mut nvmc, &mut magic).await.unwrap(); led.set_high(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 11eecc5e..fdbd5ab9 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use panic_reset as _; @@ -35,7 +35,8 @@ async fn main(_spawner: Spawner) { updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, 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 a3b66e7c..c08880fb 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use panic_reset as _; @@ -35,7 +35,8 @@ async fn main(_spawner: Spawner) { updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 5669527f..7a76a8db 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -4,7 +4,7 @@ name = "embassy-boot-stm32h7-examples" version = "0.1.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32h7/flash-boot.sh b/examples/boot/application/stm32h7/flash-boot.sh index debdb17a..a3003681 100755 --- a/examples/boot/application/stm32h7/flash-boot.sh +++ b/examples/boot/application/stm32h7/flash-boot.sh @@ -1,8 +1,9 @@ #!/bin/bash +probe-rs-cli erase --chip STM32H743ZITx mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x cp memory-bl.x ../../bootloader/stm32/memory.x -cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32F767ZITx --target thumbv7em-none-eabihf +cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32h743zi --chip STM32H743ZITx --target thumbv7em-none-eabihf rm ../../bootloader/stm32/memory.x mv ../../bootloader/stm32/memory-old.x ../../bootloader/stm32/memory.x diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 0ecf6034..f5a8fdb6 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use panic_reset as _; @@ -29,13 +29,17 @@ async fn main(_spawner: Spawner) { let mut updater = FirmwareUpdater::default(); button.wait_for_rising_edge().await; let mut offset = 0; - let mut buf: [u8; 128 * 1024] = [0; 128 * 1024]; + let mut buf = AlignedBuffer([0; 128 * 1024]); for chunk in APP_B.chunks(128 * 1024) { - buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); + buf.as_mut()[..chunk.len()].copy_from_slice(chunk); + updater + .write_firmware(offset, buf.as_ref(), &mut flash, 2048) + .await + .unwrap(); offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, magic.as_mut()).await.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 f4f1d711..f0b0b80e 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_time::{Duration, Timer}; use panic_reset as _; @@ -38,7 +38,8 @@ async fn main(_spawner: Spawner) { offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, 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/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index f4f1d711..f0b0b80e 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_time::{Duration, Timer}; use panic_reset as _; @@ -38,7 +38,8 @@ async fn main(_spawner: Spawner) { offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, 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 178b2e04..5119bad2 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use panic_reset as _; @@ -35,7 +35,8 @@ async fn main(_spawner: Spawner) { updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, magic.as_mut()).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 c71a4265..faa65077 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use panic_reset as _; @@ -37,7 +37,8 @@ async fn main(_spawner: Spawner) { updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, 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/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index bc7e0755..9031997c 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -20,10 +20,8 @@ fn main() -> ! { */ let mut bl = BootLoader::default(); - let start = bl.prepare(&mut SingleFlashProvider::new(&mut WatchdogFlash::start( - Nvmc::new(p.NVMC), - p.WDT, - 5, + let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( + &mut WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), ))); unsafe { bl.load(start) } } diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 45c511ce..bb5d3e53 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -5,7 +5,7 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, ERASE_SIZE}; +use embassy_stm32::flash::{Flash, ERASE_SIZE, ERASE_VALUE, WRITE_SIZE}; #[entry] fn main() -> ! { @@ -19,9 +19,11 @@ fn main() -> ! { } */ - let mut bl: BootLoader = BootLoader::default(); + let mut bl: BootLoader = BootLoader::default(); let mut flash = Flash::unlock(p.FLASH); - let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash)); + let start = bl.prepare(&mut SingleFlashConfig::new( + &mut BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(&mut flash), + )); core::mem::drop(flash); unsafe { bl.load(start) } }