aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2023-06-19 22:37:23 +0200
committerUlf Lilleengen <[email protected]>2023-06-19 22:39:00 +0200
commit76659d9003104f8edd2472a36149565e4a55c0e6 (patch)
tree9bbfca44bc301294c2021f6d18fd765b96382656
parent3c70f799a28f5f28d84fa8ee8b4b232f5e9aad82 (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.
-rw-r--r--embassy-boot/boot/src/firmware_updater/asynch.rs34
-rw-r--r--embassy-boot/boot/src/firmware_updater/blocking.rs32
-rw-r--r--embassy-boot/boot/src/firmware_updater/mod.rs3
-rw-r--r--embassy-boot/boot/src/lib.rs12
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)]
53mod tests { 53mod 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();