diff options
| author | Ulf Lilleengen <[email protected]> | 2024-09-19 07:24:33 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-09-19 07:24:33 +0000 |
| commit | cdb44f1272e9264324cc866ee2d141301e968e10 (patch) | |
| tree | 3c453373e1c443121ee4e18e3d2c482e41c3b971 | |
| parent | 45cbcb513dc0bbf3e12c102df0db8c15643cc78b (diff) | |
| parent | 4e1efd93fd4dc8dd692daf419d901ae22413e091 (diff) | |
Merge pull request #3349 from embassy-rs/e-b-introduce-reverted-magic
Add revert state in embassy-boot
| -rw-r--r-- | embassy-boot/src/boot_loader.rs | 6 | ||||
| -rw-r--r-- | embassy-boot/src/firmware_updater/asynch.rs | 10 | ||||
| -rw-r--r-- | embassy-boot/src/firmware_updater/blocking.rs | 12 | ||||
| -rw-r--r-- | embassy-boot/src/lib.rs | 24 | ||||
| -rw-r--r-- | examples/boot/application/nrf/build.rs | 3 | ||||
| -rw-r--r-- | examples/boot/application/nrf/src/bin/a.rs | 11 |
6 files changed, 48 insertions, 18 deletions
diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs index 61d61b96e..5bffdc5ea 100644 --- a/embassy-boot/src/boot_loader.rs +++ b/embassy-boot/src/boot_loader.rs | |||
| @@ -5,7 +5,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; | |||
| 5 | use embassy_sync::blocking_mutex::Mutex; | 5 | use embassy_sync::blocking_mutex::Mutex; |
| 6 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; | 6 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; |
| 7 | 7 | ||
| 8 | use crate::{State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; | 8 | use crate::{State, DFU_DETACH_MAGIC, REVERT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; |
| 9 | 9 | ||
| 10 | /// Errors returned by bootloader | 10 | /// Errors returned by bootloader |
| 11 | #[derive(PartialEq, Eq, Debug)] | 11 | #[derive(PartialEq, Eq, Debug)] |
| @@ -276,7 +276,7 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S | |||
| 276 | self.state.erase(0, self.state.capacity() as u32)?; | 276 | self.state.erase(0, self.state.capacity() as u32)?; |
| 277 | 277 | ||
| 278 | // Set magic | 278 | // Set magic |
| 279 | state_word.fill(BOOT_MAGIC); | 279 | state_word.fill(REVERT_MAGIC); |
| 280 | self.state.write(0, state_word)?; | 280 | self.state.write(0, state_word)?; |
| 281 | } | 281 | } |
| 282 | } | 282 | } |
| @@ -411,6 +411,8 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S | |||
| 411 | Ok(State::Swap) | 411 | Ok(State::Swap) |
| 412 | } else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) { | 412 | } else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) { |
| 413 | Ok(State::DfuDetach) | 413 | Ok(State::DfuDetach) |
| 414 | } else if !state_word.iter().any(|&b| b != REVERT_MAGIC) { | ||
| 415 | Ok(State::Revert) | ||
| 414 | } else { | 416 | } else { |
| 415 | Ok(State::Boot) | 417 | Ok(State::Boot) |
| 416 | } | 418 | } |
diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index 86b441592..d9d15b004 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs | |||
| @@ -289,7 +289,8 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | |||
| 289 | 289 | ||
| 290 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | 290 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 291 | async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 291 | async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 292 | if self.get_state().await? == State::Boot { | 292 | let state = self.get_state().await?; |
| 293 | if state == State::Boot || state == State::DfuDetach || state == State::Revert { | ||
| 293 | Ok(()) | 294 | Ok(()) |
| 294 | } else { | 295 | } else { |
| 295 | Err(FirmwareUpdaterError::BadState) | 296 | Err(FirmwareUpdaterError::BadState) |
| @@ -303,12 +304,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | |||
| 303 | /// `mark_booted`. | 304 | /// `mark_booted`. |
| 304 | pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { | 305 | pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { |
| 305 | self.state.read(0, &mut self.aligned).await?; | 306 | self.state.read(0, &mut self.aligned).await?; |
| 306 | 307 | Ok(State::from(&self.aligned)) | |
| 307 | if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { | ||
| 308 | Ok(State::Swap) | ||
| 309 | } else { | ||
| 310 | Ok(State::Boot) | ||
| 311 | } | ||
| 312 | } | 308 | } |
| 313 | 309 | ||
| 314 | /// Mark to trigger firmware swap on next boot. | 310 | /// Mark to trigger firmware swap on next boot. |
diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index d3c723456..08062b0d0 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs | |||
| @@ -324,7 +324,8 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 324 | 324 | ||
| 325 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | 325 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 326 | fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 326 | fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 327 | if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach { | 327 | let state = self.get_state()?; |
| 328 | if state == State::Boot || state == State::DfuDetach || state == State::Revert { | ||
| 328 | Ok(()) | 329 | Ok(()) |
| 329 | } else { | 330 | } else { |
| 330 | Err(FirmwareUpdaterError::BadState) | 331 | Err(FirmwareUpdaterError::BadState) |
| @@ -338,14 +339,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 338 | /// `mark_booted`. | 339 | /// `mark_booted`. |
| 339 | pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { | 340 | pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { |
| 340 | self.state.read(0, &mut self.aligned)?; | 341 | self.state.read(0, &mut self.aligned)?; |
| 341 | 342 | Ok(State::from(&self.aligned)) | |
| 342 | if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { | ||
| 343 | Ok(State::Swap) | ||
| 344 | } else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) { | ||
| 345 | Ok(State::DfuDetach) | ||
| 346 | } else { | ||
| 347 | Ok(State::Boot) | ||
| 348 | } | ||
| 349 | } | 343 | } |
| 350 | 344 | ||
| 351 | /// Mark to trigger firmware swap on next boot. | 345 | /// Mark to trigger firmware swap on next boot. |
diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs index 8849055e8..e2c4cf771 100644 --- a/embassy-boot/src/lib.rs +++ b/embassy-boot/src/lib.rs | |||
| @@ -25,6 +25,7 @@ pub use firmware_updater::{ | |||
| 25 | FirmwareUpdaterError, | 25 | FirmwareUpdaterError, |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | pub(crate) const REVERT_MAGIC: u8 = 0xC0; | ||
| 28 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; | 29 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; |
| 29 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; | 30 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; |
| 30 | pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0; | 31 | pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0; |
| @@ -37,10 +38,30 @@ pub enum State { | |||
| 37 | Boot, | 38 | Boot, |
| 38 | /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. | 39 | /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. |
| 39 | Swap, | 40 | Swap, |
| 41 | /// Bootloader has reverted the active partition with the dfu partition and will attempt boot. | ||
| 42 | Revert, | ||
| 40 | /// Application has received a request to reboot into DFU mode to apply an update. | 43 | /// Application has received a request to reboot into DFU mode to apply an update. |
| 41 | DfuDetach, | 44 | DfuDetach, |
| 42 | } | 45 | } |
| 43 | 46 | ||
| 47 | impl<T> From<T> for State | ||
| 48 | where | ||
| 49 | T: AsRef<[u8]>, | ||
| 50 | { | ||
| 51 | fn from(magic: T) -> State { | ||
| 52 | let magic = magic.as_ref(); | ||
| 53 | if !magic.iter().any(|&b| b != SWAP_MAGIC) { | ||
| 54 | State::Swap | ||
| 55 | } else if !magic.iter().any(|&b| b != REVERT_MAGIC) { | ||
| 56 | State::Revert | ||
| 57 | } else if !magic.iter().any(|&b| b != DFU_DETACH_MAGIC) { | ||
| 58 | State::DfuDetach | ||
| 59 | } else { | ||
| 60 | State::Boot | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 44 | /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. | 65 | /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. |
| 45 | #[repr(align(32))] | 66 | #[repr(align(32))] |
| 46 | pub struct AlignedBuffer<const N: usize>(pub [u8; N]); | 67 | pub struct AlignedBuffer<const N: usize>(pub [u8; N]); |
| @@ -157,6 +178,9 @@ mod tests { | |||
| 157 | // Running again should cause a revert | 178 | // Running again should cause a revert |
| 158 | assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); | 179 | assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); |
| 159 | 180 | ||
| 181 | // Next time we know it was reverted | ||
| 182 | assert_eq!(State::Revert, bootloader.prepare_boot(&mut page).unwrap()); | ||
| 183 | |||
| 160 | let mut read_buf = [0; FIRMWARE_SIZE]; | 184 | let mut read_buf = [0; FIRMWARE_SIZE]; |
| 161 | flash.active().read(0, &mut read_buf).unwrap(); | 185 | flash.active().read(0, &mut read_buf).unwrap(); |
| 162 | assert_eq!(ORIGINAL, read_buf); | 186 | assert_eq!(ORIGINAL, read_buf); |
diff --git a/examples/boot/application/nrf/build.rs b/examples/boot/application/nrf/build.rs index cd1a264c4..e1da69328 100644 --- a/examples/boot/application/nrf/build.rs +++ b/examples/boot/application/nrf/build.rs | |||
| @@ -31,4 +31,7 @@ fn main() { | |||
| 31 | 31 | ||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); |
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); |
| 34 | if env::var("CARGO_FEATURE_DEFMT").is_ok() { | ||
| 35 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 36 | } | ||
| 34 | } | 37 | } |
diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 851a3d721..2c1d1a7bb 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs | |||
| @@ -2,6 +2,9 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![macro_use] | 3 | #![macro_use] |
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt")] | ||
| 6 | use defmt_rtt as _; | ||
| 7 | use embassy_boot::State; | ||
| 5 | use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; | 8 | use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; |
| 6 | use embassy_embedded_hal::adapter::BlockingAsync; | 9 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 7 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| @@ -22,6 +25,7 @@ async fn main(_spawner: Spawner) { | |||
| 22 | 25 | ||
| 23 | let mut button = Input::new(p.P0_11, Pull::Up); | 26 | let mut button = Input::new(p.P0_11, Pull::Up); |
| 24 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); | 27 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); |
| 28 | let mut led_reverted = Output::new(p.P0_14, Level::High, OutputDrive::Standard); | ||
| 25 | 29 | ||
| 26 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); | 30 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); |
| 27 | //let mut button = Input::new(p.P1_02, Pull::Up); | 31 | //let mut button = Input::new(p.P1_02, Pull::Up); |
| @@ -53,6 +57,13 @@ async fn main(_spawner: Spawner) { | |||
| 53 | let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc); | 57 | let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc); |
| 54 | let mut magic = [0; 4]; | 58 | let mut magic = [0; 4]; |
| 55 | let mut updater = FirmwareUpdater::new(config, &mut magic); | 59 | let mut updater = FirmwareUpdater::new(config, &mut magic); |
| 60 | let state = updater.get_state().await.unwrap(); | ||
| 61 | if state == State::Revert { | ||
| 62 | led_reverted.set_low(); | ||
| 63 | } else { | ||
| 64 | led_reverted.set_high(); | ||
| 65 | } | ||
| 66 | |||
| 56 | loop { | 67 | loop { |
| 57 | led.set_low(); | 68 | led.set_low(); |
| 58 | button.wait_for_any_edge().await; | 69 | button.wait_for_any_edge().await; |
