diff options
Diffstat (limited to 'embassy-boot/boot/src')
| -rw-r--r-- | embassy-boot/boot/src/boot_loader.rs | 42 | ||||
| -rw-r--r-- | embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs | 4 | ||||
| -rw-r--r-- | embassy-boot/boot/src/firmware_updater/asynch.rs | 40 | ||||
| -rw-r--r-- | embassy-boot/boot/src/firmware_updater/blocking.rs | 44 | ||||
| -rw-r--r-- | embassy-boot/boot/src/lib.rs | 11 |
5 files changed, 85 insertions, 56 deletions
diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index a8c19197b..e568001bc 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/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, STATE_ERASE_VALUE, SWAP_MAGIC}; | 8 | use crate::{State, BOOT_MAGIC, DFU_DETACH_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)] |
| @@ -135,51 +135,44 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S | |||
| 135 | /// The provided aligned_buf argument must satisfy any alignment requirements | 135 | /// The provided aligned_buf argument must satisfy any alignment requirements |
| 136 | /// given by the partition flashes. All flash operations will use this buffer. | 136 | /// given by the partition flashes. All flash operations will use this buffer. |
| 137 | /// | 137 | /// |
| 138 | /// SWAPPING | 138 | /// ## SWAPPING |
| 139 | /// | 139 | /// |
| 140 | /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition. | 140 | /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition. |
| 141 | /// The swap index contains the copy progress, as to allow continuation of the copy process on | 141 | /// The swap index contains the copy progress, as to allow continuation of the copy process on |
| 142 | /// power failure. The index counter is represented within 1 or more pages (depending on total | 142 | /// power failure. The index counter is represented within 1 or more pages (depending on total |
| 143 | /// flash size), where a page X is considered swapped if index at location (X + WRITE_SIZE) | 143 | /// flash size), where a page X is considered swapped if index at location (`X + WRITE_SIZE`) |
| 144 | /// contains a zero value. This ensures that index updates can be performed atomically and | 144 | /// contains a zero value. This ensures that index updates can be performed atomically and |
| 145 | /// avoid a situation where the wrong index value is set (page write size is "atomic"). | 145 | /// avoid a situation where the wrong index value is set (page write size is "atomic"). |
| 146 | /// | 146 | /// |
| 147 | /// +-----------+------------+--------+--------+--------+--------+ | 147 | /// |
| 148 | /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | | 148 | /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | |
| 149 | /// +-----------+------------+--------+--------+--------+--------+ | 149 | /// |-----------|------------|--------|--------|--------|--------| |
| 150 | /// | Active | 0 | 1 | 2 | 3 | - | | 150 | /// | Active | 0 | 1 | 2 | 3 | - | |
| 151 | /// | DFU | 0 | 3 | 2 | 1 | X | | 151 | /// | DFU | 0 | 3 | 2 | 1 | X | |
| 152 | /// +-----------+------------+--------+--------+--------+--------+ | ||
| 153 | /// | 152 | /// |
| 154 | /// The algorithm starts by copying 'backwards', and after the first step, the layout is | 153 | /// The algorithm starts by copying 'backwards', and after the first step, the layout is |
| 155 | /// as follows: | 154 | /// as follows: |
| 156 | /// | 155 | /// |
| 157 | /// +-----------+------------+--------+--------+--------+--------+ | ||
| 158 | /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | | 156 | /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | |
| 159 | /// +-----------+------------+--------+--------+--------+--------+ | 157 | /// |-----------|------------|--------|--------|--------|--------| |
| 160 | /// | Active | 1 | 1 | 2 | 1 | - | | 158 | /// | Active | 1 | 1 | 2 | 1 | - | |
| 161 | /// | DFU | 1 | 3 | 2 | 1 | 3 | | 159 | /// | DFU | 1 | 3 | 2 | 1 | 3 | |
| 162 | /// +-----------+------------+--------+--------+--------+--------+ | ||
| 163 | /// | 160 | /// |
| 164 | /// The next iteration performs the same steps | 161 | /// The next iteration performs the same steps |
| 165 | /// | 162 | /// |
| 166 | /// +-----------+------------+--------+--------+--------+--------+ | ||
| 167 | /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | | 163 | /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | |
| 168 | /// +-----------+------------+--------+--------+--------+--------+ | 164 | /// |-----------|------------|--------|--------|--------|--------| |
| 169 | /// | Active | 2 | 1 | 2 | 1 | - | | 165 | /// | Active | 2 | 1 | 2 | 1 | - | |
| 170 | /// | DFU | 2 | 3 | 2 | 2 | 3 | | 166 | /// | DFU | 2 | 3 | 2 | 2 | 3 | |
| 171 | /// +-----------+------------+--------+--------+--------+--------+ | ||
| 172 | /// | 167 | /// |
| 173 | /// And again until we're done | 168 | /// And again until we're done |
| 174 | /// | 169 | /// |
| 175 | /// +-----------+------------+--------+--------+--------+--------+ | ||
| 176 | /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | | 170 | /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | |
| 177 | /// +-----------+------------+--------+--------+--------+--------+ | 171 | /// |-----------|------------|--------|--------|--------|--------| |
| 178 | /// | Active | 3 | 3 | 2 | 1 | - | | 172 | /// | Active | 3 | 3 | 2 | 1 | - | |
| 179 | /// | DFU | 3 | 3 | 1 | 2 | 3 | | 173 | /// | DFU | 3 | 3 | 1 | 2 | 3 | |
| 180 | /// +-----------+------------+--------+--------+--------+--------+ | ||
| 181 | /// | 174 | /// |
| 182 | /// REVERTING | 175 | /// ## REVERTING |
| 183 | /// | 176 | /// |
| 184 | /// The reverting algorithm uses the swap index to discover that images were swapped, but that | 177 | /// The reverting algorithm uses the swap index to discover that images were swapped, but that |
| 185 | /// the application failed to mark the boot successful. In this case, the revert algorithm will | 178 | /// the application failed to mark the boot successful. In this case, the revert algorithm will |
| @@ -190,28 +183,21 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S | |||
| 190 | /// | 183 | /// |
| 191 | /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start. | 184 | /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start. |
| 192 | /// | 185 | /// |
| 193 | /// +-----------+--------------+--------+--------+--------+--------+ | ||
| 194 | /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | | 186 | /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | |
| 195 | //*/ | 187 | /// |-----------|--------------|--------|--------|--------|--------| |
| 196 | /// +-----------+--------------+--------+--------+--------+--------+ | ||
| 197 | /// | Active | 3 | 1 | 2 | 1 | - | | 188 | /// | Active | 3 | 1 | 2 | 1 | - | |
| 198 | /// | DFU | 3 | 3 | 1 | 2 | 3 | | 189 | /// | DFU | 3 | 3 | 1 | 2 | 3 | |
| 199 | /// +-----------+--------------+--------+--------+--------+--------+ | ||
| 200 | /// | 190 | /// |
| 201 | /// | 191 | /// |
| 202 | /// +-----------+--------------+--------+--------+--------+--------+ | ||
| 203 | /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | | 192 | /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | |
| 204 | /// +-----------+--------------+--------+--------+--------+--------+ | 193 | /// |-----------|--------------|--------|--------|--------|--------| |
| 205 | /// | Active | 3 | 1 | 2 | 1 | - | | 194 | /// | Active | 3 | 1 | 2 | 1 | - | |
| 206 | /// | DFU | 3 | 3 | 2 | 2 | 3 | | 195 | /// | DFU | 3 | 3 | 2 | 2 | 3 | |
| 207 | /// +-----------+--------------+--------+--------+--------+--------+ | ||
| 208 | /// | 196 | /// |
| 209 | /// +-----------+--------------+--------+--------+--------+--------+ | ||
| 210 | /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | | 197 | /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | |
| 211 | /// +-----------+--------------+--------+--------+--------+--------+ | 198 | /// |-----------|--------------|--------|--------|--------|--------| |
| 212 | /// | Active | 3 | 1 | 2 | 3 | - | | 199 | /// | Active | 3 | 1 | 2 | 3 | - | |
| 213 | /// | DFU | 3 | 3 | 2 | 1 | 3 | | 200 | /// | DFU | 3 | 3 | 2 | 1 | 3 | |
| 214 | /// +-----------+--------------+--------+--------+--------+--------+ | ||
| 215 | /// | 201 | /// |
| 216 | pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> { | 202 | pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> { |
| 217 | // Ensure we have enough progress pages to store copy progress | 203 | // Ensure we have enough progress pages to store copy progress |
| @@ -224,6 +210,7 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S | |||
| 224 | assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE); | 210 | assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE); |
| 225 | assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE); | 211 | assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE); |
| 226 | 212 | ||
| 213 | // Ensure our partitions are able to handle boot operations | ||
| 227 | assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE); | 214 | assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE); |
| 228 | 215 | ||
| 229 | // Copy contents from partition N to active | 216 | // Copy contents from partition N to active |
| @@ -384,6 +371,8 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S | |||
| 384 | 371 | ||
| 385 | if !state_word.iter().any(|&b| b != SWAP_MAGIC) { | 372 | if !state_word.iter().any(|&b| b != SWAP_MAGIC) { |
| 386 | Ok(State::Swap) | 373 | Ok(State::Swap) |
| 374 | } else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) { | ||
| 375 | Ok(State::DfuDetach) | ||
| 387 | } else { | 376 | } else { |
| 388 | Ok(State::Boot) | 377 | Ok(State::Boot) |
| 389 | } | 378 | } |
| @@ -398,6 +387,7 @@ fn assert_partitions<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>( | |||
| 398 | ) { | 387 | ) { |
| 399 | assert_eq!(active.capacity() as u32 % page_size, 0); | 388 | assert_eq!(active.capacity() as u32 % page_size, 0); |
| 400 | assert_eq!(dfu.capacity() as u32 % page_size, 0); | 389 | assert_eq!(dfu.capacity() as u32 % page_size, 0); |
| 390 | // DFU partition has to be bigger than ACTIVE partition to handle swap algorithm | ||
| 401 | assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); | 391 | assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); |
| 402 | assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); | 392 | assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); |
| 403 | } | 393 | } |
diff --git a/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs index a184d1c51..2e4e03da3 100644 --- a/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs +++ b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | use digest::typenum::U64; | 1 | use digest::typenum::U64; |
| 2 | use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; | 2 | use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; |
| 3 | use ed25519_dalek::Digest as _; | 3 | use ed25519_dalek::Digest; |
| 4 | 4 | ||
| 5 | pub struct Sha512(ed25519_dalek::Sha512); | 5 | pub struct Sha512(ed25519_dalek::Sha512); |
| 6 | 6 | ||
| @@ -12,7 +12,7 @@ impl Default for Sha512 { | |||
| 12 | 12 | ||
| 13 | impl Update for Sha512 { | 13 | impl Update for Sha512 { |
| 14 | fn update(&mut self, data: &[u8]) { | 14 | fn update(&mut self, data: &[u8]) { |
| 15 | self.0.update(data) | 15 | Digest::update(&mut self.0, data) |
| 16 | } | 16 | } |
| 17 | } | 17 | } |
| 18 | 18 | ||
diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index ae713bb6f..64a4b32ec 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs | |||
| @@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; | |||
| 6 | use embedded_storage_async::nor_flash::NorFlash; | 6 | use embedded_storage_async::nor_flash::NorFlash; |
| 7 | 7 | ||
| 8 | use super::FirmwareUpdaterConfig; | 8 | use super::FirmwareUpdaterConfig; |
| 9 | use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; | 9 | use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; |
| 10 | 10 | ||
| 11 | /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to | 11 | /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to |
| 12 | /// 'mess up' the internal bootloader state | 12 | /// 'mess up' the internal bootloader state |
| @@ -79,8 +79,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 79 | #[cfg(feature = "_verify")] | 79 | #[cfg(feature = "_verify")] |
| 80 | pub async fn verify_and_mark_updated( | 80 | pub async fn verify_and_mark_updated( |
| 81 | &mut self, | 81 | &mut self, |
| 82 | _public_key: &[u8], | 82 | _public_key: &[u8; 32], |
| 83 | _signature: &[u8], | 83 | _signature: &[u8; 64], |
| 84 | _update_len: u32, | 84 | _update_len: u32, |
| 85 | ) -> Result<(), FirmwareUpdaterError> { | 85 | ) -> Result<(), FirmwareUpdaterError> { |
| 86 | assert!(_update_len <= self.dfu.capacity() as u32); | 86 | assert!(_update_len <= self.dfu.capacity() as u32); |
| @@ -89,14 +89,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 89 | 89 | ||
| 90 | #[cfg(feature = "ed25519-dalek")] | 90 | #[cfg(feature = "ed25519-dalek")] |
| 91 | { | 91 | { |
| 92 | use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; | 92 | use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey}; |
| 93 | 93 | ||
| 94 | use crate::digest_adapters::ed25519_dalek::Sha512; | 94 | use crate::digest_adapters::ed25519_dalek::Sha512; |
| 95 | 95 | ||
| 96 | let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); | 96 | let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); |
| 97 | 97 | ||
| 98 | let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; | 98 | let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?; |
| 99 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | 99 | let signature = Signature::from_bytes(_signature); |
| 100 | 100 | ||
| 101 | let mut chunk_buf = [0; 2]; | 101 | let mut chunk_buf = [0; 2]; |
| 102 | let mut message = [0; 64]; | 102 | let mut message = [0; 64]; |
| @@ -106,7 +106,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 106 | } | 106 | } |
| 107 | #[cfg(feature = "ed25519-salty")] | 107 | #[cfg(feature = "ed25519-salty")] |
| 108 | { | 108 | { |
| 109 | use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; | ||
| 110 | use salty::{PublicKey, Signature}; | 109 | use salty::{PublicKey, Signature}; |
| 111 | 110 | ||
| 112 | use crate::digest_adapters::salty::Sha512; | 111 | use crate::digest_adapters::salty::Sha512; |
| @@ -115,10 +114,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 115 | FirmwareUpdaterError::Signature(signature::Error::default()) | 114 | FirmwareUpdaterError::Signature(signature::Error::default()) |
| 116 | } | 115 | } |
| 117 | 116 | ||
| 118 | let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; | 117 | let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?; |
| 119 | let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; | 118 | let signature = Signature::try_from(_signature).map_err(into_signature_error)?; |
| 120 | let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; | ||
| 121 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | ||
| 122 | 119 | ||
| 123 | let mut message = [0; 64]; | 120 | let mut message = [0; 64]; |
| 124 | let mut chunk_buf = [0; 2]; | 121 | let mut chunk_buf = [0; 2]; |
| @@ -161,6 +158,12 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 161 | self.state.mark_updated().await | 158 | self.state.mark_updated().await |
| 162 | } | 159 | } |
| 163 | 160 | ||
| 161 | /// Mark to trigger USB DFU on next boot. | ||
| 162 | pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { | ||
| 163 | self.state.verify_booted().await?; | ||
| 164 | self.state.mark_dfu().await | ||
| 165 | } | ||
| 166 | |||
| 164 | /// Mark firmware boot successful and stop rollback on reset. | 167 | /// Mark firmware boot successful and stop rollback on reset. |
| 165 | pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 168 | pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 166 | self.state.mark_booted().await | 169 | self.state.mark_booted().await |
| @@ -207,6 +210,16 @@ pub struct FirmwareState<'d, STATE> { | |||
| 207 | } | 210 | } |
| 208 | 211 | ||
| 209 | impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | 212 | impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { |
| 213 | /// Create a firmware state instance from a FirmwareUpdaterConfig with a buffer for magic content and state partition. | ||
| 214 | /// | ||
| 215 | /// # Safety | ||
| 216 | /// | ||
| 217 | /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from | ||
| 218 | /// and written to. | ||
| 219 | pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self { | ||
| 220 | Self::new(config.state, aligned) | ||
| 221 | } | ||
| 222 | |||
| 210 | /// Create a firmware state instance with a buffer for magic content and state partition. | 223 | /// Create a firmware state instance with a buffer for magic content and state partition. |
| 211 | /// | 224 | /// |
| 212 | /// # Safety | 225 | /// # Safety |
| @@ -247,6 +260,11 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | |||
| 247 | self.set_magic(SWAP_MAGIC).await | 260 | self.set_magic(SWAP_MAGIC).await |
| 248 | } | 261 | } |
| 249 | 262 | ||
| 263 | /// Mark to trigger USB DFU on next boot. | ||
| 264 | pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { | ||
| 265 | self.set_magic(DFU_DETACH_MAGIC).await | ||
| 266 | } | ||
| 267 | |||
| 250 | /// Mark firmware boot successful and stop rollback on reset. | 268 | /// Mark firmware boot successful and stop rollback on reset. |
| 251 | pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 269 | pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 252 | self.set_magic(BOOT_MAGIC).await | 270 | self.set_magic(BOOT_MAGIC).await |
diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs index 76e4264a0..f1368540d 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs | |||
| @@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; | |||
| 6 | use embedded_storage::nor_flash::NorFlash; | 6 | use embedded_storage::nor_flash::NorFlash; |
| 7 | 7 | ||
| 8 | use super::FirmwareUpdaterConfig; | 8 | use super::FirmwareUpdaterConfig; |
| 9 | use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; | 9 | use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; |
| 10 | 10 | ||
| 11 | /// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to | 11 | /// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to |
| 12 | /// 'mess up' the internal bootloader state | 12 | /// 'mess up' the internal bootloader state |
| @@ -86,8 +86,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 86 | #[cfg(feature = "_verify")] | 86 | #[cfg(feature = "_verify")] |
| 87 | pub fn verify_and_mark_updated( | 87 | pub fn verify_and_mark_updated( |
| 88 | &mut self, | 88 | &mut self, |
| 89 | _public_key: &[u8], | 89 | _public_key: &[u8; 32], |
| 90 | _signature: &[u8], | 90 | _signature: &[u8; 64], |
| 91 | _update_len: u32, | 91 | _update_len: u32, |
| 92 | ) -> Result<(), FirmwareUpdaterError> { | 92 | ) -> Result<(), FirmwareUpdaterError> { |
| 93 | assert!(_update_len <= self.dfu.capacity() as u32); | 93 | assert!(_update_len <= self.dfu.capacity() as u32); |
| @@ -96,14 +96,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 96 | 96 | ||
| 97 | #[cfg(feature = "ed25519-dalek")] | 97 | #[cfg(feature = "ed25519-dalek")] |
| 98 | { | 98 | { |
| 99 | use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; | 99 | use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey}; |
| 100 | 100 | ||
| 101 | use crate::digest_adapters::ed25519_dalek::Sha512; | 101 | use crate::digest_adapters::ed25519_dalek::Sha512; |
| 102 | 102 | ||
| 103 | let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); | 103 | let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); |
| 104 | 104 | ||
| 105 | let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; | 105 | let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?; |
| 106 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | 106 | let signature = Signature::from_bytes(_signature); |
| 107 | 107 | ||
| 108 | let mut message = [0; 64]; | 108 | let mut message = [0; 64]; |
| 109 | let mut chunk_buf = [0; 2]; | 109 | let mut chunk_buf = [0; 2]; |
| @@ -113,7 +113,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 113 | } | 113 | } |
| 114 | #[cfg(feature = "ed25519-salty")] | 114 | #[cfg(feature = "ed25519-salty")] |
| 115 | { | 115 | { |
| 116 | use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; | ||
| 117 | use salty::{PublicKey, Signature}; | 116 | use salty::{PublicKey, Signature}; |
| 118 | 117 | ||
| 119 | use crate::digest_adapters::salty::Sha512; | 118 | use crate::digest_adapters::salty::Sha512; |
| @@ -122,10 +121,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 122 | FirmwareUpdaterError::Signature(signature::Error::default()) | 121 | FirmwareUpdaterError::Signature(signature::Error::default()) |
| 123 | } | 122 | } |
| 124 | 123 | ||
| 125 | let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; | 124 | let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?; |
| 126 | let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; | 125 | let signature = Signature::try_from(_signature).map_err(into_signature_error)?; |
| 127 | let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; | ||
| 128 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | ||
| 129 | 126 | ||
| 130 | let mut message = [0; 64]; | 127 | let mut message = [0; 64]; |
| 131 | let mut chunk_buf = [0; 2]; | 128 | let mut chunk_buf = [0; 2]; |
| @@ -168,6 +165,12 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 168 | self.state.mark_updated() | 165 | self.state.mark_updated() |
| 169 | } | 166 | } |
| 170 | 167 | ||
| 168 | /// Mark to trigger USB DFU device on next boot. | ||
| 169 | pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { | ||
| 170 | self.state.verify_booted()?; | ||
| 171 | self.state.mark_dfu() | ||
| 172 | } | ||
| 173 | |||
| 171 | /// Mark firmware boot successful and stop rollback on reset. | 174 | /// Mark firmware boot successful and stop rollback on reset. |
| 172 | pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 175 | pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 173 | self.state.mark_booted() | 176 | self.state.mark_booted() |
| @@ -213,6 +216,16 @@ pub struct BlockingFirmwareState<'d, STATE> { | |||
| 213 | } | 216 | } |
| 214 | 217 | ||
| 215 | impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | 218 | impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { |
| 219 | /// Creates a firmware state instance from a FirmwareUpdaterConfig, with a buffer for magic content and state partition. | ||
| 220 | /// | ||
| 221 | /// # Safety | ||
| 222 | /// | ||
| 223 | /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from | ||
| 224 | /// and written to. | ||
| 225 | pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self { | ||
| 226 | Self::new(config.state, aligned) | ||
| 227 | } | ||
| 228 | |||
| 216 | /// Create a firmware state instance with a buffer for magic content and state partition. | 229 | /// Create a firmware state instance with a buffer for magic content and state partition. |
| 217 | /// | 230 | /// |
| 218 | /// # Safety | 231 | /// # Safety |
| @@ -226,7 +239,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 226 | 239 | ||
| 227 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | 240 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 228 | fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 241 | fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 229 | if self.get_state()? == State::Boot { | 242 | if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach { |
| 230 | Ok(()) | 243 | Ok(()) |
| 231 | } else { | 244 | } else { |
| 232 | Err(FirmwareUpdaterError::BadState) | 245 | Err(FirmwareUpdaterError::BadState) |
| @@ -243,6 +256,8 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 243 | 256 | ||
| 244 | if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { | 257 | if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { |
| 245 | Ok(State::Swap) | 258 | Ok(State::Swap) |
| 259 | } else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) { | ||
| 260 | Ok(State::DfuDetach) | ||
| 246 | } else { | 261 | } else { |
| 247 | Ok(State::Boot) | 262 | Ok(State::Boot) |
| 248 | } | 263 | } |
| @@ -253,6 +268,11 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 253 | self.set_magic(SWAP_MAGIC) | 268 | self.set_magic(SWAP_MAGIC) |
| 254 | } | 269 | } |
| 255 | 270 | ||
| 271 | /// Mark to trigger USB DFU on next boot. | ||
| 272 | pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { | ||
| 273 | self.set_magic(DFU_DETACH_MAGIC) | ||
| 274 | } | ||
| 275 | |||
| 256 | /// Mark firmware boot successful and stop rollback on reset. | 276 | /// Mark firmware boot successful and stop rollback on reset. |
| 257 | pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 277 | pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 258 | self.set_magic(BOOT_MAGIC) | 278 | self.set_magic(BOOT_MAGIC) |
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 9e70a4dca..b4f03e01e 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs | |||
| @@ -23,6 +23,7 @@ pub use firmware_updater::{ | |||
| 23 | 23 | ||
| 24 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; | 24 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; |
| 25 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; | 25 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; |
| 26 | pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0; | ||
| 26 | 27 | ||
| 27 | /// The state of the bootloader after running prepare. | 28 | /// The state of the bootloader after running prepare. |
| 28 | #[derive(PartialEq, Eq, Debug)] | 29 | #[derive(PartialEq, Eq, Debug)] |
| @@ -32,6 +33,8 @@ pub enum State { | |||
| 32 | Boot, | 33 | Boot, |
| 33 | /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. | 34 | /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. |
| 34 | Swap, | 35 | Swap, |
| 36 | /// Application has received a request to reboot into DFU mode to apply an update. | ||
| 37 | DfuDetach, | ||
| 35 | } | 38 | } |
| 36 | 39 | ||
| 37 | /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. | 40 | /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. |
| @@ -272,21 +275,19 @@ mod tests { | |||
| 272 | // The following key setup is based on: | 275 | // The following key setup is based on: |
| 273 | // https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example | 276 | // https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example |
| 274 | 277 | ||
| 275 | use ed25519_dalek::Keypair; | 278 | use ed25519_dalek::{Digest, Sha512, Signature, Signer, SigningKey, VerifyingKey}; |
| 276 | use rand::rngs::OsRng; | 279 | use rand::rngs::OsRng; |
| 277 | 280 | ||
| 278 | let mut csprng = OsRng {}; | 281 | let mut csprng = OsRng {}; |
| 279 | let keypair: Keypair = Keypair::generate(&mut csprng); | 282 | let keypair = SigningKey::generate(&mut csprng); |
| 280 | 283 | ||
| 281 | use ed25519_dalek::{Digest, Sha512, Signature, Signer}; | ||
| 282 | let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU."; | 284 | let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU."; |
| 283 | let mut digest = Sha512::new(); | 285 | let mut digest = Sha512::new(); |
| 284 | digest.update(&firmware); | 286 | digest.update(&firmware); |
| 285 | let message = digest.finalize(); | 287 | let message = digest.finalize(); |
| 286 | let signature: Signature = keypair.sign(&message); | 288 | let signature: Signature = keypair.sign(&message); |
| 287 | 289 | ||
| 288 | use ed25519_dalek::PublicKey; | 290 | let public_key = keypair.verifying_key(); |
| 289 | let public_key: PublicKey = keypair.public; | ||
| 290 | 291 | ||
| 291 | // Setup flash | 292 | // Setup flash |
| 292 | let flash = BlockingTestFlash::new(BootLoaderConfig { | 293 | let flash = BlockingTestFlash::new(BootLoaderConfig { |
