Merge #1312
1312: Let bootloader partition have read/write/erase operations r=Dirbaio a=rmja This change should not have any breaking changes. Co-authored-by: Rasmus Melchior Jacobsen <rmja@laesoe.org>
This commit is contained in:
		
						commit
						0909a6cd3f
					
				| @ -79,7 +79,7 @@ impl BootLoader { | ||||
|         Self { active, dfu, state } | ||||
|     } | ||||
| 
 | ||||
|     /// Return the boot address for the active partition.
 | ||||
|     /// Return the offset of the active partition into the active flash.
 | ||||
|     pub fn boot_address(&self) -> usize { | ||||
|         self.active.from | ||||
|     } | ||||
| @ -193,13 +193,13 @@ impl BootLoader { | ||||
|                 self.revert(p, magic, page)?; | ||||
| 
 | ||||
|                 // Overwrite magic and reset progress
 | ||||
|                 let fstate = p.state(); | ||||
|                 let state_flash = p.state(); | ||||
|                 magic.fill(!P::STATE::ERASE_VALUE); | ||||
|                 fstate.write(self.state.from as u32, magic)?; | ||||
|                 fstate.erase(self.state.from as u32, self.state.to as u32)?; | ||||
|                 self.state.write_blocking(state_flash, 0, magic)?; | ||||
|                 self.state.wipe_blocking(state_flash)?; | ||||
| 
 | ||||
|                 magic.fill(BOOT_MAGIC); | ||||
|                 fstate.write(self.state.from as u32, magic)?; | ||||
|                 self.state.write_blocking(state_flash, 0, magic)?; | ||||
|             } | ||||
|         } | ||||
|         Ok(state) | ||||
| @ -218,9 +218,10 @@ impl BootLoader { | ||||
|         let max_index = ((self.state.len() - write_size) / write_size) - 1; | ||||
|         aligned.fill(!P::STATE::ERASE_VALUE); | ||||
| 
 | ||||
|         let flash = config.state(); | ||||
|         let state_flash = config.state(); | ||||
|         for i in 0..max_index { | ||||
|             flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; | ||||
|             self.state | ||||
|                 .read_blocking(state_flash, (write_size + i * write_size) as u32, aligned)?; | ||||
| 
 | ||||
|             if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { | ||||
|                 return Ok(i); | ||||
| @ -230,47 +231,39 @@ impl BootLoader { | ||||
|     } | ||||
| 
 | ||||
|     fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { | ||||
|         let flash = p.state(); | ||||
|         let write_size = magic.len(); | ||||
|         let w = self.state.from + write_size + idx * write_size; | ||||
| 
 | ||||
|         let aligned = magic; | ||||
|         aligned.fill(!P::STATE::ERASE_VALUE); | ||||
|         flash.write(w as u32, aligned)?; | ||||
|         self.state | ||||
|             .write_blocking(p.state(), (write_size + idx * write_size) as u32, aligned)?; | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     fn active_addr(&self, n: usize, page_size: usize) -> usize { | ||||
|         self.active.from + n * page_size | ||||
|     } | ||||
| 
 | ||||
|     fn dfu_addr(&self, n: usize, page_size: usize) -> usize { | ||||
|         self.dfu.from + n * page_size | ||||
|     } | ||||
| 
 | ||||
|     fn copy_page_once_to_active<P: FlashConfig>( | ||||
|         &mut self, | ||||
|         idx: usize, | ||||
|         from_page: usize, | ||||
|         to_page: usize, | ||||
|         from_offset: u32, | ||||
|         to_offset: u32, | ||||
|         p: &mut P, | ||||
|         magic: &mut [u8], | ||||
|         page: &mut [u8], | ||||
|     ) -> Result<(), BootError> { | ||||
|         let buf = page; | ||||
|         if self.current_progress(p, magic)? <= idx { | ||||
|             let mut offset = from_page; | ||||
|             let mut offset = from_offset; | ||||
|             for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { | ||||
|                 p.dfu().read(offset as u32, chunk)?; | ||||
|                 offset += chunk.len(); | ||||
|                 self.dfu.read_blocking(p.dfu(), offset, chunk)?; | ||||
|                 offset += chunk.len() as u32; | ||||
|             } | ||||
| 
 | ||||
|             p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; | ||||
|             self.active | ||||
|                 .erase_blocking(p.active(), to_offset, to_offset + buf.len() as u32)?; | ||||
| 
 | ||||
|             let mut offset = to_page; | ||||
|             let mut offset = to_offset; | ||||
|             for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { | ||||
|                 p.active().write(offset as u32, chunk)?; | ||||
|                 offset += chunk.len(); | ||||
|                 self.active.write_blocking(p.active(), offset, chunk)?; | ||||
|                 offset += chunk.len() as u32; | ||||
|             } | ||||
|             self.update_progress(idx, p, magic)?; | ||||
|         } | ||||
| @ -280,26 +273,27 @@ impl BootLoader { | ||||
|     fn copy_page_once_to_dfu<P: FlashConfig>( | ||||
|         &mut self, | ||||
|         idx: usize, | ||||
|         from_page: usize, | ||||
|         to_page: usize, | ||||
|         from_offset: u32, | ||||
|         to_offset: u32, | ||||
|         p: &mut P, | ||||
|         magic: &mut [u8], | ||||
|         page: &mut [u8], | ||||
|     ) -> Result<(), BootError> { | ||||
|         let buf = page; | ||||
|         if self.current_progress(p, magic)? <= idx { | ||||
|             let mut offset = from_page; | ||||
|             let mut offset = from_offset; | ||||
|             for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { | ||||
|                 p.active().read(offset as u32, chunk)?; | ||||
|                 offset += chunk.len(); | ||||
|                 self.active.read_blocking(p.active(), offset, chunk)?; | ||||
|                 offset += chunk.len() as u32; | ||||
|             } | ||||
| 
 | ||||
|             p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; | ||||
|             self.dfu | ||||
|                 .erase_blocking(p.dfu(), to_offset as u32, to_offset + buf.len() as u32)?; | ||||
| 
 | ||||
|             let mut offset = to_page; | ||||
|             let mut offset = to_offset; | ||||
|             for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { | ||||
|                 p.dfu().write(offset as u32, chunk)?; | ||||
|                 offset += chunk.len(); | ||||
|                 self.dfu.write_blocking(p.dfu(), offset, chunk)?; | ||||
|                 offset += chunk.len() as u32; | ||||
|             } | ||||
|             self.update_progress(idx, p, magic)?; | ||||
|         } | ||||
| @ -312,17 +306,20 @@ impl BootLoader { | ||||
|         trace!("Page count: {}", page_count); | ||||
|         for page_num in 0..page_count { | ||||
|             trace!("COPY PAGE {}", page_num); | ||||
| 
 | ||||
|             let idx = page_num * 2; | ||||
| 
 | ||||
|             // Copy active page to the 'next' DFU page.
 | ||||
|             let active_page = self.active_addr(page_count - 1 - page_num, page_size); | ||||
|             let dfu_page = self.dfu_addr(page_count - page_num, page_size); | ||||
|             //trace!("Copy active {} to dfu {}", active_page, dfu_page);
 | ||||
|             self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; | ||||
|             let active_from_offset = ((page_count - 1 - page_num) * page_size) as u32; | ||||
|             let dfu_to_offset = ((page_count - page_num) * page_size) as u32; | ||||
|             //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset);
 | ||||
|             self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?; | ||||
| 
 | ||||
|             // Copy DFU page to the active page
 | ||||
|             let active_page = self.active_addr(page_count - 1 - page_num, page_size); | ||||
|             let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); | ||||
|             //trace!("Copy dfy {} to active {}", dfu_page, active_page);
 | ||||
|             self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; | ||||
|             let active_to_offset = ((page_count - 1 - page_num) * page_size) as u32; | ||||
|             let dfu_from_offset = ((page_count - 1 - page_num) * page_size) as u32; | ||||
|             //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset);
 | ||||
|             self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?; | ||||
|         } | ||||
| 
 | ||||
|         Ok(()) | ||||
| @ -332,23 +329,24 @@ impl BootLoader { | ||||
|         let page_size = page.len(); | ||||
|         let page_count = self.active.len() / page_size; | ||||
|         for page_num in 0..page_count { | ||||
|             let idx = page_count * 2 + page_num * 2; | ||||
| 
 | ||||
|             // Copy the bad active page to the DFU page
 | ||||
|             let active_page = self.active_addr(page_num, page_size); | ||||
|             let dfu_page = self.dfu_addr(page_num, page_size); | ||||
|             self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; | ||||
|             let active_from_offset = (page_num * page_size) as u32; | ||||
|             let dfu_to_offset = (page_num * page_size) as u32; | ||||
|             self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?; | ||||
| 
 | ||||
|             // Copy the DFU page back to the active page
 | ||||
|             let active_page = self.active_addr(page_num, page_size); | ||||
|             let dfu_page = self.dfu_addr(page_num + 1, page_size); | ||||
|             self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; | ||||
|             let active_to_offset = (page_num * page_size) as u32; | ||||
|             let dfu_from_offset = ((page_num + 1) * page_size) as u32; | ||||
|             self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?; | ||||
|         } | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     fn read_state<P: FlashConfig>(&mut self, config: &mut P, magic: &mut [u8]) -> Result<State, BootError> { | ||||
|         let flash = config.state(); | ||||
|         flash.read(self.state.from as u32, magic)?; | ||||
|         self.state.read_blocking(config.state(), 0, magic)?; | ||||
| 
 | ||||
|         if !magic.iter().any(|&b| b != SWAP_MAGIC) { | ||||
|             Ok(State::Swap) | ||||
|  | ||||
| @ -84,10 +84,10 @@ impl FirmwareUpdater { | ||||
|     /// `mark_booted`.
 | ||||
|     pub async fn get_state<F: AsyncNorFlash>( | ||||
|         &mut self, | ||||
|         flash: &mut F, | ||||
|         state_flash: &mut F, | ||||
|         aligned: &mut [u8], | ||||
|     ) -> Result<State, FirmwareUpdaterError> { | ||||
|         flash.read(self.state.from as u32, aligned).await?; | ||||
|         self.state.read(state_flash, 0, aligned).await?; | ||||
| 
 | ||||
|         if !aligned.iter().any(|&b| b != SWAP_MAGIC) { | ||||
|             Ok(State::Swap) | ||||
| @ -115,17 +115,16 @@ impl FirmwareUpdater { | ||||
|     #[cfg(feature = "_verify")] | ||||
|     pub async fn verify_and_mark_updated<F: AsyncNorFlash>( | ||||
|         &mut self, | ||||
|         _flash: &mut F, | ||||
|         _state_and_dfu_flash: &mut F, | ||||
|         _public_key: &[u8], | ||||
|         _signature: &[u8], | ||||
|         _update_len: usize, | ||||
|         _aligned: &mut [u8], | ||||
|     ) -> Result<(), FirmwareUpdaterError> { | ||||
|         let _end = self.dfu.from + _update_len; | ||||
|         let _read_size = _aligned.len(); | ||||
| 
 | ||||
|         assert_eq!(_aligned.len(), F::WRITE_SIZE); | ||||
|         assert!(_end <= self.dfu.to); | ||||
|         assert!(_update_len <= self.dfu.len()); | ||||
| 
 | ||||
|         #[cfg(feature = "ed25519-dalek")] | ||||
|         { | ||||
| @ -137,21 +136,10 @@ impl FirmwareUpdater { | ||||
|             let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | ||||
| 
 | ||||
|             let mut digest = Sha512::new(); | ||||
| 
 | ||||
|             let mut offset = self.dfu.from; | ||||
|             let last_offset = _end / _read_size * _read_size; | ||||
| 
 | ||||
|             while offset < last_offset { | ||||
|                 _flash.read(offset as u32, _aligned).await?; | ||||
|                 digest.update(&_aligned); | ||||
|                 offset += _read_size; | ||||
|             } | ||||
| 
 | ||||
|             let remaining = _end % _read_size; | ||||
| 
 | ||||
|             if remaining > 0 { | ||||
|                 _flash.read(last_offset as u32, _aligned).await?; | ||||
|                 digest.update(&_aligned[0..remaining]); | ||||
|             for offset in (0.._update_len).step_by(_aligned.len()) { | ||||
|                 self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; | ||||
|                 let len = core::cmp::min(_update_len - offset, _aligned.len()); | ||||
|                 digest.update(&_aligned[..len]); | ||||
|             } | ||||
| 
 | ||||
|             public_key | ||||
| @ -173,21 +161,10 @@ impl FirmwareUpdater { | ||||
|             let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | ||||
| 
 | ||||
|             let mut digest = Sha512::new(); | ||||
| 
 | ||||
|             let mut offset = self.dfu.from; | ||||
|             let last_offset = _end / _read_size * _read_size; | ||||
| 
 | ||||
|             while offset < last_offset { | ||||
|                 _flash.read(offset as u32, _aligned).await?; | ||||
|                 digest.update(&_aligned); | ||||
|                 offset += _read_size; | ||||
|             } | ||||
| 
 | ||||
|             let remaining = _end % _read_size; | ||||
| 
 | ||||
|             if remaining > 0 { | ||||
|                 _flash.read(last_offset as u32, _aligned).await?; | ||||
|                 digest.update(&_aligned[0..remaining]); | ||||
|             for offset in (0.._update_len).step_by(_aligned.len()) { | ||||
|                 self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; | ||||
|                 let len = core::cmp::min(_update_len - offset, _aligned.len()); | ||||
|                 digest.update(&_aligned[..len]); | ||||
|             } | ||||
| 
 | ||||
|             let message = digest.finalize(); | ||||
| @ -202,7 +179,7 @@ impl FirmwareUpdater { | ||||
|             r.map_err(into_signature_error)? | ||||
|         } | ||||
| 
 | ||||
|         self.set_magic(_aligned, SWAP_MAGIC, _flash).await | ||||
|         self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await | ||||
|     } | ||||
| 
 | ||||
|     /// Mark to trigger firmware swap on next boot.
 | ||||
| @ -213,11 +190,11 @@ impl FirmwareUpdater { | ||||
|     #[cfg(not(feature = "_verify"))] | ||||
|     pub async fn mark_updated<F: AsyncNorFlash>( | ||||
|         &mut self, | ||||
|         flash: &mut F, | ||||
|         state_flash: &mut F, | ||||
|         aligned: &mut [u8], | ||||
|     ) -> Result<(), FirmwareUpdaterError> { | ||||
|         assert_eq!(aligned.len(), F::WRITE_SIZE); | ||||
|         self.set_magic(aligned, SWAP_MAGIC, flash).await | ||||
|         self.set_magic(aligned, SWAP_MAGIC, state_flash).await | ||||
|     } | ||||
| 
 | ||||
|     /// Mark firmware boot successful and stop rollback on reset.
 | ||||
| @ -227,29 +204,29 @@ impl FirmwareUpdater { | ||||
|     /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
 | ||||
|     pub async fn mark_booted<F: AsyncNorFlash>( | ||||
|         &mut self, | ||||
|         flash: &mut F, | ||||
|         state_flash: &mut F, | ||||
|         aligned: &mut [u8], | ||||
|     ) -> Result<(), FirmwareUpdaterError> { | ||||
|         assert_eq!(aligned.len(), F::WRITE_SIZE); | ||||
|         self.set_magic(aligned, BOOT_MAGIC, flash).await | ||||
|         self.set_magic(aligned, BOOT_MAGIC, state_flash).await | ||||
|     } | ||||
| 
 | ||||
|     async fn set_magic<F: AsyncNorFlash>( | ||||
|         &mut self, | ||||
|         aligned: &mut [u8], | ||||
|         magic: u8, | ||||
|         flash: &mut F, | ||||
|         state_flash: &mut F, | ||||
|     ) -> Result<(), FirmwareUpdaterError> { | ||||
|         flash.read(self.state.from as u32, aligned).await?; | ||||
|         self.state.read(state_flash, 0, aligned).await?; | ||||
| 
 | ||||
|         if aligned.iter().any(|&b| b != magic) { | ||||
|             aligned.fill(0); | ||||
| 
 | ||||
|             flash.write(self.state.from as u32, aligned).await?; | ||||
|             flash.erase(self.state.from as u32, self.state.to as u32).await?; | ||||
|             self.state.write(state_flash, 0, aligned).await?; | ||||
|             self.state.wipe(state_flash).await?; | ||||
| 
 | ||||
|             aligned.fill(magic); | ||||
|             flash.write(self.state.from as u32, aligned).await?; | ||||
|             self.state.write(state_flash, 0, aligned).await?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| @ -265,26 +242,17 @@ impl FirmwareUpdater { | ||||
|         &mut self, | ||||
|         offset: usize, | ||||
|         data: &[u8], | ||||
|         flash: &mut F, | ||||
|         dfu_flash: &mut F, | ||||
|         block_size: usize, | ||||
|     ) -> Result<(), FirmwareUpdaterError> { | ||||
|         assert!(data.len() >= F::ERASE_SIZE); | ||||
| 
 | ||||
|         flash | ||||
|             .erase( | ||||
|                 (self.dfu.from + offset) as u32, | ||||
|                 (self.dfu.from + offset + data.len()) as u32, | ||||
|             ) | ||||
|         self.dfu | ||||
|             .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) | ||||
|             .await?; | ||||
| 
 | ||||
|         trace!( | ||||
|             "Erased from {} to {}", | ||||
|             self.dfu.from + offset, | ||||
|             self.dfu.from + offset + data.len() | ||||
|         ); | ||||
| 
 | ||||
|         FirmwareWriter(self.dfu) | ||||
|             .write_block(offset, data, flash, block_size) | ||||
|             .write_block(offset, data, dfu_flash, block_size) | ||||
|             .await?; | ||||
| 
 | ||||
|         Ok(()) | ||||
| @ -297,11 +265,9 @@ impl FirmwareUpdater { | ||||
|     /// exchange for added complexity.
 | ||||
|     pub async fn prepare_update<F: AsyncNorFlash>( | ||||
|         &mut self, | ||||
|         flash: &mut F, | ||||
|         dfu_flash: &mut F, | ||||
|     ) -> Result<FirmwareWriter, FirmwareUpdaterError> { | ||||
|         flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; | ||||
| 
 | ||||
|         trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); | ||||
|         self.dfu.wipe(dfu_flash).await?; | ||||
| 
 | ||||
|         Ok(FirmwareWriter(self.dfu)) | ||||
|     } | ||||
| @ -317,10 +283,10 @@ impl FirmwareUpdater { | ||||
|     /// `mark_booted`.
 | ||||
|     pub fn get_state_blocking<F: NorFlash>( | ||||
|         &mut self, | ||||
|         flash: &mut F, | ||||
|         state_flash: &mut F, | ||||
|         aligned: &mut [u8], | ||||
|     ) -> Result<State, FirmwareUpdaterError> { | ||||
|         flash.read(self.state.from as u32, aligned)?; | ||||
|         self.state.read_blocking(state_flash, 0, aligned)?; | ||||
| 
 | ||||
|         if !aligned.iter().any(|&b| b != SWAP_MAGIC) { | ||||
|             Ok(State::Swap) | ||||
| @ -348,7 +314,7 @@ impl FirmwareUpdater { | ||||
|     #[cfg(feature = "_verify")] | ||||
|     pub fn verify_and_mark_updated_blocking<F: NorFlash>( | ||||
|         &mut self, | ||||
|         _flash: &mut F, | ||||
|         _state_and_dfu_flash: &mut F, | ||||
|         _public_key: &[u8], | ||||
|         _signature: &[u8], | ||||
|         _update_len: usize, | ||||
| @ -370,21 +336,10 @@ impl FirmwareUpdater { | ||||
|             let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | ||||
| 
 | ||||
|             let mut digest = Sha512::new(); | ||||
| 
 | ||||
|             let mut offset = self.dfu.from; | ||||
|             let last_offset = _end / _read_size * _read_size; | ||||
| 
 | ||||
|             while offset < last_offset { | ||||
|                 _flash.read(offset as u32, _aligned)?; | ||||
|                 digest.update(&_aligned); | ||||
|                 offset += _read_size; | ||||
|             } | ||||
| 
 | ||||
|             let remaining = _end % _read_size; | ||||
| 
 | ||||
|             if remaining > 0 { | ||||
|                 _flash.read(last_offset as u32, _aligned)?; | ||||
|                 digest.update(&_aligned[0..remaining]); | ||||
|             for offset in (0.._update_len).step_by(_aligned.len()) { | ||||
|                 self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; | ||||
|                 let len = core::cmp::min(_update_len - offset, _aligned.len()); | ||||
|                 digest.update(&_aligned[..len]); | ||||
|             } | ||||
| 
 | ||||
|             public_key | ||||
| @ -406,21 +361,10 @@ impl FirmwareUpdater { | ||||
|             let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | ||||
| 
 | ||||
|             let mut digest = Sha512::new(); | ||||
| 
 | ||||
|             let mut offset = self.dfu.from; | ||||
|             let last_offset = _end / _read_size * _read_size; | ||||
| 
 | ||||
|             while offset < last_offset { | ||||
|                 _flash.read(offset as u32, _aligned)?; | ||||
|                 digest.update(&_aligned); | ||||
|                 offset += _read_size; | ||||
|             } | ||||
| 
 | ||||
|             let remaining = _end % _read_size; | ||||
| 
 | ||||
|             if remaining > 0 { | ||||
|                 _flash.read(last_offset as u32, _aligned)?; | ||||
|                 digest.update(&_aligned[0..remaining]); | ||||
|             for offset in (0.._update_len).step_by(_aligned.len()) { | ||||
|                 self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; | ||||
|                 let len = core::cmp::min(_update_len - offset, _aligned.len()); | ||||
|                 digest.update(&_aligned[..len]); | ||||
|             } | ||||
| 
 | ||||
|             let message = digest.finalize(); | ||||
| @ -435,7 +379,7 @@ impl FirmwareUpdater { | ||||
|             r.map_err(into_signature_error)? | ||||
|         } | ||||
| 
 | ||||
|         self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) | ||||
|         self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) | ||||
|     } | ||||
| 
 | ||||
|     /// Mark to trigger firmware swap on next boot.
 | ||||
| @ -446,11 +390,11 @@ impl FirmwareUpdater { | ||||
|     #[cfg(not(feature = "_verify"))] | ||||
|     pub fn mark_updated_blocking<F: NorFlash>( | ||||
|         &mut self, | ||||
|         flash: &mut F, | ||||
|         state_flash: &mut F, | ||||
|         aligned: &mut [u8], | ||||
|     ) -> Result<(), FirmwareUpdaterError> { | ||||
|         assert_eq!(aligned.len(), F::WRITE_SIZE); | ||||
|         self.set_magic_blocking(aligned, SWAP_MAGIC, flash) | ||||
|         self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash) | ||||
|     } | ||||
| 
 | ||||
|     /// Mark firmware boot successful and stop rollback on reset.
 | ||||
| @ -460,29 +404,29 @@ impl FirmwareUpdater { | ||||
|     /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
 | ||||
|     pub fn mark_booted_blocking<F: NorFlash>( | ||||
|         &mut self, | ||||
|         flash: &mut F, | ||||
|         state_flash: &mut F, | ||||
|         aligned: &mut [u8], | ||||
|     ) -> Result<(), FirmwareUpdaterError> { | ||||
|         assert_eq!(aligned.len(), F::WRITE_SIZE); | ||||
|         self.set_magic_blocking(aligned, BOOT_MAGIC, flash) | ||||
|         self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash) | ||||
|     } | ||||
| 
 | ||||
|     fn set_magic_blocking<F: NorFlash>( | ||||
|         &mut self, | ||||
|         aligned: &mut [u8], | ||||
|         magic: u8, | ||||
|         flash: &mut F, | ||||
|         state_flash: &mut F, | ||||
|     ) -> Result<(), FirmwareUpdaterError> { | ||||
|         flash.read(self.state.from as u32, aligned)?; | ||||
|         self.state.read_blocking(state_flash, 0, aligned)?; | ||||
| 
 | ||||
|         if aligned.iter().any(|&b| b != magic) { | ||||
|             aligned.fill(0); | ||||
| 
 | ||||
|             flash.write(self.state.from as u32, aligned)?; | ||||
|             flash.erase(self.state.from as u32, self.state.to as u32)?; | ||||
|             self.state.write_blocking(state_flash, 0, aligned)?; | ||||
|             self.state.wipe_blocking(state_flash)?; | ||||
| 
 | ||||
|             aligned.fill(magic); | ||||
|             flash.write(self.state.from as u32, aligned)?; | ||||
|             self.state.write_blocking(state_flash, 0, aligned)?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| @ -498,23 +442,15 @@ impl FirmwareUpdater { | ||||
|         &mut self, | ||||
|         offset: usize, | ||||
|         data: &[u8], | ||||
|         flash: &mut F, | ||||
|         dfu_flash: &mut F, | ||||
|         block_size: usize, | ||||
|     ) -> Result<(), FirmwareUpdaterError> { | ||||
|         assert!(data.len() >= F::ERASE_SIZE); | ||||
| 
 | ||||
|         flash.erase( | ||||
|             (self.dfu.from + offset) as u32, | ||||
|             (self.dfu.from + offset + data.len()) as u32, | ||||
|         )?; | ||||
|         self.dfu | ||||
|             .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; | ||||
| 
 | ||||
|         trace!( | ||||
|             "Erased from {} to {}", | ||||
|             self.dfu.from + offset, | ||||
|             self.dfu.from + offset + data.len() | ||||
|         ); | ||||
| 
 | ||||
|         FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; | ||||
|         FirmwareWriter(self.dfu).write_block_blocking(offset, data, dfu_flash, block_size)?; | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| @ -528,9 +464,7 @@ impl FirmwareUpdater { | ||||
|         &mut self, | ||||
|         flash: &mut F, | ||||
|     ) -> Result<FirmwareWriter, FirmwareUpdaterError> { | ||||
|         flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; | ||||
| 
 | ||||
|         trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); | ||||
|         self.dfu.wipe_blocking(flash)?; | ||||
| 
 | ||||
|         Ok(FirmwareWriter(self.dfu)) | ||||
|     } | ||||
|  | ||||
| @ -21,32 +21,11 @@ impl FirmwareWriter { | ||||
|         flash: &mut F, | ||||
|         block_size: usize, | ||||
|     ) -> Result<(), F::Error> { | ||||
|         trace!( | ||||
|             "Writing firmware at offset 0x{:x} len {}", | ||||
|             self.0.from + offset, | ||||
|             data.len() | ||||
|         ); | ||||
| 
 | ||||
|         let mut write_offset = self.0.from + offset; | ||||
|         let mut offset = offset as u32; | ||||
|         for chunk in data.chunks(block_size) { | ||||
|             trace!("Wrote chunk at {}: {:?}", write_offset, chunk); | ||||
|             flash.write(write_offset as u32, chunk).await?; | ||||
|             write_offset += chunk.len(); | ||||
|             self.0.write(flash, offset, chunk).await?; | ||||
|             offset += chunk.len() as u32; | ||||
|         } | ||||
|         /* | ||||
|         trace!("Wrote data, reading back for verification"); | ||||
| 
 | ||||
|         let mut buf: [u8; 4096] = [0; 4096]; | ||||
|         let mut data_offset = 0; | ||||
|         let mut read_offset = self.dfu.from + offset; | ||||
|         for chunk in buf.chunks_mut(block_size) { | ||||
|             flash.read(read_offset as u32, chunk).await?; | ||||
|             trace!("Read chunk at {}: {:?}", read_offset, chunk); | ||||
|             assert_eq!(&data[data_offset..data_offset + block_size], chunk); | ||||
|             read_offset += chunk.len(); | ||||
|             data_offset += chunk.len(); | ||||
|         } | ||||
|         */ | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| @ -65,32 +44,11 @@ impl FirmwareWriter { | ||||
|         flash: &mut F, | ||||
|         block_size: usize, | ||||
|     ) -> Result<(), F::Error> { | ||||
|         trace!( | ||||
|             "Writing firmware at offset 0x{:x} len {}", | ||||
|             self.0.from + offset, | ||||
|             data.len() | ||||
|         ); | ||||
| 
 | ||||
|         let mut write_offset = self.0.from + offset; | ||||
|         let mut offset = offset as u32; | ||||
|         for chunk in data.chunks(block_size) { | ||||
|             trace!("Wrote chunk at {}: {:?}", write_offset, chunk); | ||||
|             flash.write(write_offset as u32, chunk)?; | ||||
|             write_offset += chunk.len(); | ||||
|             self.0.write_blocking(flash, offset, chunk)?; | ||||
|             offset += chunk.len() as u32; | ||||
|         } | ||||
|         /* | ||||
|         trace!("Wrote data, reading back for verification"); | ||||
| 
 | ||||
|         let mut buf: [u8; 4096] = [0; 4096]; | ||||
|         let mut data_offset = 0; | ||||
|         let mut read_offset = self.dfu.from + offset; | ||||
|         for chunk in buf.chunks_mut(block_size) { | ||||
|             flash.read(read_offset as u32, chunk).await?; | ||||
|             trace!("Read chunk at {}: {:?}", read_offset, chunk); | ||||
|             assert_eq!(&data[data_offset..data_offset + block_size], chunk); | ||||
|             read_offset += chunk.len(); | ||||
|             data_offset += chunk.len(); | ||||
|         } | ||||
|         */ | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
| @ -313,7 +313,8 @@ mod tests { | ||||
|         )) | ||||
|         .is_ok()); | ||||
|     } | ||||
|     struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]); | ||||
| 
 | ||||
|     pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>(pub [u8; SIZE]); | ||||
| 
 | ||||
|     impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash | ||||
|         for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | ||||
|  | ||||
| @ -1,10 +1,13 @@ | ||||
| use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||||
| use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; | ||||
| 
 | ||||
| /// A region in flash used by the bootloader.
 | ||||
| #[derive(Copy, Clone, Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub struct Partition { | ||||
|     /// Start of the flash region.
 | ||||
|     /// The offset into the flash where the partition starts.
 | ||||
|     pub from: usize, | ||||
|     /// End of the flash region.
 | ||||
|     /// The offset into the flash where the partition ends.
 | ||||
|     pub to: usize, | ||||
| } | ||||
| 
 | ||||
| @ -14,9 +17,134 @@ impl Partition { | ||||
|         Self { from, to } | ||||
|     } | ||||
| 
 | ||||
|     /// Return the length of the partition
 | ||||
|     /// Return the size of the partition
 | ||||
|     #[allow(clippy::len_without_is_empty)] | ||||
|     pub const fn len(&self) -> usize { | ||||
|         self.to - self.from | ||||
|     } | ||||
| 
 | ||||
|     /// Read from the partition on the provided flash
 | ||||
|     pub(crate) async fn read<F: AsyncReadNorFlash>( | ||||
|         &self, | ||||
|         flash: &mut F, | ||||
|         offset: u32, | ||||
|         bytes: &mut [u8], | ||||
|     ) -> Result<(), F::Error> { | ||||
|         let offset = self.from as u32 + offset; | ||||
|         flash.read(offset, bytes).await | ||||
|     } | ||||
| 
 | ||||
|     /// Write to the partition on the provided flash
 | ||||
|     pub(crate) async fn write<F: AsyncNorFlash>( | ||||
|         &self, | ||||
|         flash: &mut F, | ||||
|         offset: u32, | ||||
|         bytes: &[u8], | ||||
|     ) -> Result<(), F::Error> { | ||||
|         let offset = self.from as u32 + offset; | ||||
|         flash.write(offset, bytes).await?; | ||||
|         trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     /// Erase part of the partition on the provided flash
 | ||||
|     pub(crate) async fn erase<F: AsyncNorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { | ||||
|         let from = self.from as u32 + from; | ||||
|         let to = self.from as u32 + to; | ||||
|         flash.erase(from, to).await?; | ||||
|         trace!("Erased from 0x{:x} to 0x{:x}", from, to); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     /// Erase the entire partition
 | ||||
|     pub(crate) async fn wipe<F: AsyncNorFlash>(&self, flash: &mut F) -> Result<(), F::Error> { | ||||
|         let from = self.from as u32; | ||||
|         let to = self.to as u32; | ||||
|         flash.erase(from, to).await?; | ||||
|         trace!("Wiped from 0x{:x} to 0x{:x}", from, to); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     /// Read from the partition on the provided flash
 | ||||
|     pub(crate) fn read_blocking<F: ReadNorFlash>( | ||||
|         &self, | ||||
|         flash: &mut F, | ||||
|         offset: u32, | ||||
|         bytes: &mut [u8], | ||||
|     ) -> Result<(), F::Error> { | ||||
|         let offset = self.from as u32 + offset; | ||||
|         flash.read(offset, bytes) | ||||
|     } | ||||
| 
 | ||||
|     /// Write to the partition on the provided flash
 | ||||
|     pub(crate) fn write_blocking<F: NorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { | ||||
|         let offset = self.from as u32 + offset; | ||||
|         flash.write(offset, bytes)?; | ||||
|         trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     /// Erase part of the partition on the provided flash
 | ||||
|     pub(crate) fn erase_blocking<F: NorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { | ||||
|         let from = self.from as u32 + from; | ||||
|         let to = self.from as u32 + to; | ||||
|         flash.erase(from, to)?; | ||||
|         trace!("Erased from 0x{:x} to 0x{:x}", from, to); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     /// Erase the entire partition
 | ||||
|     pub(crate) fn wipe_blocking<F: NorFlash>(&self, flash: &mut F) -> Result<(), F::Error> { | ||||
|         let from = self.from as u32; | ||||
|         let to = self.to as u32; | ||||
|         flash.erase(from, to)?; | ||||
|         trace!("Wiped from 0x{:x} to 0x{:x}", from, to); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::tests::MemFlash; | ||||
|     use crate::Partition; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn can_erase() { | ||||
|         let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); | ||||
|         let partition = Partition::new(256, 512); | ||||
| 
 | ||||
|         partition.erase_blocking(&mut flash, 64, 192).unwrap(); | ||||
| 
 | ||||
|         for (index, byte) in flash.0.iter().copied().enumerate().take(256 + 64) { | ||||
|             assert_eq!(0x00, byte, "Index {}", index); | ||||
|         } | ||||
| 
 | ||||
|         for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64).take(128) { | ||||
|             assert_eq!(0xFF, byte, "Index {}", index); | ||||
|         } | ||||
| 
 | ||||
|         for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64 + 128) { | ||||
|             assert_eq!(0x00, byte, "Index {}", index); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn can_wipe() { | ||||
|         let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); | ||||
|         let partition = Partition::new(256, 512); | ||||
| 
 | ||||
|         partition.wipe_blocking(&mut flash).unwrap(); | ||||
| 
 | ||||
|         for (index, byte) in flash.0.iter().copied().enumerate().take(256) { | ||||
|             assert_eq!(0x00, byte, "Index {}", index); | ||||
|         } | ||||
| 
 | ||||
|         for (index, byte) in flash.0.iter().copied().enumerate().skip(256).take(256) { | ||||
|             assert_eq!(0xFF, byte, "Index {}", index); | ||||
|         } | ||||
| 
 | ||||
|         for (index, byte) in flash.0.iter().copied().enumerate().skip(512) { | ||||
|             assert_eq!(0x00, byte, "Index {}", index); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user