aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-boot/README.md30
-rw-r--r--embassy-boot/boot/src/lib.rs544
-rw-r--r--embassy-boot/nrf/src/lib.rs35
-rw-r--r--embassy-boot/stm32/src/lib.rs35
-rw-r--r--examples/boot/application/nrf/src/bin/a.rs3
-rw-r--r--examples/boot/application/stm32f3/src/bin/a.rs7
-rw-r--r--examples/boot/application/stm32f7/src/bin/a.rs7
-rw-r--r--examples/boot/application/stm32h7/Cargo.toml2
-rwxr-xr-xexamples/boot/application/stm32h7/flash-boot.sh3
-rw-r--r--examples/boot/application/stm32h7/src/bin/a.rs16
-rw-r--r--examples/boot/application/stm32l0/src/bin/a.rs7
-rw-r--r--examples/boot/application/stm32l1/src/bin/a.rs7
-rw-r--r--examples/boot/application/stm32l4/src/bin/a.rs7
-rw-r--r--examples/boot/application/stm32wl/src/bin/a.rs7
-rw-r--r--examples/boot/bootloader/nrf/src/main.rs6
-rw-r--r--examples/boot/bootloader/stm32/src/main.rs8
16 files changed, 431 insertions, 293 deletions
diff --git a/embassy-boot/README.md b/embassy-boot/README.md
new file mode 100644
index 000000000..414405377
--- /dev/null
+++ b/embassy-boot/README.md
@@ -0,0 +1,30 @@
1# embassy-boot
2
3An [Embassy](https://embassy.dev) project.
4
5A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks.
6
7The bootloader can be used either as a library or be flashed directly with the default configuration derived from linker scripts.
8
9By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself.
10
11## Hardware support
12
13The bootloader supports different hardware in separate crates:
14
15* `embassy-boot-nrf` - for the nRF microcontrollers.
16* `embassy-boot-stm32` - for the STM32 microcontrollers.
17
18## Minimum supported Rust version (MSRV)
19
20`embassy-boot` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals.
21
22## License
23
24This work is licensed under either of
25
26- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
27 <http://www.apache.org/licenses/LICENSE-2.0>)
28- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
29
30at your option.
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 51e1056cf..e8ebe628d 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -1,53 +1,54 @@
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)]
5#![no_std] 3#![no_std]
6///! embassy-boot is a bootloader and firmware updater for embedded devices with flash 4#![warn(missing_docs)]
7///! storage implemented using embedded-storage 5#![doc = include_str!("../../README.md")]
8///!
9///! The bootloader works in conjunction with the firmware application, and only has the
10///! ability to manage two flash banks with an active and a updatable part. It implements
11///! a swap algorithm that is power-failure safe, and allows reverting to the previous
12///! version of the firmware, should the application crash and fail to mark itself as booted.
13///!
14///! This library is intended to be used by platform-specific bootloaders, such as embassy-boot-nrf,
15///! which defines the limits and flash type for that particular platform.
16///!
17mod fmt; 6mod fmt;
18 7
19use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; 8use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
20use embedded_storage_async::nor_flash::AsyncNorFlash; 9use embedded_storage_async::nor_flash::AsyncNorFlash;
21 10
22const BOOT_MAGIC: u8 = 0xD0; 11const BOOT_MAGIC: u8 = 0xD0;
23const SWAP_MAGIC: u8 = 0xF0; 12const SWAP_MAGIC: u8 = 0xF0;
24 13
14/// A region in flash used by the bootloader.
25#[derive(Copy, Clone, Debug)] 15#[derive(Copy, Clone, Debug)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))] 16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27pub struct Partition { 17pub struct Partition {
18 /// Start of the flash region.
28 pub from: usize, 19 pub from: usize,
20 /// End of the flash region.
29 pub to: usize, 21 pub to: usize,
30} 22}
31 23
32impl Partition { 24impl Partition {
25 /// Create a new partition with the provided range
33 pub const fn new(from: usize, to: usize) -> Self { 26 pub const fn new(from: usize, to: usize) -> Self {
34 Self { from, to } 27 Self { from, to }
35 } 28 }
29
30 /// Return the length of the partition
36 pub const fn len(&self) -> usize { 31 pub const fn len(&self) -> usize {
37 self.to - self.from 32 self.to - self.from
38 } 33 }
39} 34}
40 35
41#[derive(PartialEq, Debug)] 36/// The state of the bootloader after running prepare.
37#[derive(PartialEq, Eq, Debug)]
42#[cfg_attr(feature = "defmt", derive(defmt::Format))] 38#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43pub enum State { 39pub enum State {
40 /// Bootloader is ready to boot the active partition.
44 Boot, 41 Boot,
42 /// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
45 Swap, 43 Swap,
46} 44}
47 45
48#[derive(PartialEq, Debug)] 46/// Errors returned by bootloader
47#[derive(PartialEq, Eq, Debug)]
49pub enum BootError { 48pub enum BootError {
49 /// Error from flash.
50 Flash(NorFlashErrorKind), 50 Flash(NorFlashErrorKind),
51 /// Invalid bootloader magic
51 BadMagic, 52 BadMagic,
52} 53}
53 54
@@ -60,19 +61,39 @@ where
60 } 61 }
61} 62}
62 63
63pub trait FlashConfig { 64/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
64 const BLOCK_SIZE: usize; 65#[repr(align(32))]
65 const ERASE_VALUE: u8; 66pub struct AlignedBuffer<const N: usize>(pub [u8; N]);
66 type FLASH: NorFlash + ReadNorFlash; 67
68impl<const N: usize> AsRef<[u8]> for AlignedBuffer<N> {
69 fn as_ref(&self) -> &[u8] {
70 &self.0
71 }
72}
73
74impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
75 fn as_mut(&mut self) -> &mut [u8] {
76 &mut self.0
77 }
78}
67 79
68 fn flash(&mut self) -> &mut Self::FLASH; 80/// Extension of the embedded-storage flash type information with block size and erase value.
81pub trait Flash: NorFlash + ReadNorFlash {
82 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase
83 /// size of the flash, but for external QSPI flash modules, this can be lower.
84 const BLOCK_SIZE: usize;
85 /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value.
86 const ERASE_VALUE: u8 = 0xFF;
69} 87}
70 88
71/// Trait defining the flash handles used for active and DFU partition 89/// Trait defining the flash handles used for active and DFU partition
72pub trait FlashProvider { 90pub trait FlashConfig {
73 type STATE: FlashConfig; 91 /// Flash type used for the state partition.
74 type ACTIVE: FlashConfig; 92 type STATE: Flash;
75 type DFU: FlashConfig; 93 /// Flash type used for the active partition.
94 type ACTIVE: Flash;
95 /// Flash type used for the dfu partition.
96 type DFU: Flash;
76 97
77 /// Return flash instance used to write/read to/from active partition. 98 /// Return flash instance used to write/read to/from active partition.
78 fn active(&mut self) -> &mut Self::ACTIVE; 99 fn active(&mut self) -> &mut Self::ACTIVE;
@@ -84,9 +105,7 @@ pub trait FlashProvider {
84 105
85/// BootLoader works with any flash implementing embedded_storage and can also work with 106/// BootLoader works with any flash implementing embedded_storage and can also work with
86/// different page sizes and flash write sizes. 107/// different page sizes and flash write sizes.
87/// 108pub struct BootLoader {
88/// The PAGE_SIZE const parameter must be a multiple of the ACTIVE and DFU page sizes.
89pub struct BootLoader<const PAGE_SIZE: usize> {
90 // Page with current state of bootloader. The state partition has the following format: 109 // Page with current state of bootloader. The state partition has the following format:
91 // | Range | Description | 110 // | Range | Description |
92 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 111 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
@@ -98,15 +117,16 @@ pub struct BootLoader<const PAGE_SIZE: usize> {
98 dfu: Partition, 117 dfu: Partition,
99} 118}
100 119
101impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { 120impl BootLoader {
121 /// Create a new instance of a bootloader with the given partitions.
122 ///
123 /// - All partitions must be aligned with the PAGE_SIZE const generic parameter.
124 /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition.
102 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 125 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
103 assert_eq!(active.len() % PAGE_SIZE, 0);
104 assert_eq!(dfu.len() % PAGE_SIZE, 0);
105 // DFU partition must have an extra page
106 assert!(dfu.len() - active.len() >= PAGE_SIZE);
107 Self { active, dfu, state } 126 Self { active, dfu, state }
108 } 127 }
109 128
129 /// Return the boot address for the active partition.
110 pub fn boot_address(&self) -> usize { 130 pub fn boot_address(&self) -> usize {
111 self.active.from 131 self.active.from
112 } 132 }
@@ -194,44 +214,43 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
194 /// | DFU | 3 | 3 | 2 | 1 | 3 | 214 /// | DFU | 3 | 3 | 2 | 1 | 3 |
195 /// +-----------+--------------+--------+--------+--------+--------+ 215 /// +-----------+--------------+--------+--------+--------+--------+
196 /// 216 ///
197 pub fn prepare_boot<P: FlashProvider>(&mut self, p: &mut P) -> Result<State, BootError> 217 pub fn prepare_boot<P: FlashConfig>(
198 where 218 &mut self,
199 [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, 219 p: &mut P,
200 [(); <<P as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:, 220 magic: &mut [u8],
201 { 221 page: &mut [u8],
222 ) -> Result<State, BootError> {
202 // Ensure we have enough progress pages to store copy progress 223 // Ensure we have enough progress pages to store copy progress
203 assert!( 224 assert_eq!(self.active.len() % page.len(), 0);
204 self.active.len() / PAGE_SIZE 225 assert_eq!(self.dfu.len() % page.len(), 0);
205 <= (self.state.len() - <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE) 226 assert!(self.dfu.len() - self.active.len() >= page.len());
206 / <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE 227 assert!(self.active.len() / page.len() <= (self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE);
207 ); 228 assert_eq!(magic.len(), P::STATE::WRITE_SIZE);
208 229
209 // Copy contents from partition N to active 230 // Copy contents from partition N to active
210 let state = self.read_state(p.state())?; 231 let state = self.read_state(p, magic)?;
211 match state { 232 match state {
212 State::Swap => { 233 State::Swap => {
213 // 234 //
214 // Check if we already swapped. If we're in the swap state, this means we should revert 235 // Check if we already swapped. If we're in the swap state, this means we should revert
215 // since the app has failed to mark boot as successful 236 // since the app has failed to mark boot as successful
216 // 237 //
217 if !self.is_swapped(p.state())? { 238 if !self.is_swapped(p, magic, page)? {
218 trace!("Swapping"); 239 trace!("Swapping");
219 self.swap(p)?; 240 self.swap(p, magic, page)?;
220 trace!("Swapping done"); 241 trace!("Swapping done");
221 } else { 242 } else {
222 trace!("Reverting"); 243 trace!("Reverting");
223 self.revert(p)?; 244 self.revert(p, magic, page)?;
224 245
225 // Overwrite magic and reset progress 246 // Overwrite magic and reset progress
226 let fstate = p.state().flash(); 247 let fstate = p.state();
227 let aligned = Aligned( 248 magic.fill(!P::STATE::ERASE_VALUE);
228 [!P::STATE::ERASE_VALUE; <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE], 249 fstate.write(self.state.from as u32, magic)?;
229 );
230 fstate.write(self.state.from as u32, &aligned.0)?;
231 fstate.erase(self.state.from as u32, self.state.to as u32)?; 250 fstate.erase(self.state.from as u32, self.state.to as u32)?;
232 let aligned = 251
233 Aligned([BOOT_MAGIC; <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]); 252 magic.fill(BOOT_MAGIC);
234 fstate.write(self.state.from as u32, &aligned.0)?; 253 fstate.write(self.state.from as u32, magic)?;
235 } 254 }
236 } 255 }
237 _ => {} 256 _ => {}
@@ -239,166 +258,152 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
239 Ok(state) 258 Ok(state)
240 } 259 }
241 260
242 fn is_swapped<P: FlashConfig>(&mut self, p: &mut P) -> Result<bool, BootError> 261 fn is_swapped<P: FlashConfig>(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<bool, BootError> {
243 where 262 let page_size = page.len();
244 [(); P::FLASH::WRITE_SIZE]:, 263 let page_count = self.active.len() / page_size;
245 { 264 let progress = self.current_progress(p, magic)?;
246 let page_count = self.active.len() / P::FLASH::ERASE_SIZE;
247 let progress = self.current_progress(p)?;
248 265
249 Ok(progress >= page_count * 2) 266 Ok(progress >= page_count * 2)
250 } 267 }
251 268
252 fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError> 269 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> {
253 where 270 let write_size = aligned.len();
254 [(); P::FLASH::WRITE_SIZE]:,
255 {
256 let write_size = P::FLASH::WRITE_SIZE;
257 let max_index = ((self.state.len() - write_size) / write_size) - 1; 271 let max_index = ((self.state.len() - write_size) / write_size) - 1;
258 let flash = p.flash(); 272 aligned.fill(!P::STATE::ERASE_VALUE);
259 let mut aligned = Aligned([!P::ERASE_VALUE; P::FLASH::WRITE_SIZE]); 273
274 let flash = config.state();
260 for i in 0..max_index { 275 for i in 0..max_index {
261 flash.read((self.state.from + write_size + i * write_size) as u32, &mut aligned.0)?; 276 flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?;
262 if aligned.0 == [P::ERASE_VALUE; P::FLASH::WRITE_SIZE] { 277
278 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) {
263 return Ok(i); 279 return Ok(i);
264 } 280 }
265 } 281 }
266 Ok(max_index) 282 Ok(max_index)
267 } 283 }
268 284
269 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> 285 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> {
270 where 286 let flash = p.state();
271 [(); P::FLASH::WRITE_SIZE]:, 287 let write_size = magic.len();
272 {
273 let flash = p.flash();
274 let write_size = P::FLASH::WRITE_SIZE;
275 let w = self.state.from + write_size + idx * write_size; 288 let w = self.state.from + write_size + idx * write_size;
276 let aligned = Aligned([!P::ERASE_VALUE; P::FLASH::WRITE_SIZE]); 289
277 flash.write(w as u32, &aligned.0)?; 290 let aligned = magic;
291 aligned.fill(!P::STATE::ERASE_VALUE);
292 flash.write(w as u32, aligned)?;
278 Ok(()) 293 Ok(())
279 } 294 }
280 295
281 fn active_addr(&self, n: usize) -> usize { 296 fn active_addr(&self, n: usize, page_size: usize) -> usize {
282 self.active.from + n * PAGE_SIZE 297 self.active.from + n * page_size
283 } 298 }
284 299
285 fn dfu_addr(&self, n: usize) -> usize { 300 fn dfu_addr(&self, n: usize, page_size: usize) -> usize {
286 self.dfu.from + n * PAGE_SIZE 301 self.dfu.from + n * page_size
287 } 302 }
288 303
289 fn copy_page_once_to_active<P: FlashProvider>( 304 fn copy_page_once_to_active<P: FlashConfig>(
290 &mut self, 305 &mut self,
291 idx: usize, 306 idx: usize,
292 from_page: usize, 307 from_page: usize,
293 to_page: usize, 308 to_page: usize,
294 p: &mut P, 309 p: &mut P,
295 ) -> Result<(), BootError> 310 magic: &mut [u8],
296 where 311 page: &mut [u8],
297 [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, 312 ) -> Result<(), BootError> {
298 { 313 let buf = page;
299 let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; 314 if self.current_progress(p, magic)? <= idx {
300 if self.current_progress(p.state())? <= idx {
301 let mut offset = from_page; 315 let mut offset = from_page;
302 for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { 316 for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) {
303 p.dfu().flash().read(offset as u32, chunk)?; 317 p.dfu().read(offset as u32, chunk)?;
304 offset += chunk.len(); 318 offset += chunk.len();
305 } 319 }
306 320
307 p.active().flash().erase(to_page as u32, (to_page + PAGE_SIZE) as u32)?; 321 p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?;
308 322
309 let mut offset = to_page; 323 let mut offset = to_page;
310 for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { 324 for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) {
311 p.active().flash().write(offset as u32, &chunk)?; 325 p.active().write(offset as u32, chunk)?;
312 offset += chunk.len(); 326 offset += chunk.len();
313 } 327 }
314 self.update_progress(idx, p.state())?; 328 self.update_progress(idx, p, magic)?;
315 } 329 }
316 Ok(()) 330 Ok(())
317 } 331 }
318 332
319 fn copy_page_once_to_dfu<P: FlashProvider>( 333 fn copy_page_once_to_dfu<P: FlashConfig>(
320 &mut self, 334 &mut self,
321 idx: usize, 335 idx: usize,
322 from_page: usize, 336 from_page: usize,
323 to_page: usize, 337 to_page: usize,
324 p: &mut P, 338 p: &mut P,
325 ) -> Result<(), BootError> 339 magic: &mut [u8],
326 where 340 page: &mut [u8],
327 [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, 341 ) -> Result<(), BootError> {
328 { 342 let buf = page;
329 let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; 343 if self.current_progress(p, magic)? <= idx {
330 if self.current_progress(p.state())? <= idx {
331 let mut offset = from_page; 344 let mut offset = from_page;
332 for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { 345 for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) {
333 p.active().flash().read(offset as u32, chunk)?; 346 p.active().read(offset as u32, chunk)?;
334 offset += chunk.len(); 347 offset += chunk.len();
335 } 348 }
336 349
337 p.dfu().flash().erase(to_page as u32, (to_page + PAGE_SIZE) as u32)?; 350 p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?;
338 351
339 let mut offset = to_page; 352 let mut offset = to_page;
340 for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { 353 for chunk in buf.chunks(P::DFU::BLOCK_SIZE) {
341 p.dfu().flash().write(offset as u32, chunk)?; 354 p.dfu().write(offset as u32, chunk)?;
342 offset += chunk.len(); 355 offset += chunk.len();
343 } 356 }
344 self.update_progress(idx, p.state())?; 357 self.update_progress(idx, p, magic)?;
345 } 358 }
346 Ok(()) 359 Ok(())
347 } 360 }
348 361
349 fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> 362 fn swap<P: FlashConfig>(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> {
350 where 363 let page_size = page.len();
351 [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, 364 let page_count = self.active.len() / page_size;
352 {
353 let page_count = self.active.len() / PAGE_SIZE;
354 trace!("Page count: {}", page_count); 365 trace!("Page count: {}", page_count);
355 for page in 0..page_count { 366 for page_num in 0..page_count {
356 trace!("COPY PAGE {}", page); 367 trace!("COPY PAGE {}", page_num);
357 // Copy active page to the 'next' DFU page. 368 // Copy active page to the 'next' DFU page.
358 let active_page = self.active_addr(page_count - 1 - page); 369 let active_page = self.active_addr(page_count - 1 - page_num, page_size);
359 let dfu_page = self.dfu_addr(page_count - page); 370 let dfu_page = self.dfu_addr(page_count - page_num, page_size);
360 //trace!("Copy active {} to dfu {}", active_page, dfu_page); 371 //trace!("Copy active {} to dfu {}", active_page, dfu_page);
361 self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?; 372 self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?;
362 373
363 // Copy DFU page to the active page 374 // Copy DFU page to the active page
364 let active_page = self.active_addr(page_count - 1 - page); 375 let active_page = self.active_addr(page_count - 1 - page_num, page_size);
365 let dfu_page = self.dfu_addr(page_count - 1 - page); 376 let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size);
366 //trace!("Copy dfy {} to active {}", dfu_page, active_page); 377 //trace!("Copy dfy {} to active {}", dfu_page, active_page);
367 self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?; 378 self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?;
368 } 379 }
369 380
370 Ok(()) 381 Ok(())
371 } 382 }
372 383
373 fn revert<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> 384 fn revert<P: FlashConfig>(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> {
374 where 385 let page_size = page.len();
375 [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, 386 let page_count = self.active.len() / page_size;
376 { 387 for page_num in 0..page_count {
377 let page_count = self.active.len() / PAGE_SIZE;
378 for page in 0..page_count {
379 // Copy the bad active page to the DFU page 388 // Copy the bad active page to the DFU page
380 let active_page = self.active_addr(page); 389 let active_page = self.active_addr(page_num, page_size);
381 let dfu_page = self.dfu_addr(page); 390 let dfu_page = self.dfu_addr(page_num, page_size);
382 self.copy_page_once_to_dfu(page_count * 2 + page * 2, active_page, dfu_page, p)?; 391 self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?;
383 392
384 // Copy the DFU page back to the active page 393 // Copy the DFU page back to the active page
385 let active_page = self.active_addr(page); 394 let active_page = self.active_addr(page_num, page_size);
386 let dfu_page = self.dfu_addr(page + 1); 395 let dfu_page = self.dfu_addr(page_num + 1, page_size);
387 self.copy_page_once_to_active(page_count * 2 + page * 2 + 1, dfu_page, active_page, p)?; 396 self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?;
388 } 397 }
389 398
390 Ok(()) 399 Ok(())
391 } 400 }
392 401
393 fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError> 402 fn read_state<P: FlashConfig>(&mut self, config: &mut P, magic: &mut [u8]) -> Result<State, BootError> {
394 where 403 let flash = config.state();
395 [(); P::FLASH::WRITE_SIZE]:, 404 flash.read(self.state.from as u32, magic)?;
396 {
397 let mut magic: [u8; P::FLASH::WRITE_SIZE] = [0; P::FLASH::WRITE_SIZE];
398 let flash = p.flash();
399 flash.read(self.state.from as u32, &mut magic)?;
400 405
401 if magic == [SWAP_MAGIC; P::FLASH::WRITE_SIZE] { 406 if !magic.iter().any(|&b| b != SWAP_MAGIC) {
402 Ok(State::Swap) 407 Ok(State::Swap)
403 } else { 408 } else {
404 Ok(State::Boot) 409 Ok(State::Boot)
@@ -406,108 +411,149 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
406 } 411 }
407} 412}
408 413
409/// Convenience provider that uses a single flash for everything 414/// Convenience provider that uses a single flash for all partitions.
410pub struct SingleFlashProvider<'a, F, const ERASE_VALUE: u8 = 0xFF> 415pub struct SingleFlashConfig<'a, F>
411where 416where
412 F: NorFlash + ReadNorFlash, 417 F: Flash,
413{ 418{
414 config: SingleFlashConfig<'a, F, ERASE_VALUE>, 419 flash: &'a mut F,
415} 420}
416 421
417impl<'a, F, const ERASE_VALUE: u8> SingleFlashProvider<'a, F, ERASE_VALUE> 422impl<'a, F> SingleFlashConfig<'a, F>
418where 423where
419 F: NorFlash + ReadNorFlash, 424 F: Flash,
420{ 425{
426 /// Create a provider for a single flash.
421 pub fn new(flash: &'a mut F) -> Self { 427 pub fn new(flash: &'a mut F) -> Self {
422 Self { 428 Self { flash }
423 config: SingleFlashConfig { flash }, 429 }
424 } 430}
431
432impl<'a, F> FlashConfig for SingleFlashConfig<'a, F>
433where
434 F: Flash,
435{
436 type STATE = F;
437 type ACTIVE = F;
438 type DFU = F;
439
440 fn active(&mut self) -> &mut Self::STATE {
441 self.flash
442 }
443 fn dfu(&mut self) -> &mut Self::ACTIVE {
444 self.flash
445 }
446 fn state(&mut self) -> &mut Self::DFU {
447 self.flash
425 } 448 }
426} 449}
427 450
428pub struct SingleFlashConfig<'a, F, const ERASE_VALUE: u8 = 0xFF> 451/// A flash wrapper implementing the Flash and embedded_storage traits.
452pub struct BootFlash<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF>
429where 453where
430 F: NorFlash + ReadNorFlash, 454 F: NorFlash + ReadNorFlash,
431{ 455{
432 flash: &'a mut F, 456 flash: &'a mut F,
433} 457}
434 458
435impl<'a, F> FlashProvider for SingleFlashProvider<'a, F> 459impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE>
436where 460where
437 F: NorFlash + ReadNorFlash, 461 F: NorFlash + ReadNorFlash,
438{ 462{
439 type STATE = SingleFlashConfig<'a, F>; 463 /// Create a new instance of a bootable flash
440 type ACTIVE = SingleFlashConfig<'a, F>; 464 pub fn new(flash: &'a mut F) -> Self {
441 type DFU = SingleFlashConfig<'a, F>; 465 Self { flash }
442
443 fn active(&mut self) -> &mut Self::STATE {
444 &mut self.config
445 }
446 fn dfu(&mut self) -> &mut Self::ACTIVE {
447 &mut self.config
448 }
449 fn state(&mut self) -> &mut Self::DFU {
450 &mut self.config
451 } 466 }
452} 467}
453 468
454impl<'a, F, const ERASE_VALUE: u8> FlashConfig for SingleFlashConfig<'a, F, ERASE_VALUE> 469impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE>
455where 470where
456 F: NorFlash + ReadNorFlash, 471 F: NorFlash + ReadNorFlash,
457{ 472{
458 const BLOCK_SIZE: usize = F::ERASE_SIZE; 473 const BLOCK_SIZE: usize = BLOCK_SIZE;
459 const ERASE_VALUE: u8 = ERASE_VALUE; 474 const ERASE_VALUE: u8 = ERASE_VALUE;
460 type FLASH = F; 475}
461 fn flash(&mut self) -> &mut F { 476
462 self.flash 477impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE>
478where
479 F: ReadNorFlash + NorFlash,
480{
481 type Error = F::Error;
482}
483
484impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE>
485where
486 F: ReadNorFlash + NorFlash,
487{
488 const WRITE_SIZE: usize = F::WRITE_SIZE;
489 const ERASE_SIZE: usize = F::ERASE_SIZE;
490
491 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
492 F::erase(self.flash, from, to)
493 }
494
495 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
496 F::write(self.flash, offset, bytes)
463 } 497 }
464} 498}
465 499
466/// Convenience provider that uses a single flash for everything 500impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE>
467pub struct MultiFlashProvider<'a, ACTIVE, STATE, DFU>
468where 501where
469 ACTIVE: NorFlash + ReadNorFlash, 502 F: ReadNorFlash + NorFlash,
470 STATE: NorFlash + ReadNorFlash,
471 DFU: NorFlash + ReadNorFlash,
472{ 503{
473 active: SingleFlashConfig<'a, ACTIVE>, 504 const READ_SIZE: usize = F::READ_SIZE;
474 state: SingleFlashConfig<'a, STATE>, 505
475 dfu: SingleFlashConfig<'a, DFU>, 506 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
507 F::read(self.flash, offset, bytes)
508 }
509
510 fn capacity(&self) -> usize {
511 F::capacity(self.flash)
512 }
476} 513}
477 514
478impl<'a, ACTIVE, STATE, DFU> MultiFlashProvider<'a, ACTIVE, STATE, DFU> 515/// Convenience flash provider that uses separate flash instances for each partition.
516pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU>
479where 517where
480 ACTIVE: NorFlash + ReadNorFlash, 518 ACTIVE: Flash,
481 STATE: NorFlash + ReadNorFlash, 519 STATE: Flash,
482 DFU: NorFlash + ReadNorFlash, 520 DFU: Flash,
483{ 521{
522 active: &'a mut ACTIVE,
523 state: &'a mut STATE,
524 dfu: &'a mut DFU,
525}
526
527impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU>
528where
529 ACTIVE: Flash,
530 STATE: Flash,
531 DFU: Flash,
532{
533 /// Create a new flash provider with separate configuration for all three partitions.
484 pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { 534 pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self {
485 Self { 535 Self { active, state, dfu }
486 active: SingleFlashConfig { flash: active },
487 state: SingleFlashConfig { flash: state },
488 dfu: SingleFlashConfig { flash: dfu },
489 }
490 } 536 }
491} 537}
492 538
493impl<'a, ACTIVE, STATE, DFU> FlashProvider for MultiFlashProvider<'a, ACTIVE, STATE, DFU> 539impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU>
494where 540where
495 ACTIVE: NorFlash + ReadNorFlash, 541 ACTIVE: Flash,
496 STATE: NorFlash + ReadNorFlash, 542 STATE: Flash,
497 DFU: NorFlash + ReadNorFlash, 543 DFU: Flash,
498{ 544{
499 type STATE = SingleFlashConfig<'a, STATE>; 545 type STATE = STATE;
500 type ACTIVE = SingleFlashConfig<'a, ACTIVE>; 546 type ACTIVE = ACTIVE;
501 type DFU = SingleFlashConfig<'a, DFU>; 547 type DFU = DFU;
502 548
503 fn active(&mut self) -> &mut Self::ACTIVE { 549 fn active(&mut self) -> &mut Self::ACTIVE {
504 &mut self.active 550 self.active
505 } 551 }
506 fn dfu(&mut self) -> &mut Self::DFU { 552 fn dfu(&mut self) -> &mut Self::DFU {
507 &mut self.dfu 553 self.dfu
508 } 554 }
509 fn state(&mut self) -> &mut Self::STATE { 555 fn state(&mut self) -> &mut Self::STATE {
510 &mut self.state 556 self.state
511 } 557 }
512} 558}
513 559
@@ -518,10 +564,6 @@ pub struct FirmwareUpdater {
518 dfu: Partition, 564 dfu: Partition,
519} 565}
520 566
521// NOTE: Aligned to the largest write size supported by flash
522#[repr(align(32))]
523pub struct Aligned<const N: usize>([u8; N]);
524
525impl Default for FirmwareUpdater { 567impl Default for FirmwareUpdater {
526 fn default() -> Self { 568 fn default() -> Self {
527 extern "C" { 569 extern "C" {
@@ -551,6 +593,7 @@ impl Default for FirmwareUpdater {
551} 593}
552 594
553impl FirmwareUpdater { 595impl FirmwareUpdater {
596 /// Create a firmware updater instance with partition ranges for the update and state partitions.
554 pub const fn new(dfu: Partition, state: Partition) -> Self { 597 pub const fn new(dfu: Partition, state: Partition) -> Self {
555 Self { dfu, state } 598 Self { dfu, state }
556 } 599 }
@@ -560,23 +603,24 @@ impl FirmwareUpdater {
560 self.dfu.len() 603 self.dfu.len()
561 } 604 }
562 605
563 /// Instruct bootloader that DFU should commence at next boot. 606 /// Mark to trigger firmware swap on next boot.
564 /// Must be provided with an aligned buffer to use for reading and writing magic; 607 ///
565 pub async fn update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> 608 /// # Safety
566 where 609 ///
567 [(); F::WRITE_SIZE]:, 610 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
568 { 611 pub async fn mark_updated<F: AsyncNorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> {
569 let mut aligned = Aligned([0; { F::WRITE_SIZE }]); 612 assert_eq!(aligned.len(), F::WRITE_SIZE);
570 self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await 613 self.set_magic(aligned, SWAP_MAGIC, flash).await
571 } 614 }
572 615
573 /// Mark firmware boot successfully 616 /// Mark firmware boot successful and stop rollback on reset.
574 pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> 617 ///
575 where 618 /// # Safety
576 [(); F::WRITE_SIZE]:, 619 ///
577 { 620 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
578 let mut aligned = Aligned([0; { F::WRITE_SIZE }]); 621 pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> {
579 self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).await 622 assert_eq!(aligned.len(), F::WRITE_SIZE);
623 self.set_magic(aligned, BOOT_MAGIC, flash).await
580 } 624 }
581 625
582 async fn set_magic<F: AsyncNorFlash>( 626 async fn set_magic<F: AsyncNorFlash>(
@@ -587,7 +631,7 @@ impl FirmwareUpdater {
587 ) -> Result<(), F::Error> { 631 ) -> Result<(), F::Error> {
588 flash.read(self.state.from as u32, aligned).await?; 632 flash.read(self.state.from as u32, aligned).await?;
589 633
590 if aligned.iter().find(|&&b| b != magic).is_some() { 634 if aligned.iter().any(|&b| b != magic) {
591 aligned.fill(0); 635 aligned.fill(0);
592 636
593 flash.write(self.state.from as u32, aligned).await?; 637 flash.write(self.state.from as u32, aligned).await?;
@@ -599,7 +643,13 @@ impl FirmwareUpdater {
599 Ok(()) 643 Ok(())
600 } 644 }
601 645
602 // Write to a region of the DFU page 646 /// Write data to a flash page.
647 ///
648 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
649 ///
650 /// # Safety
651 ///
652 /// Failing to meet alignment and size requirements may result in a panic.
603 pub async fn write_firmware<F: AsyncNorFlash>( 653 pub async fn write_firmware<F: AsyncNorFlash>(
604 &mut self, 654 &mut self,
605 offset: usize, 655 offset: usize,
@@ -668,7 +718,7 @@ mod tests {
668 #[test] 718 #[test]
669 fn test_bad_magic() { 719 fn test_bad_magic() {
670 let mut flash = MemFlash([0xff; 131072]); 720 let mut flash = MemFlash([0xff; 131072]);
671 let mut flash = SingleFlashProvider::new(&mut flash); 721 let mut flash = SingleFlashConfig::new(&mut flash);
672 722
673 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); 723 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);
674 724
@@ -687,11 +737,16 @@ mod tests {
687 737
688 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 738 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]);
689 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); 739 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
690 let mut flash = SingleFlashProvider::new(&mut flash); 740 let mut flash = SingleFlashConfig::new(&mut flash);
691 741
692 let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); 742 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
693 743
694 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); 744 let mut magic = [0; 4];
745 let mut page = [0; 4096];
746 assert_eq!(
747 State::Boot,
748 bootloader.prepare_boot(&mut flash, &mut magic, &mut page).unwrap()
749 );
695 } 750 }
696 751
697 #[test] 752 #[test]
@@ -703,24 +758,27 @@ mod tests {
703 758
704 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 759 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
705 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 760 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
761 let mut aligned = [0; 4];
706 762
707 for i in ACTIVE.from..ACTIVE.to { 763 for i in ACTIVE.from..ACTIVE.to {
708 flash.0[i] = original[i - ACTIVE.from]; 764 flash.0[i] = original[i - ACTIVE.from];
709 } 765 }
710 766
711 let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); 767 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
712 let mut updater = FirmwareUpdater::new(DFU, STATE); 768 let mut updater = FirmwareUpdater::new(DFU, STATE);
713 let mut offset = 0; 769 let mut offset = 0;
714 for chunk in update.chunks(4096) { 770 for chunk in update.chunks(4096) {
715 block_on(updater.write_firmware(offset, &chunk, &mut flash, 4096)).unwrap(); 771 block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap();
716 offset += chunk.len(); 772 offset += chunk.len();
717 } 773 }
718 block_on(updater.update(&mut flash)).unwrap(); 774 block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap();
719 775
776 let mut magic = [0; 4];
777 let mut page = [0; 4096];
720 assert_eq!( 778 assert_eq!(
721 State::Swap, 779 State::Swap,
722 bootloader 780 bootloader
723 .prepare_boot(&mut SingleFlashProvider::new(&mut flash)) 781 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page)
724 .unwrap() 782 .unwrap()
725 ); 783 );
726 784
@@ -737,7 +795,7 @@ mod tests {
737 assert_eq!( 795 assert_eq!(
738 State::Swap, 796 State::Swap,
739 bootloader 797 bootloader
740 .prepare_boot(&mut SingleFlashProvider::new(&mut flash)) 798 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page)
741 .unwrap() 799 .unwrap()
742 ); 800 );
743 801
@@ -751,11 +809,11 @@ mod tests {
751 } 809 }
752 810
753 // Mark as booted 811 // Mark as booted
754 block_on(updater.mark_booted(&mut flash)).unwrap(); 812 block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap();
755 assert_eq!( 813 assert_eq!(
756 State::Boot, 814 State::Boot,
757 bootloader 815 bootloader
758 .prepare_boot(&mut SingleFlashProvider::new(&mut flash)) 816 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page)
759 .unwrap() 817 .unwrap()
760 ); 818 );
761 } 819 }
@@ -769,6 +827,7 @@ mod tests {
769 let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); 827 let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]);
770 let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); 828 let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]);
771 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 829 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]);
830 let mut aligned = [0; 4];
772 831
773 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 832 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
774 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 833 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
@@ -781,16 +840,23 @@ mod tests {
781 840
782 let mut offset = 0; 841 let mut offset = 0;
783 for chunk in update.chunks(2048) { 842 for chunk in update.chunks(2048) {
784 block_on(updater.write_firmware(offset, &chunk, &mut dfu, chunk.len())).unwrap(); 843 block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap();
785 offset += chunk.len(); 844 offset += chunk.len();
786 } 845 }
787 block_on(updater.update(&mut state)).unwrap(); 846 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
847
848 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
849 let mut magic = [0; 4];
850 let mut page = [0; 4096];
788 851
789 let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE);
790 assert_eq!( 852 assert_eq!(
791 State::Swap, 853 State::Swap,
792 bootloader 854 bootloader
793 .prepare_boot(&mut MultiFlashProvider::new(&mut active, &mut state, &mut dfu,)) 855 .prepare_boot(
856 &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu),
857 &mut magic,
858 &mut page
859 )
794 .unwrap() 860 .unwrap()
795 ); 861 );
796 862
@@ -810,6 +876,7 @@ mod tests {
810 const ACTIVE: Partition = Partition::new(4096, 16384); 876 const ACTIVE: Partition = Partition::new(4096, 16384);
811 const DFU: Partition = Partition::new(0, 16384); 877 const DFU: Partition = Partition::new(0, 16384);
812 878
879 let mut aligned = [0; 4];
813 let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); 880 let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]);
814 let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); 881 let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]);
815 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 882 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]);
@@ -825,16 +892,22 @@ mod tests {
825 892
826 let mut offset = 0; 893 let mut offset = 0;
827 for chunk in update.chunks(4096) { 894 for chunk in update.chunks(4096) {
828 block_on(updater.write_firmware(offset, &chunk, &mut dfu, chunk.len())).unwrap(); 895 block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap();
829 offset += chunk.len(); 896 offset += chunk.len();
830 } 897 }
831 block_on(updater.update(&mut state)).unwrap(); 898 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
832 899
833 let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); 900 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
901 let mut magic = [0; 4];
902 let mut page = [0; 4096];
834 assert_eq!( 903 assert_eq!(
835 State::Swap, 904 State::Swap,
836 bootloader 905 bootloader
837 .prepare_boot(&mut MultiFlashProvider::new(&mut active, &mut state, &mut dfu,)) 906 .prepare_boot(
907 &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,),
908 &mut magic,
909 &mut page
910 )
838 .unwrap() 911 .unwrap()
839 ); 912 );
840 913
@@ -899,6 +972,13 @@ mod tests {
899 } 972 }
900 } 973 }
901 974
975 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> super::Flash
976 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
977 {
978 const BLOCK_SIZE: usize = ERASE_SIZE;
979 const ERASE_VALUE: u8 = 0xFF;
980 }
981
902 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash 982 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
903 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> 983 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
904 { 984 {
diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs
index 2d6b837cb..0c14781a2 100644
--- a/embassy-boot/nrf/src/lib.rs
+++ b/embassy-boot/nrf/src/lib.rs
@@ -1,19 +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)] 4#![warn(missing_docs)]
5#![feature(generic_const_exprs)] 5#![doc = include_str!("../../README.md")]
6
7mod fmt; 6mod fmt;
8 7
9pub use embassy_boot::{FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider}; 8pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig};
10use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; 9use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE};
11use embassy_nrf::peripherals::WDT; 10use embassy_nrf::peripherals::WDT;
12use embassy_nrf::wdt; 11use embassy_nrf::wdt;
13use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; 12use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
14 13
14/// A bootloader for nRF devices.
15pub struct BootLoader { 15pub struct BootLoader {
16 boot: embassy_boot::BootLoader<PAGE_SIZE>, 16 boot: embassy_boot::BootLoader,
17 magic: AlignedBuffer<4>,
18 page: AlignedBuffer<PAGE_SIZE>,
17} 19}
18 20
19impl BootLoader { 21impl BootLoader {
@@ -58,21 +60,25 @@ impl BootLoader {
58 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 60 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
59 Self { 61 Self {
60 boot: embassy_boot::BootLoader::new(active, dfu, state), 62 boot: embassy_boot::BootLoader::new(active, dfu, state),
63 magic: AlignedBuffer([0; 4]),
64 page: AlignedBuffer([0; PAGE_SIZE]),
61 } 65 }
62 } 66 }
63 67
64 /// Boots the application without softdevice mechanisms 68 /// Inspect the bootloader state and perform actions required before booting, such as swapping
65 pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize 69 /// firmware.
66 where 70 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize {
67 [(); <<F as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, 71 match self.boot.prepare_boot(flash, &mut self.magic.0, &mut self.page.0) {
68 [(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
69 {
70 match self.boot.prepare_boot(flash) {
71 Ok(_) => self.boot.boot_address(), 72 Ok(_) => self.boot.boot_address(),
72 Err(_) => panic!("boot prepare error!"), 73 Err(_) => panic!("boot prepare error!"),
73 } 74 }
74 } 75 }
75 76
77 /// Boots the application without softdevice mechanisms.
78 ///
79 /// # Safety
80 ///
81 /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
76 #[cfg(not(feature = "softdevice"))] 82 #[cfg(not(feature = "softdevice"))]
77 pub unsafe fn load(&mut self, start: usize) -> ! { 83 pub unsafe fn load(&mut self, start: usize) -> ! {
78 let mut p = cortex_m::Peripherals::steal(); 84 let mut p = cortex_m::Peripherals::steal();
@@ -81,6 +87,11 @@ impl BootLoader {
81 cortex_m::asm::bootload(start as *const u32) 87 cortex_m::asm::bootload(start as *const u32)
82 } 88 }
83 89
90 /// Boots the application assuming softdevice is present.
91 ///
92 /// # Safety
93 ///
94 /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
84 #[cfg(feature = "softdevice")] 95 #[cfg(feature = "softdevice")]
85 pub unsafe fn load(&mut self, _app: usize) -> ! { 96 pub unsafe fn load(&mut self, _app: usize) -> ! {
86 use nrf_softdevice_mbr as mbr; 97 use nrf_softdevice_mbr as mbr;
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs
index 5a4f2d058..39f080517 100644
--- a/embassy-boot/stm32/src/lib.rs
+++ b/embassy-boot/stm32/src/lib.rs
@@ -1,19 +1,20 @@
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)] 4#![warn(missing_docs)]
5#![feature(generic_const_exprs)] 5#![doc = include_str!("../../README.md")]
6
7mod fmt; 6mod fmt;
8 7
9pub use embassy_boot::{FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider, State}; 8pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State};
10use embedded_storage::nor_flash::NorFlash;
11 9
12pub struct BootLoader<const PAGE_SIZE: usize> { 10/// A bootloader for STM32 devices.
13 boot: embassy_boot::BootLoader<PAGE_SIZE>, 11pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize> {
12 boot: embassy_boot::BootLoader,
13 magic: AlignedBuffer<WRITE_SIZE>,
14 page: AlignedBuffer<PAGE_SIZE>,
14} 15}
15 16
16impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { 17impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> BootLoader<PAGE_SIZE, WRITE_SIZE> {
17 /// Create a new bootloader instance using parameters from linker script 18 /// Create a new bootloader instance using parameters from linker script
18 pub fn default() -> Self { 19 pub fn default() -> Self {
19 extern "C" { 20 extern "C" {
@@ -55,21 +56,25 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
55 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 56 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
56 Self { 57 Self {
57 boot: embassy_boot::BootLoader::new(active, dfu, state), 58 boot: embassy_boot::BootLoader::new(active, dfu, state),
59 magic: AlignedBuffer([0; WRITE_SIZE]),
60 page: AlignedBuffer([0; PAGE_SIZE]),
58 } 61 }
59 } 62 }
60 63
61 /// Boots the application 64 /// Inspect the bootloader state and perform actions required before booting, such as swapping
62 pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize 65 /// firmware.
63 where 66 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize {
64 [(); <<F as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, 67 match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) {
65 [(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
66 {
67 match self.boot.prepare_boot(flash) {
68 Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(), 68 Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(),
69 Err(_) => panic!("boot prepare error!"), 69 Err(_) => panic!("boot prepare error!"),
70 } 70 }
71 } 71 }
72 72
73 /// Boots the application.
74 ///
75 /// # Safety
76 ///
77 /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
73 pub unsafe fn load(&mut self, start: usize) -> ! { 78 pub unsafe fn load(&mut self, start: usize) -> ! {
74 trace!("Loading app at 0x{:x}", start); 79 trace!("Loading app at 0x{:x}", start);
75 #[allow(unused_mut)] 80 #[allow(unused_mut)]
diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs
index bd8fa3246..133a3e678 100644
--- a/examples/boot/application/nrf/src/bin/a.rs
+++ b/examples/boot/application/nrf/src/bin/a.rs
@@ -36,7 +36,8 @@ async fn main(_spawner: Spawner) {
36 updater.write_firmware(offset, &buf, &mut nvmc, 4096).await.unwrap(); 36 updater.write_firmware(offset, &buf, &mut nvmc, 4096).await.unwrap();
37 offset += chunk.len(); 37 offset += chunk.len();
38 } 38 }
39 updater.update(&mut nvmc).await.unwrap(); 39 let mut magic = [0; 4];
40 updater.mark_updated(&mut nvmc, &mut magic).await.unwrap();
40 led.set_high(); 41 led.set_high();
41 cortex_m::peripheral::SCB::sys_reset(); 42 cortex_m::peripheral::SCB::sys_reset();
42 } 43 }
diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs
index 11eecc5e2..fdbd5ab99 100644
--- a/examples/boot/application/stm32f3/src/bin/a.rs
+++ b/examples/boot/application/stm32f3/src/bin/a.rs
@@ -4,11 +4,11 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::FirmwareUpdater; 7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater};
8use embassy_embedded_hal::adapter::BlockingAsync; 8use embassy_embedded_hal::adapter::BlockingAsync;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::ExtiInput;
11use embassy_stm32::flash::Flash; 11use embassy_stm32::flash::{Flash, WRITE_SIZE};
12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; 12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
13use panic_reset as _; 13use panic_reset as _;
14 14
@@ -35,7 +35,8 @@ async fn main(_spawner: Spawner) {
35 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); 35 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap();
36 offset += chunk.len(); 36 offset += chunk.len();
37 } 37 }
38 updater.update(&mut flash).await.unwrap(); 38 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
39 updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap();
39 led.set_low(); 40 led.set_low();
40 cortex_m::peripheral::SCB::sys_reset(); 41 cortex_m::peripheral::SCB::sys_reset();
41} 42}
diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs
index a3b66e7c9..c08880fb3 100644
--- a/examples/boot/application/stm32f7/src/bin/a.rs
+++ b/examples/boot/application/stm32f7/src/bin/a.rs
@@ -4,11 +4,11 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::FirmwareUpdater; 7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater};
8use embassy_embedded_hal::adapter::BlockingAsync; 8use embassy_embedded_hal::adapter::BlockingAsync;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::ExtiInput;
11use embassy_stm32::flash::Flash; 11use embassy_stm32::flash::{Flash, WRITE_SIZE};
12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; 12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
13use panic_reset as _; 13use panic_reset as _;
14 14
@@ -35,7 +35,8 @@ async fn main(_spawner: Spawner) {
35 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); 35 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap();
36 offset += chunk.len(); 36 offset += chunk.len();
37 } 37 }
38 updater.update(&mut flash).await.unwrap(); 38 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
39 updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap();
39 led.set_low(); 40 led.set_low();
40 cortex_m::peripheral::SCB::sys_reset(); 41 cortex_m::peripheral::SCB::sys_reset();
41} 42}
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml
index 5669527fe..7a76a8db5 100644
--- a/examples/boot/application/stm32h7/Cargo.toml
+++ b/examples/boot/application/stm32h7/Cargo.toml
@@ -4,7 +4,7 @@ name = "embassy-boot-stm32h7-examples"
4version = "0.1.0" 4version = "0.1.0"
5 5
6[dependencies] 6[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 7embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
8embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 8embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] }
9embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } 9embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] }
10embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } 10embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] }
diff --git a/examples/boot/application/stm32h7/flash-boot.sh b/examples/boot/application/stm32h7/flash-boot.sh
index debdb17a7..a3003681a 100755
--- a/examples/boot/application/stm32h7/flash-boot.sh
+++ b/examples/boot/application/stm32h7/flash-boot.sh
@@ -1,8 +1,9 @@
1#!/bin/bash 1#!/bin/bash
2probe-rs-cli erase --chip STM32H743ZITx
2mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x 3mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x
3cp memory-bl.x ../../bootloader/stm32/memory.x 4cp memory-bl.x ../../bootloader/stm32/memory.x
4 5
5cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32F767ZITx --target thumbv7em-none-eabihf 6cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32h743zi --chip STM32H743ZITx --target thumbv7em-none-eabihf
6 7
7rm ../../bootloader/stm32/memory.x 8rm ../../bootloader/stm32/memory.x
8mv ../../bootloader/stm32/memory-old.x ../../bootloader/stm32/memory.x 9mv ../../bootloader/stm32/memory-old.x ../../bootloader/stm32/memory.x
diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs
index 0ecf60348..f5a8fdb61 100644
--- a/examples/boot/application/stm32h7/src/bin/a.rs
+++ b/examples/boot/application/stm32h7/src/bin/a.rs
@@ -4,11 +4,11 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::FirmwareUpdater; 7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater};
8use embassy_embedded_hal::adapter::BlockingAsync; 8use embassy_embedded_hal::adapter::BlockingAsync;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::ExtiInput;
11use embassy_stm32::flash::Flash; 11use embassy_stm32::flash::{Flash, WRITE_SIZE};
12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; 12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
13use panic_reset as _; 13use panic_reset as _;
14 14
@@ -29,13 +29,17 @@ async fn main(_spawner: Spawner) {
29 let mut updater = FirmwareUpdater::default(); 29 let mut updater = FirmwareUpdater::default();
30 button.wait_for_rising_edge().await; 30 button.wait_for_rising_edge().await;
31 let mut offset = 0; 31 let mut offset = 0;
32 let mut buf: [u8; 128 * 1024] = [0; 128 * 1024]; 32 let mut buf = AlignedBuffer([0; 128 * 1024]);
33 for chunk in APP_B.chunks(128 * 1024) { 33 for chunk in APP_B.chunks(128 * 1024) {
34 buf[..chunk.len()].copy_from_slice(chunk); 34 buf.as_mut()[..chunk.len()].copy_from_slice(chunk);
35 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); 35 updater
36 .write_firmware(offset, buf.as_ref(), &mut flash, 2048)
37 .await
38 .unwrap();
36 offset += chunk.len(); 39 offset += chunk.len();
37 } 40 }
38 updater.update(&mut flash).await.unwrap(); 41 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
42 updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap();
39 led.set_low(); 43 led.set_low();
40 cortex_m::peripheral::SCB::sys_reset(); 44 cortex_m::peripheral::SCB::sys_reset();
41} 45}
diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs
index f4f1d7119..f0b0b80e3 100644
--- a/examples/boot/application/stm32l0/src/bin/a.rs
+++ b/examples/boot/application/stm32l0/src/bin/a.rs
@@ -4,11 +4,11 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::FirmwareUpdater; 7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater};
8use embassy_embedded_hal::adapter::BlockingAsync; 8use embassy_embedded_hal::adapter::BlockingAsync;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::ExtiInput;
11use embassy_stm32::flash::Flash; 11use embassy_stm32::flash::{Flash, WRITE_SIZE};
12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; 12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
13use embassy_time::{Duration, Timer}; 13use embassy_time::{Duration, Timer};
14use panic_reset as _; 14use panic_reset as _;
@@ -38,7 +38,8 @@ async fn main(_spawner: Spawner) {
38 offset += chunk.len(); 38 offset += chunk.len();
39 } 39 }
40 40
41 updater.update(&mut flash).await.unwrap(); 41 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
42 updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap();
42 led.set_low(); 43 led.set_low();
43 Timer::after(Duration::from_secs(1)).await; 44 Timer::after(Duration::from_secs(1)).await;
44 cortex_m::peripheral::SCB::sys_reset(); 45 cortex_m::peripheral::SCB::sys_reset();
diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs
index f4f1d7119..f0b0b80e3 100644
--- a/examples/boot/application/stm32l1/src/bin/a.rs
+++ b/examples/boot/application/stm32l1/src/bin/a.rs
@@ -4,11 +4,11 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::FirmwareUpdater; 7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater};
8use embassy_embedded_hal::adapter::BlockingAsync; 8use embassy_embedded_hal::adapter::BlockingAsync;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::ExtiInput;
11use embassy_stm32::flash::Flash; 11use embassy_stm32::flash::{Flash, WRITE_SIZE};
12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; 12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
13use embassy_time::{Duration, Timer}; 13use embassy_time::{Duration, Timer};
14use panic_reset as _; 14use panic_reset as _;
@@ -38,7 +38,8 @@ async fn main(_spawner: Spawner) {
38 offset += chunk.len(); 38 offset += chunk.len();
39 } 39 }
40 40
41 updater.update(&mut flash).await.unwrap(); 41 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
42 updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap();
42 led.set_low(); 43 led.set_low();
43 Timer::after(Duration::from_secs(1)).await; 44 Timer::after(Duration::from_secs(1)).await;
44 cortex_m::peripheral::SCB::sys_reset(); 45 cortex_m::peripheral::SCB::sys_reset();
diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs
index 178b2e04a..5119bad2e 100644
--- a/examples/boot/application/stm32l4/src/bin/a.rs
+++ b/examples/boot/application/stm32l4/src/bin/a.rs
@@ -4,11 +4,11 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::FirmwareUpdater; 7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater};
8use embassy_embedded_hal::adapter::BlockingAsync; 8use embassy_embedded_hal::adapter::BlockingAsync;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::ExtiInput;
11use embassy_stm32::flash::Flash; 11use embassy_stm32::flash::{Flash, WRITE_SIZE};
12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; 12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
13use panic_reset as _; 13use panic_reset as _;
14 14
@@ -35,7 +35,8 @@ async fn main(_spawner: Spawner) {
35 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); 35 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap();
36 offset += chunk.len(); 36 offset += chunk.len();
37 } 37 }
38 updater.update(&mut flash).await.unwrap(); 38 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
39 updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap();
39 led.set_low(); 40 led.set_low();
40 cortex_m::peripheral::SCB::sys_reset(); 41 cortex_m::peripheral::SCB::sys_reset();
41} 42}
diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs
index c71a42654..faa650778 100644
--- a/examples/boot/application/stm32wl/src/bin/a.rs
+++ b/examples/boot/application/stm32wl/src/bin/a.rs
@@ -4,11 +4,11 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::FirmwareUpdater; 7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater};
8use embassy_embedded_hal::adapter::BlockingAsync; 8use embassy_embedded_hal::adapter::BlockingAsync;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::ExtiInput;
11use embassy_stm32::flash::Flash; 11use embassy_stm32::flash::{Flash, WRITE_SIZE};
12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; 12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
13use panic_reset as _; 13use panic_reset as _;
14 14
@@ -37,7 +37,8 @@ async fn main(_spawner: Spawner) {
37 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); 37 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap();
38 offset += chunk.len(); 38 offset += chunk.len();
39 } 39 }
40 updater.update(&mut flash).await.unwrap(); 40 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
41 updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap();
41 //defmt::info!("Marked as updated"); 42 //defmt::info!("Marked as updated");
42 led.set_low(); 43 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset(); 44 cortex_m::peripheral::SCB::sys_reset();
diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs
index bc7e0755f..9031997c2 100644
--- a/examples/boot/bootloader/nrf/src/main.rs
+++ b/examples/boot/bootloader/nrf/src/main.rs
@@ -20,10 +20,8 @@ fn main() -> ! {
20 */ 20 */
21 21
22 let mut bl = BootLoader::default(); 22 let mut bl = BootLoader::default();
23 let start = bl.prepare(&mut SingleFlashProvider::new(&mut WatchdogFlash::start( 23 let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new(
24 Nvmc::new(p.NVMC), 24 &mut WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5),
25 p.WDT,
26 5,
27 ))); 25 )));
28 unsafe { bl.load(start) } 26 unsafe { bl.load(start) }
29} 27}
diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs
index 45c511ced..bb5d3e531 100644
--- a/examples/boot/bootloader/stm32/src/main.rs
+++ b/examples/boot/bootloader/stm32/src/main.rs
@@ -5,7 +5,7 @@ use cortex_m_rt::{entry, exception};
5#[cfg(feature = "defmt")] 5#[cfg(feature = "defmt")]
6use defmt_rtt as _; 6use defmt_rtt as _;
7use embassy_boot_stm32::*; 7use embassy_boot_stm32::*;
8use embassy_stm32::flash::{Flash, ERASE_SIZE}; 8use embassy_stm32::flash::{Flash, ERASE_SIZE, ERASE_VALUE, WRITE_SIZE};
9 9
10#[entry] 10#[entry]
11fn main() -> ! { 11fn main() -> ! {
@@ -19,9 +19,11 @@ fn main() -> ! {
19 } 19 }
20 */ 20 */
21 21
22 let mut bl: BootLoader<ERASE_SIZE> = BootLoader::default(); 22 let mut bl: BootLoader<ERASE_SIZE, WRITE_SIZE> = BootLoader::default();
23 let mut flash = Flash::unlock(p.FLASH); 23 let mut flash = Flash::unlock(p.FLASH);
24 let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash)); 24 let start = bl.prepare(&mut SingleFlashConfig::new(
25 &mut BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(&mut flash),
26 ));
25 core::mem::drop(flash); 27 core::mem::drop(flash);
26 unsafe { bl.load(start) } 28 unsafe { bl.load(start) }
27} 29}