aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2022-04-19 14:42:38 +0200
committerUlf Lilleengen <[email protected]>2022-04-19 20:07:06 +0200
commit2afff617f652d0fdcfa9ffcfd49062e3d2c996a3 (patch)
treebc531122365086cd24a12a9331c94f9f042cad3a /embassy-boot
parente2ed41b3832db17633ae8ae1ee9391639c3a9229 (diff)
Support multiple flash instances in embassy-boot
* Add FlashProvider and FlashConfig traits to define flash characteristics * Use traits in bootloader to retrieve flash handles and for copying data between flash instances * Add convenience implementations for using a single flash instance.
Diffstat (limited to 'embassy-boot')
-rw-r--r--embassy-boot/boot/src/lib.rs234
-rw-r--r--embassy-boot/nrf/.cargo/config.toml1
-rw-r--r--embassy-boot/nrf/Cargo.toml2
-rw-r--r--embassy-boot/nrf/src/lib.rs6
-rw-r--r--embassy-boot/nrf/src/main.rs6
5 files changed, 203 insertions, 46 deletions
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 6f31e280d..0d33ad1a6 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -14,7 +14,7 @@
14///! 14///!
15mod fmt; 15mod fmt;
16 16
17use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; 17use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
18use embedded_storage_async::nor_flash::AsyncNorFlash; 18use embedded_storage_async::nor_flash::AsyncNorFlash;
19 19
20pub const BOOT_MAGIC: u32 = 0xD00DF00D; 20pub const BOOT_MAGIC: u32 = 0xD00DF00D;
@@ -44,18 +44,41 @@ pub enum State {
44} 44}
45 45
46#[derive(PartialEq, Debug)] 46#[derive(PartialEq, Debug)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))] 47pub enum BootError {
48pub enum BootError<E> { 48 Flash(NorFlashErrorKind),
49 Flash(E),
50 BadMagic, 49 BadMagic,
51} 50}
52 51
53impl<E> From<E> for BootError<E> { 52impl<E> From<E> for BootError
53where
54 E: NorFlashError,
55{
54 fn from(error: E) -> Self { 56 fn from(error: E) -> Self {
55 BootError::Flash(error) 57 BootError::Flash(error.kind())
56 } 58 }
57} 59}
58 60
61pub trait FlashConfig {
62 const BLOCK_SIZE: usize;
63 type FLASH: NorFlash + ReadNorFlash;
64
65 fn flash(&mut self) -> &mut Self::FLASH;
66}
67
68/// Trait defining the flash handles used for active and DFU partition
69pub trait FlashProvider {
70 type STATE: FlashConfig;
71 type ACTIVE: FlashConfig;
72 type DFU: FlashConfig;
73
74 /// Return flash instance used to write/read to/from active partition.
75 fn active(&mut self) -> &mut Self::ACTIVE;
76 /// Return flash instance used to write/read to/from dfu partition.
77 fn dfu(&mut self) -> &mut Self::DFU;
78 /// Return flash instance used to write/read to/from bootloader state.
79 fn state(&mut self) -> &mut Self::STATE;
80}
81
59/// BootLoader works with any flash implementing embedded_storage and can also work with 82/// BootLoader works with any flash implementing embedded_storage and can also work with
60/// different page sizes. 83/// different page sizes.
61pub struct BootLoader<const PAGE_SIZE: usize> { 84pub struct BootLoader<const PAGE_SIZE: usize> {
@@ -168,29 +191,27 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
168 /// | DFU | 3 | 3 | 2 | 1 | 3 | 191 /// | DFU | 3 | 3 | 2 | 1 | 3 |
169 /// +-----------+--------------+--------+--------+--------+--------+ 192 /// +-----------+--------------+--------+--------+--------+--------+
170 /// 193 ///
171 pub fn prepare_boot<F: NorFlash + ReadNorFlash>( 194 pub fn prepare_boot<P: FlashProvider>(&mut self, p: &mut P) -> Result<State, BootError> {
172 &mut self,
173 flash: &mut F,
174 ) -> Result<State, BootError<F::Error>> {
175 // Copy contents from partition N to active 195 // Copy contents from partition N to active
176 let state = self.read_state(flash)?; 196 let state = self.read_state(p.state())?;
177 match state { 197 match state {
178 State::Swap => { 198 State::Swap => {
179 // 199 //
180 // Check if we already swapped. If we're in the swap state, this means we should revert 200 // Check if we already swapped. If we're in the swap state, this means we should revert
181 // since the app has failed to mark boot as successful 201 // since the app has failed to mark boot as successful
182 // 202 //
183 if !self.is_swapped(flash)? { 203 if !self.is_swapped(p.state())? {
184 trace!("Swapping"); 204 trace!("Swapping");
185 self.swap(flash)?; 205 self.swap(p)?;
186 } else { 206 } else {
187 trace!("Reverting"); 207 trace!("Reverting");
188 self.revert(flash)?; 208 self.revert(p)?;
189 209
190 // Overwrite magic and reset progress 210 // Overwrite magic and reset progress
191 flash.write(self.state.from as u32, &[0, 0, 0, 0])?; 211 let fstate = p.state().flash();
192 flash.erase(self.state.from as u32, self.state.to as u32)?; 212 fstate.write(self.state.from as u32, &[0, 0, 0, 0])?;
193 flash.write(self.state.from as u32, &BOOT_MAGIC.to_le_bytes())?; 213 fstate.erase(self.state.from as u32, self.state.to as u32)?;
214 fstate.write(self.state.from as u32, &BOOT_MAGIC.to_le_bytes())?;
194 } 215 }
195 } 216 }
196 _ => {} 217 _ => {}
@@ -198,15 +219,16 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
198 Ok(state) 219 Ok(state)
199 } 220 }
200 221
201 fn is_swapped<F: ReadNorFlash>(&mut self, flash: &mut F) -> Result<bool, F::Error> { 222 fn is_swapped<P: FlashConfig>(&mut self, p: &mut P) -> Result<bool, BootError> {
202 let page_count = self.active.len() / PAGE_SIZE; 223 let page_count = self.active.len() / PAGE_SIZE;
203 let progress = self.current_progress(flash)?; 224 let progress = self.current_progress(p)?;
204 225
205 Ok(progress >= page_count * 2) 226 Ok(progress >= page_count * 2)
206 } 227 }
207 228
208 fn current_progress<F: ReadNorFlash>(&mut self, flash: &mut F) -> Result<usize, F::Error> { 229 fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError> {
209 let max_index = ((self.state.len() - 4) / 4) - 1; 230 let max_index = ((self.state.len() - 4) / 4) - 1;
231 let flash = p.flash();
210 for i in 0..max_index { 232 for i in 0..max_index {
211 let mut buf: [u8; 4] = [0; 4]; 233 let mut buf: [u8; 4] = [0; 4];
212 flash.read((self.state.from + 4 + i * 4) as u32, &mut buf)?; 234 flash.read((self.state.from + 4 + i * 4) as u32, &mut buf)?;
@@ -217,7 +239,8 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
217 Ok(max_index) 239 Ok(max_index)
218 } 240 }
219 241
220 fn update_progress<F: NorFlash>(&mut self, idx: usize, flash: &mut F) -> Result<(), F::Error> { 242 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> {
243 let flash = p.flash();
221 let w = self.state.from + 4 + idx * 4; 244 let w = self.state.from + 4 + idx * 4;
222 flash.write(w as u32, &[0, 0, 0, 0])?; 245 flash.write(w as u32, &[0, 0, 0, 0])?;
223 Ok(()) 246 Ok(())
@@ -231,62 +254,104 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
231 self.dfu.from + n * PAGE_SIZE 254 self.dfu.from + n * PAGE_SIZE
232 } 255 }
233 256
234 fn copy_page_once<F: NorFlash + ReadNorFlash>( 257 fn copy_page_once_to_active<P: FlashProvider>(
235 &mut self, 258 &mut self,
236 idx: usize, 259 idx: usize,
237 from: usize, 260 from_page: usize,
238 to: usize, 261 to_page: usize,
239 flash: &mut F, 262 p: &mut P,
240 ) -> Result<(), F::Error> { 263 ) -> Result<(), BootError> {
264 let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
265 if self.current_progress(p.state())? <= idx {
266 let mut offset = from_page;
267 for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) {
268 p.dfu().flash().read(offset as u32, chunk)?;
269 offset += chunk.len();
270 }
271
272 p.active()
273 .flash()
274 .erase(to_page as u32, (to_page + PAGE_SIZE) as u32)?;
275
276 let mut offset = to_page;
277 for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) {
278 p.active().flash().write(offset as u32, &chunk)?;
279 offset += chunk.len();
280 }
281 self.update_progress(idx, p.state())?;
282 }
283 Ok(())
284 }
285
286 fn copy_page_once_to_dfu<P: FlashProvider>(
287 &mut self,
288 idx: usize,
289 from_page: usize,
290 to_page: usize,
291 p: &mut P,
292 ) -> Result<(), BootError> {
241 let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; 293 let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
242 if self.current_progress(flash)? <= idx { 294 if self.current_progress(p.state())? <= idx {
243 flash.read(from as u32, &mut buf)?; 295 let mut offset = from_page;
244 flash.erase(to as u32, (to + PAGE_SIZE) as u32)?; 296 for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) {
245 flash.write(to as u32, &buf)?; 297 p.active().flash().read(offset as u32, chunk)?;
246 self.update_progress(idx, flash)?; 298 offset += chunk.len();
299 }
300
301 p.dfu()
302 .flash()
303 .erase(to_page as u32, (to_page + PAGE_SIZE) as u32)?;
304
305 let mut offset = to_page;
306 for chunk in buf.chunks(P::DFU::BLOCK_SIZE) {
307 p.dfu().flash().write(offset as u32, chunk)?;
308 offset += chunk.len();
309 }
310 self.update_progress(idx, p.state())?;
247 } 311 }
248 Ok(()) 312 Ok(())
249 } 313 }
250 314
251 fn swap<F: NorFlash + ReadNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { 315 fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> {
252 let page_count = self.active.len() / PAGE_SIZE; 316 let page_count = self.active.len() / PAGE_SIZE;
253 // trace!("Page count: {}", page_count); 317 // trace!("Page count: {}", page_count);
254 for page in 0..page_count { 318 for page in 0..page_count {
255 // Copy active page to the 'next' DFU page. 319 // Copy active page to the 'next' DFU page.
256 let active_page = self.active_addr(page_count - 1 - page); 320 let active_page = self.active_addr(page_count - 1 - page);
257 let dfu_page = self.dfu_addr(page_count - page); 321 let dfu_page = self.dfu_addr(page_count - page);
258 // info!("Copy active {} to dfu {}", active_page, dfu_page); 322 info!("Copy active {} to dfu {}", active_page, dfu_page);
259 self.copy_page_once(page * 2, active_page, dfu_page, flash)?; 323 self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?;
260 324
261 // Copy DFU page to the active page 325 // Copy DFU page to the active page
262 let active_page = self.active_addr(page_count - 1 - page); 326 let active_page = self.active_addr(page_count - 1 - page);
263 let dfu_page = self.dfu_addr(page_count - 1 - page); 327 let dfu_page = self.dfu_addr(page_count - 1 - page);
264 //info!("Copy dfy {} to active {}", dfu_page, active_page); 328 info!("Copy dfy {} to active {}", dfu_page, active_page);
265 self.copy_page_once(page * 2 + 1, dfu_page, active_page, flash)?; 329 self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?;
266 } 330 }
267 331
268 Ok(()) 332 Ok(())
269 } 333 }
270 334
271 fn revert<F: NorFlash + ReadNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { 335 fn revert<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> {
272 let page_count = self.active.len() / PAGE_SIZE; 336 let page_count = self.active.len() / PAGE_SIZE;
273 for page in 0..page_count { 337 for page in 0..page_count {
274 // Copy the bad active page to the DFU page 338 // Copy the bad active page to the DFU page
275 let active_page = self.active_addr(page); 339 let active_page = self.active_addr(page);
276 let dfu_page = self.dfu_addr(page); 340 let dfu_page = self.dfu_addr(page);
277 self.copy_page_once(page_count * 2 + page * 2, active_page, dfu_page, flash)?; 341 self.copy_page_once_to_dfu(page_count * 2 + page * 2, active_page, dfu_page, p)?;
278 342
279 // Copy the DFU page back to the active page 343 // Copy the DFU page back to the active page
280 let active_page = self.active_addr(page); 344 let active_page = self.active_addr(page);
281 let dfu_page = self.dfu_addr(page + 1); 345 let dfu_page = self.dfu_addr(page + 1);
282 self.copy_page_once(page_count * 2 + page * 2 + 1, dfu_page, active_page, flash)?; 346 self.copy_page_once_to_active(page_count * 2 + page * 2 + 1, dfu_page, active_page, p)?;
283 } 347 }
284 348
285 Ok(()) 349 Ok(())
286 } 350 }
287 351
288 fn read_state<F: ReadNorFlash>(&mut self, flash: &mut F) -> Result<State, BootError<F::Error>> { 352 fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError> {
289 let mut magic: [u8; 4] = [0; 4]; 353 let mut magic: [u8; 4] = [0; 4];
354 let flash = p.flash();
290 flash.read(self.state.from as u32, &mut magic)?; 355 flash.read(self.state.from as u32, &mut magic)?;
291 356
292 match u32::from_le_bytes(magic) { 357 match u32::from_le_bytes(magic) {
@@ -296,6 +361,62 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
296 } 361 }
297} 362}
298 363
364/// Convenience provider that uses a single flash for everything
365pub struct SingleFlashProvider<'a, F>
366where
367 F: NorFlash + ReadNorFlash,
368{
369 config: SingleFlashConfig<'a, F>,
370}
371
372impl<'a, F> SingleFlashProvider<'a, F>
373where
374 F: NorFlash + ReadNorFlash,
375{
376 pub fn new(flash: &'a mut F) -> Self {
377 Self {
378 config: SingleFlashConfig { flash },
379 }
380 }
381}
382
383pub struct SingleFlashConfig<'a, F>
384where
385 F: NorFlash + ReadNorFlash,
386{
387 flash: &'a mut F,
388}
389
390impl<'a, F> FlashProvider for SingleFlashProvider<'a, F>
391where
392 F: NorFlash + ReadNorFlash,
393{
394 type STATE = SingleFlashConfig<'a, F>;
395 type ACTIVE = SingleFlashConfig<'a, F>;
396 type DFU = SingleFlashConfig<'a, F>;
397
398 fn active(&mut self) -> &mut Self::STATE {
399 &mut self.config
400 }
401 fn dfu(&mut self) -> &mut Self::ACTIVE {
402 &mut self.config
403 }
404 fn state(&mut self) -> &mut Self::DFU {
405 &mut self.config
406 }
407}
408
409impl<'a, F> FlashConfig for SingleFlashConfig<'a, F>
410where
411 F: NorFlash + ReadNorFlash,
412{
413 const BLOCK_SIZE: usize = F::ERASE_SIZE;
414 type FLASH = F;
415 fn flash(&mut self) -> &mut F {
416 self.flash
417 }
418}
419
299/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to 420/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
300/// 'mess up' the internal bootloader state 421/// 'mess up' the internal bootloader state
301pub struct FirmwareUpdater { 422pub struct FirmwareUpdater {
@@ -371,7 +492,10 @@ impl FirmwareUpdater {
371 offset: usize, 492 offset: usize,
372 data: &[u8], 493 data: &[u8],
373 flash: &mut F, 494 flash: &mut F,
495 block_size: usize,
374 ) -> Result<(), F::Error> { 496 ) -> Result<(), F::Error> {
497 assert!(data.len() >= F::ERASE_SIZE);
498
375 trace!( 499 trace!(
376 "Writing firmware at offset 0x{:x} len {}", 500 "Writing firmware at offset 0x{:x} len {}",
377 self.dfu.from + offset, 501 self.dfu.from + offset,
@@ -384,7 +508,35 @@ impl FirmwareUpdater {
384 (self.dfu.from + offset + data.len()) as u32, 508 (self.dfu.from + offset + data.len()) as u32,
385 ) 509 )
386 .await?; 510 .await?;
387 flash.write((self.dfu.from + offset) as u32, data).await 511
512 trace!(
513 "Erased from {} to {}",
514 self.dfu.from + offset,
515 self.dfu.from + offset + data.len()
516 );
517
518 let mut write_offset = self.dfu.from + offset;
519 for chunk in data.chunks(block_size) {
520 trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
521 flash.write(write_offset as u32, chunk).await?;
522 write_offset += chunk.len();
523 }
524 /*
525 trace!("Wrote data, reading back for verification");
526
527 let mut buf: [u8; 4096] = [0; 4096];
528 let mut data_offset = 0;
529 let mut read_offset = self.dfu.from + offset;
530 for chunk in buf.chunks_mut(block_size) {
531 flash.read(read_offset as u32, chunk).await?;
532 trace!("Read chunk at {}: {:?}", read_offset, chunk);
533 assert_eq!(&data[data_offset..data_offset + block_size], chunk);
534 read_offset += chunk.len();
535 data_offset += chunk.len();
536 }
537 */
538
539 Ok(())
388 } 540 }
389} 541}
390 542
diff --git a/embassy-boot/nrf/.cargo/config.toml b/embassy-boot/nrf/.cargo/config.toml
index c3957b866..27bc9708c 100644
--- a/embassy-boot/nrf/.cargo/config.toml
+++ b/embassy-boot/nrf/.cargo/config.toml
@@ -1,5 +1,4 @@
1[unstable] 1[unstable]
2namespaced-features = true
3build-std = ["core"] 2build-std = ["core"]
4build-std-features = ["panic_immediate_abort"] 3build-std-features = ["panic_immediate_abort"]
5 4
diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml
index 512e7d378..97207ac29 100644
--- a/embassy-boot/nrf/Cargo.toml
+++ b/embassy-boot/nrf/Cargo.toml
@@ -12,7 +12,7 @@ defmt = { version = "0.3", optional = true }
12defmt-rtt = { version = "0.3", optional = true } 12defmt-rtt = { version = "0.3", optional = true }
13 13
14embassy = { path = "../../embassy", default-features = false } 14embassy = { path = "../../embassy", default-features = false }
15embassy-nrf = { path = "../../embassy-nrf", default-features = false } 15embassy-nrf = { path = "../../embassy-nrf", default-features = false, features = ["nightly"] }
16embassy-boot = { path = "../boot", default-features = false } 16embassy-boot = { path = "../boot", default-features = false }
17cortex-m = { version = "0.7" } 17cortex-m = { version = "0.7" }
18cortex-m-rt = { version = "0.7" } 18cortex-m-rt = { version = "0.7" }
diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs
index 32250b2db..785cb67e8 100644
--- a/embassy-boot/nrf/src/lib.rs
+++ b/embassy-boot/nrf/src/lib.rs
@@ -4,7 +4,9 @@
4 4
5mod fmt; 5mod fmt;
6 6
7pub use embassy_boot::{FirmwareUpdater, Partition, State, BOOT_MAGIC}; 7pub use embassy_boot::{
8 FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State, BOOT_MAGIC,
9};
8use embassy_nrf::{ 10use embassy_nrf::{
9 nvmc::{Nvmc, PAGE_SIZE}, 11 nvmc::{Nvmc, PAGE_SIZE},
10 peripherals::WDT, 12 peripherals::WDT,
@@ -62,7 +64,7 @@ impl BootLoader {
62 } 64 }
63 65
64 /// Boots the application without softdevice mechanisms 66 /// Boots the application without softdevice mechanisms
65 pub fn prepare<F: NorFlash + ReadNorFlash>(&mut self, flash: &mut F) -> usize { 67 pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize {
66 match self.boot.prepare_boot(flash) { 68 match self.boot.prepare_boot(flash) {
67 Ok(_) => self.boot.boot_address(), 69 Ok(_) => self.boot.boot_address(),
68 Err(_) => panic!("boot prepare error!"), 70 Err(_) => panic!("boot prepare error!"),
diff --git a/embassy-boot/nrf/src/main.rs b/embassy-boot/nrf/src/main.rs
index cd264d4c2..63de7c869 100644
--- a/embassy-boot/nrf/src/main.rs
+++ b/embassy-boot/nrf/src/main.rs
@@ -22,7 +22,11 @@ fn main() -> ! {
22 */ 22 */
23 23
24 let mut bl = BootLoader::default(); 24 let mut bl = BootLoader::default();
25 let start = bl.prepare(&mut WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5)); 25 let start = bl.prepare(&mut SingleFlashProvider::new(&mut WatchdogFlash::start(
26 Nvmc::new(p.NVMC),
27 p.WDT,
28 5,
29 )));
26 unsafe { bl.load(start) } 30 unsafe { bl.load(start) }
27} 31}
28 32