aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot/boot/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-boot/boot/src/lib.rs')
-rw-r--r--embassy-boot/boot/src/lib.rs1427
1 files changed, 49 insertions, 1378 deletions
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 7ce0c664a..e268d8883 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -5,36 +5,18 @@
5#![doc = include_str!("../README.md")] 5#![doc = include_str!("../README.md")]
6mod fmt; 6mod fmt;
7 7
8use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; 8mod boot_loader;
9mod digest_adapters;
10mod firmware_updater;
11mod mem_flash;
12mod partition;
9 13
10#[cfg(feature = "nightly")] 14pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig};
11use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; 15pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError};
16pub use partition::Partition;
12 17
13const BOOT_MAGIC: u8 = 0xD0; 18pub(crate) const BOOT_MAGIC: u8 = 0xD0;
14const SWAP_MAGIC: u8 = 0xF0; 19pub(crate) const SWAP_MAGIC: u8 = 0xF0;
15
16/// A region in flash used by the bootloader.
17#[derive(Copy, Clone, Debug)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub struct Partition {
20 /// Start of the flash region.
21 pub from: usize,
22 /// End of the flash region.
23 pub to: usize,
24}
25
26impl Partition {
27 /// Create a new partition with the provided range
28 pub const fn new(from: usize, to: usize) -> Self {
29 Self { from, to }
30 }
31
32 /// Return the length of the partition
33 #[allow(clippy::len_without_is_empty)]
34 pub const fn len(&self) -> usize {
35 self.to - self.from
36 }
37}
38 20
39/// The state of the bootloader after running prepare. 21/// The state of the bootloader after running prepare.
40#[derive(PartialEq, Eq, Debug)] 22#[derive(PartialEq, Eq, Debug)]
@@ -46,34 +28,6 @@ pub enum State {
46 Swap, 28 Swap,
47} 29}
48 30
49/// Errors returned by bootloader
50#[derive(PartialEq, Eq, Debug)]
51pub enum BootError {
52 /// Error from flash.
53 Flash(NorFlashErrorKind),
54 /// Invalid bootloader magic
55 BadMagic,
56}
57
58#[cfg(feature = "defmt")]
59impl defmt::Format for BootError {
60 fn format(&self, fmt: defmt::Formatter) {
61 match self {
62 BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"),
63 BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"),
64 }
65 }
66}
67
68impl<E> From<E> for BootError
69where
70 E: NorFlashError,
71{
72 fn from(error: E) -> Self {
73 BootError::Flash(error.kind())
74 }
75}
76
77/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. 31/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
78#[repr(align(32))] 32#[repr(align(32))]
79pub struct AlignedBuffer<const N: usize>(pub [u8; N]); 33pub struct AlignedBuffer<const N: usize>(pub [u8; N]);
@@ -90,1128 +44,12 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
90 } 44 }
91} 45}
92 46
93/// Extension of the embedded-storage flash type information with block size and erase value.
94pub trait Flash: NorFlash + ReadNorFlash {
95 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase
96 /// size of the flash, but for external QSPI flash modules, this can be lower.
97 const BLOCK_SIZE: usize;
98 /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value.
99 const ERASE_VALUE: u8 = 0xFF;
100}
101
102/// Trait defining the flash handles used for active and DFU partition
103pub trait FlashConfig {
104 /// Flash type used for the state partition.
105 type STATE: Flash;
106 /// Flash type used for the active partition.
107 type ACTIVE: Flash;
108 /// Flash type used for the dfu partition.
109 type DFU: Flash;
110
111 /// Return flash instance used to write/read to/from active partition.
112 fn active(&mut self) -> &mut Self::ACTIVE;
113 /// Return flash instance used to write/read to/from dfu partition.
114 fn dfu(&mut self) -> &mut Self::DFU;
115 /// Return flash instance used to write/read to/from bootloader state.
116 fn state(&mut self) -> &mut Self::STATE;
117}
118
119/// BootLoader works with any flash implementing embedded_storage and can also work with
120/// different page sizes and flash write sizes.
121pub struct BootLoader {
122 // Page with current state of bootloader. The state partition has the following format:
123 // | Range | Description |
124 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
125 // | WRITE_SIZE - N | Progress index used while swapping or reverting |
126 state: Partition,
127 // Location of the partition which will be booted from
128 active: Partition,
129 // Location of the partition which will be swapped in when requested
130 dfu: Partition,
131}
132
133impl BootLoader {
134 /// Create a new instance of a bootloader with the given partitions.
135 ///
136 /// - All partitions must be aligned with the PAGE_SIZE const generic parameter.
137 /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition.
138 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
139 Self { active, dfu, state }
140 }
141
142 /// Return the boot address for the active partition.
143 pub fn boot_address(&self) -> usize {
144 self.active.from
145 }
146
147 /// Perform necessary boot preparations like swapping images.
148 ///
149 /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap
150 /// algorithm to work correctly.
151 ///
152 /// SWAPPING
153 ///
154 /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition.
155 /// The swap index contains the copy progress, as to allow continuation of the copy process on
156 /// power failure. The index counter is represented within 1 or more pages (depending on total
157 /// flash size), where a page X is considered swapped if index at location (X + WRITE_SIZE)
158 /// contains a zero value. This ensures that index updates can be performed atomically and
159 /// avoid a situation where the wrong index value is set (page write size is "atomic").
160 ///
161 /// +-----------+------------+--------+--------+--------+--------+
162 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
163 /// +-----------+------------+--------+--------+--------+--------+
164 /// | Active | 0 | 1 | 2 | 3 | - |
165 /// | DFU | 0 | 3 | 2 | 1 | X |
166 /// +-----------+------------+--------+--------+--------+--------+
167 ///
168 /// The algorithm starts by copying 'backwards', and after the first step, the layout is
169 /// as follows:
170 ///
171 /// +-----------+------------+--------+--------+--------+--------+
172 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
173 /// +-----------+------------+--------+--------+--------+--------+
174 /// | Active | 1 | 1 | 2 | 1 | - |
175 /// | DFU | 1 | 3 | 2 | 1 | 3 |
176 /// +-----------+------------+--------+--------+--------+--------+
177 ///
178 /// The next iteration performs the same steps
179 ///
180 /// +-----------+------------+--------+--------+--------+--------+
181 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
182 /// +-----------+------------+--------+--------+--------+--------+
183 /// | Active | 2 | 1 | 2 | 1 | - |
184 /// | DFU | 2 | 3 | 2 | 2 | 3 |
185 /// +-----------+------------+--------+--------+--------+--------+
186 ///
187 /// And again until we're done
188 ///
189 /// +-----------+------------+--------+--------+--------+--------+
190 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
191 /// +-----------+------------+--------+--------+--------+--------+
192 /// | Active | 3 | 3 | 2 | 1 | - |
193 /// | DFU | 3 | 3 | 1 | 2 | 3 |
194 /// +-----------+------------+--------+--------+--------+--------+
195 ///
196 /// REVERTING
197 ///
198 /// The reverting algorithm uses the swap index to discover that images were swapped, but that
199 /// the application failed to mark the boot successful. In this case, the revert algorithm will
200 /// run.
201 ///
202 /// The revert index is located separately from the swap index, to ensure that revert can continue
203 /// on power failure.
204 ///
205 /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start.
206 ///
207 /// +-----------+--------------+--------+--------+--------+--------+
208 /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
209 //*/
210 /// +-----------+--------------+--------+--------+--------+--------+
211 /// | Active | 3 | 1 | 2 | 1 | - |
212 /// | DFU | 3 | 3 | 1 | 2 | 3 |
213 /// +-----------+--------------+--------+--------+--------+--------+
214 ///
215 ///
216 /// +-----------+--------------+--------+--------+--------+--------+
217 /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
218 /// +-----------+--------------+--------+--------+--------+--------+
219 /// | Active | 3 | 1 | 2 | 1 | - |
220 /// | DFU | 3 | 3 | 2 | 2 | 3 |
221 /// +-----------+--------------+--------+--------+--------+--------+
222 ///
223 /// +-----------+--------------+--------+--------+--------+--------+
224 /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
225 /// +-----------+--------------+--------+--------+--------+--------+
226 /// | Active | 3 | 1 | 2 | 3 | - |
227 /// | DFU | 3 | 3 | 2 | 1 | 3 |
228 /// +-----------+--------------+--------+--------+--------+--------+
229 ///
230 pub fn prepare_boot<P: FlashConfig>(
231 &mut self,
232 p: &mut P,
233 magic: &mut [u8],
234 page: &mut [u8],
235 ) -> Result<State, BootError> {
236 // Ensure we have enough progress pages to store copy progress
237 assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE);
238 assert_eq!(magic.len(), P::STATE::WRITE_SIZE);
239
240 // Copy contents from partition N to active
241 let state = self.read_state(p, magic)?;
242 if state == State::Swap {
243 //
244 // Check if we already swapped. If we're in the swap state, this means we should revert
245 // since the app has failed to mark boot as successful
246 //
247 if !self.is_swapped(p, magic, page)? {
248 trace!("Swapping");
249 self.swap(p, magic, page)?;
250 trace!("Swapping done");
251 } else {
252 trace!("Reverting");
253 self.revert(p, magic, page)?;
254
255 // Overwrite magic and reset progress
256 let fstate = p.state();
257 magic.fill(!P::STATE::ERASE_VALUE);
258 fstate.write(self.state.from as u32, magic)?;
259 fstate.erase(self.state.from as u32, self.state.to as u32)?;
260
261 magic.fill(BOOT_MAGIC);
262 fstate.write(self.state.from as u32, magic)?;
263 }
264 }
265 Ok(state)
266 }
267
268 fn is_swapped<P: FlashConfig>(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<bool, BootError> {
269 let page_size = page.len();
270 let page_count = self.active.len() / page_size;
271 let progress = self.current_progress(p, magic)?;
272
273 Ok(progress >= page_count * 2)
274 }
275
276 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> {
277 let write_size = aligned.len();
278 let max_index = ((self.state.len() - write_size) / write_size) - 1;
279 aligned.fill(!P::STATE::ERASE_VALUE);
280
281 let flash = config.state();
282 for i in 0..max_index {
283 flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?;
284
285 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) {
286 return Ok(i);
287 }
288 }
289 Ok(max_index)
290 }
291
292 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> {
293 let flash = p.state();
294 let write_size = magic.len();
295 let w = self.state.from + write_size + idx * write_size;
296
297 let aligned = magic;
298 aligned.fill(!P::STATE::ERASE_VALUE);
299 flash.write(w as u32, aligned)?;
300 Ok(())
301 }
302
303 fn active_addr(&self, n: usize, page_size: usize) -> usize {
304 self.active.from + n * page_size
305 }
306
307 fn dfu_addr(&self, n: usize, page_size: usize) -> usize {
308 self.dfu.from + n * page_size
309 }
310
311 fn copy_page_once_to_active<P: FlashConfig>(
312 &mut self,
313 idx: usize,
314 from_page: usize,
315 to_page: usize,
316 p: &mut P,
317 magic: &mut [u8],
318 page: &mut [u8],
319 ) -> Result<(), BootError> {
320 let buf = page;
321 if self.current_progress(p, magic)? <= idx {
322 let mut offset = from_page;
323 for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) {
324 p.dfu().read(offset as u32, chunk)?;
325 offset += chunk.len();
326 }
327
328 p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?;
329
330 let mut offset = to_page;
331 for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) {
332 p.active().write(offset as u32, chunk)?;
333 offset += chunk.len();
334 }
335 self.update_progress(idx, p, magic)?;
336 }
337 Ok(())
338 }
339
340 fn copy_page_once_to_dfu<P: FlashConfig>(
341 &mut self,
342 idx: usize,
343 from_page: usize,
344 to_page: usize,
345 p: &mut P,
346 magic: &mut [u8],
347 page: &mut [u8],
348 ) -> Result<(), BootError> {
349 let buf = page;
350 if self.current_progress(p, magic)? <= idx {
351 let mut offset = from_page;
352 for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) {
353 p.active().read(offset as u32, chunk)?;
354 offset += chunk.len();
355 }
356
357 p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?;
358
359 let mut offset = to_page;
360 for chunk in buf.chunks(P::DFU::BLOCK_SIZE) {
361 p.dfu().write(offset as u32, chunk)?;
362 offset += chunk.len();
363 }
364 self.update_progress(idx, p, magic)?;
365 }
366 Ok(())
367 }
368
369 fn swap<P: FlashConfig>(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> {
370 let page_size = page.len();
371 let page_count = self.active.len() / page_size;
372 trace!("Page count: {}", page_count);
373 for page_num in 0..page_count {
374 trace!("COPY PAGE {}", page_num);
375 // Copy active page to the 'next' DFU page.
376 let active_page = self.active_addr(page_count - 1 - page_num, page_size);
377 let dfu_page = self.dfu_addr(page_count - page_num, page_size);
378 //trace!("Copy active {} to dfu {}", active_page, dfu_page);
379 self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?;
380
381 // Copy DFU page to the active page
382 let active_page = self.active_addr(page_count - 1 - page_num, page_size);
383 let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size);
384 //trace!("Copy dfy {} to active {}", dfu_page, active_page);
385 self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?;
386 }
387
388 Ok(())
389 }
390
391 fn revert<P: FlashConfig>(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> {
392 let page_size = page.len();
393 let page_count = self.active.len() / page_size;
394 for page_num in 0..page_count {
395 // Copy the bad active page to the DFU page
396 let active_page = self.active_addr(page_num, page_size);
397 let dfu_page = self.dfu_addr(page_num, page_size);
398 self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?;
399
400 // Copy the DFU page back to the active page
401 let active_page = self.active_addr(page_num, page_size);
402 let dfu_page = self.dfu_addr(page_num + 1, page_size);
403 self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?;
404 }
405
406 Ok(())
407 }
408
409 fn read_state<P: FlashConfig>(&mut self, config: &mut P, magic: &mut [u8]) -> Result<State, BootError> {
410 let flash = config.state();
411 flash.read(self.state.from as u32, magic)?;
412
413 if !magic.iter().any(|&b| b != SWAP_MAGIC) {
414 Ok(State::Swap)
415 } else {
416 Ok(State::Boot)
417 }
418 }
419}
420
421fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) {
422 assert_eq!(active.len() % page_size, 0);
423 assert_eq!(dfu.len() % page_size, 0);
424 assert!(dfu.len() - active.len() >= page_size);
425 assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size);
426}
427
428/// Convenience provider that uses a single flash for all partitions.
429pub struct SingleFlashConfig<'a, F>
430where
431 F: Flash,
432{
433 flash: &'a mut F,
434}
435
436impl<'a, F> SingleFlashConfig<'a, F>
437where
438 F: Flash,
439{
440 /// Create a provider for a single flash.
441 pub fn new(flash: &'a mut F) -> Self {
442 Self { flash }
443 }
444}
445
446impl<'a, F> FlashConfig for SingleFlashConfig<'a, F>
447where
448 F: Flash,
449{
450 type STATE = F;
451 type ACTIVE = F;
452 type DFU = F;
453
454 fn active(&mut self) -> &mut Self::STATE {
455 self.flash
456 }
457 fn dfu(&mut self) -> &mut Self::ACTIVE {
458 self.flash
459 }
460 fn state(&mut self) -> &mut Self::DFU {
461 self.flash
462 }
463}
464
465/// A flash wrapper implementing the Flash and embedded_storage traits.
466pub struct BootFlash<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF>
467where
468 F: NorFlash + ReadNorFlash,
469{
470 flash: F,
471}
472
473impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
474where
475 F: NorFlash + ReadNorFlash,
476{
477 /// Create a new instance of a bootable flash
478 pub fn new(flash: F) -> Self {
479 Self { flash }
480 }
481}
482
483impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
484where
485 F: NorFlash + ReadNorFlash,
486{
487 const BLOCK_SIZE: usize = BLOCK_SIZE;
488 const ERASE_VALUE: u8 = ERASE_VALUE;
489}
490
491impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
492where
493 F: ReadNorFlash + NorFlash,
494{
495 type Error = F::Error;
496}
497
498impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
499where
500 F: ReadNorFlash + NorFlash,
501{
502 const WRITE_SIZE: usize = F::WRITE_SIZE;
503 const ERASE_SIZE: usize = F::ERASE_SIZE;
504
505 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
506 F::erase(&mut self.flash, from, to)
507 }
508
509 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
510 F::write(&mut self.flash, offset, bytes)
511 }
512}
513
514impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
515where
516 F: ReadNorFlash + NorFlash,
517{
518 const READ_SIZE: usize = F::READ_SIZE;
519
520 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
521 F::read(&mut self.flash, offset, bytes)
522 }
523
524 fn capacity(&self) -> usize {
525 F::capacity(&self.flash)
526 }
527}
528
529/// Convenience flash provider that uses separate flash instances for each partition.
530pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU>
531where
532 ACTIVE: Flash,
533 STATE: Flash,
534 DFU: Flash,
535{
536 active: &'a mut ACTIVE,
537 state: &'a mut STATE,
538 dfu: &'a mut DFU,
539}
540
541impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU>
542where
543 ACTIVE: Flash,
544 STATE: Flash,
545 DFU: Flash,
546{
547 /// Create a new flash provider with separate configuration for all three partitions.
548 pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self {
549 Self { active, state, dfu }
550 }
551}
552
553impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU>
554where
555 ACTIVE: Flash,
556 STATE: Flash,
557 DFU: Flash,
558{
559 type STATE = STATE;
560 type ACTIVE = ACTIVE;
561 type DFU = DFU;
562
563 fn active(&mut self) -> &mut Self::ACTIVE {
564 self.active
565 }
566 fn dfu(&mut self) -> &mut Self::DFU {
567 self.dfu
568 }
569 fn state(&mut self) -> &mut Self::STATE {
570 self.state
571 }
572}
573/// Errors returned by FirmwareUpdater
574#[derive(Debug)]
575pub enum FirmwareUpdaterError {
576 /// Error from flash.
577 Flash(NorFlashErrorKind),
578 /// Signature errors.
579 Signature(signature::Error),
580}
581
582#[cfg(feature = "defmt")]
583impl defmt::Format for FirmwareUpdaterError {
584 fn format(&self, fmt: defmt::Formatter) {
585 match self {
586 FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"),
587 FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"),
588 }
589 }
590}
591
592impl<E> From<E> for FirmwareUpdaterError
593where
594 E: NorFlashError,
595{
596 fn from(error: E) -> Self {
597 FirmwareUpdaterError::Flash(error.kind())
598 }
599}
600
601/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
602/// 'mess up' the internal bootloader state
603pub struct FirmwareUpdater {
604 state: Partition,
605 dfu: Partition,
606}
607
608impl Default for FirmwareUpdater {
609 fn default() -> Self {
610 extern "C" {
611 static __bootloader_state_start: u32;
612 static __bootloader_state_end: u32;
613 static __bootloader_dfu_start: u32;
614 static __bootloader_dfu_end: u32;
615 }
616
617 let dfu = unsafe {
618 Partition::new(
619 &__bootloader_dfu_start as *const u32 as usize,
620 &__bootloader_dfu_end as *const u32 as usize,
621 )
622 };
623 let state = unsafe {
624 Partition::new(
625 &__bootloader_state_start as *const u32 as usize,
626 &__bootloader_state_end as *const u32 as usize,
627 )
628 };
629
630 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
631 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
632 FirmwareUpdater::new(dfu, state)
633 }
634}
635
636impl FirmwareUpdater {
637 /// Create a firmware updater instance with partition ranges for the update and state partitions.
638 pub const fn new(dfu: Partition, state: Partition) -> Self {
639 Self { dfu, state }
640 }
641
642 /// Return the length of the DFU area
643 pub fn firmware_len(&self) -> usize {
644 self.dfu.len()
645 }
646
647 /// Obtain the current state.
648 ///
649 /// This is useful to check if the bootloader has just done a swap, in order
650 /// to do verifications and self-tests of the new image before calling
651 /// `mark_booted`.
652 #[cfg(feature = "nightly")]
653 pub async fn get_state<F: AsyncNorFlash>(
654 &mut self,
655 flash: &mut F,
656 aligned: &mut [u8],
657 ) -> Result<State, FirmwareUpdaterError> {
658 flash.read(self.state.from as u32, aligned).await?;
659
660 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
661 Ok(State::Swap)
662 } else {
663 Ok(State::Boot)
664 }
665 }
666
667 /// Verify the DFU given a public key. If there is an error then DO NOT
668 /// proceed with updating the firmware as it must be signed with a
669 /// corresponding private key (otherwise it could be malicious firmware).
670 ///
671 /// Mark to trigger firmware swap on next boot if verify suceeds.
672 ///
673 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
674 /// been generated from a SHA-512 digest of the firmware bytes.
675 ///
676 /// If no signature feature is set then this method will always return a
677 /// signature error.
678 ///
679 /// # Safety
680 ///
681 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
682 /// and written to.
683 #[cfg(feature = "_verify")]
684 pub async fn verify_and_mark_updated<F: AsyncNorFlash>(
685 &mut self,
686 _flash: &mut F,
687 _public_key: &[u8],
688 _signature: &[u8],
689 _update_len: usize,
690 _aligned: &mut [u8],
691 ) -> Result<(), FirmwareUpdaterError> {
692 let _end = self.dfu.from + _update_len;
693 let _read_size = _aligned.len();
694
695 assert_eq!(_aligned.len(), F::WRITE_SIZE);
696 assert!(_end <= self.dfu.to);
697
698 #[cfg(feature = "ed25519-dalek")]
699 {
700 use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier};
701
702 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
703
704 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
705 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
706
707 let mut digest = Sha512::new();
708
709 let mut offset = self.dfu.from;
710 let last_offset = _end / _read_size * _read_size;
711
712 while offset < last_offset {
713 _flash.read(offset as u32, _aligned).await?;
714 digest.update(&_aligned);
715 offset += _read_size;
716 }
717
718 let remaining = _end % _read_size;
719
720 if remaining > 0 {
721 _flash.read(last_offset as u32, _aligned).await?;
722 digest.update(&_aligned[0..remaining]);
723 }
724
725 public_key
726 .verify(&digest.finalize(), &signature)
727 .map_err(into_signature_error)?
728 }
729 #[cfg(feature = "ed25519-salty")]
730 {
731 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
732 use salty::{PublicKey, Sha512, Signature};
733
734 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
735 FirmwareUpdaterError::Signature(signature::Error::default())
736 }
737
738 let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
739 let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
740 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
741 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
742
743 let mut digest = Sha512::new();
744
745 let mut offset = self.dfu.from;
746 let last_offset = _end / _read_size * _read_size;
747
748 while offset < last_offset {
749 _flash.read(offset as u32, _aligned).await?;
750 digest.update(&_aligned);
751 offset += _read_size;
752 }
753
754 let remaining = _end % _read_size;
755
756 if remaining > 0 {
757 _flash.read(last_offset as u32, _aligned).await?;
758 digest.update(&_aligned[0..remaining]);
759 }
760
761 let message = digest.finalize();
762 let r = public_key.verify(&message, &signature);
763 trace!(
764 "Verifying with public key {}, signature {} and message {} yields ok: {}",
765 public_key.to_bytes(),
766 signature.to_bytes(),
767 message,
768 r.is_ok()
769 );
770 r.map_err(into_signature_error)?
771 }
772
773 self.set_magic(_aligned, SWAP_MAGIC, _flash).await
774 }
775
776 /// Mark to trigger firmware swap on next boot.
777 ///
778 /// # Safety
779 ///
780 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
781 #[cfg(not(feature = "_verify"))]
782 #[cfg(feature = "nightly")]
783 pub async fn mark_updated<F: AsyncNorFlash>(
784 &mut self,
785 flash: &mut F,
786 aligned: &mut [u8],
787 ) -> Result<(), FirmwareUpdaterError> {
788 assert_eq!(aligned.len(), F::WRITE_SIZE);
789 self.set_magic(aligned, SWAP_MAGIC, flash).await
790 }
791
792 /// Mark firmware boot successful and stop rollback on reset.
793 ///
794 /// # Safety
795 ///
796 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
797 #[cfg(feature = "nightly")]
798 pub async fn mark_booted<F: AsyncNorFlash>(
799 &mut self,
800 flash: &mut F,
801 aligned: &mut [u8],
802 ) -> Result<(), FirmwareUpdaterError> {
803 assert_eq!(aligned.len(), F::WRITE_SIZE);
804 self.set_magic(aligned, BOOT_MAGIC, flash).await
805 }
806
807 #[cfg(feature = "nightly")]
808 async fn set_magic<F: AsyncNorFlash>(
809 &mut self,
810 aligned: &mut [u8],
811 magic: u8,
812 flash: &mut F,
813 ) -> Result<(), FirmwareUpdaterError> {
814 flash.read(self.state.from as u32, aligned).await?;
815
816 if aligned.iter().any(|&b| b != magic) {
817 aligned.fill(0);
818
819 flash.write(self.state.from as u32, aligned).await?;
820 flash.erase(self.state.from as u32, self.state.to as u32).await?;
821
822 aligned.fill(magic);
823 flash.write(self.state.from as u32, aligned).await?;
824 }
825 Ok(())
826 }
827
828 /// Write data to a flash page.
829 ///
830 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
831 ///
832 /// # Safety
833 ///
834 /// Failing to meet alignment and size requirements may result in a panic.
835 #[cfg(feature = "nightly")]
836 pub async fn write_firmware<F: AsyncNorFlash>(
837 &mut self,
838 offset: usize,
839 data: &[u8],
840 flash: &mut F,
841 block_size: usize,
842 ) -> Result<(), FirmwareUpdaterError> {
843 assert!(data.len() >= F::ERASE_SIZE);
844
845 flash
846 .erase(
847 (self.dfu.from + offset) as u32,
848 (self.dfu.from + offset + data.len()) as u32,
849 )
850 .await?;
851
852 trace!(
853 "Erased from {} to {}",
854 self.dfu.from + offset,
855 self.dfu.from + offset + data.len()
856 );
857
858 FirmwareWriter(self.dfu)
859 .write_block(offset, data, flash, block_size)
860 .await?;
861
862 Ok(())
863 }
864
865 /// Prepare for an incoming DFU update by erasing the entire DFU area and
866 /// returning a `FirmwareWriter`.
867 ///
868 /// Using this instead of `write_firmware` allows for an optimized API in
869 /// exchange for added complexity.
870 #[cfg(feature = "nightly")]
871 pub async fn prepare_update<F: AsyncNorFlash>(
872 &mut self,
873 flash: &mut F,
874 ) -> Result<FirmwareWriter, FirmwareUpdaterError> {
875 flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?;
876
877 trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
878
879 Ok(FirmwareWriter(self.dfu))
880 }
881
882 //
883 // Blocking API
884 //
885
886 /// Obtain the current state.
887 ///
888 /// This is useful to check if the bootloader has just done a swap, in order
889 /// to do verifications and self-tests of the new image before calling
890 /// `mark_booted`.
891 pub fn get_state_blocking<F: NorFlash>(
892 &mut self,
893 flash: &mut F,
894 aligned: &mut [u8],
895 ) -> Result<State, FirmwareUpdaterError> {
896 flash.read(self.state.from as u32, aligned)?;
897
898 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
899 Ok(State::Swap)
900 } else {
901 Ok(State::Boot)
902 }
903 }
904
905 /// Verify the DFU given a public key. If there is an error then DO NOT
906 /// proceed with updating the firmware as it must be signed with a
907 /// corresponding private key (otherwise it could be malicious firmware).
908 ///
909 /// Mark to trigger firmware swap on next boot if verify suceeds.
910 ///
911 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
912 /// been generated from a SHA-512 digest of the firmware bytes.
913 ///
914 /// If no signature feature is set then this method will always return a
915 /// signature error.
916 ///
917 /// # Safety
918 ///
919 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
920 /// and written to.
921 #[cfg(feature = "_verify")]
922 pub fn verify_and_mark_updated_blocking<F: NorFlash>(
923 &mut self,
924 _flash: &mut F,
925 _public_key: &[u8],
926 _signature: &[u8],
927 _update_len: usize,
928 _aligned: &mut [u8],
929 ) -> Result<(), FirmwareUpdaterError> {
930 let _end = self.dfu.from + _update_len;
931 let _read_size = _aligned.len();
932
933 assert_eq!(_aligned.len(), F::WRITE_SIZE);
934 assert!(_end <= self.dfu.to);
935
936 #[cfg(feature = "ed25519-dalek")]
937 {
938 use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier};
939
940 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
941
942 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
943 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
944
945 let mut digest = Sha512::new();
946
947 let mut offset = self.dfu.from;
948 let last_offset = _end / _read_size * _read_size;
949
950 while offset < last_offset {
951 _flash.read(offset as u32, _aligned)?;
952 digest.update(&_aligned);
953 offset += _read_size;
954 }
955
956 let remaining = _end % _read_size;
957
958 if remaining > 0 {
959 _flash.read(last_offset as u32, _aligned)?;
960 digest.update(&_aligned[0..remaining]);
961 }
962
963 public_key
964 .verify(&digest.finalize(), &signature)
965 .map_err(into_signature_error)?
966 }
967 #[cfg(feature = "ed25519-salty")]
968 {
969 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
970 use salty::{PublicKey, Sha512, Signature};
971
972 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
973 FirmwareUpdaterError::Signature(signature::Error::default())
974 }
975
976 let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
977 let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
978 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
979 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
980
981 let mut digest = Sha512::new();
982
983 let mut offset = self.dfu.from;
984 let last_offset = _end / _read_size * _read_size;
985
986 while offset < last_offset {
987 _flash.read(offset as u32, _aligned)?;
988 digest.update(&_aligned);
989 offset += _read_size;
990 }
991
992 let remaining = _end % _read_size;
993
994 if remaining > 0 {
995 _flash.read(last_offset as u32, _aligned)?;
996 digest.update(&_aligned[0..remaining]);
997 }
998
999 let message = digest.finalize();
1000 let r = public_key.verify(&message, &signature);
1001 trace!(
1002 "Verifying with public key {}, signature {} and message {} yields ok: {}",
1003 public_key.to_bytes(),
1004 signature.to_bytes(),
1005 message,
1006 r.is_ok()
1007 );
1008 r.map_err(into_signature_error)?
1009 }
1010
1011 self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash)
1012 }
1013
1014 /// Mark to trigger firmware swap on next boot.
1015 ///
1016 /// # Safety
1017 ///
1018 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
1019 #[cfg(not(feature = "_verify"))]
1020 pub fn mark_updated_blocking<F: NorFlash>(
1021 &mut self,
1022 flash: &mut F,
1023 aligned: &mut [u8],
1024 ) -> Result<(), FirmwareUpdaterError> {
1025 assert_eq!(aligned.len(), F::WRITE_SIZE);
1026 self.set_magic_blocking(aligned, SWAP_MAGIC, flash)
1027 }
1028
1029 /// Mark firmware boot successful and stop rollback on reset.
1030 ///
1031 /// # Safety
1032 ///
1033 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
1034 pub fn mark_booted_blocking<F: NorFlash>(
1035 &mut self,
1036 flash: &mut F,
1037 aligned: &mut [u8],
1038 ) -> Result<(), FirmwareUpdaterError> {
1039 assert_eq!(aligned.len(), F::WRITE_SIZE);
1040 self.set_magic_blocking(aligned, BOOT_MAGIC, flash)
1041 }
1042
1043 fn set_magic_blocking<F: NorFlash>(
1044 &mut self,
1045 aligned: &mut [u8],
1046 magic: u8,
1047 flash: &mut F,
1048 ) -> Result<(), FirmwareUpdaterError> {
1049 flash.read(self.state.from as u32, aligned)?;
1050
1051 if aligned.iter().any(|&b| b != magic) {
1052 aligned.fill(0);
1053
1054 flash.write(self.state.from as u32, aligned)?;
1055 flash.erase(self.state.from as u32, self.state.to as u32)?;
1056
1057 aligned.fill(magic);
1058 flash.write(self.state.from as u32, aligned)?;
1059 }
1060 Ok(())
1061 }
1062
1063 /// Write data to a flash page.
1064 ///
1065 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
1066 ///
1067 /// # Safety
1068 ///
1069 /// Failing to meet alignment and size requirements may result in a panic.
1070 pub fn write_firmware_blocking<F: NorFlash>(
1071 &mut self,
1072 offset: usize,
1073 data: &[u8],
1074 flash: &mut F,
1075 block_size: usize,
1076 ) -> Result<(), FirmwareUpdaterError> {
1077 assert!(data.len() >= F::ERASE_SIZE);
1078
1079 flash.erase(
1080 (self.dfu.from + offset) as u32,
1081 (self.dfu.from + offset + data.len()) as u32,
1082 )?;
1083
1084 trace!(
1085 "Erased from {} to {}",
1086 self.dfu.from + offset,
1087 self.dfu.from + offset + data.len()
1088 );
1089
1090 FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?;
1091
1092 Ok(())
1093 }
1094
1095 /// Prepare for an incoming DFU update by erasing the entire DFU area and
1096 /// returning a `FirmwareWriter`.
1097 ///
1098 /// Using this instead of `write_firmware_blocking` allows for an optimized
1099 /// API in exchange for added complexity.
1100 pub fn prepare_update_blocking<F: NorFlash>(
1101 &mut self,
1102 flash: &mut F,
1103 ) -> Result<FirmwareWriter, FirmwareUpdaterError> {
1104 flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?;
1105
1106 trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
1107
1108 Ok(FirmwareWriter(self.dfu))
1109 }
1110}
1111
1112/// FirmwareWriter allows writing blocks to an already erased flash.
1113pub struct FirmwareWriter(Partition);
1114
1115impl FirmwareWriter {
1116 /// Write data to a flash page.
1117 ///
1118 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
1119 ///
1120 /// # Safety
1121 ///
1122 /// Failing to meet alignment and size requirements may result in a panic.
1123 #[cfg(feature = "nightly")]
1124 pub async fn write_block<F: AsyncNorFlash>(
1125 &mut self,
1126 offset: usize,
1127 data: &[u8],
1128 flash: &mut F,
1129 block_size: usize,
1130 ) -> Result<(), F::Error> {
1131 trace!(
1132 "Writing firmware at offset 0x{:x} len {}",
1133 self.0.from + offset,
1134 data.len()
1135 );
1136
1137 let mut write_offset = self.0.from + offset;
1138 for chunk in data.chunks(block_size) {
1139 trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
1140 flash.write(write_offset as u32, chunk).await?;
1141 write_offset += chunk.len();
1142 }
1143 /*
1144 trace!("Wrote data, reading back for verification");
1145
1146 let mut buf: [u8; 4096] = [0; 4096];
1147 let mut data_offset = 0;
1148 let mut read_offset = self.dfu.from + offset;
1149 for chunk in buf.chunks_mut(block_size) {
1150 flash.read(read_offset as u32, chunk).await?;
1151 trace!("Read chunk at {}: {:?}", read_offset, chunk);
1152 assert_eq!(&data[data_offset..data_offset + block_size], chunk);
1153 read_offset += chunk.len();
1154 data_offset += chunk.len();
1155 }
1156 */
1157
1158 Ok(())
1159 }
1160
1161 /// Write data to a flash page.
1162 ///
1163 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
1164 ///
1165 /// # Safety
1166 ///
1167 /// Failing to meet alignment and size requirements may result in a panic.
1168 pub fn write_block_blocking<F: NorFlash>(
1169 &mut self,
1170 offset: usize,
1171 data: &[u8],
1172 flash: &mut F,
1173 block_size: usize,
1174 ) -> Result<(), F::Error> {
1175 trace!(
1176 "Writing firmware at offset 0x{:x} len {}",
1177 self.0.from + offset,
1178 data.len()
1179 );
1180
1181 let mut write_offset = self.0.from + offset;
1182 for chunk in data.chunks(block_size) {
1183 trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
1184 flash.write(write_offset as u32, chunk)?;
1185 write_offset += chunk.len();
1186 }
1187 /*
1188 trace!("Wrote data, reading back for verification");
1189
1190 let mut buf: [u8; 4096] = [0; 4096];
1191 let mut data_offset = 0;
1192 let mut read_offset = self.dfu.from + offset;
1193 for chunk in buf.chunks_mut(block_size) {
1194 flash.read(read_offset as u32, chunk).await?;
1195 trace!("Read chunk at {}: {:?}", read_offset, chunk);
1196 assert_eq!(&data[data_offset..data_offset + block_size], chunk);
1197 read_offset += chunk.len();
1198 data_offset += chunk.len();
1199 }
1200 */
1201
1202 Ok(())
1203 }
1204}
1205
1206#[cfg(test)] 47#[cfg(test)]
1207mod tests { 48mod tests {
1208 use core::convert::Infallible;
1209
1210 use embedded_storage::nor_flash::ErrorType;
1211 use embedded_storage_async::nor_flash::ReadNorFlash as AsyncReadNorFlash;
1212 use futures::executor::block_on; 49 use futures::executor::block_on;
1213 50
1214 use super::*; 51 use super::*;
52 use crate::mem_flash::MemFlash;
1215 53
1216 /* 54 /*
1217 #[test] 55 #[test]
@@ -1234,18 +72,14 @@ mod tests {
1234 const ACTIVE: Partition = Partition::new(4096, 61440); 72 const ACTIVE: Partition = Partition::new(4096, 61440);
1235 const DFU: Partition = Partition::new(61440, 122880); 73 const DFU: Partition = Partition::new(61440, 122880);
1236 74
1237 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 75 let mut flash = MemFlash::<131072, 4096, 4>::default();
1238 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); 76 flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
1239 let mut flash = SingleFlashConfig::new(&mut flash); 77 let mut flash = SingleFlashConfig::new(&mut flash);
1240 78
1241 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 79 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
1242 80
1243 let mut magic = [0; 4];
1244 let mut page = [0; 4096]; 81 let mut page = [0; 4096];
1245 assert_eq!( 82 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash, &mut page).unwrap());
1246 State::Boot,
1247 bootloader.prepare_boot(&mut flash, &mut magic, &mut page).unwrap()
1248 );
1249 } 83 }
1250 84
1251 #[test] 85 #[test]
@@ -1254,66 +88,49 @@ mod tests {
1254 const STATE: Partition = Partition::new(0, 4096); 88 const STATE: Partition = Partition::new(0, 4096);
1255 const ACTIVE: Partition = Partition::new(4096, 61440); 89 const ACTIVE: Partition = Partition::new(4096, 61440);
1256 const DFU: Partition = Partition::new(61440, 122880); 90 const DFU: Partition = Partition::new(61440, 122880);
1257 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 91 let mut flash = MemFlash::<131072, 4096, 4>::random();
1258 92
1259 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 93 let original = [rand::random::<u8>(); ACTIVE.size() as usize];
1260 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 94 let update = [rand::random::<u8>(); ACTIVE.size() as usize];
1261 let mut aligned = [0; 4]; 95 let mut aligned = [0; 4];
1262 96
1263 for i in ACTIVE.from..ACTIVE.to { 97 flash.program(ACTIVE.from, &original).unwrap();
1264 flash.0[i] = original[i - ACTIVE.from];
1265 }
1266 98
1267 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 99 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
1268 let mut updater = FirmwareUpdater::new(DFU, STATE); 100 let mut updater = FirmwareUpdater::new(DFU, STATE);
1269 let mut offset = 0; 101 block_on(updater.write_firmware(0, &update, &mut flash)).unwrap();
1270 for chunk in update.chunks(4096) {
1271 block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap();
1272 offset += chunk.len();
1273 }
1274 block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); 102 block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap();
1275 103
1276 let mut magic = [0; 4]; 104 let mut page = [0; 1024];
1277 let mut page = [0; 4096];
1278 assert_eq!( 105 assert_eq!(
1279 State::Swap, 106 State::Swap,
1280 bootloader 107 bootloader
1281 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) 108 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page)
1282 .unwrap() 109 .unwrap()
1283 ); 110 );
1284 111
1285 for i in ACTIVE.from..ACTIVE.to { 112 flash.assert_eq(ACTIVE.from, &update);
1286 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i);
1287 }
1288
1289 // First DFU page is untouched 113 // First DFU page is untouched
1290 for i in DFU.from + 4096..DFU.to { 114 flash.assert_eq(DFU.from + 4096, &original);
1291 assert_eq!(flash.0[i], original[i - DFU.from - 4096], "Index {}", i);
1292 }
1293 115
1294 // Running again should cause a revert 116 // Running again should cause a revert
1295 assert_eq!( 117 assert_eq!(
1296 State::Swap, 118 State::Swap,
1297 bootloader 119 bootloader
1298 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) 120 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page)
1299 .unwrap() 121 .unwrap()
1300 ); 122 );
1301 123
1302 for i in ACTIVE.from..ACTIVE.to { 124 flash.assert_eq(ACTIVE.from, &original);
1303 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i);
1304 }
1305
1306 // Last page is untouched 125 // Last page is untouched
1307 for i in DFU.from..DFU.to - 4096 { 126 flash.assert_eq(DFU.from, &update);
1308 assert_eq!(flash.0[i], update[i - DFU.from], "Index {}", i);
1309 }
1310 127
1311 // Mark as booted 128 // Mark as booted
1312 block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap(); 129 block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap();
1313 assert_eq!( 130 assert_eq!(
1314 State::Boot, 131 State::Boot,
1315 bootloader 132 bootloader
1316 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) 133 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page)
1317 .unwrap() 134 .unwrap()
1318 ); 135 );
1319 } 136 }
@@ -1325,50 +142,34 @@ mod tests {
1325 const ACTIVE: Partition = Partition::new(4096, 16384); 142 const ACTIVE: Partition = Partition::new(4096, 16384);
1326 const DFU: Partition = Partition::new(0, 16384); 143 const DFU: Partition = Partition::new(0, 16384);
1327 144
1328 let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); 145 let mut active = MemFlash::<16384, 4096, 8>::random();
1329 let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); 146 let mut dfu = MemFlash::<16384, 2048, 8>::random();
1330 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 147 let mut state = MemFlash::<4096, 128, 4>::random();
1331 let mut aligned = [0; 4]; 148 let mut aligned = [0; 4];
1332 149
1333 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 150 let original = [rand::random::<u8>(); ACTIVE.size() as usize];
1334 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 151 let update = [rand::random::<u8>(); ACTIVE.size() as usize];
1335 152
1336 for i in ACTIVE.from..ACTIVE.to { 153 active.program(ACTIVE.from, &original).unwrap();
1337 active.0[i] = original[i - ACTIVE.from];
1338 }
1339 154
1340 let mut updater = FirmwareUpdater::new(DFU, STATE); 155 let mut updater = FirmwareUpdater::new(DFU, STATE);
1341 156
1342 let mut offset = 0; 157 block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap();
1343 for chunk in update.chunks(2048) {
1344 block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap();
1345 offset += chunk.len();
1346 }
1347 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); 158 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
1348 159
1349 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 160 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
1350 let mut magic = [0; 4];
1351 let mut page = [0; 4096]; 161 let mut page = [0; 4096];
1352 162
1353 assert_eq!( 163 assert_eq!(
1354 State::Swap, 164 State::Swap,
1355 bootloader 165 bootloader
1356 .prepare_boot( 166 .prepare_boot(&mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), &mut page)
1357 &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu),
1358 &mut magic,
1359 &mut page
1360 )
1361 .unwrap() 167 .unwrap()
1362 ); 168 );
1363 169
1364 for i in ACTIVE.from..ACTIVE.to { 170 active.assert_eq(ACTIVE.from, &update);
1365 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i);
1366 }
1367
1368 // First DFU page is untouched 171 // First DFU page is untouched
1369 for i in DFU.from + 4096..DFU.to { 172 dfu.assert_eq(DFU.from + 4096, &original);
1370 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i);
1371 }
1372 } 173 }
1373 174
1374 #[test] 175 #[test]
@@ -1379,57 +180,35 @@ mod tests {
1379 const DFU: Partition = Partition::new(0, 16384); 180 const DFU: Partition = Partition::new(0, 16384);
1380 181
1381 let mut aligned = [0; 4]; 182 let mut aligned = [0; 4];
1382 let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); 183 let mut active = MemFlash::<16384, 2048, 4>::random();
1383 let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); 184 let mut dfu = MemFlash::<16384, 4096, 8>::random();
1384 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 185 let mut state = MemFlash::<4096, 128, 4>::random();
1385 186
1386 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 187 let original = [rand::random::<u8>(); ACTIVE.size() as usize];
1387 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 188 let update = [rand::random::<u8>(); ACTIVE.size() as usize];
1388 189
1389 for i in ACTIVE.from..ACTIVE.to { 190 active.program(ACTIVE.from, &original).unwrap();
1390 active.0[i] = original[i - ACTIVE.from];
1391 }
1392 191
1393 let mut updater = FirmwareUpdater::new(DFU, STATE); 192 let mut updater = FirmwareUpdater::new(DFU, STATE);
1394 193
1395 let mut offset = 0; 194 block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap();
1396 for chunk in update.chunks(4096) {
1397 block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap();
1398 offset += chunk.len();
1399 }
1400 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); 195 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
1401 196
1402 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 197 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
1403 let mut magic = [0; 4];
1404 let mut page = [0; 4096]; 198 let mut page = [0; 4096];
1405 assert_eq!( 199 assert_eq!(
1406 State::Swap, 200 State::Swap,
1407 bootloader 201 bootloader
1408 .prepare_boot( 202 .prepare_boot(
1409 &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,), 203 &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,),
1410 &mut magic,
1411 &mut page 204 &mut page
1412 ) 205 )
1413 .unwrap() 206 .unwrap()
1414 ); 207 );
1415 208
1416 for i in ACTIVE.from..ACTIVE.to { 209 active.assert_eq(ACTIVE.from, &update);
1417 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i);
1418 }
1419
1420 // First DFU page is untouched 210 // First DFU page is untouched
1421 for i in DFU.from + 4096..DFU.to { 211 dfu.assert_eq(DFU.from + 4096, &original);
1422 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i);
1423 }
1424 }
1425
1426 #[test]
1427 #[should_panic]
1428 fn test_range_asserts() {
1429 const ACTIVE: Partition = Partition::new(4096, 4194304);
1430 const DFU: Partition = Partition::new(4194304, 2 * 4194304);
1431 const STATE: Partition = Partition::new(0, 4096);
1432 assert_partitions(ACTIVE, DFU, STATE, 4096, 4);
1433 } 212 }
1434 213
1435 #[test] 214 #[test]
@@ -1458,13 +237,13 @@ mod tests {
1458 237
1459 const STATE: Partition = Partition::new(0, 4096); 238 const STATE: Partition = Partition::new(0, 4096);
1460 const DFU: Partition = Partition::new(4096, 8192); 239 const DFU: Partition = Partition::new(4096, 8192);
1461 let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); 240 let mut flash = MemFlash::<8192, 4096, 4>::default();
1462 241
1463 let firmware_len = firmware.len(); 242 let firmware_len = firmware.len();
1464 243
1465 let mut write_buf = [0; 4096]; 244 let mut write_buf = [0; 4096];
1466 write_buf[0..firmware_len].copy_from_slice(firmware); 245 write_buf[0..firmware_len].copy_from_slice(firmware);
1467 NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); 246 DFU.write_blocking(&mut flash, 0, &write_buf).unwrap();
1468 247
1469 // On with the test 248 // On with the test
1470 249
@@ -1476,117 +255,9 @@ mod tests {
1476 &mut flash, 255 &mut flash,
1477 &public_key.to_bytes(), 256 &public_key.to_bytes(),
1478 &signature.to_bytes(), 257 &signature.to_bytes(),
1479 firmware_len, 258 firmware_len as u32,
1480 &mut aligned, 259 &mut aligned,
1481 )) 260 ))
1482 .is_ok()); 261 .is_ok());
1483 } 262 }
1484 struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]);
1485
1486 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
1487 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1488 {
1489 const WRITE_SIZE: usize = WRITE_SIZE;
1490 const ERASE_SIZE: usize = ERASE_SIZE;
1491 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
1492 let from = from as usize;
1493 let to = to as usize;
1494 assert!(from % ERASE_SIZE == 0);
1495 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
1496 for i in from..to {
1497 self.0[i] = 0xFF;
1498 }
1499 Ok(())
1500 }
1501
1502 fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
1503 assert!(data.len() % WRITE_SIZE == 0);
1504 assert!(offset as usize % WRITE_SIZE == 0);
1505 assert!(offset as usize + data.len() <= SIZE);
1506
1507 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
1508
1509 Ok(())
1510 }
1511 }
1512
1513 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
1514 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1515 {
1516 type Error = Infallible;
1517 }
1518
1519 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
1520 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1521 {
1522 const READ_SIZE: usize = 1;
1523
1524 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
1525 let len = buf.len();
1526 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
1527 Ok(())
1528 }
1529
1530 fn capacity(&self) -> usize {
1531 SIZE
1532 }
1533 }
1534
1535 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> super::Flash
1536 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1537 {
1538 const BLOCK_SIZE: usize = ERASE_SIZE;
1539 const ERASE_VALUE: u8 = 0xFF;
1540 }
1541
1542 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
1543 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1544 {
1545 const READ_SIZE: usize = 1;
1546
1547 async fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
1548 let len = buf.len();
1549 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
1550 Ok(())
1551 }
1552
1553 fn capacity(&self) -> usize {
1554 SIZE
1555 }
1556 }
1557
1558 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
1559 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1560 {
1561 const WRITE_SIZE: usize = WRITE_SIZE;
1562 const ERASE_SIZE: usize = ERASE_SIZE;
1563
1564 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
1565 let from = from as usize;
1566 let to = to as usize;
1567 assert!(from % ERASE_SIZE == 0);
1568 assert!(to % ERASE_SIZE == 0);
1569 for i in from..to {
1570 self.0[i] = 0xFF;
1571 }
1572 Ok(())
1573 }
1574
1575 async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
1576 info!("Writing {} bytes to 0x{:x}", data.len(), offset);
1577 assert!(data.len() % WRITE_SIZE == 0);
1578 assert!(offset as usize % WRITE_SIZE == 0);
1579 assert!(
1580 offset as usize + data.len() <= SIZE,
1581 "OFFSET: {}, LEN: {}, FLASH SIZE: {}",
1582 offset,
1583 data.len(),
1584 SIZE
1585 );
1586
1587 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
1588
1589 Ok(())
1590 }
1591 }
1592} 263}