diff options
| author | Rasmus Melchior Jacobsen <[email protected]> | 2023-04-04 23:16:01 +0200 |
|---|---|---|
| committer | Rasmus Melchior Jacobsen <[email protected]> | 2023-04-04 23:16:01 +0200 |
| commit | 3deb65bc87b53d686694f0abcbbf96ff976d1f93 (patch) | |
| tree | 44f9c33e5d4a2377ad6305bca2d619ef80ce16dd /embassy-boot | |
| parent | bfebf7a43648e06b313234a2ddc7496eb526bc69 (diff) | |
| parent | 5923e143e35547b1972f2e48082e93dfbe1dadac (diff) | |
Merge branch 'master' into flash-regions
Diffstat (limited to 'embassy-boot')
| -rw-r--r-- | embassy-boot/boot/src/boot_loader.rs | 139 | ||||
| -rw-r--r-- | embassy-boot/boot/src/firmware_updater.rs | 225 | ||||
| -rw-r--r-- | embassy-boot/boot/src/firmware_writer.rs | 97 | ||||
| -rw-r--r-- | embassy-boot/boot/src/lib.rs | 166 | ||||
| -rw-r--r-- | embassy-boot/boot/src/mem_flash.rs | 156 | ||||
| -rw-r--r-- | embassy-boot/boot/src/partition.rs | 124 |
6 files changed, 468 insertions, 439 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. |
diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 2d8712277..2b5cc72fa 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; | 1 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; |
| 2 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; | 2 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; |
| 3 | 3 | ||
| 4 | use crate::{FirmwareWriter, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; | 4 | use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; |
| 5 | 5 | ||
| 6 | /// Errors returned by FirmwareUpdater | 6 | /// Errors returned by FirmwareUpdater |
| 7 | #[derive(Debug)] | 7 | #[derive(Debug)] |
| @@ -84,10 +84,10 @@ impl FirmwareUpdater { | |||
| 84 | /// `mark_booted`. | 84 | /// `mark_booted`. |
| 85 | pub async fn get_state<F: AsyncNorFlash>( | 85 | pub async fn get_state<F: AsyncNorFlash>( |
| 86 | &mut self, | 86 | &mut self, |
| 87 | flash: &mut F, | 87 | state_flash: &mut F, |
| 88 | aligned: &mut [u8], | 88 | aligned: &mut [u8], |
| 89 | ) -> Result<State, FirmwareUpdaterError> { | 89 | ) -> Result<State, FirmwareUpdaterError> { |
| 90 | flash.read(self.state.from as u32, aligned).await?; | 90 | self.state.read(state_flash, 0, aligned).await?; |
| 91 | 91 | ||
| 92 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { | 92 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { |
| 93 | Ok(State::Swap) | 93 | Ok(State::Swap) |
| @@ -115,17 +115,16 @@ impl FirmwareUpdater { | |||
| 115 | #[cfg(feature = "_verify")] | 115 | #[cfg(feature = "_verify")] |
| 116 | pub async fn verify_and_mark_updated<F: AsyncNorFlash>( | 116 | pub async fn verify_and_mark_updated<F: AsyncNorFlash>( |
| 117 | &mut self, | 117 | &mut self, |
| 118 | _flash: &mut F, | 118 | _state_and_dfu_flash: &mut F, |
| 119 | _public_key: &[u8], | 119 | _public_key: &[u8], |
| 120 | _signature: &[u8], | 120 | _signature: &[u8], |
| 121 | _update_len: usize, | 121 | _update_len: usize, |
| 122 | _aligned: &mut [u8], | 122 | _aligned: &mut [u8], |
| 123 | ) -> Result<(), FirmwareUpdaterError> { | 123 | ) -> Result<(), FirmwareUpdaterError> { |
| 124 | let _end = self.dfu.from + _update_len; | ||
| 125 | let _read_size = _aligned.len(); | 124 | let _read_size = _aligned.len(); |
| 126 | 125 | ||
| 127 | assert_eq!(_aligned.len(), F::WRITE_SIZE); | 126 | assert_eq!(_aligned.len(), F::WRITE_SIZE); |
| 128 | assert!(_end <= self.dfu.to); | 127 | assert!(_update_len <= self.dfu.len()); |
| 129 | 128 | ||
| 130 | #[cfg(feature = "ed25519-dalek")] | 129 | #[cfg(feature = "ed25519-dalek")] |
| 131 | { | 130 | { |
| @@ -137,21 +136,10 @@ impl FirmwareUpdater { | |||
| 137 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | 136 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; |
| 138 | 137 | ||
| 139 | let mut digest = Sha512::new(); | 138 | let mut digest = Sha512::new(); |
| 140 | 139 | for offset in (0.._update_len).step_by(_aligned.len()) { | |
| 141 | let mut offset = self.dfu.from; | 140 | self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; |
| 142 | let last_offset = _end / _read_size * _read_size; | 141 | let len = core::cmp::min(_update_len - offset, _aligned.len()); |
| 143 | 142 | digest.update(&_aligned[..len]); | |
| 144 | while offset < last_offset { | ||
| 145 | _flash.read(offset as u32, _aligned).await?; | ||
| 146 | digest.update(&_aligned); | ||
| 147 | offset += _read_size; | ||
| 148 | } | ||
| 149 | |||
| 150 | let remaining = _end % _read_size; | ||
| 151 | |||
| 152 | if remaining > 0 { | ||
| 153 | _flash.read(last_offset as u32, _aligned).await?; | ||
| 154 | digest.update(&_aligned[0..remaining]); | ||
| 155 | } | 143 | } |
| 156 | 144 | ||
| 157 | public_key | 145 | public_key |
| @@ -173,21 +161,10 @@ impl FirmwareUpdater { | |||
| 173 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | 161 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; |
| 174 | 162 | ||
| 175 | let mut digest = Sha512::new(); | 163 | let mut digest = Sha512::new(); |
| 176 | 164 | for offset in (0.._update_len).step_by(_aligned.len()) { | |
| 177 | let mut offset = self.dfu.from; | 165 | self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; |
| 178 | let last_offset = _end / _read_size * _read_size; | 166 | let len = core::cmp::min(_update_len - offset, _aligned.len()); |
| 179 | 167 | digest.update(&_aligned[..len]); | |
| 180 | while offset < last_offset { | ||
| 181 | _flash.read(offset as u32, _aligned).await?; | ||
| 182 | digest.update(&_aligned); | ||
| 183 | offset += _read_size; | ||
| 184 | } | ||
| 185 | |||
| 186 | let remaining = _end % _read_size; | ||
| 187 | |||
| 188 | if remaining > 0 { | ||
| 189 | _flash.read(last_offset as u32, _aligned).await?; | ||
| 190 | digest.update(&_aligned[0..remaining]); | ||
| 191 | } | 168 | } |
| 192 | 169 | ||
| 193 | let message = digest.finalize(); | 170 | let message = digest.finalize(); |
| @@ -202,7 +179,7 @@ impl FirmwareUpdater { | |||
| 202 | r.map_err(into_signature_error)? | 179 | r.map_err(into_signature_error)? |
| 203 | } | 180 | } |
| 204 | 181 | ||
| 205 | self.set_magic(_aligned, SWAP_MAGIC, _flash).await | 182 | self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await |
| 206 | } | 183 | } |
| 207 | 184 | ||
| 208 | /// Mark to trigger firmware swap on next boot. | 185 | /// Mark to trigger firmware swap on next boot. |
| @@ -213,11 +190,11 @@ impl FirmwareUpdater { | |||
| 213 | #[cfg(not(feature = "_verify"))] | 190 | #[cfg(not(feature = "_verify"))] |
| 214 | pub async fn mark_updated<F: AsyncNorFlash>( | 191 | pub async fn mark_updated<F: AsyncNorFlash>( |
| 215 | &mut self, | 192 | &mut self, |
| 216 | flash: &mut F, | 193 | state_flash: &mut F, |
| 217 | aligned: &mut [u8], | 194 | aligned: &mut [u8], |
| 218 | ) -> Result<(), FirmwareUpdaterError> { | 195 | ) -> Result<(), FirmwareUpdaterError> { |
| 219 | assert_eq!(aligned.len(), F::WRITE_SIZE); | 196 | assert_eq!(aligned.len(), F::WRITE_SIZE); |
| 220 | self.set_magic(aligned, SWAP_MAGIC, flash).await | 197 | self.set_magic(aligned, SWAP_MAGIC, state_flash).await |
| 221 | } | 198 | } |
| 222 | 199 | ||
| 223 | /// Mark firmware boot successful and stop rollback on reset. | 200 | /// Mark firmware boot successful and stop rollback on reset. |
| @@ -227,29 +204,42 @@ impl FirmwareUpdater { | |||
| 227 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. | 204 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. |
| 228 | pub async fn mark_booted<F: AsyncNorFlash>( | 205 | pub async fn mark_booted<F: AsyncNorFlash>( |
| 229 | &mut self, | 206 | &mut self, |
| 230 | flash: &mut F, | 207 | state_flash: &mut F, |
| 231 | aligned: &mut [u8], | 208 | aligned: &mut [u8], |
| 232 | ) -> Result<(), FirmwareUpdaterError> { | 209 | ) -> Result<(), FirmwareUpdaterError> { |
| 233 | assert_eq!(aligned.len(), F::WRITE_SIZE); | 210 | assert_eq!(aligned.len(), F::WRITE_SIZE); |
| 234 | self.set_magic(aligned, BOOT_MAGIC, flash).await | 211 | self.set_magic(aligned, BOOT_MAGIC, state_flash).await |
| 235 | } | 212 | } |
| 236 | 213 | ||
| 237 | async fn set_magic<F: AsyncNorFlash>( | 214 | async fn set_magic<F: AsyncNorFlash>( |
| 238 | &mut self, | 215 | &mut self, |
| 239 | aligned: &mut [u8], | 216 | aligned: &mut [u8], |
| 240 | magic: u8, | 217 | magic: u8, |
| 241 | flash: &mut F, | 218 | state_flash: &mut F, |
| 242 | ) -> Result<(), FirmwareUpdaterError> { | 219 | ) -> Result<(), FirmwareUpdaterError> { |
| 243 | flash.read(self.state.from as u32, aligned).await?; | 220 | self.state.read(state_flash, 0, aligned).await?; |
| 244 | 221 | ||
| 245 | if aligned.iter().any(|&b| b != magic) { | 222 | if aligned.iter().any(|&b| b != magic) { |
| 246 | aligned.fill(0); | 223 | // Read progress validity |
| 224 | self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; | ||
| 225 | |||
| 226 | // FIXME: Do not make this assumption. | ||
| 227 | const STATE_ERASE_VALUE: u8 = 0xFF; | ||
| 228 | |||
| 229 | if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { | ||
| 230 | // The current progress validity marker is invalid | ||
| 231 | } else { | ||
| 232 | // Invalidate progress | ||
| 233 | aligned.fill(!STATE_ERASE_VALUE); | ||
| 234 | self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; | ||
| 235 | } | ||
| 247 | 236 | ||
| 248 | flash.write(self.state.from as u32, aligned).await?; | 237 | // Clear magic and progress |
| 249 | flash.erase(self.state.from as u32, self.state.to as u32).await?; | 238 | self.state.wipe(state_flash).await?; |
| 250 | 239 | ||
| 240 | // Set magic | ||
| 251 | aligned.fill(magic); | 241 | aligned.fill(magic); |
| 252 | flash.write(self.state.from as u32, aligned).await?; | 242 | self.state.write(state_flash, 0, aligned).await?; |
| 253 | } | 243 | } |
| 254 | Ok(()) | 244 | Ok(()) |
| 255 | } | 245 | } |
| @@ -265,45 +255,31 @@ impl FirmwareUpdater { | |||
| 265 | &mut self, | 255 | &mut self, |
| 266 | offset: usize, | 256 | offset: usize, |
| 267 | data: &[u8], | 257 | data: &[u8], |
| 268 | flash: &mut F, | 258 | dfu_flash: &mut F, |
| 269 | block_size: usize, | ||
| 270 | ) -> Result<(), FirmwareUpdaterError> { | 259 | ) -> Result<(), FirmwareUpdaterError> { |
| 271 | assert!(data.len() >= F::ERASE_SIZE); | 260 | assert!(data.len() >= F::ERASE_SIZE); |
| 272 | 261 | ||
| 273 | flash | 262 | self.dfu |
| 274 | .erase( | 263 | .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) |
| 275 | (self.dfu.from + offset) as u32, | ||
| 276 | (self.dfu.from + offset + data.len()) as u32, | ||
| 277 | ) | ||
| 278 | .await?; | 264 | .await?; |
| 279 | 265 | ||
| 280 | trace!( | 266 | self.dfu.write(dfu_flash, offset as u32, data).await?; |
| 281 | "Erased from {} to {}", | ||
| 282 | self.dfu.from + offset, | ||
| 283 | self.dfu.from + offset + data.len() | ||
| 284 | ); | ||
| 285 | |||
| 286 | FirmwareWriter(self.dfu) | ||
| 287 | .write_block(offset, data, flash, block_size) | ||
| 288 | .await?; | ||
| 289 | 267 | ||
| 290 | Ok(()) | 268 | Ok(()) |
| 291 | } | 269 | } |
| 292 | 270 | ||
| 293 | /// Prepare for an incoming DFU update by erasing the entire DFU area and | 271 | /// Prepare for an incoming DFU update by erasing the entire DFU area and |
| 294 | /// returning a `FirmwareWriter`. | 272 | /// returning its `Partition`. |
| 295 | /// | 273 | /// |
| 296 | /// Using this instead of `write_firmware` allows for an optimized API in | 274 | /// Using this instead of `write_firmware` allows for an optimized API in |
| 297 | /// exchange for added complexity. | 275 | /// exchange for added complexity. |
| 298 | pub async fn prepare_update<F: AsyncNorFlash>( | 276 | pub async fn prepare_update<F: AsyncNorFlash>( |
| 299 | &mut self, | 277 | &mut self, |
| 300 | flash: &mut F, | 278 | dfu_flash: &mut F, |
| 301 | ) -> Result<FirmwareWriter, FirmwareUpdaterError> { | 279 | ) -> Result<Partition, FirmwareUpdaterError> { |
| 302 | flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; | 280 | self.dfu.wipe(dfu_flash).await?; |
| 303 | 281 | ||
| 304 | trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); | 282 | Ok(self.dfu) |
| 305 | |||
| 306 | Ok(FirmwareWriter(self.dfu)) | ||
| 307 | } | 283 | } |
| 308 | 284 | ||
| 309 | // | 285 | // |
| @@ -317,10 +293,10 @@ impl FirmwareUpdater { | |||
| 317 | /// `mark_booted`. | 293 | /// `mark_booted`. |
| 318 | pub fn get_state_blocking<F: NorFlash>( | 294 | pub fn get_state_blocking<F: NorFlash>( |
| 319 | &mut self, | 295 | &mut self, |
| 320 | flash: &mut F, | 296 | state_flash: &mut F, |
| 321 | aligned: &mut [u8], | 297 | aligned: &mut [u8], |
| 322 | ) -> Result<State, FirmwareUpdaterError> { | 298 | ) -> Result<State, FirmwareUpdaterError> { |
| 323 | flash.read(self.state.from as u32, aligned)?; | 299 | self.state.read_blocking(state_flash, 0, aligned)?; |
| 324 | 300 | ||
| 325 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { | 301 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { |
| 326 | Ok(State::Swap) | 302 | Ok(State::Swap) |
| @@ -348,7 +324,7 @@ impl FirmwareUpdater { | |||
| 348 | #[cfg(feature = "_verify")] | 324 | #[cfg(feature = "_verify")] |
| 349 | pub fn verify_and_mark_updated_blocking<F: NorFlash>( | 325 | pub fn verify_and_mark_updated_blocking<F: NorFlash>( |
| 350 | &mut self, | 326 | &mut self, |
| 351 | _flash: &mut F, | 327 | _state_and_dfu_flash: &mut F, |
| 352 | _public_key: &[u8], | 328 | _public_key: &[u8], |
| 353 | _signature: &[u8], | 329 | _signature: &[u8], |
| 354 | _update_len: usize, | 330 | _update_len: usize, |
| @@ -370,21 +346,10 @@ impl FirmwareUpdater { | |||
| 370 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | 346 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; |
| 371 | 347 | ||
| 372 | let mut digest = Sha512::new(); | 348 | let mut digest = Sha512::new(); |
| 373 | 349 | for offset in (0.._update_len).step_by(_aligned.len()) { | |
| 374 | let mut offset = self.dfu.from; | 350 | self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; |
| 375 | let last_offset = _end / _read_size * _read_size; | 351 | let len = core::cmp::min(_update_len - offset, _aligned.len()); |
| 376 | 352 | digest.update(&_aligned[..len]); | |
| 377 | while offset < last_offset { | ||
| 378 | _flash.read(offset as u32, _aligned)?; | ||
| 379 | digest.update(&_aligned); | ||
| 380 | offset += _read_size; | ||
| 381 | } | ||
| 382 | |||
| 383 | let remaining = _end % _read_size; | ||
| 384 | |||
| 385 | if remaining > 0 { | ||
| 386 | _flash.read(last_offset as u32, _aligned)?; | ||
| 387 | digest.update(&_aligned[0..remaining]); | ||
| 388 | } | 353 | } |
| 389 | 354 | ||
| 390 | public_key | 355 | public_key |
| @@ -406,21 +371,10 @@ impl FirmwareUpdater { | |||
| 406 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | 371 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; |
| 407 | 372 | ||
| 408 | let mut digest = Sha512::new(); | 373 | let mut digest = Sha512::new(); |
| 409 | 374 | for offset in (0.._update_len).step_by(_aligned.len()) { | |
| 410 | let mut offset = self.dfu.from; | 375 | self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; |
| 411 | let last_offset = _end / _read_size * _read_size; | 376 | let len = core::cmp::min(_update_len - offset, _aligned.len()); |
| 412 | 377 | digest.update(&_aligned[..len]); | |
| 413 | while offset < last_offset { | ||
| 414 | _flash.read(offset as u32, _aligned)?; | ||
| 415 | digest.update(&_aligned); | ||
| 416 | offset += _read_size; | ||
| 417 | } | ||
| 418 | |||
| 419 | let remaining = _end % _read_size; | ||
| 420 | |||
| 421 | if remaining > 0 { | ||
| 422 | _flash.read(last_offset as u32, _aligned)?; | ||
| 423 | digest.update(&_aligned[0..remaining]); | ||
| 424 | } | 378 | } |
| 425 | 379 | ||
| 426 | let message = digest.finalize(); | 380 | let message = digest.finalize(); |
| @@ -435,7 +389,7 @@ impl FirmwareUpdater { | |||
| 435 | r.map_err(into_signature_error)? | 389 | r.map_err(into_signature_error)? |
| 436 | } | 390 | } |
| 437 | 391 | ||
| 438 | self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) | 392 | self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) |
| 439 | } | 393 | } |
| 440 | 394 | ||
| 441 | /// Mark to trigger firmware swap on next boot. | 395 | /// Mark to trigger firmware swap on next boot. |
| @@ -446,11 +400,11 @@ impl FirmwareUpdater { | |||
| 446 | #[cfg(not(feature = "_verify"))] | 400 | #[cfg(not(feature = "_verify"))] |
| 447 | pub fn mark_updated_blocking<F: NorFlash>( | 401 | pub fn mark_updated_blocking<F: NorFlash>( |
| 448 | &mut self, | 402 | &mut self, |
| 449 | flash: &mut F, | 403 | state_flash: &mut F, |
| 450 | aligned: &mut [u8], | 404 | aligned: &mut [u8], |
| 451 | ) -> Result<(), FirmwareUpdaterError> { | 405 | ) -> Result<(), FirmwareUpdaterError> { |
| 452 | assert_eq!(aligned.len(), F::WRITE_SIZE); | 406 | assert_eq!(aligned.len(), F::WRITE_SIZE); |
| 453 | self.set_magic_blocking(aligned, SWAP_MAGIC, flash) | 407 | self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash) |
| 454 | } | 408 | } |
| 455 | 409 | ||
| 456 | /// Mark firmware boot successful and stop rollback on reset. | 410 | /// Mark firmware boot successful and stop rollback on reset. |
| @@ -460,29 +414,42 @@ impl FirmwareUpdater { | |||
| 460 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. | 414 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. |
| 461 | pub fn mark_booted_blocking<F: NorFlash>( | 415 | pub fn mark_booted_blocking<F: NorFlash>( |
| 462 | &mut self, | 416 | &mut self, |
| 463 | flash: &mut F, | 417 | state_flash: &mut F, |
| 464 | aligned: &mut [u8], | 418 | aligned: &mut [u8], |
| 465 | ) -> Result<(), FirmwareUpdaterError> { | 419 | ) -> Result<(), FirmwareUpdaterError> { |
| 466 | assert_eq!(aligned.len(), F::WRITE_SIZE); | 420 | assert_eq!(aligned.len(), F::WRITE_SIZE); |
| 467 | self.set_magic_blocking(aligned, BOOT_MAGIC, flash) | 421 | self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash) |
| 468 | } | 422 | } |
| 469 | 423 | ||
| 470 | fn set_magic_blocking<F: NorFlash>( | 424 | fn set_magic_blocking<F: NorFlash>( |
| 471 | &mut self, | 425 | &mut self, |
| 472 | aligned: &mut [u8], | 426 | aligned: &mut [u8], |
| 473 | magic: u8, | 427 | magic: u8, |
| 474 | flash: &mut F, | 428 | state_flash: &mut F, |
| 475 | ) -> Result<(), FirmwareUpdaterError> { | 429 | ) -> Result<(), FirmwareUpdaterError> { |
| 476 | flash.read(self.state.from as u32, aligned)?; | 430 | self.state.read_blocking(state_flash, 0, aligned)?; |
| 477 | 431 | ||
| 478 | if aligned.iter().any(|&b| b != magic) { | 432 | if aligned.iter().any(|&b| b != magic) { |
| 479 | aligned.fill(0); | 433 | // Read progress validity |
| 434 | self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; | ||
| 435 | |||
| 436 | // FIXME: Do not make this assumption. | ||
| 437 | const STATE_ERASE_VALUE: u8 = 0xFF; | ||
| 438 | |||
| 439 | if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { | ||
| 440 | // The current progress validity marker is invalid | ||
| 441 | } else { | ||
| 442 | // Invalidate progress | ||
| 443 | aligned.fill(!STATE_ERASE_VALUE); | ||
| 444 | self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; | ||
| 445 | } | ||
| 480 | 446 | ||
| 481 | flash.write(self.state.from as u32, aligned)?; | 447 | // Clear magic and progress |
| 482 | flash.erase(self.state.from as u32, self.state.to as u32)?; | 448 | self.state.wipe_blocking(state_flash)?; |
| 483 | 449 | ||
| 450 | // Set magic | ||
| 484 | aligned.fill(magic); | 451 | aligned.fill(magic); |
| 485 | flash.write(self.state.from as u32, aligned)?; | 452 | self.state.write_blocking(state_flash, 0, aligned)?; |
| 486 | } | 453 | } |
| 487 | Ok(()) | 454 | Ok(()) |
| 488 | } | 455 | } |
| @@ -498,40 +465,26 @@ impl FirmwareUpdater { | |||
| 498 | &mut self, | 465 | &mut self, |
| 499 | offset: usize, | 466 | offset: usize, |
| 500 | data: &[u8], | 467 | data: &[u8], |
| 501 | flash: &mut F, | 468 | dfu_flash: &mut F, |
| 502 | block_size: usize, | ||
| 503 | ) -> Result<(), FirmwareUpdaterError> { | 469 | ) -> Result<(), FirmwareUpdaterError> { |
| 504 | assert!(data.len() >= F::ERASE_SIZE); | 470 | assert!(data.len() >= F::ERASE_SIZE); |
| 505 | 471 | ||
| 506 | flash.erase( | 472 | self.dfu |
| 507 | (self.dfu.from + offset) as u32, | 473 | .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; |
| 508 | (self.dfu.from + offset + data.len()) as u32, | ||
| 509 | )?; | ||
| 510 | 474 | ||
| 511 | trace!( | 475 | self.dfu.write_blocking(dfu_flash, offset as u32, data)?; |
| 512 | "Erased from {} to {}", | ||
| 513 | self.dfu.from + offset, | ||
| 514 | self.dfu.from + offset + data.len() | ||
| 515 | ); | ||
| 516 | |||
| 517 | FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; | ||
| 518 | 476 | ||
| 519 | Ok(()) | 477 | Ok(()) |
| 520 | } | 478 | } |
| 521 | 479 | ||
| 522 | /// Prepare for an incoming DFU update by erasing the entire DFU area and | 480 | /// Prepare for an incoming DFU update by erasing the entire DFU area and |
| 523 | /// returning a `FirmwareWriter`. | 481 | /// returning its `Partition`. |
| 524 | /// | 482 | /// |
| 525 | /// Using this instead of `write_firmware_blocking` allows for an optimized | 483 | /// Using this instead of `write_firmware_blocking` allows for an optimized |
| 526 | /// API in exchange for added complexity. | 484 | /// API in exchange for added complexity. |
| 527 | pub fn prepare_update_blocking<F: NorFlash>( | 485 | pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<Partition, FirmwareUpdaterError> { |
| 528 | &mut self, | 486 | self.dfu.wipe_blocking(flash)?; |
| 529 | flash: &mut F, | ||
| 530 | ) -> Result<FirmwareWriter, FirmwareUpdaterError> { | ||
| 531 | flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; | ||
| 532 | |||
| 533 | trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); | ||
| 534 | 487 | ||
| 535 | Ok(FirmwareWriter(self.dfu)) | 488 | Ok(self.dfu) |
| 536 | } | 489 | } |
| 537 | } | 490 | } |
diff --git a/embassy-boot/boot/src/firmware_writer.rs b/embassy-boot/boot/src/firmware_writer.rs deleted file mode 100644 index f992021bb..000000000 --- a/embassy-boot/boot/src/firmware_writer.rs +++ /dev/null | |||
| @@ -1,97 +0,0 @@ | |||
| 1 | use embedded_storage::nor_flash::NorFlash; | ||
| 2 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; | ||
| 3 | |||
| 4 | use crate::Partition; | ||
| 5 | |||
| 6 | /// FirmwareWriter allows writing blocks to an already erased flash. | ||
| 7 | pub struct FirmwareWriter(pub(crate) Partition); | ||
| 8 | |||
| 9 | impl FirmwareWriter { | ||
| 10 | /// Write data to a flash page. | ||
| 11 | /// | ||
| 12 | /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. | ||
| 13 | /// | ||
| 14 | /// # Safety | ||
| 15 | /// | ||
| 16 | /// Failing to meet alignment and size requirements may result in a panic. | ||
| 17 | pub async fn write_block<F: AsyncNorFlash>( | ||
| 18 | &mut self, | ||
| 19 | offset: usize, | ||
| 20 | data: &[u8], | ||
| 21 | flash: &mut F, | ||
| 22 | block_size: usize, | ||
| 23 | ) -> Result<(), F::Error> { | ||
| 24 | trace!( | ||
| 25 | "Writing firmware at offset 0x{:x} len {}", | ||
| 26 | self.0.from + offset, | ||
| 27 | data.len() | ||
| 28 | ); | ||
| 29 | |||
| 30 | let mut write_offset = self.0.from + offset; | ||
| 31 | for chunk in data.chunks(block_size) { | ||
| 32 | trace!("Wrote chunk at {}: {:?}", write_offset, chunk); | ||
| 33 | flash.write(write_offset as u32, chunk).await?; | ||
| 34 | write_offset += chunk.len(); | ||
| 35 | } | ||
| 36 | /* | ||
| 37 | trace!("Wrote data, reading back for verification"); | ||
| 38 | |||
| 39 | let mut buf: [u8; 4096] = [0; 4096]; | ||
| 40 | let mut data_offset = 0; | ||
| 41 | let mut read_offset = self.dfu.from + offset; | ||
| 42 | for chunk in buf.chunks_mut(block_size) { | ||
| 43 | flash.read(read_offset as u32, chunk).await?; | ||
| 44 | trace!("Read chunk at {}: {:?}", read_offset, chunk); | ||
| 45 | assert_eq!(&data[data_offset..data_offset + block_size], chunk); | ||
| 46 | read_offset += chunk.len(); | ||
| 47 | data_offset += chunk.len(); | ||
| 48 | } | ||
| 49 | */ | ||
| 50 | |||
| 51 | Ok(()) | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Write data to a flash page. | ||
| 55 | /// | ||
| 56 | /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. | ||
| 57 | /// | ||
| 58 | /// # Safety | ||
| 59 | /// | ||
| 60 | /// Failing to meet alignment and size requirements may result in a panic. | ||
| 61 | pub fn write_block_blocking<F: NorFlash>( | ||
| 62 | &mut self, | ||
| 63 | offset: usize, | ||
| 64 | data: &[u8], | ||
| 65 | flash: &mut F, | ||
| 66 | block_size: usize, | ||
| 67 | ) -> Result<(), F::Error> { | ||
| 68 | trace!( | ||
| 69 | "Writing firmware at offset 0x{:x} len {}", | ||
| 70 | self.0.from + offset, | ||
| 71 | data.len() | ||
| 72 | ); | ||
| 73 | |||
| 74 | let mut write_offset = self.0.from + offset; | ||
| 75 | for chunk in data.chunks(block_size) { | ||
| 76 | trace!("Wrote chunk at {}: {:?}", write_offset, chunk); | ||
| 77 | flash.write(write_offset as u32, chunk)?; | ||
| 78 | write_offset += chunk.len(); | ||
| 79 | } | ||
| 80 | /* | ||
| 81 | trace!("Wrote data, reading back for verification"); | ||
| 82 | |||
| 83 | let mut buf: [u8; 4096] = [0; 4096]; | ||
| 84 | let mut data_offset = 0; | ||
| 85 | let mut read_offset = self.dfu.from + offset; | ||
| 86 | for chunk in buf.chunks_mut(block_size) { | ||
| 87 | flash.read(read_offset as u32, chunk).await?; | ||
| 88 | trace!("Read chunk at {}: {:?}", read_offset, chunk); | ||
| 89 | assert_eq!(&data[data_offset..data_offset + block_size], chunk); | ||
| 90 | read_offset += chunk.len(); | ||
| 91 | data_offset += chunk.len(); | ||
| 92 | } | ||
| 93 | */ | ||
| 94 | |||
| 95 | Ok(()) | ||
| 96 | } | ||
| 97 | } | ||
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index a2259411f..d53c613a3 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs | |||
| @@ -7,12 +7,11 @@ mod fmt; | |||
| 7 | 7 | ||
| 8 | mod boot_loader; | 8 | mod boot_loader; |
| 9 | mod firmware_updater; | 9 | mod firmware_updater; |
| 10 | mod firmware_writer; | 10 | mod mem_flash; |
| 11 | mod partition; | 11 | mod partition; |
| 12 | 12 | ||
| 13 | pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; | 13 | pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; |
| 14 | pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; | 14 | pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; |
| 15 | pub use firmware_writer::FirmwareWriter; | ||
| 16 | pub use partition::Partition; | 15 | pub use partition::Partition; |
| 17 | 16 | ||
| 18 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; | 17 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; |
| @@ -46,13 +45,10 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> { | |||
| 46 | 45 | ||
| 47 | #[cfg(test)] | 46 | #[cfg(test)] |
| 48 | mod tests { | 47 | mod tests { |
| 49 | use core::convert::Infallible; | ||
| 50 | |||
| 51 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; | ||
| 52 | use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; | ||
| 53 | use futures::executor::block_on; | 48 | use futures::executor::block_on; |
| 54 | 49 | ||
| 55 | use super::*; | 50 | use super::*; |
| 51 | use crate::mem_flash::MemFlash; | ||
| 56 | 52 | ||
| 57 | /* | 53 | /* |
| 58 | #[test] | 54 | #[test] |
| @@ -75,8 +71,8 @@ mod tests { | |||
| 75 | const ACTIVE: Partition = Partition::new(4096, 61440); | 71 | const ACTIVE: Partition = Partition::new(4096, 61440); |
| 76 | const DFU: Partition = Partition::new(61440, 122880); | 72 | const DFU: Partition = Partition::new(61440, 122880); |
| 77 | 73 | ||
| 78 | let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); | 74 | let mut flash = MemFlash::<131072, 4096, 4>::default(); |
| 79 | flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); | 75 | flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); |
| 80 | let mut flash = SingleFlashConfig::new(&mut flash); | 76 | let mut flash = SingleFlashConfig::new(&mut flash); |
| 81 | 77 | ||
| 82 | let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); | 78 | let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); |
| @@ -95,21 +91,21 @@ mod tests { | |||
| 95 | const STATE: Partition = Partition::new(0, 4096); | 91 | const STATE: Partition = Partition::new(0, 4096); |
| 96 | const ACTIVE: Partition = Partition::new(4096, 61440); | 92 | const ACTIVE: Partition = Partition::new(4096, 61440); |
| 97 | const DFU: Partition = Partition::new(61440, 122880); | 93 | const DFU: Partition = Partition::new(61440, 122880); |
| 98 | let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); | 94 | let mut flash = MemFlash::<131072, 4096, 4>::random(); |
| 99 | 95 | ||
| 100 | let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; | 96 | let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; |
| 101 | let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; | 97 | let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; |
| 102 | let mut aligned = [0; 4]; | 98 | let mut aligned = [0; 4]; |
| 103 | 99 | ||
| 104 | for i in ACTIVE.from..ACTIVE.to { | 100 | for i in ACTIVE.from..ACTIVE.to { |
| 105 | flash.0[i] = original[i - ACTIVE.from]; | 101 | flash.mem[i] = original[i - ACTIVE.from]; |
| 106 | } | 102 | } |
| 107 | 103 | ||
| 108 | let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); | 104 | let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); |
| 109 | let mut updater = FirmwareUpdater::new(DFU, STATE); | 105 | let mut updater = FirmwareUpdater::new(DFU, STATE); |
| 110 | let mut offset = 0; | 106 | let mut offset = 0; |
| 111 | for chunk in update.chunks(4096) { | 107 | for chunk in update.chunks(4096) { |
| 112 | block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap(); | 108 | block_on(updater.write_firmware(offset, chunk, &mut flash)).unwrap(); |
| 113 | offset += chunk.len(); | 109 | offset += chunk.len(); |
| 114 | } | 110 | } |
| 115 | block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); | 111 | block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); |
| @@ -124,12 +120,12 @@ mod tests { | |||
| 124 | ); | 120 | ); |
| 125 | 121 | ||
| 126 | for i in ACTIVE.from..ACTIVE.to { | 122 | for i in ACTIVE.from..ACTIVE.to { |
| 127 | assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i); | 123 | assert_eq!(flash.mem[i], update[i - ACTIVE.from], "Index {}", i); |
| 128 | } | 124 | } |
| 129 | 125 | ||
| 130 | // First DFU page is untouched | 126 | // First DFU page is untouched |
| 131 | for i in DFU.from + 4096..DFU.to { | 127 | for i in DFU.from + 4096..DFU.to { |
| 132 | assert_eq!(flash.0[i], original[i - DFU.from - 4096], "Index {}", i); | 128 | assert_eq!(flash.mem[i], original[i - DFU.from - 4096], "Index {}", i); |
| 133 | } | 129 | } |
| 134 | 130 | ||
| 135 | // Running again should cause a revert | 131 | // Running again should cause a revert |
| @@ -141,12 +137,12 @@ mod tests { | |||
| 141 | ); | 137 | ); |
| 142 | 138 | ||
| 143 | for i in ACTIVE.from..ACTIVE.to { | 139 | for i in ACTIVE.from..ACTIVE.to { |
| 144 | assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i); | 140 | assert_eq!(flash.mem[i], original[i - ACTIVE.from], "Index {}", i); |
| 145 | } | 141 | } |
| 146 | 142 | ||
| 147 | // Last page is untouched | 143 | // Last page is untouched |
| 148 | for i in DFU.from..DFU.to - 4096 { | 144 | for i in DFU.from..DFU.to - 4096 { |
| 149 | assert_eq!(flash.0[i], update[i - DFU.from], "Index {}", i); | 145 | assert_eq!(flash.mem[i], update[i - DFU.from], "Index {}", i); |
| 150 | } | 146 | } |
| 151 | 147 | ||
| 152 | // Mark as booted | 148 | // Mark as booted |
| @@ -166,23 +162,23 @@ mod tests { | |||
| 166 | const ACTIVE: Partition = Partition::new(4096, 16384); | 162 | const ACTIVE: Partition = Partition::new(4096, 16384); |
| 167 | const DFU: Partition = Partition::new(0, 16384); | 163 | const DFU: Partition = Partition::new(0, 16384); |
| 168 | 164 | ||
| 169 | let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); | 165 | let mut active = MemFlash::<16384, 4096, 8>::random(); |
| 170 | let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); | 166 | let mut dfu = MemFlash::<16384, 2048, 8>::random(); |
| 171 | let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); | 167 | let mut state = MemFlash::<4096, 128, 4>::random(); |
| 172 | let mut aligned = [0; 4]; | 168 | let mut aligned = [0; 4]; |
| 173 | 169 | ||
| 174 | let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; | 170 | let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; |
| 175 | let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; | 171 | let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; |
| 176 | 172 | ||
| 177 | for i in ACTIVE.from..ACTIVE.to { | 173 | for i in ACTIVE.from..ACTIVE.to { |
| 178 | active.0[i] = original[i - ACTIVE.from]; | 174 | active.mem[i] = original[i - ACTIVE.from]; |
| 179 | } | 175 | } |
| 180 | 176 | ||
| 181 | let mut updater = FirmwareUpdater::new(DFU, STATE); | 177 | let mut updater = FirmwareUpdater::new(DFU, STATE); |
| 182 | 178 | ||
| 183 | let mut offset = 0; | 179 | let mut offset = 0; |
| 184 | for chunk in update.chunks(2048) { | 180 | for chunk in update.chunks(2048) { |
| 185 | block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); | 181 | block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap(); |
| 186 | offset += chunk.len(); | 182 | offset += chunk.len(); |
| 187 | } | 183 | } |
| 188 | block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); | 184 | block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); |
| @@ -203,12 +199,12 @@ mod tests { | |||
| 203 | ); | 199 | ); |
| 204 | 200 | ||
| 205 | for i in ACTIVE.from..ACTIVE.to { | 201 | for i in ACTIVE.from..ACTIVE.to { |
| 206 | assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); | 202 | assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); |
| 207 | } | 203 | } |
| 208 | 204 | ||
| 209 | // First DFU page is untouched | 205 | // First DFU page is untouched |
| 210 | for i in DFU.from + 4096..DFU.to { | 206 | for i in DFU.from + 4096..DFU.to { |
| 211 | assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); | 207 | assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); |
| 212 | } | 208 | } |
| 213 | } | 209 | } |
| 214 | 210 | ||
| @@ -220,22 +216,22 @@ mod tests { | |||
| 220 | const DFU: Partition = Partition::new(0, 16384); | 216 | const DFU: Partition = Partition::new(0, 16384); |
| 221 | 217 | ||
| 222 | let mut aligned = [0; 4]; | 218 | let mut aligned = [0; 4]; |
| 223 | let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); | 219 | let mut active = MemFlash::<16384, 2048, 4>::random(); |
| 224 | let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); | 220 | let mut dfu = MemFlash::<16384, 4096, 8>::random(); |
| 225 | let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); | 221 | let mut state = MemFlash::<4096, 128, 4>::random(); |
| 226 | 222 | ||
| 227 | let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; | 223 | let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; |
| 228 | let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; | 224 | let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; |
| 229 | 225 | ||
| 230 | for i in ACTIVE.from..ACTIVE.to { | 226 | for i in ACTIVE.from..ACTIVE.to { |
| 231 | active.0[i] = original[i - ACTIVE.from]; | 227 | active.mem[i] = original[i - ACTIVE.from]; |
| 232 | } | 228 | } |
| 233 | 229 | ||
| 234 | let mut updater = FirmwareUpdater::new(DFU, STATE); | 230 | let mut updater = FirmwareUpdater::new(DFU, STATE); |
| 235 | 231 | ||
| 236 | let mut offset = 0; | 232 | let mut offset = 0; |
| 237 | for chunk in update.chunks(4096) { | 233 | for chunk in update.chunks(4096) { |
| 238 | block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); | 234 | block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap(); |
| 239 | offset += chunk.len(); | 235 | offset += chunk.len(); |
| 240 | } | 236 | } |
| 241 | block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); | 237 | block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); |
| @@ -255,12 +251,12 @@ mod tests { | |||
| 255 | ); | 251 | ); |
| 256 | 252 | ||
| 257 | for i in ACTIVE.from..ACTIVE.to { | 253 | for i in ACTIVE.from..ACTIVE.to { |
| 258 | assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); | 254 | assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); |
| 259 | } | 255 | } |
| 260 | 256 | ||
| 261 | // First DFU page is untouched | 257 | // First DFU page is untouched |
| 262 | for i in DFU.from + 4096..DFU.to { | 258 | for i in DFU.from + 4096..DFU.to { |
| 263 | assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); | 259 | assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); |
| 264 | } | 260 | } |
| 265 | } | 261 | } |
| 266 | 262 | ||
| @@ -290,13 +286,13 @@ mod tests { | |||
| 290 | 286 | ||
| 291 | const STATE: Partition = Partition::new(0, 4096); | 287 | const STATE: Partition = Partition::new(0, 4096); |
| 292 | const DFU: Partition = Partition::new(4096, 8192); | 288 | const DFU: Partition = Partition::new(4096, 8192); |
| 293 | let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); | 289 | let mut flash = MemFlash::<8192, 4096, 4>::default(); |
| 294 | 290 | ||
| 295 | let firmware_len = firmware.len(); | 291 | let firmware_len = firmware.len(); |
| 296 | 292 | ||
| 297 | let mut write_buf = [0; 4096]; | 293 | let mut write_buf = [0; 4096]; |
| 298 | write_buf[0..firmware_len].copy_from_slice(firmware); | 294 | write_buf[0..firmware_len].copy_from_slice(firmware); |
| 299 | NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); | 295 | DFU.write_blocking(&mut flash, 0, &write_buf).unwrap(); |
| 300 | 296 | ||
| 301 | // On with the test | 297 | // On with the test |
| 302 | 298 | ||
| @@ -313,112 +309,4 @@ mod tests { | |||
| 313 | )) | 309 | )) |
| 314 | .is_ok()); | 310 | .is_ok()); |
| 315 | } | 311 | } |
| 316 | struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]); | ||
| 317 | |||
| 318 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash | ||
| 319 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 320 | { | ||
| 321 | const WRITE_SIZE: usize = WRITE_SIZE; | ||
| 322 | const ERASE_SIZE: usize = ERASE_SIZE; | ||
| 323 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | ||
| 324 | let from = from as usize; | ||
| 325 | let to = to as usize; | ||
| 326 | assert!(from % ERASE_SIZE == 0); | ||
| 327 | assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); | ||
| 328 | for i in from..to { | ||
| 329 | self.0[i] = 0xFF; | ||
| 330 | } | ||
| 331 | Ok(()) | ||
| 332 | } | ||
| 333 | |||
| 334 | fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { | ||
| 335 | assert!(data.len() % WRITE_SIZE == 0); | ||
| 336 | assert!(offset as usize % WRITE_SIZE == 0); | ||
| 337 | assert!(offset as usize + data.len() <= SIZE); | ||
| 338 | |||
| 339 | self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data); | ||
| 340 | |||
| 341 | Ok(()) | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType | ||
| 346 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 347 | { | ||
| 348 | type Error = Infallible; | ||
| 349 | } | ||
| 350 | |||
| 351 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash | ||
| 352 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 353 | { | ||
| 354 | const READ_SIZE: usize = 1; | ||
| 355 | |||
| 356 | fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { | ||
| 357 | let len = buf.len(); | ||
| 358 | buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]); | ||
| 359 | Ok(()) | ||
| 360 | } | ||
| 361 | |||
| 362 | fn capacity(&self) -> usize { | ||
| 363 | SIZE | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> super::Flash | ||
| 368 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 369 | { | ||
| 370 | const BLOCK_SIZE: usize = ERASE_SIZE; | ||
| 371 | const ERASE_VALUE: u8 = 0xFF; | ||
| 372 | } | ||
| 373 | |||
| 374 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash | ||
| 375 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 376 | { | ||
| 377 | const READ_SIZE: usize = 1; | ||
| 378 | |||
| 379 | async fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { | ||
| 380 | let len = buf.len(); | ||
| 381 | buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]); | ||
| 382 | Ok(()) | ||
| 383 | } | ||
| 384 | |||
| 385 | fn capacity(&self) -> usize { | ||
| 386 | SIZE | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash | ||
| 391 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 392 | { | ||
| 393 | const WRITE_SIZE: usize = WRITE_SIZE; | ||
| 394 | const ERASE_SIZE: usize = ERASE_SIZE; | ||
| 395 | |||
| 396 | async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | ||
| 397 | let from = from as usize; | ||
| 398 | let to = to as usize; | ||
| 399 | assert!(from % ERASE_SIZE == 0); | ||
| 400 | assert!(to % ERASE_SIZE == 0); | ||
| 401 | for i in from..to { | ||
| 402 | self.0[i] = 0xFF; | ||
| 403 | } | ||
| 404 | Ok(()) | ||
| 405 | } | ||
| 406 | |||
| 407 | async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { | ||
| 408 | info!("Writing {} bytes to 0x{:x}", data.len(), offset); | ||
| 409 | assert!(data.len() % WRITE_SIZE == 0); | ||
| 410 | assert!(offset as usize % WRITE_SIZE == 0); | ||
| 411 | assert!( | ||
| 412 | offset as usize + data.len() <= SIZE, | ||
| 413 | "OFFSET: {}, LEN: {}, FLASH SIZE: {}", | ||
| 414 | offset, | ||
| 415 | data.len(), | ||
| 416 | SIZE | ||
| 417 | ); | ||
| 418 | |||
| 419 | self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data); | ||
| 420 | |||
| 421 | Ok(()) | ||
| 422 | } | ||
| 423 | } | ||
| 424 | } | 312 | } |
diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs new file mode 100644 index 000000000..828aad9d9 --- /dev/null +++ b/embassy-boot/boot/src/mem_flash.rs | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | #![allow(unused)] | ||
| 2 | |||
| 3 | use core::ops::{Bound, Range, RangeBounds}; | ||
| 4 | |||
| 5 | use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; | ||
| 6 | use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; | ||
| 7 | |||
| 8 | use crate::Flash; | ||
| 9 | |||
| 10 | pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> { | ||
| 11 | pub mem: [u8; SIZE], | ||
| 12 | pub pending_write_successes: Option<usize>, | ||
| 13 | } | ||
| 14 | |||
| 15 | #[derive(Debug)] | ||
| 16 | pub struct MemFlashError; | ||
| 17 | |||
| 18 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> { | ||
| 19 | pub const fn new(fill: u8) -> Self { | ||
| 20 | Self { | ||
| 21 | mem: [fill; SIZE], | ||
| 22 | pending_write_successes: None, | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | #[cfg(test)] | ||
| 27 | pub fn random() -> Self { | ||
| 28 | let mut mem = [0; SIZE]; | ||
| 29 | for byte in mem.iter_mut() { | ||
| 30 | *byte = rand::random::<u8>(); | ||
| 31 | } | ||
| 32 | Self { | ||
| 33 | mem, | ||
| 34 | pending_write_successes: None, | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Default | ||
| 40 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 41 | { | ||
| 42 | fn default() -> Self { | ||
| 43 | Self::new(0xFF) | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Flash | ||
| 48 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 49 | { | ||
| 50 | const BLOCK_SIZE: usize = ERASE_SIZE; | ||
| 51 | const ERASE_VALUE: u8 = 0xFF; | ||
| 52 | } | ||
| 53 | |||
| 54 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType | ||
| 55 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 56 | { | ||
| 57 | type Error = MemFlashError; | ||
| 58 | } | ||
| 59 | |||
| 60 | impl NorFlashError for MemFlashError { | ||
| 61 | fn kind(&self) -> NorFlashErrorKind { | ||
| 62 | NorFlashErrorKind::Other | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash | ||
| 67 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 68 | { | ||
| 69 | const READ_SIZE: usize = 1; | ||
| 70 | |||
| 71 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | ||
| 72 | let len = bytes.len(); | ||
| 73 | bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); | ||
| 74 | Ok(()) | ||
| 75 | } | ||
| 76 | |||
| 77 | fn capacity(&self) -> usize { | ||
| 78 | SIZE | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash | ||
| 83 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 84 | { | ||
| 85 | const WRITE_SIZE: usize = WRITE_SIZE; | ||
| 86 | const ERASE_SIZE: usize = ERASE_SIZE; | ||
| 87 | |||
| 88 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | ||
| 89 | let from = from as usize; | ||
| 90 | let to = to as usize; | ||
| 91 | assert!(from % ERASE_SIZE == 0); | ||
| 92 | assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); | ||
| 93 | for i in from..to { | ||
| 94 | self.mem[i] = 0xFF; | ||
| 95 | } | ||
| 96 | Ok(()) | ||
| 97 | } | ||
| 98 | |||
| 99 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { | ||
| 100 | let offset = offset as usize; | ||
| 101 | assert!(bytes.len() % WRITE_SIZE == 0); | ||
| 102 | assert!(offset % WRITE_SIZE == 0); | ||
| 103 | assert!(offset + bytes.len() <= SIZE); | ||
| 104 | |||
| 105 | if let Some(pending_successes) = self.pending_write_successes { | ||
| 106 | if pending_successes > 0 { | ||
| 107 | self.pending_write_successes = Some(pending_successes - 1); | ||
| 108 | } else { | ||
| 109 | return Err(MemFlashError); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | for ((offset, mem_byte), new_byte) in self | ||
| 114 | .mem | ||
| 115 | .iter_mut() | ||
| 116 | .enumerate() | ||
| 117 | .skip(offset) | ||
| 118 | .take(bytes.len()) | ||
| 119 | .zip(bytes) | ||
| 120 | { | ||
| 121 | assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); | ||
| 122 | *mem_byte = *new_byte; | ||
| 123 | } | ||
| 124 | |||
| 125 | Ok(()) | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash | ||
| 130 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 131 | { | ||
| 132 | const READ_SIZE: usize = 1; | ||
| 133 | |||
| 134 | async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | ||
| 135 | <Self as ReadNorFlash>::read(self, offset, bytes) | ||
| 136 | } | ||
| 137 | |||
| 138 | fn capacity(&self) -> usize { | ||
| 139 | <Self as ReadNorFlash>::capacity(self) | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash | ||
| 144 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||
| 145 | { | ||
| 146 | const WRITE_SIZE: usize = WRITE_SIZE; | ||
| 147 | const ERASE_SIZE: usize = ERASE_SIZE; | ||
| 148 | |||
| 149 | async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | ||
| 150 | <Self as NorFlash>::erase(self, from, to) | ||
| 151 | } | ||
| 152 | |||
| 153 | async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { | ||
| 154 | <Self as NorFlash>::write(self, offset, bytes) | ||
| 155 | } | ||
| 156 | } | ||
diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 46f80a23c..ac6b0ed0f 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs | |||
| @@ -1,10 +1,13 @@ | |||
| 1 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 2 | use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; | ||
| 3 | |||
| 1 | /// A region in flash used by the bootloader. | 4 | /// A region in flash used by the bootloader. |
| 2 | #[derive(Copy, Clone, Debug)] | 5 | #[derive(Copy, Clone, Debug)] |
| 3 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 6 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 4 | pub struct Partition { | 7 | pub struct Partition { |
| 5 | /// Start of the flash region. | 8 | /// The offset into the flash where the partition starts. |
| 6 | pub from: usize, | 9 | pub from: usize, |
| 7 | /// End of the flash region. | 10 | /// The offset into the flash where the partition ends. |
| 8 | pub to: usize, | 11 | pub to: usize, |
| 9 | } | 12 | } |
| 10 | 13 | ||
| @@ -14,9 +17,124 @@ impl Partition { | |||
| 14 | Self { from, to } | 17 | Self { from, to } |
| 15 | } | 18 | } |
| 16 | 19 | ||
| 17 | /// Return the length of the partition | 20 | /// Return the size of the partition |
| 18 | #[allow(clippy::len_without_is_empty)] | 21 | #[allow(clippy::len_without_is_empty)] |
| 19 | pub const fn len(&self) -> usize { | 22 | pub const fn len(&self) -> usize { |
| 20 | self.to - self.from | 23 | self.to - self.from |
| 21 | } | 24 | } |
| 25 | |||
| 26 | /// Read from the partition on the provided flash | ||
| 27 | pub async fn read<F: AsyncReadNorFlash>( | ||
| 28 | &self, | ||
| 29 | flash: &mut F, | ||
| 30 | offset: u32, | ||
| 31 | bytes: &mut [u8], | ||
| 32 | ) -> Result<(), F::Error> { | ||
| 33 | let offset = self.from as u32 + offset; | ||
| 34 | flash.read(offset, bytes).await | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Write to the partition on the provided flash | ||
| 38 | pub async fn write<F: AsyncNorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { | ||
| 39 | let offset = self.from as u32 + offset; | ||
| 40 | flash.write(offset, bytes).await?; | ||
| 41 | trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); | ||
| 42 | Ok(()) | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Erase part of the partition on the provided flash | ||
| 46 | pub async fn erase<F: AsyncNorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { | ||
| 47 | let from = self.from as u32 + from; | ||
| 48 | let to = self.from as u32 + to; | ||
| 49 | flash.erase(from, to).await?; | ||
| 50 | trace!("Erased from 0x{:x} to 0x{:x}", from, to); | ||
| 51 | Ok(()) | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Erase the entire partition | ||
| 55 | pub(crate) async fn wipe<F: AsyncNorFlash>(&self, flash: &mut F) -> Result<(), F::Error> { | ||
| 56 | let from = self.from as u32; | ||
| 57 | let to = self.to as u32; | ||
| 58 | flash.erase(from, to).await?; | ||
| 59 | trace!("Wiped from 0x{:x} to 0x{:x}", from, to); | ||
| 60 | Ok(()) | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Read from the partition on the provided flash | ||
| 64 | pub fn read_blocking<F: ReadNorFlash>(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> { | ||
| 65 | let offset = self.from as u32 + offset; | ||
| 66 | flash.read(offset, bytes) | ||
| 67 | } | ||
| 68 | |||
| 69 | /// Write to the partition on the provided flash | ||
| 70 | pub fn write_blocking<F: NorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { | ||
| 71 | let offset = self.from as u32 + offset; | ||
| 72 | flash.write(offset, bytes)?; | ||
| 73 | trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); | ||
| 74 | Ok(()) | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Erase part of the partition on the provided flash | ||
| 78 | pub fn erase_blocking<F: NorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { | ||
| 79 | let from = self.from as u32 + from; | ||
| 80 | let to = self.from as u32 + to; | ||
| 81 | flash.erase(from, to)?; | ||
| 82 | trace!("Erased from 0x{:x} to 0x{:x}", from, to); | ||
| 83 | Ok(()) | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Erase the entire partition | ||
| 87 | pub(crate) fn wipe_blocking<F: NorFlash>(&self, flash: &mut F) -> Result<(), F::Error> { | ||
| 88 | let from = self.from as u32; | ||
| 89 | let to = self.to as u32; | ||
| 90 | flash.erase(from, to)?; | ||
| 91 | trace!("Wiped from 0x{:x} to 0x{:x}", from, to); | ||
| 92 | Ok(()) | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | #[cfg(test)] | ||
| 97 | mod tests { | ||
| 98 | use crate::mem_flash::MemFlash; | ||
| 99 | use crate::Partition; | ||
| 100 | |||
| 101 | #[test] | ||
| 102 | fn can_erase() { | ||
| 103 | let mut flash = MemFlash::<1024, 64, 4>::new(0x00); | ||
| 104 | let partition = Partition::new(256, 512); | ||
| 105 | |||
| 106 | partition.erase_blocking(&mut flash, 64, 192).unwrap(); | ||
| 107 | |||
| 108 | for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) { | ||
| 109 | assert_eq!(0x00, byte, "Index {}", index); | ||
| 110 | } | ||
| 111 | |||
| 112 | for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) { | ||
| 113 | assert_eq!(0xFF, byte, "Index {}", index); | ||
| 114 | } | ||
| 115 | |||
| 116 | for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) { | ||
| 117 | assert_eq!(0x00, byte, "Index {}", index); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | #[test] | ||
| 122 | fn can_wipe() { | ||
| 123 | let mut flash = MemFlash::<1024, 64, 4>::new(0x00); | ||
| 124 | let partition = Partition::new(256, 512); | ||
| 125 | |||
| 126 | partition.wipe_blocking(&mut flash).unwrap(); | ||
| 127 | |||
| 128 | for (index, byte) in flash.mem.iter().copied().enumerate().take(256) { | ||
| 129 | assert_eq!(0x00, byte, "Index {}", index); | ||
| 130 | } | ||
| 131 | |||
| 132 | for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) { | ||
| 133 | assert_eq!(0xFF, byte, "Index {}", index); | ||
| 134 | } | ||
| 135 | |||
| 136 | for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) { | ||
| 137 | assert_eq!(0x00, byte, "Index {}", index); | ||
| 138 | } | ||
| 139 | } | ||
| 22 | } | 140 | } |
