diff options
| -rw-r--r-- | embassy-boot/boot/src/lib.rs | 216 |
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. | ||
| 817 | pub struct FirmwareWriter(Partition); | ||
| 818 | |||
| 819 | impl 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)] |
