aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/modules/ROOT/pages/bootloader.adoc3
-rw-r--r--embassy-boot/boot/src/lib.rs344
-rw-r--r--embassy-boot/nrf/src/lib.rs14
-rw-r--r--embassy-boot/stm32/src/lib.rs20
-rw-r--r--embassy-boot/stm32/src/main.rs4
-rw-r--r--examples/boot/nrf/src/bin/a.rs2
-rw-r--r--examples/boot/stm32l0/src/bin/a.rs2
-rw-r--r--examples/boot/stm32l1/src/bin/a.rs2
-rw-r--r--examples/boot/stm32l4/src/bin/a.rs2
-rw-r--r--examples/boot/stm32wl/src/bin/a.rs2
10 files changed, 312 insertions, 83 deletions
diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc
index 1a984d6dc..7539774c4 100644
--- a/docs/modules/ROOT/pages/bootloader.adoc
+++ b/docs/modules/ROOT/pages/bootloader.adoc
@@ -27,6 +27,7 @@ The bootloader divides the storage into 4 main partitions, configured by a linke
27* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. 27* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application.
28* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped. 28* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped.
29 29
30The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash, but they have to support compatible page sizes. 30The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes.
31The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined.
31 32
32The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice. 33The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice.
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 554709250..87bb973a7 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -62,6 +62,7 @@ where
62 62
63pub trait FlashConfig { 63pub trait FlashConfig {
64 const BLOCK_SIZE: usize; 64 const BLOCK_SIZE: usize;
65 const ERASE_VALUE: u8;
65 type FLASH: NorFlash + ReadNorFlash; 66 type FLASH: NorFlash + ReadNorFlash;
66 67
67 fn flash(&mut self) -> &mut Self::FLASH; 68 fn flash(&mut self) -> &mut Self::FLASH;
@@ -83,7 +84,9 @@ pub trait FlashProvider {
83 84
84/// BootLoader works with any flash implementing embedded_storage and can also work with 85/// BootLoader works with any flash implementing embedded_storage and can also work with
85/// different page sizes and flash write sizes. 86/// different page sizes and flash write sizes.
86pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> { 87///
88/// The PAGE_SIZE const parameter must be a multiple of the ACTIVE and DFU page sizes.
89pub struct BootLoader<const PAGE_SIZE: usize> {
87 // Page with current state of bootloader. The state partition has the following format: 90 // Page with current state of bootloader. The state partition has the following format:
88 // | Range | Description | 91 // | Range | Description |
89 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 92 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
@@ -95,16 +98,12 @@ pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERA
95 dfu: Partition, 98 dfu: Partition,
96} 99}
97 100
98impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> 101impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
99 BootLoader<PAGE_SIZE, WRITE_SIZE, ERASE_VALUE>
100{
101 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 102 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
102 assert_eq!(active.len() % PAGE_SIZE, 0); 103 assert_eq!(active.len() % PAGE_SIZE, 0);
103 assert_eq!(dfu.len() % PAGE_SIZE, 0); 104 assert_eq!(dfu.len() % PAGE_SIZE, 0);
104 // DFU partition must have an extra page 105 // DFU partition must have an extra page
105 assert!(dfu.len() - active.len() >= PAGE_SIZE); 106 assert!(dfu.len() - active.len() >= PAGE_SIZE);
106 // Ensure we have enough progress pages to store copy progress
107 assert!(active.len() / PAGE_SIZE >= (state.len() - WRITE_SIZE) / PAGE_SIZE);
108 Self { active, dfu, state } 107 Self { active, dfu, state }
109 } 108 }
110 109
@@ -195,7 +194,19 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
195 /// | DFU | 3 | 3 | 2 | 1 | 3 | 194 /// | DFU | 3 | 3 | 2 | 1 | 3 |
196 /// +-----------+--------------+--------+--------+--------+--------+ 195 /// +-----------+--------------+--------+--------+--------+--------+
197 /// 196 ///
198 pub fn prepare_boot<P: FlashProvider>(&mut self, p: &mut P) -> Result<State, BootError> { 197 pub fn prepare_boot<P: FlashProvider>(&mut self, p: &mut P) -> Result<State, BootError>
198 where
199 [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
200 [(); <<P as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
201 {
202 // Ensure we have enough progress pages to store copy progress
203 assert!(
204 self.active.len() / PAGE_SIZE
205 <= (self.state.len()
206 - <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE)
207 / <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE
208 );
209
199 // Copy contents from partition N to active 210 // Copy contents from partition N to active
200 let state = self.read_state(p.state())?; 211 let state = self.read_state(p.state())?;
201 match state { 212 match state {
@@ -214,10 +225,16 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
214 225
215 // Overwrite magic and reset progress 226 // Overwrite magic and reset progress
216 let fstate = p.state().flash(); 227 let fstate = p.state().flash();
217 let aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]); 228 let aligned = Aligned(
229 [!P::STATE::ERASE_VALUE;
230 <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE],
231 );
218 fstate.write(self.state.from as u32, &aligned.0)?; 232 fstate.write(self.state.from as u32, &aligned.0)?;
219 fstate.erase(self.state.from as u32, self.state.to as u32)?; 233 fstate.erase(self.state.from as u32, self.state.to as u32)?;
220 let aligned = Aligned([BOOT_MAGIC; WRITE_SIZE]); 234 let aligned = Aligned(
235 [BOOT_MAGIC;
236 <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE],
237 );
221 fstate.write(self.state.from as u32, &aligned.0)?; 238 fstate.write(self.state.from as u32, &aligned.0)?;
222 } 239 }
223 } 240 }
@@ -226,33 +243,44 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
226 Ok(state) 243 Ok(state)
227 } 244 }
228 245
229 fn is_swapped<P: FlashConfig>(&mut self, p: &mut P) -> Result<bool, BootError> { 246 fn is_swapped<P: FlashConfig>(&mut self, p: &mut P) -> Result<bool, BootError>
230 let page_count = self.active.len() / PAGE_SIZE; 247 where
248 [(); P::FLASH::WRITE_SIZE]:,
249 {
250 let page_count = self.active.len() / P::FLASH::ERASE_SIZE;
231 let progress = self.current_progress(p)?; 251 let progress = self.current_progress(p)?;
232 252
233 Ok(progress >= page_count * 2) 253 Ok(progress >= page_count * 2)
234 } 254 }
235 255
236 fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError> { 256 fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError>
237 let max_index = ((self.state.len() - WRITE_SIZE) / WRITE_SIZE) - 1; 257 where
258 [(); P::FLASH::WRITE_SIZE]:,
259 {
260 let write_size = P::FLASH::WRITE_SIZE;
261 let max_index = ((self.state.len() - write_size) / write_size) - 1;
238 let flash = p.flash(); 262 let flash = p.flash();
239 let mut aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]); 263 let mut aligned = Aligned([!P::ERASE_VALUE; P::FLASH::WRITE_SIZE]);
240 for i in 0..max_index { 264 for i in 0..max_index {
241 flash.read( 265 flash.read(
242 (self.state.from + WRITE_SIZE + i * WRITE_SIZE) as u32, 266 (self.state.from + write_size + i * write_size) as u32,
243 &mut aligned.0, 267 &mut aligned.0,
244 )?; 268 )?;
245 if aligned.0 == [ERASE_VALUE; WRITE_SIZE] { 269 if aligned.0 == [P::ERASE_VALUE; P::FLASH::WRITE_SIZE] {
246 return Ok(i); 270 return Ok(i);
247 } 271 }
248 } 272 }
249 Ok(max_index) 273 Ok(max_index)
250 } 274 }
251 275
252 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> { 276 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError>
277 where
278 [(); P::FLASH::WRITE_SIZE]:,
279 {
253 let flash = p.flash(); 280 let flash = p.flash();
254 let w = self.state.from + WRITE_SIZE + idx * WRITE_SIZE; 281 let write_size = P::FLASH::WRITE_SIZE;
255 let aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]); 282 let w = self.state.from + write_size + idx * write_size;
283 let aligned = Aligned([!P::ERASE_VALUE; P::FLASH::WRITE_SIZE]);
256 flash.write(w as u32, &aligned.0)?; 284 flash.write(w as u32, &aligned.0)?;
257 Ok(()) 285 Ok(())
258 } 286 }
@@ -271,7 +299,10 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
271 from_page: usize, 299 from_page: usize,
272 to_page: usize, 300 to_page: usize,
273 p: &mut P, 301 p: &mut P,
274 ) -> Result<(), BootError> { 302 ) -> Result<(), BootError>
303 where
304 [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
305 {
275 let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; 306 let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
276 if self.current_progress(p.state())? <= idx { 307 if self.current_progress(p.state())? <= idx {
277 let mut offset = from_page; 308 let mut offset = from_page;
@@ -300,7 +331,10 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
300 from_page: usize, 331 from_page: usize,
301 to_page: usize, 332 to_page: usize,
302 p: &mut P, 333 p: &mut P,
303 ) -> Result<(), BootError> { 334 ) -> Result<(), BootError>
335 where
336 [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
337 {
304 let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; 338 let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
305 if self.current_progress(p.state())? <= idx { 339 if self.current_progress(p.state())? <= idx {
306 let mut offset = from_page; 340 let mut offset = from_page;
@@ -323,7 +357,10 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
323 Ok(()) 357 Ok(())
324 } 358 }
325 359
326 fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> { 360 fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError>
361 where
362 [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
363 {
327 let page_count = self.active.len() / PAGE_SIZE; 364 let page_count = self.active.len() / PAGE_SIZE;
328 trace!("Page count: {}", page_count); 365 trace!("Page count: {}", page_count);
329 for page in 0..page_count { 366 for page in 0..page_count {
@@ -344,7 +381,10 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
344 Ok(()) 381 Ok(())
345 } 382 }
346 383
347 fn revert<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> { 384 fn revert<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError>
385 where
386 [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
387 {
348 let page_count = self.active.len() / PAGE_SIZE; 388 let page_count = self.active.len() / PAGE_SIZE;
349 for page in 0..page_count { 389 for page in 0..page_count {
350 // Copy the bad active page to the DFU page 390 // Copy the bad active page to the DFU page
@@ -361,12 +401,15 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
361 Ok(()) 401 Ok(())
362 } 402 }
363 403
364 fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError> { 404 fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError>
365 let mut magic: [u8; WRITE_SIZE] = [0; WRITE_SIZE]; 405 where
406 [(); P::FLASH::WRITE_SIZE]:,
407 {
408 let mut magic: [u8; P::FLASH::WRITE_SIZE] = [0; P::FLASH::WRITE_SIZE];
366 let flash = p.flash(); 409 let flash = p.flash();
367 flash.read(self.state.from as u32, &mut magic)?; 410 flash.read(self.state.from as u32, &mut magic)?;
368 411
369 if magic == [SWAP_MAGIC; WRITE_SIZE] { 412 if magic == [SWAP_MAGIC; P::FLASH::WRITE_SIZE] {
370 Ok(State::Swap) 413 Ok(State::Swap)
371 } else { 414 } else {
372 Ok(State::Boot) 415 Ok(State::Boot)
@@ -375,14 +418,14 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
375} 418}
376 419
377/// Convenience provider that uses a single flash for everything 420/// Convenience provider that uses a single flash for everything
378pub struct SingleFlashProvider<'a, F> 421pub struct SingleFlashProvider<'a, F, const ERASE_VALUE: u8 = 0xFF>
379where 422where
380 F: NorFlash + ReadNorFlash, 423 F: NorFlash + ReadNorFlash,
381{ 424{
382 config: SingleFlashConfig<'a, F>, 425 config: SingleFlashConfig<'a, F, ERASE_VALUE>,
383} 426}
384 427
385impl<'a, F> SingleFlashProvider<'a, F> 428impl<'a, F, const ERASE_VALUE: u8> SingleFlashProvider<'a, F, ERASE_VALUE>
386where 429where
387 F: NorFlash + ReadNorFlash, 430 F: NorFlash + ReadNorFlash,
388{ 431{
@@ -393,7 +436,7 @@ where
393 } 436 }
394} 437}
395 438
396pub struct SingleFlashConfig<'a, F> 439pub struct SingleFlashConfig<'a, F, const ERASE_VALUE: u8 = 0xFF>
397where 440where
398 F: NorFlash + ReadNorFlash, 441 F: NorFlash + ReadNorFlash,
399{ 442{
@@ -419,17 +462,66 @@ where
419 } 462 }
420} 463}
421 464
422impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> 465impl<'a, F, const ERASE_VALUE: u8> FlashConfig for SingleFlashConfig<'a, F, ERASE_VALUE>
423where 466where
424 F: NorFlash + ReadNorFlash, 467 F: NorFlash + ReadNorFlash,
425{ 468{
426 const BLOCK_SIZE: usize = F::ERASE_SIZE; 469 const BLOCK_SIZE: usize = F::ERASE_SIZE;
470 const ERASE_VALUE: u8 = ERASE_VALUE;
427 type FLASH = F; 471 type FLASH = F;
428 fn flash(&mut self) -> &mut F { 472 fn flash(&mut self) -> &mut F {
429 self.flash 473 self.flash
430 } 474 }
431} 475}
432 476
477/// Convenience provider that uses a single flash for everything
478pub struct MultiFlashProvider<'a, ACTIVE, STATE, DFU>
479where
480 ACTIVE: NorFlash + ReadNorFlash,
481 STATE: NorFlash + ReadNorFlash,
482 DFU: NorFlash + ReadNorFlash,
483{
484 active: SingleFlashConfig<'a, ACTIVE>,
485 state: SingleFlashConfig<'a, STATE>,
486 dfu: SingleFlashConfig<'a, DFU>,
487}
488
489impl<'a, ACTIVE, STATE, DFU> MultiFlashProvider<'a, ACTIVE, STATE, DFU>
490where
491 ACTIVE: NorFlash + ReadNorFlash,
492 STATE: NorFlash + ReadNorFlash,
493 DFU: NorFlash + ReadNorFlash,
494{
495 pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self {
496 Self {
497 active: SingleFlashConfig { flash: active },
498 state: SingleFlashConfig { flash: state },
499 dfu: SingleFlashConfig { flash: dfu },
500 }
501 }
502}
503
504impl<'a, ACTIVE, STATE, DFU> FlashProvider for MultiFlashProvider<'a, ACTIVE, STATE, DFU>
505where
506 ACTIVE: NorFlash + ReadNorFlash,
507 STATE: NorFlash + ReadNorFlash,
508 DFU: NorFlash + ReadNorFlash,
509{
510 type STATE = SingleFlashConfig<'a, STATE>;
511 type ACTIVE = SingleFlashConfig<'a, ACTIVE>;
512 type DFU = SingleFlashConfig<'a, DFU>;
513
514 fn active(&mut self) -> &mut Self::ACTIVE {
515 &mut self.active
516 }
517 fn dfu(&mut self) -> &mut Self::DFU {
518 &mut self.dfu
519 }
520 fn state(&mut self) -> &mut Self::STATE {
521 &mut self.state
522 }
523}
524
433/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to 525/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
434/// 'mess up' the internal bootloader state 526/// 'mess up' the internal bootloader state
435pub struct FirmwareUpdater { 527pub struct FirmwareUpdater {
@@ -481,7 +573,7 @@ impl FirmwareUpdater {
481 573
482 /// Instruct bootloader that DFU should commence at next boot. 574 /// Instruct bootloader that DFU should commence at next boot.
483 /// Must be provided with an aligned buffer to use for reading and writing magic; 575 /// Must be provided with an aligned buffer to use for reading and writing magic;
484 pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> 576 pub async fn update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error>
485 where 577 where
486 [(); F::WRITE_SIZE]:, 578 [(); F::WRITE_SIZE]:,
487 { 579 {
@@ -592,10 +684,6 @@ mod tests {
592 use embedded_storage_async::nor_flash::AsyncReadNorFlash; 684 use embedded_storage_async::nor_flash::AsyncReadNorFlash;
593 use futures::executor::block_on; 685 use futures::executor::block_on;
594 686
595 const STATE: Partition = Partition::new(0, 4096);
596 const ACTIVE: Partition = Partition::new(4096, 61440);
597 const DFU: Partition = Partition::new(61440, 122880);
598
599 /* 687 /*
600 #[test] 688 #[test]
601 fn test_bad_magic() { 689 fn test_bad_magic() {
@@ -613,19 +701,25 @@ mod tests {
613 701
614 #[test] 702 #[test]
615 fn test_boot_state() { 703 fn test_boot_state() {
616 let mut flash = MemFlash([0xff; 131072]); 704 const STATE: Partition = Partition::new(0, 4096);
705 const ACTIVE: Partition = Partition::new(4096, 61440);
706 const DFU: Partition = Partition::new(61440, 122880);
707
708 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]);
617 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); 709 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
618 let mut flash = SingleFlashProvider::new(&mut flash); 710 let mut flash = SingleFlashProvider::new(&mut flash);
619 711
620 let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE); 712 let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE);
621 713
622 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); 714 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap());
623 } 715 }
624 716
625 #[test] 717 #[test]
626 fn test_swap_state() { 718 fn test_swap_state() {
627 env_logger::init(); 719 const STATE: Partition = Partition::new(0, 4096);
628 let mut flash = MemFlash([0xff; 131072]); 720 const ACTIVE: Partition = Partition::new(4096, 61440);
721 const DFU: Partition = Partition::new(61440, 122880);
722 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]);
629 723
630 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 724 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
631 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 725 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
@@ -634,14 +728,14 @@ mod tests {
634 flash.0[i] = original[i - ACTIVE.from]; 728 flash.0[i] = original[i - ACTIVE.from];
635 } 729 }
636 730
637 let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE); 731 let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE);
638 let mut updater = FirmwareUpdater::new(DFU, STATE); 732 let mut updater = FirmwareUpdater::new(DFU, STATE);
639 let mut offset = 0; 733 let mut offset = 0;
640 for chunk in update.chunks(4096) { 734 for chunk in update.chunks(4096) {
641 block_on(updater.write_firmware(offset, &chunk, &mut flash, 4096)).unwrap(); 735 block_on(updater.write_firmware(offset, &chunk, &mut flash, 4096)).unwrap();
642 offset += chunk.len(); 736 offset += chunk.len();
643 } 737 }
644 block_on(updater.mark_update(&mut flash)).unwrap(); 738 block_on(updater.update(&mut flash)).unwrap();
645 739
646 assert_eq!( 740 assert_eq!(
647 State::Swap, 741 State::Swap,
@@ -686,27 +780,131 @@ mod tests {
686 ); 780 );
687 } 781 }
688 782
689 struct MemFlash([u8; 131072]); 783 #[test]
784 fn test_separate_flash_active_page_biggest() {
785 const STATE: Partition = Partition::new(2048, 4096);
786 const ACTIVE: Partition = Partition::new(4096, 16384);
787 const DFU: Partition = Partition::new(0, 16384);
788
789 let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]);
790 let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]);
791 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]);
792
793 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
794 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
795
796 for i in ACTIVE.from..ACTIVE.to {
797 active.0[i] = original[i - ACTIVE.from];
798 }
799
800 let mut updater = FirmwareUpdater::new(DFU, STATE);
801
802 let mut offset = 0;
803 for chunk in update.chunks(2048) {
804 block_on(updater.write_firmware(offset, &chunk, &mut dfu, chunk.len())).unwrap();
805 offset += chunk.len();
806 }
807 block_on(updater.update(&mut state)).unwrap();
808
809 let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE);
810 assert_eq!(
811 State::Swap,
812 bootloader
813 .prepare_boot(&mut MultiFlashProvider::new(
814 &mut active,
815 &mut state,
816 &mut dfu,
817 ))
818 .unwrap()
819 );
820
821 for i in ACTIVE.from..ACTIVE.to {
822 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i);
823 }
824
825 // First DFU page is untouched
826 for i in DFU.from + 4096..DFU.to {
827 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i);
828 }
829 }
830
831 #[test]
832 fn test_separate_flash_dfu_page_biggest() {
833 const STATE: Partition = Partition::new(2048, 4096);
834 const ACTIVE: Partition = Partition::new(4096, 16384);
835 const DFU: Partition = Partition::new(0, 16384);
836
837 let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]);
838 let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]);
839 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]);
840
841 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
842 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
690 843
691 impl NorFlash for MemFlash { 844 for i in ACTIVE.from..ACTIVE.to {
692 const WRITE_SIZE: usize = 4; 845 active.0[i] = original[i - ACTIVE.from];
693 const ERASE_SIZE: usize = 4096; 846 }
847
848 let mut updater = FirmwareUpdater::new(DFU, STATE);
849
850 let mut offset = 0;
851 for chunk in update.chunks(4096) {
852 block_on(updater.write_firmware(offset, &chunk, &mut dfu, chunk.len())).unwrap();
853 offset += chunk.len();
854 }
855 block_on(updater.update(&mut state)).unwrap();
856
857 let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE);
858 assert_eq!(
859 State::Swap,
860 bootloader
861 .prepare_boot(&mut MultiFlashProvider::new(
862 &mut active,
863 &mut state,
864 &mut dfu,
865 ))
866 .unwrap()
867 );
868
869 for i in ACTIVE.from..ACTIVE.to {
870 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i);
871 }
872
873 // First DFU page is untouched
874 for i in DFU.from + 4096..DFU.to {
875 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i);
876 }
877 }
878
879 struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>(
880 [u8; SIZE],
881 );
882
883 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
884 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
885 {
886 const WRITE_SIZE: usize = WRITE_SIZE;
887 const ERASE_SIZE: usize = ERASE_SIZE;
694 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { 888 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
695 let from = from as usize; 889 let from = from as usize;
696 let to = to as usize; 890 let to = to as usize;
891 assert!(from % ERASE_SIZE == 0);
892 assert!(
893 to % ERASE_SIZE == 0,
894 "To: {}, erase size: {}",
895 to,
896 ERASE_SIZE
897 );
697 for i in from..to { 898 for i in from..to {
698 self.0[i] = 0xFF; 899 self.0[i] = 0xFF;
699 self.0[i] = 0xFF;
700 self.0[i] = 0xFF;
701 self.0[i] = 0xFF;
702 } 900 }
703 Ok(()) 901 Ok(())
704 } 902 }
705 903
706 fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { 904 fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
707 assert!(data.len() % 4 == 0); 905 assert!(data.len() % WRITE_SIZE == 0);
708 assert!(offset % 4 == 0); 906 assert!(offset as usize % WRITE_SIZE == 0);
709 assert!(offset as usize + data.len() < 131072); 907 assert!(offset as usize + data.len() <= SIZE);
710 908
711 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data); 909 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
712 910
@@ -714,11 +912,15 @@ mod tests {
714 } 912 }
715 } 913 }
716 914
717 impl ErrorType for MemFlash { 915 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
916 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
917 {
718 type Error = Infallible; 918 type Error = Infallible;
719 } 919 }
720 920
721 impl ReadNorFlash for MemFlash { 921 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
922 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
923 {
722 const READ_SIZE: usize = 4; 924 const READ_SIZE: usize = 4;
723 925
724 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { 926 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
@@ -728,11 +930,13 @@ mod tests {
728 } 930 }
729 931
730 fn capacity(&self) -> usize { 932 fn capacity(&self) -> usize {
731 131072 933 SIZE
732 } 934 }
733 } 935 }
734 936
735 impl AsyncReadNorFlash for MemFlash { 937 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
938 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
939 {
736 const READ_SIZE: usize = 4; 940 const READ_SIZE: usize = 4;
737 941
738 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a; 942 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
@@ -745,24 +949,25 @@ mod tests {
745 } 949 }
746 950
747 fn capacity(&self) -> usize { 951 fn capacity(&self) -> usize {
748 131072 952 SIZE
749 } 953 }
750 } 954 }
751 955
752 impl AsyncNorFlash for MemFlash { 956 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
753 const WRITE_SIZE: usize = 4; 957 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
754 const ERASE_SIZE: usize = 4096; 958 {
959 const WRITE_SIZE: usize = WRITE_SIZE;
960 const ERASE_SIZE: usize = ERASE_SIZE;
755 961
756 type EraseFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a; 962 type EraseFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
757 fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { 963 fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> {
758 async move { 964 async move {
759 let from = from as usize; 965 let from = from as usize;
760 let to = to as usize; 966 let to = to as usize;
967 assert!(from % ERASE_SIZE == 0);
968 assert!(to % ERASE_SIZE == 0);
761 for i in from..to { 969 for i in from..to {
762 self.0[i] = 0xFF; 970 self.0[i] = 0xFF;
763 self.0[i] = 0xFF;
764 self.0[i] = 0xFF;
765 self.0[i] = 0xFF;
766 } 971 }
767 Ok(()) 972 Ok(())
768 } 973 }
@@ -770,10 +975,17 @@ mod tests {
770 975
771 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a; 976 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
772 fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { 977 fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> {
978 info!("Writing {} bytes to 0x{:x}", data.len(), offset);
773 async move { 979 async move {
774 assert!(data.len() % 4 == 0); 980 assert!(data.len() % WRITE_SIZE == 0);
775 assert!(offset % 4 == 0); 981 assert!(offset as usize % WRITE_SIZE == 0);
776 assert!(offset as usize + data.len() < 131072); 982 assert!(
983 offset as usize + data.len() <= SIZE,
984 "OFFSET: {}, LEN: {}, FLASH SIZE: {}",
985 offset,
986 data.len(),
987 SIZE
988 );
777 989
778 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data); 990 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
779 991
diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs
index c12899d77..9f891887c 100644
--- a/embassy-boot/nrf/src/lib.rs
+++ b/embassy-boot/nrf/src/lib.rs
@@ -1,10 +1,14 @@
1#![no_std] 1#![no_std]
2#![feature(generic_associated_types)] 2#![feature(generic_associated_types)]
3#![feature(type_alias_impl_trait)] 3#![feature(type_alias_impl_trait)]
4#![allow(incomplete_features)]
5#![feature(generic_const_exprs)]
4 6
5mod fmt; 7mod fmt;
6 8
7pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider}; 9pub use embassy_boot::{
10 FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider,
11};
8use embassy_nrf::{ 12use embassy_nrf::{
9 nvmc::{Nvmc, PAGE_SIZE}, 13 nvmc::{Nvmc, PAGE_SIZE},
10 peripherals::WDT, 14 peripherals::WDT,
@@ -13,7 +17,7 @@ use embassy_nrf::{
13use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; 17use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
14 18
15pub struct BootLoader { 19pub struct BootLoader {
16 boot: embassy_boot::BootLoader<PAGE_SIZE, 4, 0xFF>, 20 boot: embassy_boot::BootLoader<PAGE_SIZE>,
17} 21}
18 22
19impl BootLoader { 23impl BootLoader {
@@ -62,7 +66,11 @@ impl BootLoader {
62 } 66 }
63 67
64 /// Boots the application without softdevice mechanisms 68 /// Boots the application without softdevice mechanisms
65 pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize { 69 pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize
70 where
71 [(); <<F as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
72 [(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
73 {
66 match self.boot.prepare_boot(flash) { 74 match self.boot.prepare_boot(flash) {
67 Ok(_) => self.boot.boot_address(), 75 Ok(_) => self.boot.boot_address(),
68 Err(_) => panic!("boot prepare error!"), 76 Err(_) => panic!("boot prepare error!"),
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs
index 68220780c..82e32a97d 100644
--- a/embassy-boot/stm32/src/lib.rs
+++ b/embassy-boot/stm32/src/lib.rs
@@ -1,17 +1,21 @@
1#![no_std] 1#![no_std]
2#![feature(generic_associated_types)] 2#![feature(generic_associated_types)]
3#![feature(type_alias_impl_trait)] 3#![feature(type_alias_impl_trait)]
4#![allow(incomplete_features)]
5#![feature(generic_const_exprs)]
4 6
5mod fmt; 7mod fmt;
6 8
7pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State}; 9pub use embassy_boot::{
8use embassy_stm32::flash::{ERASE_SIZE, ERASE_VALUE, WRITE_SIZE}; 10 FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider, State,
11};
12use embedded_storage::nor_flash::NorFlash;
9 13
10pub struct BootLoader { 14pub struct BootLoader<const PAGE_SIZE: usize> {
11 boot: embassy_boot::BootLoader<ERASE_SIZE, WRITE_SIZE, ERASE_VALUE>, 15 boot: embassy_boot::BootLoader<PAGE_SIZE>,
12} 16}
13 17
14impl BootLoader { 18impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
15 /// Create a new bootloader instance using parameters from linker script 19 /// Create a new bootloader instance using parameters from linker script
16 pub fn default() -> Self { 20 pub fn default() -> Self {
17 extern "C" { 21 extern "C" {
@@ -57,7 +61,11 @@ impl BootLoader {
57 } 61 }
58 62
59 /// Boots the application 63 /// Boots the application
60 pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize { 64 pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize
65 where
66 [(); <<F as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
67 [(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
68 {
61 match self.boot.prepare_boot(flash) { 69 match self.boot.prepare_boot(flash) {
62 Ok(_) => self.boot.boot_address(), 70 Ok(_) => self.boot.boot_address(),
63 Err(_) => panic!("boot prepare error!"), 71 Err(_) => panic!("boot prepare error!"),
diff --git a/embassy-boot/stm32/src/main.rs b/embassy-boot/stm32/src/main.rs
index 563bc55d3..d79b14c67 100644
--- a/embassy-boot/stm32/src/main.rs
+++ b/embassy-boot/stm32/src/main.rs
@@ -7,7 +7,7 @@ use cortex_m_rt::{entry, exception};
7use defmt_rtt as _; 7use defmt_rtt as _;
8 8
9use embassy_boot_stm32::*; 9use embassy_boot_stm32::*;
10use embassy_stm32::flash::Flash; 10use embassy_stm32::flash::{Flash, ERASE_SIZE};
11 11
12#[entry] 12#[entry]
13fn main() -> ! { 13fn main() -> ! {
@@ -21,7 +21,7 @@ fn main() -> ! {
21 } 21 }
22 */ 22 */
23 23
24 let mut bl = BootLoader::default(); 24 let mut bl: BootLoader<ERASE_SIZE> = BootLoader::default();
25 let mut flash = Flash::unlock(p.FLASH); 25 let mut flash = Flash::unlock(p.FLASH);
26 let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash)); 26 let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash));
27 core::mem::drop(flash); 27 core::mem::drop(flash);
diff --git a/examples/boot/nrf/src/bin/a.rs b/examples/boot/nrf/src/bin/a.rs
index caf8140d8..2f05c817b 100644
--- a/examples/boot/nrf/src/bin/a.rs
+++ b/examples/boot/nrf/src/bin/a.rs
@@ -40,7 +40,7 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
40 .unwrap(); 40 .unwrap();
41 offset += chunk.len(); 41 offset += chunk.len();
42 } 42 }
43 updater.mark_update(&mut nvmc).await.unwrap(); 43 updater.update(&mut nvmc).await.unwrap();
44 led.set_high(); 44 led.set_high();
45 cortex_m::peripheral::SCB::sys_reset(); 45 cortex_m::peripheral::SCB::sys_reset();
46 } 46 }
diff --git a/examples/boot/stm32l0/src/bin/a.rs b/examples/boot/stm32l0/src/bin/a.rs
index 7b9000c91..9e603a226 100644
--- a/examples/boot/stm32l0/src/bin/a.rs
+++ b/examples/boot/stm32l0/src/bin/a.rs
@@ -41,7 +41,7 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
41 offset += chunk.len(); 41 offset += chunk.len();
42 } 42 }
43 43
44 updater.mark_update(&mut flash).await.unwrap(); 44 updater.update(&mut flash).await.unwrap();
45 led.set_low(); 45 led.set_low();
46 Timer::after(Duration::from_secs(1)).await; 46 Timer::after(Duration::from_secs(1)).await;
47 cortex_m::peripheral::SCB::sys_reset(); 47 cortex_m::peripheral::SCB::sys_reset();
diff --git a/examples/boot/stm32l1/src/bin/a.rs b/examples/boot/stm32l1/src/bin/a.rs
index 7b9000c91..9e603a226 100644
--- a/examples/boot/stm32l1/src/bin/a.rs
+++ b/examples/boot/stm32l1/src/bin/a.rs
@@ -41,7 +41,7 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
41 offset += chunk.len(); 41 offset += chunk.len();
42 } 42 }
43 43
44 updater.mark_update(&mut flash).await.unwrap(); 44 updater.update(&mut flash).await.unwrap();
45 led.set_low(); 45 led.set_low();
46 Timer::after(Duration::from_secs(1)).await; 46 Timer::after(Duration::from_secs(1)).await;
47 cortex_m::peripheral::SCB::sys_reset(); 47 cortex_m::peripheral::SCB::sys_reset();
diff --git a/examples/boot/stm32l4/src/bin/a.rs b/examples/boot/stm32l4/src/bin/a.rs
index a5a9e2302..41684b2f0 100644
--- a/examples/boot/stm32l4/src/bin/a.rs
+++ b/examples/boot/stm32l4/src/bin/a.rs
@@ -38,7 +38,7 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
38 .unwrap(); 38 .unwrap();
39 offset += chunk.len(); 39 offset += chunk.len();
40 } 40 }
41 updater.mark_update(&mut flash).await.unwrap(); 41 updater.update(&mut flash).await.unwrap();
42 led.set_low(); 42 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset(); 43 cortex_m::peripheral::SCB::sys_reset();
44} 44}
diff --git a/examples/boot/stm32wl/src/bin/a.rs b/examples/boot/stm32wl/src/bin/a.rs
index d01a72f2d..b3e9efa75 100644
--- a/examples/boot/stm32wl/src/bin/a.rs
+++ b/examples/boot/stm32wl/src/bin/a.rs
@@ -40,7 +40,7 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
40 .unwrap(); 40 .unwrap();
41 offset += chunk.len(); 41 offset += chunk.len();
42 } 42 }
43 updater.mark_update(&mut flash).await.unwrap(); 43 updater.update(&mut flash).await.unwrap();
44 //defmt::info!("Marked as updated"); 44 //defmt::info!("Marked as updated");
45 led.set_low(); 45 led.set_low();
46 cortex_m::peripheral::SCB::sys_reset(); 46 cortex_m::peripheral::SCB::sys_reset();