diff options
| author | Ulf Lilleengen <[email protected]> | 2022-04-26 18:33:09 +0200 |
|---|---|---|
| committer | Ulf Lilleengen <[email protected]> | 2022-04-27 15:17:18 +0200 |
| commit | da61611f8f57410a87106961efd24d80e6a8f63e (patch) | |
| tree | 81bf5f96a052be8cc74fa4f513592adf1f4bb1db /embassy-boot/boot | |
| parent | 484e0acc638c27366e19275c32db9c8487ea8fba (diff) | |
Add bootloader to CI
Diffstat (limited to 'embassy-boot/boot')
| -rw-r--r-- | embassy-boot/boot/Cargo.toml | 5 | ||||
| -rw-r--r-- | embassy-boot/boot/src/lib.rs | 57 |
2 files changed, 24 insertions, 38 deletions
diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 04deab30b..0a3006ffc 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml | |||
| @@ -21,8 +21,3 @@ log = "0.4" | |||
| 21 | env_logger = "0.9" | 21 | env_logger = "0.9" |
| 22 | rand = "0.8" | 22 | rand = "0.8" |
| 23 | futures = { version = "0.3", features = ["executor"] } | 23 | futures = { version = "0.3", features = ["executor"] } |
| 24 | |||
| 25 | [features] | ||
| 26 | write-4 = [] | ||
| 27 | write-8 = [] | ||
| 28 | invert-erase = [] | ||
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 080ea2426..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,24 +19,9 @@ mod fmt; | |||
| 17 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; | 19 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; |
| 18 | use embedded_storage_async::nor_flash::AsyncNorFlash; | 20 | use embedded_storage_async::nor_flash::AsyncNorFlash; |
| 19 | 21 | ||
| 20 | #[cfg(not(any(feature = "write-4", feature = "write-8",)))] | ||
| 21 | compile_error!("No write size/alignment specified. Must specify exactly one of the following features: write-4, write-8"); | ||
| 22 | |||
| 23 | const BOOT_MAGIC: u8 = 0xD0; | 22 | const BOOT_MAGIC: u8 = 0xD0; |
| 24 | const SWAP_MAGIC: u8 = 0xF0; | 23 | const SWAP_MAGIC: u8 = 0xF0; |
| 25 | 24 | ||
| 26 | #[cfg(feature = "write-4")] | ||
| 27 | const WRITE_SIZE: usize = 4; | ||
| 28 | |||
| 29 | #[cfg(feature = "write-8")] | ||
| 30 | const WRITE_SIZE: usize = 8; | ||
| 31 | |||
| 32 | #[cfg(feature = "invert-erase")] | ||
| 33 | const ERASE_VALUE: u8 = 0x00; | ||
| 34 | |||
| 35 | #[cfg(not(feature = "invert-erase"))] | ||
| 36 | const ERASE_VALUE: u8 = 0xFF; | ||
| 37 | |||
| 38 | #[derive(Copy, Clone, Debug)] | 25 | #[derive(Copy, Clone, Debug)] |
| 39 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 26 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 40 | pub struct Partition { | 27 | pub struct Partition { |
| @@ -96,7 +83,7 @@ pub trait FlashProvider { | |||
| 96 | 83 | ||
| 97 | /// 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 |
| 98 | /// different page sizes and flash write sizes. | 85 | /// different page sizes and flash write sizes. |
| 99 | pub struct BootLoader<const PAGE_SIZE: usize> { | 86 | pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> { |
| 100 | // 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: |
| 101 | // | Range | Description | | 88 | // | Range | Description | |
| 102 | // | 0 - WRITE_SIZE | 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. | |
| @@ -108,7 +95,9 @@ pub struct BootLoader<const PAGE_SIZE: usize> { | |||
| 108 | dfu: Partition, | 95 | dfu: Partition, |
| 109 | } | 96 | } |
| 110 | 97 | ||
| 111 | impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { | 98 | impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> |
| 99 | BootLoader<PAGE_SIZE, WRITE_SIZE, ERASE_VALUE> | ||
| 100 | { | ||
| 112 | pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { | 101 | pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { |
| 113 | assert_eq!(active.len() % PAGE_SIZE, 0); | 102 | assert_eq!(active.len() % PAGE_SIZE, 0); |
| 114 | assert_eq!(dfu.len() % PAGE_SIZE, 0); | 103 | assert_eq!(dfu.len() % PAGE_SIZE, 0); |
| @@ -352,8 +341,6 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { | |||
| 352 | 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)?; |
| 353 | } | 342 | } |
| 354 | 343 | ||
| 355 | info!("DONE COPYING"); | ||
| 356 | |||
| 357 | Ok(()) | 344 | Ok(()) |
| 358 | } | 345 | } |
| 359 | 346 | ||
| @@ -379,7 +366,6 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { | |||
| 379 | let flash = p.flash(); | 366 | let flash = p.flash(); |
| 380 | flash.read(self.state.from as u32, &mut magic)?; | 367 | flash.read(self.state.from as u32, &mut magic)?; |
| 381 | 368 | ||
| 382 | info!("Read magic: {:x}", magic); | ||
| 383 | if magic == [SWAP_MAGIC; WRITE_SIZE] { | 369 | if magic == [SWAP_MAGIC; WRITE_SIZE] { |
| 384 | Ok(State::Swap) | 370 | Ok(State::Swap) |
| 385 | } else { | 371 | } else { |
| @@ -451,13 +437,9 @@ pub struct FirmwareUpdater { | |||
| 451 | dfu: Partition, | 437 | dfu: Partition, |
| 452 | } | 438 | } |
| 453 | 439 | ||
| 454 | #[cfg(feature = "write-4")] | 440 | // NOTE: Aligned to the largest write size supported by flash |
| 455 | #[repr(align(4))] | 441 | #[repr(align(32))] |
| 456 | pub struct Aligned([u8; 4]); | 442 | pub struct Aligned<const N: usize>([u8; N]); |
| 457 | |||
| 458 | #[cfg(feature = "write-8")] | ||
| 459 | #[repr(align(8))] | ||
| 460 | pub struct Aligned([u8; 8]); | ||
| 461 | 443 | ||
| 462 | impl Default for FirmwareUpdater { | 444 | impl Default for FirmwareUpdater { |
| 463 | fn default() -> Self { | 445 | fn default() -> Self { |
| @@ -480,6 +462,9 @@ impl Default for FirmwareUpdater { | |||
| 480 | &__bootloader_state_end as *const u32 as usize, | 462 | &__bootloader_state_end as *const u32 as usize, |
| 481 | ) | 463 | ) |
| 482 | }; | 464 | }; |
| 465 | |||
| 466 | trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); | ||
| 467 | trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); | ||
| 483 | FirmwareUpdater::new(dfu, state) | 468 | FirmwareUpdater::new(dfu, state) |
| 484 | } | 469 | } |
| 485 | } | 470 | } |
| @@ -496,14 +481,20 @@ impl FirmwareUpdater { | |||
| 496 | 481 | ||
| 497 | /// Instruct bootloader that DFU should commence at next boot. | 482 | /// Instruct bootloader that DFU should commence at next boot. |
| 498 | /// Must be provided with an aligned buffer to use for reading and writing magic; | 483 | /// Must be provided with an aligned buffer to use for reading and writing magic; |
| 499 | pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { | 484 | pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> |
| 500 | let mut aligned = Aligned([0; WRITE_SIZE]); | 485 | where |
| 486 | [(); F::WRITE_SIZE]:, | ||
| 487 | { | ||
| 488 | let mut aligned = Aligned([0; { F::WRITE_SIZE }]); | ||
| 501 | self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await | 489 | self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await |
| 502 | } | 490 | } |
| 503 | 491 | ||
| 504 | /// Mark firmware boot successfully | 492 | /// Mark firmware boot successfully |
| 505 | 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> |
| 506 | let mut aligned = Aligned([0; WRITE_SIZE]); | 494 | where |
| 495 | [(); F::WRITE_SIZE]:, | ||
| 496 | { | ||
| 497 | let mut aligned = Aligned([0; { F::WRITE_SIZE }]); | ||
| 507 | self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).await | 498 | self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).await |
| 508 | } | 499 | } |
| 509 | 500 | ||
| @@ -626,7 +617,7 @@ mod tests { | |||
| 626 | flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); | 617 | flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); |
| 627 | let mut flash = SingleFlashProvider::new(&mut flash); | 618 | let mut flash = SingleFlashProvider::new(&mut flash); |
| 628 | 619 | ||
| 629 | let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); | 620 | let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE); |
| 630 | 621 | ||
| 631 | assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); | 622 | assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); |
| 632 | } | 623 | } |
| @@ -643,7 +634,7 @@ mod tests { | |||
| 643 | flash.0[i] = original[i - ACTIVE.from]; | 634 | flash.0[i] = original[i - ACTIVE.from]; |
| 644 | } | 635 | } |
| 645 | 636 | ||
| 646 | let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); | 637 | let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE); |
| 647 | let mut updater = FirmwareUpdater::new(DFU, STATE); | 638 | let mut updater = FirmwareUpdater::new(DFU, STATE); |
| 648 | let mut offset = 0; | 639 | let mut offset = 0; |
| 649 | for chunk in update.chunks(4096) { | 640 | for chunk in update.chunks(4096) { |
