aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot/boot/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-04-27 13:43:15 +0000
committerGitHub <[email protected]>2022-04-27 13:43:15 +0000
commit663642eabbe7c8b0a66a4c1d00d1cc681c1d497e (patch)
treea2821e2a58f7b58eef8595ce69f87db31ed5f32f /embassy-boot/boot/src
parent9c283cd44504d6d9d6f9e352e4c7a8d043bd673f (diff)
parent93c17be32e150d0afffdf25fddab47767c779c3f (diff)
Merge #724
724: STM32 Flash + Bootloader r=Dirbaio a=lulf Not working. Co-authored-by: Ulf Lilleengen <[email protected]> Co-authored-by: Ulf Lilleengen <[email protected]>
Diffstat (limited to 'embassy-boot/boot/src')
-rw-r--r--embassy-boot/boot/src/lib.rs222
1 files changed, 141 insertions, 81 deletions
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 0d33ad1a6..554709250 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -1,5 +1,7 @@
1#![feature(type_alias_impl_trait)] 1#![feature(type_alias_impl_trait)]
2#![feature(generic_associated_types)] 2#![feature(generic_associated_types)]
3#![feature(generic_const_exprs)]
4#![allow(incomplete_features)]
3#![no_std] 5#![no_std]
4///! embassy-boot is a bootloader and firmware updater for embedded devices with flash 6///! embassy-boot is a bootloader and firmware updater for embedded devices with flash
5///! storage implemented using embedded-storage 7///! storage implemented using embedded-storage
@@ -17,8 +19,8 @@ mod fmt;
17use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; 19use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
18use embedded_storage_async::nor_flash::AsyncNorFlash; 20use embedded_storage_async::nor_flash::AsyncNorFlash;
19 21
20pub const BOOT_MAGIC: u32 = 0xD00DF00D; 22const BOOT_MAGIC: u8 = 0xD0;
21pub const SWAP_MAGIC: u32 = 0xF00FDAAD; 23const SWAP_MAGIC: u8 = 0xF0;
22 24
23#[derive(Copy, Clone, Debug)] 25#[derive(Copy, Clone, Debug)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))] 26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -80,12 +82,12 @@ pub trait FlashProvider {
80} 82}
81 83
82/// BootLoader works with any flash implementing embedded_storage and can also work with 84/// BootLoader works with any flash implementing embedded_storage and can also work with
83/// different page sizes. 85/// different page sizes and flash write sizes.
84pub struct BootLoader<const PAGE_SIZE: usize> { 86pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> {
85 // Page with current state of bootloader. The state partition has the following format: 87 // Page with current state of bootloader. The state partition has the following format:
86 // | Range | Description | 88 // | Range | Description |
87 // | 0 - 4 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 89 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
88 // | 4 - N | Progress index used while swapping or reverting | 90 // | WRITE_SIZE - N | Progress index used while swapping or reverting |
89 state: Partition, 91 state: Partition,
90 // Location of the partition which will be booted from 92 // Location of the partition which will be booted from
91 active: Partition, 93 active: Partition,
@@ -93,14 +95,16 @@ pub struct BootLoader<const PAGE_SIZE: usize> {
93 dfu: Partition, 95 dfu: Partition,
94} 96}
95 97
96impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { 98impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
99 BootLoader<PAGE_SIZE, WRITE_SIZE, ERASE_VALUE>
100{
97 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 101 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
98 assert_eq!(active.len() % PAGE_SIZE, 0); 102 assert_eq!(active.len() % PAGE_SIZE, 0);
99 assert_eq!(dfu.len() % PAGE_SIZE, 0); 103 assert_eq!(dfu.len() % PAGE_SIZE, 0);
100 // DFU partition must have an extra page 104 // DFU partition must have an extra page
101 assert!(dfu.len() - active.len() >= PAGE_SIZE); 105 assert!(dfu.len() - active.len() >= PAGE_SIZE);
102 // Ensure we have enough progress pages to store copy progress 106 // Ensure we have enough progress pages to store copy progress
103 assert!(active.len() / PAGE_SIZE >= (state.len() - 4) / PAGE_SIZE); 107 assert!(active.len() / PAGE_SIZE >= (state.len() - WRITE_SIZE) / PAGE_SIZE);
104 Self { active, dfu, state } 108 Self { active, dfu, state }
105 } 109 }
106 110
@@ -203,15 +207,18 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
203 if !self.is_swapped(p.state())? { 207 if !self.is_swapped(p.state())? {
204 trace!("Swapping"); 208 trace!("Swapping");
205 self.swap(p)?; 209 self.swap(p)?;
210 trace!("Swapping done");
206 } else { 211 } else {
207 trace!("Reverting"); 212 trace!("Reverting");
208 self.revert(p)?; 213 self.revert(p)?;
209 214
210 // Overwrite magic and reset progress 215 // Overwrite magic and reset progress
211 let fstate = p.state().flash(); 216 let fstate = p.state().flash();
212 fstate.write(self.state.from as u32, &[0, 0, 0, 0])?; 217 let aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
218 fstate.write(self.state.from as u32, &aligned.0)?;
213 fstate.erase(self.state.from as u32, self.state.to as u32)?; 219 fstate.erase(self.state.from as u32, self.state.to as u32)?;
214 fstate.write(self.state.from as u32, &BOOT_MAGIC.to_le_bytes())?; 220 let aligned = Aligned([BOOT_MAGIC; WRITE_SIZE]);
221 fstate.write(self.state.from as u32, &aligned.0)?;
215 } 222 }
216 } 223 }
217 _ => {} 224 _ => {}
@@ -227,12 +234,15 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
227 } 234 }
228 235
229 fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError> { 236 fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError> {
230 let max_index = ((self.state.len() - 4) / 4) - 1; 237 let max_index = ((self.state.len() - WRITE_SIZE) / WRITE_SIZE) - 1;
231 let flash = p.flash(); 238 let flash = p.flash();
239 let mut aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
232 for i in 0..max_index { 240 for i in 0..max_index {
233 let mut buf: [u8; 4] = [0; 4]; 241 flash.read(
234 flash.read((self.state.from + 4 + i * 4) as u32, &mut buf)?; 242 (self.state.from + WRITE_SIZE + i * WRITE_SIZE) as u32,
235 if buf == [0xFF, 0xFF, 0xFF, 0xFF] { 243 &mut aligned.0,
244 )?;
245 if aligned.0 == [ERASE_VALUE; WRITE_SIZE] {
236 return Ok(i); 246 return Ok(i);
237 } 247 }
238 } 248 }
@@ -241,8 +251,9 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
241 251
242 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> { 252 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> {
243 let flash = p.flash(); 253 let flash = p.flash();
244 let w = self.state.from + 4 + idx * 4; 254 let w = self.state.from + WRITE_SIZE + idx * WRITE_SIZE;
245 flash.write(w as u32, &[0, 0, 0, 0])?; 255 let aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
256 flash.write(w as u32, &aligned.0)?;
246 Ok(()) 257 Ok(())
247 } 258 }
248 259
@@ -314,18 +325,19 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
314 325
315 fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> { 326 fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> {
316 let page_count = self.active.len() / PAGE_SIZE; 327 let page_count = self.active.len() / PAGE_SIZE;
317 // trace!("Page count: {}", page_count); 328 trace!("Page count: {}", page_count);
318 for page in 0..page_count { 329 for page in 0..page_count {
330 trace!("COPY PAGE {}", page);
319 // Copy active page to the 'next' DFU page. 331 // Copy active page to the 'next' DFU page.
320 let active_page = self.active_addr(page_count - 1 - page); 332 let active_page = self.active_addr(page_count - 1 - page);
321 let dfu_page = self.dfu_addr(page_count - page); 333 let dfu_page = self.dfu_addr(page_count - page);
322 info!("Copy active {} to dfu {}", active_page, dfu_page); 334 //trace!("Copy active {} to dfu {}", active_page, dfu_page);
323 self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?; 335 self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?;
324 336
325 // Copy DFU page to the active page 337 // Copy DFU page to the active page
326 let active_page = self.active_addr(page_count - 1 - page); 338 let active_page = self.active_addr(page_count - 1 - page);
327 let dfu_page = self.dfu_addr(page_count - 1 - page); 339 let dfu_page = self.dfu_addr(page_count - 1 - page);
328 info!("Copy dfy {} to active {}", dfu_page, active_page); 340 //trace!("Copy dfy {} to active {}", dfu_page, active_page);
329 self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?; 341 self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?;
330 } 342 }
331 343
@@ -350,13 +362,14 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
350 } 362 }
351 363
352 fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError> { 364 fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError> {
353 let mut magic: [u8; 4] = [0; 4]; 365 let mut magic: [u8; WRITE_SIZE] = [0; WRITE_SIZE];
354 let flash = p.flash(); 366 let flash = p.flash();
355 flash.read(self.state.from as u32, &mut magic)?; 367 flash.read(self.state.from as u32, &mut magic)?;
356 368
357 match u32::from_le_bytes(magic) { 369 if magic == [SWAP_MAGIC; WRITE_SIZE] {
358 SWAP_MAGIC => Ok(State::Swap), 370 Ok(State::Swap)
359 _ => Ok(State::Boot), 371 } else {
372 Ok(State::Boot)
360 } 373 }
361 } 374 }
362} 375}
@@ -424,6 +437,38 @@ pub struct FirmwareUpdater {
424 dfu: Partition, 437 dfu: Partition,
425} 438}
426 439
440// NOTE: Aligned to the largest write size supported by flash
441#[repr(align(32))]
442pub struct Aligned<const N: usize>([u8; N]);
443
444impl Default for FirmwareUpdater {
445 fn default() -> Self {
446 extern "C" {
447 static __bootloader_state_start: u32;
448 static __bootloader_state_end: u32;
449 static __bootloader_dfu_start: u32;
450 static __bootloader_dfu_end: u32;
451 }
452
453 let dfu = unsafe {
454 Partition::new(
455 &__bootloader_dfu_start as *const u32 as usize,
456 &__bootloader_dfu_end as *const u32 as usize,
457 )
458 };
459 let state = unsafe {
460 Partition::new(
461 &__bootloader_state_start as *const u32 as usize,
462 &__bootloader_state_end as *const u32 as usize,
463 )
464 };
465
466 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
467 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
468 FirmwareUpdater::new(dfu, state)
469 }
470}
471
427impl FirmwareUpdater { 472impl FirmwareUpdater {
428 pub const fn new(dfu: Partition, state: Partition) -> Self { 473 pub const fn new(dfu: Partition, state: Partition) -> Self {
429 Self { dfu, state } 474 Self { dfu, state }
@@ -435,53 +480,51 @@ impl FirmwareUpdater {
435 } 480 }
436 481
437 /// Instruct bootloader that DFU should commence at next boot. 482 /// Instruct bootloader that DFU should commence at next boot.
438 pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { 483 /// Must be provided with an aligned buffer to use for reading and writing magic;
439 #[repr(align(4))] 484 pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error>
440 struct Aligned([u8; 4]); 485 where
441 486 [(); F::WRITE_SIZE]:,
442 let mut magic = Aligned([0; 4]); 487 {
443 flash.read(self.state.from as u32, &mut magic.0).await?; 488 let mut aligned = Aligned([0; { F::WRITE_SIZE }]);
444 let magic = u32::from_le_bytes(magic.0); 489 self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await
445
446 if magic != SWAP_MAGIC {
447 flash
448 .write(self.state.from as u32, &Aligned([0; 4]).0)
449 .await?;
450 flash
451 .erase(self.state.from as u32, self.state.to as u32)
452 .await?;
453 trace!(
454 "Setting swap magic at {} to 0x{:x}, LE: 0x{:x}",
455 self.state.from,
456 &SWAP_MAGIC,
457 &SWAP_MAGIC.to_le_bytes()
458 );
459 flash
460 .write(self.state.from as u32, &SWAP_MAGIC.to_le_bytes())
461 .await?;
462 }
463 Ok(())
464 } 490 }
465 491
466 /// Mark firmware boot successfully 492 /// Mark firmware boot successfully
467 pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { 493 pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error>
468 #[repr(align(4))] 494 where
469 struct Aligned([u8; 4]); 495 [(); F::WRITE_SIZE]:,
496 {
497 let mut aligned = Aligned([0; { F::WRITE_SIZE }]);
498 self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).await
499 }
470 500
471 let mut magic = Aligned([0; 4]); 501 async fn set_magic<F: AsyncNorFlash>(
472 flash.read(self.state.from as u32, &mut magic.0).await?; 502 &mut self,
473 let magic = u32::from_le_bytes(magic.0); 503 aligned: &mut [u8],
504 magic: u8,
505 flash: &mut F,
506 ) -> Result<(), F::Error> {
507 flash.read(self.state.from as u32, aligned).await?;
474 508
475 if magic != BOOT_MAGIC { 509 let mut is_set = true;
476 flash 510 for b in 0..aligned.len() {
477 .write(self.state.from as u32, &Aligned([0; 4]).0) 511 if aligned[b] != magic {
478 .await?; 512 is_set = false;
513 }
514 }
515 if !is_set {
516 for i in 0..aligned.len() {
517 aligned[i] = 0;
518 }
519 flash.write(self.state.from as u32, aligned).await?;
479 flash 520 flash
480 .erase(self.state.from as u32, self.state.to as u32) 521 .erase(self.state.from as u32, self.state.to as u32)
481 .await?; 522 .await?;
482 flash 523
483 .write(self.state.from as u32, &BOOT_MAGIC.to_le_bytes()) 524 for i in 0..aligned.len() {
484 .await?; 525 aligned[i] = magic;
526 }
527 flash.write(self.state.from as u32, aligned).await?;
485 } 528 }
486 Ok(()) 529 Ok(())
487 } 530 }
@@ -545,6 +588,7 @@ mod tests {
545 use super::*; 588 use super::*;
546 use core::convert::Infallible; 589 use core::convert::Infallible;
547 use core::future::Future; 590 use core::future::Future;
591 use embedded_storage::nor_flash::ErrorType;
548 use embedded_storage_async::nor_flash::AsyncReadNorFlash; 592 use embedded_storage_async::nor_flash::AsyncReadNorFlash;
549 use futures::executor::block_on; 593 use futures::executor::block_on;
550 594
@@ -552,9 +596,11 @@ mod tests {
552 const ACTIVE: Partition = Partition::new(4096, 61440); 596 const ACTIVE: Partition = Partition::new(4096, 61440);
553 const DFU: Partition = Partition::new(61440, 122880); 597 const DFU: Partition = Partition::new(61440, 122880);
554 598
599 /*
555 #[test] 600 #[test]
556 fn test_bad_magic() { 601 fn test_bad_magic() {
557 let mut flash = MemFlash([0xff; 131072]); 602 let mut flash = MemFlash([0xff; 131072]);
603 let mut flash = SingleFlashProvider::new(&mut flash);
558 604
559 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); 605 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);
560 606
@@ -563,13 +609,15 @@ mod tests {
563 Err(BootError::BadMagic) 609 Err(BootError::BadMagic)
564 ); 610 );
565 } 611 }
612 */
566 613
567 #[test] 614 #[test]
568 fn test_boot_state() { 615 fn test_boot_state() {
569 let mut flash = MemFlash([0xff; 131072]); 616 let mut flash = MemFlash([0xff; 131072]);
570 flash.0[0..4].copy_from_slice(&BOOT_MAGIC.to_le_bytes()); 617 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
618 let mut flash = SingleFlashProvider::new(&mut flash);
571 619
572 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); 620 let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE);
573 621
574 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); 622 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap());
575 } 623 }
@@ -586,21 +634,21 @@ mod tests {
586 flash.0[i] = original[i - ACTIVE.from]; 634 flash.0[i] = original[i - ACTIVE.from];
587 } 635 }
588 636
589 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); 637 let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE);
590 let mut updater = FirmwareUpdater::new(DFU, STATE); 638 let mut updater = FirmwareUpdater::new(DFU, STATE);
591 for i in (DFU.from..DFU.to).step_by(4) { 639 let mut offset = 0;
592 let base = i - DFU.from; 640 for chunk in update.chunks(4096) {
593 let data: [u8; 4] = [ 641 block_on(updater.write_firmware(offset, &chunk, &mut flash, 4096)).unwrap();
594 update[base], 642 offset += chunk.len();
595 update[base + 1],
596 update[base + 2],
597 update[base + 3],
598 ];
599 block_on(updater.write_firmware(i - DFU.from, &data, &mut flash)).unwrap();
600 } 643 }
601 block_on(updater.mark_update(&mut flash)).unwrap(); 644 block_on(updater.mark_update(&mut flash)).unwrap();
602 645
603 assert_eq!(State::Swap, bootloader.prepare_boot(&mut flash).unwrap()); 646 assert_eq!(
647 State::Swap,
648 bootloader
649 .prepare_boot(&mut SingleFlashProvider::new(&mut flash))
650 .unwrap()
651 );
604 652
605 for i in ACTIVE.from..ACTIVE.to { 653 for i in ACTIVE.from..ACTIVE.to {
606 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i); 654 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i);
@@ -612,7 +660,12 @@ mod tests {
612 } 660 }
613 661
614 // Running again should cause a revert 662 // Running again should cause a revert
615 assert_eq!(State::Swap, bootloader.prepare_boot(&mut flash).unwrap()); 663 assert_eq!(
664 State::Swap,
665 bootloader
666 .prepare_boot(&mut SingleFlashProvider::new(&mut flash))
667 .unwrap()
668 );
616 669
617 for i in ACTIVE.from..ACTIVE.to { 670 for i in ACTIVE.from..ACTIVE.to {
618 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i); 671 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i);
@@ -625,7 +678,12 @@ mod tests {
625 678
626 // Mark as booted 679 // Mark as booted
627 block_on(updater.mark_booted(&mut flash)).unwrap(); 680 block_on(updater.mark_booted(&mut flash)).unwrap();
628 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); 681 assert_eq!(
682 State::Boot,
683 bootloader
684 .prepare_boot(&mut SingleFlashProvider::new(&mut flash))
685 .unwrap()
686 );
629 } 687 }
630 688
631 struct MemFlash([u8; 131072]); 689 struct MemFlash([u8; 131072]);
@@ -656,9 +714,12 @@ mod tests {
656 } 714 }
657 } 715 }
658 716
717 impl ErrorType for MemFlash {
718 type Error = Infallible;
719 }
720
659 impl ReadNorFlash for MemFlash { 721 impl ReadNorFlash for MemFlash {
660 const READ_SIZE: usize = 4; 722 const READ_SIZE: usize = 4;
661 type Error = Infallible;
662 723
663 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { 724 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
664 let len = buf.len(); 725 let len = buf.len();
@@ -673,10 +734,9 @@ mod tests {
673 734
674 impl AsyncReadNorFlash for MemFlash { 735 impl AsyncReadNorFlash for MemFlash {
675 const READ_SIZE: usize = 4; 736 const READ_SIZE: usize = 4;
676 type Error = Infallible;
677 737
678 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a; 738 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
679 fn read<'a>(&'a mut self, offset: usize, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { 739 fn read<'a>(&'a mut self, offset: u32, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
680 async move { 740 async move {
681 let len = buf.len(); 741 let len = buf.len();
682 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]); 742 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);