aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRasmus Melchior Jacobsen <[email protected]>2023-04-04 21:09:30 +0200
committerRasmus Melchior Jacobsen <[email protected]>2023-04-04 21:09:30 +0200
commit25577e0eafd8a3d4ffaa4b8f17cb55399fd58038 (patch)
tree8e465872041cd22d1f15c2fd81cd43063d1d1a58
parent9242ad89d436422fd5abdafadb2faf845e730a16 (diff)
Assert active and dfu have same erase size and copy in smaller chunks
The copy from active to dfu (and vice versa) is now done in smaller portions depending on aligned_buf, which now does not need to be erase_size big.
-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