aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-09-26 08:53:25 +0000
committerGitHub <[email protected]>2022-09-26 08:53:25 +0000
commit3c06a18b94ec4d716cc54d47c46da9dffec928c8 (patch)
tree8784d74280e9bae990a9054c0a30cf84e250ce90
parent3c24ad2db64ff46d8b16a55248bbe7af0798a9a4 (diff)
parent6fa74b0c022c41c9ac6dd0b937ef402846cbdfae (diff)
Merge #971
971: (embassy-boot): add blocking API to FirmwareUpdater r=lulf a=MathiasKoch Also add a split `prepare_update` + `write_firmware` API, to allow for an optimized update API at the exchange of added complexity. The old API is left in place to allow users to choose the complexity level that fits their needs. Co-authored-by: Mathias <[email protected]>
-rw-r--r--embassy-boot/boot/src/lib.rs216
1 files changed, 209 insertions, 7 deletions
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 96878ace9..8286601ec 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -604,6 +604,21 @@ impl FirmwareUpdater {
604 self.dfu.len() 604 self.dfu.len()
605 } 605 }
606 606
607 /// Obtain the current state.
608 ///
609 /// This is useful to check if the bootloader has just done a swap, in order
610 /// to do verifications and self-tests of the new image before calling
611 /// `mark_booted`.
612 pub async fn get_state<F: AsyncNorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<State, F::Error> {
613 flash.read(self.state.from as u32, aligned).await?;
614
615 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
616 Ok(State::Swap)
617 } else {
618 Ok(State::Boot)
619 }
620 }
621
607 /// Mark to trigger firmware swap on next boot. 622 /// Mark to trigger firmware swap on next boot.
608 /// 623 ///
609 /// # Safety 624 /// # Safety
@@ -660,12 +675,6 @@ impl FirmwareUpdater {
660 ) -> Result<(), F::Error> { 675 ) -> Result<(), F::Error> {
661 assert!(data.len() >= F::ERASE_SIZE); 676 assert!(data.len() >= F::ERASE_SIZE);
662 677
663 trace!(
664 "Writing firmware at offset 0x{:x} len {}",
665 self.dfu.from + offset,
666 data.len()
667 );
668
669 flash 678 flash
670 .erase( 679 .erase(
671 (self.dfu.from + offset) as u32, 680 (self.dfu.from + offset) as u32,
@@ -679,7 +688,156 @@ impl FirmwareUpdater {
679 self.dfu.from + offset + data.len() 688 self.dfu.from + offset + data.len()
680 ); 689 );
681 690
682 let mut write_offset = self.dfu.from + offset; 691 FirmwareWriter(self.dfu)
692 .write_block(offset, data, flash, block_size)
693 .await?;
694
695 Ok(())
696 }
697
698 /// Prepare for an incoming DFU update by erasing the entire DFU area and
699 /// returning a `FirmwareWriter`.
700 ///
701 /// Using this instead of `write_firmware` allows for an optimized API in
702 /// exchange for added complexity.
703 pub async fn prepare_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<FirmwareWriter, F::Error> {
704 flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?;
705
706 trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
707
708 Ok(FirmwareWriter(self.dfu))
709 }
710
711 //
712 // Blocking API
713 //
714
715 /// Obtain the current state.
716 ///
717 /// This is useful to check if the bootloader has just done a swap, in order
718 /// to do verifications and self-tests of the new image before calling
719 /// `mark_booted`.
720 pub fn get_state_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<State, F::Error> {
721 flash.read(self.state.from as u32, aligned)?;
722
723 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
724 Ok(State::Swap)
725 } else {
726 Ok(State::Boot)
727 }
728 }
729
730 /// Mark to trigger firmware swap on next boot.
731 ///
732 /// # Safety
733 ///
734 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
735 pub fn mark_updated_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> {
736 assert_eq!(aligned.len(), F::WRITE_SIZE);
737 self.set_magic_blocking(aligned, SWAP_MAGIC, flash)
738 }
739
740 /// Mark firmware boot successful and stop rollback on reset.
741 ///
742 /// # Safety
743 ///
744 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
745 pub fn mark_booted_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> {
746 assert_eq!(aligned.len(), F::WRITE_SIZE);
747 self.set_magic_blocking(aligned, BOOT_MAGIC, flash)
748 }
749
750 fn set_magic_blocking<F: NorFlash>(
751 &mut self,
752 aligned: &mut [u8],
753 magic: u8,
754 flash: &mut F,
755 ) -> Result<(), F::Error> {
756 flash.read(self.state.from as u32, aligned)?;
757
758 if aligned.iter().any(|&b| b != magic) {
759 aligned.fill(0);
760
761 flash.write(self.state.from as u32, aligned)?;
762 flash.erase(self.state.from as u32, self.state.to as u32)?;
763
764 aligned.fill(magic);
765 flash.write(self.state.from as u32, aligned)?;
766 }
767 Ok(())
768 }
769
770 /// Write data to a flash page.
771 ///
772 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
773 ///
774 /// # Safety
775 ///
776 /// Failing to meet alignment and size requirements may result in a panic.
777 pub fn write_firmware_blocking<F: NorFlash>(
778 &mut self,
779 offset: usize,
780 data: &[u8],
781 flash: &mut F,
782 block_size: usize,
783 ) -> Result<(), F::Error> {
784 assert!(data.len() >= F::ERASE_SIZE);
785
786 flash.erase(
787 (self.dfu.from + offset) as u32,
788 (self.dfu.from + offset + data.len()) as u32,
789 )?;
790
791 trace!(
792 "Erased from {} to {}",
793 self.dfu.from + offset,
794 self.dfu.from + offset + data.len()
795 );
796
797 FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?;
798
799 Ok(())
800 }
801
802 /// Prepare for an incoming DFU update by erasing the entire DFU area and
803 /// returning a `FirmwareWriter`.
804 ///
805 /// Using this instead of `write_firmware_blocking` allows for an optimized
806 /// API in exchange for added complexity.
807 pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<FirmwareWriter, F::Error> {
808 flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?;
809
810 trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
811
812 Ok(FirmwareWriter(self.dfu))
813 }
814}
815
816/// FirmwareWriter allows writing blocks to an already erased flash.
817pub struct FirmwareWriter(Partition);
818
819impl FirmwareWriter {
820 /// Write data to a flash page.
821 ///
822 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
823 ///
824 /// # Safety
825 ///
826 /// Failing to meet alignment and size requirements may result in a panic.
827 pub async fn write_block<F: AsyncNorFlash>(
828 &mut self,
829 offset: usize,
830 data: &[u8],
831 flash: &mut F,
832 block_size: usize,
833 ) -> Result<(), F::Error> {
834 trace!(
835 "Writing firmware at offset 0x{:x} len {}",
836 self.0.from + offset,
837 data.len()
838 );
839
840 let mut write_offset = self.0.from + offset;
683 for chunk in data.chunks(block_size) { 841 for chunk in data.chunks(block_size) {
684 trace!("Wrote chunk at {}: {:?}", write_offset, chunk); 842 trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
685 flash.write(write_offset as u32, chunk).await?; 843 flash.write(write_offset as u32, chunk).await?;
@@ -702,6 +860,50 @@ impl FirmwareUpdater {
702 860
703 Ok(()) 861 Ok(())
704 } 862 }
863
864 /// Write data to a flash page.
865 ///
866 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
867 ///
868 /// # Safety
869 ///
870 /// Failing to meet alignment and size requirements may result in a panic.
871 pub fn write_block_blocking<F: NorFlash>(
872 &mut self,
873 offset: usize,
874 data: &[u8],
875 flash: &mut F,
876 block_size: usize,
877 ) -> Result<(), F::Error> {
878 trace!(
879 "Writing firmware at offset 0x{:x} len {}",
880 self.0.from + offset,
881 data.len()
882 );
883
884 let mut write_offset = self.0.from + offset;
885 for chunk in data.chunks(block_size) {
886 trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
887 flash.write(write_offset as u32, chunk)?;
888 write_offset += chunk.len();
889 }
890 /*
891 trace!("Wrote data, reading back for verification");
892
893 let mut buf: [u8; 4096] = [0; 4096];
894 let mut data_offset = 0;
895 let mut read_offset = self.dfu.from + offset;
896 for chunk in buf.chunks_mut(block_size) {
897 flash.read(read_offset as u32, chunk).await?;
898 trace!("Read chunk at {}: {:?}", read_offset, chunk);
899 assert_eq!(&data[data_offset..data_offset + block_size], chunk);
900 read_offset += chunk.len();
901 data_offset += chunk.len();
902 }
903 */
904
905 Ok(())
906 }
705} 907}
706 908
707#[cfg(test)] 909#[cfg(test)]