diff options
Diffstat (limited to 'embassy-boot/boot/src/boot_loader.rs')
| -rw-r--r-- | embassy-boot/boot/src/boot_loader.rs | 139 |
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. |
| 34 | pub trait Flash: NorFlash + ReadNorFlash { | 34 | pub 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. |
| 61 | pub struct BootLoader { | 61 | pub 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. |
