aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)]