diff options
| -rw-r--r-- | docs/modules/ROOT/pages/bootloader.adoc | 3 | ||||
| -rw-r--r-- | embassy-boot/boot/src/lib.rs | 344 | ||||
| -rw-r--r-- | embassy-boot/nrf/src/lib.rs | 14 | ||||
| -rw-r--r-- | embassy-boot/stm32/src/lib.rs | 20 | ||||
| -rw-r--r-- | embassy-boot/stm32/src/main.rs | 4 | ||||
| -rw-r--r-- | examples/boot/nrf/src/bin/a.rs | 2 | ||||
| -rw-r--r-- | examples/boot/stm32l0/src/bin/a.rs | 2 | ||||
| -rw-r--r-- | examples/boot/stm32l1/src/bin/a.rs | 2 | ||||
| -rw-r--r-- | examples/boot/stm32l4/src/bin/a.rs | 2 | ||||
| -rw-r--r-- | examples/boot/stm32wl/src/bin/a.rs | 2 |
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 | ||
| 30 | The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash, but they have to support compatible page sizes. | 30 | The 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. |
| 31 | The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. | ||
| 31 | 32 | ||
| 32 | The 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. | 33 | The 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 | ||
| 63 | pub trait FlashConfig { | 63 | pub 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. |
| 86 | pub 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. | ||
| 89 | pub 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 | ||
| 98 | impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> | 101 | impl<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 |
| 378 | pub struct SingleFlashProvider<'a, F> | 421 | pub struct SingleFlashProvider<'a, F, const ERASE_VALUE: u8 = 0xFF> |
| 379 | where | 422 | where |
| 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 | ||
| 385 | impl<'a, F> SingleFlashProvider<'a, F> | 428 | impl<'a, F, const ERASE_VALUE: u8> SingleFlashProvider<'a, F, ERASE_VALUE> |
| 386 | where | 429 | where |
| 387 | F: NorFlash + ReadNorFlash, | 430 | F: NorFlash + ReadNorFlash, |
| 388 | { | 431 | { |
| @@ -393,7 +436,7 @@ where | |||
| 393 | } | 436 | } |
| 394 | } | 437 | } |
| 395 | 438 | ||
| 396 | pub struct SingleFlashConfig<'a, F> | 439 | pub struct SingleFlashConfig<'a, F, const ERASE_VALUE: u8 = 0xFF> |
| 397 | where | 440 | where |
| 398 | F: NorFlash + ReadNorFlash, | 441 | F: NorFlash + ReadNorFlash, |
| 399 | { | 442 | { |
| @@ -419,17 +462,66 @@ where | |||
| 419 | } | 462 | } |
| 420 | } | 463 | } |
| 421 | 464 | ||
| 422 | impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> | 465 | impl<'a, F, const ERASE_VALUE: u8> FlashConfig for SingleFlashConfig<'a, F, ERASE_VALUE> |
| 423 | where | 466 | where |
| 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 | ||
| 478 | pub struct MultiFlashProvider<'a, ACTIVE, STATE, DFU> | ||
| 479 | where | ||
| 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 | |||
| 489 | impl<'a, ACTIVE, STATE, DFU> MultiFlashProvider<'a, ACTIVE, STATE, DFU> | ||
| 490 | where | ||
| 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 | |||
| 504 | impl<'a, ACTIVE, STATE, DFU> FlashProvider for MultiFlashProvider<'a, ACTIVE, STATE, DFU> | ||
| 505 | where | ||
| 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 |
| 435 | pub struct FirmwareUpdater { | 527 | pub 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 | ||
| 5 | mod fmt; | 7 | mod fmt; |
| 6 | 8 | ||
| 7 | pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider}; | 9 | pub use embassy_boot::{ |
| 10 | FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider, | ||
| 11 | }; | ||
| 8 | use embassy_nrf::{ | 12 | use 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::{ | |||
| 13 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; | 17 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; |
| 14 | 18 | ||
| 15 | pub struct BootLoader { | 19 | pub struct BootLoader { |
| 16 | boot: embassy_boot::BootLoader<PAGE_SIZE, 4, 0xFF>, | 20 | boot: embassy_boot::BootLoader<PAGE_SIZE>, |
| 17 | } | 21 | } |
| 18 | 22 | ||
| 19 | impl BootLoader { | 23 | impl 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 | ||
| 5 | mod fmt; | 7 | mod fmt; |
| 6 | 8 | ||
| 7 | pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State}; | 9 | pub use embassy_boot::{ |
| 8 | use embassy_stm32::flash::{ERASE_SIZE, ERASE_VALUE, WRITE_SIZE}; | 10 | FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider, State, |
| 11 | }; | ||
| 12 | use embedded_storage::nor_flash::NorFlash; | ||
| 9 | 13 | ||
| 10 | pub struct BootLoader { | 14 | pub 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 | ||
| 14 | impl BootLoader { | 18 | impl<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}; | |||
| 7 | use defmt_rtt as _; | 7 | use defmt_rtt as _; |
| 8 | 8 | ||
| 9 | use embassy_boot_stm32::*; | 9 | use embassy_boot_stm32::*; |
| 10 | use embassy_stm32::flash::Flash; | 10 | use embassy_stm32::flash::{Flash, ERASE_SIZE}; |
| 11 | 11 | ||
| 12 | #[entry] | 12 | #[entry] |
| 13 | fn main() -> ! { | 13 | fn 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(); |
