diff options
Diffstat (limited to 'embassy-boot/src')
| -rw-r--r-- | embassy-boot/src/boot_loader.rs | 6 | ||||
| -rw-r--r-- | embassy-boot/src/firmware_updater/asynch.rs | 33 | ||||
| -rw-r--r-- | embassy-boot/src/firmware_updater/blocking.rs | 35 | ||||
| -rw-r--r-- | embassy-boot/src/firmware_updater/mod.rs | 2 | ||||
| -rw-r--r-- | embassy-boot/src/lib.rs | 28 |
5 files changed, 77 insertions, 27 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 26f65f295..66e311e38 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs | |||
| @@ -107,7 +107,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 107 | let mut message = [0; 64]; | 107 | let mut message = [0; 64]; |
| 108 | self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message).await?; | 108 | self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message).await?; |
| 109 | 109 | ||
| 110 | public_key.verify(&message, &signature).map_err(into_signature_error)? | 110 | public_key.verify(&message, &signature).map_err(into_signature_error)?; |
| 111 | return self.state.mark_updated().await; | ||
| 111 | } | 112 | } |
| 112 | #[cfg(feature = "ed25519-salty")] | 113 | #[cfg(feature = "ed25519-salty")] |
| 113 | { | 114 | { |
| @@ -134,10 +135,13 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 134 | message, | 135 | message, |
| 135 | r.is_ok() | 136 | r.is_ok() |
| 136 | ); | 137 | ); |
| 137 | r.map_err(into_signature_error)? | 138 | r.map_err(into_signature_error)?; |
| 139 | return self.state.mark_updated().await; | ||
| 140 | } | ||
| 141 | #[cfg(not(any(feature = "ed25519-dalek", feature = "ed25519-salty")))] | ||
| 142 | { | ||
| 143 | Err(FirmwareUpdaterError::Signature(signature::Error::new())) | ||
| 138 | } | 144 | } |
| 139 | |||
| 140 | self.state.mark_updated().await | ||
| 141 | } | 145 | } |
| 142 | 146 | ||
| 143 | /// Verify the update in DFU with any digest. | 147 | /// Verify the update in DFU with any digest. |
| @@ -157,6 +161,17 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 157 | Ok(()) | 161 | Ok(()) |
| 158 | } | 162 | } |
| 159 | 163 | ||
| 164 | /// Read a slice of data from the DFU storage peripheral, starting the read | ||
| 165 | /// operation at the given address offset, and reading `buf.len()` bytes. | ||
| 166 | /// | ||
| 167 | /// # Errors | ||
| 168 | /// | ||
| 169 | /// Returns an error if the arguments are not aligned or out of bounds. | ||
| 170 | pub async fn read_dfu(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), FirmwareUpdaterError> { | ||
| 171 | self.dfu.read(offset, buf).await?; | ||
| 172 | Ok(()) | ||
| 173 | } | ||
| 174 | |||
| 160 | /// Mark to trigger firmware swap on next boot. | 175 | /// Mark to trigger firmware swap on next boot. |
| 161 | #[cfg(not(feature = "_verify"))] | 176 | #[cfg(not(feature = "_verify"))] |
| 162 | pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { | 177 | pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { |
| @@ -285,7 +300,8 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | |||
| 285 | 300 | ||
| 286 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | 301 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 287 | async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 302 | async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 288 | if self.get_state().await? == State::Boot { | 303 | let state = self.get_state().await?; |
| 304 | if state == State::Boot || state == State::DfuDetach || state == State::Revert { | ||
| 289 | Ok(()) | 305 | Ok(()) |
| 290 | } else { | 306 | } else { |
| 291 | Err(FirmwareUpdaterError::BadState) | 307 | Err(FirmwareUpdaterError::BadState) |
| @@ -299,12 +315,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | |||
| 299 | /// `mark_booted`. | 315 | /// `mark_booted`. |
| 300 | pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { | 316 | pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { |
| 301 | self.state.read(0, &mut self.aligned).await?; | 317 | self.state.read(0, &mut self.aligned).await?; |
| 302 | 318 | Ok(State::from(&self.aligned[..STATE::WRITE_SIZE])) | |
| 303 | if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { | ||
| 304 | Ok(State::Swap) | ||
| 305 | } else { | ||
| 306 | Ok(State::Boot) | ||
| 307 | } | ||
| 308 | } | 319 | } |
| 309 | 320 | ||
| 310 | /// Mark to trigger firmware swap on next boot. | 321 | /// 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 35772a856..0fedac1ea 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs | |||
| @@ -142,7 +142,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 142 | let mut chunk_buf = [0; 2]; | 142 | let mut chunk_buf = [0; 2]; |
| 143 | self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message)?; | 143 | self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message)?; |
| 144 | 144 | ||
| 145 | public_key.verify(&message, &signature).map_err(into_signature_error)? | 145 | public_key.verify(&message, &signature).map_err(into_signature_error)?; |
| 146 | return self.state.mark_updated(); | ||
| 146 | } | 147 | } |
| 147 | #[cfg(feature = "ed25519-salty")] | 148 | #[cfg(feature = "ed25519-salty")] |
| 148 | { | 149 | { |
| @@ -169,10 +170,13 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 169 | message, | 170 | message, |
| 170 | r.is_ok() | 171 | r.is_ok() |
| 171 | ); | 172 | ); |
| 172 | r.map_err(into_signature_error)? | 173 | r.map_err(into_signature_error)?; |
| 174 | return self.state.mark_updated(); | ||
| 175 | } | ||
| 176 | #[cfg(not(any(feature = "ed25519-dalek", feature = "ed25519-salty")))] | ||
| 177 | { | ||
| 178 | Err(FirmwareUpdaterError::Signature(signature::Error::new())) | ||
| 173 | } | 179 | } |
| 174 | |||
| 175 | self.state.mark_updated() | ||
| 176 | } | 180 | } |
| 177 | 181 | ||
| 178 | /// Verify the update in DFU with any digest. | 182 | /// Verify the update in DFU with any digest. |
| @@ -192,6 +196,17 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 192 | Ok(()) | 196 | Ok(()) |
| 193 | } | 197 | } |
| 194 | 198 | ||
| 199 | /// Read a slice of data from the DFU storage peripheral, starting the read | ||
| 200 | /// operation at the given address offset, and reading `buf.len()` bytes. | ||
| 201 | /// | ||
| 202 | /// # Errors | ||
| 203 | /// | ||
| 204 | /// Returns an error if the arguments are not aligned or out of bounds. | ||
| 205 | pub fn read_dfu(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), FirmwareUpdaterError> { | ||
| 206 | self.dfu.read(offset, buf)?; | ||
| 207 | Ok(()) | ||
| 208 | } | ||
| 209 | |||
| 195 | /// Mark to trigger firmware swap on next boot. | 210 | /// Mark to trigger firmware swap on next boot. |
| 196 | #[cfg(not(feature = "_verify"))] | 211 | #[cfg(not(feature = "_verify"))] |
| 197 | pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { | 212 | pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { |
| @@ -320,7 +335,8 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 320 | 335 | ||
| 321 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | 336 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 322 | fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 337 | fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 323 | if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach { | 338 | let state = self.get_state()?; |
| 339 | if state == State::Boot || state == State::DfuDetach || state == State::Revert { | ||
| 324 | Ok(()) | 340 | Ok(()) |
| 325 | } else { | 341 | } else { |
| 326 | Err(FirmwareUpdaterError::BadState) | 342 | Err(FirmwareUpdaterError::BadState) |
| @@ -334,14 +350,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 334 | /// `mark_booted`. | 350 | /// `mark_booted`. |
| 335 | pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { | 351 | pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { |
| 336 | self.state.read(0, &mut self.aligned)?; | 352 | self.state.read(0, &mut self.aligned)?; |
| 337 | 353 | Ok(State::from(&self.aligned)) | |
| 338 | if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { | ||
| 339 | Ok(State::Swap) | ||
| 340 | } else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) { | ||
| 341 | Ok(State::DfuDetach) | ||
| 342 | } else { | ||
| 343 | Ok(State::Boot) | ||
| 344 | } | ||
| 345 | } | 354 | } |
| 346 | 355 | ||
| 347 | /// Mark to trigger firmware swap on next boot. | 356 | /// Mark to trigger firmware swap on next boot. |
diff --git a/embassy-boot/src/firmware_updater/mod.rs b/embassy-boot/src/firmware_updater/mod.rs index 4c4f4f10b..4814786bf 100644 --- a/embassy-boot/src/firmware_updater/mod.rs +++ b/embassy-boot/src/firmware_updater/mod.rs | |||
| @@ -8,7 +8,7 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; | |||
| 8 | /// Firmware updater flash configuration holding the two flashes used by the updater | 8 | /// Firmware updater flash configuration holding the two flashes used by the updater |
| 9 | /// | 9 | /// |
| 10 | /// If only a single flash is actually used, then that flash should be partitioned into two partitions before use. | 10 | /// If only a single flash is actually used, then that flash should be partitioned into two partitions before use. |
| 11 | /// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile_blocking`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition | 11 | /// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition |
| 12 | /// the provided flash according to symbols defined in the linkerfile. | 12 | /// the provided flash according to symbols defined in the linkerfile. |
| 13 | pub struct FirmwareUpdaterConfig<DFU, STATE> { | 13 | pub struct FirmwareUpdaterConfig<DFU, STATE> { |
| 14 | /// The dfu flash partition | 14 | /// The dfu flash partition |
diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs index b4f03e01e..e2c4cf771 100644 --- a/embassy-boot/src/lib.rs +++ b/embassy-boot/src/lib.rs | |||
| @@ -14,13 +14,18 @@ mod test_flash; | |||
| 14 | 14 | ||
| 15 | // The expected value of the flash after an erase | 15 | // The expected value of the flash after an erase |
| 16 | // TODO: Use the value provided by NorFlash when available | 16 | // TODO: Use the value provided by NorFlash when available |
| 17 | #[cfg(not(feature = "flash-erase-zero"))] | ||
| 17 | pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF; | 18 | pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF; |
| 19 | #[cfg(feature = "flash-erase-zero")] | ||
| 20 | pub(crate) const STATE_ERASE_VALUE: u8 = 0x00; | ||
| 21 | |||
| 18 | pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; | 22 | pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; |
| 19 | pub use firmware_updater::{ | 23 | pub use firmware_updater::{ |
| 20 | BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareState, FirmwareUpdater, FirmwareUpdaterConfig, | 24 | BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareState, FirmwareUpdater, FirmwareUpdaterConfig, |
| 21 | FirmwareUpdaterError, | 25 | FirmwareUpdaterError, |
| 22 | }; | 26 | }; |
| 23 | 27 | ||
| 28 | pub(crate) const REVERT_MAGIC: u8 = 0xC0; | ||
| 24 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; | 29 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; |
| 25 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; | 30 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; |
| 26 | pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0; | 31 | pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0; |
| @@ -33,10 +38,30 @@ pub enum State { | |||
| 33 | Boot, | 38 | Boot, |
| 34 | /// 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. |
| 35 | Swap, | 40 | Swap, |
| 41 | /// Bootloader has reverted the active partition with the dfu partition and will attempt boot. | ||
| 42 | Revert, | ||
| 36 | /// 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. |
| 37 | DfuDetach, | 44 | DfuDetach, |
| 38 | } | 45 | } |
| 39 | 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 | |||
| 40 | /// 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. |
| 41 | #[repr(align(32))] | 66 | #[repr(align(32))] |
| 42 | pub struct AlignedBuffer<const N: usize>(pub [u8; N]); | 67 | pub struct AlignedBuffer<const N: usize>(pub [u8; N]); |
| @@ -153,6 +178,9 @@ mod tests { | |||
| 153 | // Running again should cause a revert | 178 | // Running again should cause a revert |
| 154 | assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); | 179 | assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); |
| 155 | 180 | ||
| 181 | // Next time we know it was reverted | ||
| 182 | assert_eq!(State::Revert, bootloader.prepare_boot(&mut page).unwrap()); | ||
| 183 | |||
| 156 | let mut read_buf = [0; FIRMWARE_SIZE]; | 184 | let mut read_buf = [0; FIRMWARE_SIZE]; |
| 157 | flash.active().read(0, &mut read_buf).unwrap(); | 185 | flash.active().read(0, &mut read_buf).unwrap(); |
| 158 | assert_eq!(ORIGINAL, read_buf); | 186 | assert_eq!(ORIGINAL, read_buf); |
