aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot/boot/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-boot/boot/src')
-rw-r--r--embassy-boot/boot/src/boot_loader.rs42
-rw-r--r--embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs4
-rw-r--r--embassy-boot/boot/src/firmware_updater/asynch.rs40
-rw-r--r--embassy-boot/boot/src/firmware_updater/blocking.rs44
-rw-r--r--embassy-boot/boot/src/lib.rs11
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;
5use embassy_sync::blocking_mutex::Mutex; 5use embassy_sync::blocking_mutex::Mutex;
6use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; 6use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
7 7
8use crate::{State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; 8use 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 @@
1use digest::typenum::U64; 1use digest::typenum::U64;
2use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; 2use digest::{FixedOutput, HashMarker, OutputSizeUser, Update};
3use ed25519_dalek::Digest as _; 3use ed25519_dalek::Digest;
4 4
5pub struct Sha512(ed25519_dalek::Sha512); 5pub struct Sha512(ed25519_dalek::Sha512);
6 6
@@ -12,7 +12,7 @@ impl Default for Sha512 {
12 12
13impl Update for Sha512 { 13impl 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;
6use embedded_storage_async::nor_flash::NorFlash; 6use embedded_storage_async::nor_flash::NorFlash;
7 7
8use super::FirmwareUpdaterConfig; 8use super::FirmwareUpdaterConfig;
9use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; 9use 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
209impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { 212impl<'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;
6use embedded_storage::nor_flash::NorFlash; 6use embedded_storage::nor_flash::NorFlash;
7 7
8use super::FirmwareUpdaterConfig; 8use super::FirmwareUpdaterConfig;
9use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; 9use 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
215impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { 218impl<'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
24pub(crate) const BOOT_MAGIC: u8 = 0xD0; 24pub(crate) const BOOT_MAGIC: u8 = 0xD0;
25pub(crate) const SWAP_MAGIC: u8 = 0xF0; 25pub(crate) const SWAP_MAGIC: u8 = 0xF0;
26pub(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 {