diff options
| author | Ulf Lilleengen <[email protected]> | 2023-12-14 19:56:04 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-12-14 19:56:04 +0000 |
| commit | 5ec2fbe3a2ce3234ed6477fe5923c3d2425b55a7 (patch) | |
| tree | 94b50831f7e5d606643eba7e0c9c1e5c7bf1ec86 /embassy-boot | |
| parent | 485765320aaef82adbd4865b25e7171fb8f4041a (diff) | |
| parent | 33e8943e5b6e637b82f13c77bd88bb56d55ab515 (diff) | |
Merge pull request #2284 from Redrield/feature/embassy-usb-dfu
Add embassy-usb-dfu crate, with related modifications to embassy-boot
Diffstat (limited to 'embassy-boot')
| -rw-r--r-- | embassy-boot/boot/src/boot_loader.rs | 4 | ||||
| -rw-r--r-- | embassy-boot/boot/src/firmware_updater/asynch.rs | 23 | ||||
| -rw-r--r-- | embassy-boot/boot/src/firmware_updater/blocking.rs | 27 | ||||
| -rw-r--r-- | embassy-boot/boot/src/lib.rs | 3 | ||||
| -rw-r--r-- | embassy-boot/stm32/src/lib.rs | 9 |
5 files changed, 59 insertions, 7 deletions
diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 65b12dc5f..e568001bc 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs | |||
| @@ -5,7 +5,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; | |||
| 5 | use embassy_sync::blocking_mutex::Mutex; | 5 | use embassy_sync::blocking_mutex::Mutex; |
| 6 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; | 6 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; |
| 7 | 7 | ||
| 8 | use crate::{State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; | 8 | use crate::{State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; |
| 9 | 9 | ||
| 10 | /// Errors returned by bootloader | 10 | /// Errors returned by bootloader |
| 11 | #[derive(PartialEq, Eq, Debug)] | 11 | #[derive(PartialEq, Eq, Debug)] |
| @@ -371,6 +371,8 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S | |||
| 371 | 371 | ||
| 372 | if !state_word.iter().any(|&b| b != SWAP_MAGIC) { | 372 | if !state_word.iter().any(|&b| b != SWAP_MAGIC) { |
| 373 | Ok(State::Swap) | 373 | Ok(State::Swap) |
| 374 | } else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) { | ||
| 375 | Ok(State::DfuDetach) | ||
| 374 | } else { | 376 | } else { |
| 375 | Ok(State::Boot) | 377 | Ok(State::Boot) |
| 376 | } | 378 | } |
diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index ae713bb6f..d8d85c3d2 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs | |||
| @@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; | |||
| 6 | use embedded_storage_async::nor_flash::NorFlash; | 6 | use embedded_storage_async::nor_flash::NorFlash; |
| 7 | 7 | ||
| 8 | use super::FirmwareUpdaterConfig; | 8 | use super::FirmwareUpdaterConfig; |
| 9 | use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; | 9 | use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; |
| 10 | 10 | ||
| 11 | /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to | 11 | /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to |
| 12 | /// 'mess up' the internal bootloader state | 12 | /// 'mess up' the internal bootloader state |
| @@ -161,6 +161,12 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 161 | self.state.mark_updated().await | 161 | self.state.mark_updated().await |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | /// Mark to trigger USB DFU on next boot. | ||
| 165 | pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { | ||
| 166 | self.state.verify_booted().await?; | ||
| 167 | self.state.mark_dfu().await | ||
| 168 | } | ||
| 169 | |||
| 164 | /// Mark firmware boot successful and stop rollback on reset. | 170 | /// Mark firmware boot successful and stop rollback on reset. |
| 165 | pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 171 | pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 166 | self.state.mark_booted().await | 172 | self.state.mark_booted().await |
| @@ -207,6 +213,16 @@ pub struct FirmwareState<'d, STATE> { | |||
| 207 | } | 213 | } |
| 208 | 214 | ||
| 209 | impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | 215 | impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { |
| 216 | /// Create a firmware state instance from a FirmwareUpdaterConfig with a buffer for magic content and state partition. | ||
| 217 | /// | ||
| 218 | /// # Safety | ||
| 219 | /// | ||
| 220 | /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from | ||
| 221 | /// and written to. | ||
| 222 | pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self { | ||
| 223 | Self::new(config.state, aligned) | ||
| 224 | } | ||
| 225 | |||
| 210 | /// Create a firmware state instance with a buffer for magic content and state partition. | 226 | /// Create a firmware state instance with a buffer for magic content and state partition. |
| 211 | /// | 227 | /// |
| 212 | /// # Safety | 228 | /// # Safety |
| @@ -247,6 +263,11 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | |||
| 247 | self.set_magic(SWAP_MAGIC).await | 263 | self.set_magic(SWAP_MAGIC).await |
| 248 | } | 264 | } |
| 249 | 265 | ||
| 266 | /// Mark to trigger USB DFU on next boot. | ||
| 267 | pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { | ||
| 268 | self.set_magic(DFU_DETACH_MAGIC).await | ||
| 269 | } | ||
| 270 | |||
| 250 | /// Mark firmware boot successful and stop rollback on reset. | 271 | /// Mark firmware boot successful and stop rollback on reset. |
| 251 | pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 272 | pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 252 | self.set_magic(BOOT_MAGIC).await | 273 | self.set_magic(BOOT_MAGIC).await |
diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs index 76e4264a0..c4c142169 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs | |||
| @@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; | |||
| 6 | use embedded_storage::nor_flash::NorFlash; | 6 | use embedded_storage::nor_flash::NorFlash; |
| 7 | 7 | ||
| 8 | use super::FirmwareUpdaterConfig; | 8 | use super::FirmwareUpdaterConfig; |
| 9 | use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; | 9 | use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; |
| 10 | 10 | ||
| 11 | /// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to | 11 | /// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to |
| 12 | /// 'mess up' the internal bootloader state | 12 | /// 'mess up' the internal bootloader state |
| @@ -168,6 +168,12 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 168 | self.state.mark_updated() | 168 | self.state.mark_updated() |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | /// Mark to trigger USB DFU device on next boot. | ||
| 172 | pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { | ||
| 173 | self.state.verify_booted()?; | ||
| 174 | self.state.mark_dfu() | ||
| 175 | } | ||
| 176 | |||
| 171 | /// Mark firmware boot successful and stop rollback on reset. | 177 | /// Mark firmware boot successful and stop rollback on reset. |
| 172 | pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 178 | pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 173 | self.state.mark_booted() | 179 | self.state.mark_booted() |
| @@ -213,6 +219,16 @@ pub struct BlockingFirmwareState<'d, STATE> { | |||
| 213 | } | 219 | } |
| 214 | 220 | ||
| 215 | impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | 221 | impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { |
| 222 | /// Creates a firmware state instance from a FirmwareUpdaterConfig, with a buffer for magic content and state partition. | ||
| 223 | /// | ||
| 224 | /// # Safety | ||
| 225 | /// | ||
| 226 | /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from | ||
| 227 | /// and written to. | ||
| 228 | pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self { | ||
| 229 | Self::new(config.state, aligned) | ||
| 230 | } | ||
| 231 | |||
| 216 | /// Create a firmware state instance with a buffer for magic content and state partition. | 232 | /// Create a firmware state instance with a buffer for magic content and state partition. |
| 217 | /// | 233 | /// |
| 218 | /// # Safety | 234 | /// # Safety |
| @@ -226,7 +242,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 226 | 242 | ||
| 227 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | 243 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 228 | fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 244 | fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 229 | if self.get_state()? == State::Boot { | 245 | if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach { |
| 230 | Ok(()) | 246 | Ok(()) |
| 231 | } else { | 247 | } else { |
| 232 | Err(FirmwareUpdaterError::BadState) | 248 | Err(FirmwareUpdaterError::BadState) |
| @@ -243,6 +259,8 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 243 | 259 | ||
| 244 | if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { | 260 | if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { |
| 245 | Ok(State::Swap) | 261 | Ok(State::Swap) |
| 262 | } else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) { | ||
| 263 | Ok(State::DfuDetach) | ||
| 246 | } else { | 264 | } else { |
| 247 | Ok(State::Boot) | 265 | Ok(State::Boot) |
| 248 | } | 266 | } |
| @@ -253,6 +271,11 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 253 | self.set_magic(SWAP_MAGIC) | 271 | self.set_magic(SWAP_MAGIC) |
| 254 | } | 272 | } |
| 255 | 273 | ||
| 274 | /// Mark to trigger USB DFU on next boot. | ||
| 275 | pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> { | ||
| 276 | self.set_magic(DFU_DETACH_MAGIC) | ||
| 277 | } | ||
| 278 | |||
| 256 | /// Mark firmware boot successful and stop rollback on reset. | 279 | /// Mark firmware boot successful and stop rollback on reset. |
| 257 | pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 280 | pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 258 | self.set_magic(BOOT_MAGIC) | 281 | self.set_magic(BOOT_MAGIC) |
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 9e70a4dca..15b69f69d 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs | |||
| @@ -23,6 +23,7 @@ pub use firmware_updater::{ | |||
| 23 | 23 | ||
| 24 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; | 24 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; |
| 25 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; | 25 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; |
| 26 | pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0; | ||
| 26 | 27 | ||
| 27 | /// The state of the bootloader after running prepare. | 28 | /// The state of the bootloader after running prepare. |
| 28 | #[derive(PartialEq, Eq, Debug)] | 29 | #[derive(PartialEq, Eq, Debug)] |
| @@ -32,6 +33,8 @@ pub enum State { | |||
| 32 | Boot, | 33 | Boot, |
| 33 | /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. | 34 | /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. |
| 34 | Swap, | 35 | Swap, |
| 36 | /// Application has received a request to reboot into DFU mode to apply an update. | ||
| 37 | DfuDetach, | ||
| 35 | } | 38 | } |
| 36 | 39 | ||
| 37 | /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. | 40 | /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. |
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index c418cb262..4b4091ac9 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs | |||
| @@ -10,7 +10,10 @@ pub use embassy_boot::{ | |||
| 10 | use embedded_storage::nor_flash::NorFlash; | 10 | use embedded_storage::nor_flash::NorFlash; |
| 11 | 11 | ||
| 12 | /// A bootloader for STM32 devices. | 12 | /// A bootloader for STM32 devices. |
| 13 | pub struct BootLoader; | 13 | pub struct BootLoader { |
| 14 | /// The reported state of the bootloader after preparing for boot | ||
| 15 | pub state: State, | ||
| 16 | } | ||
| 14 | 17 | ||
| 15 | impl BootLoader { | 18 | impl BootLoader { |
| 16 | /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware | 19 | /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware |
| @@ -19,8 +22,8 @@ impl BootLoader { | |||
| 19 | ) -> Self { | 22 | ) -> Self { |
| 20 | let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); | 23 | let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); |
| 21 | let mut boot = embassy_boot::BootLoader::new(config); | 24 | let mut boot = embassy_boot::BootLoader::new(config); |
| 22 | boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error"); | 25 | let state = boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error"); |
| 23 | Self | 26 | Self { state } |
| 24 | } | 27 | } |
| 25 | 28 | ||
| 26 | /// Boots the application. | 29 | /// Boots the application. |
