aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-boot/boot/Cargo.toml6
-rw-r--r--embassy-boot/boot/src/boot_loader.rs452
-rw-r--r--embassy-boot/boot/src/firmware_updater/asynch.rs176
-rw-r--r--embassy-boot/boot/src/firmware_updater/blocking.rs187
-rw-r--r--embassy-boot/boot/src/firmware_updater/mod.rs51
-rw-r--r--embassy-boot/boot/src/lib.rs265
-rw-r--r--embassy-boot/boot/src/mem_flash.rs106
-rw-r--r--embassy-boot/boot/src/partition.rs144
-rw-r--r--embassy-boot/boot/src/test_flash/asynch.rs64
-rw-r--r--embassy-boot/boot/src/test_flash/blocking.rs69
-rw-r--r--embassy-boot/boot/src/test_flash/mod.rs7
-rw-r--r--embassy-boot/nrf/src/lib.rs73
-rw-r--r--embassy-boot/rp/src/lib.rs71
-rw-r--r--embassy-boot/stm32/src/lib.rs72
-rw-r--r--embassy-embedded-hal/src/flash/partition/asynch.rs10
-rw-r--r--embassy-embedded-hal/src/flash/partition/blocking.rs10
-rw-r--r--examples/boot/application/nrf/src/bin/a.rs12
-rw-r--r--examples/boot/application/rp/Cargo.toml1
-rw-r--r--examples/boot/application/rp/src/bin/a.rs22
-rw-r--r--examples/boot/application/stm32f3/src/bin/a.rs12
-rw-r--r--examples/boot/application/stm32f7/src/bin/a.rs16
-rw-r--r--examples/boot/application/stm32h7/src/bin/a.rs17
-rw-r--r--examples/boot/application/stm32l1/src/bin/a.rs11
-rw-r--r--examples/boot/application/stm32l4/src/bin/a.rs9
-rw-r--r--examples/boot/application/stm32wl/src/bin/a.rs9
-rw-r--r--examples/boot/bootloader/nrf/Cargo.toml1
-rw-r--r--examples/boot/bootloader/nrf/src/main.rs21
-rw-r--r--examples/boot/bootloader/rp/Cargo.toml1
-rw-r--r--examples/boot/bootloader/rp/src/main.rs16
-rw-r--r--examples/boot/bootloader/stm32/Cargo.toml1
-rw-r--r--examples/boot/bootloader/stm32/src/main.rs19
31 files changed, 933 insertions, 998 deletions
diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml
index f641d5e1c..415d7960f 100644
--- a/embassy-boot/boot/Cargo.toml
+++ b/embassy-boot/boot/Cargo.toml
@@ -27,9 +27,10 @@ defmt = { version = "0.3", optional = true }
27digest = "0.10" 27digest = "0.10"
28log = { version = "0.4", optional = true } 28log = { version = "0.4", optional = true }
29ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } 29ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
30embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
30embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } 31embassy-sync = { version = "0.2.0", path = "../../embassy-sync" }
31embedded-storage = "0.3.0" 32embedded-storage = "0.3.0"
32embedded-storage-async = { version = "0.4.0", optional = true} 33embedded-storage-async = { version = "0.4.0", optional = true }
33salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } 34salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
34signature = { version = "1.6.4", default-features = false } 35signature = { version = "1.6.4", default-features = false }
35 36
@@ -39,6 +40,7 @@ env_logger = "0.9"
39rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version 40rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version
40futures = { version = "0.3", features = ["executor"] } 41futures = { version = "0.3", features = ["executor"] }
41sha1 = "0.10.5" 42sha1 = "0.10.5"
43critical-section = { version = "1.1.1", features = ["std"] }
42 44
43[dev-dependencies.ed25519-dalek] 45[dev-dependencies.ed25519-dalek]
44default_features = false 46default_features = false
@@ -48,7 +50,7 @@ features = ["rand", "std", "u32_backend"]
48ed25519-dalek = ["dep:ed25519-dalek", "_verify"] 50ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
49ed25519-salty = ["dep:salty", "_verify"] 51ed25519-salty = ["dep:salty", "_verify"]
50 52
51nightly = ["dep:embedded-storage-async"] 53nightly = ["dep:embedded-storage-async", "embassy-embedded-hal/nightly"]
52 54
53#Internal features 55#Internal features
54_verify = [] 56_verify = []
diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs
index b959de2c4..a8c19197b 100644
--- a/embassy-boot/boot/src/boot_loader.rs
+++ b/embassy-boot/boot/src/boot_loader.rs
@@ -1,6 +1,11 @@
1use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; 1use core::cell::RefCell;
2 2
3use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; 3use embassy_embedded_hal::flash::partition::BlockingPartition;
4use embassy_sync::blocking_mutex::raw::NoopRawMutex;
5use embassy_sync::blocking_mutex::Mutex;
6use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
7
8use crate::{State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
4 9
5/// Errors returned by bootloader 10/// Errors returned by bootloader
6#[derive(PartialEq, Eq, Debug)] 11#[derive(PartialEq, Eq, Debug)]
@@ -30,63 +35,96 @@ where
30 } 35 }
31} 36}
32 37
33/// Trait defining the flash handles used for active and DFU partition. 38/// Bootloader flash configuration holding the three flashes used by the bootloader
34pub trait FlashConfig { 39///
35 /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value. 40/// If only a single flash is actually used, then that flash should be partitioned into three partitions before use.
36 const STATE_ERASE_VALUE: u8 = 0xFF; 41/// The easiest way to do this is to use [`BootLoaderConfig::from_linkerfile_blocking`] which will partition
42/// the provided flash according to symbols defined in the linkerfile.
43pub struct BootLoaderConfig<ACTIVE, DFU, STATE> {
44 /// Flash type used for the active partition - the partition which will be booted from.
45 pub active: ACTIVE,
46 /// Flash type used for the dfu partition - the partition which will be swapped in when requested.
47 pub dfu: DFU,
37 /// Flash type used for the state partition. 48 /// Flash type used for the state partition.
38 type STATE: NorFlash; 49 pub state: STATE,
39 /// Flash type used for the active partition.
40 type ACTIVE: NorFlash;
41 /// Flash type used for the dfu partition.
42 type DFU: NorFlash;
43
44 /// Return flash instance used to write/read to/from active partition.
45 fn active(&mut self) -> &mut Self::ACTIVE;
46 /// Return flash instance used to write/read to/from dfu partition.
47 fn dfu(&mut self) -> &mut Self::DFU;
48 /// Return flash instance used to write/read to/from bootloader state.
49 fn state(&mut self) -> &mut Self::STATE;
50} 50}
51 51
52trait FlashConfigEx { 52impl<'a, FLASH: NorFlash>
53 fn page_size() -> u32; 53 BootLoaderConfig<
54} 54 BlockingPartition<'a, NoopRawMutex, FLASH>,
55 BlockingPartition<'a, NoopRawMutex, FLASH>,
56 BlockingPartition<'a, NoopRawMutex, FLASH>,
57 >
58{
59 /// Create a bootloader config from the flash and address symbols defined in the linkerfile
60 // #[cfg(target_os = "none")]
61 pub fn from_linkerfile_blocking(flash: &'a Mutex<NoopRawMutex, RefCell<FLASH>>) -> Self {
62 extern "C" {
63 static __bootloader_state_start: u32;
64 static __bootloader_state_end: u32;
65 static __bootloader_active_start: u32;
66 static __bootloader_active_end: u32;
67 static __bootloader_dfu_start: u32;
68 static __bootloader_dfu_end: u32;
69 }
55 70
56impl<T: FlashConfig> FlashConfigEx for T { 71 let active = unsafe {
57 /// Get the page size which is the "unit of operation" within the bootloader. 72 let start = &__bootloader_active_start as *const u32 as u32;
58 fn page_size() -> u32 { 73 let end = &__bootloader_active_end as *const u32 as u32;
59 core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) as u32 74 trace!("ACTIVE: 0x{:x} - 0x{:x}", start, end);
75
76 BlockingPartition::new(flash, start, end - start)
77 };
78 let dfu = unsafe {
79 let start = &__bootloader_dfu_start as *const u32 as u32;
80 let end = &__bootloader_dfu_end as *const u32 as u32;
81 trace!("DFU: 0x{:x} - 0x{:x}", start, end);
82
83 BlockingPartition::new(flash, start, end - start)
84 };
85 let state = unsafe {
86 let start = &__bootloader_state_start as *const u32 as u32;
87 let end = &__bootloader_state_end as *const u32 as u32;
88 trace!("STATE: 0x{:x} - 0x{:x}", start, end);
89
90 BlockingPartition::new(flash, start, end - start)
91 };
92
93 Self { active, dfu, state }
60 } 94 }
61} 95}
62 96
63/// BootLoader works with any flash implementing embedded_storage. 97/// BootLoader works with any flash implementing embedded_storage.
64pub struct BootLoader { 98pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> {
65 // Page with current state of bootloader. The state partition has the following format: 99 active: ACTIVE,
66 // All ranges are in multiples of WRITE_SIZE bytes. 100 dfu: DFU,
67 // | Range | Description | 101 /// The state partition has the following format:
68 // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 102 /// All ranges are in multiples of WRITE_SIZE bytes.
69 // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | 103 /// | Range | Description |
70 // | 2..2 + N | Progress index used while swapping or reverting | 104 /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
71 state: Partition, 105 /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. |
72 // Location of the partition which will be booted from 106 /// | 2..2 + N | Progress index used while swapping or reverting
73 active: Partition, 107 state: STATE,
74 // Location of the partition which will be swapped in when requested
75 dfu: Partition,
76} 108}
77 109
78impl BootLoader { 110impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, STATE> {
79 /// Create a new instance of a bootloader with the given partitions. 111 /// Get the page size which is the "unit of operation" within the bootloader.
112 const PAGE_SIZE: u32 = if ACTIVE::ERASE_SIZE > DFU::ERASE_SIZE {
113 ACTIVE::ERASE_SIZE as u32
114 } else {
115 DFU::ERASE_SIZE as u32
116 };
117
118 /// Create a new instance of a bootloader with the flash partitions.
80 /// 119 ///
81 /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. 120 /// - All partitions must be aligned with the PAGE_SIZE const generic parameter.
82 /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. 121 /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition.
83 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 122 pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
84 Self { active, dfu, state } 123 Self {
85 } 124 active: config.active,
86 125 dfu: config.dfu,
87 /// Return the offset of the active partition into the active flash. 126 state: config.state,
88 pub fn boot_address(&self) -> usize { 127 }
89 self.active.from as usize
90 } 128 }
91 129
92 /// Perform necessary boot preparations like swapping images. 130 /// Perform necessary boot preparations like swapping images.
@@ -175,195 +213,174 @@ impl BootLoader {
175 /// | DFU | 3 | 3 | 2 | 1 | 3 | 213 /// | DFU | 3 | 3 | 2 | 1 | 3 |
176 /// +-----------+--------------+--------+--------+--------+--------+ 214 /// +-----------+--------------+--------+--------+--------+--------+
177 /// 215 ///
178 pub fn prepare_boot<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<State, BootError> { 216 pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> {
179 // Ensure we have enough progress pages to store copy progress 217 // Ensure we have enough progress pages to store copy progress
180 assert_eq!(0, P::page_size() % aligned_buf.len() as u32); 218 assert_eq!(0, Self::PAGE_SIZE % aligned_buf.len() as u32);
181 assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE as u32); 219 assert_eq!(0, Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32);
182 assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE as u32); 220 assert_eq!(0, Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32);
183 assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE as u32); 221 assert_eq!(0, Self::PAGE_SIZE % DFU::WRITE_SIZE as u32);
184 assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE as u32); 222 assert_eq!(0, Self::PAGE_SIZE % DFU::ERASE_SIZE as u32);
185 assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); 223 assert!(aligned_buf.len() >= STATE::WRITE_SIZE);
186 assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE); 224 assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE);
187 assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE); 225 assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE);
188 assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); 226
227 assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE);
189 228
190 // Copy contents from partition N to active 229 // Copy contents from partition N to active
191 let state = self.read_state(p, aligned_buf)?; 230 let state = self.read_state(aligned_buf)?;
192 if state == State::Swap { 231 if state == State::Swap {
193 // 232 //
194 // Check if we already swapped. If we're in the swap state, this means we should revert 233 // Check if we already swapped. If we're in the swap state, this means we should revert
195 // since the app has failed to mark boot as successful 234 // since the app has failed to mark boot as successful
196 // 235 //
197 if !self.is_swapped(p, aligned_buf)? { 236 if !self.is_swapped(aligned_buf)? {
198 trace!("Swapping"); 237 trace!("Swapping");
199 self.swap(p, aligned_buf)?; 238 self.swap(aligned_buf)?;
200 trace!("Swapping done"); 239 trace!("Swapping done");
201 } else { 240 } else {
202 trace!("Reverting"); 241 trace!("Reverting");
203 self.revert(p, aligned_buf)?; 242 self.revert(aligned_buf)?;
204 243
205 let state_flash = p.state(); 244 let state_word = &mut aligned_buf[..STATE::WRITE_SIZE];
206 let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE];
207 245
208 // Invalidate progress 246 // Invalidate progress
209 state_word.fill(!P::STATE_ERASE_VALUE); 247 state_word.fill(!STATE_ERASE_VALUE);
210 self.state 248 self.state.write(STATE::WRITE_SIZE as u32, state_word)?;
211 .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?;
212 249
213 // Clear magic and progress 250 // Clear magic and progress
214 self.state.wipe_blocking(state_flash)?; 251 self.state.erase(0, self.state.capacity() as u32)?;
215 252
216 // Set magic 253 // Set magic
217 state_word.fill(BOOT_MAGIC); 254 state_word.fill(BOOT_MAGIC);
218 self.state.write_blocking(state_flash, 0, state_word)?; 255 self.state.write(0, state_word)?;
219 } 256 }
220 } 257 }
221 Ok(state) 258 Ok(state)
222 } 259 }
223 260
224 fn is_swapped<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<bool, BootError> { 261 fn is_swapped(&mut self, aligned_buf: &mut [u8]) -> Result<bool, BootError> {
225 let page_count = (self.active.size() / P::page_size()) as usize; 262 let page_count = self.active.capacity() / Self::PAGE_SIZE as usize;
226 let progress = self.current_progress(p, aligned_buf)?; 263 let progress = self.current_progress(aligned_buf)?;
227 264
228 Ok(progress >= page_count * 2) 265 Ok(progress >= page_count * 2)
229 } 266 }
230 267
231 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result<usize, BootError> { 268 fn current_progress(&mut self, aligned_buf: &mut [u8]) -> Result<usize, BootError> {
232 let write_size = P::STATE::WRITE_SIZE as u32; 269 let write_size = STATE::WRITE_SIZE as u32;
233 let max_index = (((self.state.size() - write_size) / write_size) - 2) as usize; 270 let max_index = ((self.state.capacity() - STATE::WRITE_SIZE) / STATE::WRITE_SIZE) - 2;
234 let state_flash = config.state();
235 let state_word = &mut aligned_buf[..write_size as usize]; 271 let state_word = &mut aligned_buf[..write_size as usize];
236 272
237 self.state.read_blocking(state_flash, write_size, state_word)?; 273 self.state.read(write_size, state_word)?;
238 if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) { 274 if state_word.iter().any(|&b| b != STATE_ERASE_VALUE) {
239 // Progress is invalid 275 // Progress is invalid
240 return Ok(max_index); 276 return Ok(max_index);
241 } 277 }
242 278
243 for index in 0..max_index { 279 for index in 0..max_index {
244 self.state 280 self.state.read((2 + index) as u32 * write_size, state_word)?;
245 .read_blocking(state_flash, (2 + index) as u32 * write_size, state_word)?;
246 281
247 if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) { 282 if state_word.iter().any(|&b| b == STATE_ERASE_VALUE) {
248 return Ok(index); 283 return Ok(index);
249 } 284 }
250 } 285 }
251 Ok(max_index) 286 Ok(max_index)
252 } 287 }
253 288
254 fn update_progress<P: FlashConfig>( 289 fn update_progress(&mut self, progress_index: usize, aligned_buf: &mut [u8]) -> Result<(), BootError> {
255 &mut self, 290 let state_word = &mut aligned_buf[..STATE::WRITE_SIZE];
256 progress_index: usize, 291 state_word.fill(!STATE_ERASE_VALUE);
257 p: &mut P, 292 self.state
258 aligned_buf: &mut [u8], 293 .write((2 + progress_index) as u32 * STATE::WRITE_SIZE as u32, state_word)?;
259 ) -> Result<(), BootError> {
260 let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE];
261 state_word.fill(!P::STATE_ERASE_VALUE);
262 self.state.write_blocking(
263 p.state(),
264 (2 + progress_index) as u32 * P::STATE::WRITE_SIZE as u32,
265 state_word,
266 )?;
267 Ok(()) 294 Ok(())
268 } 295 }
269 296
270 fn copy_page_once_to_active<P: FlashConfig>( 297 fn copy_page_once_to_active(
271 &mut self, 298 &mut self,
272 progress_index: usize, 299 progress_index: usize,
273 from_offset: u32, 300 from_offset: u32,
274 to_offset: u32, 301 to_offset: u32,
275 p: &mut P,
276 aligned_buf: &mut [u8], 302 aligned_buf: &mut [u8],
277 ) -> Result<(), BootError> { 303 ) -> Result<(), BootError> {
278 if self.current_progress(p, aligned_buf)? <= progress_index { 304 if self.current_progress(aligned_buf)? <= progress_index {
279 let page_size = P::page_size() as u32; 305 let page_size = Self::PAGE_SIZE as u32;
280 306
281 self.active 307 self.active.erase(to_offset, to_offset + page_size)?;
282 .erase_blocking(p.active(), to_offset, to_offset + page_size)?;
283 308
284 for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { 309 for offset_in_page in (0..page_size).step_by(aligned_buf.len()) {
285 self.dfu 310 self.dfu.read(from_offset + offset_in_page as u32, aligned_buf)?;
286 .read_blocking(p.dfu(), from_offset + offset_in_page as u32, aligned_buf)?; 311 self.active.write(to_offset + offset_in_page as u32, aligned_buf)?;
287 self.active
288 .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?;
289 } 312 }
290 313
291 self.update_progress(progress_index, p, aligned_buf)?; 314 self.update_progress(progress_index, aligned_buf)?;
292 } 315 }
293 Ok(()) 316 Ok(())
294 } 317 }
295 318
296 fn copy_page_once_to_dfu<P: FlashConfig>( 319 fn copy_page_once_to_dfu(
297 &mut self, 320 &mut self,
298 progress_index: usize, 321 progress_index: usize,
299 from_offset: u32, 322 from_offset: u32,
300 to_offset: u32, 323 to_offset: u32,
301 p: &mut P,
302 aligned_buf: &mut [u8], 324 aligned_buf: &mut [u8],
303 ) -> Result<(), BootError> { 325 ) -> Result<(), BootError> {
304 if self.current_progress(p, aligned_buf)? <= progress_index { 326 if self.current_progress(aligned_buf)? <= progress_index {
305 let page_size = P::page_size() as u32; 327 let page_size = Self::PAGE_SIZE as u32;
306 328
307 self.dfu 329 self.dfu.erase(to_offset as u32, to_offset + page_size)?;
308 .erase_blocking(p.dfu(), to_offset as u32, to_offset + page_size)?;
309 330
310 for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { 331 for offset_in_page in (0..page_size).step_by(aligned_buf.len()) {
311 self.active 332 self.active.read(from_offset + offset_in_page as u32, aligned_buf)?;
312 .read_blocking(p.active(), from_offset + offset_in_page as u32, aligned_buf)?; 333 self.dfu.write(to_offset + offset_in_page as u32, aligned_buf)?;
313 self.dfu
314 .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?;
315 } 334 }
316 335
317 self.update_progress(progress_index, p, aligned_buf)?; 336 self.update_progress(progress_index, aligned_buf)?;
318 } 337 }
319 Ok(()) 338 Ok(())
320 } 339 }
321 340
322 fn swap<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { 341 fn swap(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> {
323 let page_size = P::page_size(); 342 let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE;
324 let page_count = self.active.size() / page_size;
325 for page_num in 0..page_count { 343 for page_num in 0..page_count {
326 let progress_index = (page_num * 2) as usize; 344 let progress_index = (page_num * 2) as usize;
327 345
328 // Copy active page to the 'next' DFU page. 346 // Copy active page to the 'next' DFU page.
329 let active_from_offset = (page_count - 1 - page_num) * page_size; 347 let active_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE;
330 let dfu_to_offset = (page_count - page_num) * page_size; 348 let dfu_to_offset = (page_count - page_num) * Self::PAGE_SIZE;
331 //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); 349 //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset);
332 self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; 350 self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?;
333 351
334 // Copy DFU page to the active page 352 // Copy DFU page to the active page
335 let active_to_offset = (page_count - 1 - page_num) * page_size; 353 let active_to_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE;
336 let dfu_from_offset = (page_count - 1 - page_num) * page_size; 354 let dfu_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE;
337 //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); 355 //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset);
338 self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; 356 self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?;
339 } 357 }
340 358
341 Ok(()) 359 Ok(())
342 } 360 }
343 361
344 fn revert<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { 362 fn revert(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> {
345 let page_size = P::page_size(); 363 let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE;
346 let page_count = self.active.size() / page_size;
347 for page_num in 0..page_count { 364 for page_num in 0..page_count {
348 let progress_index = (page_count * 2 + page_num * 2) as usize; 365 let progress_index = (page_count * 2 + page_num * 2) as usize;
349 366
350 // Copy the bad active page to the DFU page 367 // Copy the bad active page to the DFU page
351 let active_from_offset = page_num * page_size; 368 let active_from_offset = page_num * Self::PAGE_SIZE;
352 let dfu_to_offset = page_num * page_size; 369 let dfu_to_offset = page_num * Self::PAGE_SIZE;
353 self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; 370 self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?;
354 371
355 // Copy the DFU page back to the active page 372 // Copy the DFU page back to the active page
356 let active_to_offset = page_num * page_size; 373 let active_to_offset = page_num * Self::PAGE_SIZE;
357 let dfu_from_offset = (page_num + 1) * page_size; 374 let dfu_from_offset = (page_num + 1) * Self::PAGE_SIZE;
358 self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; 375 self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?;
359 } 376 }
360 377
361 Ok(()) 378 Ok(())
362 } 379 }
363 380
364 fn read_state<P: FlashConfig>(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result<State, BootError> { 381 fn read_state(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> {
365 let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; 382 let state_word = &mut aligned_buf[..STATE::WRITE_SIZE];
366 self.state.read_blocking(config.state(), 0, state_word)?; 383 self.state.read(0, state_word)?;
367 384
368 if !state_word.iter().any(|&b| b != SWAP_MAGIC) { 385 if !state_word.iter().any(|&b| b != SWAP_MAGIC) {
369 Ok(State::Swap) 386 Ok(State::Swap)
@@ -373,161 +390,32 @@ impl BootLoader {
373 } 390 }
374} 391}
375 392
376fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: u32, state_write_size: usize) { 393fn assert_partitions<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
377 assert_eq!(active.size() % page_size, 0); 394 active: &ACTIVE,
378 assert_eq!(dfu.size() % page_size, 0); 395 dfu: &DFU,
379 assert!(dfu.size() - active.size() >= page_size); 396 state: &STATE,
380 assert!(2 + 2 * (active.size() / page_size) <= state.size() / state_write_size as u32); 397 page_size: u32,
381} 398) {
382 399 assert_eq!(active.capacity() as u32 % page_size, 0);
383/// A flash wrapper implementing the Flash and embedded_storage traits. 400 assert_eq!(dfu.capacity() as u32 % page_size, 0);
384pub struct BootFlash<F> 401 assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size);
385where 402 assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32);
386 F: NorFlash,
387{
388 flash: F,
389}
390
391impl<F> BootFlash<F>
392where
393 F: NorFlash,
394{
395 /// Create a new instance of a bootable flash
396 pub fn new(flash: F) -> Self {
397 Self { flash }
398 }
399}
400
401impl<F> ErrorType for BootFlash<F>
402where
403 F: NorFlash,
404{
405 type Error = F::Error;
406}
407
408impl<F> NorFlash for BootFlash<F>
409where
410 F: NorFlash,
411{
412 const WRITE_SIZE: usize = F::WRITE_SIZE;
413 const ERASE_SIZE: usize = F::ERASE_SIZE;
414
415 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
416 F::erase(&mut self.flash, from, to)
417 }
418
419 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
420 F::write(&mut self.flash, offset, bytes)
421 }
422}
423
424impl<F> ReadNorFlash for BootFlash<F>
425where
426 F: NorFlash,
427{
428 const READ_SIZE: usize = F::READ_SIZE;
429
430 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
431 F::read(&mut self.flash, offset, bytes)
432 }
433
434 fn capacity(&self) -> usize {
435 F::capacity(&self.flash)
436 }
437}
438
439/// Convenience provider that uses a single flash for all partitions.
440pub struct SingleFlashConfig<'a, F>
441where
442 F: NorFlash,
443{
444 flash: &'a mut F,
445}
446
447impl<'a, F> SingleFlashConfig<'a, F>
448where
449 F: NorFlash,
450{
451 /// Create a provider for a single flash.
452 pub fn new(flash: &'a mut F) -> Self {
453 Self { flash }
454 }
455}
456
457impl<'a, F> FlashConfig for SingleFlashConfig<'a, F>
458where
459 F: NorFlash,
460{
461 type STATE = F;
462 type ACTIVE = F;
463 type DFU = F;
464
465 fn active(&mut self) -> &mut Self::STATE {
466 self.flash
467 }
468 fn dfu(&mut self) -> &mut Self::ACTIVE {
469 self.flash
470 }
471 fn state(&mut self) -> &mut Self::DFU {
472 self.flash
473 }
474}
475
476/// Convenience flash provider that uses separate flash instances for each partition.
477pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU>
478where
479 ACTIVE: NorFlash,
480 STATE: NorFlash,
481 DFU: NorFlash,
482{
483 active: &'a mut ACTIVE,
484 state: &'a mut STATE,
485 dfu: &'a mut DFU,
486}
487
488impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU>
489where
490 ACTIVE: NorFlash,
491 STATE: NorFlash,
492 DFU: NorFlash,
493{
494 /// Create a new flash provider with separate configuration for all three partitions.
495 pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self {
496 Self { active, state, dfu }
497 }
498}
499
500impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU>
501where
502 ACTIVE: NorFlash,
503 STATE: NorFlash,
504 DFU: NorFlash,
505{
506 type STATE = STATE;
507 type ACTIVE = ACTIVE;
508 type DFU = DFU;
509
510 fn active(&mut self) -> &mut Self::ACTIVE {
511 self.active
512 }
513 fn dfu(&mut self) -> &mut Self::DFU {
514 self.dfu
515 }
516 fn state(&mut self) -> &mut Self::STATE {
517 self.state
518 }
519} 403}
520 404
521#[cfg(test)] 405#[cfg(test)]
522mod tests { 406mod tests {
523 use super::*; 407 use super::*;
408 use crate::mem_flash::MemFlash;
524 409
525 #[test] 410 #[test]
526 #[should_panic] 411 #[should_panic]
527 fn test_range_asserts() { 412 fn test_range_asserts() {
528 const ACTIVE: Partition = Partition::new(4096, 4194304); 413 const ACTIVE_SIZE: usize = 4194304 - 4096;
529 const DFU: Partition = Partition::new(4194304, 2 * 4194304); 414 const DFU_SIZE: usize = 4194304;
530 const STATE: Partition = Partition::new(0, 4096); 415 const STATE_SIZE: usize = 4096;
531 assert_partitions(ACTIVE, DFU, STATE, 4096, 4); 416 static ACTIVE: MemFlash<ACTIVE_SIZE, 4, 4> = MemFlash::new(0xFF);
417 static DFU: MemFlash<DFU_SIZE, 4, 4> = MemFlash::new(0xFF);
418 static STATE: MemFlash<STATE_SIZE, 4, 4> = MemFlash::new(0xFF);
419 assert_partitions(&ACTIVE, &DFU, &STATE, 4096);
532 } 420 }
533} 421}
diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs
index bdd03bff4..0b3f88313 100644
--- a/embassy-boot/boot/src/firmware_updater/asynch.rs
+++ b/embassy-boot/boot/src/firmware_updater/asynch.rs
@@ -1,20 +1,68 @@
1use digest::Digest; 1use digest::Digest;
2use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; 2#[cfg(target_os = "none")]
3use embassy_embedded_hal::flash::partition::Partition;
4#[cfg(target_os = "none")]
5use embassy_sync::blocking_mutex::raw::NoopRawMutex;
6use embedded_storage_async::nor_flash::NorFlash;
7
8use super::FirmwareUpdaterConfig;
9use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
10
11/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
12/// 'mess up' the internal bootloader state
13pub struct FirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
14 dfu: DFU,
15 state: STATE,
16}
17
18#[cfg(target_os = "none")]
19impl<'a, FLASH: NorFlash>
20 FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, FLASH>, Partition<'a, NoopRawMutex, FLASH>>
21{
22 /// Create a firmware updater config from the flash and address symbols defined in the linkerfile
23 pub fn from_linkerfile(flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, FLASH>) -> Self {
24 extern "C" {
25 static __bootloader_state_start: u32;
26 static __bootloader_state_end: u32;
27 static __bootloader_dfu_start: u32;
28 static __bootloader_dfu_end: u32;
29 }
30
31 let dfu = unsafe {
32 let start = &__bootloader_dfu_start as *const u32 as u32;
33 let end = &__bootloader_dfu_end as *const u32 as u32;
34 trace!("DFU: 0x{:x} - 0x{:x}", start, end);
35
36 Partition::new(flash, start, end - start)
37 };
38 let state = unsafe {
39 let start = &__bootloader_state_start as *const u32 as u32;
40 let end = &__bootloader_state_end as *const u32 as u32;
41 trace!("STATE: 0x{:x} - 0x{:x}", start, end);
42
43 Partition::new(flash, start, end - start)
44 };
3 45
4use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; 46 Self { dfu, state }
47 }
48}
49
50impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
51 /// Create a firmware updater instance with partition ranges for the update and state partitions.
52 pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self {
53 Self {
54 dfu: config.dfu,
55 state: config.state,
56 }
57 }
5 58
6impl FirmwareUpdater {
7 /// Obtain the current state. 59 /// Obtain the current state.
8 /// 60 ///
9 /// This is useful to check if the bootloader has just done a swap, in order 61 /// This is useful to check if the bootloader has just done a swap, in order
10 /// to do verifications and self-tests of the new image before calling 62 /// to do verifications and self-tests of the new image before calling
11 /// `mark_booted`. 63 /// `mark_booted`.
12 pub async fn get_state<F: AsyncNorFlash>( 64 pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
13 &mut self, 65 self.state.read(0, aligned).await?;
14 state_flash: &mut F,
15 aligned: &mut [u8],
16 ) -> Result<State, FirmwareUpdaterError> {
17 self.state.read(state_flash, 0, aligned).await?;
18 66
19 if !aligned.iter().any(|&b| b != SWAP_MAGIC) { 67 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
20 Ok(State::Swap) 68 Ok(State::Swap)
@@ -37,19 +85,18 @@ impl FirmwareUpdater {
37 /// 85 ///
38 /// # Safety 86 /// # Safety
39 /// 87 ///
40 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from 88 /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
41 /// and written to. 89 /// and written to.
42 #[cfg(all(feature = "_verify", feature = "nightly"))] 90 #[cfg(feature = "_verify")]
43 pub async fn verify_and_mark_updated<F: AsyncNorFlash>( 91 pub async fn verify_and_mark_updated(
44 &mut self, 92 &mut self,
45 _state_and_dfu_flash: &mut F,
46 _public_key: &[u8], 93 _public_key: &[u8],
47 _signature: &[u8], 94 _signature: &[u8],
48 _update_len: u32, 95 _update_len: u32,
49 _aligned: &mut [u8], 96 _aligned: &mut [u8],
50 ) -> Result<(), FirmwareUpdaterError> { 97 ) -> Result<(), FirmwareUpdaterError> {
51 assert_eq!(_aligned.len(), F::WRITE_SIZE); 98 assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
52 assert!(_update_len <= self.dfu.size()); 99 assert!(_update_len <= self.dfu.capacity() as u32);
53 100
54 #[cfg(feature = "ed25519-dalek")] 101 #[cfg(feature = "ed25519-dalek")]
55 { 102 {
@@ -63,8 +110,7 @@ impl FirmwareUpdater {
63 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; 110 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
64 111
65 let mut message = [0; 64]; 112 let mut message = [0; 64];
66 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) 113 self.hash::<Sha512>(_update_len, _aligned, &mut message).await?;
67 .await?;
68 114
69 public_key.verify(&message, &signature).map_err(into_signature_error)? 115 public_key.verify(&message, &signature).map_err(into_signature_error)?
70 } 116 }
@@ -85,8 +131,7 @@ impl FirmwareUpdater {
85 let signature = Signature::try_from(&signature).map_err(into_signature_error)?; 131 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
86 132
87 let mut message = [0; 64]; 133 let mut message = [0; 64];
88 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) 134 self.hash::<Sha512>(_update_len, _aligned, &mut message).await?;
89 .await?;
90 135
91 let r = public_key.verify(&message, &signature); 136 let r = public_key.verify(&message, &signature);
92 trace!( 137 trace!(
@@ -99,20 +144,19 @@ impl FirmwareUpdater {
99 r.map_err(into_signature_error)? 144 r.map_err(into_signature_error)?
100 } 145 }
101 146
102 self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await 147 self.set_magic(_aligned, SWAP_MAGIC).await
103 } 148 }
104 149
105 /// Verify the update in DFU with any digest. 150 /// Verify the update in DFU with any digest.
106 pub async fn hash<F: AsyncNorFlash, D: Digest>( 151 pub async fn hash<D: Digest>(
107 &mut self, 152 &mut self,
108 dfu_flash: &mut F,
109 update_len: u32, 153 update_len: u32,
110 chunk_buf: &mut [u8], 154 chunk_buf: &mut [u8],
111 output: &mut [u8], 155 output: &mut [u8],
112 ) -> Result<(), FirmwareUpdaterError> { 156 ) -> Result<(), FirmwareUpdaterError> {
113 let mut digest = D::new(); 157 let mut digest = D::new();
114 for offset in (0..update_len).step_by(chunk_buf.len()) { 158 for offset in (0..update_len).step_by(chunk_buf.len()) {
115 self.dfu.read(dfu_flash, offset, chunk_buf).await?; 159 self.dfu.read(offset, chunk_buf).await?;
116 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); 160 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
117 digest.update(&chunk_buf[..len]); 161 digest.update(&chunk_buf[..len]);
118 } 162 }
@@ -124,60 +168,44 @@ impl FirmwareUpdater {
124 /// 168 ///
125 /// # Safety 169 /// # Safety
126 /// 170 ///
127 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. 171 /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
128 #[cfg(all(feature = "nightly", not(feature = "_verify")))] 172 #[cfg(not(feature = "_verify"))]
129 pub async fn mark_updated<F: AsyncNorFlash>( 173 pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
130 &mut self, 174 assert_eq!(aligned.len(), STATE::WRITE_SIZE);
131 state_flash: &mut F, 175 self.set_magic(aligned, SWAP_MAGIC).await
132 aligned: &mut [u8],
133 ) -> Result<(), FirmwareUpdaterError> {
134 assert_eq!(aligned.len(), F::WRITE_SIZE);
135 self.set_magic(aligned, SWAP_MAGIC, state_flash).await
136 } 176 }
137 177
138 /// Mark firmware boot successful and stop rollback on reset. 178 /// Mark firmware boot successful and stop rollback on reset.
139 /// 179 ///
140 /// # Safety 180 /// # Safety
141 /// 181 ///
142 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. 182 /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
143 pub async fn mark_booted<F: AsyncNorFlash>( 183 pub async fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
144 &mut self, 184 assert_eq!(aligned.len(), STATE::WRITE_SIZE);
145 state_flash: &mut F, 185 self.set_magic(aligned, BOOT_MAGIC).await
146 aligned: &mut [u8],
147 ) -> Result<(), FirmwareUpdaterError> {
148 assert_eq!(aligned.len(), F::WRITE_SIZE);
149 self.set_magic(aligned, BOOT_MAGIC, state_flash).await
150 } 186 }
151 187
152 async fn set_magic<F: AsyncNorFlash>( 188 async fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> {
153 &mut self, 189 self.state.read(0, aligned).await?;
154 aligned: &mut [u8],
155 magic: u8,
156 state_flash: &mut F,
157 ) -> Result<(), FirmwareUpdaterError> {
158 self.state.read(state_flash, 0, aligned).await?;
159 190
160 if aligned.iter().any(|&b| b != magic) { 191 if aligned.iter().any(|&b| b != magic) {
161 // Read progress validity 192 // Read progress validity
162 self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; 193 self.state.read(STATE::WRITE_SIZE as u32, aligned).await?;
163
164 // FIXME: Do not make this assumption.
165 const STATE_ERASE_VALUE: u8 = 0xFF;
166 194
167 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { 195 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
168 // The current progress validity marker is invalid 196 // The current progress validity marker is invalid
169 } else { 197 } else {
170 // Invalidate progress 198 // Invalidate progress
171 aligned.fill(!STATE_ERASE_VALUE); 199 aligned.fill(!STATE_ERASE_VALUE);
172 self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; 200 self.state.write(STATE::WRITE_SIZE as u32, aligned).await?;
173 } 201 }
174 202
175 // Clear magic and progress 203 // Clear magic and progress
176 self.state.wipe(state_flash).await?; 204 self.state.erase(0, self.state.capacity() as u32).await?;
177 205
178 // Set magic 206 // Set magic
179 aligned.fill(magic); 207 aligned.fill(magic);
180 self.state.write(state_flash, 0, aligned).await?; 208 self.state.write(0, aligned).await?;
181 } 209 }
182 Ok(()) 210 Ok(())
183 } 211 }
@@ -189,19 +217,12 @@ impl FirmwareUpdater {
189 /// # Safety 217 /// # Safety
190 /// 218 ///
191 /// Failing to meet alignment and size requirements may result in a panic. 219 /// Failing to meet alignment and size requirements may result in a panic.
192 pub async fn write_firmware<F: AsyncNorFlash>( 220 pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
193 &mut self, 221 assert!(data.len() >= DFU::ERASE_SIZE);
194 offset: usize,
195 data: &[u8],
196 dfu_flash: &mut F,
197 ) -> Result<(), FirmwareUpdaterError> {
198 assert!(data.len() >= F::ERASE_SIZE);
199 222
200 self.dfu 223 self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?;
201 .erase(dfu_flash, offset as u32, (offset + data.len()) as u32)
202 .await?;
203 224
204 self.dfu.write(dfu_flash, offset as u32, data).await?; 225 self.dfu.write(offset as u32, data).await?;
205 226
206 Ok(()) 227 Ok(())
207 } 228 }
@@ -211,18 +232,18 @@ impl FirmwareUpdater {
211 /// 232 ///
212 /// Using this instead of `write_firmware` allows for an optimized API in 233 /// Using this instead of `write_firmware` allows for an optimized API in
213 /// exchange for added complexity. 234 /// exchange for added complexity.
214 pub async fn prepare_update<F: AsyncNorFlash>( 235 pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
215 &mut self, 236 self.dfu.erase(0, self.dfu.capacity() as u32).await?;
216 dfu_flash: &mut F,
217 ) -> Result<Partition, FirmwareUpdaterError> {
218 self.dfu.wipe(dfu_flash).await?;
219 237
220 Ok(self.dfu) 238 Ok(&mut self.dfu)
221 } 239 }
222} 240}
223 241
224#[cfg(test)] 242#[cfg(test)]
225mod tests { 243mod tests {
244 use embassy_embedded_hal::flash::partition::Partition;
245 use embassy_sync::blocking_mutex::raw::NoopRawMutex;
246 use embassy_sync::mutex::Mutex;
226 use futures::executor::block_on; 247 use futures::executor::block_on;
227 use sha1::{Digest, Sha1}; 248 use sha1::{Digest, Sha1};
228 249
@@ -231,20 +252,19 @@ mod tests {
231 252
232 #[test] 253 #[test]
233 fn can_verify_sha1() { 254 fn can_verify_sha1() {
234 const STATE: Partition = Partition::new(0, 4096); 255 let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default());
235 const DFU: Partition = Partition::new(65536, 131072); 256 let state = Partition::new(&flash, 0, 4096);
236 257 let dfu = Partition::new(&flash, 65536, 65536);
237 let mut flash = MemFlash::<131072, 4096, 8>::default();
238 258
239 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; 259 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
240 let mut to_write = [0; 4096]; 260 let mut to_write = [0; 4096];
241 to_write[..7].copy_from_slice(update.as_slice()); 261 to_write[..7].copy_from_slice(update.as_slice());
242 262
243 let mut updater = FirmwareUpdater::new(DFU, STATE); 263 let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
244 block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); 264 block_on(updater.write_firmware(0, to_write.as_slice())).unwrap();
245 let mut chunk_buf = [0; 2]; 265 let mut chunk_buf = [0; 2];
246 let mut hash = [0; 20]; 266 let mut hash = [0; 20];
247 block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); 267 block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
248 268
249 assert_eq!(Sha1::digest(update).as_slice(), hash); 269 assert_eq!(Sha1::digest(update).as_slice(), hash);
250 } 270 }
diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs
index 50caaf08c..551150c4f 100644
--- a/embassy-boot/boot/src/firmware_updater/blocking.rs
+++ b/embassy-boot/boot/src/firmware_updater/blocking.rs
@@ -1,25 +1,70 @@
1use digest::Digest; 1use digest::Digest;
2#[cfg(target_os = "none")]
3use embassy_embedded_hal::flash::partition::BlockingPartition;
4#[cfg(target_os = "none")]
5use embassy_sync::blocking_mutex::raw::NoopRawMutex;
2use embedded_storage::nor_flash::NorFlash; 6use embedded_storage::nor_flash::NorFlash;
3 7
4use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; 8use super::FirmwareUpdaterConfig;
9use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
10
11/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
12/// 'mess up' the internal bootloader state
13pub struct BlockingFirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
14 dfu: DFU,
15 state: STATE,
16}
17
18#[cfg(target_os = "none")]
19impl<'a, FLASH: NorFlash>
20 FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, FLASH>, BlockingPartition<'a, NoopRawMutex, FLASH>>
21{
22 /// Create a firmware updater config from the flash and address symbols defined in the linkerfile
23 pub fn from_linkerfile_blocking(
24 flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<FLASH>>,
25 ) -> Self {
26 extern "C" {
27 static __bootloader_state_start: u32;
28 static __bootloader_state_end: u32;
29 static __bootloader_dfu_start: u32;
30 static __bootloader_dfu_end: u32;
31 }
32
33 let dfu = unsafe {
34 let start = &__bootloader_dfu_start as *const u32 as u32;
35 let end = &__bootloader_dfu_end as *const u32 as u32;
36 trace!("DFU: 0x{:x} - 0x{:x}", start, end);
37
38 BlockingPartition::new(flash, start, end - start)
39 };
40 let state = unsafe {
41 let start = &__bootloader_state_start as *const u32 as u32;
42 let end = &__bootloader_state_end as *const u32 as u32;
43 trace!("STATE: 0x{:x} - 0x{:x}", start, end);
44
45 BlockingPartition::new(flash, start, end - start)
46 };
5 47
6impl FirmwareUpdater {
7 /// Create a firmware updater instance with partition ranges for the update and state partitions.
8 pub const fn new(dfu: Partition, state: Partition) -> Self {
9 Self { dfu, state } 48 Self { dfu, state }
10 } 49 }
50}
51
52impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
53 /// Create a firmware updater instance with partition ranges for the update and state partitions.
54 pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self {
55 Self {
56 dfu: config.dfu,
57 state: config.state,
58 }
59 }
11 60
12 /// Obtain the current state. 61 /// Obtain the current state.
13 /// 62 ///
14 /// This is useful to check if the bootloader has just done a swap, in order 63 /// This is useful to check if the bootloader has just done a swap, in order
15 /// to do verifications and self-tests of the new image before calling 64 /// to do verifications and self-tests of the new image before calling
16 /// `mark_booted`. 65 /// `mark_booted`.
17 pub fn get_state_blocking<F: NorFlash>( 66 pub fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
18 &mut self, 67 self.state.read(0, aligned)?;
19 state_flash: &mut F,
20 aligned: &mut [u8],
21 ) -> Result<State, FirmwareUpdaterError> {
22 self.state.read_blocking(state_flash, 0, aligned)?;
23 68
24 if !aligned.iter().any(|&b| b != SWAP_MAGIC) { 69 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
25 Ok(State::Swap) 70 Ok(State::Swap)
@@ -42,19 +87,18 @@ impl FirmwareUpdater {
42 /// 87 ///
43 /// # Safety 88 /// # Safety
44 /// 89 ///
45 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from 90 /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
46 /// and written to. 91 /// and written to.
47 #[cfg(feature = "_verify")] 92 #[cfg(feature = "_verify")]
48 pub fn verify_and_mark_updated_blocking<F: NorFlash>( 93 pub fn verify_and_mark_updated(
49 &mut self, 94 &mut self,
50 _state_and_dfu_flash: &mut F,
51 _public_key: &[u8], 95 _public_key: &[u8],
52 _signature: &[u8], 96 _signature: &[u8],
53 _update_len: u32, 97 _update_len: u32,
54 _aligned: &mut [u8], 98 _aligned: &mut [u8],
55 ) -> Result<(), FirmwareUpdaterError> { 99 ) -> Result<(), FirmwareUpdaterError> {
56 assert_eq!(_aligned.len(), F::WRITE_SIZE); 100 assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
57 assert!(_update_len <= self.dfu.size()); 101 assert!(_update_len <= self.dfu.capacity() as u32);
58 102
59 #[cfg(feature = "ed25519-dalek")] 103 #[cfg(feature = "ed25519-dalek")]
60 { 104 {
@@ -68,7 +112,7 @@ impl FirmwareUpdater {
68 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; 112 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
69 113
70 let mut message = [0; 64]; 114 let mut message = [0; 64];
71 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; 115 self.hash::<Sha512>(_update_len, _aligned, &mut message)?;
72 116
73 public_key.verify(&message, &signature).map_err(into_signature_error)? 117 public_key.verify(&message, &signature).map_err(into_signature_error)?
74 } 118 }
@@ -89,7 +133,7 @@ impl FirmwareUpdater {
89 let signature = Signature::try_from(&signature).map_err(into_signature_error)?; 133 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
90 134
91 let mut message = [0; 64]; 135 let mut message = [0; 64];
92 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; 136 self.hash::<Sha512>(_update_len, _aligned, &mut message)?;
93 137
94 let r = public_key.verify(&message, &signature); 138 let r = public_key.verify(&message, &signature);
95 trace!( 139 trace!(
@@ -102,20 +146,19 @@ impl FirmwareUpdater {
102 r.map_err(into_signature_error)? 146 r.map_err(into_signature_error)?
103 } 147 }
104 148
105 self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) 149 self.set_magic(_aligned, SWAP_MAGIC)
106 } 150 }
107 151
108 /// Verify the update in DFU with any digest. 152 /// Verify the update in DFU with any digest.
109 pub fn hash_blocking<F: NorFlash, D: Digest>( 153 pub fn hash<D: Digest>(
110 &mut self, 154 &mut self,
111 dfu_flash: &mut F,
112 update_len: u32, 155 update_len: u32,
113 chunk_buf: &mut [u8], 156 chunk_buf: &mut [u8],
114 output: &mut [u8], 157 output: &mut [u8],
115 ) -> Result<(), FirmwareUpdaterError> { 158 ) -> Result<(), FirmwareUpdaterError> {
116 let mut digest = D::new(); 159 let mut digest = D::new();
117 for offset in (0..update_len).step_by(chunk_buf.len()) { 160 for offset in (0..update_len).step_by(chunk_buf.len()) {
118 self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; 161 self.dfu.read(offset, chunk_buf)?;
119 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); 162 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
120 digest.update(&chunk_buf[..len]); 163 digest.update(&chunk_buf[..len]);
121 } 164 }
@@ -127,60 +170,44 @@ impl FirmwareUpdater {
127 /// 170 ///
128 /// # Safety 171 /// # Safety
129 /// 172 ///
130 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. 173 /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
131 #[cfg(not(feature = "_verify"))] 174 #[cfg(not(feature = "_verify"))]
132 pub fn mark_updated_blocking<F: NorFlash>( 175 pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
133 &mut self, 176 assert_eq!(aligned.len(), STATE::WRITE_SIZE);
134 state_flash: &mut F, 177 self.set_magic(aligned, SWAP_MAGIC)
135 aligned: &mut [u8],
136 ) -> Result<(), FirmwareUpdaterError> {
137 assert_eq!(aligned.len(), F::WRITE_SIZE);
138 self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash)
139 } 178 }
140 179
141 /// Mark firmware boot successful and stop rollback on reset. 180 /// Mark firmware boot successful and stop rollback on reset.
142 /// 181 ///
143 /// # Safety 182 /// # Safety
144 /// 183 ///
145 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. 184 /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
146 pub fn mark_booted_blocking<F: NorFlash>( 185 pub fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
147 &mut self, 186 assert_eq!(aligned.len(), STATE::WRITE_SIZE);
148 state_flash: &mut F, 187 self.set_magic(aligned, BOOT_MAGIC)
149 aligned: &mut [u8],
150 ) -> Result<(), FirmwareUpdaterError> {
151 assert_eq!(aligned.len(), F::WRITE_SIZE);
152 self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash)
153 } 188 }
154 189
155 fn set_magic_blocking<F: NorFlash>( 190 fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> {
156 &mut self, 191 self.state.read(0, aligned)?;
157 aligned: &mut [u8],
158 magic: u8,
159 state_flash: &mut F,
160 ) -> Result<(), FirmwareUpdaterError> {
161 self.state.read_blocking(state_flash, 0, aligned)?;
162 192
163 if aligned.iter().any(|&b| b != magic) { 193 if aligned.iter().any(|&b| b != magic) {
164 // Read progress validity 194 // Read progress validity
165 self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; 195 self.state.read(STATE::WRITE_SIZE as u32, aligned)?;
166
167 // FIXME: Do not make this assumption.
168 const STATE_ERASE_VALUE: u8 = 0xFF;
169 196
170 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { 197 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
171 // The current progress validity marker is invalid 198 // The current progress validity marker is invalid
172 } else { 199 } else {
173 // Invalidate progress 200 // Invalidate progress
174 aligned.fill(!STATE_ERASE_VALUE); 201 aligned.fill(!STATE_ERASE_VALUE);
175 self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; 202 self.state.write(STATE::WRITE_SIZE as u32, aligned)?;
176 } 203 }
177 204
178 // Clear magic and progress 205 // Clear magic and progress
179 self.state.wipe_blocking(state_flash)?; 206 self.state.erase(0, self.state.capacity() as u32)?;
180 207
181 // Set magic 208 // Set magic
182 aligned.fill(magic); 209 aligned.fill(magic);
183 self.state.write_blocking(state_flash, 0, aligned)?; 210 self.state.write(0, aligned)?;
184 } 211 }
185 Ok(()) 212 Ok(())
186 } 213 }
@@ -192,18 +219,12 @@ impl FirmwareUpdater {
192 /// # Safety 219 /// # Safety
193 /// 220 ///
194 /// Failing to meet alignment and size requirements may result in a panic. 221 /// Failing to meet alignment and size requirements may result in a panic.
195 pub fn write_firmware_blocking<F: NorFlash>( 222 pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
196 &mut self, 223 assert!(data.len() >= DFU::ERASE_SIZE);
197 offset: usize,
198 data: &[u8],
199 dfu_flash: &mut F,
200 ) -> Result<(), FirmwareUpdaterError> {
201 assert!(data.len() >= F::ERASE_SIZE);
202 224
203 self.dfu 225 self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
204 .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?;
205 226
206 self.dfu.write_blocking(dfu_flash, offset as u32, data)?; 227 self.dfu.write(offset as u32, data)?;
207 228
208 Ok(()) 229 Ok(())
209 } 230 }
@@ -211,11 +232,45 @@ impl FirmwareUpdater {
211 /// Prepare for an incoming DFU update by erasing the entire DFU area and 232 /// Prepare for an incoming DFU update by erasing the entire DFU area and
212 /// returning its `Partition`. 233 /// returning its `Partition`.
213 /// 234 ///
214 /// Using this instead of `write_firmware_blocking` allows for an optimized 235 /// Using this instead of `write_firmware` allows for an optimized API in
215 /// API in exchange for added complexity. 236 /// exchange for added complexity.
216 pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<Partition, FirmwareUpdaterError> { 237 pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
217 self.dfu.wipe_blocking(flash)?; 238 self.dfu.erase(0, self.dfu.capacity() as u32)?;
239
240 Ok(&mut self.dfu)
241 }
242}
218 243
219 Ok(self.dfu) 244#[cfg(test)]
245mod tests {
246 use core::cell::RefCell;
247
248 use embassy_embedded_hal::flash::partition::BlockingPartition;
249 use embassy_sync::blocking_mutex::raw::NoopRawMutex;
250 use embassy_sync::blocking_mutex::Mutex;
251 use sha1::{Digest, Sha1};
252
253 use super::*;
254 use crate::mem_flash::MemFlash;
255
256 #[test]
257 fn can_verify_sha1() {
258 let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
259 let state = BlockingPartition::new(&flash, 0, 4096);
260 let dfu = BlockingPartition::new(&flash, 65536, 65536);
261
262 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
263 let mut to_write = [0; 4096];
264 to_write[..7].copy_from_slice(update.as_slice());
265
266 let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
267 updater.write_firmware(0, to_write.as_slice()).unwrap();
268 let mut chunk_buf = [0; 2];
269 let mut hash = [0; 20];
270 updater
271 .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
272 .unwrap();
273
274 assert_eq!(Sha1::digest(update).as_slice(), hash);
220 } 275 }
221} 276}
diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs
index e09f5eb6c..a37984a3a 100644
--- a/embassy-boot/boot/src/firmware_updater/mod.rs
+++ b/embassy-boot/boot/src/firmware_updater/mod.rs
@@ -2,9 +2,22 @@
2mod asynch; 2mod asynch;
3mod blocking; 3mod blocking;
4 4
5#[cfg(feature = "nightly")]
6pub use asynch::FirmwareUpdater;
7pub use blocking::BlockingFirmwareUpdater;
5use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; 8use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
6 9
7use crate::Partition; 10/// Firmware updater flash configuration holding the two flashes used by the updater
11///
12/// If only a single flash is actually used, then that flash should be partitioned into two partitions before use.
13/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition
14/// the provided flash according to symbols defined in the linkerfile.
15pub struct FirmwareUpdaterConfig<DFU, STATE> {
16 /// The dfu flash partition
17 pub dfu: DFU,
18 /// The state flash partition
19 pub state: STATE,
20}
8 21
9/// Errors returned by FirmwareUpdater 22/// Errors returned by FirmwareUpdater
10#[derive(Debug)] 23#[derive(Debug)]
@@ -33,39 +46,3 @@ where
33 FirmwareUpdaterError::Flash(error.kind()) 46 FirmwareUpdaterError::Flash(error.kind())
34 } 47 }
35} 48}
36
37/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
38/// 'mess up' the internal bootloader state
39pub struct FirmwareUpdater {
40 state: Partition,
41 dfu: Partition,
42}
43
44#[cfg(target_os = "none")]
45impl Default for FirmwareUpdater {
46 fn default() -> Self {
47 extern "C" {
48 static __bootloader_state_start: u32;
49 static __bootloader_state_end: u32;
50 static __bootloader_dfu_start: u32;
51 static __bootloader_dfu_end: u32;
52 }
53
54 let dfu = unsafe {
55 Partition::new(
56 &__bootloader_dfu_start as *const u32 as u32,
57 &__bootloader_dfu_end as *const u32 as u32,
58 )
59 };
60 let state = unsafe {
61 Partition::new(
62 &__bootloader_state_start as *const u32 as u32,
63 &__bootloader_state_end as *const u32 as u32,
64 )
65 };
66
67 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
68 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
69 FirmwareUpdater::new(dfu, state)
70 }
71}
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 4521fecb0..45a87bd0e 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -7,12 +7,18 @@ mod fmt;
7mod boot_loader; 7mod boot_loader;
8mod digest_adapters; 8mod digest_adapters;
9mod firmware_updater; 9mod firmware_updater;
10#[cfg(test)]
10mod mem_flash; 11mod mem_flash;
11mod partition; 12#[cfg(test)]
13mod test_flash;
12 14
13pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig}; 15// The expected value of the flash after an erase
14pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; 16// TODO: Use the value provided by NorFlash when available
15pub use partition::Partition; 17pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
18pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
19#[cfg(feature = "nightly")]
20pub use firmware_updater::FirmwareUpdater;
21pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError};
16 22
17pub(crate) const BOOT_MAGIC: u8 = 0xD0; 23pub(crate) const BOOT_MAGIC: u8 = 0xD0;
18pub(crate) const SWAP_MAGIC: u8 = 0xF0; 24pub(crate) const SWAP_MAGIC: u8 = 0xF0;
@@ -45,10 +51,18 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
45 51
46#[cfg(test)] 52#[cfg(test)]
47mod tests { 53mod tests {
54 use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
55 #[cfg(feature = "nightly")]
56 use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
48 use futures::executor::block_on; 57 use futures::executor::block_on;
49 58
50 use super::*; 59 use super::*;
60 use crate::boot_loader::BootLoaderConfig;
61 use crate::firmware_updater::FirmwareUpdaterConfig;
51 use crate::mem_flash::MemFlash; 62 use crate::mem_flash::MemFlash;
63 #[cfg(feature = "nightly")]
64 use crate::test_flash::AsyncTestFlash;
65 use crate::test_flash::BlockingTestFlash;
52 66
53 /* 67 /*
54 #[test] 68 #[test]
@@ -67,147 +81,173 @@ mod tests {
67 81
68 #[test] 82 #[test]
69 fn test_boot_state() { 83 fn test_boot_state() {
70 const STATE: Partition = Partition::new(0, 4096); 84 let flash = BlockingTestFlash::new(BootLoaderConfig {
71 const ACTIVE: Partition = Partition::new(4096, 61440); 85 active: MemFlash::<57344, 4096, 4>::default(),
72 const DFU: Partition = Partition::new(61440, 122880); 86 dfu: MemFlash::<61440, 4096, 4>::default(),
87 state: MemFlash::<4096, 4096, 4>::default(),
88 });
73 89
74 let mut flash = MemFlash::<131072, 4096, 4>::default(); 90 flash.state().write(0, &[BOOT_MAGIC; 4]).unwrap();
75 flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
76 let mut flash = SingleFlashConfig::new(&mut flash);
77 91
78 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 92 let mut bootloader = BootLoader::new(BootLoaderConfig {
93 active: flash.active(),
94 dfu: flash.dfu(),
95 state: flash.state(),
96 });
79 97
80 let mut page = [0; 4096]; 98 let mut page = [0; 4096];
81 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash, &mut page).unwrap()); 99 assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap());
82 } 100 }
83 101
84 #[test] 102 #[test]
85 #[cfg(all(feature = "nightly", not(feature = "_verify")))] 103 #[cfg(all(feature = "nightly", not(feature = "_verify")))]
86 fn test_swap_state() { 104 fn test_swap_state() {
87 const STATE: Partition = Partition::new(0, 4096); 105 const FIRMWARE_SIZE: usize = 57344;
88 const ACTIVE: Partition = Partition::new(4096, 61440); 106 let flash = AsyncTestFlash::new(BootLoaderConfig {
89 const DFU: Partition = Partition::new(61440, 122880); 107 active: MemFlash::<FIRMWARE_SIZE, 4096, 4>::default(),
90 let mut flash = MemFlash::<131072, 4096, 4>::random(); 108 dfu: MemFlash::<61440, 4096, 4>::default(),
91 109 state: MemFlash::<4096, 4096, 4>::default(),
92 let original = [rand::random::<u8>(); ACTIVE.size() as usize]; 110 });
93 let update = [rand::random::<u8>(); ACTIVE.size() as usize]; 111
112 const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];
113 const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];
94 let mut aligned = [0; 4]; 114 let mut aligned = [0; 4];
95 115
96 flash.program(ACTIVE.from, &original).unwrap(); 116 block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
117 block_on(flash.active().write(0, &ORIGINAL)).unwrap();
118
119 let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
120 dfu: flash.dfu(),
121 state: flash.state(),
122 });
123 block_on(updater.write_firmware(0, &UPDATE)).unwrap();
124 block_on(updater.mark_updated(&mut aligned)).unwrap();
97 125
98 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 126 let flash = flash.into_blocking();
99 let mut updater = FirmwareUpdater::new(DFU, STATE); 127 let mut bootloader = BootLoader::new(BootLoaderConfig {
100 block_on(updater.write_firmware(0, &update, &mut flash)).unwrap(); 128 active: flash.active(),
101 block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); 129 dfu: flash.dfu(),
130 state: flash.state(),
131 });
102 132
103 let mut page = [0; 1024]; 133 let mut page = [0; 1024];
104 assert_eq!( 134 assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
105 State::Swap,
106 bootloader
107 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page)
108 .unwrap()
109 );
110 135
111 flash.assert_eq(ACTIVE.from, &update); 136 let mut read_buf = [0; FIRMWARE_SIZE];
137 flash.active().read(0, &mut read_buf).unwrap();
138 assert_eq!(UPDATE, read_buf);
112 // First DFU page is untouched 139 // First DFU page is untouched
113 flash.assert_eq(DFU.from + 4096, &original); 140 flash.dfu().read(4096, &mut read_buf).unwrap();
141 assert_eq!(ORIGINAL, read_buf);
114 142
115 // Running again should cause a revert 143 // Running again should cause a revert
116 assert_eq!( 144 assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
117 State::Swap,
118 bootloader
119 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page)
120 .unwrap()
121 );
122 145
123 flash.assert_eq(ACTIVE.from, &original); 146 let mut read_buf = [0; FIRMWARE_SIZE];
124 // Last page is untouched 147 flash.active().read(0, &mut read_buf).unwrap();
125 flash.assert_eq(DFU.from, &update); 148 assert_eq!(ORIGINAL, read_buf);
149 // Last DFU page is untouched
150 flash.dfu().read(0, &mut read_buf).unwrap();
151 assert_eq!(UPDATE, read_buf);
126 152
127 // Mark as booted 153 // Mark as booted
128 block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap(); 154 let flash = flash.into_async();
129 assert_eq!( 155 let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
130 State::Boot, 156 dfu: flash.dfu(),
131 bootloader 157 state: flash.state(),
132 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) 158 });
133 .unwrap() 159 block_on(updater.mark_booted(&mut aligned)).unwrap();
134 ); 160
161 let flash = flash.into_blocking();
162 let mut bootloader = BootLoader::new(BootLoaderConfig {
163 active: flash.active(),
164 dfu: flash.dfu(),
165 state: flash.state(),
166 });
167 assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap());
135 } 168 }
136 169
137 #[test] 170 #[test]
138 #[cfg(all(feature = "nightly", not(feature = "_verify")))] 171 #[cfg(all(feature = "nightly", not(feature = "_verify")))]
139 fn test_separate_flash_active_page_biggest() { 172 fn test_swap_state_active_page_biggest() {
140 const STATE: Partition = Partition::new(2048, 4096); 173 const FIRMWARE_SIZE: usize = 12288;
141 const ACTIVE: Partition = Partition::new(4096, 16384); 174 let flash = AsyncTestFlash::new(BootLoaderConfig {
142 const DFU: Partition = Partition::new(0, 16384); 175 active: MemFlash::<12288, 4096, 8>::random(),
143 176 dfu: MemFlash::<16384, 2048, 8>::random(),
144 let mut active = MemFlash::<16384, 4096, 8>::random(); 177 state: MemFlash::<2048, 128, 4>::random(),
145 let mut dfu = MemFlash::<16384, 2048, 8>::random(); 178 });
146 let mut state = MemFlash::<4096, 128, 4>::random(); 179
180 const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];
181 const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];
147 let mut aligned = [0; 4]; 182 let mut aligned = [0; 4];
148 183
149 let original = [rand::random::<u8>(); ACTIVE.size() as usize]; 184 block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
150 let update = [rand::random::<u8>(); ACTIVE.size() as usize]; 185 block_on(flash.active().write(0, &ORIGINAL)).unwrap();
151
152 active.program(ACTIVE.from, &original).unwrap();
153 186
154 let mut updater = FirmwareUpdater::new(DFU, STATE); 187 let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
188 dfu: flash.dfu(),
189 state: flash.state(),
190 });
191 block_on(updater.write_firmware(0, &UPDATE)).unwrap();
192 block_on(updater.mark_updated(&mut aligned)).unwrap();
155 193
156 block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); 194 let flash = flash.into_blocking();
157 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); 195 let mut bootloader = BootLoader::new(BootLoaderConfig {
196 active: flash.active(),
197 dfu: flash.dfu(),
198 state: flash.state(),
199 });
158 200
159 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
160 let mut page = [0; 4096]; 201 let mut page = [0; 4096];
202 assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
161 203
162 assert_eq!( 204 let mut read_buf = [0; FIRMWARE_SIZE];
163 State::Swap, 205 flash.active().read(0, &mut read_buf).unwrap();
164 bootloader 206 assert_eq!(UPDATE, read_buf);
165 .prepare_boot(&mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), &mut page)
166 .unwrap()
167 );
168
169 active.assert_eq(ACTIVE.from, &update);
170 // First DFU page is untouched 207 // First DFU page is untouched
171 dfu.assert_eq(DFU.from + 4096, &original); 208 flash.dfu().read(4096, &mut read_buf).unwrap();
209 assert_eq!(ORIGINAL, read_buf);
172 } 210 }
173 211
174 #[test] 212 #[test]
175 #[cfg(all(feature = "nightly", not(feature = "_verify")))] 213 #[cfg(all(feature = "nightly", not(feature = "_verify")))]
176 fn test_separate_flash_dfu_page_biggest() { 214 fn test_swap_state_dfu_page_biggest() {
177 const STATE: Partition = Partition::new(2048, 4096); 215 const FIRMWARE_SIZE: usize = 12288;
178 const ACTIVE: Partition = Partition::new(4096, 16384); 216 let flash = AsyncTestFlash::new(BootLoaderConfig {
179 const DFU: Partition = Partition::new(0, 16384); 217 active: MemFlash::<FIRMWARE_SIZE, 2048, 4>::random(),
180 218 dfu: MemFlash::<16384, 4096, 8>::random(),
219 state: MemFlash::<2048, 128, 4>::random(),
220 });
221
222 const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];
223 const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];
181 let mut aligned = [0; 4]; 224 let mut aligned = [0; 4];
182 let mut active = MemFlash::<16384, 2048, 4>::random();
183 let mut dfu = MemFlash::<16384, 4096, 8>::random();
184 let mut state = MemFlash::<4096, 128, 4>::random();
185
186 let original = [rand::random::<u8>(); ACTIVE.size() as usize];
187 let update = [rand::random::<u8>(); ACTIVE.size() as usize];
188
189 active.program(ACTIVE.from, &original).unwrap();
190 225
191 let mut updater = FirmwareUpdater::new(DFU, STATE); 226 block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
192 227 block_on(flash.active().write(0, &ORIGINAL)).unwrap();
193 block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); 228
194 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); 229 let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
195 230 dfu: flash.dfu(),
196 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 231 state: flash.state(),
232 });
233 block_on(updater.write_firmware(0, &UPDATE)).unwrap();
234 block_on(updater.mark_updated(&mut aligned)).unwrap();
235
236 let flash = flash.into_blocking();
237 let mut bootloader = BootLoader::new(BootLoaderConfig {
238 active: flash.active(),
239 dfu: flash.dfu(),
240 state: flash.state(),
241 });
197 let mut page = [0; 4096]; 242 let mut page = [0; 4096];
198 assert_eq!( 243 assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
199 State::Swap,
200 bootloader
201 .prepare_boot(
202 &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,),
203 &mut page
204 )
205 .unwrap()
206 );
207 244
208 active.assert_eq(ACTIVE.from, &update); 245 let mut read_buf = [0; FIRMWARE_SIZE];
246 flash.active().read(0, &mut read_buf).unwrap();
247 assert_eq!(UPDATE, read_buf);
209 // First DFU page is untouched 248 // First DFU page is untouched
210 dfu.assert_eq(DFU.from + 4096, &original); 249 flash.dfu().read(4096, &mut read_buf).unwrap();
250 assert_eq!(ORIGINAL, read_buf);
211 } 251 }
212 252
213 #[test] 253 #[test]
@@ -233,25 +273,28 @@ mod tests {
233 let public_key: PublicKey = keypair.public; 273 let public_key: PublicKey = keypair.public;
234 274
235 // Setup flash 275 // Setup flash
236 276 let flash = BlockingTestFlash::new(BootLoaderConfig {
237 const STATE: Partition = Partition::new(0, 4096); 277 active: MemFlash::<0, 0, 0>::default(),
238 const DFU: Partition = Partition::new(4096, 8192); 278 dfu: MemFlash::<4096, 4096, 4>::default(),
239 let mut flash = MemFlash::<8192, 4096, 4>::default(); 279 state: MemFlash::<4096, 4096, 4>::default(),
280 });
240 281
241 let firmware_len = firmware.len(); 282 let firmware_len = firmware.len();
242 283
243 let mut write_buf = [0; 4096]; 284 let mut write_buf = [0; 4096];
244 write_buf[0..firmware_len].copy_from_slice(firmware); 285 write_buf[0..firmware_len].copy_from_slice(firmware);
245 DFU.write_blocking(&mut flash, 0, &write_buf).unwrap(); 286 flash.dfu().write(0, &write_buf).unwrap();
246 287
247 // On with the test 288 // On with the test
248 289 let flash = flash.into_async();
249 let mut updater = FirmwareUpdater::new(DFU, STATE); 290 let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
291 dfu: flash.dfu(),
292 state: flash.state(),
293 });
250 294
251 let mut aligned = [0; 4]; 295 let mut aligned = [0; 4];
252 296
253 assert!(block_on(updater.verify_and_mark_updated( 297 assert!(block_on(updater.verify_and_mark_updated(
254 &mut flash,
255 &public_key.to_bytes(), 298 &public_key.to_bytes(),
256 &signature.to_bytes(), 299 &signature.to_bytes(),
257 firmware_len as u32, 300 firmware_len as u32,
diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs
index 3ba84a92c..2728e9720 100644
--- a/embassy-boot/boot/src/mem_flash.rs
+++ b/embassy-boot/boot/src/mem_flash.rs
@@ -34,21 +34,61 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFla
34 } 34 }
35 } 35 }
36 36
37 pub fn program(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> { 37 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), MemFlashError> {
38 let len = bytes.len();
39 bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]);
40 Ok(())
41 }
42
43 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> {
38 let offset = offset as usize; 44 let offset = offset as usize;
39 assert!(bytes.len() % WRITE_SIZE == 0); 45 assert!(bytes.len() % WRITE_SIZE == 0);
40 assert!(offset % WRITE_SIZE == 0); 46 assert!(offset % WRITE_SIZE == 0);
41 assert!(offset + bytes.len() <= SIZE); 47 assert!(offset + bytes.len() <= SIZE);
42 48
43 self.mem[offset..offset + bytes.len()].copy_from_slice(bytes); 49 if let Some(pending_successes) = self.pending_write_successes {
50 if pending_successes > 0 {
51 self.pending_write_successes = Some(pending_successes - 1);
52 } else {
53 return Err(MemFlashError);
54 }
55 }
56
57 for ((offset, mem_byte), new_byte) in self
58 .mem
59 .iter_mut()
60 .enumerate()
61 .skip(offset)
62 .take(bytes.len())
63 .zip(bytes)
64 {
65 assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset);
66 *mem_byte = *new_byte;
67 }
44 68
45 Ok(()) 69 Ok(())
46 } 70 }
47 71
48 pub fn assert_eq(&self, offset: u32, expectation: &[u8]) { 72 fn erase(&mut self, from: u32, to: u32) -> Result<(), MemFlashError> {
49 for i in 0..expectation.len() { 73 let from = from as usize;
50 assert_eq!(self.mem[offset as usize + i], expectation[i], "Index {}", i); 74 let to = to as usize;
75 assert!(from % ERASE_SIZE == 0);
76 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
77 for i in from..to {
78 self.mem[i] = 0xFF;
51 } 79 }
80 Ok(())
81 }
82
83 pub fn program(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> {
84 let offset = offset as usize;
85 assert!(bytes.len() % WRITE_SIZE == 0);
86 assert!(offset % WRITE_SIZE == 0);
87 assert!(offset + bytes.len() <= SIZE);
88
89 self.mem[offset..offset + bytes.len()].copy_from_slice(bytes);
90
91 Ok(())
52 } 92 }
53} 93}
54 94
@@ -78,9 +118,7 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNo
78 const READ_SIZE: usize = 1; 118 const READ_SIZE: usize = 1;
79 119
80 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { 120 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
81 let len = bytes.len(); 121 self.read(offset, bytes)
82 bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]);
83 Ok(())
84 } 122 }
85 123
86 fn capacity(&self) -> usize { 124 fn capacity(&self) -> usize {
@@ -94,44 +132,12 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFla
94 const WRITE_SIZE: usize = WRITE_SIZE; 132 const WRITE_SIZE: usize = WRITE_SIZE;
95 const ERASE_SIZE: usize = ERASE_SIZE; 133 const ERASE_SIZE: usize = ERASE_SIZE;
96 134
97 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
98 let from = from as usize;
99 let to = to as usize;
100 assert!(from % ERASE_SIZE == 0);
101 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
102 for i in from..to {
103 self.mem[i] = 0xFF;
104 }
105 Ok(())
106 }
107
108 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { 135 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
109 let offset = offset as usize; 136 self.write(offset, bytes)
110 assert!(bytes.len() % WRITE_SIZE == 0); 137 }
111 assert!(offset % WRITE_SIZE == 0);
112 assert!(offset + bytes.len() <= SIZE);
113
114 if let Some(pending_successes) = self.pending_write_successes {
115 if pending_successes > 0 {
116 self.pending_write_successes = Some(pending_successes - 1);
117 } else {
118 return Err(MemFlashError);
119 }
120 }
121
122 for ((offset, mem_byte), new_byte) in self
123 .mem
124 .iter_mut()
125 .enumerate()
126 .skip(offset)
127 .take(bytes.len())
128 .zip(bytes)
129 {
130 assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset);
131 *mem_byte = *new_byte;
132 }
133 138
134 Ok(()) 139 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
140 self.erase(from, to)
135 } 141 }
136} 142}
137 143
@@ -142,11 +148,11 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncR
142 const READ_SIZE: usize = 1; 148 const READ_SIZE: usize = 1;
143 149
144 async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { 150 async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
145 <Self as ReadNorFlash>::read(self, offset, bytes) 151 self.read(offset, bytes)
146 } 152 }
147 153
148 fn capacity(&self) -> usize { 154 fn capacity(&self) -> usize {
149 <Self as ReadNorFlash>::capacity(self) 155 SIZE
150 } 156 }
151} 157}
152 158
@@ -157,11 +163,11 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncN
157 const WRITE_SIZE: usize = WRITE_SIZE; 163 const WRITE_SIZE: usize = WRITE_SIZE;
158 const ERASE_SIZE: usize = ERASE_SIZE; 164 const ERASE_SIZE: usize = ERASE_SIZE;
159 165
160 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { 166 async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
161 <Self as NorFlash>::erase(self, from, to) 167 self.write(offset, bytes)
162 } 168 }
163 169
164 async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { 170 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
165 <Self as NorFlash>::write(self, offset, bytes) 171 self.erase(from, to)
166 } 172 }
167} 173}
diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs
deleted file mode 100644
index 7b56a8240..000000000
--- a/embassy-boot/boot/src/partition.rs
+++ /dev/null
@@ -1,144 +0,0 @@
1use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
2#[cfg(feature = "nightly")]
3use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
4
5/// A region in flash used by the bootloader.
6#[derive(Copy, Clone, Debug)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub struct Partition {
9 /// The offset into the flash where the partition starts.
10 pub from: u32,
11 /// The offset into the flash where the partition ends.
12 pub to: u32,
13}
14
15impl Partition {
16 /// Create a new partition with the provided range
17 pub const fn new(from: u32, to: u32) -> Self {
18 Self { from, to }
19 }
20
21 /// Return the size of the partition
22 pub const fn size(&self) -> u32 {
23 self.to - self.from
24 }
25
26 /// Read from the partition on the provided flash
27 #[cfg(feature = "nightly")]
28 pub async fn read<F: AsyncReadNorFlash>(
29 &self,
30 flash: &mut F,
31 offset: u32,
32 bytes: &mut [u8],
33 ) -> Result<(), F::Error> {
34 let offset = self.from as u32 + offset;
35 flash.read(offset, bytes).await
36 }
37
38 /// Write to the partition on the provided flash
39 #[cfg(feature = "nightly")]
40 pub async fn write<F: AsyncNorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
41 let offset = self.from as u32 + offset;
42 flash.write(offset, bytes).await?;
43 trace!("Wrote from 0x{:x} len {}", offset, bytes.len());
44 Ok(())
45 }
46
47 /// Erase part of the partition on the provided flash
48 #[cfg(feature = "nightly")]
49 pub async fn erase<F: AsyncNorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
50 let from = self.from as u32 + from;
51 let to = self.from as u32 + to;
52 flash.erase(from, to).await?;
53 trace!("Erased from 0x{:x} to 0x{:x}", from, to);
54 Ok(())
55 }
56
57 /// Erase the entire partition
58 #[cfg(feature = "nightly")]
59 pub(crate) async fn wipe<F: AsyncNorFlash>(&self, flash: &mut F) -> Result<(), F::Error> {
60 let from = self.from as u32;
61 let to = self.to as u32;
62 flash.erase(from, to).await?;
63 trace!("Wiped from 0x{:x} to 0x{:x}", from, to);
64 Ok(())
65 }
66
67 /// Read from the partition on the provided flash
68 pub fn read_blocking<F: ReadNorFlash>(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> {
69 let offset = self.from as u32 + offset;
70 flash.read(offset, bytes)
71 }
72
73 /// Write to the partition on the provided flash
74 pub fn write_blocking<F: NorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
75 let offset = self.from as u32 + offset;
76 flash.write(offset, bytes)?;
77 trace!("Wrote from 0x{:x} len {}", offset, bytes.len());
78 Ok(())
79 }
80
81 /// Erase part of the partition on the provided flash
82 pub fn erase_blocking<F: NorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
83 let from = self.from as u32 + from;
84 let to = self.from as u32 + to;
85 flash.erase(from, to)?;
86 trace!("Erased from 0x{:x} to 0x{:x}", from, to);
87 Ok(())
88 }
89
90 /// Erase the entire partition
91 pub(crate) fn wipe_blocking<F: NorFlash>(&self, flash: &mut F) -> Result<(), F::Error> {
92 let from = self.from as u32;
93 let to = self.to as u32;
94 flash.erase(from, to)?;
95 trace!("Wiped from 0x{:x} to 0x{:x}", from, to);
96 Ok(())
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use crate::mem_flash::MemFlash;
103 use crate::Partition;
104
105 #[test]
106 fn can_erase() {
107 let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
108 let partition = Partition::new(256, 512);
109
110 partition.erase_blocking(&mut flash, 64, 192).unwrap();
111
112 for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) {
113 assert_eq!(0x00, byte, "Index {}", index);
114 }
115
116 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) {
117 assert_eq!(0xFF, byte, "Index {}", index);
118 }
119
120 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) {
121 assert_eq!(0x00, byte, "Index {}", index);
122 }
123 }
124
125 #[test]
126 fn can_wipe() {
127 let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
128 let partition = Partition::new(256, 512);
129
130 partition.wipe_blocking(&mut flash).unwrap();
131
132 for (index, byte) in flash.mem.iter().copied().enumerate().take(256) {
133 assert_eq!(0x00, byte, "Index {}", index);
134 }
135
136 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) {
137 assert_eq!(0xFF, byte, "Index {}", index);
138 }
139
140 for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) {
141 assert_eq!(0x00, byte, "Index {}", index);
142 }
143 }
144}
diff --git a/embassy-boot/boot/src/test_flash/asynch.rs b/embassy-boot/boot/src/test_flash/asynch.rs
new file mode 100644
index 000000000..3ac9e71ab
--- /dev/null
+++ b/embassy-boot/boot/src/test_flash/asynch.rs
@@ -0,0 +1,64 @@
1use embassy_embedded_hal::flash::partition::Partition;
2use embassy_sync::blocking_mutex::raw::NoopRawMutex;
3use embassy_sync::mutex::Mutex;
4use embedded_storage_async::nor_flash::NorFlash;
5
6use crate::BootLoaderConfig;
7
8pub struct AsyncTestFlash<ACTIVE, DFU, STATE>
9where
10 ACTIVE: NorFlash,
11 DFU: NorFlash,
12 STATE: NorFlash,
13{
14 active: Mutex<NoopRawMutex, ACTIVE>,
15 dfu: Mutex<NoopRawMutex, DFU>,
16 state: Mutex<NoopRawMutex, STATE>,
17}
18
19impl<ACTIVE, DFU, STATE> AsyncTestFlash<ACTIVE, DFU, STATE>
20where
21 ACTIVE: NorFlash,
22 DFU: NorFlash,
23 STATE: NorFlash,
24{
25 pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
26 Self {
27 active: Mutex::new(config.active),
28 dfu: Mutex::new(config.dfu),
29 state: Mutex::new(config.state),
30 }
31 }
32
33 pub fn active(&self) -> Partition<NoopRawMutex, ACTIVE> {
34 Self::create_partition(&self.active)
35 }
36
37 pub fn dfu(&self) -> Partition<NoopRawMutex, DFU> {
38 Self::create_partition(&self.dfu)
39 }
40
41 pub fn state(&self) -> Partition<NoopRawMutex, STATE> {
42 Self::create_partition(&self.state)
43 }
44
45 fn create_partition<T: NorFlash>(mutex: &Mutex<NoopRawMutex, T>) -> Partition<NoopRawMutex, T> {
46 Partition::new(mutex, 0, mutex.try_lock().unwrap().capacity() as u32)
47 }
48}
49
50impl<ACTIVE, DFU, STATE> AsyncTestFlash<ACTIVE, DFU, STATE>
51where
52 ACTIVE: NorFlash + embedded_storage::nor_flash::NorFlash,
53 DFU: NorFlash + embedded_storage::nor_flash::NorFlash,
54 STATE: NorFlash + embedded_storage::nor_flash::NorFlash,
55{
56 pub fn into_blocking(self) -> super::BlockingTestFlash<ACTIVE, DFU, STATE> {
57 let config = BootLoaderConfig {
58 active: self.active.into_inner(),
59 dfu: self.dfu.into_inner(),
60 state: self.state.into_inner(),
61 };
62 super::BlockingTestFlash::new(config)
63 }
64}
diff --git a/embassy-boot/boot/src/test_flash/blocking.rs b/embassy-boot/boot/src/test_flash/blocking.rs
new file mode 100644
index 000000000..ba33c9208
--- /dev/null
+++ b/embassy-boot/boot/src/test_flash/blocking.rs
@@ -0,0 +1,69 @@
1use core::cell::RefCell;
2
3use embassy_embedded_hal::flash::partition::BlockingPartition;
4use embassy_sync::blocking_mutex::raw::NoopRawMutex;
5use embassy_sync::blocking_mutex::Mutex;
6use embedded_storage::nor_flash::NorFlash;
7
8use crate::BootLoaderConfig;
9
10pub struct BlockingTestFlash<ACTIVE, DFU, STATE>
11where
12 ACTIVE: NorFlash,
13 DFU: NorFlash,
14 STATE: NorFlash,
15{
16 active: Mutex<NoopRawMutex, RefCell<ACTIVE>>,
17 dfu: Mutex<NoopRawMutex, RefCell<DFU>>,
18 state: Mutex<NoopRawMutex, RefCell<STATE>>,
19}
20
21impl<ACTIVE, DFU, STATE> BlockingTestFlash<ACTIVE, DFU, STATE>
22where
23 ACTIVE: NorFlash,
24 DFU: NorFlash,
25 STATE: NorFlash,
26{
27 pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
28 Self {
29 active: Mutex::new(RefCell::new(config.active)),
30 dfu: Mutex::new(RefCell::new(config.dfu)),
31 state: Mutex::new(RefCell::new(config.state)),
32 }
33 }
34
35 pub fn active(&self) -> BlockingPartition<NoopRawMutex, ACTIVE> {
36 Self::create_partition(&self.active)
37 }
38
39 pub fn dfu(&self) -> BlockingPartition<NoopRawMutex, DFU> {
40 Self::create_partition(&self.dfu)
41 }
42
43 pub fn state(&self) -> BlockingPartition<NoopRawMutex, STATE> {
44 Self::create_partition(&self.state)
45 }
46
47 pub fn create_partition<T: NorFlash>(
48 mutex: &Mutex<NoopRawMutex, RefCell<T>>,
49 ) -> BlockingPartition<NoopRawMutex, T> {
50 BlockingPartition::new(mutex, 0, mutex.lock(|f| f.borrow().capacity()) as u32)
51 }
52}
53
54#[cfg(feature = "nightly")]
55impl<ACTIVE, DFU, STATE> BlockingTestFlash<ACTIVE, DFU, STATE>
56where
57 ACTIVE: NorFlash + embedded_storage_async::nor_flash::NorFlash,
58 DFU: NorFlash + embedded_storage_async::nor_flash::NorFlash,
59 STATE: NorFlash + embedded_storage_async::nor_flash::NorFlash,
60{
61 pub fn into_async(self) -> super::AsyncTestFlash<ACTIVE, DFU, STATE> {
62 let config = BootLoaderConfig {
63 active: self.active.into_inner().into_inner(),
64 dfu: self.dfu.into_inner().into_inner(),
65 state: self.state.into_inner().into_inner(),
66 };
67 super::AsyncTestFlash::new(config)
68 }
69}
diff --git a/embassy-boot/boot/src/test_flash/mod.rs b/embassy-boot/boot/src/test_flash/mod.rs
new file mode 100644
index 000000000..a0672322e
--- /dev/null
+++ b/embassy-boot/boot/src/test_flash/mod.rs
@@ -0,0 +1,7 @@
1#[cfg(feature = "nightly")]
2mod asynch;
3mod blocking;
4
5#[cfg(feature = "nightly")]
6pub(crate) use asynch::AsyncTestFlash;
7pub(crate) use blocking::BlockingTestFlash;
diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs
index 710798bdb..bb702073c 100644
--- a/embassy-boot/nrf/src/lib.rs
+++ b/embassy-boot/nrf/src/lib.rs
@@ -3,74 +3,37 @@
3#![doc = include_str!("../README.md")] 3#![doc = include_str!("../README.md")]
4mod fmt; 4mod fmt;
5 5
6pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig}; 6#[cfg(feature = "nightly")]
7pub use embassy_boot::FirmwareUpdater;
8pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig};
7use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; 9use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE};
8use embassy_nrf::peripherals::WDT; 10use embassy_nrf::peripherals::WDT;
9use embassy_nrf::wdt; 11use embassy_nrf::wdt;
10use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; 12use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
11 13
12/// A bootloader for nRF devices. 14/// A bootloader for nRF devices.
13pub struct BootLoader<const BUFFER_SIZE: usize = PAGE_SIZE> { 15pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = PAGE_SIZE> {
14 boot: embassy_boot::BootLoader, 16 boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
15 aligned_buf: AlignedBuffer<BUFFER_SIZE>, 17 aligned_buf: AlignedBuffer<BUFFER_SIZE>,
16} 18}
17 19
18#[cfg(target_os = "none")] 20impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
19impl Default for BootLoader<PAGE_SIZE> { 21 BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE>
20 /// Create a new bootloader instance using parameters from linker script 22{
21 fn default() -> Self {
22 extern "C" {
23 static __bootloader_state_start: u32;
24 static __bootloader_state_end: u32;
25 static __bootloader_active_start: u32;
26 static __bootloader_active_end: u32;
27 static __bootloader_dfu_start: u32;
28 static __bootloader_dfu_end: u32;
29 }
30
31 let active = unsafe {
32 Partition::new(
33 &__bootloader_active_start as *const u32 as u32,
34 &__bootloader_active_end as *const u32 as u32,
35 )
36 };
37 let dfu = unsafe {
38 Partition::new(
39 &__bootloader_dfu_start as *const u32 as u32,
40 &__bootloader_dfu_end as *const u32 as u32,
41 )
42 };
43 let state = unsafe {
44 Partition::new(
45 &__bootloader_state_start as *const u32 as u32,
46 &__bootloader_state_end as *const u32 as u32,
47 )
48 };
49
50 trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to);
51 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
52 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
53
54 Self::new(active, dfu, state)
55 }
56}
57
58impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
59 /// Create a new bootloader instance using the supplied partitions for active, dfu and state. 23 /// Create a new bootloader instance using the supplied partitions for active, dfu and state.
60 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 24 pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
61 Self { 25 Self {
62 boot: embassy_boot::BootLoader::new(active, dfu, state), 26 boot: embassy_boot::BootLoader::new(config),
63 aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), 27 aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
64 } 28 }
65 } 29 }
66 30
67 /// Inspect the bootloader state and perform actions required before booting, such as swapping 31 /// Inspect the bootloader state and perform actions required before booting, such as swapping
68 /// firmware. 32 /// firmware.
69 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize { 33 pub fn prepare(&mut self) {
70 match self.boot.prepare_boot(flash, &mut self.aligned_buf.0) { 34 self.boot
71 Ok(_) => self.boot.boot_address(), 35 .prepare_boot(&mut self.aligned_buf.0)
72 Err(_) => panic!("boot prepare error!"), 36 .expect("Boot prepare error");
73 }
74 } 37 }
75 38
76 /// Boots the application without softdevice mechanisms. 39 /// Boots the application without softdevice mechanisms.
@@ -79,10 +42,12 @@ impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
79 /// 42 ///
80 /// This modifies the stack pointer and reset vector and will run code placed in the active partition. 43 /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
81 #[cfg(not(feature = "softdevice"))] 44 #[cfg(not(feature = "softdevice"))]
82 pub unsafe fn load(&mut self, start: usize) -> ! { 45 pub unsafe fn load(self, start: u32) -> ! {
46 core::mem::drop(self.boot);
47
83 let mut p = cortex_m::Peripherals::steal(); 48 let mut p = cortex_m::Peripherals::steal();
84 p.SCB.invalidate_icache(); 49 p.SCB.invalidate_icache();
85 p.SCB.vtor.write(start as u32); 50 p.SCB.vtor.write(start);
86 cortex_m::asm::bootload(start as *const u32) 51 cortex_m::asm::bootload(start as *const u32)
87 } 52 }
88 53
@@ -92,7 +57,7 @@ impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
92 /// 57 ///
93 /// This modifies the stack pointer and reset vector and will run code placed in the active partition. 58 /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
94 #[cfg(feature = "softdevice")] 59 #[cfg(feature = "softdevice")]
95 pub unsafe fn load(&mut self, _app: usize) -> ! { 60 pub unsafe fn load(&mut self, _app: u32) -> ! {
96 use nrf_softdevice_mbr as mbr; 61 use nrf_softdevice_mbr as mbr;
97 const NRF_SUCCESS: u32 = 0; 62 const NRF_SUCCESS: u32 = 0;
98 63
diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs
index fb9bc3242..25329f9e9 100644
--- a/embassy-boot/rp/src/lib.rs
+++ b/embassy-boot/rp/src/lib.rs
@@ -3,7 +3,9 @@
3#![doc = include_str!("../README.md")] 3#![doc = include_str!("../README.md")]
4mod fmt; 4mod fmt;
5 5
6pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; 6#[cfg(feature = "nightly")]
7pub use embassy_boot::FirmwareUpdater;
8pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State};
7use embassy_rp::flash::{Flash, ERASE_SIZE}; 9use embassy_rp::flash::{Flash, ERASE_SIZE};
8use embassy_rp::peripherals::{FLASH, WATCHDOG}; 10use embassy_rp::peripherals::{FLASH, WATCHDOG};
9use embassy_rp::watchdog::Watchdog; 11use embassy_rp::watchdog::Watchdog;
@@ -11,27 +13,28 @@ use embassy_time::Duration;
11use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; 13use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
12 14
13/// A bootloader for RP2040 devices. 15/// A bootloader for RP2040 devices.
14pub struct BootLoader<const BUFFER_SIZE: usize = ERASE_SIZE> { 16pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = ERASE_SIZE> {
15 boot: embassy_boot::BootLoader, 17 boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
16 aligned_buf: AlignedBuffer<BUFFER_SIZE>, 18 aligned_buf: AlignedBuffer<BUFFER_SIZE>,
17} 19}
18 20
19impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> { 21impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
22 BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE>
23{
20 /// Create a new bootloader instance using the supplied partitions for active, dfu and state. 24 /// Create a new bootloader instance using the supplied partitions for active, dfu and state.
21 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 25 pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
22 Self { 26 Self {
23 boot: embassy_boot::BootLoader::new(active, dfu, state), 27 boot: embassy_boot::BootLoader::new(config),
24 aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), 28 aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
25 } 29 }
26 } 30 }
27 31
28 /// Inspect the bootloader state and perform actions required before booting, such as swapping 32 /// Inspect the bootloader state and perform actions required before booting, such as swapping
29 /// firmware. 33 /// firmware.
30 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize { 34 pub fn prepare(&mut self) {
31 match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { 35 self.boot
32 Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(), 36 .prepare_boot(self.aligned_buf.as_mut())
33 Err(_) => panic!("boot prepare error!"), 37 .expect("Boot prepare error");
34 }
35 } 38 }
36 39
37 /// Boots the application. 40 /// Boots the application.
@@ -39,58 +42,20 @@ impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
39 /// # Safety 42 /// # Safety
40 /// 43 ///
41 /// This modifies the stack pointer and reset vector and will run code placed in the active partition. 44 /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
42 pub unsafe fn load(&mut self, start: usize) -> ! { 45 pub unsafe fn load(self, start: u32) -> ! {
46 core::mem::drop(self.boot);
47
43 trace!("Loading app at 0x{:x}", start); 48 trace!("Loading app at 0x{:x}", start);
44 #[allow(unused_mut)] 49 #[allow(unused_mut)]
45 let mut p = cortex_m::Peripherals::steal(); 50 let mut p = cortex_m::Peripherals::steal();
46 #[cfg(not(armv6m))] 51 #[cfg(not(armv6m))]
47 p.SCB.invalidate_icache(); 52 p.SCB.invalidate_icache();
48 p.SCB.vtor.write(start as u32); 53 p.SCB.vtor.write(start);
49 54
50 cortex_m::asm::bootload(start as *const u32) 55 cortex_m::asm::bootload(start as *const u32)
51 } 56 }
52} 57}
53 58
54#[cfg(target_os = "none")]
55impl Default for BootLoader<ERASE_SIZE> {
56 /// Create a new bootloader instance using parameters from linker script
57 fn default() -> Self {
58 extern "C" {
59 static __bootloader_state_start: u32;
60 static __bootloader_state_end: u32;
61 static __bootloader_active_start: u32;
62 static __bootloader_active_end: u32;
63 static __bootloader_dfu_start: u32;
64 static __bootloader_dfu_end: u32;
65 }
66
67 let active = unsafe {
68 Partition::new(
69 &__bootloader_active_start as *const u32 as u32,
70 &__bootloader_active_end as *const u32 as u32,
71 )
72 };
73 let dfu = unsafe {
74 Partition::new(
75 &__bootloader_dfu_start as *const u32 as u32,
76 &__bootloader_dfu_end as *const u32 as u32,
77 )
78 };
79 let state = unsafe {
80 Partition::new(
81 &__bootloader_state_start as *const u32 as u32,
82 &__bootloader_state_end as *const u32 as u32,
83 )
84 };
85
86 trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to);
87 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
88 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
89
90 Self::new(active, dfu, state)
91 }
92}
93
94/// A flash implementation that will feed a watchdog when touching flash. 59/// A flash implementation that will feed a watchdog when touching flash.
95pub struct WatchdogFlash<'d, const SIZE: usize> { 60pub struct WatchdogFlash<'d, const SIZE: usize> {
96 flash: Flash<'d, FLASH, SIZE>, 61 flash: Flash<'d, FLASH, SIZE>,
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs
index ccf136c74..069de0d1a 100644
--- a/embassy-boot/stm32/src/lib.rs
+++ b/embassy-boot/stm32/src/lib.rs
@@ -3,30 +3,34 @@
3#![doc = include_str!("../README.md")] 3#![doc = include_str!("../README.md")]
4mod fmt; 4mod fmt;
5 5
6pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; 6#[cfg(feature = "nightly")]
7pub use embassy_boot::FirmwareUpdater;
8pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State};
9use embedded_storage::nor_flash::NorFlash;
7 10
8/// A bootloader for STM32 devices. 11/// A bootloader for STM32 devices.
9pub struct BootLoader<const BUFFER_SIZE: usize> { 12pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> {
10 boot: embassy_boot::BootLoader, 13 boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
11 aligned_buf: AlignedBuffer<BUFFER_SIZE>, 14 aligned_buf: AlignedBuffer<BUFFER_SIZE>,
12} 15}
13 16
14impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> { 17impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
18 BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE>
19{
15 /// Create a new bootloader instance using the supplied partitions for active, dfu and state. 20 /// Create a new bootloader instance using the supplied partitions for active, dfu and state.
16 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 21 pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
17 Self { 22 Self {
18 boot: embassy_boot::BootLoader::new(active, dfu, state), 23 boot: embassy_boot::BootLoader::new(config),
19 aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), 24 aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
20 } 25 }
21 } 26 }
22 27
23 /// Inspect the bootloader state and perform actions required before booting, such as swapping 28 /// Inspect the bootloader state and perform actions required before booting, such as swapping
24 /// firmware. 29 /// firmware.
25 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize { 30 pub fn prepare(&mut self) {
26 match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { 31 self.boot
27 Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(), 32 .prepare_boot(self.aligned_buf.as_mut())
28 Err(_) => panic!("boot prepare error!"), 33 .expect("Boot prepare error");
29 }
30 } 34 }
31 35
32 /// Boots the application. 36 /// Boots the application.
@@ -34,54 +38,16 @@ impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
34 /// # Safety 38 /// # Safety
35 /// 39 ///
36 /// This modifies the stack pointer and reset vector and will run code placed in the active partition. 40 /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
37 pub unsafe fn load(&mut self, start: usize) -> ! { 41 pub unsafe fn load(self, start: u32) -> ! {
42 core::mem::drop(self.boot);
43
38 trace!("Loading app at 0x{:x}", start); 44 trace!("Loading app at 0x{:x}", start);
39 #[allow(unused_mut)] 45 #[allow(unused_mut)]
40 let mut p = cortex_m::Peripherals::steal(); 46 let mut p = cortex_m::Peripherals::steal();
41 #[cfg(not(armv6m))] 47 #[cfg(not(armv6m))]
42 p.SCB.invalidate_icache(); 48 p.SCB.invalidate_icache();
43 p.SCB.vtor.write(start as u32); 49 p.SCB.vtor.write(start);
44 50
45 cortex_m::asm::bootload(start as *const u32) 51 cortex_m::asm::bootload(start as *const u32)
46 } 52 }
47} 53}
48
49#[cfg(target_os = "none")]
50impl<const BUFFER_SIZE: usize> Default for BootLoader<BUFFER_SIZE> {
51 /// Create a new bootloader instance using parameters from linker script
52 fn default() -> Self {
53 extern "C" {
54 static __bootloader_state_start: u32;
55 static __bootloader_state_end: u32;
56 static __bootloader_active_start: u32;
57 static __bootloader_active_end: u32;
58 static __bootloader_dfu_start: u32;
59 static __bootloader_dfu_end: u32;
60 }
61
62 let active = unsafe {
63 Partition::new(
64 &__bootloader_active_start as *const u32 as u32,
65 &__bootloader_active_end as *const u32 as u32,
66 )
67 };
68 let dfu = unsafe {
69 Partition::new(
70 &__bootloader_dfu_start as *const u32 as u32,
71 &__bootloader_dfu_end as *const u32 as u32,
72 )
73 };
74 let state = unsafe {
75 Partition::new(
76 &__bootloader_state_start as *const u32 as u32,
77 &__bootloader_state_end as *const u32 as u32,
78 )
79 };
80
81 trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to);
82 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
83 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
84
85 Self::new(active, dfu, state)
86 }
87}
diff --git a/embassy-embedded-hal/src/flash/partition/asynch.rs b/embassy-embedded-hal/src/flash/partition/asynch.rs
index 141e0d9fc..5920436dd 100644
--- a/embassy-embedded-hal/src/flash/partition/asynch.rs
+++ b/embassy-embedded-hal/src/flash/partition/asynch.rs
@@ -30,6 +30,16 @@ impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> {
30 } 30 }
31 Self { flash, offset, size } 31 Self { flash, offset, size }
32 } 32 }
33
34 /// Get the partition offset within the flash
35 pub const fn offset(&self) -> u32 {
36 self.offset
37 }
38
39 /// Get the partition size
40 pub const fn size(&self) -> u32 {
41 self.size
42 }
33} 43}
34 44
35impl<M: RawMutex, T: NorFlash> ErrorType for Partition<'_, M, T> { 45impl<M: RawMutex, T: NorFlash> ErrorType for Partition<'_, M, T> {
diff --git a/embassy-embedded-hal/src/flash/partition/blocking.rs b/embassy-embedded-hal/src/flash/partition/blocking.rs
index dc52e292a..2ddbe3de0 100644
--- a/embassy-embedded-hal/src/flash/partition/blocking.rs
+++ b/embassy-embedded-hal/src/flash/partition/blocking.rs
@@ -31,6 +31,16 @@ impl<'a, M: RawMutex, T: NorFlash> BlockingPartition<'a, M, T> {
31 } 31 }
32 Self { flash, offset, size } 32 Self { flash, offset, size }
33 } 33 }
34
35 /// Get the partition offset within the flash
36 pub const fn offset(&self) -> u32 {
37 self.offset
38 }
39
40 /// Get the partition size
41 pub const fn size(&self) -> u32 {
42 self.size
43 }
34} 44}
35 45
36impl<M: RawMutex, T: NorFlash> ErrorType for BlockingPartition<'_, M, T> { 46impl<M: RawMutex, T: NorFlash> ErrorType for BlockingPartition<'_, M, T> {
diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs
index 090a05b23..06c237781 100644
--- a/examples/boot/application/nrf/src/bin/a.rs
+++ b/examples/boot/application/nrf/src/bin/a.rs
@@ -3,12 +3,13 @@
3#![macro_use] 3#![macro_use]
4#![feature(type_alias_impl_trait)] 4#![feature(type_alias_impl_trait)]
5 5
6use embassy_boot_nrf::FirmwareUpdater; 6use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig};
7use embassy_embedded_hal::adapter::BlockingAsync; 7use embassy_embedded_hal::adapter::BlockingAsync;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; 9use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
10use embassy_nrf::nvmc::Nvmc; 10use embassy_nrf::nvmc::Nvmc;
11use embassy_nrf::wdt::{self, Watchdog}; 11use embassy_nrf::wdt::{self, Watchdog};
12use embassy_sync::mutex::Mutex;
12use panic_reset as _; 13use panic_reset as _;
13 14
14static APP_B: &[u8] = include_bytes!("../../b.bin"); 15static APP_B: &[u8] = include_bytes!("../../b.bin");
@@ -45,9 +46,10 @@ async fn main(_spawner: Spawner) {
45 }; 46 };
46 47
47 let nvmc = Nvmc::new(p.NVMC); 48 let nvmc = Nvmc::new(p.NVMC);
48 let mut nvmc = BlockingAsync::new(nvmc); 49 let nvmc = Mutex::new(BlockingAsync::new(nvmc));
49 50
50 let mut updater = FirmwareUpdater::default(); 51 let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc);
52 let mut updater = FirmwareUpdater::new(config);
51 loop { 53 loop {
52 led.set_low(); 54 led.set_low();
53 button.wait_for_any_edge().await; 55 button.wait_for_any_edge().await;
@@ -56,11 +58,11 @@ async fn main(_spawner: Spawner) {
56 for chunk in APP_B.chunks(4096) { 58 for chunk in APP_B.chunks(4096) {
57 let mut buf: [u8; 4096] = [0; 4096]; 59 let mut buf: [u8; 4096] = [0; 4096];
58 buf[..chunk.len()].copy_from_slice(chunk); 60 buf[..chunk.len()].copy_from_slice(chunk);
59 updater.write_firmware(offset, &buf, &mut nvmc, 4096).await.unwrap(); 61 updater.write_firmware(offset, &buf).await.unwrap();
60 offset += chunk.len(); 62 offset += chunk.len();
61 } 63 }
62 let mut magic = [0; 4]; 64 let mut magic = [0; 4];
63 updater.mark_updated(&mut nvmc, &mut magic).await.unwrap(); 65 updater.mark_updated(&mut magic).await.unwrap();
64 led.set_high(); 66 led.set_high();
65 cortex_m::peripheral::SCB::sys_reset(); 67 cortex_m::peripheral::SCB::sys_reset();
66 } 68 }
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml
index 64c2b8925..4a2c5dd8f 100644
--- a/examples/boot/application/rp/Cargo.toml
+++ b/examples/boot/application/rp/Cargo.toml
@@ -20,6 +20,7 @@ embedded-hal = { version = "0.2.6" }
20 20
21cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 21cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
22cortex-m-rt = "0.7.0" 22cortex-m-rt = "0.7.0"
23embedded-storage = "0.3.0"
23 24
24[features] 25[features]
25default = ["panic-reset"] 26default = ["panic-reset"]
diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs
index 47f1d16d8..69850069b 100644
--- a/examples/boot/application/rp/src/bin/a.rs
+++ b/examples/boot/application/rp/src/bin/a.rs
@@ -2,13 +2,17 @@
2#![no_main] 2#![no_main]
3#![feature(type_alias_impl_trait)] 3#![feature(type_alias_impl_trait)]
4 4
5use core::cell::RefCell;
6
5use defmt_rtt as _; 7use defmt_rtt as _;
6use embassy_boot_rp::*; 8use embassy_boot_rp::*;
7use embassy_executor::Spawner; 9use embassy_executor::Spawner;
8use embassy_rp::flash::Flash; 10use embassy_rp::flash::Flash;
9use embassy_rp::gpio::{Level, Output}; 11use embassy_rp::gpio::{Level, Output};
10use embassy_rp::watchdog::Watchdog; 12use embassy_rp::watchdog::Watchdog;
13use embassy_sync::blocking_mutex::Mutex;
11use embassy_time::{Duration, Timer}; 14use embassy_time::{Duration, Timer};
15use embedded_storage::nor_flash::NorFlash;
12#[cfg(feature = "panic-probe")] 16#[cfg(feature = "panic-probe")]
13use panic_probe as _; 17use panic_probe as _;
14#[cfg(feature = "panic-reset")] 18#[cfg(feature = "panic-reset")]
@@ -26,9 +30,11 @@ async fn main(_s: Spawner) {
26 let mut watchdog = Watchdog::new(p.WATCHDOG); 30 let mut watchdog = Watchdog::new(p.WATCHDOG);
27 watchdog.start(Duration::from_secs(8)); 31 watchdog.start(Duration::from_secs(8));
28 32
29 let mut flash: Flash<_, FLASH_SIZE> = Flash::new_blocking(p.FLASH); 33 let flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH);
34 let flash = Mutex::new(RefCell::new(flash));
30 35
31 let mut updater = FirmwareUpdater::default(); 36 let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
37 let mut updater = BlockingFirmwareUpdater::new(config);
32 38
33 Timer::after(Duration::from_secs(5)).await; 39 Timer::after(Duration::from_secs(5)).await;
34 watchdog.feed(); 40 watchdog.feed();
@@ -36,22 +42,20 @@ async fn main(_s: Spawner) {
36 let mut offset = 0; 42 let mut offset = 0;
37 let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); 43 let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]);
38 defmt::info!("preparing update"); 44 defmt::info!("preparing update");
39 let mut writer = updater 45 let writer = updater
40 .prepare_update_blocking(&mut flash) 46 .prepare_update()
41 .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) 47 .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e)))
42 .unwrap(); 48 .unwrap();
43 defmt::info!("writer created, starting write"); 49 defmt::info!("writer created, starting write");
44 for chunk in APP_B.chunks(4096) { 50 for chunk in APP_B.chunks(4096) {
45 buf.0[..chunk.len()].copy_from_slice(chunk); 51 buf.0[..chunk.len()].copy_from_slice(chunk);
46 defmt::info!("writing block at offset {}", offset); 52 defmt::info!("writing block at offset {}", offset);
47 writer 53 writer.write(offset, &buf.0[..]).unwrap();
48 .write_block_blocking(offset, &buf.0[..], &mut flash, 256) 54 offset += chunk.len() as u32;
49 .unwrap();
50 offset += chunk.len();
51 } 55 }
52 watchdog.feed(); 56 watchdog.feed();
53 defmt::info!("firmware written, marking update"); 57 defmt::info!("firmware written, marking update");
54 updater.mark_updated_blocking(&mut flash, &mut buf.0[..1]).unwrap(); 58 updater.mark_updated(&mut buf.0[..1]).unwrap();
55 Timer::after(Duration::from_secs(2)).await; 59 Timer::after(Duration::from_secs(2)).await;
56 led.set_low(); 60 led.set_low();
57 defmt::info!("update marked, resetting"); 61 defmt::info!("update marked, resetting");
diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs
index 5db1dbb57..c94676f09 100644
--- a/examples/boot/application/stm32f3/src/bin/a.rs
+++ b/examples/boot/application/stm32f3/src/bin/a.rs
@@ -4,12 +4,13 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; 7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};
8use embassy_embedded_hal::adapter::BlockingAsync; 8use embassy_embedded_hal::adapter::BlockingAsync;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::ExtiInput;
11use embassy_stm32::flash::{Flash, WRITE_SIZE}; 11use embassy_stm32::flash::{Flash, WRITE_SIZE};
12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; 12use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
13use embassy_sync::mutex::Mutex;
13use panic_reset as _; 14use panic_reset as _;
14 15
15static APP_B: &[u8] = include_bytes!("../../b.bin"); 16static APP_B: &[u8] = include_bytes!("../../b.bin");
@@ -18,7 +19,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
18async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
19 let p = embassy_stm32::init(Default::default()); 20 let p = embassy_stm32::init(Default::default());
20 let flash = Flash::new_blocking(p.FLASH); 21 let flash = Flash::new_blocking(p.FLASH);
21 let mut flash = BlockingAsync::new(flash); 22 let flash = Mutex::new(BlockingAsync::new(flash));
22 23
23 let button = Input::new(p.PC13, Pull::Up); 24 let button = Input::new(p.PC13, Pull::Up);
24 let mut button = ExtiInput::new(button, p.EXTI13); 25 let mut button = ExtiInput::new(button, p.EXTI13);
@@ -26,17 +27,18 @@ async fn main(_spawner: Spawner) {
26 let mut led = Output::new(p.PA5, Level::Low, Speed::Low); 27 let mut led = Output::new(p.PA5, Level::Low, Speed::Low);
27 led.set_high(); 28 led.set_high();
28 29
29 let mut updater = FirmwareUpdater::default(); 30 let config = FirmwareUpdaterConfig::from_linkerfile(&flash);
31 let mut updater = FirmwareUpdater::new(config);
30 button.wait_for_falling_edge().await; 32 button.wait_for_falling_edge().await;
31 let mut offset = 0; 33 let mut offset = 0;
32 for chunk in APP_B.chunks(2048) { 34 for chunk in APP_B.chunks(2048) {
33 let mut buf: [u8; 2048] = [0; 2048]; 35 let mut buf: [u8; 2048] = [0; 2048];
34 buf[..chunk.len()].copy_from_slice(chunk); 36 buf[..chunk.len()].copy_from_slice(chunk);
35 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); 37 updater.write_firmware(offset, &buf).await.unwrap();
36 offset += chunk.len(); 38 offset += chunk.len();
37 } 39 }
38 let mut magic = AlignedBuffer([0; WRITE_SIZE]); 40 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
39 updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); 41 updater.mark_updated(magic.as_mut()).await.unwrap();
40 led.set_low(); 42 led.set_low();
41 cortex_m::peripheral::SCB::sys_reset(); 43 cortex_m::peripheral::SCB::sys_reset();
42} 44}
diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs
index 5d586445c..fc2702c91 100644
--- a/examples/boot/application/stm32f7/src/bin/a.rs
+++ b/examples/boot/application/stm32f7/src/bin/a.rs
@@ -4,7 +4,7 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; 7use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig};
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::exti::ExtiInput; 9use embassy_stm32::exti::ExtiInput;
10use embassy_stm32::flash::{Flash, WRITE_SIZE}; 10use embassy_stm32::flash::{Flash, WRITE_SIZE};
@@ -16,7 +16,8 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
16#[embassy_executor::main] 16#[embassy_executor::main]
17async fn main(_spawner: Spawner) { 17async fn main(_spawner: Spawner) {
18 let p = embassy_stm32::init(Default::default()); 18 let p = embassy_stm32::init(Default::default());
19 let mut flash = Flash::new_blocking(p.FLASH); 19 let flash = Flash::new_blocking(p.FLASH);
20 let flash = Mutex::new(RefCell::new(flash));
20 21
21 let button = Input::new(p.PC13, Pull::Down); 22 let button = Input::new(p.PC13, Pull::Down);
22 let mut button = ExtiInput::new(button, p.EXTI13); 23 let mut button = ExtiInput::new(button, p.EXTI13);
@@ -24,20 +25,19 @@ async fn main(_spawner: Spawner) {
24 let mut led = Output::new(p.PB7, Level::Low, Speed::Low); 25 let mut led = Output::new(p.PB7, Level::Low, Speed::Low);
25 led.set_high(); 26 led.set_high();
26 27
27 let mut updater = FirmwareUpdater::default(); 28 let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
28 let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); 29 let mut updater = BlockingFirmwareUpdater::new(config);
30 let mut writer = updater.prepare_update().unwrap();
29 button.wait_for_rising_edge().await; 31 button.wait_for_rising_edge().await;
30 let mut offset = 0; 32 let mut offset = 0;
31 let mut buf = AlignedBuffer([0; 4096]); 33 let mut buf = AlignedBuffer([0; 4096]);
32 for chunk in APP_B.chunks(4096) { 34 for chunk in APP_B.chunks(4096) {
33 buf.as_mut()[..chunk.len()].copy_from_slice(chunk); 35 buf.as_mut()[..chunk.len()].copy_from_slice(chunk);
34 writer 36 writer.write(offset, buf.as_ref()).unwrap();
35 .write_block_blocking(offset, buf.as_ref(), &mut flash, chunk.len())
36 .unwrap();
37 offset += chunk.len(); 37 offset += chunk.len();
38 } 38 }
39 let mut magic = AlignedBuffer([0; WRITE_SIZE]); 39 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
40 updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); 40 updater.mark_updated(magic.as_mut()).unwrap();
41 led.set_low(); 41 led.set_low();
42 cortex_m::peripheral::SCB::sys_reset(); 42 cortex_m::peripheral::SCB::sys_reset();
43} 43}
diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs
index 202220223..1a54464d0 100644
--- a/examples/boot/application/stm32h7/src/bin/a.rs
+++ b/examples/boot/application/stm32h7/src/bin/a.rs
@@ -4,7 +4,7 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; 7use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig};
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::exti::ExtiInput; 9use embassy_stm32::exti::ExtiInput;
10use embassy_stm32::flash::{Flash, WRITE_SIZE}; 10use embassy_stm32::flash::{Flash, WRITE_SIZE};
@@ -16,7 +16,8 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
16#[embassy_executor::main] 16#[embassy_executor::main]
17async fn main(_spawner: Spawner) { 17async fn main(_spawner: Spawner) {
18 let p = embassy_stm32::init(Default::default()); 18 let p = embassy_stm32::init(Default::default());
19 let mut flash = Flash::new_blocking(p.FLASH); 19 let flash = Flash::new_blocking(p.FLASH);
20 let flash = Mutex::new(RefCell::new(flash));
20 21
21 let button = Input::new(p.PC13, Pull::Down); 22 let button = Input::new(p.PC13, Pull::Down);
22 let mut button = ExtiInput::new(button, p.EXTI13); 23 let mut button = ExtiInput::new(button, p.EXTI13);
@@ -24,21 +25,19 @@ async fn main(_spawner: Spawner) {
24 let mut led = Output::new(p.PB14, Level::Low, Speed::Low); 25 let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
25 led.set_high(); 26 led.set_high();
26 27
27 let mut updater = FirmwareUpdater::default(); 28 let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
28 29 let mut updater = BlockingFirmwareUpdater::new(config);
29 let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); 30 let mut writer = updater.prepare_update().unwrap();
30 button.wait_for_rising_edge().await; 31 button.wait_for_rising_edge().await;
31 let mut offset = 0; 32 let mut offset = 0;
32 let mut buf = AlignedBuffer([0; 4096]); 33 let mut buf = AlignedBuffer([0; 4096]);
33 for chunk in APP_B.chunks(4096) { 34 for chunk in APP_B.chunks(4096) {
34 buf.as_mut()[..chunk.len()].copy_from_slice(chunk); 35 buf.as_mut()[..chunk.len()].copy_from_slice(chunk);
35 writer 36 writer.write(offset, buf.as_ref()).unwrap();
36 .write_block_blocking(offset, buf.as_ref(), &mut flash, 4096)
37 .unwrap();
38 offset += chunk.len(); 37 offset += chunk.len();
39 } 38 }
40 let mut magic = AlignedBuffer([0; WRITE_SIZE]); 39 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
41 updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); 40 updater.mark_updated(magic.as_mut()).unwrap();
42 led.set_low(); 41 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset(); 42 cortex_m::peripheral::SCB::sys_reset();
44} 43}
diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs
index 4033ac590..00ddda636 100644
--- a/examples/boot/application/stm32l1/src/bin/a.rs
+++ b/examples/boot/application/stm32l1/src/bin/a.rs
@@ -4,7 +4,7 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; 7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};
8use embassy_embedded_hal::adapter::BlockingAsync; 8use embassy_embedded_hal::adapter::BlockingAsync;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::ExtiInput;
@@ -19,7 +19,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
19async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
20 let p = embassy_stm32::init(Default::default()); 20 let p = embassy_stm32::init(Default::default());
21 let flash = Flash::new_blocking(p.FLASH); 21 let flash = Flash::new_blocking(p.FLASH);
22 let mut flash = BlockingAsync::new(flash); 22 let flash = Mutex::new(BlockingAsync::new(flash));
23 23
24 let button = Input::new(p.PB2, Pull::Up); 24 let button = Input::new(p.PB2, Pull::Up);
25 let mut button = ExtiInput::new(button, p.EXTI2); 25 let mut button = ExtiInput::new(button, p.EXTI2);
@@ -28,18 +28,19 @@ async fn main(_spawner: Spawner) {
28 28
29 led.set_high(); 29 led.set_high();
30 30
31 let mut updater = FirmwareUpdater::default(); 31 let config = FirmwareUpdaterConfig::from_linkerfile(&flash);
32 let mut updater = FirmwareUpdater::new(config);
32 button.wait_for_falling_edge().await; 33 button.wait_for_falling_edge().await;
33 let mut offset = 0; 34 let mut offset = 0;
34 for chunk in APP_B.chunks(128) { 35 for chunk in APP_B.chunks(128) {
35 let mut buf: [u8; 128] = [0; 128]; 36 let mut buf: [u8; 128] = [0; 128];
36 buf[..chunk.len()].copy_from_slice(chunk); 37 buf[..chunk.len()].copy_from_slice(chunk);
37 updater.write_firmware(offset, &buf, &mut flash, 128).await.unwrap(); 38 updater.write_firmware(offset, &buf).await.unwrap();
38 offset += chunk.len(); 39 offset += chunk.len();
39 } 40 }
40 41
41 let mut magic = AlignedBuffer([0; WRITE_SIZE]); 42 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
42 updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); 43 updater.mark_updated(magic.as_mut()).await.unwrap();
43 led.set_low(); 44 led.set_low();
44 Timer::after(Duration::from_secs(1)).await; 45 Timer::after(Duration::from_secs(1)).await;
45 cortex_m::peripheral::SCB::sys_reset(); 46 cortex_m::peripheral::SCB::sys_reset();
diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs
index 141d82afd..54579e4ac 100644
--- a/examples/boot/application/stm32l4/src/bin/a.rs
+++ b/examples/boot/application/stm32l4/src/bin/a.rs
@@ -4,7 +4,7 @@
4 4
5#[cfg(feature = "defmt-rtt")] 5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt::*; 6use defmt_rtt::*;
7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; 7use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};
8use embassy_embedded_hal::adapter::BlockingAsync; 8use embassy_embedded_hal::adapter::BlockingAsync;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::exti::ExtiInput; 10use embassy_stm32::exti::ExtiInput;
@@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
18async fn main(_spawner: Spawner) { 18async fn main(_spawner: Spawner) {
19 let p = embassy_stm32::init(Default::default()); 19 let p = embassy_stm32::init(Default::default());
20 let flash = Flash::new_blocking(p.FLASH); 20 let flash = Flash::new_blocking(p.FLASH);
21 let mut flash = BlockingAsync::new(flash); 21 let flash = Mutex::new(BlockingAsync::new(flash));
22 22
23 let button = Input::new(p.PC13, Pull::Up); 23 let button = Input::new(p.PC13, Pull::Up);
24 let mut button = ExtiInput::new(button, p.EXTI13); 24 let mut button = ExtiInput::new(button, p.EXTI13);
@@ -26,13 +26,14 @@ async fn main(_spawner: Spawner) {
26 let mut led = Output::new(p.PB14, Level::Low, Speed::Low); 26 let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
27 led.set_high(); 27 led.set_high();
28 28
29 let mut updater = FirmwareUpdater::default(); 29 let config = FirmwareUpdaterConfig::from_linkerfile(&flash);
30 let mut updater = FirmwareUpdater::new(config);
30 button.wait_for_falling_edge().await; 31 button.wait_for_falling_edge().await;
31 let mut offset = 0; 32 let mut offset = 0;
32 for chunk in APP_B.chunks(2048) { 33 for chunk in APP_B.chunks(2048) {
33 let mut buf: [u8; 2048] = [0; 2048]; 34 let mut buf: [u8; 2048] = [0; 2048];
34 buf[..chunk.len()].copy_from_slice(chunk); 35 buf[..chunk.len()].copy_from_slice(chunk);
35 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); 36 updater.write_firmware(offset, &buf).await.unwrap();
36 offset += chunk.len(); 37 offset += chunk.len();
37 } 38 }
38 let mut magic = AlignedBuffer([0; WRITE_SIZE]); 39 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs
index 5f48dbe51..0c6fa05f9 100644
--- a/examples/boot/application/stm32wl/src/bin/a.rs
+++ b/examples/boot/application/stm32wl/src/bin/a.rs
@@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
18async fn main(_spawner: Spawner) { 18async fn main(_spawner: Spawner) {
19 let p = embassy_stm32::init(Default::default()); 19 let p = embassy_stm32::init(Default::default());
20 let flash = Flash::new_blocking(p.FLASH); 20 let flash = Flash::new_blocking(p.FLASH);
21 let mut flash = BlockingAsync::new(flash); 21 let mut flash = Mutex::new(BlockingAsync::new(flash));
22 22
23 let button = Input::new(p.PA0, Pull::Up); 23 let button = Input::new(p.PA0, Pull::Up);
24 let mut button = ExtiInput::new(button, p.EXTI0); 24 let mut button = ExtiInput::new(button, p.EXTI0);
@@ -26,7 +26,8 @@ async fn main(_spawner: Spawner) {
26 let mut led = Output::new(p.PB9, Level::Low, Speed::Low); 26 let mut led = Output::new(p.PB9, Level::Low, Speed::Low);
27 led.set_high(); 27 led.set_high();
28 28
29 let mut updater = FirmwareUpdater::default(); 29 let config = FirmwareUpdaterConfig::from_linkerfile(&flash);
30 let mut updater = FirmwareUpdater::new(config);
30 button.wait_for_falling_edge().await; 31 button.wait_for_falling_edge().await;
31 //defmt::info!("Starting update"); 32 //defmt::info!("Starting update");
32 let mut offset = 0; 33 let mut offset = 0;
@@ -34,11 +35,11 @@ async fn main(_spawner: Spawner) {
34 let mut buf: [u8; 2048] = [0; 2048]; 35 let mut buf: [u8; 2048] = [0; 2048];
35 buf[..chunk.len()].copy_from_slice(chunk); 36 buf[..chunk.len()].copy_from_slice(chunk);
36 // defmt::info!("Writing chunk at 0x{:x}", offset); 37 // defmt::info!("Writing chunk at 0x{:x}", offset);
37 updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); 38 updater.write_firmware(offset, &buf).await.unwrap();
38 offset += chunk.len(); 39 offset += chunk.len();
39 } 40 }
40 let mut magic = AlignedBuffer([0; WRITE_SIZE]); 41 let mut magic = AlignedBuffer([0; WRITE_SIZE]);
41 updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); 42 updater.mark_updated(magic.as_mut()).await.unwrap();
42 //defmt::info!("Marked as updated"); 43 //defmt::info!("Marked as updated");
43 led.set_low(); 44 led.set_low();
44 cortex_m::peripheral::SCB::sys_reset(); 45 cortex_m::peripheral::SCB::sys_reset();
diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml
index 8c2fb4c5f..40656f359 100644
--- a/examples/boot/bootloader/nrf/Cargo.toml
+++ b/examples/boot/bootloader/nrf/Cargo.toml
@@ -12,6 +12,7 @@ defmt-rtt = { version = "0.4", optional = true }
12embassy-nrf = { path = "../../../../embassy-nrf", features = ["nightly"] } 12embassy-nrf = { path = "../../../../embassy-nrf", features = ["nightly"] }
13embassy-boot-nrf = { path = "../../../../embassy-boot/nrf" } 13embassy-boot-nrf = { path = "../../../../embassy-boot/nrf" }
14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
15embassy-sync = { path = "../../../../embassy-sync" }
15cortex-m-rt = { version = "0.7" } 16cortex-m-rt = { version = "0.7" }
16cfg-if = "1.0.0" 17cfg-if = "1.0.0"
17 18
diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs
index 8818a23b8..72c95c02a 100644
--- a/examples/boot/bootloader/nrf/src/main.rs
+++ b/examples/boot/bootloader/nrf/src/main.rs
@@ -1,12 +1,15 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::cell::RefCell;
5
4use cortex_m_rt::{entry, exception}; 6use cortex_m_rt::{entry, exception};
5#[cfg(feature = "defmt")] 7#[cfg(feature = "defmt")]
6use defmt_rtt as _; 8use defmt_rtt as _;
7use embassy_boot_nrf::*; 9use embassy_boot_nrf::*;
8use embassy_nrf::nvmc::Nvmc; 10use embassy_nrf::nvmc::Nvmc;
9use embassy_nrf::wdt; 11use embassy_nrf::wdt;
12use embassy_sync::blocking_mutex::Mutex;
10 13
11#[entry] 14#[entry]
12fn main() -> ! { 15fn main() -> ! {
@@ -20,19 +23,21 @@ fn main() -> ! {
20 } 23 }
21 */ 24 */
22 25
23 let mut bl = BootLoader::default();
24
25 let mut wdt_config = wdt::Config::default(); 26 let mut wdt_config = wdt::Config::default();
26 wdt_config.timeout_ticks = 32768 * 5; // timeout seconds 27 wdt_config.timeout_ticks = 32768 * 5; // timeout seconds
27 wdt_config.run_during_sleep = true; 28 wdt_config.run_during_sleep = true;
28 wdt_config.run_during_debug_halt = false; 29 wdt_config.run_during_debug_halt = false;
29 30
30 let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::new(WatchdogFlash::start( 31 let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config);
31 Nvmc::new(p.NVMC), 32 let flash = Mutex::new(RefCell::new(flash));
32 p.WDT, 33
33 wdt_config, 34 let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
34 )))); 35 let active_offset = config.active.offset();
35 unsafe { bl.load(start) } 36 let mut bl: BootLoader<_, _, _> = BootLoader::new(config);
37
38 bl.prepare();
39
40 unsafe { bl.load(active_offset) }
36} 41}
37 42
38#[no_mangle] 43#[no_mangle]
diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml
index bf9226993..8d60f18be 100644
--- a/examples/boot/bootloader/rp/Cargo.toml
+++ b/examples/boot/bootloader/rp/Cargo.toml
@@ -11,6 +11,7 @@ defmt-rtt = { version = "0.4", optional = true }
11 11
12embassy-rp = { path = "../../../../embassy-rp", features = ["nightly"] } 12embassy-rp = { path = "../../../../embassy-rp", features = ["nightly"] }
13embassy-boot-rp = { path = "../../../../embassy-boot/rp" } 13embassy-boot-rp = { path = "../../../../embassy-boot/rp" }
14embassy-sync = { path = "../../../../embassy-sync" }
14embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } 15embassy-time = { path = "../../../../embassy-time", features = ["nightly"] }
15 16
16cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 17cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs
index 8129591fa..6a81db804 100644
--- a/examples/boot/bootloader/rp/src/main.rs
+++ b/examples/boot/bootloader/rp/src/main.rs
@@ -1,10 +1,13 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::cell::RefCell;
5
4use cortex_m_rt::{entry, exception}; 6use cortex_m_rt::{entry, exception};
5#[cfg(feature = "defmt")] 7#[cfg(feature = "defmt")]
6use defmt_rtt as _; 8use defmt_rtt as _;
7use embassy_boot_rp::*; 9use embassy_boot_rp::*;
10use embassy_sync::blocking_mutex::Mutex;
8use embassy_time::Duration; 11use embassy_time::Duration;
9 12
10const FLASH_SIZE: usize = 2 * 1024 * 1024; 13const FLASH_SIZE: usize = 2 * 1024 * 1024;
@@ -21,13 +24,16 @@ fn main() -> ! {
21 } 24 }
22 */ 25 */
23 26
24 let mut bl: BootLoader = BootLoader::default();
25 let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); 27 let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8));
26 let mut flash = BootFlash::new(flash); 28 let flash = Mutex::new(RefCell::new(flash));
27 let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); 29
28 core::mem::drop(flash); 30 let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
31 let active_offset = config.active.offset();
32 let mut bl: BootLoader<_, _, _> = BootLoader::new(config);
33
34 bl.prepare();
29 35
30 unsafe { bl.load(start) } 36 unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) }
31} 37}
32 38
33#[no_mangle] 39#[no_mangle]
diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml
index fbc80b34c..6436f2fee 100644
--- a/examples/boot/bootloader/stm32/Cargo.toml
+++ b/examples/boot/bootloader/stm32/Cargo.toml
@@ -12,6 +12,7 @@ defmt-rtt = { version = "0.4", optional = true }
12embassy-stm32 = { path = "../../../../embassy-stm32", features = ["nightly"] } 12embassy-stm32 = { path = "../../../../embassy-stm32", features = ["nightly"] }
13embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32" } 13embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32" }
14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
15embassy-sync = { path = "../../../../embassy-sync" }
15cortex-m-rt = { version = "0.7" } 16cortex-m-rt = { version = "0.7" }
16embedded-storage = "0.3.0" 17embedded-storage = "0.3.0"
17embedded-storage-async = "0.4.0" 18embedded-storage-async = "0.4.0"
diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs
index f81fdbc5f..262eed200 100644
--- a/examples/boot/bootloader/stm32/src/main.rs
+++ b/examples/boot/bootloader/stm32/src/main.rs
@@ -1,11 +1,14 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::cell::RefCell;
5
4use cortex_m_rt::{entry, exception}; 6use cortex_m_rt::{entry, exception};
5#[cfg(feature = "defmt")] 7#[cfg(feature = "defmt")]
6use defmt_rtt as _; 8use defmt_rtt as _;
7use embassy_boot_stm32::*; 9use embassy_boot_stm32::*;
8use embassy_stm32::flash::Flash; 10use embassy_stm32::flash::{Flash, BANK1_REGION};
11use embassy_sync::blocking_mutex::Mutex;
9 12
10#[entry] 13#[entry]
11fn main() -> ! { 14fn main() -> ! {
@@ -19,12 +22,16 @@ fn main() -> ! {
19 } 22 }
20 */ 23 */
21 24
22 let mut bl: BootLoader<2048> = BootLoader::default();
23 let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); 25 let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
24 let mut flash = BootFlash::new(layout.bank1_region); 26 let flash = Mutex::new(RefCell::new(layout.bank1_region));
25 let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); 27
26 core::mem::drop(flash); 28 let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
27 unsafe { bl.load(start) } 29 let active_offset = config.active.offset();
30 let mut bl: BootLoader<_, _, _, 2048> = BootLoader::new(config);
31
32 bl.prepare();
33
34 unsafe { bl.load(BANK1_REGION.base + active_offset) }
28} 35}
29 36
30#[no_mangle] 37#[no_mangle]