aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot/boot
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-boot/boot')
-rw-r--r--embassy-boot/boot/src/boot_loader.rs139
-rw-r--r--embassy-boot/boot/src/firmware_updater.rs225
-rw-r--r--embassy-boot/boot/src/firmware_writer.rs97
-rw-r--r--embassy-boot/boot/src/lib.rs166
-rw-r--r--embassy-boot/boot/src/mem_flash.rs156
-rw-r--r--embassy-boot/boot/src/partition.rs124
6 files changed, 468 insertions, 439 deletions
diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs
index ad6735112..9d047f778 100644
--- a/embassy-boot/boot/src/boot_loader.rs
+++ b/embassy-boot/boot/src/boot_loader.rs
@@ -31,7 +31,7 @@ where
31} 31}
32 32
33/// Extension of the embedded-storage flash type information with block size and erase value. 33/// Extension of the embedded-storage flash type information with block size and erase value.
34pub trait Flash: NorFlash + ReadNorFlash { 34pub trait Flash: NorFlash {
35 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase 35 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase
36 /// size of the flash, but for external QSPI flash modules, this can be lower. 36 /// size of the flash, but for external QSPI flash modules, this can be lower.
37 const BLOCK_SIZE: usize; 37 const BLOCK_SIZE: usize;
@@ -60,9 +60,11 @@ pub trait FlashConfig {
60/// different page sizes and flash write sizes. 60/// different page sizes and flash write sizes.
61pub struct BootLoader { 61pub struct BootLoader {
62 // Page with current state of bootloader. The state partition has the following format: 62 // Page with current state of bootloader. The state partition has the following format:
63 // | Range | Description | 63 // All ranges are in multiples of WRITE_SIZE bytes.
64 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 64 // | Range | Description |
65 // | WRITE_SIZE - N | Progress index used while swapping or reverting | 65 // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
66 // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. |
67 // | 2..2 + N | Progress index used while swapping or reverting |
66 state: Partition, 68 state: Partition,
67 // Location of the partition which will be booted from 69 // Location of the partition which will be booted from
68 active: Partition, 70 active: Partition,
@@ -79,7 +81,7 @@ impl BootLoader {
79 Self { active, dfu, state } 81 Self { active, dfu, state }
80 } 82 }
81 83
82 /// Return the boot address for the active partition. 84 /// Return the offset of the active partition into the active flash.
83 pub fn boot_address(&self) -> usize { 85 pub fn boot_address(&self) -> usize {
84 self.active.from 86 self.active.from
85 } 87 }
@@ -192,14 +194,19 @@ impl BootLoader {
192 trace!("Reverting"); 194 trace!("Reverting");
193 self.revert(p, magic, page)?; 195 self.revert(p, magic, page)?;
194 196
195 // Overwrite magic and reset progress 197 let state_flash = p.state();
196 let fstate = p.state(); 198
199 // Invalidate progress
197 magic.fill(!P::STATE::ERASE_VALUE); 200 magic.fill(!P::STATE::ERASE_VALUE);
198 fstate.write(self.state.from as u32, magic)?; 201 self.state
199 fstate.erase(self.state.from as u32, self.state.to as u32)?; 202 .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, magic)?;
203
204 // Clear magic and progress
205 self.state.wipe_blocking(state_flash)?;
200 206
207 // Set magic
201 magic.fill(BOOT_MAGIC); 208 magic.fill(BOOT_MAGIC);
202 fstate.write(self.state.from as u32, magic)?; 209 self.state.write_blocking(state_flash, 0, magic)?;
203 } 210 }
204 } 211 }
205 Ok(state) 212 Ok(state)
@@ -215,62 +222,61 @@ impl BootLoader {
215 222
216 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> { 223 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> {
217 let write_size = aligned.len(); 224 let write_size = aligned.len();
218 let max_index = ((self.state.len() - write_size) / write_size) - 1; 225 let max_index = ((self.state.len() - write_size) / write_size) - 2;
219 aligned.fill(!P::STATE::ERASE_VALUE); 226 aligned.fill(!P::STATE::ERASE_VALUE);
220 227
221 let flash = config.state(); 228 let state_flash = config.state();
222 for i in 0..max_index { 229
223 flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; 230 self.state
231 .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, aligned)?;
232 if aligned.iter().any(|&b| b != P::STATE::ERASE_VALUE) {
233 // Progress is invalid
234 return Ok(max_index);
235 }
236
237 for index in 0..max_index {
238 self.state
239 .read_blocking(state_flash, (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?;
224 240
225 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { 241 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) {
226 return Ok(i); 242 return Ok(index);
227 } 243 }
228 } 244 }
229 Ok(max_index) 245 Ok(max_index)
230 } 246 }
231 247
232 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { 248 fn update_progress<P: FlashConfig>(&mut self, index: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> {
233 let flash = p.state();
234 let write_size = magic.len();
235 let w = self.state.from + write_size + idx * write_size;
236
237 let aligned = magic; 249 let aligned = magic;
238 aligned.fill(!P::STATE::ERASE_VALUE); 250 aligned.fill(!P::STATE::ERASE_VALUE);
239 flash.write(w as u32, aligned)?; 251 self.state
252 .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?;
240 Ok(()) 253 Ok(())
241 } 254 }
242 255
243 fn active_addr(&self, n: usize, page_size: usize) -> usize {
244 self.active.from + n * page_size
245 }
246
247 fn dfu_addr(&self, n: usize, page_size: usize) -> usize {
248 self.dfu.from + n * page_size
249 }
250
251 fn copy_page_once_to_active<P: FlashConfig>( 256 fn copy_page_once_to_active<P: FlashConfig>(
252 &mut self, 257 &mut self,
253 idx: usize, 258 idx: usize,
254 from_page: usize, 259 from_offset: u32,
255 to_page: usize, 260 to_offset: u32,
256 p: &mut P, 261 p: &mut P,
257 magic: &mut [u8], 262 magic: &mut [u8],
258 page: &mut [u8], 263 page: &mut [u8],
259 ) -> Result<(), BootError> { 264 ) -> Result<(), BootError> {
260 let buf = page; 265 let buf = page;
261 if self.current_progress(p, magic)? <= idx { 266 if self.current_progress(p, magic)? <= idx {
262 let mut offset = from_page; 267 let mut offset = from_offset;
263 for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { 268 for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) {
264 p.dfu().read(offset as u32, chunk)?; 269 self.dfu.read_blocking(p.dfu(), offset, chunk)?;
265 offset += chunk.len(); 270 offset += chunk.len() as u32;
266 } 271 }
267 272
268 p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; 273 self.active
274 .erase_blocking(p.active(), to_offset, to_offset + buf.len() as u32)?;
269 275
270 let mut offset = to_page; 276 let mut offset = to_offset;
271 for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { 277 for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) {
272 p.active().write(offset as u32, chunk)?; 278 self.active.write_blocking(p.active(), offset, chunk)?;
273 offset += chunk.len(); 279 offset += chunk.len() as u32;
274 } 280 }
275 self.update_progress(idx, p, magic)?; 281 self.update_progress(idx, p, magic)?;
276 } 282 }
@@ -280,26 +286,27 @@ impl BootLoader {
280 fn copy_page_once_to_dfu<P: FlashConfig>( 286 fn copy_page_once_to_dfu<P: FlashConfig>(
281 &mut self, 287 &mut self,
282 idx: usize, 288 idx: usize,
283 from_page: usize, 289 from_offset: u32,
284 to_page: usize, 290 to_offset: u32,
285 p: &mut P, 291 p: &mut P,
286 magic: &mut [u8], 292 magic: &mut [u8],
287 page: &mut [u8], 293 page: &mut [u8],
288 ) -> Result<(), BootError> { 294 ) -> Result<(), BootError> {
289 let buf = page; 295 let buf = page;
290 if self.current_progress(p, magic)? <= idx { 296 if self.current_progress(p, magic)? <= idx {
291 let mut offset = from_page; 297 let mut offset = from_offset;
292 for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { 298 for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) {
293 p.active().read(offset as u32, chunk)?; 299 self.active.read_blocking(p.active(), offset, chunk)?;
294 offset += chunk.len(); 300 offset += chunk.len() as u32;
295 } 301 }
296 302
297 p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; 303 self.dfu
304 .erase_blocking(p.dfu(), to_offset as u32, to_offset + buf.len() as u32)?;
298 305
299 let mut offset = to_page; 306 let mut offset = to_offset;
300 for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { 307 for chunk in buf.chunks(P::DFU::BLOCK_SIZE) {
301 p.dfu().write(offset as u32, chunk)?; 308 self.dfu.write_blocking(p.dfu(), offset, chunk)?;
302 offset += chunk.len(); 309 offset += chunk.len() as u32;
303 } 310 }
304 self.update_progress(idx, p, magic)?; 311 self.update_progress(idx, p, magic)?;
305 } 312 }
@@ -312,17 +319,20 @@ impl BootLoader {
312 trace!("Page count: {}", page_count); 319 trace!("Page count: {}", page_count);
313 for page_num in 0..page_count { 320 for page_num in 0..page_count {
314 trace!("COPY PAGE {}", page_num); 321 trace!("COPY PAGE {}", page_num);
322
323 let idx = page_num * 2;
324
315 // Copy active page to the 'next' DFU page. 325 // Copy active page to the 'next' DFU page.
316 let active_page = self.active_addr(page_count - 1 - page_num, page_size); 326 let active_from_offset = ((page_count - 1 - page_num) * page_size) as u32;
317 let dfu_page = self.dfu_addr(page_count - page_num, page_size); 327 let dfu_to_offset = ((page_count - page_num) * page_size) as u32;
318 //trace!("Copy active {} to dfu {}", active_page, dfu_page); 328 //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset);
319 self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; 329 self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?;
320 330
321 // Copy DFU page to the active page 331 // Copy DFU page to the active page
322 let active_page = self.active_addr(page_count - 1 - page_num, page_size); 332 let active_to_offset = ((page_count - 1 - page_num) * page_size) as u32;
323 let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); 333 let dfu_from_offset = ((page_count - 1 - page_num) * page_size) as u32;
324 //trace!("Copy dfy {} to active {}", dfu_page, active_page); 334 //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset);
325 self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; 335 self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?;
326 } 336 }
327 337
328 Ok(()) 338 Ok(())
@@ -332,23 +342,24 @@ impl BootLoader {
332 let page_size = page.len(); 342 let page_size = page.len();
333 let page_count = self.active.len() / page_size; 343 let page_count = self.active.len() / page_size;
334 for page_num in 0..page_count { 344 for page_num in 0..page_count {
345 let idx = page_count * 2 + page_num * 2;
346
335 // Copy the bad active page to the DFU page 347 // Copy the bad active page to the DFU page
336 let active_page = self.active_addr(page_num, page_size); 348 let active_from_offset = (page_num * page_size) as u32;
337 let dfu_page = self.dfu_addr(page_num, page_size); 349 let dfu_to_offset = (page_num * page_size) as u32;
338 self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; 350 self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?;
339 351
340 // Copy the DFU page back to the active page 352 // Copy the DFU page back to the active page
341 let active_page = self.active_addr(page_num, page_size); 353 let active_to_offset = (page_num * page_size) as u32;
342 let dfu_page = self.dfu_addr(page_num + 1, page_size); 354 let dfu_from_offset = ((page_num + 1) * page_size) as u32;
343 self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; 355 self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?;
344 } 356 }
345 357
346 Ok(()) 358 Ok(())
347 } 359 }
348 360
349 fn read_state<P: FlashConfig>(&mut self, config: &mut P, magic: &mut [u8]) -> Result<State, BootError> { 361 fn read_state<P: FlashConfig>(&mut self, config: &mut P, magic: &mut [u8]) -> Result<State, BootError> {
350 let flash = config.state(); 362 self.state.read_blocking(config.state(), 0, magic)?;
351 flash.read(self.state.from as u32, magic)?;
352 363
353 if !magic.iter().any(|&b| b != SWAP_MAGIC) { 364 if !magic.iter().any(|&b| b != SWAP_MAGIC) {
354 Ok(State::Swap) 365 Ok(State::Swap)
@@ -362,7 +373,7 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s
362 assert_eq!(active.len() % page_size, 0); 373 assert_eq!(active.len() % page_size, 0);
363 assert_eq!(dfu.len() % page_size, 0); 374 assert_eq!(dfu.len() % page_size, 0);
364 assert!(dfu.len() - active.len() >= page_size); 375 assert!(dfu.len() - active.len() >= page_size);
365 assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); 376 assert!(2 + 2 * (active.len() / page_size) <= state.len() / write_size);
366} 377}
367 378
368/// A flash wrapper implementing the Flash and embedded_storage traits. 379/// A flash wrapper implementing the Flash and embedded_storage traits.
diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs
index 2d8712277..2b5cc72fa 100644
--- a/embassy-boot/boot/src/firmware_updater.rs
+++ b/embassy-boot/boot/src/firmware_updater.rs
@@ -1,7 +1,7 @@
1use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; 1use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
2use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; 2use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
3 3
4use crate::{FirmwareWriter, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; 4use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC};
5 5
6/// Errors returned by FirmwareUpdater 6/// Errors returned by FirmwareUpdater
7#[derive(Debug)] 7#[derive(Debug)]
@@ -84,10 +84,10 @@ impl FirmwareUpdater {
84 /// `mark_booted`. 84 /// `mark_booted`.
85 pub async fn get_state<F: AsyncNorFlash>( 85 pub async fn get_state<F: AsyncNorFlash>(
86 &mut self, 86 &mut self,
87 flash: &mut F, 87 state_flash: &mut F,
88 aligned: &mut [u8], 88 aligned: &mut [u8],
89 ) -> Result<State, FirmwareUpdaterError> { 89 ) -> Result<State, FirmwareUpdaterError> {
90 flash.read(self.state.from as u32, aligned).await?; 90 self.state.read(state_flash, 0, aligned).await?;
91 91
92 if !aligned.iter().any(|&b| b != SWAP_MAGIC) { 92 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
93 Ok(State::Swap) 93 Ok(State::Swap)
@@ -115,17 +115,16 @@ impl FirmwareUpdater {
115 #[cfg(feature = "_verify")] 115 #[cfg(feature = "_verify")]
116 pub async fn verify_and_mark_updated<F: AsyncNorFlash>( 116 pub async fn verify_and_mark_updated<F: AsyncNorFlash>(
117 &mut self, 117 &mut self,
118 _flash: &mut F, 118 _state_and_dfu_flash: &mut F,
119 _public_key: &[u8], 119 _public_key: &[u8],
120 _signature: &[u8], 120 _signature: &[u8],
121 _update_len: usize, 121 _update_len: usize,
122 _aligned: &mut [u8], 122 _aligned: &mut [u8],
123 ) -> Result<(), FirmwareUpdaterError> { 123 ) -> Result<(), FirmwareUpdaterError> {
124 let _end = self.dfu.from + _update_len;
125 let _read_size = _aligned.len(); 124 let _read_size = _aligned.len();
126 125
127 assert_eq!(_aligned.len(), F::WRITE_SIZE); 126 assert_eq!(_aligned.len(), F::WRITE_SIZE);
128 assert!(_end <= self.dfu.to); 127 assert!(_update_len <= self.dfu.len());
129 128
130 #[cfg(feature = "ed25519-dalek")] 129 #[cfg(feature = "ed25519-dalek")]
131 { 130 {
@@ -137,21 +136,10 @@ impl FirmwareUpdater {
137 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; 136 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
138 137
139 let mut digest = Sha512::new(); 138 let mut digest = Sha512::new();
140 139 for offset in (0.._update_len).step_by(_aligned.len()) {
141 let mut offset = self.dfu.from; 140 self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?;
142 let last_offset = _end / _read_size * _read_size; 141 let len = core::cmp::min(_update_len - offset, _aligned.len());
143 142 digest.update(&_aligned[..len]);
144 while offset < last_offset {
145 _flash.read(offset as u32, _aligned).await?;
146 digest.update(&_aligned);
147 offset += _read_size;
148 }
149
150 let remaining = _end % _read_size;
151
152 if remaining > 0 {
153 _flash.read(last_offset as u32, _aligned).await?;
154 digest.update(&_aligned[0..remaining]);
155 } 143 }
156 144
157 public_key 145 public_key
@@ -173,21 +161,10 @@ impl FirmwareUpdater {
173 let signature = Signature::try_from(&signature).map_err(into_signature_error)?; 161 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
174 162
175 let mut digest = Sha512::new(); 163 let mut digest = Sha512::new();
176 164 for offset in (0.._update_len).step_by(_aligned.len()) {
177 let mut offset = self.dfu.from; 165 self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?;
178 let last_offset = _end / _read_size * _read_size; 166 let len = core::cmp::min(_update_len - offset, _aligned.len());
179 167 digest.update(&_aligned[..len]);
180 while offset < last_offset {
181 _flash.read(offset as u32, _aligned).await?;
182 digest.update(&_aligned);
183 offset += _read_size;
184 }
185
186 let remaining = _end % _read_size;
187
188 if remaining > 0 {
189 _flash.read(last_offset as u32, _aligned).await?;
190 digest.update(&_aligned[0..remaining]);
191 } 168 }
192 169
193 let message = digest.finalize(); 170 let message = digest.finalize();
@@ -202,7 +179,7 @@ impl FirmwareUpdater {
202 r.map_err(into_signature_error)? 179 r.map_err(into_signature_error)?
203 } 180 }
204 181
205 self.set_magic(_aligned, SWAP_MAGIC, _flash).await 182 self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await
206 } 183 }
207 184
208 /// Mark to trigger firmware swap on next boot. 185 /// Mark to trigger firmware swap on next boot.
@@ -213,11 +190,11 @@ impl FirmwareUpdater {
213 #[cfg(not(feature = "_verify"))] 190 #[cfg(not(feature = "_verify"))]
214 pub async fn mark_updated<F: AsyncNorFlash>( 191 pub async fn mark_updated<F: AsyncNorFlash>(
215 &mut self, 192 &mut self,
216 flash: &mut F, 193 state_flash: &mut F,
217 aligned: &mut [u8], 194 aligned: &mut [u8],
218 ) -> Result<(), FirmwareUpdaterError> { 195 ) -> Result<(), FirmwareUpdaterError> {
219 assert_eq!(aligned.len(), F::WRITE_SIZE); 196 assert_eq!(aligned.len(), F::WRITE_SIZE);
220 self.set_magic(aligned, SWAP_MAGIC, flash).await 197 self.set_magic(aligned, SWAP_MAGIC, state_flash).await
221 } 198 }
222 199
223 /// Mark firmware boot successful and stop rollback on reset. 200 /// Mark firmware boot successful and stop rollback on reset.
@@ -227,29 +204,42 @@ impl FirmwareUpdater {
227 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. 204 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
228 pub async fn mark_booted<F: AsyncNorFlash>( 205 pub async fn mark_booted<F: AsyncNorFlash>(
229 &mut self, 206 &mut self,
230 flash: &mut F, 207 state_flash: &mut F,
231 aligned: &mut [u8], 208 aligned: &mut [u8],
232 ) -> Result<(), FirmwareUpdaterError> { 209 ) -> Result<(), FirmwareUpdaterError> {
233 assert_eq!(aligned.len(), F::WRITE_SIZE); 210 assert_eq!(aligned.len(), F::WRITE_SIZE);
234 self.set_magic(aligned, BOOT_MAGIC, flash).await 211 self.set_magic(aligned, BOOT_MAGIC, state_flash).await
235 } 212 }
236 213
237 async fn set_magic<F: AsyncNorFlash>( 214 async fn set_magic<F: AsyncNorFlash>(
238 &mut self, 215 &mut self,
239 aligned: &mut [u8], 216 aligned: &mut [u8],
240 magic: u8, 217 magic: u8,
241 flash: &mut F, 218 state_flash: &mut F,
242 ) -> Result<(), FirmwareUpdaterError> { 219 ) -> Result<(), FirmwareUpdaterError> {
243 flash.read(self.state.from as u32, aligned).await?; 220 self.state.read(state_flash, 0, aligned).await?;
244 221
245 if aligned.iter().any(|&b| b != magic) { 222 if aligned.iter().any(|&b| b != magic) {
246 aligned.fill(0); 223 // Read progress validity
224 self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?;
225
226 // FIXME: Do not make this assumption.
227 const STATE_ERASE_VALUE: u8 = 0xFF;
228
229 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
230 // The current progress validity marker is invalid
231 } else {
232 // Invalidate progress
233 aligned.fill(!STATE_ERASE_VALUE);
234 self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?;
235 }
247 236
248 flash.write(self.state.from as u32, aligned).await?; 237 // Clear magic and progress
249 flash.erase(self.state.from as u32, self.state.to as u32).await?; 238 self.state.wipe(state_flash).await?;
250 239
240 // Set magic
251 aligned.fill(magic); 241 aligned.fill(magic);
252 flash.write(self.state.from as u32, aligned).await?; 242 self.state.write(state_flash, 0, aligned).await?;
253 } 243 }
254 Ok(()) 244 Ok(())
255 } 245 }
@@ -265,45 +255,31 @@ impl FirmwareUpdater {
265 &mut self, 255 &mut self,
266 offset: usize, 256 offset: usize,
267 data: &[u8], 257 data: &[u8],
268 flash: &mut F, 258 dfu_flash: &mut F,
269 block_size: usize,
270 ) -> Result<(), FirmwareUpdaterError> { 259 ) -> Result<(), FirmwareUpdaterError> {
271 assert!(data.len() >= F::ERASE_SIZE); 260 assert!(data.len() >= F::ERASE_SIZE);
272 261
273 flash 262 self.dfu
274 .erase( 263 .erase(dfu_flash, offset as u32, (offset + data.len()) as u32)
275 (self.dfu.from + offset) as u32,
276 (self.dfu.from + offset + data.len()) as u32,
277 )
278 .await?; 264 .await?;
279 265
280 trace!( 266 self.dfu.write(dfu_flash, offset as u32, data).await?;
281 "Erased from {} to {}",
282 self.dfu.from + offset,
283 self.dfu.from + offset + data.len()
284 );
285
286 FirmwareWriter(self.dfu)
287 .write_block(offset, data, flash, block_size)
288 .await?;
289 267
290 Ok(()) 268 Ok(())
291 } 269 }
292 270
293 /// Prepare for an incoming DFU update by erasing the entire DFU area and 271 /// Prepare for an incoming DFU update by erasing the entire DFU area and
294 /// returning a `FirmwareWriter`. 272 /// returning its `Partition`.
295 /// 273 ///
296 /// Using this instead of `write_firmware` allows for an optimized API in 274 /// Using this instead of `write_firmware` allows for an optimized API in
297 /// exchange for added complexity. 275 /// exchange for added complexity.
298 pub async fn prepare_update<F: AsyncNorFlash>( 276 pub async fn prepare_update<F: AsyncNorFlash>(
299 &mut self, 277 &mut self,
300 flash: &mut F, 278 dfu_flash: &mut F,
301 ) -> Result<FirmwareWriter, FirmwareUpdaterError> { 279 ) -> Result<Partition, FirmwareUpdaterError> {
302 flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; 280 self.dfu.wipe(dfu_flash).await?;
303 281
304 trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); 282 Ok(self.dfu)
305
306 Ok(FirmwareWriter(self.dfu))
307 } 283 }
308 284
309 // 285 //
@@ -317,10 +293,10 @@ impl FirmwareUpdater {
317 /// `mark_booted`. 293 /// `mark_booted`.
318 pub fn get_state_blocking<F: NorFlash>( 294 pub fn get_state_blocking<F: NorFlash>(
319 &mut self, 295 &mut self,
320 flash: &mut F, 296 state_flash: &mut F,
321 aligned: &mut [u8], 297 aligned: &mut [u8],
322 ) -> Result<State, FirmwareUpdaterError> { 298 ) -> Result<State, FirmwareUpdaterError> {
323 flash.read(self.state.from as u32, aligned)?; 299 self.state.read_blocking(state_flash, 0, aligned)?;
324 300
325 if !aligned.iter().any(|&b| b != SWAP_MAGIC) { 301 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
326 Ok(State::Swap) 302 Ok(State::Swap)
@@ -348,7 +324,7 @@ impl FirmwareUpdater {
348 #[cfg(feature = "_verify")] 324 #[cfg(feature = "_verify")]
349 pub fn verify_and_mark_updated_blocking<F: NorFlash>( 325 pub fn verify_and_mark_updated_blocking<F: NorFlash>(
350 &mut self, 326 &mut self,
351 _flash: &mut F, 327 _state_and_dfu_flash: &mut F,
352 _public_key: &[u8], 328 _public_key: &[u8],
353 _signature: &[u8], 329 _signature: &[u8],
354 _update_len: usize, 330 _update_len: usize,
@@ -370,21 +346,10 @@ impl FirmwareUpdater {
370 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; 346 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
371 347
372 let mut digest = Sha512::new(); 348 let mut digest = Sha512::new();
373 349 for offset in (0.._update_len).step_by(_aligned.len()) {
374 let mut offset = self.dfu.from; 350 self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?;
375 let last_offset = _end / _read_size * _read_size; 351 let len = core::cmp::min(_update_len - offset, _aligned.len());
376 352 digest.update(&_aligned[..len]);
377 while offset < last_offset {
378 _flash.read(offset as u32, _aligned)?;
379 digest.update(&_aligned);
380 offset += _read_size;
381 }
382
383 let remaining = _end % _read_size;
384
385 if remaining > 0 {
386 _flash.read(last_offset as u32, _aligned)?;
387 digest.update(&_aligned[0..remaining]);
388 } 353 }
389 354
390 public_key 355 public_key
@@ -406,21 +371,10 @@ impl FirmwareUpdater {
406 let signature = Signature::try_from(&signature).map_err(into_signature_error)?; 371 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
407 372
408 let mut digest = Sha512::new(); 373 let mut digest = Sha512::new();
409 374 for offset in (0.._update_len).step_by(_aligned.len()) {
410 let mut offset = self.dfu.from; 375 self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?;
411 let last_offset = _end / _read_size * _read_size; 376 let len = core::cmp::min(_update_len - offset, _aligned.len());
412 377 digest.update(&_aligned[..len]);
413 while offset < last_offset {
414 _flash.read(offset as u32, _aligned)?;
415 digest.update(&_aligned);
416 offset += _read_size;
417 }
418
419 let remaining = _end % _read_size;
420
421 if remaining > 0 {
422 _flash.read(last_offset as u32, _aligned)?;
423 digest.update(&_aligned[0..remaining]);
424 } 378 }
425 379
426 let message = digest.finalize(); 380 let message = digest.finalize();
@@ -435,7 +389,7 @@ impl FirmwareUpdater {
435 r.map_err(into_signature_error)? 389 r.map_err(into_signature_error)?
436 } 390 }
437 391
438 self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) 392 self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash)
439 } 393 }
440 394
441 /// Mark to trigger firmware swap on next boot. 395 /// Mark to trigger firmware swap on next boot.
@@ -446,11 +400,11 @@ impl FirmwareUpdater {
446 #[cfg(not(feature = "_verify"))] 400 #[cfg(not(feature = "_verify"))]
447 pub fn mark_updated_blocking<F: NorFlash>( 401 pub fn mark_updated_blocking<F: NorFlash>(
448 &mut self, 402 &mut self,
449 flash: &mut F, 403 state_flash: &mut F,
450 aligned: &mut [u8], 404 aligned: &mut [u8],
451 ) -> Result<(), FirmwareUpdaterError> { 405 ) -> Result<(), FirmwareUpdaterError> {
452 assert_eq!(aligned.len(), F::WRITE_SIZE); 406 assert_eq!(aligned.len(), F::WRITE_SIZE);
453 self.set_magic_blocking(aligned, SWAP_MAGIC, flash) 407 self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash)
454 } 408 }
455 409
456 /// Mark firmware boot successful and stop rollback on reset. 410 /// Mark firmware boot successful and stop rollback on reset.
@@ -460,29 +414,42 @@ impl FirmwareUpdater {
460 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. 414 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
461 pub fn mark_booted_blocking<F: NorFlash>( 415 pub fn mark_booted_blocking<F: NorFlash>(
462 &mut self, 416 &mut self,
463 flash: &mut F, 417 state_flash: &mut F,
464 aligned: &mut [u8], 418 aligned: &mut [u8],
465 ) -> Result<(), FirmwareUpdaterError> { 419 ) -> Result<(), FirmwareUpdaterError> {
466 assert_eq!(aligned.len(), F::WRITE_SIZE); 420 assert_eq!(aligned.len(), F::WRITE_SIZE);
467 self.set_magic_blocking(aligned, BOOT_MAGIC, flash) 421 self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash)
468 } 422 }
469 423
470 fn set_magic_blocking<F: NorFlash>( 424 fn set_magic_blocking<F: NorFlash>(
471 &mut self, 425 &mut self,
472 aligned: &mut [u8], 426 aligned: &mut [u8],
473 magic: u8, 427 magic: u8,
474 flash: &mut F, 428 state_flash: &mut F,
475 ) -> Result<(), FirmwareUpdaterError> { 429 ) -> Result<(), FirmwareUpdaterError> {
476 flash.read(self.state.from as u32, aligned)?; 430 self.state.read_blocking(state_flash, 0, aligned)?;
477 431
478 if aligned.iter().any(|&b| b != magic) { 432 if aligned.iter().any(|&b| b != magic) {
479 aligned.fill(0); 433 // Read progress validity
434 self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
435
436 // FIXME: Do not make this assumption.
437 const STATE_ERASE_VALUE: u8 = 0xFF;
438
439 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
440 // The current progress validity marker is invalid
441 } else {
442 // Invalidate progress
443 aligned.fill(!STATE_ERASE_VALUE);
444 self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
445 }
480 446
481 flash.write(self.state.from as u32, aligned)?; 447 // Clear magic and progress
482 flash.erase(self.state.from as u32, self.state.to as u32)?; 448 self.state.wipe_blocking(state_flash)?;
483 449
450 // Set magic
484 aligned.fill(magic); 451 aligned.fill(magic);
485 flash.write(self.state.from as u32, aligned)?; 452 self.state.write_blocking(state_flash, 0, aligned)?;
486 } 453 }
487 Ok(()) 454 Ok(())
488 } 455 }
@@ -498,40 +465,26 @@ impl FirmwareUpdater {
498 &mut self, 465 &mut self,
499 offset: usize, 466 offset: usize,
500 data: &[u8], 467 data: &[u8],
501 flash: &mut F, 468 dfu_flash: &mut F,
502 block_size: usize,
503 ) -> Result<(), FirmwareUpdaterError> { 469 ) -> Result<(), FirmwareUpdaterError> {
504 assert!(data.len() >= F::ERASE_SIZE); 470 assert!(data.len() >= F::ERASE_SIZE);
505 471
506 flash.erase( 472 self.dfu
507 (self.dfu.from + offset) as u32, 473 .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?;
508 (self.dfu.from + offset + data.len()) as u32,
509 )?;
510 474
511 trace!( 475 self.dfu.write_blocking(dfu_flash, offset as u32, data)?;
512 "Erased from {} to {}",
513 self.dfu.from + offset,
514 self.dfu.from + offset + data.len()
515 );
516
517 FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?;
518 476
519 Ok(()) 477 Ok(())
520 } 478 }
521 479
522 /// Prepare for an incoming DFU update by erasing the entire DFU area and 480 /// Prepare for an incoming DFU update by erasing the entire DFU area and
523 /// returning a `FirmwareWriter`. 481 /// returning its `Partition`.
524 /// 482 ///
525 /// Using this instead of `write_firmware_blocking` allows for an optimized 483 /// Using this instead of `write_firmware_blocking` allows for an optimized
526 /// API in exchange for added complexity. 484 /// API in exchange for added complexity.
527 pub fn prepare_update_blocking<F: NorFlash>( 485 pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<Partition, FirmwareUpdaterError> {
528 &mut self, 486 self.dfu.wipe_blocking(flash)?;
529 flash: &mut F,
530 ) -> Result<FirmwareWriter, FirmwareUpdaterError> {
531 flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?;
532
533 trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
534 487
535 Ok(FirmwareWriter(self.dfu)) 488 Ok(self.dfu)
536 } 489 }
537} 490}
diff --git a/embassy-boot/boot/src/firmware_writer.rs b/embassy-boot/boot/src/firmware_writer.rs
deleted file mode 100644
index f992021bb..000000000
--- a/embassy-boot/boot/src/firmware_writer.rs
+++ /dev/null
@@ -1,97 +0,0 @@
1use embedded_storage::nor_flash::NorFlash;
2use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
3
4use crate::Partition;
5
6/// FirmwareWriter allows writing blocks to an already erased flash.
7pub struct FirmwareWriter(pub(crate) Partition);
8
9impl FirmwareWriter {
10 /// Write data to a flash page.
11 ///
12 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
13 ///
14 /// # Safety
15 ///
16 /// Failing to meet alignment and size requirements may result in a panic.
17 pub async fn write_block<F: AsyncNorFlash>(
18 &mut self,
19 offset: usize,
20 data: &[u8],
21 flash: &mut F,
22 block_size: usize,
23 ) -> Result<(), F::Error> {
24 trace!(
25 "Writing firmware at offset 0x{:x} len {}",
26 self.0.from + offset,
27 data.len()
28 );
29
30 let mut write_offset = self.0.from + offset;
31 for chunk in data.chunks(block_size) {
32 trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
33 flash.write(write_offset as u32, chunk).await?;
34 write_offset += chunk.len();
35 }
36 /*
37 trace!("Wrote data, reading back for verification");
38
39 let mut buf: [u8; 4096] = [0; 4096];
40 let mut data_offset = 0;
41 let mut read_offset = self.dfu.from + offset;
42 for chunk in buf.chunks_mut(block_size) {
43 flash.read(read_offset as u32, chunk).await?;
44 trace!("Read chunk at {}: {:?}", read_offset, chunk);
45 assert_eq!(&data[data_offset..data_offset + block_size], chunk);
46 read_offset += chunk.len();
47 data_offset += chunk.len();
48 }
49 */
50
51 Ok(())
52 }
53
54 /// Write data to a flash page.
55 ///
56 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
57 ///
58 /// # Safety
59 ///
60 /// Failing to meet alignment and size requirements may result in a panic.
61 pub fn write_block_blocking<F: NorFlash>(
62 &mut self,
63 offset: usize,
64 data: &[u8],
65 flash: &mut F,
66 block_size: usize,
67 ) -> Result<(), F::Error> {
68 trace!(
69 "Writing firmware at offset 0x{:x} len {}",
70 self.0.from + offset,
71 data.len()
72 );
73
74 let mut write_offset = self.0.from + offset;
75 for chunk in data.chunks(block_size) {
76 trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
77 flash.write(write_offset as u32, chunk)?;
78 write_offset += chunk.len();
79 }
80 /*
81 trace!("Wrote data, reading back for verification");
82
83 let mut buf: [u8; 4096] = [0; 4096];
84 let mut data_offset = 0;
85 let mut read_offset = self.dfu.from + offset;
86 for chunk in buf.chunks_mut(block_size) {
87 flash.read(read_offset as u32, chunk).await?;
88 trace!("Read chunk at {}: {:?}", read_offset, chunk);
89 assert_eq!(&data[data_offset..data_offset + block_size], chunk);
90 read_offset += chunk.len();
91 data_offset += chunk.len();
92 }
93 */
94
95 Ok(())
96 }
97}
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index a2259411f..d53c613a3 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -7,12 +7,11 @@ mod fmt;
7 7
8mod boot_loader; 8mod boot_loader;
9mod firmware_updater; 9mod firmware_updater;
10mod firmware_writer; 10mod mem_flash;
11mod partition; 11mod partition;
12 12
13pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; 13pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig};
14pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; 14pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError};
15pub use firmware_writer::FirmwareWriter;
16pub use partition::Partition; 15pub use partition::Partition;
17 16
18pub(crate) const BOOT_MAGIC: u8 = 0xD0; 17pub(crate) const BOOT_MAGIC: u8 = 0xD0;
@@ -46,13 +45,10 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
46 45
47#[cfg(test)] 46#[cfg(test)]
48mod tests { 47mod tests {
49 use core::convert::Infallible;
50
51 use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
52 use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
53 use futures::executor::block_on; 48 use futures::executor::block_on;
54 49
55 use super::*; 50 use super::*;
51 use crate::mem_flash::MemFlash;
56 52
57 /* 53 /*
58 #[test] 54 #[test]
@@ -75,8 +71,8 @@ mod tests {
75 const ACTIVE: Partition = Partition::new(4096, 61440); 71 const ACTIVE: Partition = Partition::new(4096, 61440);
76 const DFU: Partition = Partition::new(61440, 122880); 72 const DFU: Partition = Partition::new(61440, 122880);
77 73
78 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 74 let mut flash = MemFlash::<131072, 4096, 4>::default();
79 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); 75 flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
80 let mut flash = SingleFlashConfig::new(&mut flash); 76 let mut flash = SingleFlashConfig::new(&mut flash);
81 77
82 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 78 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
@@ -95,21 +91,21 @@ mod tests {
95 const STATE: Partition = Partition::new(0, 4096); 91 const STATE: Partition = Partition::new(0, 4096);
96 const ACTIVE: Partition = Partition::new(4096, 61440); 92 const ACTIVE: Partition = Partition::new(4096, 61440);
97 const DFU: Partition = Partition::new(61440, 122880); 93 const DFU: Partition = Partition::new(61440, 122880);
98 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 94 let mut flash = MemFlash::<131072, 4096, 4>::random();
99 95
100 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 96 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
101 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 97 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
102 let mut aligned = [0; 4]; 98 let mut aligned = [0; 4];
103 99
104 for i in ACTIVE.from..ACTIVE.to { 100 for i in ACTIVE.from..ACTIVE.to {
105 flash.0[i] = original[i - ACTIVE.from]; 101 flash.mem[i] = original[i - ACTIVE.from];
106 } 102 }
107 103
108 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 104 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
109 let mut updater = FirmwareUpdater::new(DFU, STATE); 105 let mut updater = FirmwareUpdater::new(DFU, STATE);
110 let mut offset = 0; 106 let mut offset = 0;
111 for chunk in update.chunks(4096) { 107 for chunk in update.chunks(4096) {
112 block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap(); 108 block_on(updater.write_firmware(offset, chunk, &mut flash)).unwrap();
113 offset += chunk.len(); 109 offset += chunk.len();
114 } 110 }
115 block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); 111 block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap();
@@ -124,12 +120,12 @@ mod tests {
124 ); 120 );
125 121
126 for i in ACTIVE.from..ACTIVE.to { 122 for i in ACTIVE.from..ACTIVE.to {
127 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i); 123 assert_eq!(flash.mem[i], update[i - ACTIVE.from], "Index {}", i);
128 } 124 }
129 125
130 // First DFU page is untouched 126 // First DFU page is untouched
131 for i in DFU.from + 4096..DFU.to { 127 for i in DFU.from + 4096..DFU.to {
132 assert_eq!(flash.0[i], original[i - DFU.from - 4096], "Index {}", i); 128 assert_eq!(flash.mem[i], original[i - DFU.from - 4096], "Index {}", i);
133 } 129 }
134 130
135 // Running again should cause a revert 131 // Running again should cause a revert
@@ -141,12 +137,12 @@ mod tests {
141 ); 137 );
142 138
143 for i in ACTIVE.from..ACTIVE.to { 139 for i in ACTIVE.from..ACTIVE.to {
144 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i); 140 assert_eq!(flash.mem[i], original[i - ACTIVE.from], "Index {}", i);
145 } 141 }
146 142
147 // Last page is untouched 143 // Last page is untouched
148 for i in DFU.from..DFU.to - 4096 { 144 for i in DFU.from..DFU.to - 4096 {
149 assert_eq!(flash.0[i], update[i - DFU.from], "Index {}", i); 145 assert_eq!(flash.mem[i], update[i - DFU.from], "Index {}", i);
150 } 146 }
151 147
152 // Mark as booted 148 // Mark as booted
@@ -166,23 +162,23 @@ mod tests {
166 const ACTIVE: Partition = Partition::new(4096, 16384); 162 const ACTIVE: Partition = Partition::new(4096, 16384);
167 const DFU: Partition = Partition::new(0, 16384); 163 const DFU: Partition = Partition::new(0, 16384);
168 164
169 let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); 165 let mut active = MemFlash::<16384, 4096, 8>::random();
170 let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); 166 let mut dfu = MemFlash::<16384, 2048, 8>::random();
171 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 167 let mut state = MemFlash::<4096, 128, 4>::random();
172 let mut aligned = [0; 4]; 168 let mut aligned = [0; 4];
173 169
174 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 170 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
175 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 171 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
176 172
177 for i in ACTIVE.from..ACTIVE.to { 173 for i in ACTIVE.from..ACTIVE.to {
178 active.0[i] = original[i - ACTIVE.from]; 174 active.mem[i] = original[i - ACTIVE.from];
179 } 175 }
180 176
181 let mut updater = FirmwareUpdater::new(DFU, STATE); 177 let mut updater = FirmwareUpdater::new(DFU, STATE);
182 178
183 let mut offset = 0; 179 let mut offset = 0;
184 for chunk in update.chunks(2048) { 180 for chunk in update.chunks(2048) {
185 block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); 181 block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap();
186 offset += chunk.len(); 182 offset += chunk.len();
187 } 183 }
188 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); 184 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
@@ -203,12 +199,12 @@ mod tests {
203 ); 199 );
204 200
205 for i in ACTIVE.from..ACTIVE.to { 201 for i in ACTIVE.from..ACTIVE.to {
206 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); 202 assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i);
207 } 203 }
208 204
209 // First DFU page is untouched 205 // First DFU page is untouched
210 for i in DFU.from + 4096..DFU.to { 206 for i in DFU.from + 4096..DFU.to {
211 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); 207 assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i);
212 } 208 }
213 } 209 }
214 210
@@ -220,22 +216,22 @@ mod tests {
220 const DFU: Partition = Partition::new(0, 16384); 216 const DFU: Partition = Partition::new(0, 16384);
221 217
222 let mut aligned = [0; 4]; 218 let mut aligned = [0; 4];
223 let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); 219 let mut active = MemFlash::<16384, 2048, 4>::random();
224 let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); 220 let mut dfu = MemFlash::<16384, 4096, 8>::random();
225 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 221 let mut state = MemFlash::<4096, 128, 4>::random();
226 222
227 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 223 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
228 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 224 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
229 225
230 for i in ACTIVE.from..ACTIVE.to { 226 for i in ACTIVE.from..ACTIVE.to {
231 active.0[i] = original[i - ACTIVE.from]; 227 active.mem[i] = original[i - ACTIVE.from];
232 } 228 }
233 229
234 let mut updater = FirmwareUpdater::new(DFU, STATE); 230 let mut updater = FirmwareUpdater::new(DFU, STATE);
235 231
236 let mut offset = 0; 232 let mut offset = 0;
237 for chunk in update.chunks(4096) { 233 for chunk in update.chunks(4096) {
238 block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); 234 block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap();
239 offset += chunk.len(); 235 offset += chunk.len();
240 } 236 }
241 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); 237 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
@@ -255,12 +251,12 @@ mod tests {
255 ); 251 );
256 252
257 for i in ACTIVE.from..ACTIVE.to { 253 for i in ACTIVE.from..ACTIVE.to {
258 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); 254 assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i);
259 } 255 }
260 256
261 // First DFU page is untouched 257 // First DFU page is untouched
262 for i in DFU.from + 4096..DFU.to { 258 for i in DFU.from + 4096..DFU.to {
263 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); 259 assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i);
264 } 260 }
265 } 261 }
266 262
@@ -290,13 +286,13 @@ mod tests {
290 286
291 const STATE: Partition = Partition::new(0, 4096); 287 const STATE: Partition = Partition::new(0, 4096);
292 const DFU: Partition = Partition::new(4096, 8192); 288 const DFU: Partition = Partition::new(4096, 8192);
293 let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); 289 let mut flash = MemFlash::<8192, 4096, 4>::default();
294 290
295 let firmware_len = firmware.len(); 291 let firmware_len = firmware.len();
296 292
297 let mut write_buf = [0; 4096]; 293 let mut write_buf = [0; 4096];
298 write_buf[0..firmware_len].copy_from_slice(firmware); 294 write_buf[0..firmware_len].copy_from_slice(firmware);
299 NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); 295 DFU.write_blocking(&mut flash, 0, &write_buf).unwrap();
300 296
301 // On with the test 297 // On with the test
302 298
@@ -313,112 +309,4 @@ mod tests {
313 )) 309 ))
314 .is_ok()); 310 .is_ok());
315 } 311 }
316 struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]);
317
318 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
319 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
320 {
321 const WRITE_SIZE: usize = WRITE_SIZE;
322 const ERASE_SIZE: usize = ERASE_SIZE;
323 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
324 let from = from as usize;
325 let to = to as usize;
326 assert!(from % ERASE_SIZE == 0);
327 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
328 for i in from..to {
329 self.0[i] = 0xFF;
330 }
331 Ok(())
332 }
333
334 fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
335 assert!(data.len() % WRITE_SIZE == 0);
336 assert!(offset as usize % WRITE_SIZE == 0);
337 assert!(offset as usize + data.len() <= SIZE);
338
339 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
340
341 Ok(())
342 }
343 }
344
345 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
346 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
347 {
348 type Error = Infallible;
349 }
350
351 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
352 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
353 {
354 const READ_SIZE: usize = 1;
355
356 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
357 let len = buf.len();
358 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
359 Ok(())
360 }
361
362 fn capacity(&self) -> usize {
363 SIZE
364 }
365 }
366
367 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> super::Flash
368 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
369 {
370 const BLOCK_SIZE: usize = ERASE_SIZE;
371 const ERASE_VALUE: u8 = 0xFF;
372 }
373
374 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
375 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
376 {
377 const READ_SIZE: usize = 1;
378
379 async fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
380 let len = buf.len();
381 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
382 Ok(())
383 }
384
385 fn capacity(&self) -> usize {
386 SIZE
387 }
388 }
389
390 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
391 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
392 {
393 const WRITE_SIZE: usize = WRITE_SIZE;
394 const ERASE_SIZE: usize = ERASE_SIZE;
395
396 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
397 let from = from as usize;
398 let to = to as usize;
399 assert!(from % ERASE_SIZE == 0);
400 assert!(to % ERASE_SIZE == 0);
401 for i in from..to {
402 self.0[i] = 0xFF;
403 }
404 Ok(())
405 }
406
407 async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
408 info!("Writing {} bytes to 0x{:x}", data.len(), offset);
409 assert!(data.len() % WRITE_SIZE == 0);
410 assert!(offset as usize % WRITE_SIZE == 0);
411 assert!(
412 offset as usize + data.len() <= SIZE,
413 "OFFSET: {}, LEN: {}, FLASH SIZE: {}",
414 offset,
415 data.len(),
416 SIZE
417 );
418
419 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
420
421 Ok(())
422 }
423 }
424} 312}
diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs
new file mode 100644
index 000000000..828aad9d9
--- /dev/null
+++ b/embassy-boot/boot/src/mem_flash.rs
@@ -0,0 +1,156 @@
1#![allow(unused)]
2
3use core::ops::{Bound, Range, RangeBounds};
4
5use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
6use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
7
8use crate::Flash;
9
10pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> {
11 pub mem: [u8; SIZE],
12 pub pending_write_successes: Option<usize>,
13}
14
15#[derive(Debug)]
16pub struct MemFlashError;
17
18impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> {
19 pub const fn new(fill: u8) -> Self {
20 Self {
21 mem: [fill; SIZE],
22 pending_write_successes: None,
23 }
24 }
25
26 #[cfg(test)]
27 pub fn random() -> Self {
28 let mut mem = [0; SIZE];
29 for byte in mem.iter_mut() {
30 *byte = rand::random::<u8>();
31 }
32 Self {
33 mem,
34 pending_write_successes: None,
35 }
36 }
37}
38
39impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Default
40 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
41{
42 fn default() -> Self {
43 Self::new(0xFF)
44 }
45}
46
47impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Flash
48 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
49{
50 const BLOCK_SIZE: usize = ERASE_SIZE;
51 const ERASE_VALUE: u8 = 0xFF;
52}
53
54impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
55 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
56{
57 type Error = MemFlashError;
58}
59
60impl NorFlashError for MemFlashError {
61 fn kind(&self) -> NorFlashErrorKind {
62 NorFlashErrorKind::Other
63 }
64}
65
66impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
67 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
68{
69 const READ_SIZE: usize = 1;
70
71 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
72 let len = bytes.len();
73 bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]);
74 Ok(())
75 }
76
77 fn capacity(&self) -> usize {
78 SIZE
79 }
80}
81
82impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
83 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
84{
85 const WRITE_SIZE: usize = WRITE_SIZE;
86 const ERASE_SIZE: usize = ERASE_SIZE;
87
88 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
89 let from = from as usize;
90 let to = to as usize;
91 assert!(from % ERASE_SIZE == 0);
92 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
93 for i in from..to {
94 self.mem[i] = 0xFF;
95 }
96 Ok(())
97 }
98
99 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
100 let offset = offset as usize;
101 assert!(bytes.len() % WRITE_SIZE == 0);
102 assert!(offset % WRITE_SIZE == 0);
103 assert!(offset + bytes.len() <= SIZE);
104
105 if let Some(pending_successes) = self.pending_write_successes {
106 if pending_successes > 0 {
107 self.pending_write_successes = Some(pending_successes - 1);
108 } else {
109 return Err(MemFlashError);
110 }
111 }
112
113 for ((offset, mem_byte), new_byte) in self
114 .mem
115 .iter_mut()
116 .enumerate()
117 .skip(offset)
118 .take(bytes.len())
119 .zip(bytes)
120 {
121 assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset);
122 *mem_byte = *new_byte;
123 }
124
125 Ok(())
126 }
127}
128
129impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
130 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
131{
132 const READ_SIZE: usize = 1;
133
134 async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
135 <Self as ReadNorFlash>::read(self, offset, bytes)
136 }
137
138 fn capacity(&self) -> usize {
139 <Self as ReadNorFlash>::capacity(self)
140 }
141}
142
143impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
144 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
145{
146 const WRITE_SIZE: usize = WRITE_SIZE;
147 const ERASE_SIZE: usize = ERASE_SIZE;
148
149 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
150 <Self as NorFlash>::erase(self, from, to)
151 }
152
153 async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
154 <Self as NorFlash>::write(self, offset, bytes)
155 }
156}
diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs
index 46f80a23c..ac6b0ed0f 100644
--- a/embassy-boot/boot/src/partition.rs
+++ b/embassy-boot/boot/src/partition.rs
@@ -1,10 +1,13 @@
1use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
2use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
3
1/// A region in flash used by the bootloader. 4/// A region in flash used by the bootloader.
2#[derive(Copy, Clone, Debug)] 5#[derive(Copy, Clone, Debug)]
3#[cfg_attr(feature = "defmt", derive(defmt::Format))] 6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
4pub struct Partition { 7pub struct Partition {
5 /// Start of the flash region. 8 /// The offset into the flash where the partition starts.
6 pub from: usize, 9 pub from: usize,
7 /// End of the flash region. 10 /// The offset into the flash where the partition ends.
8 pub to: usize, 11 pub to: usize,
9} 12}
10 13
@@ -14,9 +17,124 @@ impl Partition {
14 Self { from, to } 17 Self { from, to }
15 } 18 }
16 19
17 /// Return the length of the partition 20 /// Return the size of the partition
18 #[allow(clippy::len_without_is_empty)] 21 #[allow(clippy::len_without_is_empty)]
19 pub const fn len(&self) -> usize { 22 pub const fn len(&self) -> usize {
20 self.to - self.from 23 self.to - self.from
21 } 24 }
25
26 /// Read from the partition on the provided flash
27 pub async fn read<F: AsyncReadNorFlash>(
28 &self,
29 flash: &mut F,
30 offset: u32,
31 bytes: &mut [u8],
32 ) -> Result<(), F::Error> {
33 let offset = self.from as u32 + offset;
34 flash.read(offset, bytes).await
35 }
36
37 /// Write to the partition on the provided flash
38 pub async fn write<F: AsyncNorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
39 let offset = self.from as u32 + offset;
40 flash.write(offset, bytes).await?;
41 trace!("Wrote from 0x{:x} len {}", offset, bytes.len());
42 Ok(())
43 }
44
45 /// Erase part of the partition on the provided flash
46 pub async fn erase<F: AsyncNorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
47 let from = self.from as u32 + from;
48 let to = self.from as u32 + to;
49 flash.erase(from, to).await?;
50 trace!("Erased from 0x{:x} to 0x{:x}", from, to);
51 Ok(())
52 }
53
54 /// Erase the entire partition
55 pub(crate) async fn wipe<F: AsyncNorFlash>(&self, flash: &mut F) -> Result<(), F::Error> {
56 let from = self.from as u32;
57 let to = self.to as u32;
58 flash.erase(from, to).await?;
59 trace!("Wiped from 0x{:x} to 0x{:x}", from, to);
60 Ok(())
61 }
62
63 /// Read from the partition on the provided flash
64 pub fn read_blocking<F: ReadNorFlash>(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> {
65 let offset = self.from as u32 + offset;
66 flash.read(offset, bytes)
67 }
68
69 /// Write to the partition on the provided flash
70 pub fn write_blocking<F: NorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
71 let offset = self.from as u32 + offset;
72 flash.write(offset, bytes)?;
73 trace!("Wrote from 0x{:x} len {}", offset, bytes.len());
74 Ok(())
75 }
76
77 /// Erase part of the partition on the provided flash
78 pub fn erase_blocking<F: NorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
79 let from = self.from as u32 + from;
80 let to = self.from as u32 + to;
81 flash.erase(from, to)?;
82 trace!("Erased from 0x{:x} to 0x{:x}", from, to);
83 Ok(())
84 }
85
86 /// Erase the entire partition
87 pub(crate) fn wipe_blocking<F: NorFlash>(&self, flash: &mut F) -> Result<(), F::Error> {
88 let from = self.from as u32;
89 let to = self.to as u32;
90 flash.erase(from, to)?;
91 trace!("Wiped from 0x{:x} to 0x{:x}", from, to);
92 Ok(())
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use crate::mem_flash::MemFlash;
99 use crate::Partition;
100
101 #[test]
102 fn can_erase() {
103 let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
104 let partition = Partition::new(256, 512);
105
106 partition.erase_blocking(&mut flash, 64, 192).unwrap();
107
108 for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) {
109 assert_eq!(0x00, byte, "Index {}", index);
110 }
111
112 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) {
113 assert_eq!(0xFF, byte, "Index {}", index);
114 }
115
116 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) {
117 assert_eq!(0x00, byte, "Index {}", index);
118 }
119 }
120
121 #[test]
122 fn can_wipe() {
123 let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
124 let partition = Partition::new(256, 512);
125
126 partition.wipe_blocking(&mut flash).unwrap();
127
128 for (index, byte) in flash.mem.iter().copied().enumerate().take(256) {
129 assert_eq!(0x00, byte, "Index {}", index);
130 }
131
132 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) {
133 assert_eq!(0xFF, byte, "Index {}", index);
134 }
135
136 for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) {
137 assert_eq!(0x00, byte, "Index {}", index);
138 }
139 }
22} 140}