aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRasmus Melchior Jacobsen <[email protected]>2023-05-30 13:36:42 +0200
committerRasmus Melchior Jacobsen <[email protected]>2023-05-30 13:36:42 +0200
commit5205b5b09588d6e0006e5479764ee94b113b03b9 (patch)
tree0397bf901259342755bd21e85a5946e50042ba1c
parent311236e81eb5759966c6d30b5dbfac3f014cceca (diff)
Split FirmwareUpdater into async and blocking types
-rw-r--r--embassy-boot/boot/src/firmware_updater/asynch.rs176
-rw-r--r--embassy-boot/boot/src/firmware_updater/blocking.rs187
-rw-r--r--embassy-boot/boot/src/firmware_updater/mod.rs51
-rw-r--r--embassy-boot/boot/src/lib.rs4
4 files changed, 236 insertions, 182 deletions
diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs
index bdd03bff4..d0780bdf1 100644
--- a/embassy-boot/boot/src/firmware_updater/asynch.rs
+++ b/embassy-boot/boot/src/firmware_updater/asynch.rs
@@ -1,20 +1,68 @@
1use digest::Digest; 1use digest::Digest;
2use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; 2use embassy_embedded_hal::flash::partition::Partition;
3use embassy_sync::blocking_mutex::raw::NoopRawMutex;
4use embedded_storage_async::nor_flash::NorFlash;
5
6use super::FirmwareUpdaterConfig;
7use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
8
9/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
10/// 'mess up' the internal bootloader state
11pub struct FirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
12 dfu: DFU,
13 state: STATE,
14}
15
16impl<'a, FLASH: NorFlash>
17 FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, FLASH>, Partition<'a, NoopRawMutex, FLASH>>
18{
19 /// Create a firmware updater config from the flash and address symbols defined in the linkerfile
20 #[cfg(target_os = "none")]
21 pub fn from_linkerfile(flash: &'a Mutex<NoopRawMutex, FLASH>) -> Self {
22 use embassy_sync::mutex::Mutex;
23
24 extern "C" {
25 static __bootloader_state_start: u32;
26 static __bootloader_state_end: u32;
27 static __bootloader_dfu_start: u32;
28 static __bootloader_dfu_end: u32;
29 }
30
31 let dfu = unsafe {
32 let start = &__bootloader_dfu_start as *const u32 as u32;
33 let end = &__bootloader_dfu_end as *const u32 as u32;
34 trace!("DFU: 0x{:x} - 0x{:x}", start, end);
35
36 Partition::new(flash, start, end - start)
37 };
38 let state = unsafe {
39 let start = &__bootloader_state_start as *const u32 as u32;
40 let end = &__bootloader_state_end as *const u32 as u32;
41 trace!("STATE: 0x{:x} - 0x{:x}", start, end);
42
43 Partition::new(flash, start, end - start)
44 };
3 45
4use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; 46 Self { dfu, state }
47 }
48}
49
50impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
51 /// Create a firmware updater instance with partition ranges for the update and state partitions.
52 pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self {
53 Self {
54 dfu: config.dfu,
55 state: config.state,
56 }
57 }
5 58
6impl FirmwareUpdater {
7 /// Obtain the current state. 59 /// Obtain the current state.
8 /// 60 ///
9 /// This is useful to check if the bootloader has just done a swap, in order 61 /// This is useful to check if the bootloader has just done a swap, in order
10 /// to do verifications and self-tests of the new image before calling 62 /// to do verifications and self-tests of the new image before calling
11 /// `mark_booted`. 63 /// `mark_booted`.
12 pub async fn get_state<F: AsyncNorFlash>( 64 pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
13 &mut self, 65 self.state.read(0, aligned).await?;
14 state_flash: &mut F,
15 aligned: &mut [u8],
16 ) -> Result<State, FirmwareUpdaterError> {
17 self.state.read(state_flash, 0, aligned).await?;
18 66
19 if !aligned.iter().any(|&b| b != SWAP_MAGIC) { 67 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
20 Ok(State::Swap) 68 Ok(State::Swap)
@@ -37,19 +85,18 @@ impl FirmwareUpdater {
37 /// 85 ///
38 /// # Safety 86 /// # Safety
39 /// 87 ///
40 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from 88 /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
41 /// and written to. 89 /// and written to.
42 #[cfg(all(feature = "_verify", feature = "nightly"))] 90 #[cfg(feature = "_verify")]
43 pub async fn verify_and_mark_updated<F: AsyncNorFlash>( 91 pub async fn verify_and_mark_updated(
44 &mut self, 92 &mut self,
45 _state_and_dfu_flash: &mut F,
46 _public_key: &[u8], 93 _public_key: &[u8],
47 _signature: &[u8], 94 _signature: &[u8],
48 _update_len: u32, 95 _update_len: u32,
49 _aligned: &mut [u8], 96 _aligned: &mut [u8],
50 ) -> Result<(), FirmwareUpdaterError> { 97 ) -> Result<(), FirmwareUpdaterError> {
51 assert_eq!(_aligned.len(), F::WRITE_SIZE); 98 assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
52 assert!(_update_len <= self.dfu.size()); 99 assert!(_update_len <= self.dfu.capacity() as u32);
53 100
54 #[cfg(feature = "ed25519-dalek")] 101 #[cfg(feature = "ed25519-dalek")]
55 { 102 {
@@ -63,8 +110,7 @@ impl FirmwareUpdater {
63 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; 110 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
64 111
65 let mut message = [0; 64]; 112 let mut message = [0; 64];
66 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) 113 self.hash::<Sha512>(_update_len, _aligned, &mut message).await?;
67 .await?;
68 114
69 public_key.verify(&message, &signature).map_err(into_signature_error)? 115 public_key.verify(&message, &signature).map_err(into_signature_error)?
70 } 116 }
@@ -85,8 +131,7 @@ impl FirmwareUpdater {
85 let signature = Signature::try_from(&signature).map_err(into_signature_error)?; 131 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
86 132
87 let mut message = [0; 64]; 133 let mut message = [0; 64];
88 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) 134 self.hash::<Sha512>(_update_len, _aligned, &mut message).await?;
89 .await?;
90 135
91 let r = public_key.verify(&message, &signature); 136 let r = public_key.verify(&message, &signature);
92 trace!( 137 trace!(
@@ -99,20 +144,19 @@ impl FirmwareUpdater {
99 r.map_err(into_signature_error)? 144 r.map_err(into_signature_error)?
100 } 145 }
101 146
102 self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await 147 self.set_magic(_aligned, SWAP_MAGIC).await
103 } 148 }
104 149
105 /// Verify the update in DFU with any digest. 150 /// Verify the update in DFU with any digest.
106 pub async fn hash<F: AsyncNorFlash, D: Digest>( 151 pub async fn hash<D: Digest>(
107 &mut self, 152 &mut self,
108 dfu_flash: &mut F,
109 update_len: u32, 153 update_len: u32,
110 chunk_buf: &mut [u8], 154 chunk_buf: &mut [u8],
111 output: &mut [u8], 155 output: &mut [u8],
112 ) -> Result<(), FirmwareUpdaterError> { 156 ) -> Result<(), FirmwareUpdaterError> {
113 let mut digest = D::new(); 157 let mut digest = D::new();
114 for offset in (0..update_len).step_by(chunk_buf.len()) { 158 for offset in (0..update_len).step_by(chunk_buf.len()) {
115 self.dfu.read(dfu_flash, offset, chunk_buf).await?; 159 self.dfu.read(offset, chunk_buf).await?;
116 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); 160 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
117 digest.update(&chunk_buf[..len]); 161 digest.update(&chunk_buf[..len]);
118 } 162 }
@@ -124,60 +168,44 @@ impl FirmwareUpdater {
124 /// 168 ///
125 /// # Safety 169 /// # Safety
126 /// 170 ///
127 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. 171 /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
128 #[cfg(all(feature = "nightly", not(feature = "_verify")))] 172 #[cfg(not(feature = "_verify"))]
129 pub async fn mark_updated<F: AsyncNorFlash>( 173 pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
130 &mut self, 174 assert_eq!(aligned.len(), STATE::WRITE_SIZE);
131 state_flash: &mut F, 175 self.set_magic(aligned, SWAP_MAGIC).await
132 aligned: &mut [u8],
133 ) -> Result<(), FirmwareUpdaterError> {
134 assert_eq!(aligned.len(), F::WRITE_SIZE);
135 self.set_magic(aligned, SWAP_MAGIC, state_flash).await
136 } 176 }
137 177
138 /// Mark firmware boot successful and stop rollback on reset. 178 /// Mark firmware boot successful and stop rollback on reset.
139 /// 179 ///
140 /// # Safety 180 /// # Safety
141 /// 181 ///
142 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. 182 /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
143 pub async fn mark_booted<F: AsyncNorFlash>( 183 pub async fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
144 &mut self, 184 assert_eq!(aligned.len(), STATE::WRITE_SIZE);
145 state_flash: &mut F, 185 self.set_magic(aligned, BOOT_MAGIC).await
146 aligned: &mut [u8],
147 ) -> Result<(), FirmwareUpdaterError> {
148 assert_eq!(aligned.len(), F::WRITE_SIZE);
149 self.set_magic(aligned, BOOT_MAGIC, state_flash).await
150 } 186 }
151 187
152 async fn set_magic<F: AsyncNorFlash>( 188 async fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> {
153 &mut self, 189 self.state.read(0, aligned).await?;
154 aligned: &mut [u8],
155 magic: u8,
156 state_flash: &mut F,
157 ) -> Result<(), FirmwareUpdaterError> {
158 self.state.read(state_flash, 0, aligned).await?;
159 190
160 if aligned.iter().any(|&b| b != magic) { 191 if aligned.iter().any(|&b| b != magic) {
161 // Read progress validity 192 // Read progress validity
162 self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; 193 self.state.read(STATE::WRITE_SIZE as u32, aligned).await?;
163
164 // FIXME: Do not make this assumption.
165 const STATE_ERASE_VALUE: u8 = 0xFF;
166 194
167 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { 195 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
168 // The current progress validity marker is invalid 196 // The current progress validity marker is invalid
169 } else { 197 } else {
170 // Invalidate progress 198 // Invalidate progress
171 aligned.fill(!STATE_ERASE_VALUE); 199 aligned.fill(!STATE_ERASE_VALUE);
172 self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; 200 self.state.write(STATE::WRITE_SIZE as u32, aligned).await?;
173 } 201 }
174 202
175 // Clear magic and progress 203 // Clear magic and progress
176 self.state.wipe(state_flash).await?; 204 self.state.erase(0, self.state.capacity() as u32).await?;
177 205
178 // Set magic 206 // Set magic
179 aligned.fill(magic); 207 aligned.fill(magic);
180 self.state.write(state_flash, 0, aligned).await?; 208 self.state.write(0, aligned).await?;
181 } 209 }
182 Ok(()) 210 Ok(())
183 } 211 }
@@ -189,19 +217,12 @@ impl FirmwareUpdater {
189 /// # Safety 217 /// # Safety
190 /// 218 ///
191 /// Failing to meet alignment and size requirements may result in a panic. 219 /// Failing to meet alignment and size requirements may result in a panic.
192 pub async fn write_firmware<F: AsyncNorFlash>( 220 pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
193 &mut self, 221 assert!(data.len() >= DFU::ERASE_SIZE);
194 offset: usize,
195 data: &[u8],
196 dfu_flash: &mut F,
197 ) -> Result<(), FirmwareUpdaterError> {
198 assert!(data.len() >= F::ERASE_SIZE);
199 222
200 self.dfu 223 self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?;
201 .erase(dfu_flash, offset as u32, (offset + data.len()) as u32)
202 .await?;
203 224
204 self.dfu.write(dfu_flash, offset as u32, data).await?; 225 self.dfu.write(offset as u32, data).await?;
205 226
206 Ok(()) 227 Ok(())
207 } 228 }
@@ -211,18 +232,18 @@ impl FirmwareUpdater {
211 /// 232 ///
212 /// Using this instead of `write_firmware` allows for an optimized API in 233 /// Using this instead of `write_firmware` allows for an optimized API in
213 /// exchange for added complexity. 234 /// exchange for added complexity.
214 pub async fn prepare_update<F: AsyncNorFlash>( 235 pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
215 &mut self, 236 self.dfu.erase(0, self.dfu.capacity() as u32).await?;
216 dfu_flash: &mut F,
217 ) -> Result<Partition, FirmwareUpdaterError> {
218 self.dfu.wipe(dfu_flash).await?;
219 237
220 Ok(self.dfu) 238 Ok(&mut self.dfu)
221 } 239 }
222} 240}
223 241
224#[cfg(test)] 242#[cfg(test)]
225mod tests { 243mod tests {
244 use embassy_embedded_hal::flash::partition::Partition;
245 use embassy_sync::blocking_mutex::raw::NoopRawMutex;
246 use embassy_sync::mutex::Mutex;
226 use futures::executor::block_on; 247 use futures::executor::block_on;
227 use sha1::{Digest, Sha1}; 248 use sha1::{Digest, Sha1};
228 249
@@ -231,20 +252,19 @@ mod tests {
231 252
232 #[test] 253 #[test]
233 fn can_verify_sha1() { 254 fn can_verify_sha1() {
234 const STATE: Partition = Partition::new(0, 4096); 255 let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default());
235 const DFU: Partition = Partition::new(65536, 131072); 256 let state = Partition::new(&flash, 0, 4096);
236 257 let dfu = Partition::new(&flash, 65536, 65536);
237 let mut flash = MemFlash::<131072, 4096, 8>::default();
238 258
239 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; 259 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
240 let mut to_write = [0; 4096]; 260 let mut to_write = [0; 4096];
241 to_write[..7].copy_from_slice(update.as_slice()); 261 to_write[..7].copy_from_slice(update.as_slice());
242 262
243 let mut updater = FirmwareUpdater::new(DFU, STATE); 263 let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
244 block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); 264 block_on(updater.write_firmware(0, to_write.as_slice())).unwrap();
245 let mut chunk_buf = [0; 2]; 265 let mut chunk_buf = [0; 2];
246 let mut hash = [0; 20]; 266 let mut hash = [0; 20];
247 block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); 267 block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
248 268
249 assert_eq!(Sha1::digest(update).as_slice(), hash); 269 assert_eq!(Sha1::digest(update).as_slice(), hash);
250 } 270 }
diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs
index 50caaf08c..c44126149 100644
--- a/embassy-boot/boot/src/firmware_updater/blocking.rs
+++ b/embassy-boot/boot/src/firmware_updater/blocking.rs
@@ -1,25 +1,70 @@
1use digest::Digest; 1use digest::Digest;
2use embassy_embedded_hal::flash::partition::BlockingPartition;
3use embassy_sync::blocking_mutex::raw::NoopRawMutex;
2use embedded_storage::nor_flash::NorFlash; 4use embedded_storage::nor_flash::NorFlash;
3 5
4use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; 6use super::FirmwareUpdaterConfig;
7use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
8
9/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
10/// 'mess up' the internal bootloader state
11pub struct BlockingFirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
12 dfu: DFU,
13 state: STATE,
14}
15
16impl<'a, FLASH: NorFlash>
17 FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, FLASH>, BlockingPartition<'a, NoopRawMutex, FLASH>>
18{
19 /// Create a firmware updater config from the flash and address symbols defined in the linkerfile
20 #[cfg(target_os = "none")]
21 pub fn from_linkerfile_blocking(flash: &'a Mutex<NoopRawMutex, RefCell<FLASH>>) -> Self {
22 use core::cell::RefCell;
23
24 use embassy_sync::blocking_mutex::Mutex;
25
26 extern "C" {
27 static __bootloader_state_start: u32;
28 static __bootloader_state_end: u32;
29 static __bootloader_dfu_start: u32;
30 static __bootloader_dfu_end: u32;
31 }
32
33 let dfu = unsafe {
34 let start = &__bootloader_dfu_start as *const u32 as u32;
35 let end = &__bootloader_dfu_end as *const u32 as u32;
36 trace!("DFU: 0x{:x} - 0x{:x}", start, end);
37
38 BlockingPartition::new(flash, start, end - start)
39 };
40 let state = unsafe {
41 let start = &__bootloader_state_start as *const u32 as u32;
42 let end = &__bootloader_state_end as *const u32 as u32;
43 trace!("STATE: 0x{:x} - 0x{:x}", start, end);
44
45 BlockingPartition::new(flash, start, end - start)
46 };
5 47
6impl FirmwareUpdater {
7 /// Create a firmware updater instance with partition ranges for the update and state partitions.
8 pub const fn new(dfu: Partition, state: Partition) -> Self {
9 Self { dfu, state } 48 Self { dfu, state }
10 } 49 }
50}
51
52impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
53 /// Create a firmware updater instance with partition ranges for the update and state partitions.
54 pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self {
55 Self {
56 dfu: config.dfu,
57 state: config.state,
58 }
59 }
11 60
12 /// Obtain the current state. 61 /// Obtain the current state.
13 /// 62 ///
14 /// This is useful to check if the bootloader has just done a swap, in order 63 /// This is useful to check if the bootloader has just done a swap, in order
15 /// to do verifications and self-tests of the new image before calling 64 /// to do verifications and self-tests of the new image before calling
16 /// `mark_booted`. 65 /// `mark_booted`.
17 pub fn get_state_blocking<F: NorFlash>( 66 pub fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
18 &mut self, 67 self.state.read(0, aligned)?;
19 state_flash: &mut F,
20 aligned: &mut [u8],
21 ) -> Result<State, FirmwareUpdaterError> {
22 self.state.read_blocking(state_flash, 0, aligned)?;
23 68
24 if !aligned.iter().any(|&b| b != SWAP_MAGIC) { 69 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
25 Ok(State::Swap) 70 Ok(State::Swap)
@@ -42,19 +87,18 @@ impl FirmwareUpdater {
42 /// 87 ///
43 /// # Safety 88 /// # Safety
44 /// 89 ///
45 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from 90 /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
46 /// and written to. 91 /// and written to.
47 #[cfg(feature = "_verify")] 92 #[cfg(feature = "_verify")]
48 pub fn verify_and_mark_updated_blocking<F: NorFlash>( 93 pub fn verify_and_mark_updated(
49 &mut self, 94 &mut self,
50 _state_and_dfu_flash: &mut F,
51 _public_key: &[u8], 95 _public_key: &[u8],
52 _signature: &[u8], 96 _signature: &[u8],
53 _update_len: u32, 97 _update_len: u32,
54 _aligned: &mut [u8], 98 _aligned: &mut [u8],
55 ) -> Result<(), FirmwareUpdaterError> { 99 ) -> Result<(), FirmwareUpdaterError> {
56 assert_eq!(_aligned.len(), F::WRITE_SIZE); 100 assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
57 assert!(_update_len <= self.dfu.size()); 101 assert!(_update_len <= self.dfu.capacity() as u32);
58 102
59 #[cfg(feature = "ed25519-dalek")] 103 #[cfg(feature = "ed25519-dalek")]
60 { 104 {
@@ -68,7 +112,7 @@ impl FirmwareUpdater {
68 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; 112 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
69 113
70 let mut message = [0; 64]; 114 let mut message = [0; 64];
71 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; 115 self.hash::<Sha512>(_update_len, _aligned, &mut message)?;
72 116
73 public_key.verify(&message, &signature).map_err(into_signature_error)? 117 public_key.verify(&message, &signature).map_err(into_signature_error)?
74 } 118 }
@@ -89,7 +133,7 @@ impl FirmwareUpdater {
89 let signature = Signature::try_from(&signature).map_err(into_signature_error)?; 133 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
90 134
91 let mut message = [0; 64]; 135 let mut message = [0; 64];
92 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; 136 self.hash::<Sha512>(_update_len, _aligned, &mut message)?;
93 137
94 let r = public_key.verify(&message, &signature); 138 let r = public_key.verify(&message, &signature);
95 trace!( 139 trace!(
@@ -102,20 +146,19 @@ impl FirmwareUpdater {
102 r.map_err(into_signature_error)? 146 r.map_err(into_signature_error)?
103 } 147 }
104 148
105 self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) 149 self.set_magic(_aligned, SWAP_MAGIC)
106 } 150 }
107 151
108 /// Verify the update in DFU with any digest. 152 /// Verify the update in DFU with any digest.
109 pub fn hash_blocking<F: NorFlash, D: Digest>( 153 pub fn hash<D: Digest>(
110 &mut self, 154 &mut self,
111 dfu_flash: &mut F,
112 update_len: u32, 155 update_len: u32,
113 chunk_buf: &mut [u8], 156 chunk_buf: &mut [u8],
114 output: &mut [u8], 157 output: &mut [u8],
115 ) -> Result<(), FirmwareUpdaterError> { 158 ) -> Result<(), FirmwareUpdaterError> {
116 let mut digest = D::new(); 159 let mut digest = D::new();
117 for offset in (0..update_len).step_by(chunk_buf.len()) { 160 for offset in (0..update_len).step_by(chunk_buf.len()) {
118 self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; 161 self.dfu.read(offset, chunk_buf)?;
119 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); 162 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
120 digest.update(&chunk_buf[..len]); 163 digest.update(&chunk_buf[..len]);
121 } 164 }
@@ -127,60 +170,44 @@ impl FirmwareUpdater {
127 /// 170 ///
128 /// # Safety 171 /// # Safety
129 /// 172 ///
130 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. 173 /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
131 #[cfg(not(feature = "_verify"))] 174 #[cfg(not(feature = "_verify"))]
132 pub fn mark_updated_blocking<F: NorFlash>( 175 pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
133 &mut self, 176 assert_eq!(aligned.len(), STATE::WRITE_SIZE);
134 state_flash: &mut F, 177 self.set_magic(aligned, SWAP_MAGIC)
135 aligned: &mut [u8],
136 ) -> Result<(), FirmwareUpdaterError> {
137 assert_eq!(aligned.len(), F::WRITE_SIZE);
138 self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash)
139 } 178 }
140 179
141 /// Mark firmware boot successful and stop rollback on reset. 180 /// Mark firmware boot successful and stop rollback on reset.
142 /// 181 ///
143 /// # Safety 182 /// # Safety
144 /// 183 ///
145 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. 184 /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
146 pub fn mark_booted_blocking<F: NorFlash>( 185 pub fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
147 &mut self, 186 assert_eq!(aligned.len(), STATE::WRITE_SIZE);
148 state_flash: &mut F, 187 self.set_magic(aligned, BOOT_MAGIC)
149 aligned: &mut [u8],
150 ) -> Result<(), FirmwareUpdaterError> {
151 assert_eq!(aligned.len(), F::WRITE_SIZE);
152 self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash)
153 } 188 }
154 189
155 fn set_magic_blocking<F: NorFlash>( 190 fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> {
156 &mut self, 191 self.state.read(0, aligned)?;
157 aligned: &mut [u8],
158 magic: u8,
159 state_flash: &mut F,
160 ) -> Result<(), FirmwareUpdaterError> {
161 self.state.read_blocking(state_flash, 0, aligned)?;
162 192
163 if aligned.iter().any(|&b| b != magic) { 193 if aligned.iter().any(|&b| b != magic) {
164 // Read progress validity 194 // Read progress validity
165 self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; 195 self.state.read(STATE::WRITE_SIZE as u32, aligned)?;
166
167 // FIXME: Do not make this assumption.
168 const STATE_ERASE_VALUE: u8 = 0xFF;
169 196
170 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { 197 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
171 // The current progress validity marker is invalid 198 // The current progress validity marker is invalid
172 } else { 199 } else {
173 // Invalidate progress 200 // Invalidate progress
174 aligned.fill(!STATE_ERASE_VALUE); 201 aligned.fill(!STATE_ERASE_VALUE);
175 self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; 202 self.state.write(STATE::WRITE_SIZE as u32, aligned)?;
176 } 203 }
177 204
178 // Clear magic and progress 205 // Clear magic and progress
179 self.state.wipe_blocking(state_flash)?; 206 self.state.erase(0, self.state.capacity() as u32)?;
180 207
181 // Set magic 208 // Set magic
182 aligned.fill(magic); 209 aligned.fill(magic);
183 self.state.write_blocking(state_flash, 0, aligned)?; 210 self.state.write(0, aligned)?;
184 } 211 }
185 Ok(()) 212 Ok(())
186 } 213 }
@@ -192,18 +219,12 @@ impl FirmwareUpdater {
192 /// # Safety 219 /// # Safety
193 /// 220 ///
194 /// Failing to meet alignment and size requirements may result in a panic. 221 /// Failing to meet alignment and size requirements may result in a panic.
195 pub fn write_firmware_blocking<F: NorFlash>( 222 pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
196 &mut self, 223 assert!(data.len() >= DFU::ERASE_SIZE);
197 offset: usize,
198 data: &[u8],
199 dfu_flash: &mut F,
200 ) -> Result<(), FirmwareUpdaterError> {
201 assert!(data.len() >= F::ERASE_SIZE);
202 224
203 self.dfu 225 self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
204 .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?;
205 226
206 self.dfu.write_blocking(dfu_flash, offset as u32, data)?; 227 self.dfu.write(offset as u32, data)?;
207 228
208 Ok(()) 229 Ok(())
209 } 230 }
@@ -211,11 +232,45 @@ impl FirmwareUpdater {
211 /// Prepare for an incoming DFU update by erasing the entire DFU area and 232 /// Prepare for an incoming DFU update by erasing the entire DFU area and
212 /// returning its `Partition`. 233 /// returning its `Partition`.
213 /// 234 ///
214 /// Using this instead of `write_firmware_blocking` allows for an optimized 235 /// Using this instead of `write_firmware` allows for an optimized API in
215 /// API in exchange for added complexity. 236 /// exchange for added complexity.
216 pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<Partition, FirmwareUpdaterError> { 237 pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
217 self.dfu.wipe_blocking(flash)?; 238 self.dfu.erase(0, self.dfu.capacity() as u32)?;
239
240 Ok(&mut self.dfu)
241 }
242}
218 243
219 Ok(self.dfu) 244#[cfg(test)]
245mod tests {
246 use core::cell::RefCell;
247
248 use embassy_embedded_hal::flash::partition::BlockingPartition;
249 use embassy_sync::blocking_mutex::raw::NoopRawMutex;
250 use embassy_sync::blocking_mutex::Mutex;
251 use sha1::{Digest, Sha1};
252
253 use super::*;
254 use crate::mem_flash::MemFlash;
255
256 #[test]
257 fn can_verify_sha1() {
258 let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
259 let state = BlockingPartition::new(&flash, 0, 4096);
260 let dfu = BlockingPartition::new(&flash, 65536, 65536);
261
262 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
263 let mut to_write = [0; 4096];
264 to_write[..7].copy_from_slice(update.as_slice());
265
266 let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
267 updater.write_firmware(0, to_write.as_slice()).unwrap();
268 let mut chunk_buf = [0; 2];
269 let mut hash = [0; 20];
270 updater
271 .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
272 .unwrap();
273
274 assert_eq!(Sha1::digest(update).as_slice(), hash);
220 } 275 }
221} 276}
diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs
index e09f5eb6c..a37984a3a 100644
--- a/embassy-boot/boot/src/firmware_updater/mod.rs
+++ b/embassy-boot/boot/src/firmware_updater/mod.rs
@@ -2,9 +2,22 @@
2mod asynch; 2mod asynch;
3mod blocking; 3mod blocking;
4 4
5#[cfg(feature = "nightly")]
6pub use asynch::FirmwareUpdater;
7pub use blocking::BlockingFirmwareUpdater;
5use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; 8use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
6 9
7use crate::Partition; 10/// Firmware updater flash configuration holding the two flashes used by the updater
11///
12/// If only a single flash is actually used, then that flash should be partitioned into two partitions before use.
13/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition
14/// the provided flash according to symbols defined in the linkerfile.
15pub struct FirmwareUpdaterConfig<DFU, STATE> {
16 /// The dfu flash partition
17 pub dfu: DFU,
18 /// The state flash partition
19 pub state: STATE,
20}
8 21
9/// Errors returned by FirmwareUpdater 22/// Errors returned by FirmwareUpdater
10#[derive(Debug)] 23#[derive(Debug)]
@@ -33,39 +46,3 @@ where
33 FirmwareUpdaterError::Flash(error.kind()) 46 FirmwareUpdaterError::Flash(error.kind())
34 } 47 }
35} 48}
36
37/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
38/// 'mess up' the internal bootloader state
39pub struct FirmwareUpdater {
40 state: Partition,
41 dfu: Partition,
42}
43
44#[cfg(target_os = "none")]
45impl Default for FirmwareUpdater {
46 fn default() -> Self {
47 extern "C" {
48 static __bootloader_state_start: u32;
49 static __bootloader_state_end: u32;
50 static __bootloader_dfu_start: u32;
51 static __bootloader_dfu_end: u32;
52 }
53
54 let dfu = unsafe {
55 Partition::new(
56 &__bootloader_dfu_start as *const u32 as u32,
57 &__bootloader_dfu_end as *const u32 as u32,
58 )
59 };
60 let state = unsafe {
61 Partition::new(
62 &__bootloader_state_start as *const u32 as u32,
63 &__bootloader_state_end as *const u32 as u32,
64 )
65 };
66
67 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
68 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
69 FirmwareUpdater::new(dfu, state)
70 }
71}
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 4521fecb0..af2d3ce02 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -11,8 +11,10 @@ mod mem_flash;
11mod partition; 11mod partition;
12 12
13pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig}; 13pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig};
14pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError};
15pub use partition::Partition; 14pub use partition::Partition;
15#[cfg(feature = "nightly")]
16pub use firmware_updater::FirmwareUpdater;
17pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError};
16 18
17pub(crate) const BOOT_MAGIC: u8 = 0xD0; 19pub(crate) const BOOT_MAGIC: u8 = 0xD0;
18pub(crate) const SWAP_MAGIC: u8 = 0xF0; 20pub(crate) const SWAP_MAGIC: u8 = 0xF0;