aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot
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 /embassy-boot
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.
Diffstat (limited to 'embassy-boot')
-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();