aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot/boot/src/boot_loader.rs
diff options
context:
space:
mode:
authorRasmus Melchior Jacobsen <[email protected]>2023-04-04 23:16:01 +0200
committerRasmus Melchior Jacobsen <[email protected]>2023-04-04 23:16:01 +0200
commit3deb65bc87b53d686694f0abcbbf96ff976d1f93 (patch)
tree44f9c33e5d4a2377ad6305bca2d619ef80ce16dd /embassy-boot/boot/src/boot_loader.rs
parentbfebf7a43648e06b313234a2ddc7496eb526bc69 (diff)
parent5923e143e35547b1972f2e48082e93dfbe1dadac (diff)
Merge branch 'master' into flash-regions
Diffstat (limited to 'embassy-boot/boot/src/boot_loader.rs')
-rw-r--r--embassy-boot/boot/src/boot_loader.rs139
1 files changed, 75 insertions, 64 deletions
diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs
index ad6735112..9d047f778 100644
--- a/embassy-boot/boot/src/boot_loader.rs
+++ b/embassy-boot/boot/src/boot_loader.rs
@@ -31,7 +31,7 @@ where
31} 31}
32 32
33/// Extension of the embedded-storage flash type information with block size and erase value. 33/// Extension of the embedded-storage flash type information with block size and erase value.
34pub trait Flash: NorFlash + ReadNorFlash { 34pub trait Flash: NorFlash {
35 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase 35 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase
36 /// size of the flash, but for external QSPI flash modules, this can be lower. 36 /// size of the flash, but for external QSPI flash modules, this can be lower.
37 const BLOCK_SIZE: usize; 37 const BLOCK_SIZE: usize;
@@ -60,9 +60,11 @@ pub trait FlashConfig {
60/// different page sizes and flash write sizes. 60/// different page sizes and flash write sizes.
61pub struct BootLoader { 61pub struct BootLoader {
62 // Page with current state of bootloader. The state partition has the following format: 62 // Page with current state of bootloader. The state partition has the following format:
63 // | Range | Description | 63 // All ranges are in multiples of WRITE_SIZE bytes.
64 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 64 // | Range | Description |
65 // | WRITE_SIZE - N | Progress index used while swapping or reverting | 65 // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
66 // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. |
67 // | 2..2 + N | Progress index used while swapping or reverting |
66 state: Partition, 68 state: Partition,
67 // Location of the partition which will be booted from 69 // Location of the partition which will be booted from
68 active: Partition, 70 active: Partition,
@@ -79,7 +81,7 @@ impl BootLoader {
79 Self { active, dfu, state } 81 Self { active, dfu, state }
80 } 82 }
81 83
82 /// Return the boot address for the active partition. 84 /// Return the offset of the active partition into the active flash.
83 pub fn boot_address(&self) -> usize { 85 pub fn boot_address(&self) -> usize {
84 self.active.from 86 self.active.from
85 } 87 }
@@ -192,14 +194,19 @@ impl BootLoader {
192 trace!("Reverting"); 194 trace!("Reverting");
193 self.revert(p, magic, page)?; 195 self.revert(p, magic, page)?;
194 196
195 // Overwrite magic and reset progress 197 let state_flash = p.state();
196 let fstate = p.state(); 198
199 // Invalidate progress
197 magic.fill(!P::STATE::ERASE_VALUE); 200 magic.fill(!P::STATE::ERASE_VALUE);
198 fstate.write(self.state.from as u32, magic)?; 201 self.state
199 fstate.erase(self.state.from as u32, self.state.to as u32)?; 202 .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, magic)?;
203
204 // Clear magic and progress
205 self.state.wipe_blocking(state_flash)?;
200 206
207 // Set magic
201 magic.fill(BOOT_MAGIC); 208 magic.fill(BOOT_MAGIC);
202 fstate.write(self.state.from as u32, magic)?; 209 self.state.write_blocking(state_flash, 0, magic)?;
203 } 210 }
204 } 211 }
205 Ok(state) 212 Ok(state)
@@ -215,62 +222,61 @@ impl BootLoader {
215 222
216 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> { 223 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> {
217 let write_size = aligned.len(); 224 let write_size = aligned.len();
218 let max_index = ((self.state.len() - write_size) / write_size) - 1; 225 let max_index = ((self.state.len() - write_size) / write_size) - 2;
219 aligned.fill(!P::STATE::ERASE_VALUE); 226 aligned.fill(!P::STATE::ERASE_VALUE);
220 227
221 let flash = config.state(); 228 let state_flash = config.state();
222 for i in 0..max_index { 229
223 flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; 230 self.state
231 .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, aligned)?;
232 if aligned.iter().any(|&b| b != P::STATE::ERASE_VALUE) {
233 // Progress is invalid
234 return Ok(max_index);
235 }
236
237 for index in 0..max_index {
238 self.state
239 .read_blocking(state_flash, (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?;
224 240
225 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { 241 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) {
226 return Ok(i); 242 return Ok(index);
227 } 243 }
228 } 244 }
229 Ok(max_index) 245 Ok(max_index)
230 } 246 }
231 247
232 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { 248 fn update_progress<P: FlashConfig>(&mut self, index: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> {
233 let flash = p.state();
234 let write_size = magic.len();
235 let w = self.state.from + write_size + idx * write_size;
236
237 let aligned = magic; 249 let aligned = magic;
238 aligned.fill(!P::STATE::ERASE_VALUE); 250 aligned.fill(!P::STATE::ERASE_VALUE);
239 flash.write(w as u32, aligned)?; 251 self.state
252 .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?;
240 Ok(()) 253 Ok(())
241 } 254 }
242 255
243 fn active_addr(&self, n: usize, page_size: usize) -> usize {
244 self.active.from + n * page_size
245 }
246
247 fn dfu_addr(&self, n: usize, page_size: usize) -> usize {
248 self.dfu.from + n * page_size
249 }
250
251 fn copy_page_once_to_active<P: FlashConfig>( 256 fn copy_page_once_to_active<P: FlashConfig>(
252 &mut self, 257 &mut self,
253 idx: usize, 258 idx: usize,
254 from_page: usize, 259 from_offset: u32,
255 to_page: usize, 260 to_offset: u32,
256 p: &mut P, 261 p: &mut P,
257 magic: &mut [u8], 262 magic: &mut [u8],
258 page: &mut [u8], 263 page: &mut [u8],
259 ) -> Result<(), BootError> { 264 ) -> Result<(), BootError> {
260 let buf = page; 265 let buf = page;
261 if self.current_progress(p, magic)? <= idx { 266 if self.current_progress(p, magic)? <= idx {
262 let mut offset = from_page; 267 let mut offset = from_offset;
263 for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { 268 for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) {
264 p.dfu().read(offset as u32, chunk)?; 269 self.dfu.read_blocking(p.dfu(), offset, chunk)?;
265 offset += chunk.len(); 270 offset += chunk.len() as u32;
266 } 271 }
267 272
268 p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; 273 self.active
274 .erase_blocking(p.active(), to_offset, to_offset + buf.len() as u32)?;
269 275
270 let mut offset = to_page; 276 let mut offset = to_offset;
271 for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { 277 for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) {
272 p.active().write(offset as u32, chunk)?; 278 self.active.write_blocking(p.active(), offset, chunk)?;
273 offset += chunk.len(); 279 offset += chunk.len() as u32;
274 } 280 }
275 self.update_progress(idx, p, magic)?; 281 self.update_progress(idx, p, magic)?;
276 } 282 }
@@ -280,26 +286,27 @@ impl BootLoader {
280 fn copy_page_once_to_dfu<P: FlashConfig>( 286 fn copy_page_once_to_dfu<P: FlashConfig>(
281 &mut self, 287 &mut self,
282 idx: usize, 288 idx: usize,
283 from_page: usize, 289 from_offset: u32,
284 to_page: usize, 290 to_offset: u32,
285 p: &mut P, 291 p: &mut P,
286 magic: &mut [u8], 292 magic: &mut [u8],
287 page: &mut [u8], 293 page: &mut [u8],
288 ) -> Result<(), BootError> { 294 ) -> Result<(), BootError> {
289 let buf = page; 295 let buf = page;
290 if self.current_progress(p, magic)? <= idx { 296 if self.current_progress(p, magic)? <= idx {
291 let mut offset = from_page; 297 let mut offset = from_offset;
292 for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { 298 for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) {
293 p.active().read(offset as u32, chunk)?; 299 self.active.read_blocking(p.active(), offset, chunk)?;
294 offset += chunk.len(); 300 offset += chunk.len() as u32;
295 } 301 }
296 302
297 p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; 303 self.dfu
304 .erase_blocking(p.dfu(), to_offset as u32, to_offset + buf.len() as u32)?;
298 305
299 let mut offset = to_page; 306 let mut offset = to_offset;
300 for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { 307 for chunk in buf.chunks(P::DFU::BLOCK_SIZE) {
301 p.dfu().write(offset as u32, chunk)?; 308 self.dfu.write_blocking(p.dfu(), offset, chunk)?;
302 offset += chunk.len(); 309 offset += chunk.len() as u32;
303 } 310 }
304 self.update_progress(idx, p, magic)?; 311 self.update_progress(idx, p, magic)?;
305 } 312 }
@@ -312,17 +319,20 @@ impl BootLoader {
312 trace!("Page count: {}", page_count); 319 trace!("Page count: {}", page_count);
313 for page_num in 0..page_count { 320 for page_num in 0..page_count {
314 trace!("COPY PAGE {}", page_num); 321 trace!("COPY PAGE {}", page_num);
322
323 let idx = page_num * 2;
324
315 // Copy active page to the 'next' DFU page. 325 // Copy active page to the 'next' DFU page.
316 let active_page = self.active_addr(page_count - 1 - page_num, page_size); 326 let active_from_offset = ((page_count - 1 - page_num) * page_size) as u32;
317 let dfu_page = self.dfu_addr(page_count - page_num, page_size); 327 let dfu_to_offset = ((page_count - page_num) * page_size) as u32;
318 //trace!("Copy active {} to dfu {}", active_page, dfu_page); 328 //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset);
319 self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; 329 self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?;
320 330
321 // Copy DFU page to the active page 331 // Copy DFU page to the active page
322 let active_page = self.active_addr(page_count - 1 - page_num, page_size); 332 let active_to_offset = ((page_count - 1 - page_num) * page_size) as u32;
323 let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); 333 let dfu_from_offset = ((page_count - 1 - page_num) * page_size) as u32;
324 //trace!("Copy dfy {} to active {}", dfu_page, active_page); 334 //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset);
325 self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; 335 self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?;
326 } 336 }
327 337
328 Ok(()) 338 Ok(())
@@ -332,23 +342,24 @@ impl BootLoader {
332 let page_size = page.len(); 342 let page_size = page.len();
333 let page_count = self.active.len() / page_size; 343 let page_count = self.active.len() / page_size;
334 for page_num in 0..page_count { 344 for page_num in 0..page_count {
345 let idx = page_count * 2 + page_num * 2;
346
335 // Copy the bad active page to the DFU page 347 // Copy the bad active page to the DFU page
336 let active_page = self.active_addr(page_num, page_size); 348 let active_from_offset = (page_num * page_size) as u32;
337 let dfu_page = self.dfu_addr(page_num, page_size); 349 let dfu_to_offset = (page_num * page_size) as u32;
338 self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; 350 self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?;
339 351
340 // Copy the DFU page back to the active page 352 // Copy the DFU page back to the active page
341 let active_page = self.active_addr(page_num, page_size); 353 let active_to_offset = (page_num * page_size) as u32;
342 let dfu_page = self.dfu_addr(page_num + 1, page_size); 354 let dfu_from_offset = ((page_num + 1) * page_size) as u32;
343 self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; 355 self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?;
344 } 356 }
345 357
346 Ok(()) 358 Ok(())
347 } 359 }
348 360
349 fn read_state<P: FlashConfig>(&mut self, config: &mut P, magic: &mut [u8]) -> Result<State, BootError> { 361 fn read_state<P: FlashConfig>(&mut self, config: &mut P, magic: &mut [u8]) -> Result<State, BootError> {
350 let flash = config.state(); 362 self.state.read_blocking(config.state(), 0, magic)?;
351 flash.read(self.state.from as u32, magic)?;
352 363
353 if !magic.iter().any(|&b| b != SWAP_MAGIC) { 364 if !magic.iter().any(|&b| b != SWAP_MAGIC) {
354 Ok(State::Swap) 365 Ok(State::Swap)
@@ -362,7 +373,7 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s
362 assert_eq!(active.len() % page_size, 0); 373 assert_eq!(active.len() % page_size, 0);
363 assert_eq!(dfu.len() % page_size, 0); 374 assert_eq!(dfu.len() % page_size, 0);
364 assert!(dfu.len() - active.len() >= page_size); 375 assert!(dfu.len() - active.len() >= page_size);
365 assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); 376 assert!(2 + 2 * (active.len() / page_size) <= state.len() / write_size);
366} 377}
367 378
368/// A flash wrapper implementing the Flash and embedded_storage traits. 379/// A flash wrapper implementing the Flash and embedded_storage traits.