aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-boot/boot/src/boot_loader.rs66
-rw-r--r--embassy-boot/boot/src/large_erase.rs76
-rw-r--r--embassy-boot/boot/src/lib.rs32
-rw-r--r--embassy-boot/boot/src/mem_flash.rs1
4 files changed, 118 insertions, 57 deletions
diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs
index 698075599..db067da5b 100644
--- a/embassy-boot/boot/src/boot_loader.rs
+++ b/embassy-boot/boot/src/boot_loader.rs
@@ -32,14 +32,13 @@ where
32 32
33/// Extension of the embedded-storage flash type information with block size and erase value. 33/// Extension of the embedded-storage flash type information with block size and erase value.
34pub trait Flash: NorFlash { 34pub trait Flash: NorFlash {
35 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase
36 /// size of the flash, but for external QSPI flash modules, this can be lower.
37 const BLOCK_SIZE: usize;
38 /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. 35 /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value.
39 const ERASE_VALUE: u8 = 0xFF; 36 const ERASE_VALUE: u8 = 0xFF;
40} 37}
41 38
42/// Trait defining the flash handles used for active and DFU partition 39/// Trait defining the flash handles used for active and DFU partition.
40/// The ACTIVE and DFU erase sizes must be equal. If this is not the case, then consider adding an adapter for the
41/// smallest flash to increase its erase size such that they match. See e.g. [`crate::large_erase::LargeErase`].
43pub trait FlashConfig { 42pub trait FlashConfig {
44 /// Flash type used for the state partition. 43 /// Flash type used for the state partition.
45 type STATE: Flash; 44 type STATE: Flash;
@@ -62,12 +61,12 @@ trait FlashConfigEx {
62 61
63impl<T: FlashConfig> FlashConfigEx for T { 62impl<T: FlashConfig> FlashConfigEx for T {
64 fn page_size() -> usize { 63 fn page_size() -> usize {
65 core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) 64 assert_eq!(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE);
65 T::ACTIVE::ERASE_SIZE
66 } 66 }
67} 67}
68 68
69/// BootLoader works with any flash implementing embedded_storage and can also work with 69/// BootLoader works with any flash implementing embedded_storage.
70/// different page sizes and flash write sizes.
71pub struct BootLoader { 70pub struct BootLoader {
72 // Page with current state of bootloader. The state partition has the following format: 71 // Page with current state of bootloader. The state partition has the following format:
73 // All ranges are in multiples of WRITE_SIZE bytes. 72 // All ranges are in multiples of WRITE_SIZE bytes.
@@ -184,7 +183,9 @@ impl BootLoader {
184 /// 183 ///
185 pub fn prepare_boot<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<State, BootError> { 184 pub fn prepare_boot<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<State, BootError> {
186 // Ensure we have enough progress pages to store copy progress 185 // Ensure we have enough progress pages to store copy progress
187 assert_eq!(aligned_buf.len(), P::page_size()); 186 assert_eq!(0, P::page_size() % aligned_buf.len());
187 assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE);
188 assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE);
188 assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); 189 assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE);
189 assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); 190 assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE);
190 191
@@ -277,20 +278,18 @@ impl BootLoader {
277 aligned_buf: &mut [u8], 278 aligned_buf: &mut [u8],
278 ) -> Result<(), BootError> { 279 ) -> Result<(), BootError> {
279 if self.current_progress(p, aligned_buf)? <= idx { 280 if self.current_progress(p, aligned_buf)? <= idx {
280 let mut offset = from_offset; 281 let page_size = P::page_size() as u32;
281 for chunk in aligned_buf.chunks_mut(P::DFU::BLOCK_SIZE) {
282 self.dfu.read_blocking(p.dfu(), offset, chunk)?;
283 offset += chunk.len() as u32;
284 }
285 282
286 self.active 283 self.active
287 .erase_blocking(p.active(), to_offset, to_offset + P::page_size() as u32)?; 284 .erase_blocking(p.active(), to_offset, to_offset + page_size)?;
288 285
289 let mut offset = to_offset; 286 for offset_in_page in (0..page_size).step_by(aligned_buf.len()) {
290 for chunk in aligned_buf.chunks(P::ACTIVE::BLOCK_SIZE) { 287 self.dfu
291 self.active.write_blocking(p.active(), offset, chunk)?; 288 .read_blocking(p.dfu(), from_offset + offset_in_page as u32, aligned_buf)?;
292 offset += chunk.len() as u32; 289 self.active
290 .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?;
293 } 291 }
292
294 self.update_progress(idx, p, aligned_buf)?; 293 self.update_progress(idx, p, aligned_buf)?;
295 } 294 }
296 Ok(()) 295 Ok(())
@@ -305,20 +304,18 @@ impl BootLoader {
305 aligned_buf: &mut [u8], 304 aligned_buf: &mut [u8],
306 ) -> Result<(), BootError> { 305 ) -> Result<(), BootError> {
307 if self.current_progress(p, aligned_buf)? <= idx { 306 if self.current_progress(p, aligned_buf)? <= idx {
308 let mut offset = from_offset; 307 let page_size = P::page_size() as u32;
309 for chunk in aligned_buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) {
310 self.active.read_blocking(p.active(), offset, chunk)?;
311 offset += chunk.len() as u32;
312 }
313 308
314 self.dfu 309 self.dfu
315 .erase_blocking(p.dfu(), to_offset as u32, to_offset + P::page_size() as u32)?; 310 .erase_blocking(p.dfu(), to_offset as u32, to_offset + page_size)?;
316 311
317 let mut offset = to_offset; 312 for offset_in_page in (0..page_size).step_by(aligned_buf.len()) {
318 for chunk in aligned_buf.chunks(P::DFU::BLOCK_SIZE) { 313 self.active
319 self.dfu.write_blocking(p.dfu(), offset, chunk)?; 314 .read_blocking(p.active(), from_offset + offset_in_page as u32, aligned_buf)?;
320 offset += chunk.len() as u32; 315 self.dfu
316 .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?;
321 } 317 }
318
322 self.update_progress(idx, p, aligned_buf)?; 319 self.update_progress(idx, p, aligned_buf)?;
323 } 320 }
324 Ok(()) 321 Ok(())
@@ -389,14 +386,14 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s
389} 386}
390 387
391/// A flash wrapper implementing the Flash and embedded_storage traits. 388/// A flash wrapper implementing the Flash and embedded_storage traits.
392pub struct BootFlash<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF> 389pub struct BootFlash<F, const ERASE_VALUE: u8 = 0xFF>
393where 390where
394 F: NorFlash + ReadNorFlash, 391 F: NorFlash + ReadNorFlash,
395{ 392{
396 flash: F, 393 flash: F,
397} 394}
398 395
399impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<F, BLOCK_SIZE, ERASE_VALUE> 396impl<F, const ERASE_VALUE: u8> BootFlash<F, ERASE_VALUE>
400where 397where
401 F: NorFlash + ReadNorFlash, 398 F: NorFlash + ReadNorFlash,
402{ 399{
@@ -406,22 +403,21 @@ where
406 } 403 }
407} 404}
408 405
409impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE> 406impl<F, const ERASE_VALUE: u8> Flash for BootFlash<F, ERASE_VALUE>
410where 407where
411 F: NorFlash + ReadNorFlash, 408 F: NorFlash + ReadNorFlash,
412{ 409{
413 const BLOCK_SIZE: usize = BLOCK_SIZE;
414 const ERASE_VALUE: u8 = ERASE_VALUE; 410 const ERASE_VALUE: u8 = ERASE_VALUE;
415} 411}
416 412
417impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<F, BLOCK_SIZE, ERASE_VALUE> 413impl<F, const ERASE_VALUE: u8> ErrorType for BootFlash<F, ERASE_VALUE>
418where 414where
419 F: ReadNorFlash + NorFlash, 415 F: ReadNorFlash + NorFlash,
420{ 416{
421 type Error = F::Error; 417 type Error = F::Error;
422} 418}
423 419
424impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE> 420impl<F, const ERASE_VALUE: u8> NorFlash for BootFlash<F, ERASE_VALUE>
425where 421where
426 F: ReadNorFlash + NorFlash, 422 F: ReadNorFlash + NorFlash,
427{ 423{
@@ -437,7 +433,7 @@ where
437 } 433 }
438} 434}
439 435
440impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE> 436impl<F, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<F, ERASE_VALUE>
441where 437where
442 F: ReadNorFlash + NorFlash, 438 F: ReadNorFlash + NorFlash,
443{ 439{
diff --git a/embassy-boot/boot/src/large_erase.rs b/embassy-boot/boot/src/large_erase.rs
new file mode 100644
index 000000000..d00d43599
--- /dev/null
+++ b/embassy-boot/boot/src/large_erase.rs
@@ -0,0 +1,76 @@
1#![allow(unused)]
2
3use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
4use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
5
6use crate::Flash;
7
8pub struct LargeErase<F, const ERASE_SIZE: usize>(pub F);
9
10impl<F, const ERASE_SIZE: usize> LargeErase<F, ERASE_SIZE> {
11 pub const fn new(flash: F) -> Self {
12 Self(flash)
13 }
14}
15
16impl<F: Flash, const ERASE_SIZE: usize> Flash for LargeErase<F, ERASE_SIZE> {
17 const ERASE_VALUE: u8 = F::ERASE_VALUE;
18}
19
20impl<F: ErrorType, const ERASE_SIZE: usize> ErrorType for LargeErase<F, ERASE_SIZE> {
21 type Error = F::Error;
22}
23
24impl<F: NorFlash, const ERASE_SIZE: usize> NorFlash for LargeErase<F, ERASE_SIZE> {
25 const WRITE_SIZE: usize = F::ERASE_SIZE;
26 const ERASE_SIZE: usize = ERASE_SIZE;
27
28 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
29 assert!(ERASE_SIZE >= F::ERASE_SIZE);
30 assert_eq!(0, ERASE_SIZE % F::ERASE_SIZE);
31 self.0.erase(from, to)
32 }
33
34 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
35 self.0.write(offset, bytes)
36 }
37}
38
39impl<F: ReadNorFlash, const ERASE_SIZE: usize> ReadNorFlash for LargeErase<F, ERASE_SIZE> {
40 const READ_SIZE: usize = F::READ_SIZE;
41
42 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
43 self.0.read(offset, bytes)
44 }
45
46 fn capacity(&self) -> usize {
47 self.0.capacity()
48 }
49}
50
51impl<F: AsyncNorFlash, const ERASE_SIZE: usize> AsyncNorFlash for LargeErase<F, ERASE_SIZE> {
52 const WRITE_SIZE: usize = F::ERASE_SIZE;
53 const ERASE_SIZE: usize = ERASE_SIZE;
54
55 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
56 assert!(ERASE_SIZE >= F::ERASE_SIZE);
57 assert_eq!(0, ERASE_SIZE % F::ERASE_SIZE);
58 self.0.erase(from, to).await
59 }
60
61 async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
62 self.0.write(offset, bytes).await
63 }
64}
65
66impl<F: AsyncReadNorFlash, const ERASE_SIZE: usize> AsyncReadNorFlash for LargeErase<F, ERASE_SIZE> {
67 const READ_SIZE: usize = F::READ_SIZE;
68
69 async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
70 self.0.read(offset, bytes).await
71 }
72
73 fn capacity(&self) -> usize {
74 self.0.capacity()
75 }
76}
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 896498c0b..79759124b 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -7,6 +7,7 @@ mod fmt;
7 7
8mod boot_loader; 8mod boot_loader;
9mod firmware_updater; 9mod firmware_updater;
10mod large_erase;
10mod mem_flash; 11mod mem_flash;
11mod partition; 12mod partition;
12 13
@@ -48,6 +49,7 @@ mod tests {
48 use futures::executor::block_on; 49 use futures::executor::block_on;
49 50
50 use super::*; 51 use super::*;
52 use crate::large_erase::LargeErase;
51 use crate::mem_flash::MemFlash; 53 use crate::mem_flash::MemFlash;
52 54
53 /* 55 /*
@@ -99,14 +101,10 @@ mod tests {
99 101
100 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 102 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
101 let mut updater = FirmwareUpdater::new(DFU, STATE); 103 let mut updater = FirmwareUpdater::new(DFU, STATE);
102 let mut offset = 0; 104 block_on(updater.write_firmware(0, &update, &mut flash)).unwrap();
103 for chunk in update.chunks(4096) {
104 block_on(updater.write_firmware(offset, chunk, &mut flash)).unwrap();
105 offset += chunk.len();
106 }
107 block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); 105 block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap();
108 106
109 let mut page = [0; 4096]; 107 let mut page = [0; 1024];
110 assert_eq!( 108 assert_eq!(
111 State::Swap, 109 State::Swap,
112 bootloader 110 bootloader
@@ -158,7 +156,7 @@ mod tests {
158 const DFU: Partition = Partition::new(0, 16384); 156 const DFU: Partition = Partition::new(0, 16384);
159 157
160 let mut active = MemFlash::<16384, 4096, 8>::random(); 158 let mut active = MemFlash::<16384, 4096, 8>::random();
161 let mut dfu = MemFlash::<16384, 2048, 8>::random(); 159 let mut dfu = LargeErase::<_, 4096>::new(MemFlash::<16384, 2048, 8>::random());
162 let mut state = MemFlash::<4096, 128, 4>::random(); 160 let mut state = MemFlash::<4096, 128, 4>::random();
163 let mut aligned = [0; 4]; 161 let mut aligned = [0; 4];
164 162
@@ -171,11 +169,7 @@ mod tests {
171 169
172 let mut updater = FirmwareUpdater::new(DFU, STATE); 170 let mut updater = FirmwareUpdater::new(DFU, STATE);
173 171
174 let mut offset = 0; 172 block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap();
175 for chunk in update.chunks(2048) {
176 block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap();
177 offset += chunk.len();
178 }
179 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); 173 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
180 174
181 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 175 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
@@ -194,7 +188,7 @@ mod tests {
194 188
195 // First DFU page is untouched 189 // First DFU page is untouched
196 for i in DFU.from + 4096..DFU.to { 190 for i in DFU.from + 4096..DFU.to {
197 assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); 191 assert_eq!(dfu.0.mem[i], original[i - DFU.from - 4096], "Index {}", i);
198 } 192 }
199 } 193 }
200 194
@@ -206,7 +200,7 @@ mod tests {
206 const DFU: Partition = Partition::new(0, 16384); 200 const DFU: Partition = Partition::new(0, 16384);
207 201
208 let mut aligned = [0; 4]; 202 let mut aligned = [0; 4];
209 let mut active = MemFlash::<16384, 2048, 4>::random(); 203 let mut active = LargeErase::<_, 4096>::new(MemFlash::<16384, 2048, 4>::random());
210 let mut dfu = MemFlash::<16384, 4096, 8>::random(); 204 let mut dfu = MemFlash::<16384, 4096, 8>::random();
211 let mut state = MemFlash::<4096, 128, 4>::random(); 205 let mut state = MemFlash::<4096, 128, 4>::random();
212 206
@@ -214,16 +208,12 @@ mod tests {
214 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 208 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
215 209
216 for i in ACTIVE.from..ACTIVE.to { 210 for i in ACTIVE.from..ACTIVE.to {
217 active.mem[i] = original[i - ACTIVE.from]; 211 active.0.mem[i] = original[i - ACTIVE.from];
218 } 212 }
219 213
220 let mut updater = FirmwareUpdater::new(DFU, STATE); 214 let mut updater = FirmwareUpdater::new(DFU, STATE);
221 215
222 let mut offset = 0; 216 block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap();
223 for chunk in update.chunks(4096) {
224 block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap();
225 offset += chunk.len();
226 }
227 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); 217 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
228 218
229 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 219 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
@@ -239,7 +229,7 @@ mod tests {
239 ); 229 );
240 230
241 for i in ACTIVE.from..ACTIVE.to { 231 for i in ACTIVE.from..ACTIVE.to {
242 assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); 232 assert_eq!(active.0.mem[i], update[i - ACTIVE.from], "Index {}", i);
243 } 233 }
244 234
245 // First DFU page is untouched 235 // First DFU page is untouched
diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs
index 828aad9d9..2598bf4de 100644
--- a/embassy-boot/boot/src/mem_flash.rs
+++ b/embassy-boot/boot/src/mem_flash.rs
@@ -47,7 +47,6 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Defaul
47impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Flash 47impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Flash
48 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> 48 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
49{ 49{
50 const BLOCK_SIZE: usize = ERASE_SIZE;
51 const ERASE_VALUE: u8 = 0xFF; 50 const ERASE_VALUE: u8 = 0xFF;
52} 51}
53 52