diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-02-14 09:35:41 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-02-14 09:35:41 +0100 |
| commit | 63d592c7b0d1115b57abfd4e8ed918059749b8ff (patch) | |
| tree | 86fe975042bcbd6a965a3891aa5a740e0e0881d0 /embassy-boot/src | |
| parent | 50b621694a21f692d7f7ac2822a70df6483c933f (diff) | |
| parent | 7dd974aa0dd88ad319971b81083f63a190d278bf (diff) | |
Merge pull request #2543 from badrbouslikhin/usb-dfu-erase-then-write
feat(boot): enhance firmware write functionality
Diffstat (limited to 'embassy-boot/src')
| -rw-r--r-- | embassy-boot/src/firmware_updater/asynch.rs | 139 | ||||
| -rw-r--r-- | embassy-boot/src/firmware_updater/blocking.rs | 144 |
2 files changed, 266 insertions, 17 deletions
diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index a7c360a35..26f65f295 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs | |||
| @@ -13,6 +13,7 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA | |||
| 13 | pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { | 13 | pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { |
| 14 | dfu: DFU, | 14 | dfu: DFU, |
| 15 | state: FirmwareState<'d, STATE>, | 15 | state: FirmwareState<'d, STATE>, |
| 16 | last_erased_dfu_sector_index: Option<usize>, | ||
| 16 | } | 17 | } |
| 17 | 18 | ||
| 18 | #[cfg(target_os = "none")] | 19 | #[cfg(target_os = "none")] |
| @@ -56,6 +57,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 56 | Self { | 57 | Self { |
| 57 | dfu: config.dfu, | 58 | dfu: config.dfu, |
| 58 | state: FirmwareState::new(config.state, aligned), | 59 | state: FirmwareState::new(config.state, aligned), |
| 60 | last_erased_dfu_sector_index: None, | ||
| 59 | } | 61 | } |
| 60 | } | 62 | } |
| 61 | 63 | ||
| @@ -72,7 +74,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 72 | /// proceed with updating the firmware as it must be signed with a | 74 | /// proceed with updating the firmware as it must be signed with a |
| 73 | /// corresponding private key (otherwise it could be malicious firmware). | 75 | /// corresponding private key (otherwise it could be malicious firmware). |
| 74 | /// | 76 | /// |
| 75 | /// Mark to trigger firmware swap on next boot if verify suceeds. | 77 | /// Mark to trigger firmware swap on next boot if verify succeeds. |
| 76 | /// | 78 | /// |
| 77 | /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have | 79 | /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have |
| 78 | /// been generated from a SHA-512 digest of the firmware bytes. | 80 | /// been generated from a SHA-512 digest of the firmware bytes. |
| @@ -172,21 +174,68 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 172 | self.state.mark_booted().await | 174 | self.state.mark_booted().await |
| 173 | } | 175 | } |
| 174 | 176 | ||
| 175 | /// Write data to a flash page. | 177 | /// Writes firmware data to the device. |
| 176 | /// | 178 | /// |
| 177 | /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. | 179 | /// This function writes the given data to the firmware area starting at the specified offset. |
| 180 | /// It handles sector erasures and data writes while verifying the device is in a proper state | ||
| 181 | /// for firmware updates. The function ensures that only unerased sectors are erased before | ||
| 182 | /// writing and efficiently handles the writing process across sector boundaries and in | ||
| 183 | /// various configurations (data size, sector size, etc.). | ||
| 178 | /// | 184 | /// |
| 179 | /// # Safety | 185 | /// # Arguments |
| 186 | /// | ||
| 187 | /// * `offset` - The starting offset within the firmware area where data writing should begin. | ||
| 188 | /// * `data` - A slice of bytes representing the firmware data to be written. It must be a | ||
| 189 | /// multiple of NorFlash WRITE_SIZE. | ||
| 190 | /// | ||
| 191 | /// # Returns | ||
| 192 | /// | ||
| 193 | /// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation. | ||
| 194 | /// | ||
| 195 | /// # Errors | ||
| 180 | /// | 196 | /// |
| 181 | /// Failing to meet alignment and size requirements may result in a panic. | 197 | /// This function will return an error if: |
| 198 | /// | ||
| 199 | /// - The device is not in a proper state to receive firmware updates (e.g., not booted). | ||
| 200 | /// - There is a failure erasing a sector before writing. | ||
| 201 | /// - There is a failure writing data to the device. | ||
| 182 | pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { | 202 | pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { |
| 183 | assert!(data.len() >= DFU::ERASE_SIZE); | 203 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 184 | |||
| 185 | self.state.verify_booted().await?; | 204 | self.state.verify_booted().await?; |
| 186 | 205 | ||
| 187 | self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; | 206 | // Initialize variables to keep track of the remaining data and the current offset. |
| 207 | let mut remaining_data = data; | ||
| 208 | let mut offset = offset; | ||
| 209 | |||
| 210 | // Continue writing as long as there is data left to write. | ||
| 211 | while !remaining_data.is_empty() { | ||
| 212 | // Compute the current sector and its boundaries. | ||
| 213 | let current_sector = offset / DFU::ERASE_SIZE; | ||
| 214 | let sector_start = current_sector * DFU::ERASE_SIZE; | ||
| 215 | let sector_end = sector_start + DFU::ERASE_SIZE; | ||
| 216 | // Determine if the current sector needs to be erased before writing. | ||
| 217 | let need_erase = self | ||
| 218 | .last_erased_dfu_sector_index | ||
| 219 | .map_or(true, |last_erased_sector| current_sector != last_erased_sector); | ||
| 220 | |||
| 221 | // If the sector needs to be erased, erase it and update the last erased sector index. | ||
| 222 | if need_erase { | ||
| 223 | self.dfu.erase(sector_start as u32, sector_end as u32).await?; | ||
| 224 | self.last_erased_dfu_sector_index = Some(current_sector); | ||
| 225 | } | ||
| 226 | |||
| 227 | // Calculate the size of the data chunk that can be written in the current iteration. | ||
| 228 | let write_size = core::cmp::min(remaining_data.len(), sector_end - offset); | ||
| 229 | // Split the data to get the current chunk to be written and the remaining data. | ||
| 230 | let (data_chunk, rest) = remaining_data.split_at(write_size); | ||
| 188 | 231 | ||
| 189 | self.dfu.write(offset as u32, data).await?; | 232 | // Write the current data chunk. |
| 233 | self.dfu.write(offset as u32, data_chunk).await?; | ||
| 234 | |||
| 235 | // Update the offset and remaining data for the next iteration. | ||
| 236 | remaining_data = rest; | ||
| 237 | offset += write_size; | ||
| 238 | } | ||
| 190 | 239 | ||
| 191 | Ok(()) | 240 | Ok(()) |
| 192 | } | 241 | } |
| @@ -338,4 +387,76 @@ mod tests { | |||
| 338 | 387 | ||
| 339 | assert_eq!(Sha1::digest(update).as_slice(), hash); | 388 | assert_eq!(Sha1::digest(update).as_slice(), hash); |
| 340 | } | 389 | } |
| 390 | |||
| 391 | #[test] | ||
| 392 | fn can_verify_sha1_sector_bigger_than_chunk() { | ||
| 393 | let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default()); | ||
| 394 | let state = Partition::new(&flash, 0, 4096); | ||
| 395 | let dfu = Partition::new(&flash, 65536, 65536); | ||
| 396 | let mut aligned = [0; 8]; | ||
| 397 | |||
| 398 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | ||
| 399 | let mut to_write = [0; 4096]; | ||
| 400 | to_write[..7].copy_from_slice(update.as_slice()); | ||
| 401 | |||
| 402 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); | ||
| 403 | let mut offset = 0; | ||
| 404 | for chunk in to_write.chunks(1024) { | ||
| 405 | block_on(updater.write_firmware(offset, chunk)).unwrap(); | ||
| 406 | offset += chunk.len(); | ||
| 407 | } | ||
| 408 | let mut chunk_buf = [0; 2]; | ||
| 409 | let mut hash = [0; 20]; | ||
| 410 | block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); | ||
| 411 | |||
| 412 | assert_eq!(Sha1::digest(update).as_slice(), hash); | ||
| 413 | } | ||
| 414 | |||
| 415 | #[test] | ||
| 416 | fn can_verify_sha1_sector_smaller_than_chunk() { | ||
| 417 | let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default()); | ||
| 418 | let state = Partition::new(&flash, 0, 4096); | ||
| 419 | let dfu = Partition::new(&flash, 65536, 65536); | ||
| 420 | let mut aligned = [0; 8]; | ||
| 421 | |||
| 422 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | ||
| 423 | let mut to_write = [0; 4096]; | ||
| 424 | to_write[..7].copy_from_slice(update.as_slice()); | ||
| 425 | |||
| 426 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); | ||
| 427 | let mut offset = 0; | ||
| 428 | for chunk in to_write.chunks(2048) { | ||
| 429 | block_on(updater.write_firmware(offset, chunk)).unwrap(); | ||
| 430 | offset += chunk.len(); | ||
| 431 | } | ||
| 432 | let mut chunk_buf = [0; 2]; | ||
| 433 | let mut hash = [0; 20]; | ||
| 434 | block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); | ||
| 435 | |||
| 436 | assert_eq!(Sha1::digest(update).as_slice(), hash); | ||
| 437 | } | ||
| 438 | |||
| 439 | #[test] | ||
| 440 | fn can_verify_sha1_cross_sector_boundary() { | ||
| 441 | let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default()); | ||
| 442 | let state = Partition::new(&flash, 0, 4096); | ||
| 443 | let dfu = Partition::new(&flash, 65536, 65536); | ||
| 444 | let mut aligned = [0; 8]; | ||
| 445 | |||
| 446 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | ||
| 447 | let mut to_write = [0; 4096]; | ||
| 448 | to_write[..7].copy_from_slice(update.as_slice()); | ||
| 449 | |||
| 450 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); | ||
| 451 | let mut offset = 0; | ||
| 452 | for chunk in to_write.chunks(896) { | ||
| 453 | block_on(updater.write_firmware(offset, chunk)).unwrap(); | ||
| 454 | offset += chunk.len(); | ||
| 455 | } | ||
| 456 | let mut chunk_buf = [0; 2]; | ||
| 457 | let mut hash = [0; 20]; | ||
| 458 | block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); | ||
| 459 | |||
| 460 | assert_eq!(Sha1::digest(update).as_slice(), hash); | ||
| 461 | } | ||
| 341 | } | 462 | } |
diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index 4044871f0..35772a856 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs | |||
| @@ -13,6 +13,7 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA | |||
| 13 | pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { | 13 | pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { |
| 14 | dfu: DFU, | 14 | dfu: DFU, |
| 15 | state: BlockingFirmwareState<'d, STATE>, | 15 | state: BlockingFirmwareState<'d, STATE>, |
| 16 | last_erased_dfu_sector_index: Option<usize>, | ||
| 16 | } | 17 | } |
| 17 | 18 | ||
| 18 | #[cfg(target_os = "none")] | 19 | #[cfg(target_os = "none")] |
| @@ -91,6 +92,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 91 | Self { | 92 | Self { |
| 92 | dfu: config.dfu, | 93 | dfu: config.dfu, |
| 93 | state: BlockingFirmwareState::new(config.state, aligned), | 94 | state: BlockingFirmwareState::new(config.state, aligned), |
| 95 | last_erased_dfu_sector_index: None, | ||
| 94 | } | 96 | } |
| 95 | } | 97 | } |
| 96 | 98 | ||
| @@ -107,7 +109,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 107 | /// proceed with updating the firmware as it must be signed with a | 109 | /// proceed with updating the firmware as it must be signed with a |
| 108 | /// corresponding private key (otherwise it could be malicious firmware). | 110 | /// corresponding private key (otherwise it could be malicious firmware). |
| 109 | /// | 111 | /// |
| 110 | /// Mark to trigger firmware swap on next boot if verify suceeds. | 112 | /// Mark to trigger firmware swap on next boot if verify succeeds. |
| 111 | /// | 113 | /// |
| 112 | /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have | 114 | /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have |
| 113 | /// been generated from a SHA-512 digest of the firmware bytes. | 115 | /// been generated from a SHA-512 digest of the firmware bytes. |
| @@ -207,20 +209,68 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 207 | self.state.mark_booted() | 209 | self.state.mark_booted() |
| 208 | } | 210 | } |
| 209 | 211 | ||
| 210 | /// Write data to a flash page. | 212 | /// Writes firmware data to the device. |
| 211 | /// | 213 | /// |
| 212 | /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. | 214 | /// This function writes the given data to the firmware area starting at the specified offset. |
| 215 | /// It handles sector erasures and data writes while verifying the device is in a proper state | ||
| 216 | /// for firmware updates. The function ensures that only unerased sectors are erased before | ||
| 217 | /// writing and efficiently handles the writing process across sector boundaries and in | ||
| 218 | /// various configurations (data size, sector size, etc.). | ||
| 213 | /// | 219 | /// |
| 214 | /// # Safety | 220 | /// # Arguments |
| 221 | /// | ||
| 222 | /// * `offset` - The starting offset within the firmware area where data writing should begin. | ||
| 223 | /// * `data` - A slice of bytes representing the firmware data to be written. It must be a | ||
| 224 | /// multiple of NorFlash WRITE_SIZE. | ||
| 225 | /// | ||
| 226 | /// # Returns | ||
| 227 | /// | ||
| 228 | /// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation. | ||
| 215 | /// | 229 | /// |
| 216 | /// Failing to meet alignment and size requirements may result in a panic. | 230 | /// # Errors |
| 231 | /// | ||
| 232 | /// This function will return an error if: | ||
| 233 | /// | ||
| 234 | /// - The device is not in a proper state to receive firmware updates (e.g., not booted). | ||
| 235 | /// - There is a failure erasing a sector before writing. | ||
| 236 | /// - There is a failure writing data to the device. | ||
| 217 | pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { | 237 | pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { |
| 218 | assert!(data.len() >= DFU::ERASE_SIZE); | 238 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 219 | self.state.verify_booted()?; | 239 | self.state.verify_booted()?; |
| 220 | 240 | ||
| 221 | self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; | 241 | // Initialize variables to keep track of the remaining data and the current offset. |
| 242 | let mut remaining_data = data; | ||
| 243 | let mut offset = offset; | ||
| 244 | |||
| 245 | // Continue writing as long as there is data left to write. | ||
| 246 | while !remaining_data.is_empty() { | ||
| 247 | // Compute the current sector and its boundaries. | ||
| 248 | let current_sector = offset / DFU::ERASE_SIZE; | ||
| 249 | let sector_start = current_sector * DFU::ERASE_SIZE; | ||
| 250 | let sector_end = sector_start + DFU::ERASE_SIZE; | ||
| 251 | // Determine if the current sector needs to be erased before writing. | ||
| 252 | let need_erase = self | ||
| 253 | .last_erased_dfu_sector_index | ||
| 254 | .map_or(true, |last_erased_sector| current_sector != last_erased_sector); | ||
| 255 | |||
| 256 | // If the sector needs to be erased, erase it and update the last erased sector index. | ||
| 257 | if need_erase { | ||
| 258 | self.dfu.erase(sector_start as u32, sector_end as u32)?; | ||
| 259 | self.last_erased_dfu_sector_index = Some(current_sector); | ||
| 260 | } | ||
| 261 | |||
| 262 | // Calculate the size of the data chunk that can be written in the current iteration. | ||
| 263 | let write_size = core::cmp::min(remaining_data.len(), sector_end - offset); | ||
| 264 | // Split the data to get the current chunk to be written and the remaining data. | ||
| 265 | let (data_chunk, rest) = remaining_data.split_at(write_size); | ||
| 222 | 266 | ||
| 223 | self.dfu.write(offset as u32, data)?; | 267 | // Write the current data chunk. |
| 268 | self.dfu.write(offset as u32, data_chunk)?; | ||
| 269 | |||
| 270 | // Update the offset and remaining data for the next iteration. | ||
| 271 | remaining_data = rest; | ||
| 272 | offset += write_size; | ||
| 273 | } | ||
| 224 | 274 | ||
| 225 | Ok(()) | 275 | Ok(()) |
| 226 | } | 276 | } |
| @@ -368,4 +418,82 @@ mod tests { | |||
| 368 | 418 | ||
| 369 | assert_eq!(Sha1::digest(update).as_slice(), hash); | 419 | assert_eq!(Sha1::digest(update).as_slice(), hash); |
| 370 | } | 420 | } |
| 421 | |||
| 422 | #[test] | ||
| 423 | fn can_verify_sha1_sector_bigger_than_chunk() { | ||
| 424 | let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default())); | ||
| 425 | let state = BlockingPartition::new(&flash, 0, 4096); | ||
| 426 | let dfu = BlockingPartition::new(&flash, 65536, 65536); | ||
| 427 | let mut aligned = [0; 8]; | ||
| 428 | |||
| 429 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | ||
| 430 | let mut to_write = [0; 4096]; | ||
| 431 | to_write[..7].copy_from_slice(update.as_slice()); | ||
| 432 | |||
| 433 | let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); | ||
| 434 | let mut offset = 0; | ||
| 435 | for chunk in to_write.chunks(1024) { | ||
| 436 | updater.write_firmware(offset, chunk).unwrap(); | ||
| 437 | offset += chunk.len(); | ||
| 438 | } | ||
| 439 | let mut chunk_buf = [0; 2]; | ||
| 440 | let mut hash = [0; 20]; | ||
| 441 | updater | ||
| 442 | .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash) | ||
| 443 | .unwrap(); | ||
| 444 | |||
| 445 | assert_eq!(Sha1::digest(update).as_slice(), hash); | ||
| 446 | } | ||
| 447 | |||
| 448 | #[test] | ||
| 449 | fn can_verify_sha1_sector_smaller_than_chunk() { | ||
| 450 | let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default())); | ||
| 451 | let state = BlockingPartition::new(&flash, 0, 4096); | ||
| 452 | let dfu = BlockingPartition::new(&flash, 65536, 65536); | ||
| 453 | let mut aligned = [0; 8]; | ||
| 454 | |||
| 455 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | ||
| 456 | let mut to_write = [0; 4096]; | ||
| 457 | to_write[..7].copy_from_slice(update.as_slice()); | ||
| 458 | |||
| 459 | let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); | ||
| 460 | let mut offset = 0; | ||
| 461 | for chunk in to_write.chunks(2048) { | ||
| 462 | updater.write_firmware(offset, chunk).unwrap(); | ||
| 463 | offset += chunk.len(); | ||
| 464 | } | ||
| 465 | let mut chunk_buf = [0; 2]; | ||
| 466 | let mut hash = [0; 20]; | ||
| 467 | updater | ||
| 468 | .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash) | ||
| 469 | .unwrap(); | ||
| 470 | |||
| 471 | assert_eq!(Sha1::digest(update).as_slice(), hash); | ||
| 472 | } | ||
| 473 | |||
| 474 | #[test] | ||
| 475 | fn can_verify_sha1_cross_sector_boundary() { | ||
| 476 | let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default())); | ||
| 477 | let state = BlockingPartition::new(&flash, 0, 4096); | ||
| 478 | let dfu = BlockingPartition::new(&flash, 65536, 65536); | ||
| 479 | let mut aligned = [0; 8]; | ||
| 480 | |||
| 481 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | ||
| 482 | let mut to_write = [0; 4096]; | ||
| 483 | to_write[..7].copy_from_slice(update.as_slice()); | ||
| 484 | |||
| 485 | let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); | ||
| 486 | let mut offset = 0; | ||
| 487 | for chunk in to_write.chunks(896) { | ||
| 488 | updater.write_firmware(offset, chunk).unwrap(); | ||
| 489 | offset += chunk.len(); | ||
| 490 | } | ||
| 491 | let mut chunk_buf = [0; 2]; | ||
| 492 | let mut hash = [0; 20]; | ||
| 493 | updater | ||
| 494 | .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash) | ||
| 495 | .unwrap(); | ||
| 496 | |||
| 497 | assert_eq!(Sha1::digest(update).as_slice(), hash); | ||
| 498 | } | ||
| 371 | } | 499 | } |
