Skip to content

Commit de7dd59

Browse files
bors[bot]lulf
andauthored
Merge #713
713: Bootloader external flash r=lulf a=lulf Includes e-s and e-s-a impls for nrf QSPI WIP: Working on testing it. Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
2 parents e2ed41b + 2afff61 commit de7dd59

File tree

8 files changed

+207
-52
lines changed

8 files changed

+207
-52
lines changed

embassy-boot/boot/src/lib.rs

Lines changed: 193 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
///!
1515
mod fmt;
1616

17-
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
17+
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
1818
use embedded_storage_async::nor_flash::AsyncNorFlash;
1919

2020
pub const BOOT_MAGIC: u32 = 0xD00DF00D;
@@ -44,18 +44,41 @@ pub enum State {
4444
}
4545

4646
#[derive(PartialEq, Debug)]
47-
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48-
pub enum BootError<E> {
49-
Flash(E),
47+
pub enum BootError {
48+
Flash(NorFlashErrorKind),
5049
BadMagic,
5150
}
5251

53-
impl<E> From<E> for BootError<E> {
52+
impl<E> From<E> for BootError
53+
where
54+
E: NorFlashError,
55+
{
5456
fn from(error: E) -> Self {
55-
BootError::Flash(error)
57+
BootError::Flash(error.kind())
5658
}
5759
}
5860

61+
pub 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
69+
pub 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+
5982
/// BootLoader works with any flash implementing embedded_storage and can also work with
6083
/// different page sizes.
6184
pub struct BootLoader<const PAGE_SIZE: usize> {
@@ -168,45 +191,44 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
168191
/// | DFU | 3 | 3 | 2 | 1 | 3 |
169192
/// +-----------+--------------+--------+--------+--------+--------+
170193
///
171-
pub fn prepare_boot<F: NorFlash + ReadNorFlash>(
172-
&mut self,
173-
flash: &mut F,
174-
) -> Result<State, BootError<F::Error>> {
194+
pub fn prepare_boot<P: FlashProvider>(&mut self, p: &mut P) -> Result<State, BootError> {
175195
// Copy contents from partition N to active
176-
let state = self.read_state(flash)?;
196+
let state = self.read_state(p.state())?;
177197
match state {
178198
State::Swap => {
179199
//
180200
// Check if we already swapped. If we're in the swap state, this means we should revert
181201
// since the app has failed to mark boot as successful
182202
//
183-
if !self.is_swapped(flash)? {
203+
if !self.is_swapped(p.state())? {
184204
trace!("Swapping");
185-
self.swap(flash)?;
205+
self.swap(p)?;
186206
} else {
187207
trace!("Reverting");
188-
self.revert(flash)?;
208+
self.revert(p)?;
189209

190210
// Overwrite magic and reset progress
191-
flash.write(self.state.from as u32, &[0, 0, 0, 0])?;
192-
flash.erase(self.state.from as u32, self.state.to as u32)?;
193-
flash.write(self.state.from as u32, &BOOT_MAGIC.to_le_bytes())?;
211+
let fstate = p.state().flash();
212+
fstate.write(self.state.from as u32, &[0, 0, 0, 0])?;
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())?;
194215
}
195216
}
196217
_ => {}
197218
}
198219
Ok(state)
199220
}
200221

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> {
202223
let page_count = self.active.len() / PAGE_SIZE;
203-
let progress = self.current_progress(flash)?;
224+
let progress = self.current_progress(p)?;
204225

205226
Ok(progress >= page_count * 2)
206227
}
207228

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> {
209230
let max_index = ((self.state.len() - 4) / 4) - 1;
231+
let flash = p.flash();
210232
for i in 0..max_index {
211233
let mut buf: [u8; 4] = [0; 4];
212234
flash.read((self.state.from + 4 + i * 4) as u32, &mut buf)?;
@@ -217,7 +239,8 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
217239
Ok(max_index)
218240
}
219241

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();
221244
let w = self.state.from + 4 + idx * 4;
222245
flash.write(w as u32, &[0, 0, 0, 0])?;
223246
Ok(())
@@ -231,62 +254,104 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
231254
self.dfu.from + n * PAGE_SIZE
232255
}
233256

234-
fn copy_page_once<F: NorFlash + ReadNorFlash>(
257+
fn copy_page_once_to_active<P: FlashProvider>(
235258
&mut self,
236259
idx: usize,
237-
from: usize,
238-
to: usize,
239-
flash: &mut F,
240-
) -> Result<(), F::Error> {
260+
from_page: usize,
261+
to_page: usize,
262+
p: &mut P,
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> {
241293
let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
242-
if self.current_progress(flash)? <= idx {
243-
flash.read(from as u32, &mut buf)?;
244-
flash.erase(to as u32, (to + PAGE_SIZE) as u32)?;
245-
flash.write(to as u32, &buf)?;
246-
self.update_progress(idx, flash)?;
294+
if self.current_progress(p.state())? <= idx {
295+
let mut offset = from_page;
296+
for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) {
297+
p.active().flash().read(offset as u32, chunk)?;
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())?;
247311
}
248312
Ok(())
249313
}
250314

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> {
252316
let page_count = self.active.len() / PAGE_SIZE;
253317
// trace!("Page count: {}", page_count);
254318
for page in 0..page_count {
255319
// Copy active page to the 'next' DFU page.
256320
let active_page = self.active_addr(page_count - 1 - page);
257321
let dfu_page = self.dfu_addr(page_count - page);
258-
// info!("Copy active {} to dfu {}", active_page, dfu_page);
259-
self.copy_page_once(page * 2, active_page, dfu_page, flash)?;
322+
info!("Copy active {} to dfu {}", active_page, dfu_page);
323+
self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?;
260324

261325
// Copy DFU page to the active page
262326
let active_page = self.active_addr(page_count - 1 - page);
263327
let dfu_page = self.dfu_addr(page_count - 1 - page);
264-
//info!("Copy dfy {} to active {}", dfu_page, active_page);
265-
self.copy_page_once(page * 2 + 1, dfu_page, active_page, flash)?;
328+
info!("Copy dfy {} to active {}", dfu_page, active_page);
329+
self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?;
266330
}
267331

268332
Ok(())
269333
}
270334

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> {
272336
let page_count = self.active.len() / PAGE_SIZE;
273337
for page in 0..page_count {
274338
// Copy the bad active page to the DFU page
275339
let active_page = self.active_addr(page);
276340
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)?;
278342

279343
// Copy the DFU page back to the active page
280344
let active_page = self.active_addr(page);
281345
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)?;
283347
}
284348

285349
Ok(())
286350
}
287351

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> {
289353
let mut magic: [u8; 4] = [0; 4];
354+
let flash = p.flash();
290355
flash.read(self.state.from as u32, &mut magic)?;
291356

292357
match u32::from_le_bytes(magic) {
@@ -296,6 +361,62 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
296361
}
297362
}
298363

364+
/// Convenience provider that uses a single flash for everything
365+
pub struct SingleFlashProvider<'a, F>
366+
where
367+
F: NorFlash + ReadNorFlash,
368+
{
369+
config: SingleFlashConfig<'a, F>,
370+
}
371+
372+
impl<'a, F> SingleFlashProvider<'a, F>
373+
where
374+
F: NorFlash + ReadNorFlash,
375+
{
376+
pub fn new(flash: &'a mut F) -> Self {
377+
Self {
378+
config: SingleFlashConfig { flash },
379+
}
380+
}
381+
}
382+
383+
pub struct SingleFlashConfig<'a, F>
384+
where
385+
F: NorFlash + ReadNorFlash,
386+
{
387+
flash: &'a mut F,
388+
}
389+
390+
impl<'a, F> FlashProvider for SingleFlashProvider<'a, F>
391+
where
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+
409+
impl<'a, F> FlashConfig for SingleFlashConfig<'a, F>
410+
where
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+
299420
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
300421
/// 'mess up' the internal bootloader state
301422
pub struct FirmwareUpdater {
@@ -371,7 +492,10 @@ impl FirmwareUpdater {
371492
offset: usize,
372493
data: &[u8],
373494
flash: &mut F,
495+
block_size: usize,
374496
) -> Result<(), F::Error> {
497+
assert!(data.len() >= F::ERASE_SIZE);
498+
375499
trace!(
376500
"Writing firmware at offset 0x{:x} len {}",
377501
self.dfu.from + offset,
@@ -384,7 +508,35 @@ impl FirmwareUpdater {
384508
(self.dfu.from + offset + data.len()) as u32,
385509
)
386510
.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(())
388540
}
389541
}
390542

embassy-boot/nrf/.cargo/config.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
[unstable]
2-
namespaced-features = true
32
build-std = ["core"]
43
build-std-features = ["panic_immediate_abort"]
54

embassy-boot/nrf/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ defmt = { version = "0.3", optional = true }
1212
defmt-rtt = { version = "0.3", optional = true }
1313

1414
embassy = { path = "../../embassy", default-features = false }
15-
embassy-nrf = { path = "../../embassy-nrf", default-features = false }
15+
embassy-nrf = { path = "../../embassy-nrf", default-features = false, features = ["nightly"] }
1616
embassy-boot = { path = "../boot", default-features = false }
1717
cortex-m = { version = "0.7" }
1818
cortex-m-rt = { version = "0.7" }

embassy-boot/nrf/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
mod fmt;
66

7-
pub use embassy_boot::{FirmwareUpdater, Partition, State, BOOT_MAGIC};
7+
pub use embassy_boot::{
8+
FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State, BOOT_MAGIC,
9+
};
810
use embassy_nrf::{
911
nvmc::{Nvmc, PAGE_SIZE},
1012
peripherals::WDT,
@@ -62,7 +64,7 @@ impl BootLoader {
6264
}
6365

6466
/// 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 {
6668
match self.boot.prepare_boot(flash) {
6769
Ok(_) => self.boot.boot_address(),
6870
Err(_) => panic!("boot prepare error!"),

0 commit comments

Comments
 (0)