diff options
| author | Ulf Lilleengen <[email protected]> | 2023-06-19 22:37:23 +0200 |
|---|---|---|
| committer | Ulf Lilleengen <[email protected]> | 2023-06-19 22:39:00 +0200 |
| commit | 76659d9003104f8edd2472a36149565e4a55c0e6 (patch) | |
| tree | 9bbfca44bc301294c2021f6d18fd765b96382656 /embassy-boot | |
| parent | 3c70f799a28f5f28d84fa8ee8b4b232f5e9aad82 (diff) | |
Prevent accidental revert when using firmware updater
This change prevents accidentally overwriting the previous firmware before
the new one has been marked as booted.
Diffstat (limited to 'embassy-boot')
| -rw-r--r-- | embassy-boot/boot/src/firmware_updater/asynch.rs | 34 | ||||
| -rw-r--r-- | embassy-boot/boot/src/firmware_updater/blocking.rs | 32 | ||||
| -rw-r--r-- | embassy-boot/boot/src/firmware_updater/mod.rs | 3 | ||||
| -rw-r--r-- | embassy-boot/boot/src/lib.rs | 12 |
4 files changed, 72 insertions, 9 deletions
diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index 0b3f88313..20731ee0a 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs | |||
| @@ -56,6 +56,16 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> { | |||
| 56 | } | 56 | } |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | ||
| 60 | async fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { | ||
| 61 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); | ||
| 62 | if self.get_state(aligned).await? == State::Boot { | ||
| 63 | Ok(()) | ||
| 64 | } else { | ||
| 65 | Err(FirmwareUpdaterError::BadState) | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 59 | /// Obtain the current state. | 69 | /// Obtain the current state. |
| 60 | /// | 70 | /// |
| 61 | /// This is useful to check if the bootloader has just done a swap, in order | 71 | /// This is useful to check if the bootloader has just done a swap, in order |
| @@ -98,6 +108,8 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> { | |||
| 98 | assert_eq!(_aligned.len(), STATE::WRITE_SIZE); | 108 | assert_eq!(_aligned.len(), STATE::WRITE_SIZE); |
| 99 | assert!(_update_len <= self.dfu.capacity() as u32); | 109 | assert!(_update_len <= self.dfu.capacity() as u32); |
| 100 | 110 | ||
| 111 | self.verify_booted(_aligned).await?; | ||
| 112 | |||
| 101 | #[cfg(feature = "ed25519-dalek")] | 113 | #[cfg(feature = "ed25519-dalek")] |
| 102 | { | 114 | { |
| 103 | use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; | 115 | use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; |
| @@ -217,8 +229,16 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> { | |||
| 217 | /// # Safety | 229 | /// # Safety |
| 218 | /// | 230 | /// |
| 219 | /// Failing to meet alignment and size requirements may result in a panic. | 231 | /// Failing to meet alignment and size requirements may result in a panic. |
| 220 | pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { | 232 | pub async fn write_firmware( |
| 233 | &mut self, | ||
| 234 | aligned: &mut [u8], | ||
| 235 | offset: usize, | ||
| 236 | data: &[u8], | ||
| 237 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 221 | assert!(data.len() >= DFU::ERASE_SIZE); | 238 | assert!(data.len() >= DFU::ERASE_SIZE); |
| 239 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); | ||
| 240 | |||
| 241 | self.verify_booted(aligned).await?; | ||
| 222 | 242 | ||
| 223 | self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; | 243 | self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; |
| 224 | 244 | ||
| @@ -232,7 +252,14 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> { | |||
| 232 | /// | 252 | /// |
| 233 | /// Using this instead of `write_firmware` allows for an optimized API in | 253 | /// Using this instead of `write_firmware` allows for an optimized API in |
| 234 | /// exchange for added complexity. | 254 | /// exchange for added complexity. |
| 235 | pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { | 255 | /// |
| 256 | /// # Safety | ||
| 257 | /// | ||
| 258 | /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. | ||
| 259 | pub async fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> { | ||
| 260 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); | ||
| 261 | self.verify_booted(aligned).await?; | ||
| 262 | |||
| 236 | self.dfu.erase(0, self.dfu.capacity() as u32).await?; | 263 | self.dfu.erase(0, self.dfu.capacity() as u32).await?; |
| 237 | 264 | ||
| 238 | Ok(&mut self.dfu) | 265 | Ok(&mut self.dfu) |
| @@ -255,13 +282,14 @@ mod tests { | |||
| 255 | let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default()); | 282 | let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default()); |
| 256 | let state = Partition::new(&flash, 0, 4096); | 283 | let state = Partition::new(&flash, 0, 4096); |
| 257 | let dfu = Partition::new(&flash, 65536, 65536); | 284 | let dfu = Partition::new(&flash, 65536, 65536); |
| 285 | let mut aligned = [0; 8]; | ||
| 258 | 286 | ||
| 259 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | 287 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; |
| 260 | let mut to_write = [0; 4096]; | 288 | let mut to_write = [0; 4096]; |
| 261 | to_write[..7].copy_from_slice(update.as_slice()); | 289 | to_write[..7].copy_from_slice(update.as_slice()); |
| 262 | 290 | ||
| 263 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); | 291 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); |
| 264 | block_on(updater.write_firmware(0, to_write.as_slice())).unwrap(); | 292 | block_on(updater.write_firmware(&mut aligned, 0, to_write.as_slice())).unwrap(); |
| 265 | let mut chunk_buf = [0; 2]; | 293 | let mut chunk_buf = [0; 2]; |
| 266 | let mut hash = [0; 20]; | 294 | let mut hash = [0; 20]; |
| 267 | block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); | 295 | block_on(updater.hash::<Sha1>(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 551150c4f..f03f53e4d 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs | |||
| @@ -58,6 +58,16 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> { | |||
| 58 | } | 58 | } |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | ||
| 62 | fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { | ||
| 63 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); | ||
| 64 | if self.get_state(aligned)? == State::Boot { | ||
| 65 | Ok(()) | ||
| 66 | } else { | ||
| 67 | Err(FirmwareUpdaterError::BadState) | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 61 | /// Obtain the current state. | 71 | /// Obtain the current state. |
| 62 | /// | 72 | /// |
| 63 | /// This is useful to check if the bootloader has just done a swap, in order | 73 | /// This is useful to check if the bootloader has just done a swap, in order |
| @@ -100,6 +110,8 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> { | |||
| 100 | assert_eq!(_aligned.len(), STATE::WRITE_SIZE); | 110 | assert_eq!(_aligned.len(), STATE::WRITE_SIZE); |
| 101 | assert!(_update_len <= self.dfu.capacity() as u32); | 111 | assert!(_update_len <= self.dfu.capacity() as u32); |
| 102 | 112 | ||
| 113 | self.verify_booted(_aligned)?; | ||
| 114 | |||
| 103 | #[cfg(feature = "ed25519-dalek")] | 115 | #[cfg(feature = "ed25519-dalek")] |
| 104 | { | 116 | { |
| 105 | use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; | 117 | use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; |
| @@ -219,8 +231,15 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> { | |||
| 219 | /// # Safety | 231 | /// # Safety |
| 220 | /// | 232 | /// |
| 221 | /// Failing to meet alignment and size requirements may result in a panic. | 233 | /// Failing to meet alignment and size requirements may result in a panic. |
| 222 | pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { | 234 | pub fn write_firmware( |
| 235 | &mut self, | ||
| 236 | aligned: &mut [u8], | ||
| 237 | offset: usize, | ||
| 238 | data: &[u8], | ||
| 239 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 223 | assert!(data.len() >= DFU::ERASE_SIZE); | 240 | assert!(data.len() >= DFU::ERASE_SIZE); |
| 241 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); | ||
| 242 | self.verify_booted(aligned)?; | ||
| 224 | 243 | ||
| 225 | self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; | 244 | self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; |
| 226 | 245 | ||
| @@ -234,7 +253,13 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> { | |||
| 234 | /// | 253 | /// |
| 235 | /// Using this instead of `write_firmware` allows for an optimized API in | 254 | /// Using this instead of `write_firmware` allows for an optimized API in |
| 236 | /// exchange for added complexity. | 255 | /// exchange for added complexity. |
| 237 | pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { | 256 | /// |
| 257 | /// # Safety | ||
| 258 | /// | ||
| 259 | /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. | ||
| 260 | pub fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> { | ||
| 261 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); | ||
| 262 | self.verify_booted(aligned)?; | ||
| 238 | self.dfu.erase(0, self.dfu.capacity() as u32)?; | 263 | self.dfu.erase(0, self.dfu.capacity() as u32)?; |
| 239 | 264 | ||
| 240 | Ok(&mut self.dfu) | 265 | Ok(&mut self.dfu) |
| @@ -264,7 +289,8 @@ mod tests { | |||
| 264 | to_write[..7].copy_from_slice(update.as_slice()); | 289 | to_write[..7].copy_from_slice(update.as_slice()); |
| 265 | 290 | ||
| 266 | let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); | 291 | let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); |
| 267 | updater.write_firmware(0, to_write.as_slice()).unwrap(); | 292 | let mut aligned = [0; 8]; |
| 293 | updater.write_firmware(&mut aligned, 0, to_write.as_slice()).unwrap(); | ||
| 268 | let mut chunk_buf = [0; 2]; | 294 | let mut chunk_buf = [0; 2]; |
| 269 | let mut hash = [0; 20]; | 295 | let mut hash = [0; 20]; |
| 270 | updater | 296 | updater |
diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs index a37984a3a..55ce8f363 100644 --- a/embassy-boot/boot/src/firmware_updater/mod.rs +++ b/embassy-boot/boot/src/firmware_updater/mod.rs | |||
| @@ -26,6 +26,8 @@ pub enum FirmwareUpdaterError { | |||
| 26 | Flash(NorFlashErrorKind), | 26 | Flash(NorFlashErrorKind), |
| 27 | /// Signature errors. | 27 | /// Signature errors. |
| 28 | Signature(signature::Error), | 28 | Signature(signature::Error), |
| 29 | /// Bad state. | ||
| 30 | BadState, | ||
| 29 | } | 31 | } |
| 30 | 32 | ||
| 31 | #[cfg(feature = "defmt")] | 33 | #[cfg(feature = "defmt")] |
| @@ -34,6 +36,7 @@ impl defmt::Format for FirmwareUpdaterError { | |||
| 34 | match self { | 36 | match self { |
| 35 | FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), | 37 | FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), |
| 36 | FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), | 38 | FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), |
| 39 | FirmwareUpdaterError::BadState => defmt::write!(fmt, "FirmwareUpdaterError::BadState"), | ||
| 37 | } | 40 | } |
| 38 | } | 41 | } |
| 39 | } | 42 | } |
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 45a87bd0e..016362b86 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs | |||
| @@ -51,6 +51,8 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> { | |||
| 51 | 51 | ||
| 52 | #[cfg(test)] | 52 | #[cfg(test)] |
| 53 | mod tests { | 53 | mod tests { |
| 54 | #![allow(unused_imports)] | ||
| 55 | |||
| 54 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | 56 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; |
| 55 | #[cfg(feature = "nightly")] | 57 | #[cfg(feature = "nightly")] |
| 56 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; | 58 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; |
| @@ -120,9 +122,13 @@ mod tests { | |||
| 120 | dfu: flash.dfu(), | 122 | dfu: flash.dfu(), |
| 121 | state: flash.state(), | 123 | state: flash.state(), |
| 122 | }); | 124 | }); |
| 123 | block_on(updater.write_firmware(0, &UPDATE)).unwrap(); | 125 | block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); |
| 124 | block_on(updater.mark_updated(&mut aligned)).unwrap(); | 126 | block_on(updater.mark_updated(&mut aligned)).unwrap(); |
| 125 | 127 | ||
| 128 | // Writing after marking updated is not allowed until marked as booted. | ||
| 129 | let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)); | ||
| 130 | assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState))); | ||
| 131 | |||
| 126 | let flash = flash.into_blocking(); | 132 | let flash = flash.into_blocking(); |
| 127 | let mut bootloader = BootLoader::new(BootLoaderConfig { | 133 | let mut bootloader = BootLoader::new(BootLoaderConfig { |
| 128 | active: flash.active(), | 134 | active: flash.active(), |
| @@ -188,7 +194,7 @@ mod tests { | |||
| 188 | dfu: flash.dfu(), | 194 | dfu: flash.dfu(), |
| 189 | state: flash.state(), | 195 | state: flash.state(), |
| 190 | }); | 196 | }); |
| 191 | block_on(updater.write_firmware(0, &UPDATE)).unwrap(); | 197 | block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); |
| 192 | block_on(updater.mark_updated(&mut aligned)).unwrap(); | 198 | block_on(updater.mark_updated(&mut aligned)).unwrap(); |
| 193 | 199 | ||
| 194 | let flash = flash.into_blocking(); | 200 | let flash = flash.into_blocking(); |
| @@ -230,7 +236,7 @@ mod tests { | |||
| 230 | dfu: flash.dfu(), | 236 | dfu: flash.dfu(), |
| 231 | state: flash.state(), | 237 | state: flash.state(), |
| 232 | }); | 238 | }); |
| 233 | block_on(updater.write_firmware(0, &UPDATE)).unwrap(); | 239 | block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); |
| 234 | block_on(updater.mark_updated(&mut aligned)).unwrap(); | 240 | block_on(updater.mark_updated(&mut aligned)).unwrap(); |
| 235 | 241 | ||
| 236 | let flash = flash.into_blocking(); | 242 | let flash = flash.into_blocking(); |
