Merge #740
740: Allow using separate page sizes for state and dfu r=lulf a=lulf * Less generics on bootloader. Keep PAGE_SIZE as a common multiple of DFU and ACTIVE page sizes. * Document restriction * Add unit tests for different page sizes Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
This commit is contained in:
		@@ -27,6 +27,7 @@ The bootloader divides the storage into 4 main partitions, configured by a linke
 | 
				
			|||||||
* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application.
 | 
					* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application.
 | 
				
			||||||
* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped.
 | 
					* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash, but they have to support compatible page sizes.
 | 
					The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes.
 | 
				
			||||||
 | 
					The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice.
 | 
					The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,6 +62,7 @@ where
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
pub trait FlashConfig {
 | 
					pub trait FlashConfig {
 | 
				
			||||||
    const BLOCK_SIZE: usize;
 | 
					    const BLOCK_SIZE: usize;
 | 
				
			||||||
 | 
					    const ERASE_VALUE: u8;
 | 
				
			||||||
    type FLASH: NorFlash + ReadNorFlash;
 | 
					    type FLASH: NorFlash + ReadNorFlash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn flash(&mut self) -> &mut Self::FLASH;
 | 
					    fn flash(&mut self) -> &mut Self::FLASH;
 | 
				
			||||||
@@ -83,7 +84,9 @@ pub trait FlashProvider {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// BootLoader works with any flash implementing embedded_storage and can also work with
 | 
					/// BootLoader works with any flash implementing embedded_storage and can also work with
 | 
				
			||||||
/// different page sizes and flash write sizes.
 | 
					/// different page sizes and flash write sizes.
 | 
				
			||||||
pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> {
 | 
					///
 | 
				
			||||||
 | 
					/// The PAGE_SIZE const parameter must be a multiple of the ACTIVE and DFU page sizes.
 | 
				
			||||||
 | 
					pub struct BootLoader<const PAGE_SIZE: usize> {
 | 
				
			||||||
    // Page with current state of bootloader. The state partition has the following format:
 | 
					    // Page with current state of bootloader. The state partition has the following format:
 | 
				
			||||||
    // | Range          | Description                                                                      |
 | 
					    // | Range          | Description                                                                      |
 | 
				
			||||||
    // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
 | 
					    // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
 | 
				
			||||||
@@ -95,16 +98,12 @@ pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERA
 | 
				
			|||||||
    dfu: Partition,
 | 
					    dfu: Partition,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
 | 
					impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
 | 
				
			||||||
    BootLoader<PAGE_SIZE, WRITE_SIZE, ERASE_VALUE>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
 | 
					    pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
 | 
				
			||||||
        assert_eq!(active.len() % PAGE_SIZE, 0);
 | 
					        assert_eq!(active.len() % PAGE_SIZE, 0);
 | 
				
			||||||
        assert_eq!(dfu.len() % PAGE_SIZE, 0);
 | 
					        assert_eq!(dfu.len() % PAGE_SIZE, 0);
 | 
				
			||||||
        // DFU partition must have an extra page
 | 
					        // DFU partition must have an extra page
 | 
				
			||||||
        assert!(dfu.len() - active.len() >= PAGE_SIZE);
 | 
					        assert!(dfu.len() - active.len() >= PAGE_SIZE);
 | 
				
			||||||
        // Ensure we have enough progress pages to store copy progress
 | 
					 | 
				
			||||||
        assert!(active.len() / PAGE_SIZE >= (state.len() - WRITE_SIZE) / PAGE_SIZE);
 | 
					 | 
				
			||||||
        Self { active, dfu, state }
 | 
					        Self { active, dfu, state }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -195,7 +194,19 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
 | 
				
			|||||||
    /// |       DFU |            3 |      3 |      2 |      1 |      3 |
 | 
					    /// |       DFU |            3 |      3 |      2 |      1 |      3 |
 | 
				
			||||||
    /// +-----------+--------------+--------+--------+--------+--------+
 | 
					    /// +-----------+--------------+--------+--------+--------+--------+
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    pub fn prepare_boot<P: FlashProvider>(&mut self, p: &mut P) -> Result<State, BootError> {
 | 
					    pub fn prepare_boot<P: FlashProvider>(&mut self, p: &mut P) -> Result<State, BootError>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					        [(); <<P as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Ensure we have enough progress pages to store copy progress
 | 
				
			||||||
 | 
					        assert!(
 | 
				
			||||||
 | 
					            self.active.len() / PAGE_SIZE
 | 
				
			||||||
 | 
					                <= (self.state.len()
 | 
				
			||||||
 | 
					                    - <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE)
 | 
				
			||||||
 | 
					                    / <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Copy contents from partition N to active
 | 
					        // Copy contents from partition N to active
 | 
				
			||||||
        let state = self.read_state(p.state())?;
 | 
					        let state = self.read_state(p.state())?;
 | 
				
			||||||
        match state {
 | 
					        match state {
 | 
				
			||||||
@@ -214,10 +225,16 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    // Overwrite magic and reset progress
 | 
					                    // Overwrite magic and reset progress
 | 
				
			||||||
                    let fstate = p.state().flash();
 | 
					                    let fstate = p.state().flash();
 | 
				
			||||||
                    let aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
 | 
					                    let aligned = Aligned(
 | 
				
			||||||
 | 
					                        [!P::STATE::ERASE_VALUE;
 | 
				
			||||||
 | 
					                            <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE],
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
                    fstate.write(self.state.from as u32, &aligned.0)?;
 | 
					                    fstate.write(self.state.from as u32, &aligned.0)?;
 | 
				
			||||||
                    fstate.erase(self.state.from as u32, self.state.to as u32)?;
 | 
					                    fstate.erase(self.state.from as u32, self.state.to as u32)?;
 | 
				
			||||||
                    let aligned = Aligned([BOOT_MAGIC; WRITE_SIZE]);
 | 
					                    let aligned = Aligned(
 | 
				
			||||||
 | 
					                        [BOOT_MAGIC;
 | 
				
			||||||
 | 
					                            <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE],
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
                    fstate.write(self.state.from as u32, &aligned.0)?;
 | 
					                    fstate.write(self.state.from as u32, &aligned.0)?;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -226,33 +243,44 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
 | 
				
			|||||||
        Ok(state)
 | 
					        Ok(state)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn is_swapped<P: FlashConfig>(&mut self, p: &mut P) -> Result<bool, BootError> {
 | 
					    fn is_swapped<P: FlashConfig>(&mut self, p: &mut P) -> Result<bool, BootError>
 | 
				
			||||||
        let page_count = self.active.len() / PAGE_SIZE;
 | 
					    where
 | 
				
			||||||
 | 
					        [(); P::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let page_count = self.active.len() / P::FLASH::ERASE_SIZE;
 | 
				
			||||||
        let progress = self.current_progress(p)?;
 | 
					        let progress = self.current_progress(p)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(progress >= page_count * 2)
 | 
					        Ok(progress >= page_count * 2)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError> {
 | 
					    fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError>
 | 
				
			||||||
        let max_index = ((self.state.len() - WRITE_SIZE) / WRITE_SIZE) - 1;
 | 
					    where
 | 
				
			||||||
 | 
					        [(); P::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let write_size = P::FLASH::WRITE_SIZE;
 | 
				
			||||||
 | 
					        let max_index = ((self.state.len() - write_size) / write_size) - 1;
 | 
				
			||||||
        let flash = p.flash();
 | 
					        let flash = p.flash();
 | 
				
			||||||
        let mut aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
 | 
					        let mut aligned = Aligned([!P::ERASE_VALUE; P::FLASH::WRITE_SIZE]);
 | 
				
			||||||
        for i in 0..max_index {
 | 
					        for i in 0..max_index {
 | 
				
			||||||
            flash.read(
 | 
					            flash.read(
 | 
				
			||||||
                (self.state.from + WRITE_SIZE + i * WRITE_SIZE) as u32,
 | 
					                (self.state.from + write_size + i * write_size) as u32,
 | 
				
			||||||
                &mut aligned.0,
 | 
					                &mut aligned.0,
 | 
				
			||||||
            )?;
 | 
					            )?;
 | 
				
			||||||
            if aligned.0 == [ERASE_VALUE; WRITE_SIZE] {
 | 
					            if aligned.0 == [P::ERASE_VALUE; P::FLASH::WRITE_SIZE] {
 | 
				
			||||||
                return Ok(i);
 | 
					                return Ok(i);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(max_index)
 | 
					        Ok(max_index)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> {
 | 
					    fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        [(); P::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        let flash = p.flash();
 | 
					        let flash = p.flash();
 | 
				
			||||||
        let w = self.state.from + WRITE_SIZE + idx * WRITE_SIZE;
 | 
					        let write_size = P::FLASH::WRITE_SIZE;
 | 
				
			||||||
        let aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
 | 
					        let w = self.state.from + write_size + idx * write_size;
 | 
				
			||||||
 | 
					        let aligned = Aligned([!P::ERASE_VALUE; P::FLASH::WRITE_SIZE]);
 | 
				
			||||||
        flash.write(w as u32, &aligned.0)?;
 | 
					        flash.write(w as u32, &aligned.0)?;
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -271,7 +299,10 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
 | 
				
			|||||||
        from_page: usize,
 | 
					        from_page: usize,
 | 
				
			||||||
        to_page: usize,
 | 
					        to_page: usize,
 | 
				
			||||||
        p: &mut P,
 | 
					        p: &mut P,
 | 
				
			||||||
    ) -> Result<(), BootError> {
 | 
					    ) -> Result<(), BootError>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
 | 
					        let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
 | 
				
			||||||
        if self.current_progress(p.state())? <= idx {
 | 
					        if self.current_progress(p.state())? <= idx {
 | 
				
			||||||
            let mut offset = from_page;
 | 
					            let mut offset = from_page;
 | 
				
			||||||
@@ -300,7 +331,10 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
 | 
				
			|||||||
        from_page: usize,
 | 
					        from_page: usize,
 | 
				
			||||||
        to_page: usize,
 | 
					        to_page: usize,
 | 
				
			||||||
        p: &mut P,
 | 
					        p: &mut P,
 | 
				
			||||||
    ) -> Result<(), BootError> {
 | 
					    ) -> Result<(), BootError>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
 | 
					        let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
 | 
				
			||||||
        if self.current_progress(p.state())? <= idx {
 | 
					        if self.current_progress(p.state())? <= idx {
 | 
				
			||||||
            let mut offset = from_page;
 | 
					            let mut offset = from_page;
 | 
				
			||||||
@@ -323,7 +357,10 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
 | 
				
			|||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> {
 | 
					    fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        let page_count = self.active.len() / PAGE_SIZE;
 | 
					        let page_count = self.active.len() / PAGE_SIZE;
 | 
				
			||||||
        trace!("Page count: {}", page_count);
 | 
					        trace!("Page count: {}", page_count);
 | 
				
			||||||
        for page in 0..page_count {
 | 
					        for page in 0..page_count {
 | 
				
			||||||
@@ -344,7 +381,10 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
 | 
				
			|||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn revert<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> {
 | 
					    fn revert<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        [(); <<P as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        let page_count = self.active.len() / PAGE_SIZE;
 | 
					        let page_count = self.active.len() / PAGE_SIZE;
 | 
				
			||||||
        for page in 0..page_count {
 | 
					        for page in 0..page_count {
 | 
				
			||||||
            // Copy the bad active page to the DFU page
 | 
					            // Copy the bad active page to the DFU page
 | 
				
			||||||
@@ -361,12 +401,15 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
 | 
				
			|||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError> {
 | 
					    fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError>
 | 
				
			||||||
        let mut magic: [u8; WRITE_SIZE] = [0; WRITE_SIZE];
 | 
					    where
 | 
				
			||||||
 | 
					        [(); P::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let mut magic: [u8; P::FLASH::WRITE_SIZE] = [0; P::FLASH::WRITE_SIZE];
 | 
				
			||||||
        let flash = p.flash();
 | 
					        let flash = p.flash();
 | 
				
			||||||
        flash.read(self.state.from as u32, &mut magic)?;
 | 
					        flash.read(self.state.from as u32, &mut magic)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if magic == [SWAP_MAGIC; WRITE_SIZE] {
 | 
					        if magic == [SWAP_MAGIC; P::FLASH::WRITE_SIZE] {
 | 
				
			||||||
            Ok(State::Swap)
 | 
					            Ok(State::Swap)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            Ok(State::Boot)
 | 
					            Ok(State::Boot)
 | 
				
			||||||
@@ -375,14 +418,14 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Convenience provider that uses a single flash for everything
 | 
					/// Convenience provider that uses a single flash for everything
 | 
				
			||||||
pub struct SingleFlashProvider<'a, F>
 | 
					pub struct SingleFlashProvider<'a, F, const ERASE_VALUE: u8 = 0xFF>
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
    F: NorFlash + ReadNorFlash,
 | 
					    F: NorFlash + ReadNorFlash,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    config: SingleFlashConfig<'a, F>,
 | 
					    config: SingleFlashConfig<'a, F, ERASE_VALUE>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'a, F> SingleFlashProvider<'a, F>
 | 
					impl<'a, F, const ERASE_VALUE: u8> SingleFlashProvider<'a, F, ERASE_VALUE>
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
    F: NorFlash + ReadNorFlash,
 | 
					    F: NorFlash + ReadNorFlash,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -393,7 +436,7 @@ where
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct SingleFlashConfig<'a, F>
 | 
					pub struct SingleFlashConfig<'a, F, const ERASE_VALUE: u8 = 0xFF>
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
    F: NorFlash + ReadNorFlash,
 | 
					    F: NorFlash + ReadNorFlash,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -419,17 +462,66 @@ where
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'a, F> FlashConfig for SingleFlashConfig<'a, F>
 | 
					impl<'a, F, const ERASE_VALUE: u8> FlashConfig for SingleFlashConfig<'a, F, ERASE_VALUE>
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
    F: NorFlash + ReadNorFlash,
 | 
					    F: NorFlash + ReadNorFlash,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    const BLOCK_SIZE: usize = F::ERASE_SIZE;
 | 
					    const BLOCK_SIZE: usize = F::ERASE_SIZE;
 | 
				
			||||||
 | 
					    const ERASE_VALUE: u8 = ERASE_VALUE;
 | 
				
			||||||
    type FLASH = F;
 | 
					    type FLASH = F;
 | 
				
			||||||
    fn flash(&mut self) -> &mut F {
 | 
					    fn flash(&mut self) -> &mut F {
 | 
				
			||||||
        self.flash
 | 
					        self.flash
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Convenience provider that uses a single flash for everything
 | 
				
			||||||
 | 
					pub struct MultiFlashProvider<'a, ACTIVE, STATE, DFU>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    ACTIVE: NorFlash + ReadNorFlash,
 | 
				
			||||||
 | 
					    STATE: NorFlash + ReadNorFlash,
 | 
				
			||||||
 | 
					    DFU: NorFlash + ReadNorFlash,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    active: SingleFlashConfig<'a, ACTIVE>,
 | 
				
			||||||
 | 
					    state: SingleFlashConfig<'a, STATE>,
 | 
				
			||||||
 | 
					    dfu: SingleFlashConfig<'a, DFU>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, ACTIVE, STATE, DFU> MultiFlashProvider<'a, ACTIVE, STATE, DFU>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    ACTIVE: NorFlash + ReadNorFlash,
 | 
				
			||||||
 | 
					    STATE: NorFlash + ReadNorFlash,
 | 
				
			||||||
 | 
					    DFU: NorFlash + ReadNorFlash,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            active: SingleFlashConfig { flash: active },
 | 
				
			||||||
 | 
					            state: SingleFlashConfig { flash: state },
 | 
				
			||||||
 | 
					            dfu: SingleFlashConfig { flash: dfu },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, ACTIVE, STATE, DFU> FlashProvider for MultiFlashProvider<'a, ACTIVE, STATE, DFU>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    ACTIVE: NorFlash + ReadNorFlash,
 | 
				
			||||||
 | 
					    STATE: NorFlash + ReadNorFlash,
 | 
				
			||||||
 | 
					    DFU: NorFlash + ReadNorFlash,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type STATE = SingleFlashConfig<'a, STATE>;
 | 
				
			||||||
 | 
					    type ACTIVE = SingleFlashConfig<'a, ACTIVE>;
 | 
				
			||||||
 | 
					    type DFU = SingleFlashConfig<'a, DFU>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn active(&mut self) -> &mut Self::ACTIVE {
 | 
				
			||||||
 | 
					        &mut self.active
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn dfu(&mut self) -> &mut Self::DFU {
 | 
				
			||||||
 | 
					        &mut self.dfu
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn state(&mut self) -> &mut Self::STATE {
 | 
				
			||||||
 | 
					        &mut self.state
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
 | 
					/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
 | 
				
			||||||
/// 'mess up' the internal bootloader state
 | 
					/// 'mess up' the internal bootloader state
 | 
				
			||||||
pub struct FirmwareUpdater {
 | 
					pub struct FirmwareUpdater {
 | 
				
			||||||
@@ -481,7 +573,7 @@ impl FirmwareUpdater {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Instruct bootloader that DFU should commence at next boot.
 | 
					    /// Instruct bootloader that DFU should commence at next boot.
 | 
				
			||||||
    /// Must be provided with an aligned buffer to use for reading and writing magic;
 | 
					    /// Must be provided with an aligned buffer to use for reading and writing magic;
 | 
				
			||||||
    pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error>
 | 
					    pub async fn update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error>
 | 
				
			||||||
    where
 | 
					    where
 | 
				
			||||||
        [(); F::WRITE_SIZE]:,
 | 
					        [(); F::WRITE_SIZE]:,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -592,10 +684,6 @@ mod tests {
 | 
				
			|||||||
    use embedded_storage_async::nor_flash::AsyncReadNorFlash;
 | 
					    use embedded_storage_async::nor_flash::AsyncReadNorFlash;
 | 
				
			||||||
    use futures::executor::block_on;
 | 
					    use futures::executor::block_on;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const STATE: Partition = Partition::new(0, 4096);
 | 
					 | 
				
			||||||
    const ACTIVE: Partition = Partition::new(4096, 61440);
 | 
					 | 
				
			||||||
    const DFU: Partition = Partition::new(61440, 122880);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn test_bad_magic() {
 | 
					    fn test_bad_magic() {
 | 
				
			||||||
@@ -613,19 +701,25 @@ mod tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn test_boot_state() {
 | 
					    fn test_boot_state() {
 | 
				
			||||||
        let mut flash = MemFlash([0xff; 131072]);
 | 
					        const STATE: Partition = Partition::new(0, 4096);
 | 
				
			||||||
 | 
					        const ACTIVE: Partition = Partition::new(4096, 61440);
 | 
				
			||||||
 | 
					        const DFU: Partition = Partition::new(61440, 122880);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]);
 | 
				
			||||||
        flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
 | 
					        flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
 | 
				
			||||||
        let mut flash = SingleFlashProvider::new(&mut flash);
 | 
					        let mut flash = SingleFlashProvider::new(&mut flash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE);
 | 
					        let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap());
 | 
					        assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn test_swap_state() {
 | 
					    fn test_swap_state() {
 | 
				
			||||||
        env_logger::init();
 | 
					        const STATE: Partition = Partition::new(0, 4096);
 | 
				
			||||||
        let mut flash = MemFlash([0xff; 131072]);
 | 
					        const ACTIVE: Partition = Partition::new(4096, 61440);
 | 
				
			||||||
 | 
					        const DFU: Partition = Partition::new(61440, 122880);
 | 
				
			||||||
 | 
					        let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
 | 
					        let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
 | 
				
			||||||
        let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
 | 
					        let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
 | 
				
			||||||
@@ -634,14 +728,14 @@ mod tests {
 | 
				
			|||||||
            flash.0[i] = original[i - ACTIVE.from];
 | 
					            flash.0[i] = original[i - ACTIVE.from];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE);
 | 
					        let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE);
 | 
				
			||||||
        let mut updater = FirmwareUpdater::new(DFU, STATE);
 | 
					        let mut updater = FirmwareUpdater::new(DFU, STATE);
 | 
				
			||||||
        let mut offset = 0;
 | 
					        let mut offset = 0;
 | 
				
			||||||
        for chunk in update.chunks(4096) {
 | 
					        for chunk in update.chunks(4096) {
 | 
				
			||||||
            block_on(updater.write_firmware(offset, &chunk, &mut flash, 4096)).unwrap();
 | 
					            block_on(updater.write_firmware(offset, &chunk, &mut flash, 4096)).unwrap();
 | 
				
			||||||
            offset += chunk.len();
 | 
					            offset += chunk.len();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        block_on(updater.mark_update(&mut flash)).unwrap();
 | 
					        block_on(updater.update(&mut flash)).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_eq!(
 | 
				
			||||||
            State::Swap,
 | 
					            State::Swap,
 | 
				
			||||||
@@ -686,27 +780,131 @@ mod tests {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct MemFlash([u8; 131072]);
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_separate_flash_active_page_biggest() {
 | 
				
			||||||
 | 
					        const STATE: Partition = Partition::new(2048, 4096);
 | 
				
			||||||
 | 
					        const ACTIVE: Partition = Partition::new(4096, 16384);
 | 
				
			||||||
 | 
					        const DFU: Partition = Partition::new(0, 16384);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl NorFlash for MemFlash {
 | 
					        let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]);
 | 
				
			||||||
        const WRITE_SIZE: usize = 4;
 | 
					        let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]);
 | 
				
			||||||
        const ERASE_SIZE: usize = 4096;
 | 
					        let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
 | 
				
			||||||
 | 
					        let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
 | 
					            active.0[i] = original[i - ACTIVE.from];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut updater = FirmwareUpdater::new(DFU, STATE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut offset = 0;
 | 
				
			||||||
 | 
					        for chunk in update.chunks(2048) {
 | 
				
			||||||
 | 
					            block_on(updater.write_firmware(offset, &chunk, &mut dfu, chunk.len())).unwrap();
 | 
				
			||||||
 | 
					            offset += chunk.len();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        block_on(updater.update(&mut state)).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE);
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            State::Swap,
 | 
				
			||||||
 | 
					            bootloader
 | 
				
			||||||
 | 
					                .prepare_boot(&mut MultiFlashProvider::new(
 | 
				
			||||||
 | 
					                    &mut active,
 | 
				
			||||||
 | 
					                    &mut state,
 | 
				
			||||||
 | 
					                    &mut dfu,
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					                .unwrap()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
 | 
					            assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // First DFU page is untouched
 | 
				
			||||||
 | 
					        for i in DFU.from + 4096..DFU.to {
 | 
				
			||||||
 | 
					            assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_separate_flash_dfu_page_biggest() {
 | 
				
			||||||
 | 
					        const STATE: Partition = Partition::new(2048, 4096);
 | 
				
			||||||
 | 
					        const ACTIVE: Partition = Partition::new(4096, 16384);
 | 
				
			||||||
 | 
					        const DFU: Partition = Partition::new(0, 16384);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]);
 | 
				
			||||||
 | 
					        let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]);
 | 
				
			||||||
 | 
					        let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
 | 
				
			||||||
 | 
					        let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
 | 
					            active.0[i] = original[i - ACTIVE.from];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut updater = FirmwareUpdater::new(DFU, STATE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut offset = 0;
 | 
				
			||||||
 | 
					        for chunk in update.chunks(4096) {
 | 
				
			||||||
 | 
					            block_on(updater.write_firmware(offset, &chunk, &mut dfu, chunk.len())).unwrap();
 | 
				
			||||||
 | 
					            offset += chunk.len();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        block_on(updater.update(&mut state)).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE);
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            State::Swap,
 | 
				
			||||||
 | 
					            bootloader
 | 
				
			||||||
 | 
					                .prepare_boot(&mut MultiFlashProvider::new(
 | 
				
			||||||
 | 
					                    &mut active,
 | 
				
			||||||
 | 
					                    &mut state,
 | 
				
			||||||
 | 
					                    &mut dfu,
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					                .unwrap()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in ACTIVE.from..ACTIVE.to {
 | 
				
			||||||
 | 
					            assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // First DFU page is untouched
 | 
				
			||||||
 | 
					        for i in DFU.from + 4096..DFU.to {
 | 
				
			||||||
 | 
					            assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>(
 | 
				
			||||||
 | 
					        [u8; SIZE],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
 | 
				
			||||||
 | 
					        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const WRITE_SIZE: usize = WRITE_SIZE;
 | 
				
			||||||
 | 
					        const ERASE_SIZE: usize = ERASE_SIZE;
 | 
				
			||||||
        fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
 | 
					        fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
 | 
				
			||||||
            let from = from as usize;
 | 
					            let from = from as usize;
 | 
				
			||||||
            let to = to as usize;
 | 
					            let to = to as usize;
 | 
				
			||||||
 | 
					            assert!(from % ERASE_SIZE == 0);
 | 
				
			||||||
 | 
					            assert!(
 | 
				
			||||||
 | 
					                to % ERASE_SIZE == 0,
 | 
				
			||||||
 | 
					                "To: {}, erase size: {}",
 | 
				
			||||||
 | 
					                to,
 | 
				
			||||||
 | 
					                ERASE_SIZE
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            for i in from..to {
 | 
					            for i in from..to {
 | 
				
			||||||
                self.0[i] = 0xFF;
 | 
					                self.0[i] = 0xFF;
 | 
				
			||||||
                self.0[i] = 0xFF;
 | 
					 | 
				
			||||||
                self.0[i] = 0xFF;
 | 
					 | 
				
			||||||
                self.0[i] = 0xFF;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Ok(())
 | 
					            Ok(())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
 | 
					        fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
            assert!(data.len() % 4 == 0);
 | 
					            assert!(data.len() % WRITE_SIZE == 0);
 | 
				
			||||||
            assert!(offset % 4 == 0);
 | 
					            assert!(offset as usize % WRITE_SIZE == 0);
 | 
				
			||||||
            assert!(offset as usize + data.len() < 131072);
 | 
					            assert!(offset as usize + data.len() <= SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
 | 
					            self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -714,11 +912,15 @@ mod tests {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl ErrorType for MemFlash {
 | 
					    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
 | 
				
			||||||
 | 
					        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        type Error = Infallible;
 | 
					        type Error = Infallible;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl ReadNorFlash for MemFlash {
 | 
					    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
 | 
				
			||||||
 | 
					        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        const READ_SIZE: usize = 4;
 | 
					        const READ_SIZE: usize = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
 | 
					        fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
 | 
				
			||||||
@@ -728,11 +930,13 @@ mod tests {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fn capacity(&self) -> usize {
 | 
					        fn capacity(&self) -> usize {
 | 
				
			||||||
            131072
 | 
					            SIZE
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl AsyncReadNorFlash for MemFlash {
 | 
					    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
 | 
				
			||||||
 | 
					        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        const READ_SIZE: usize = 4;
 | 
					        const READ_SIZE: usize = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
 | 
					        type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
 | 
				
			||||||
@@ -745,24 +949,25 @@ mod tests {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fn capacity(&self) -> usize {
 | 
					        fn capacity(&self) -> usize {
 | 
				
			||||||
            131072
 | 
					            SIZE
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl AsyncNorFlash for MemFlash {
 | 
					    impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
 | 
				
			||||||
        const WRITE_SIZE: usize = 4;
 | 
					        for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
 | 
				
			||||||
        const ERASE_SIZE: usize = 4096;
 | 
					    {
 | 
				
			||||||
 | 
					        const WRITE_SIZE: usize = WRITE_SIZE;
 | 
				
			||||||
 | 
					        const ERASE_SIZE: usize = ERASE_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        type EraseFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
 | 
					        type EraseFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
 | 
				
			||||||
        fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> {
 | 
					        fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> {
 | 
				
			||||||
            async move {
 | 
					            async move {
 | 
				
			||||||
                let from = from as usize;
 | 
					                let from = from as usize;
 | 
				
			||||||
                let to = to as usize;
 | 
					                let to = to as usize;
 | 
				
			||||||
 | 
					                assert!(from % ERASE_SIZE == 0);
 | 
				
			||||||
 | 
					                assert!(to % ERASE_SIZE == 0);
 | 
				
			||||||
                for i in from..to {
 | 
					                for i in from..to {
 | 
				
			||||||
                    self.0[i] = 0xFF;
 | 
					                    self.0[i] = 0xFF;
 | 
				
			||||||
                    self.0[i] = 0xFF;
 | 
					 | 
				
			||||||
                    self.0[i] = 0xFF;
 | 
					 | 
				
			||||||
                    self.0[i] = 0xFF;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                Ok(())
 | 
					                Ok(())
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -770,10 +975,17 @@ mod tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
 | 
					        type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
 | 
				
			||||||
        fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> {
 | 
					        fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> {
 | 
				
			||||||
 | 
					            info!("Writing {} bytes to 0x{:x}", data.len(), offset);
 | 
				
			||||||
            async move {
 | 
					            async move {
 | 
				
			||||||
                assert!(data.len() % 4 == 0);
 | 
					                assert!(data.len() % WRITE_SIZE == 0);
 | 
				
			||||||
                assert!(offset % 4 == 0);
 | 
					                assert!(offset as usize % WRITE_SIZE == 0);
 | 
				
			||||||
                assert!(offset as usize + data.len() < 131072);
 | 
					                assert!(
 | 
				
			||||||
 | 
					                    offset as usize + data.len() <= SIZE,
 | 
				
			||||||
 | 
					                    "OFFSET: {}, LEN: {}, FLASH SIZE: {}",
 | 
				
			||||||
 | 
					                    offset,
 | 
				
			||||||
 | 
					                    data.len(),
 | 
				
			||||||
 | 
					                    SIZE
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
 | 
					                self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,14 @@
 | 
				
			|||||||
#![no_std]
 | 
					#![no_std]
 | 
				
			||||||
#![feature(generic_associated_types)]
 | 
					#![feature(generic_associated_types)]
 | 
				
			||||||
#![feature(type_alias_impl_trait)]
 | 
					#![feature(type_alias_impl_trait)]
 | 
				
			||||||
 | 
					#![allow(incomplete_features)]
 | 
				
			||||||
 | 
					#![feature(generic_const_exprs)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod fmt;
 | 
					mod fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider};
 | 
					pub use embassy_boot::{
 | 
				
			||||||
 | 
					    FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
use embassy_nrf::{
 | 
					use embassy_nrf::{
 | 
				
			||||||
    nvmc::{Nvmc, PAGE_SIZE},
 | 
					    nvmc::{Nvmc, PAGE_SIZE},
 | 
				
			||||||
    peripherals::WDT,
 | 
					    peripherals::WDT,
 | 
				
			||||||
@@ -13,7 +17,7 @@ use embassy_nrf::{
 | 
				
			|||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
 | 
					use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct BootLoader {
 | 
					pub struct BootLoader {
 | 
				
			||||||
    boot: embassy_boot::BootLoader<PAGE_SIZE, 4, 0xFF>,
 | 
					    boot: embassy_boot::BootLoader<PAGE_SIZE>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BootLoader {
 | 
					impl BootLoader {
 | 
				
			||||||
@@ -62,7 +66,11 @@ impl BootLoader {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Boots the application without softdevice mechanisms
 | 
					    /// Boots the application without softdevice mechanisms
 | 
				
			||||||
    pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize {
 | 
					    pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        [(); <<F as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					        [(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        match self.boot.prepare_boot(flash) {
 | 
					        match self.boot.prepare_boot(flash) {
 | 
				
			||||||
            Ok(_) => self.boot.boot_address(),
 | 
					            Ok(_) => self.boot.boot_address(),
 | 
				
			||||||
            Err(_) => panic!("boot prepare error!"),
 | 
					            Err(_) => panic!("boot prepare error!"),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,21 @@
 | 
				
			|||||||
#![no_std]
 | 
					#![no_std]
 | 
				
			||||||
#![feature(generic_associated_types)]
 | 
					#![feature(generic_associated_types)]
 | 
				
			||||||
#![feature(type_alias_impl_trait)]
 | 
					#![feature(type_alias_impl_trait)]
 | 
				
			||||||
 | 
					#![allow(incomplete_features)]
 | 
				
			||||||
 | 
					#![feature(generic_const_exprs)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod fmt;
 | 
					mod fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State};
 | 
					pub use embassy_boot::{
 | 
				
			||||||
use embassy_stm32::flash::{ERASE_SIZE, ERASE_VALUE, WRITE_SIZE};
 | 
					    FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider, State,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use embedded_storage::nor_flash::NorFlash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct BootLoader {
 | 
					pub struct BootLoader<const PAGE_SIZE: usize> {
 | 
				
			||||||
    boot: embassy_boot::BootLoader<ERASE_SIZE, WRITE_SIZE, ERASE_VALUE>,
 | 
					    boot: embassy_boot::BootLoader<PAGE_SIZE>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BootLoader {
 | 
					impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
 | 
				
			||||||
    /// Create a new bootloader instance using parameters from linker script
 | 
					    /// Create a new bootloader instance using parameters from linker script
 | 
				
			||||||
    pub fn default() -> Self {
 | 
					    pub fn default() -> Self {
 | 
				
			||||||
        extern "C" {
 | 
					        extern "C" {
 | 
				
			||||||
@@ -57,7 +61,11 @@ impl BootLoader {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Boots the application
 | 
					    /// Boots the application
 | 
				
			||||||
    pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize {
 | 
					    pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        [(); <<F as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
 | 
				
			||||||
 | 
					        [(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        match self.boot.prepare_boot(flash) {
 | 
					        match self.boot.prepare_boot(flash) {
 | 
				
			||||||
            Ok(_) => self.boot.boot_address(),
 | 
					            Ok(_) => self.boot.boot_address(),
 | 
				
			||||||
            Err(_) => panic!("boot prepare error!"),
 | 
					            Err(_) => panic!("boot prepare error!"),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ use cortex_m_rt::{entry, exception};
 | 
				
			|||||||
use defmt_rtt as _;
 | 
					use defmt_rtt as _;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use embassy_boot_stm32::*;
 | 
					use embassy_boot_stm32::*;
 | 
				
			||||||
use embassy_stm32::flash::Flash;
 | 
					use embassy_stm32::flash::{Flash, ERASE_SIZE};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[entry]
 | 
					#[entry]
 | 
				
			||||||
fn main() -> ! {
 | 
					fn main() -> ! {
 | 
				
			||||||
@@ -21,7 +21,7 @@ fn main() -> ! {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut bl = BootLoader::default();
 | 
					    let mut bl: BootLoader<ERASE_SIZE> = BootLoader::default();
 | 
				
			||||||
    let mut flash = Flash::unlock(p.FLASH);
 | 
					    let mut flash = Flash::unlock(p.FLASH);
 | 
				
			||||||
    let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash));
 | 
					    let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash));
 | 
				
			||||||
    core::mem::drop(flash);
 | 
					    core::mem::drop(flash);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
 | 
				
			|||||||
                    .unwrap();
 | 
					                    .unwrap();
 | 
				
			||||||
                offset += chunk.len();
 | 
					                offset += chunk.len();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            updater.mark_update(&mut nvmc).await.unwrap();
 | 
					            updater.update(&mut nvmc).await.unwrap();
 | 
				
			||||||
            led.set_high();
 | 
					            led.set_high();
 | 
				
			||||||
            cortex_m::peripheral::SCB::sys_reset();
 | 
					            cortex_m::peripheral::SCB::sys_reset();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
 | 
				
			|||||||
        offset += chunk.len();
 | 
					        offset += chunk.len();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    updater.mark_update(&mut flash).await.unwrap();
 | 
					    updater.update(&mut flash).await.unwrap();
 | 
				
			||||||
    led.set_low();
 | 
					    led.set_low();
 | 
				
			||||||
    Timer::after(Duration::from_secs(1)).await;
 | 
					    Timer::after(Duration::from_secs(1)).await;
 | 
				
			||||||
    cortex_m::peripheral::SCB::sys_reset();
 | 
					    cortex_m::peripheral::SCB::sys_reset();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
 | 
				
			|||||||
        offset += chunk.len();
 | 
					        offset += chunk.len();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    updater.mark_update(&mut flash).await.unwrap();
 | 
					    updater.update(&mut flash).await.unwrap();
 | 
				
			||||||
    led.set_low();
 | 
					    led.set_low();
 | 
				
			||||||
    Timer::after(Duration::from_secs(1)).await;
 | 
					    Timer::after(Duration::from_secs(1)).await;
 | 
				
			||||||
    cortex_m::peripheral::SCB::sys_reset();
 | 
					    cortex_m::peripheral::SCB::sys_reset();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
 | 
				
			|||||||
            .unwrap();
 | 
					            .unwrap();
 | 
				
			||||||
        offset += chunk.len();
 | 
					        offset += chunk.len();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    updater.mark_update(&mut flash).await.unwrap();
 | 
					    updater.update(&mut flash).await.unwrap();
 | 
				
			||||||
    led.set_low();
 | 
					    led.set_low();
 | 
				
			||||||
    cortex_m::peripheral::SCB::sys_reset();
 | 
					    cortex_m::peripheral::SCB::sys_reset();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
 | 
				
			|||||||
            .unwrap();
 | 
					            .unwrap();
 | 
				
			||||||
        offset += chunk.len();
 | 
					        offset += chunk.len();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    updater.mark_update(&mut flash).await.unwrap();
 | 
					    updater.update(&mut flash).await.unwrap();
 | 
				
			||||||
    //defmt::info!("Marked as updated");
 | 
					    //defmt::info!("Marked as updated");
 | 
				
			||||||
    led.set_low();
 | 
					    led.set_low();
 | 
				
			||||||
    cortex_m::peripheral::SCB::sys_reset();
 | 
					    cortex_m::peripheral::SCB::sys_reset();
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user