diff options
31 files changed, 933 insertions, 998 deletions
diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index f641d5e1c..415d7960f 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml | |||
| @@ -27,9 +27,10 @@ defmt = { version = "0.3", optional = true } | |||
| 27 | digest = "0.10" | 27 | digest = "0.10" |
| 28 | log = { version = "0.4", optional = true } | 28 | log = { version = "0.4", optional = true } |
| 29 | ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } | 29 | ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } |
| 30 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } | ||
| 30 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } | 31 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } |
| 31 | embedded-storage = "0.3.0" | 32 | embedded-storage = "0.3.0" |
| 32 | embedded-storage-async = { version = "0.4.0", optional = true} | 33 | embedded-storage-async = { version = "0.4.0", optional = true } |
| 33 | salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } | 34 | salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } |
| 34 | signature = { version = "1.6.4", default-features = false } | 35 | signature = { version = "1.6.4", default-features = false } |
| 35 | 36 | ||
| @@ -39,6 +40,7 @@ env_logger = "0.9" | |||
| 39 | rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version | 40 | rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version |
| 40 | futures = { version = "0.3", features = ["executor"] } | 41 | futures = { version = "0.3", features = ["executor"] } |
| 41 | sha1 = "0.10.5" | 42 | sha1 = "0.10.5" |
| 43 | critical-section = { version = "1.1.1", features = ["std"] } | ||
| 42 | 44 | ||
| 43 | [dev-dependencies.ed25519-dalek] | 45 | [dev-dependencies.ed25519-dalek] |
| 44 | default_features = false | 46 | default_features = false |
| @@ -48,7 +50,7 @@ features = ["rand", "std", "u32_backend"] | |||
| 48 | ed25519-dalek = ["dep:ed25519-dalek", "_verify"] | 50 | ed25519-dalek = ["dep:ed25519-dalek", "_verify"] |
| 49 | ed25519-salty = ["dep:salty", "_verify"] | 51 | ed25519-salty = ["dep:salty", "_verify"] |
| 50 | 52 | ||
| 51 | nightly = ["dep:embedded-storage-async"] | 53 | nightly = ["dep:embedded-storage-async", "embassy-embedded-hal/nightly"] |
| 52 | 54 | ||
| 53 | #Internal features | 55 | #Internal features |
| 54 | _verify = [] | 56 | _verify = [] |
diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index b959de2c4..a8c19197b 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs | |||
| @@ -1,6 +1,11 @@ | |||
| 1 | use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; | 1 | use core::cell::RefCell; |
| 2 | 2 | ||
| 3 | use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; | 3 | use embassy_embedded_hal::flash::partition::BlockingPartition; |
| 4 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 5 | use embassy_sync::blocking_mutex::Mutex; | ||
| 6 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; | ||
| 7 | |||
| 8 | use crate::{State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; | ||
| 4 | 9 | ||
| 5 | /// Errors returned by bootloader | 10 | /// Errors returned by bootloader |
| 6 | #[derive(PartialEq, Eq, Debug)] | 11 | #[derive(PartialEq, Eq, Debug)] |
| @@ -30,63 +35,96 @@ where | |||
| 30 | } | 35 | } |
| 31 | } | 36 | } |
| 32 | 37 | ||
| 33 | /// Trait defining the flash handles used for active and DFU partition. | 38 | /// Bootloader flash configuration holding the three flashes used by the bootloader |
| 34 | pub trait FlashConfig { | 39 | /// |
| 35 | /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value. | 40 | /// If only a single flash is actually used, then that flash should be partitioned into three partitions before use. |
| 36 | const STATE_ERASE_VALUE: u8 = 0xFF; | 41 | /// The easiest way to do this is to use [`BootLoaderConfig::from_linkerfile_blocking`] which will partition |
| 42 | /// the provided flash according to symbols defined in the linkerfile. | ||
| 43 | pub struct BootLoaderConfig<ACTIVE, DFU, STATE> { | ||
| 44 | /// Flash type used for the active partition - the partition which will be booted from. | ||
| 45 | pub active: ACTIVE, | ||
| 46 | /// Flash type used for the dfu partition - the partition which will be swapped in when requested. | ||
| 47 | pub dfu: DFU, | ||
| 37 | /// Flash type used for the state partition. | 48 | /// Flash type used for the state partition. |
| 38 | type STATE: NorFlash; | 49 | pub state: STATE, |
| 39 | /// Flash type used for the active partition. | ||
| 40 | type ACTIVE: NorFlash; | ||
| 41 | /// Flash type used for the dfu partition. | ||
| 42 | type DFU: NorFlash; | ||
| 43 | |||
| 44 | /// Return flash instance used to write/read to/from active partition. | ||
| 45 | fn active(&mut self) -> &mut Self::ACTIVE; | ||
| 46 | /// Return flash instance used to write/read to/from dfu partition. | ||
| 47 | fn dfu(&mut self) -> &mut Self::DFU; | ||
| 48 | /// Return flash instance used to write/read to/from bootloader state. | ||
| 49 | fn state(&mut self) -> &mut Self::STATE; | ||
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | trait FlashConfigEx { | 52 | impl<'a, FLASH: NorFlash> |
| 53 | fn page_size() -> u32; | 53 | BootLoaderConfig< |
| 54 | } | 54 | BlockingPartition<'a, NoopRawMutex, FLASH>, |
| 55 | BlockingPartition<'a, NoopRawMutex, FLASH>, | ||
| 56 | BlockingPartition<'a, NoopRawMutex, FLASH>, | ||
| 57 | > | ||
| 58 | { | ||
| 59 | /// Create a bootloader config from the flash and address symbols defined in the linkerfile | ||
| 60 | // #[cfg(target_os = "none")] | ||
| 61 | pub fn from_linkerfile_blocking(flash: &'a Mutex<NoopRawMutex, RefCell<FLASH>>) -> Self { | ||
| 62 | extern "C" { | ||
| 63 | static __bootloader_state_start: u32; | ||
| 64 | static __bootloader_state_end: u32; | ||
| 65 | static __bootloader_active_start: u32; | ||
| 66 | static __bootloader_active_end: u32; | ||
| 67 | static __bootloader_dfu_start: u32; | ||
| 68 | static __bootloader_dfu_end: u32; | ||
| 69 | } | ||
| 55 | 70 | ||
| 56 | impl<T: FlashConfig> FlashConfigEx for T { | 71 | let active = unsafe { |
| 57 | /// Get the page size which is the "unit of operation" within the bootloader. | 72 | let start = &__bootloader_active_start as *const u32 as u32; |
| 58 | fn page_size() -> u32 { | 73 | let end = &__bootloader_active_end as *const u32 as u32; |
| 59 | core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) as u32 | 74 | trace!("ACTIVE: 0x{:x} - 0x{:x}", start, end); |
| 75 | |||
| 76 | BlockingPartition::new(flash, start, end - start) | ||
| 77 | }; | ||
| 78 | let dfu = unsafe { | ||
| 79 | let start = &__bootloader_dfu_start as *const u32 as u32; | ||
| 80 | let end = &__bootloader_dfu_end as *const u32 as u32; | ||
| 81 | trace!("DFU: 0x{:x} - 0x{:x}", start, end); | ||
| 82 | |||
| 83 | BlockingPartition::new(flash, start, end - start) | ||
| 84 | }; | ||
| 85 | let state = unsafe { | ||
| 86 | let start = &__bootloader_state_start as *const u32 as u32; | ||
| 87 | let end = &__bootloader_state_end as *const u32 as u32; | ||
| 88 | trace!("STATE: 0x{:x} - 0x{:x}", start, end); | ||
| 89 | |||
| 90 | BlockingPartition::new(flash, start, end - start) | ||
| 91 | }; | ||
| 92 | |||
| 93 | Self { active, dfu, state } | ||
| 60 | } | 94 | } |
| 61 | } | 95 | } |
| 62 | 96 | ||
| 63 | /// BootLoader works with any flash implementing embedded_storage. | 97 | /// BootLoader works with any flash implementing embedded_storage. |
| 64 | pub struct BootLoader { | 98 | pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> { |
| 65 | // Page with current state of bootloader. The state partition has the following format: | 99 | active: ACTIVE, |
| 66 | // All ranges are in multiples of WRITE_SIZE bytes. | 100 | dfu: DFU, |
| 67 | // | Range | Description | | 101 | /// The state partition has the following format: |
| 68 | // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | | 102 | /// All ranges are in multiples of WRITE_SIZE bytes. |
| 69 | // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | | 103 | /// | Range | Description | |
| 70 | // | 2..2 + N | Progress index used while swapping or reverting | | 104 | /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | |
| 71 | state: Partition, | 105 | /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | |
| 72 | // Location of the partition which will be booted from | 106 | /// | 2..2 + N | Progress index used while swapping or reverting |
| 73 | active: Partition, | 107 | state: STATE, |
| 74 | // Location of the partition which will be swapped in when requested | ||
| 75 | dfu: Partition, | ||
| 76 | } | 108 | } |
| 77 | 109 | ||
| 78 | impl BootLoader { | 110 | impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, STATE> { |
| 79 | /// Create a new instance of a bootloader with the given partitions. | 111 | /// Get the page size which is the "unit of operation" within the bootloader. |
| 112 | const PAGE_SIZE: u32 = if ACTIVE::ERASE_SIZE > DFU::ERASE_SIZE { | ||
| 113 | ACTIVE::ERASE_SIZE as u32 | ||
| 114 | } else { | ||
| 115 | DFU::ERASE_SIZE as u32 | ||
| 116 | }; | ||
| 117 | |||
| 118 | /// Create a new instance of a bootloader with the flash partitions. | ||
| 80 | /// | 119 | /// |
| 81 | /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. | 120 | /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. |
| 82 | /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. | 121 | /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. |
| 83 | pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { | 122 | pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self { |
| 84 | Self { active, dfu, state } | 123 | Self { |
| 85 | } | 124 | active: config.active, |
| 86 | 125 | dfu: config.dfu, | |
| 87 | /// Return the offset of the active partition into the active flash. | 126 | state: config.state, |
| 88 | pub fn boot_address(&self) -> usize { | 127 | } |
| 89 | self.active.from as usize | ||
| 90 | } | 128 | } |
| 91 | 129 | ||
| 92 | /// Perform necessary boot preparations like swapping images. | 130 | /// Perform necessary boot preparations like swapping images. |
| @@ -175,195 +213,174 @@ impl BootLoader { | |||
| 175 | /// | DFU | 3 | 3 | 2 | 1 | 3 | | 213 | /// | DFU | 3 | 3 | 2 | 1 | 3 | |
| 176 | /// +-----------+--------------+--------+--------+--------+--------+ | 214 | /// +-----------+--------------+--------+--------+--------+--------+ |
| 177 | /// | 215 | /// |
| 178 | pub fn prepare_boot<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<State, BootError> { | 216 | pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> { |
| 179 | // Ensure we have enough progress pages to store copy progress | 217 | // Ensure we have enough progress pages to store copy progress |
| 180 | assert_eq!(0, P::page_size() % aligned_buf.len() as u32); | 218 | assert_eq!(0, Self::PAGE_SIZE % aligned_buf.len() as u32); |
| 181 | assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE as u32); | 219 | assert_eq!(0, Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32); |
| 182 | assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE as u32); | 220 | assert_eq!(0, Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32); |
| 183 | assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE as u32); | 221 | assert_eq!(0, Self::PAGE_SIZE % DFU::WRITE_SIZE as u32); |
| 184 | assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE as u32); | 222 | assert_eq!(0, Self::PAGE_SIZE % DFU::ERASE_SIZE as u32); |
| 185 | assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); | 223 | assert!(aligned_buf.len() >= STATE::WRITE_SIZE); |
| 186 | assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE); | 224 | assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE); |
| 187 | assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE); | 225 | assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE); |
| 188 | assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); | 226 | |
| 227 | assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE); | ||
| 189 | 228 | ||
| 190 | // Copy contents from partition N to active | 229 | // Copy contents from partition N to active |
| 191 | let state = self.read_state(p, aligned_buf)?; | 230 | let state = self.read_state(aligned_buf)?; |
| 192 | if state == State::Swap { | 231 | if state == State::Swap { |
| 193 | // | 232 | // |
| 194 | // Check if we already swapped. If we're in the swap state, this means we should revert | 233 | // Check if we already swapped. If we're in the swap state, this means we should revert |
| 195 | // since the app has failed to mark boot as successful | 234 | // since the app has failed to mark boot as successful |
| 196 | // | 235 | // |
| 197 | if !self.is_swapped(p, aligned_buf)? { | 236 | if !self.is_swapped(aligned_buf)? { |
| 198 | trace!("Swapping"); | 237 | trace!("Swapping"); |
| 199 | self.swap(p, aligned_buf)?; | 238 | self.swap(aligned_buf)?; |
| 200 | trace!("Swapping done"); | 239 | trace!("Swapping done"); |
| 201 | } else { | 240 | } else { |
| 202 | trace!("Reverting"); | 241 | trace!("Reverting"); |
| 203 | self.revert(p, aligned_buf)?; | 242 | self.revert(aligned_buf)?; |
| 204 | 243 | ||
| 205 | let state_flash = p.state(); | 244 | let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; |
| 206 | let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; | ||
| 207 | 245 | ||
| 208 | // Invalidate progress | 246 | // Invalidate progress |
| 209 | state_word.fill(!P::STATE_ERASE_VALUE); | 247 | state_word.fill(!STATE_ERASE_VALUE); |
| 210 | self.state | 248 | self.state.write(STATE::WRITE_SIZE as u32, state_word)?; |
| 211 | .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; | ||
| 212 | 249 | ||
| 213 | // Clear magic and progress | 250 | // Clear magic and progress |
| 214 | self.state.wipe_blocking(state_flash)?; | 251 | self.state.erase(0, self.state.capacity() as u32)?; |
| 215 | 252 | ||
| 216 | // Set magic | 253 | // Set magic |
| 217 | state_word.fill(BOOT_MAGIC); | 254 | state_word.fill(BOOT_MAGIC); |
| 218 | self.state.write_blocking(state_flash, 0, state_word)?; | 255 | self.state.write(0, state_word)?; |
| 219 | } | 256 | } |
| 220 | } | 257 | } |
| 221 | Ok(state) | 258 | Ok(state) |
| 222 | } | 259 | } |
| 223 | 260 | ||
| 224 | fn is_swapped<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<bool, BootError> { | 261 | fn is_swapped(&mut self, aligned_buf: &mut [u8]) -> Result<bool, BootError> { |
| 225 | let page_count = (self.active.size() / P::page_size()) as usize; | 262 | let page_count = self.active.capacity() / Self::PAGE_SIZE as usize; |
| 226 | let progress = self.current_progress(p, aligned_buf)?; | 263 | let progress = self.current_progress(aligned_buf)?; |
| 227 | 264 | ||
| 228 | Ok(progress >= page_count * 2) | 265 | Ok(progress >= page_count * 2) |
| 229 | } | 266 | } |
| 230 | 267 | ||
| 231 | fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result<usize, BootError> { | 268 | fn current_progress(&mut self, aligned_buf: &mut [u8]) -> Result<usize, BootError> { |
| 232 | let write_size = P::STATE::WRITE_SIZE as u32; | 269 | let write_size = STATE::WRITE_SIZE as u32; |
| 233 | let max_index = (((self.state.size() - write_size) / write_size) - 2) as usize; | 270 | let max_index = ((self.state.capacity() - STATE::WRITE_SIZE) / STATE::WRITE_SIZE) - 2; |
| 234 | let state_flash = config.state(); | ||
| 235 | let state_word = &mut aligned_buf[..write_size as usize]; | 271 | let state_word = &mut aligned_buf[..write_size as usize]; |
| 236 | 272 | ||
| 237 | self.state.read_blocking(state_flash, write_size, state_word)?; | 273 | self.state.read(write_size, state_word)?; |
| 238 | if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) { | 274 | if state_word.iter().any(|&b| b != STATE_ERASE_VALUE) { |
| 239 | // Progress is invalid | 275 | // Progress is invalid |
| 240 | return Ok(max_index); | 276 | return Ok(max_index); |
| 241 | } | 277 | } |
| 242 | 278 | ||
| 243 | for index in 0..max_index { | 279 | for index in 0..max_index { |
| 244 | self.state | 280 | self.state.read((2 + index) as u32 * write_size, state_word)?; |
| 245 | .read_blocking(state_flash, (2 + index) as u32 * write_size, state_word)?; | ||
| 246 | 281 | ||
| 247 | if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) { | 282 | if state_word.iter().any(|&b| b == STATE_ERASE_VALUE) { |
| 248 | return Ok(index); | 283 | return Ok(index); |
| 249 | } | 284 | } |
| 250 | } | 285 | } |
| 251 | Ok(max_index) | 286 | Ok(max_index) |
| 252 | } | 287 | } |
| 253 | 288 | ||
| 254 | fn update_progress<P: FlashConfig>( | 289 | fn update_progress(&mut self, progress_index: usize, aligned_buf: &mut [u8]) -> Result<(), BootError> { |
| 255 | &mut self, | 290 | let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; |
| 256 | progress_index: usize, | 291 | state_word.fill(!STATE_ERASE_VALUE); |
| 257 | p: &mut P, | 292 | self.state |
| 258 | aligned_buf: &mut [u8], | 293 | .write((2 + progress_index) as u32 * STATE::WRITE_SIZE as u32, state_word)?; |
| 259 | ) -> Result<(), BootError> { | ||
| 260 | let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; | ||
| 261 | state_word.fill(!P::STATE_ERASE_VALUE); | ||
| 262 | self.state.write_blocking( | ||
| 263 | p.state(), | ||
| 264 | (2 + progress_index) as u32 * P::STATE::WRITE_SIZE as u32, | ||
| 265 | state_word, | ||
| 266 | )?; | ||
| 267 | Ok(()) | 294 | Ok(()) |
| 268 | } | 295 | } |
| 269 | 296 | ||
| 270 | fn copy_page_once_to_active<P: FlashConfig>( | 297 | fn copy_page_once_to_active( |
| 271 | &mut self, | 298 | &mut self, |
| 272 | progress_index: usize, | 299 | progress_index: usize, |
| 273 | from_offset: u32, | 300 | from_offset: u32, |
| 274 | to_offset: u32, | 301 | to_offset: u32, |
| 275 | p: &mut P, | ||
| 276 | aligned_buf: &mut [u8], | 302 | aligned_buf: &mut [u8], |
| 277 | ) -> Result<(), BootError> { | 303 | ) -> Result<(), BootError> { |
| 278 | if self.current_progress(p, aligned_buf)? <= progress_index { | 304 | if self.current_progress(aligned_buf)? <= progress_index { |
| 279 | let page_size = P::page_size() as u32; | 305 | let page_size = Self::PAGE_SIZE as u32; |
| 280 | 306 | ||
| 281 | self.active | 307 | self.active.erase(to_offset, to_offset + page_size)?; |
| 282 | .erase_blocking(p.active(), to_offset, to_offset + page_size)?; | ||
| 283 | 308 | ||
| 284 | for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { | 309 | for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { |
| 285 | self.dfu | 310 | self.dfu.read(from_offset + offset_in_page as u32, aligned_buf)?; |
| 286 | .read_blocking(p.dfu(), from_offset + offset_in_page as u32, aligned_buf)?; | 311 | self.active.write(to_offset + offset_in_page as u32, aligned_buf)?; |
| 287 | self.active | ||
| 288 | .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?; | ||
| 289 | } | 312 | } |
| 290 | 313 | ||
| 291 | self.update_progress(progress_index, p, aligned_buf)?; | 314 | self.update_progress(progress_index, aligned_buf)?; |
| 292 | } | 315 | } |
| 293 | Ok(()) | 316 | Ok(()) |
| 294 | } | 317 | } |
| 295 | 318 | ||
| 296 | fn copy_page_once_to_dfu<P: FlashConfig>( | 319 | fn copy_page_once_to_dfu( |
| 297 | &mut self, | 320 | &mut self, |
| 298 | progress_index: usize, | 321 | progress_index: usize, |
| 299 | from_offset: u32, | 322 | from_offset: u32, |
| 300 | to_offset: u32, | 323 | to_offset: u32, |
| 301 | p: &mut P, | ||
| 302 | aligned_buf: &mut [u8], | 324 | aligned_buf: &mut [u8], |
| 303 | ) -> Result<(), BootError> { | 325 | ) -> Result<(), BootError> { |
| 304 | if self.current_progress(p, aligned_buf)? <= progress_index { | 326 | if self.current_progress(aligned_buf)? <= progress_index { |
| 305 | let page_size = P::page_size() as u32; | 327 | let page_size = Self::PAGE_SIZE as u32; |
| 306 | 328 | ||
| 307 | self.dfu | 329 | self.dfu.erase(to_offset as u32, to_offset + page_size)?; |
| 308 | .erase_blocking(p.dfu(), to_offset as u32, to_offset + page_size)?; | ||
| 309 | 330 | ||
| 310 | for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { | 331 | for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { |
| 311 | self.active | 332 | self.active.read(from_offset + offset_in_page as u32, aligned_buf)?; |
| 312 | .read_blocking(p.active(), from_offset + offset_in_page as u32, aligned_buf)?; | 333 | self.dfu.write(to_offset + offset_in_page as u32, aligned_buf)?; |
| 313 | self.dfu | ||
| 314 | .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?; | ||
| 315 | } | 334 | } |
| 316 | 335 | ||
| 317 | self.update_progress(progress_index, p, aligned_buf)?; | 336 | self.update_progress(progress_index, aligned_buf)?; |
| 318 | } | 337 | } |
| 319 | Ok(()) | 338 | Ok(()) |
| 320 | } | 339 | } |
| 321 | 340 | ||
| 322 | fn swap<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { | 341 | fn swap(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> { |
| 323 | let page_size = P::page_size(); | 342 | let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE; |
| 324 | let page_count = self.active.size() / page_size; | ||
| 325 | for page_num in 0..page_count { | 343 | for page_num in 0..page_count { |
| 326 | let progress_index = (page_num * 2) as usize; | 344 | let progress_index = (page_num * 2) as usize; |
| 327 | 345 | ||
| 328 | // Copy active page to the 'next' DFU page. | 346 | // Copy active page to the 'next' DFU page. |
| 329 | let active_from_offset = (page_count - 1 - page_num) * page_size; | 347 | let active_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; |
| 330 | let dfu_to_offset = (page_count - page_num) * page_size; | 348 | let dfu_to_offset = (page_count - page_num) * Self::PAGE_SIZE; |
| 331 | //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); | 349 | //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); |
| 332 | self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; | 350 | self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?; |
| 333 | 351 | ||
| 334 | // Copy DFU page to the active page | 352 | // Copy DFU page to the active page |
| 335 | let active_to_offset = (page_count - 1 - page_num) * page_size; | 353 | let active_to_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; |
| 336 | let dfu_from_offset = (page_count - 1 - page_num) * page_size; | 354 | let dfu_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; |
| 337 | //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); | 355 | //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); |
| 338 | self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; | 356 | self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?; |
| 339 | } | 357 | } |
| 340 | 358 | ||
| 341 | Ok(()) | 359 | Ok(()) |
| 342 | } | 360 | } |
| 343 | 361 | ||
| 344 | fn revert<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { | 362 | fn revert(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> { |
| 345 | let page_size = P::page_size(); | 363 | let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE; |
| 346 | let page_count = self.active.size() / page_size; | ||
| 347 | for page_num in 0..page_count { | 364 | for page_num in 0..page_count { |
| 348 | let progress_index = (page_count * 2 + page_num * 2) as usize; | 365 | let progress_index = (page_count * 2 + page_num * 2) as usize; |
| 349 | 366 | ||
| 350 | // Copy the bad active page to the DFU page | 367 | // Copy the bad active page to the DFU page |
| 351 | let active_from_offset = page_num * page_size; | 368 | let active_from_offset = page_num * Self::PAGE_SIZE; |
| 352 | let dfu_to_offset = page_num * page_size; | 369 | let dfu_to_offset = page_num * Self::PAGE_SIZE; |
| 353 | self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; | 370 | self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?; |
| 354 | 371 | ||
| 355 | // Copy the DFU page back to the active page | 372 | // Copy the DFU page back to the active page |
| 356 | let active_to_offset = page_num * page_size; | 373 | let active_to_offset = page_num * Self::PAGE_SIZE; |
| 357 | let dfu_from_offset = (page_num + 1) * page_size; | 374 | let dfu_from_offset = (page_num + 1) * Self::PAGE_SIZE; |
| 358 | self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; | 375 | self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?; |
| 359 | } | 376 | } |
| 360 | 377 | ||
| 361 | Ok(()) | 378 | Ok(()) |
| 362 | } | 379 | } |
| 363 | 380 | ||
| 364 | fn read_state<P: FlashConfig>(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result<State, BootError> { | 381 | fn read_state(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> { |
| 365 | let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; | 382 | let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; |
| 366 | self.state.read_blocking(config.state(), 0, state_word)?; | 383 | self.state.read(0, state_word)?; |
| 367 | 384 | ||
| 368 | if !state_word.iter().any(|&b| b != SWAP_MAGIC) { | 385 | if !state_word.iter().any(|&b| b != SWAP_MAGIC) { |
| 369 | Ok(State::Swap) | 386 | Ok(State::Swap) |
| @@ -373,161 +390,32 @@ impl BootLoader { | |||
| 373 | } | 390 | } |
| 374 | } | 391 | } |
| 375 | 392 | ||
| 376 | fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: u32, state_write_size: usize) { | 393 | fn assert_partitions<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>( |
| 377 | assert_eq!(active.size() % page_size, 0); | 394 | active: &ACTIVE, |
| 378 | assert_eq!(dfu.size() % page_size, 0); | 395 | dfu: &DFU, |
| 379 | assert!(dfu.size() - active.size() >= page_size); | 396 | state: &STATE, |
| 380 | assert!(2 + 2 * (active.size() / page_size) <= state.size() / state_write_size as u32); | 397 | page_size: u32, |
| 381 | } | 398 | ) { |
| 382 | 399 | assert_eq!(active.capacity() as u32 % page_size, 0); | |
| 383 | /// A flash wrapper implementing the Flash and embedded_storage traits. | 400 | assert_eq!(dfu.capacity() as u32 % page_size, 0); |
| 384 | pub struct BootFlash<F> | 401 | assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); |
| 385 | where | 402 | assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); |
| 386 | F: NorFlash, | ||
| 387 | { | ||
| 388 | flash: F, | ||
| 389 | } | ||
| 390 | |||
| 391 | impl<F> BootFlash<F> | ||
| 392 | where | ||
| 393 | F: NorFlash, | ||
| 394 | { | ||
| 395 | /// Create a new instance of a bootable flash | ||
| 396 | pub fn new(flash: F) -> Self { | ||
| 397 | Self { flash } | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | impl<F> ErrorType for BootFlash<F> | ||
| 402 | where | ||
| 403 | F: NorFlash, | ||
| 404 | { | ||
| 405 | type Error = F::Error; | ||
| 406 | } | ||
| 407 | |||
| 408 | impl<F> NorFlash for BootFlash<F> | ||
| 409 | where | ||
| 410 | F: NorFlash, | ||
| 411 | { | ||
| 412 | const WRITE_SIZE: usize = F::WRITE_SIZE; | ||
| 413 | const ERASE_SIZE: usize = F::ERASE_SIZE; | ||
| 414 | |||
| 415 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | ||
| 416 | F::erase(&mut self.flash, from, to) | ||
| 417 | } | ||
| 418 | |||
| 419 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { | ||
| 420 | F::write(&mut self.flash, offset, bytes) | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | impl<F> ReadNorFlash for BootFlash<F> | ||
| 425 | where | ||
| 426 | F: NorFlash, | ||
| 427 | { | ||
| 428 | const READ_SIZE: usize = F::READ_SIZE; | ||
| 429 | |||
| 430 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | ||
| 431 | F::read(&mut self.flash, offset, bytes) | ||
| 432 | } | ||
| 433 | |||
| 434 | fn capacity(&self) -> usize { | ||
| 435 | F::capacity(&self.flash) | ||
| 436 | } | ||
| 437 | } | ||
| 438 | |||
| 439 | /// Convenience provider that uses a single flash for all partitions. | ||
| 440 | pub struct SingleFlashConfig<'a, F> | ||
| 441 | where | ||
| 442 | F: NorFlash, | ||
| 443 | { | ||
| 444 | flash: &'a mut F, | ||
| 445 | } | ||
| 446 | |||
| 447 | impl<'a, F> SingleFlashConfig<'a, F> | ||
| 448 | where | ||
| 449 | F: NorFlash, | ||
| 450 | { | ||
| 451 | /// Create a provider for a single flash. | ||
| 452 | pub fn new(flash: &'a mut F) -> Self { | ||
| 453 | Self { flash } | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> | ||
| 458 | where | ||
| 459 | F: NorFlash, | ||
| 460 | { | ||
| 461 | type STATE = F; | ||
| 462 | type ACTIVE = F; | ||
| 463 | type DFU = F; | ||
| 464 | |||
| 465 | fn active(&mut self) -> &mut Self::STATE { | ||
| 466 | self.flash | ||
| 467 | } | ||
| 468 | fn dfu(&mut self) -> &mut Self::ACTIVE { | ||
| 469 | self.flash | ||
| 470 | } | ||
| 471 | fn state(&mut self) -> &mut Self::DFU { | ||
| 472 | self.flash | ||
| 473 | } | ||
| 474 | } | ||
| 475 | |||
| 476 | /// Convenience flash provider that uses separate flash instances for each partition. | ||
| 477 | pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> | ||
| 478 | where | ||
| 479 | ACTIVE: NorFlash, | ||
| 480 | STATE: NorFlash, | ||
| 481 | DFU: NorFlash, | ||
| 482 | { | ||
| 483 | active: &'a mut ACTIVE, | ||
| 484 | state: &'a mut STATE, | ||
| 485 | dfu: &'a mut DFU, | ||
| 486 | } | ||
| 487 | |||
| 488 | impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> | ||
| 489 | where | ||
| 490 | ACTIVE: NorFlash, | ||
| 491 | STATE: NorFlash, | ||
| 492 | DFU: NorFlash, | ||
| 493 | { | ||
| 494 | /// Create a new flash provider with separate configuration for all three partitions. | ||
| 495 | pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { | ||
| 496 | Self { active, state, dfu } | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> | ||
| 501 | where | ||
| 502 | ACTIVE: NorFlash, | ||
| 503 | STATE: NorFlash, | ||
| 504 | DFU: NorFlash, | ||
| 505 | { | ||
| 506 | type STATE = STATE; | ||
| 507 | type ACTIVE = ACTIVE; | ||
| 508 | type DFU = DFU; | ||
| 509 | |||
| 510 | fn active(&mut self) -> &mut Self::ACTIVE { | ||
| 511 | self.active | ||
| 512 | } | ||
| 513 | fn dfu(&mut self) -> &mut Self::DFU { | ||
| 514 | self.dfu | ||
| 515 | } | ||
| 516 | fn state(&mut self) -> &mut Self::STATE { | ||
| 517 | self.state | ||
| 518 | } | ||
| 519 | } | 403 | } |
| 520 | 404 | ||
| 521 | #[cfg(test)] | 405 | #[cfg(test)] |
| 522 | mod tests { | 406 | mod tests { |
| 523 | use super::*; | 407 | use super::*; |
| 408 | use crate::mem_flash::MemFlash; | ||
| 524 | 409 | ||
| 525 | #[test] | 410 | #[test] |
| 526 | #[should_panic] | 411 | #[should_panic] |
| 527 | fn test_range_asserts() { | 412 | fn test_range_asserts() { |
| 528 | const ACTIVE: Partition = Partition::new(4096, 4194304); | 413 | const ACTIVE_SIZE: usize = 4194304 - 4096; |
| 529 | const DFU: Partition = Partition::new(4194304, 2 * 4194304); | 414 | const DFU_SIZE: usize = 4194304; |
| 530 | const STATE: Partition = Partition::new(0, 4096); | 415 | const STATE_SIZE: usize = 4096; |
| 531 | assert_partitions(ACTIVE, DFU, STATE, 4096, 4); | 416 | static ACTIVE: MemFlash<ACTIVE_SIZE, 4, 4> = MemFlash::new(0xFF); |
| 417 | static DFU: MemFlash<DFU_SIZE, 4, 4> = MemFlash::new(0xFF); | ||
| 418 | static STATE: MemFlash<STATE_SIZE, 4, 4> = MemFlash::new(0xFF); | ||
| 419 | assert_partitions(&ACTIVE, &DFU, &STATE, 4096); | ||
| 532 | } | 420 | } |
| 533 | } | 421 | } |
diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index bdd03bff4..0b3f88313 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs | |||
| @@ -1,20 +1,68 @@ | |||
| 1 | use digest::Digest; | 1 | use digest::Digest; |
| 2 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; | 2 | #[cfg(target_os = "none")] |
| 3 | use embassy_embedded_hal::flash::partition::Partition; | ||
| 4 | #[cfg(target_os = "none")] | ||
| 5 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 6 | use embedded_storage_async::nor_flash::NorFlash; | ||
| 7 | |||
| 8 | use super::FirmwareUpdaterConfig; | ||
| 9 | use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; | ||
| 10 | |||
| 11 | /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to | ||
| 12 | /// 'mess up' the internal bootloader state | ||
| 13 | pub struct FirmwareUpdater<DFU: NorFlash, STATE: NorFlash> { | ||
| 14 | dfu: DFU, | ||
| 15 | state: STATE, | ||
| 16 | } | ||
| 17 | |||
| 18 | #[cfg(target_os = "none")] | ||
| 19 | impl<'a, FLASH: NorFlash> | ||
| 20 | FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, FLASH>, Partition<'a, NoopRawMutex, FLASH>> | ||
| 21 | { | ||
| 22 | /// Create a firmware updater config from the flash and address symbols defined in the linkerfile | ||
| 23 | pub fn from_linkerfile(flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, FLASH>) -> Self { | ||
| 24 | extern "C" { | ||
| 25 | static __bootloader_state_start: u32; | ||
| 26 | static __bootloader_state_end: u32; | ||
| 27 | static __bootloader_dfu_start: u32; | ||
| 28 | static __bootloader_dfu_end: u32; | ||
| 29 | } | ||
| 30 | |||
| 31 | let dfu = unsafe { | ||
| 32 | let start = &__bootloader_dfu_start as *const u32 as u32; | ||
| 33 | let end = &__bootloader_dfu_end as *const u32 as u32; | ||
| 34 | trace!("DFU: 0x{:x} - 0x{:x}", start, end); | ||
| 35 | |||
| 36 | Partition::new(flash, start, end - start) | ||
| 37 | }; | ||
| 38 | let state = unsafe { | ||
| 39 | let start = &__bootloader_state_start as *const u32 as u32; | ||
| 40 | let end = &__bootloader_state_end as *const u32 as u32; | ||
| 41 | trace!("STATE: 0x{:x} - 0x{:x}", start, end); | ||
| 42 | |||
| 43 | Partition::new(flash, start, end - start) | ||
| 44 | }; | ||
| 3 | 45 | ||
| 4 | use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; | 46 | Self { dfu, state } |
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> { | ||
| 51 | /// Create a firmware updater instance with partition ranges for the update and state partitions. | ||
| 52 | pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self { | ||
| 53 | Self { | ||
| 54 | dfu: config.dfu, | ||
| 55 | state: config.state, | ||
| 56 | } | ||
| 57 | } | ||
| 5 | 58 | ||
| 6 | impl FirmwareUpdater { | ||
| 7 | /// Obtain the current state. | 59 | /// Obtain the current state. |
| 8 | /// | 60 | /// |
| 9 | /// This is useful to check if the bootloader has just done a swap, in order | 61 | /// This is useful to check if the bootloader has just done a swap, in order |
| 10 | /// to do verifications and self-tests of the new image before calling | 62 | /// to do verifications and self-tests of the new image before calling |
| 11 | /// `mark_booted`. | 63 | /// `mark_booted`. |
| 12 | pub async fn get_state<F: AsyncNorFlash>( | 64 | pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> { |
| 13 | &mut self, | 65 | self.state.read(0, aligned).await?; |
| 14 | state_flash: &mut F, | ||
| 15 | aligned: &mut [u8], | ||
| 16 | ) -> Result<State, FirmwareUpdaterError> { | ||
| 17 | self.state.read(state_flash, 0, aligned).await?; | ||
| 18 | 66 | ||
| 19 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { | 67 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { |
| 20 | Ok(State::Swap) | 68 | Ok(State::Swap) |
| @@ -37,19 +85,18 @@ impl FirmwareUpdater { | |||
| 37 | /// | 85 | /// |
| 38 | /// # Safety | 86 | /// # Safety |
| 39 | /// | 87 | /// |
| 40 | /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from | 88 | /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from |
| 41 | /// and written to. | 89 | /// and written to. |
| 42 | #[cfg(all(feature = "_verify", feature = "nightly"))] | 90 | #[cfg(feature = "_verify")] |
| 43 | pub async fn verify_and_mark_updated<F: AsyncNorFlash>( | 91 | pub async fn verify_and_mark_updated( |
| 44 | &mut self, | 92 | &mut self, |
| 45 | _state_and_dfu_flash: &mut F, | ||
| 46 | _public_key: &[u8], | 93 | _public_key: &[u8], |
| 47 | _signature: &[u8], | 94 | _signature: &[u8], |
| 48 | _update_len: u32, | 95 | _update_len: u32, |
| 49 | _aligned: &mut [u8], | 96 | _aligned: &mut [u8], |
| 50 | ) -> Result<(), FirmwareUpdaterError> { | 97 | ) -> Result<(), FirmwareUpdaterError> { |
| 51 | assert_eq!(_aligned.len(), F::WRITE_SIZE); | 98 | assert_eq!(_aligned.len(), STATE::WRITE_SIZE); |
| 52 | assert!(_update_len <= self.dfu.size()); | 99 | assert!(_update_len <= self.dfu.capacity() as u32); |
| 53 | 100 | ||
| 54 | #[cfg(feature = "ed25519-dalek")] | 101 | #[cfg(feature = "ed25519-dalek")] |
| 55 | { | 102 | { |
| @@ -63,8 +110,7 @@ impl FirmwareUpdater { | |||
| 63 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | 110 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; |
| 64 | 111 | ||
| 65 | let mut message = [0; 64]; | 112 | let mut message = [0; 64]; |
| 66 | self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) | 113 | self.hash::<Sha512>(_update_len, _aligned, &mut message).await?; |
| 67 | .await?; | ||
| 68 | 114 | ||
| 69 | public_key.verify(&message, &signature).map_err(into_signature_error)? | 115 | public_key.verify(&message, &signature).map_err(into_signature_error)? |
| 70 | } | 116 | } |
| @@ -85,8 +131,7 @@ impl FirmwareUpdater { | |||
| 85 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | 131 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; |
| 86 | 132 | ||
| 87 | let mut message = [0; 64]; | 133 | let mut message = [0; 64]; |
| 88 | self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) | 134 | self.hash::<Sha512>(_update_len, _aligned, &mut message).await?; |
| 89 | .await?; | ||
| 90 | 135 | ||
| 91 | let r = public_key.verify(&message, &signature); | 136 | let r = public_key.verify(&message, &signature); |
| 92 | trace!( | 137 | trace!( |
| @@ -99,20 +144,19 @@ impl FirmwareUpdater { | |||
| 99 | r.map_err(into_signature_error)? | 144 | r.map_err(into_signature_error)? |
| 100 | } | 145 | } |
| 101 | 146 | ||
| 102 | self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await | 147 | self.set_magic(_aligned, SWAP_MAGIC).await |
| 103 | } | 148 | } |
| 104 | 149 | ||
| 105 | /// Verify the update in DFU with any digest. | 150 | /// Verify the update in DFU with any digest. |
| 106 | pub async fn hash<F: AsyncNorFlash, D: Digest>( | 151 | pub async fn hash<D: Digest>( |
| 107 | &mut self, | 152 | &mut self, |
| 108 | dfu_flash: &mut F, | ||
| 109 | update_len: u32, | 153 | update_len: u32, |
| 110 | chunk_buf: &mut [u8], | 154 | chunk_buf: &mut [u8], |
| 111 | output: &mut [u8], | 155 | output: &mut [u8], |
| 112 | ) -> Result<(), FirmwareUpdaterError> { | 156 | ) -> Result<(), FirmwareUpdaterError> { |
| 113 | let mut digest = D::new(); | 157 | let mut digest = D::new(); |
| 114 | for offset in (0..update_len).step_by(chunk_buf.len()) { | 158 | for offset in (0..update_len).step_by(chunk_buf.len()) { |
| 115 | self.dfu.read(dfu_flash, offset, chunk_buf).await?; | 159 | self.dfu.read(offset, chunk_buf).await?; |
| 116 | let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); | 160 | let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); |
| 117 | digest.update(&chunk_buf[..len]); | 161 | digest.update(&chunk_buf[..len]); |
| 118 | } | 162 | } |
| @@ -124,60 +168,44 @@ impl FirmwareUpdater { | |||
| 124 | /// | 168 | /// |
| 125 | /// # Safety | 169 | /// # Safety |
| 126 | /// | 170 | /// |
| 127 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. | 171 | /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. |
| 128 | #[cfg(all(feature = "nightly", not(feature = "_verify")))] | 172 | #[cfg(not(feature = "_verify"))] |
| 129 | pub async fn mark_updated<F: AsyncNorFlash>( | 173 | pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { |
| 130 | &mut self, | 174 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); |
| 131 | state_flash: &mut F, | 175 | self.set_magic(aligned, SWAP_MAGIC).await |
| 132 | aligned: &mut [u8], | ||
| 133 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 134 | assert_eq!(aligned.len(), F::WRITE_SIZE); | ||
| 135 | self.set_magic(aligned, SWAP_MAGIC, state_flash).await | ||
| 136 | } | 176 | } |
| 137 | 177 | ||
| 138 | /// Mark firmware boot successful and stop rollback on reset. | 178 | /// Mark firmware boot successful and stop rollback on reset. |
| 139 | /// | 179 | /// |
| 140 | /// # Safety | 180 | /// # Safety |
| 141 | /// | 181 | /// |
| 142 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. | 182 | /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. |
| 143 | pub async fn mark_booted<F: AsyncNorFlash>( | 183 | pub async fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { |
| 144 | &mut self, | 184 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); |
| 145 | state_flash: &mut F, | 185 | self.set_magic(aligned, BOOT_MAGIC).await |
| 146 | aligned: &mut [u8], | ||
| 147 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 148 | assert_eq!(aligned.len(), F::WRITE_SIZE); | ||
| 149 | self.set_magic(aligned, BOOT_MAGIC, state_flash).await | ||
| 150 | } | 186 | } |
| 151 | 187 | ||
| 152 | async fn set_magic<F: AsyncNorFlash>( | 188 | async fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> { |
| 153 | &mut self, | 189 | self.state.read(0, aligned).await?; |
| 154 | aligned: &mut [u8], | ||
| 155 | magic: u8, | ||
| 156 | state_flash: &mut F, | ||
| 157 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 158 | self.state.read(state_flash, 0, aligned).await?; | ||
| 159 | 190 | ||
| 160 | if aligned.iter().any(|&b| b != magic) { | 191 | if aligned.iter().any(|&b| b != magic) { |
| 161 | // Read progress validity | 192 | // Read progress validity |
| 162 | self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; | 193 | self.state.read(STATE::WRITE_SIZE as u32, aligned).await?; |
| 163 | |||
| 164 | // FIXME: Do not make this assumption. | ||
| 165 | const STATE_ERASE_VALUE: u8 = 0xFF; | ||
| 166 | 194 | ||
| 167 | if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { | 195 | if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { |
| 168 | // The current progress validity marker is invalid | 196 | // The current progress validity marker is invalid |
| 169 | } else { | 197 | } else { |
| 170 | // Invalidate progress | 198 | // Invalidate progress |
| 171 | aligned.fill(!STATE_ERASE_VALUE); | 199 | aligned.fill(!STATE_ERASE_VALUE); |
| 172 | self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; | 200 | self.state.write(STATE::WRITE_SIZE as u32, aligned).await?; |
| 173 | } | 201 | } |
| 174 | 202 | ||
| 175 | // Clear magic and progress | 203 | // Clear magic and progress |
| 176 | self.state.wipe(state_flash).await?; | 204 | self.state.erase(0, self.state.capacity() as u32).await?; |
| 177 | 205 | ||
| 178 | // Set magic | 206 | // Set magic |
| 179 | aligned.fill(magic); | 207 | aligned.fill(magic); |
| 180 | self.state.write(state_flash, 0, aligned).await?; | 208 | self.state.write(0, aligned).await?; |
| 181 | } | 209 | } |
| 182 | Ok(()) | 210 | Ok(()) |
| 183 | } | 211 | } |
| @@ -189,19 +217,12 @@ impl FirmwareUpdater { | |||
| 189 | /// # Safety | 217 | /// # Safety |
| 190 | /// | 218 | /// |
| 191 | /// Failing to meet alignment and size requirements may result in a panic. | 219 | /// Failing to meet alignment and size requirements may result in a panic. |
| 192 | pub async fn write_firmware<F: AsyncNorFlash>( | 220 | pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { |
| 193 | &mut self, | 221 | assert!(data.len() >= DFU::ERASE_SIZE); |
| 194 | offset: usize, | ||
| 195 | data: &[u8], | ||
| 196 | dfu_flash: &mut F, | ||
| 197 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 198 | assert!(data.len() >= F::ERASE_SIZE); | ||
| 199 | 222 | ||
| 200 | self.dfu | 223 | self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; |
| 201 | .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) | ||
| 202 | .await?; | ||
| 203 | 224 | ||
| 204 | self.dfu.write(dfu_flash, offset as u32, data).await?; | 225 | self.dfu.write(offset as u32, data).await?; |
| 205 | 226 | ||
| 206 | Ok(()) | 227 | Ok(()) |
| 207 | } | 228 | } |
| @@ -211,18 +232,18 @@ impl FirmwareUpdater { | |||
| 211 | /// | 232 | /// |
| 212 | /// Using this instead of `write_firmware` allows for an optimized API in | 233 | /// Using this instead of `write_firmware` allows for an optimized API in |
| 213 | /// exchange for added complexity. | 234 | /// exchange for added complexity. |
| 214 | pub async fn prepare_update<F: AsyncNorFlash>( | 235 | pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { |
| 215 | &mut self, | 236 | self.dfu.erase(0, self.dfu.capacity() as u32).await?; |
| 216 | dfu_flash: &mut F, | ||
| 217 | ) -> Result<Partition, FirmwareUpdaterError> { | ||
| 218 | self.dfu.wipe(dfu_flash).await?; | ||
| 219 | 237 | ||
| 220 | Ok(self.dfu) | 238 | Ok(&mut self.dfu) |
| 221 | } | 239 | } |
| 222 | } | 240 | } |
| 223 | 241 | ||
| 224 | #[cfg(test)] | 242 | #[cfg(test)] |
| 225 | mod tests { | 243 | mod tests { |
| 244 | use embassy_embedded_hal::flash::partition::Partition; | ||
| 245 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 246 | use embassy_sync::mutex::Mutex; | ||
| 226 | use futures::executor::block_on; | 247 | use futures::executor::block_on; |
| 227 | use sha1::{Digest, Sha1}; | 248 | use sha1::{Digest, Sha1}; |
| 228 | 249 | ||
| @@ -231,20 +252,19 @@ mod tests { | |||
| 231 | 252 | ||
| 232 | #[test] | 253 | #[test] |
| 233 | fn can_verify_sha1() { | 254 | fn can_verify_sha1() { |
| 234 | const STATE: Partition = Partition::new(0, 4096); | 255 | let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default()); |
| 235 | const DFU: Partition = Partition::new(65536, 131072); | 256 | let state = Partition::new(&flash, 0, 4096); |
| 236 | 257 | let dfu = Partition::new(&flash, 65536, 65536); | |
| 237 | let mut flash = MemFlash::<131072, 4096, 8>::default(); | ||
| 238 | 258 | ||
| 239 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | 259 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; |
| 240 | let mut to_write = [0; 4096]; | 260 | let mut to_write = [0; 4096]; |
| 241 | to_write[..7].copy_from_slice(update.as_slice()); | 261 | to_write[..7].copy_from_slice(update.as_slice()); |
| 242 | 262 | ||
| 243 | let mut updater = FirmwareUpdater::new(DFU, STATE); | 263 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); |
| 244 | block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); | 264 | block_on(updater.write_firmware(0, to_write.as_slice())).unwrap(); |
| 245 | let mut chunk_buf = [0; 2]; | 265 | let mut chunk_buf = [0; 2]; |
| 246 | let mut hash = [0; 20]; | 266 | let mut hash = [0; 20]; |
| 247 | block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); | 267 | block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); |
| 248 | 268 | ||
| 249 | assert_eq!(Sha1::digest(update).as_slice(), hash); | 269 | assert_eq!(Sha1::digest(update).as_slice(), hash); |
| 250 | } | 270 | } |
diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs index 50caaf08c..551150c4f 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs | |||
| @@ -1,25 +1,70 @@ | |||
| 1 | use digest::Digest; | 1 | use digest::Digest; |
| 2 | #[cfg(target_os = "none")] | ||
| 3 | use embassy_embedded_hal::flash::partition::BlockingPartition; | ||
| 4 | #[cfg(target_os = "none")] | ||
| 5 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 2 | use embedded_storage::nor_flash::NorFlash; | 6 | use embedded_storage::nor_flash::NorFlash; |
| 3 | 7 | ||
| 4 | use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; | 8 | use super::FirmwareUpdaterConfig; |
| 9 | use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; | ||
| 10 | |||
| 11 | /// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to | ||
| 12 | /// 'mess up' the internal bootloader state | ||
| 13 | pub struct BlockingFirmwareUpdater<DFU: NorFlash, STATE: NorFlash> { | ||
| 14 | dfu: DFU, | ||
| 15 | state: STATE, | ||
| 16 | } | ||
| 17 | |||
| 18 | #[cfg(target_os = "none")] | ||
| 19 | impl<'a, FLASH: NorFlash> | ||
| 20 | FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, FLASH>, BlockingPartition<'a, NoopRawMutex, FLASH>> | ||
| 21 | { | ||
| 22 | /// Create a firmware updater config from the flash and address symbols defined in the linkerfile | ||
| 23 | pub fn from_linkerfile_blocking( | ||
| 24 | flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<FLASH>>, | ||
| 25 | ) -> Self { | ||
| 26 | extern "C" { | ||
| 27 | static __bootloader_state_start: u32; | ||
| 28 | static __bootloader_state_end: u32; | ||
| 29 | static __bootloader_dfu_start: u32; | ||
| 30 | static __bootloader_dfu_end: u32; | ||
| 31 | } | ||
| 32 | |||
| 33 | let dfu = unsafe { | ||
| 34 | let start = &__bootloader_dfu_start as *const u32 as u32; | ||
| 35 | let end = &__bootloader_dfu_end as *const u32 as u32; | ||
| 36 | trace!("DFU: 0x{:x} - 0x{:x}", start, end); | ||
| 37 | |||
| 38 | BlockingPartition::new(flash, start, end - start) | ||
| 39 | }; | ||
| 40 | let state = unsafe { | ||
| 41 | let start = &__bootloader_state_start as *const u32 as u32; | ||
| 42 | let end = &__bootloader_state_end as *const u32 as u32; | ||
| 43 | trace!("STATE: 0x{:x} - 0x{:x}", start, end); | ||
| 44 | |||
| 45 | BlockingPartition::new(flash, start, end - start) | ||
| 46 | }; | ||
| 5 | 47 | ||
| 6 | impl FirmwareUpdater { | ||
| 7 | /// Create a firmware updater instance with partition ranges for the update and state partitions. | ||
| 8 | pub const fn new(dfu: Partition, state: Partition) -> Self { | ||
| 9 | Self { dfu, state } | 48 | Self { dfu, state } |
| 10 | } | 49 | } |
| 50 | } | ||
| 51 | |||
| 52 | impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> { | ||
| 53 | /// Create a firmware updater instance with partition ranges for the update and state partitions. | ||
| 54 | pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self { | ||
| 55 | Self { | ||
| 56 | dfu: config.dfu, | ||
| 57 | state: config.state, | ||
| 58 | } | ||
| 59 | } | ||
| 11 | 60 | ||
| 12 | /// Obtain the current state. | 61 | /// Obtain the current state. |
| 13 | /// | 62 | /// |
| 14 | /// This is useful to check if the bootloader has just done a swap, in order | 63 | /// This is useful to check if the bootloader has just done a swap, in order |
| 15 | /// to do verifications and self-tests of the new image before calling | 64 | /// to do verifications and self-tests of the new image before calling |
| 16 | /// `mark_booted`. | 65 | /// `mark_booted`. |
| 17 | pub fn get_state_blocking<F: NorFlash>( | 66 | pub fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> { |
| 18 | &mut self, | 67 | self.state.read(0, aligned)?; |
| 19 | state_flash: &mut F, | ||
| 20 | aligned: &mut [u8], | ||
| 21 | ) -> Result<State, FirmwareUpdaterError> { | ||
| 22 | self.state.read_blocking(state_flash, 0, aligned)?; | ||
| 23 | 68 | ||
| 24 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { | 69 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { |
| 25 | Ok(State::Swap) | 70 | Ok(State::Swap) |
| @@ -42,19 +87,18 @@ impl FirmwareUpdater { | |||
| 42 | /// | 87 | /// |
| 43 | /// # Safety | 88 | /// # Safety |
| 44 | /// | 89 | /// |
| 45 | /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from | 90 | /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from |
| 46 | /// and written to. | 91 | /// and written to. |
| 47 | #[cfg(feature = "_verify")] | 92 | #[cfg(feature = "_verify")] |
| 48 | pub fn verify_and_mark_updated_blocking<F: NorFlash>( | 93 | pub fn verify_and_mark_updated( |
| 49 | &mut self, | 94 | &mut self, |
| 50 | _state_and_dfu_flash: &mut F, | ||
| 51 | _public_key: &[u8], | 95 | _public_key: &[u8], |
| 52 | _signature: &[u8], | 96 | _signature: &[u8], |
| 53 | _update_len: u32, | 97 | _update_len: u32, |
| 54 | _aligned: &mut [u8], | 98 | _aligned: &mut [u8], |
| 55 | ) -> Result<(), FirmwareUpdaterError> { | 99 | ) -> Result<(), FirmwareUpdaterError> { |
| 56 | assert_eq!(_aligned.len(), F::WRITE_SIZE); | 100 | assert_eq!(_aligned.len(), STATE::WRITE_SIZE); |
| 57 | assert!(_update_len <= self.dfu.size()); | 101 | assert!(_update_len <= self.dfu.capacity() as u32); |
| 58 | 102 | ||
| 59 | #[cfg(feature = "ed25519-dalek")] | 103 | #[cfg(feature = "ed25519-dalek")] |
| 60 | { | 104 | { |
| @@ -68,7 +112,7 @@ impl FirmwareUpdater { | |||
| 68 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | 112 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; |
| 69 | 113 | ||
| 70 | let mut message = [0; 64]; | 114 | let mut message = [0; 64]; |
| 71 | self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; | 115 | self.hash::<Sha512>(_update_len, _aligned, &mut message)?; |
| 72 | 116 | ||
| 73 | public_key.verify(&message, &signature).map_err(into_signature_error)? | 117 | public_key.verify(&message, &signature).map_err(into_signature_error)? |
| 74 | } | 118 | } |
| @@ -89,7 +133,7 @@ impl FirmwareUpdater { | |||
| 89 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | 133 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; |
| 90 | 134 | ||
| 91 | let mut message = [0; 64]; | 135 | let mut message = [0; 64]; |
| 92 | self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; | 136 | self.hash::<Sha512>(_update_len, _aligned, &mut message)?; |
| 93 | 137 | ||
| 94 | let r = public_key.verify(&message, &signature); | 138 | let r = public_key.verify(&message, &signature); |
| 95 | trace!( | 139 | trace!( |
| @@ -102,20 +146,19 @@ impl FirmwareUpdater { | |||
| 102 | r.map_err(into_signature_error)? | 146 | r.map_err(into_signature_error)? |
| 103 | } | 147 | } |
| 104 | 148 | ||
| 105 | self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) | 149 | self.set_magic(_aligned, SWAP_MAGIC) |
| 106 | } | 150 | } |
| 107 | 151 | ||
| 108 | /// Verify the update in DFU with any digest. | 152 | /// Verify the update in DFU with any digest. |
| 109 | pub fn hash_blocking<F: NorFlash, D: Digest>( | 153 | pub fn hash<D: Digest>( |
| 110 | &mut self, | 154 | &mut self, |
| 111 | dfu_flash: &mut F, | ||
| 112 | update_len: u32, | 155 | update_len: u32, |
| 113 | chunk_buf: &mut [u8], | 156 | chunk_buf: &mut [u8], |
| 114 | output: &mut [u8], | 157 | output: &mut [u8], |
| 115 | ) -> Result<(), FirmwareUpdaterError> { | 158 | ) -> Result<(), FirmwareUpdaterError> { |
| 116 | let mut digest = D::new(); | 159 | let mut digest = D::new(); |
| 117 | for offset in (0..update_len).step_by(chunk_buf.len()) { | 160 | for offset in (0..update_len).step_by(chunk_buf.len()) { |
| 118 | self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; | 161 | self.dfu.read(offset, chunk_buf)?; |
| 119 | let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); | 162 | let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); |
| 120 | digest.update(&chunk_buf[..len]); | 163 | digest.update(&chunk_buf[..len]); |
| 121 | } | 164 | } |
| @@ -127,60 +170,44 @@ impl FirmwareUpdater { | |||
| 127 | /// | 170 | /// |
| 128 | /// # Safety | 171 | /// # Safety |
| 129 | /// | 172 | /// |
| 130 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. | 173 | /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. |
| 131 | #[cfg(not(feature = "_verify"))] | 174 | #[cfg(not(feature = "_verify"))] |
| 132 | pub fn mark_updated_blocking<F: NorFlash>( | 175 | pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { |
| 133 | &mut self, | 176 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); |
| 134 | state_flash: &mut F, | 177 | self.set_magic(aligned, SWAP_MAGIC) |
| 135 | aligned: &mut [u8], | ||
| 136 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 137 | assert_eq!(aligned.len(), F::WRITE_SIZE); | ||
| 138 | self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash) | ||
| 139 | } | 178 | } |
| 140 | 179 | ||
| 141 | /// Mark firmware boot successful and stop rollback on reset. | 180 | /// Mark firmware boot successful and stop rollback on reset. |
| 142 | /// | 181 | /// |
| 143 | /// # Safety | 182 | /// # Safety |
| 144 | /// | 183 | /// |
| 145 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. | 184 | /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. |
| 146 | pub fn mark_booted_blocking<F: NorFlash>( | 185 | pub fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { |
| 147 | &mut self, | 186 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); |
| 148 | state_flash: &mut F, | 187 | self.set_magic(aligned, BOOT_MAGIC) |
| 149 | aligned: &mut [u8], | ||
| 150 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 151 | assert_eq!(aligned.len(), F::WRITE_SIZE); | ||
| 152 | self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash) | ||
| 153 | } | 188 | } |
| 154 | 189 | ||
| 155 | fn set_magic_blocking<F: NorFlash>( | 190 | fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> { |
| 156 | &mut self, | 191 | self.state.read(0, aligned)?; |
| 157 | aligned: &mut [u8], | ||
| 158 | magic: u8, | ||
| 159 | state_flash: &mut F, | ||
| 160 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 161 | self.state.read_blocking(state_flash, 0, aligned)?; | ||
| 162 | 192 | ||
| 163 | if aligned.iter().any(|&b| b != magic) { | 193 | if aligned.iter().any(|&b| b != magic) { |
| 164 | // Read progress validity | 194 | // Read progress validity |
| 165 | self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; | 195 | self.state.read(STATE::WRITE_SIZE as u32, aligned)?; |
| 166 | |||
| 167 | // FIXME: Do not make this assumption. | ||
| 168 | const STATE_ERASE_VALUE: u8 = 0xFF; | ||
| 169 | 196 | ||
| 170 | if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { | 197 | if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { |
| 171 | // The current progress validity marker is invalid | 198 | // The current progress validity marker is invalid |
| 172 | } else { | 199 | } else { |
| 173 | // Invalidate progress | 200 | // Invalidate progress |
| 174 | aligned.fill(!STATE_ERASE_VALUE); | 201 | aligned.fill(!STATE_ERASE_VALUE); |
| 175 | self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; | 202 | self.state.write(STATE::WRITE_SIZE as u32, aligned)?; |
| 176 | } | 203 | } |
| 177 | 204 | ||
| 178 | // Clear magic and progress | 205 | // Clear magic and progress |
| 179 | self.state.wipe_blocking(state_flash)?; | 206 | self.state.erase(0, self.state.capacity() as u32)?; |
| 180 | 207 | ||
| 181 | // Set magic | 208 | // Set magic |
| 182 | aligned.fill(magic); | 209 | aligned.fill(magic); |
| 183 | self.state.write_blocking(state_flash, 0, aligned)?; | 210 | self.state.write(0, aligned)?; |
| 184 | } | 211 | } |
| 185 | Ok(()) | 212 | Ok(()) |
| 186 | } | 213 | } |
| @@ -192,18 +219,12 @@ impl FirmwareUpdater { | |||
| 192 | /// # Safety | 219 | /// # Safety |
| 193 | /// | 220 | /// |
| 194 | /// Failing to meet alignment and size requirements may result in a panic. | 221 | /// Failing to meet alignment and size requirements may result in a panic. |
| 195 | pub fn write_firmware_blocking<F: NorFlash>( | 222 | pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { |
| 196 | &mut self, | 223 | assert!(data.len() >= DFU::ERASE_SIZE); |
| 197 | offset: usize, | ||
| 198 | data: &[u8], | ||
| 199 | dfu_flash: &mut F, | ||
| 200 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 201 | assert!(data.len() >= F::ERASE_SIZE); | ||
| 202 | 224 | ||
| 203 | self.dfu | 225 | self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; |
| 204 | .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; | ||
| 205 | 226 | ||
| 206 | self.dfu.write_blocking(dfu_flash, offset as u32, data)?; | 227 | self.dfu.write(offset as u32, data)?; |
| 207 | 228 | ||
| 208 | Ok(()) | 229 | Ok(()) |
| 209 | } | 230 | } |
| @@ -211,11 +232,45 @@ impl FirmwareUpdater { | |||
| 211 | /// Prepare for an incoming DFU update by erasing the entire DFU area and | 232 | /// Prepare for an incoming DFU update by erasing the entire DFU area and |
| 212 | /// returning its `Partition`. | 233 | /// returning its `Partition`. |
| 213 | /// | 234 | /// |
| 214 | /// Using this instead of `write_firmware_blocking` allows for an optimized | 235 | /// Using this instead of `write_firmware` allows for an optimized API in |
| 215 | /// API in exchange for added complexity. | 236 | /// exchange for added complexity. |
| 216 | pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<Partition, FirmwareUpdaterError> { | 237 | pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { |
| 217 | self.dfu.wipe_blocking(flash)?; | 238 | self.dfu.erase(0, self.dfu.capacity() as u32)?; |
| 239 | |||
| 240 | Ok(&mut self.dfu) | ||
| 241 | } | ||
| 242 | } | ||
| 218 | 243 | ||
| 219 | Ok(self.dfu) | 244 | #[cfg(test)] |
| 245 | mod tests { | ||
| 246 | use core::cell::RefCell; | ||
| 247 | |||
| 248 | use embassy_embedded_hal::flash::partition::BlockingPartition; | ||
| 249 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 250 | use embassy_sync::blocking_mutex::Mutex; | ||
| 251 | use sha1::{Digest, Sha1}; | ||
| 252 | |||
| 253 | use super::*; | ||
| 254 | use crate::mem_flash::MemFlash; | ||
| 255 | |||
| 256 | #[test] | ||
| 257 | fn can_verify_sha1() { | ||
| 258 | let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default())); | ||
| 259 | let state = BlockingPartition::new(&flash, 0, 4096); | ||
| 260 | let dfu = BlockingPartition::new(&flash, 65536, 65536); | ||
| 261 | |||
| 262 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | ||
| 263 | let mut to_write = [0; 4096]; | ||
| 264 | to_write[..7].copy_from_slice(update.as_slice()); | ||
| 265 | |||
| 266 | let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); | ||
| 267 | updater.write_firmware(0, to_write.as_slice()).unwrap(); | ||
| 268 | let mut chunk_buf = [0; 2]; | ||
| 269 | let mut hash = [0; 20]; | ||
| 270 | updater | ||
| 271 | .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash) | ||
| 272 | .unwrap(); | ||
| 273 | |||
| 274 | assert_eq!(Sha1::digest(update).as_slice(), hash); | ||
| 220 | } | 275 | } |
| 221 | } | 276 | } |
diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs index e09f5eb6c..a37984a3a 100644 --- a/embassy-boot/boot/src/firmware_updater/mod.rs +++ b/embassy-boot/boot/src/firmware_updater/mod.rs | |||
| @@ -2,9 +2,22 @@ | |||
| 2 | mod asynch; | 2 | mod asynch; |
| 3 | mod blocking; | 3 | mod blocking; |
| 4 | 4 | ||
| 5 | #[cfg(feature = "nightly")] | ||
| 6 | pub use asynch::FirmwareUpdater; | ||
| 7 | pub use blocking::BlockingFirmwareUpdater; | ||
| 5 | use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; | 8 | use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; |
| 6 | 9 | ||
| 7 | use crate::Partition; | 10 | /// Firmware updater flash configuration holding the two flashes used by the updater |
| 11 | /// | ||
| 12 | /// If only a single flash is actually used, then that flash should be partitioned into two partitions before use. | ||
| 13 | /// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition | ||
| 14 | /// the provided flash according to symbols defined in the linkerfile. | ||
| 15 | pub struct FirmwareUpdaterConfig<DFU, STATE> { | ||
| 16 | /// The dfu flash partition | ||
| 17 | pub dfu: DFU, | ||
| 18 | /// The state flash partition | ||
| 19 | pub state: STATE, | ||
| 20 | } | ||
| 8 | 21 | ||
| 9 | /// Errors returned by FirmwareUpdater | 22 | /// Errors returned by FirmwareUpdater |
| 10 | #[derive(Debug)] | 23 | #[derive(Debug)] |
| @@ -33,39 +46,3 @@ where | |||
| 33 | FirmwareUpdaterError::Flash(error.kind()) | 46 | FirmwareUpdaterError::Flash(error.kind()) |
| 34 | } | 47 | } |
| 35 | } | 48 | } |
| 36 | |||
| 37 | /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to | ||
| 38 | /// 'mess up' the internal bootloader state | ||
| 39 | pub struct FirmwareUpdater { | ||
| 40 | state: Partition, | ||
| 41 | dfu: Partition, | ||
| 42 | } | ||
| 43 | |||
| 44 | #[cfg(target_os = "none")] | ||
| 45 | impl Default for FirmwareUpdater { | ||
| 46 | fn default() -> Self { | ||
| 47 | extern "C" { | ||
| 48 | static __bootloader_state_start: u32; | ||
| 49 | static __bootloader_state_end: u32; | ||
| 50 | static __bootloader_dfu_start: u32; | ||
| 51 | static __bootloader_dfu_end: u32; | ||
| 52 | } | ||
| 53 | |||
| 54 | let dfu = unsafe { | ||
| 55 | Partition::new( | ||
| 56 | &__bootloader_dfu_start as *const u32 as u32, | ||
| 57 | &__bootloader_dfu_end as *const u32 as u32, | ||
| 58 | ) | ||
| 59 | }; | ||
| 60 | let state = unsafe { | ||
| 61 | Partition::new( | ||
| 62 | &__bootloader_state_start as *const u32 as u32, | ||
| 63 | &__bootloader_state_end as *const u32 as u32, | ||
| 64 | ) | ||
| 65 | }; | ||
| 66 | |||
| 67 | trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); | ||
| 68 | trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); | ||
| 69 | FirmwareUpdater::new(dfu, state) | ||
| 70 | } | ||
| 71 | } | ||
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4521fecb0..45a87bd0e 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs | |||
| @@ -7,12 +7,18 @@ mod fmt; | |||
| 7 | mod boot_loader; | 7 | mod boot_loader; |
| 8 | mod digest_adapters; | 8 | mod digest_adapters; |
| 9 | mod firmware_updater; | 9 | mod firmware_updater; |
| 10 | #[cfg(test)] | ||
| 10 | mod mem_flash; | 11 | mod mem_flash; |
| 11 | mod partition; | 12 | #[cfg(test)] |
| 13 | mod test_flash; | ||
| 12 | 14 | ||
| 13 | pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig}; | 15 | // The expected value of the flash after an erase |
| 14 | pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; | 16 | // TODO: Use the value provided by NorFlash when available |
| 15 | pub use partition::Partition; | 17 | pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF; |
| 18 | pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; | ||
| 19 | #[cfg(feature = "nightly")] | ||
| 20 | pub use firmware_updater::FirmwareUpdater; | ||
| 21 | pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError}; | ||
| 16 | 22 | ||
| 17 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; | 23 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; |
| 18 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; | 24 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; |
| @@ -45,10 +51,18 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> { | |||
| 45 | 51 | ||
| 46 | #[cfg(test)] | 52 | #[cfg(test)] |
| 47 | mod tests { | 53 | mod tests { |
| 54 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 55 | #[cfg(feature = "nightly")] | ||
| 56 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; | ||
| 48 | use futures::executor::block_on; | 57 | use futures::executor::block_on; |
| 49 | 58 | ||
| 50 | use super::*; | 59 | use super::*; |
| 60 | use crate::boot_loader::BootLoaderConfig; | ||
| 61 | use crate::firmware_updater::FirmwareUpdaterConfig; | ||
| 51 | use crate::mem_flash::MemFlash; | 62 | use crate::mem_flash::MemFlash; |
| 63 | #[cfg(feature = "nightly")] | ||
| 64 | use crate::test_flash::AsyncTestFlash; | ||
| 65 | use crate::test_flash::BlockingTestFlash; | ||
| 52 | 66 | ||
| 53 | /* | 67 | /* |
| 54 | #[test] | 68 | #[test] |
| @@ -67,147 +81,173 @@ mod tests { | |||
| 67 | 81 | ||
| 68 | #[test] | 82 | #[test] |
| 69 | fn test_boot_state() { | 83 | fn test_boot_state() { |
| 70 | const STATE: Partition = Partition::new(0, 4096); | 84 | let flash = BlockingTestFlash::new(BootLoaderConfig { |
| 71 | const ACTIVE: Partition = Partition::new(4096, 61440); | 85 | active: MemFlash::<57344, 4096, 4>::default(), |
| 72 | const DFU: Partition = Partition::new(61440, 122880); | 86 | dfu: MemFlash::<61440, 4096, 4>::default(), |
| 87 | state: MemFlash::<4096, 4096, 4>::default(), | ||
| 88 | }); | ||
| 73 | 89 | ||
| 74 | let mut flash = MemFlash::<131072, 4096, 4>::default(); | 90 | flash.state().write(0, &[BOOT_MAGIC; 4]).unwrap(); |
| 75 | flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); | ||
| 76 | let mut flash = SingleFlashConfig::new(&mut flash); | ||
| 77 | 91 | ||
| 78 | let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); | 92 | let mut bootloader = BootLoader::new(BootLoaderConfig { |
| 93 | active: flash.active(), | ||
| 94 | dfu: flash.dfu(), | ||
| 95 | state: flash.state(), | ||
| 96 | }); | ||
| 79 | 97 | ||
| 80 | let mut page = [0; 4096]; | 98 | let mut page = [0; 4096]; |
| 81 | assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash, &mut page).unwrap()); | 99 | assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap()); |
| 82 | } | 100 | } |
| 83 | 101 | ||
| 84 | #[test] | 102 | #[test] |
| 85 | #[cfg(all(feature = "nightly", not(feature = "_verify")))] | 103 | #[cfg(all(feature = "nightly", not(feature = "_verify")))] |
| 86 | fn test_swap_state() { | 104 | fn test_swap_state() { |
| 87 | const STATE: Partition = Partition::new(0, 4096); | 105 | const FIRMWARE_SIZE: usize = 57344; |
| 88 | const ACTIVE: Partition = Partition::new(4096, 61440); | 106 | let flash = AsyncTestFlash::new(BootLoaderConfig { |
| 89 | const DFU: Partition = Partition::new(61440, 122880); | 107 | active: MemFlash::<FIRMWARE_SIZE, 4096, 4>::default(), |
| 90 | let mut flash = MemFlash::<131072, 4096, 4>::random(); | 108 | dfu: MemFlash::<61440, 4096, 4>::default(), |
| 91 | 109 | state: MemFlash::<4096, 4096, 4>::default(), | |
| 92 | let original = [rand::random::<u8>(); ACTIVE.size() as usize]; | 110 | }); |
| 93 | let update = [rand::random::<u8>(); ACTIVE.size() as usize]; | 111 | |
| 112 | const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE]; | ||
| 113 | const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE]; | ||
| 94 | let mut aligned = [0; 4]; | 114 | let mut aligned = [0; 4]; |
| 95 | 115 | ||
| 96 | flash.program(ACTIVE.from, &original).unwrap(); | 116 | block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); |
| 117 | block_on(flash.active().write(0, &ORIGINAL)).unwrap(); | ||
| 118 | |||
| 119 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { | ||
| 120 | dfu: flash.dfu(), | ||
| 121 | state: flash.state(), | ||
| 122 | }); | ||
| 123 | block_on(updater.write_firmware(0, &UPDATE)).unwrap(); | ||
| 124 | block_on(updater.mark_updated(&mut aligned)).unwrap(); | ||
| 97 | 125 | ||
| 98 | let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); | 126 | let flash = flash.into_blocking(); |
| 99 | let mut updater = FirmwareUpdater::new(DFU, STATE); | 127 | let mut bootloader = BootLoader::new(BootLoaderConfig { |
| 100 | block_on(updater.write_firmware(0, &update, &mut flash)).unwrap(); | 128 | active: flash.active(), |
| 101 | block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); | 129 | dfu: flash.dfu(), |
| 130 | state: flash.state(), | ||
| 131 | }); | ||
| 102 | 132 | ||
| 103 | let mut page = [0; 1024]; | 133 | let mut page = [0; 1024]; |
| 104 | assert_eq!( | 134 | assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); |
| 105 | State::Swap, | ||
| 106 | bootloader | ||
| 107 | .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) | ||
| 108 | .unwrap() | ||
| 109 | ); | ||
| 110 | 135 | ||
| 111 | flash.assert_eq(ACTIVE.from, &update); | 136 | let mut read_buf = [0; FIRMWARE_SIZE]; |
| 137 | flash.active().read(0, &mut read_buf).unwrap(); | ||
| 138 | assert_eq!(UPDATE, read_buf); | ||
| 112 | // First DFU page is untouched | 139 | // First DFU page is untouched |
| 113 | flash.assert_eq(DFU.from + 4096, &original); | 140 | flash.dfu().read(4096, &mut read_buf).unwrap(); |
| 141 | assert_eq!(ORIGINAL, read_buf); | ||
| 114 | 142 | ||
| 115 | // Running again should cause a revert | 143 | // Running again should cause a revert |
| 116 | assert_eq!( | 144 | assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); |
| 117 | State::Swap, | ||
| 118 | bootloader | ||
| 119 | .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) | ||
| 120 | .unwrap() | ||
| 121 | ); | ||
| 122 | 145 | ||
| 123 | flash.assert_eq(ACTIVE.from, &original); | 146 | let mut read_buf = [0; FIRMWARE_SIZE]; |
| 124 | // Last page is untouched | 147 | flash.active().read(0, &mut read_buf).unwrap(); |
| 125 | flash.assert_eq(DFU.from, &update); | 148 | assert_eq!(ORIGINAL, read_buf); |
| 149 | // Last DFU page is untouched | ||
| 150 | flash.dfu().read(0, &mut read_buf).unwrap(); | ||
| 151 | assert_eq!(UPDATE, read_buf); | ||
| 126 | 152 | ||
| 127 | // Mark as booted | 153 | // Mark as booted |
| 128 | block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap(); | 154 | let flash = flash.into_async(); |
| 129 | assert_eq!( | 155 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { |
| 130 | State::Boot, | 156 | dfu: flash.dfu(), |
| 131 | bootloader | 157 | state: flash.state(), |
| 132 | .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) | 158 | }); |
| 133 | .unwrap() | 159 | block_on(updater.mark_booted(&mut aligned)).unwrap(); |
| 134 | ); | 160 | |
| 161 | let flash = flash.into_blocking(); | ||
| 162 | let mut bootloader = BootLoader::new(BootLoaderConfig { | ||
| 163 | active: flash.active(), | ||
| 164 | dfu: flash.dfu(), | ||
| 165 | state: flash.state(), | ||
| 166 | }); | ||
| 167 | assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap()); | ||
| 135 | } | 168 | } |
| 136 | 169 | ||
| 137 | #[test] | 170 | #[test] |
| 138 | #[cfg(all(feature = "nightly", not(feature = "_verify")))] | 171 | #[cfg(all(feature = "nightly", not(feature = "_verify")))] |
| 139 | fn test_separate_flash_active_page_biggest() { | 172 | fn test_swap_state_active_page_biggest() { |
| 140 | const STATE: Partition = Partition::new(2048, 4096); | 173 | const FIRMWARE_SIZE: usize = 12288; |
| 141 | const ACTIVE: Partition = Partition::new(4096, 16384); | 174 | let flash = AsyncTestFlash::new(BootLoaderConfig { |
| 142 | const DFU: Partition = Partition::new(0, 16384); | 175 | active: MemFlash::<12288, 4096, 8>::random(), |
| 143 | 176 | dfu: MemFlash::<16384, 2048, 8>::random(), | |
| 144 | let mut active = MemFlash::<16384, 4096, 8>::random(); | 177 | state: MemFlash::<2048, 128, 4>::random(), |
| 145 | let mut dfu = MemFlash::<16384, 2048, 8>::random(); | 178 | }); |
| 146 | let mut state = MemFlash::<4096, 128, 4>::random(); | 179 | |
| 180 | const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE]; | ||
| 181 | const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE]; | ||
| 147 | let mut aligned = [0; 4]; | 182 | let mut aligned = [0; 4]; |
| 148 | 183 | ||
| 149 | let original = [rand::random::<u8>(); ACTIVE.size() as usize]; | 184 | block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); |
| 150 | let update = [rand::random::<u8>(); ACTIVE.size() as usize]; | 185 | block_on(flash.active().write(0, &ORIGINAL)).unwrap(); |
| 151 | |||
| 152 | active.program(ACTIVE.from, &original).unwrap(); | ||
| 153 | 186 | ||
| 154 | let mut updater = FirmwareUpdater::new(DFU, STATE); | 187 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { |
| 188 | dfu: flash.dfu(), | ||
| 189 | state: flash.state(), | ||
| 190 | }); | ||
| 191 | block_on(updater.write_firmware(0, &UPDATE)).unwrap(); | ||
| 192 | block_on(updater.mark_updated(&mut aligned)).unwrap(); | ||
| 155 | 193 | ||
| 156 | block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); | 194 | let flash = flash.into_blocking(); |
| 157 | block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); | 195 | let mut bootloader = BootLoader::new(BootLoaderConfig { |
| 196 | active: flash.active(), | ||
| 197 | dfu: flash.dfu(), | ||
| 198 | state: flash.state(), | ||
| 199 | }); | ||
| 158 | 200 | ||
| 159 | let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); | ||
| 160 | let mut page = [0; 4096]; | 201 | let mut page = [0; 4096]; |
| 202 | assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); | ||
| 161 | 203 | ||
| 162 | assert_eq!( | 204 | let mut read_buf = [0; FIRMWARE_SIZE]; |
| 163 | State::Swap, | 205 | flash.active().read(0, &mut read_buf).unwrap(); |
| 164 | bootloader | 206 | assert_eq!(UPDATE, read_buf); |
| 165 | .prepare_boot(&mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), &mut page) | ||
| 166 | .unwrap() | ||
| 167 | ); | ||
| 168 | |||
| 169 | active.assert_eq(ACTIVE.from, &update); | ||
| 170 | // First DFU page is untouched | 207 | // First DFU page is untouched |
| 171 | dfu.assert_eq(DFU.from + 4096, &original); | 208 | flash.dfu().read(4096, &mut read_buf).unwrap(); |
| 209 | assert_eq!(ORIGINAL, read_buf); | ||
| 172 | } | 210 | } |
| 173 | 211 | ||
| 174 | #[test] | 212 | #[test] |
| 175 | #[cfg(all(feature = "nightly", not(feature = "_verify")))] | 213 | #[cfg(all(feature = "nightly", not(feature = "_verify")))] |
| 176 | fn test_separate_flash_dfu_page_biggest() { | 214 | fn test_swap_state_dfu_page_biggest() { |
| 177 | const STATE: Partition = Partition::new(2048, 4096); | 215 | const FIRMWARE_SIZE: usize = 12288; |
| 178 | const ACTIVE: Partition = Partition::new(4096, 16384); | 216 | let flash = AsyncTestFlash::new(BootLoaderConfig { |
| 179 | const DFU: Partition = Partition::new(0, 16384); | 217 | active: MemFlash::<FIRMWARE_SIZE, 2048, 4>::random(), |
| 180 | 218 | dfu: MemFlash::<16384, 4096, 8>::random(), | |
| 219 | state: MemFlash::<2048, 128, 4>::random(), | ||
| 220 | }); | ||
| 221 | |||
| 222 | const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE]; | ||
| 223 | const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE]; | ||
| 181 | let mut aligned = [0; 4]; | 224 | let mut aligned = [0; 4]; |
| 182 | let mut active = MemFlash::<16384, 2048, 4>::random(); | ||
| 183 | let mut dfu = MemFlash::<16384, 4096, 8>::random(); | ||
| 184 | let mut state = MemFlash::<4096, 128, 4>::random(); | ||
| 185 | |||
| 186 | let original = [rand::random::<u8>(); ACTIVE.size() as usize]; | ||
| 187 | let update = [rand::random::<u8>(); ACTIVE.size() as usize]; | ||
| 188 | |||
| 189 | active.program(ACTIVE.from, &original).unwrap(); | ||
| 190 | 225 | ||
| 191 | let mut updater = FirmwareUpdater::new(DFU, STATE); | 226 | block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); |
| 192 | 227 | block_on(flash.active().write(0, &ORIGINAL)).unwrap(); | |
| 193 | block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); | 228 | |
| 194 | block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); | 229 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { |
| 195 | 230 | dfu: flash.dfu(), | |
| 196 | let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); | 231 | state: flash.state(), |
| 232 | }); | ||
| 233 | block_on(updater.write_firmware(0, &UPDATE)).unwrap(); | ||
| 234 | block_on(updater.mark_updated(&mut aligned)).unwrap(); | ||
| 235 | |||
| 236 | let flash = flash.into_blocking(); | ||
| 237 | let mut bootloader = BootLoader::new(BootLoaderConfig { | ||
| 238 | active: flash.active(), | ||
| 239 | dfu: flash.dfu(), | ||
| 240 | state: flash.state(), | ||
| 241 | }); | ||
| 197 | let mut page = [0; 4096]; | 242 | let mut page = [0; 4096]; |
| 198 | assert_eq!( | 243 | assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); |
| 199 | State::Swap, | ||
| 200 | bootloader | ||
| 201 | .prepare_boot( | ||
| 202 | &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,), | ||
| 203 | &mut page | ||
| 204 | ) | ||
| 205 | .unwrap() | ||
| 206 | ); | ||
| 207 | 244 | ||
| 208 | active.assert_eq(ACTIVE.from, &update); | 245 | let mut read_buf = [0; FIRMWARE_SIZE]; |
| 246 | flash.active().read(0, &mut read_buf).unwrap(); | ||
| 247 | assert_eq!(UPDATE, read_buf); | ||
| 209 | // First DFU page is untouched | 248 | // First DFU page is untouched |
| 210 | dfu.assert_eq(DFU.from + 4096, &original); | 249 | flash.dfu().read(4096, &mut read_buf).unwrap(); |
| 250 | assert_eq!(ORIGINAL, read_buf); | ||
| 211 | } | 251 | } |
| 212 | 252 | ||
| 213 | #[test] | 253 | #[test] |
| @@ -233,25 +273,28 @@ mod tests { | |||
| 233 | let public_key: PublicKey = keypair.public; | 273 | let public_key: PublicKey = keypair.public; |
| 234 | 274 | ||
| 235 | // Setup flash | 275 | // Setup flash |
| 236 | 276 | let flash = BlockingTestFlash::new(BootLoaderConfig { | |
| 237 | const STATE: Partition = Partition::new(0, 4096); | 277 | active: MemFlash::<0, 0, 0>::default(), |
| 238 | const DFU: Partition = Partition::new(4096, 8192); | 278 | dfu: MemFlash::<4096, 4096, 4>::default(), |
| 239 | let mut flash = MemFlash::<8192, 4096, 4>::default(); | 279 | state: MemFlash::<4096, 4096, 4>::default(), |
| 280 | }); | ||
| 240 | 281 | ||
| 241 | let firmware_len = firmware.len(); | 282 | let firmware_len = firmware.len(); |
| 242 | 283 | ||
| 243 | let mut write_buf = [0; 4096]; | 284 | let mut write_buf = [0; 4096]; |
| 244 | write_buf[0..firmware_len].copy_from_slice(firmware); | 285 | write_buf[0..firmware_len].copy_from_slice(firmware); |
| 245 | DFU.write_blocking(&mut flash, 0, &write_buf).unwrap(); | 286 | flash.dfu().write(0, &write_buf).unwrap(); |
| 246 | 287 | ||
| 247 | // On with the test | 288 | // On with the test |
| 248 | 289 | let flash = flash.into_async(); | |
| 249 | let mut updater = FirmwareUpdater::new(DFU, STATE); | 290 | let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { |
| 291 | dfu: flash.dfu(), | ||
| 292 | state: flash.state(), | ||
| 293 | }); | ||
| 250 | 294 | ||
| 251 | let mut aligned = [0; 4]; | 295 | let mut aligned = [0; 4]; |
| 252 | 296 | ||
| 253 | assert!(block_on(updater.verify_and_mark_updated( | 297 | assert!(block_on(updater.verify_and_mark_updated( |
| 254 | &mut flash, | ||
| 255 | &public_key.to_bytes(), | 298 | &public_key.to_bytes(), |
| 256 | &signature.to_bytes(), | 299 | &signature.to_bytes(), |
| 257 | firmware_len as u32, | 300 | firmware_len as u32, |
diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index 3ba84a92c..2728e9720 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs | |||
| @@ -34,21 +34,61 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFla | |||
| 34 | } | 34 | } |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | pub fn program(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> { | 37 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), MemFlashError> { |
| 38 | let len = bytes.len(); | ||
| 39 | bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); | ||
| 40 | Ok(()) | ||
| 41 | } | ||
| 42 | |||
| 43 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> { | ||
| 38 | let offset = offset as usize; | 44 | let offset = offset as usize; |
| 39 | assert!(bytes.len() % WRITE_SIZE == 0); | 45 | assert!(bytes.len() % WRITE_SIZE == 0); |
| 40 | assert!(offset % WRITE_SIZE == 0); | 46 | assert!(offset % WRITE_SIZE == 0); |
| 41 | assert!(offset + bytes.len() <= SIZE); | 47 | assert!(offset + bytes.len() <= SIZE); |
| 42 | 48 | ||
| 43 | self.mem[offset..offset + bytes.len()].copy_from_slice(bytes); | 49 | if let Some(pending_successes) = self.pending_write_successes { |
| 50 | if pending_successes > 0 { | ||
| 51 | self.pending_write_successes = Some(pending_successes - 1); | ||
| 52 | } else { | ||
| 53 | return Err(MemFlashError); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | for ((offset, mem_byte), new_byte) in self | ||
| 58 | .mem | ||
| 59 | .iter_mut() | ||
| 60 | .enumerate() | ||
| 61 | .skip(offset) | ||
| 62 | .take(bytes.len()) | ||
| 63 | .zip(bytes) | ||
| 64 | { | ||
| 65 | assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); | ||
| 66 | *mem_byte = *new_byte; | ||
| 67 | } | ||
| 44 | 68 | ||
| 45 | Ok(()) | 69 | Ok(()) |
| 46 | } | 70 | } |
| 47 | 71 | ||
| 48 | pub fn assert_eq(&self, offset: u32, expectation: &[u8]) { | 72 | fn erase(&mut self, from: u32, to: u32) -> Result<(), MemFlashError> { |
| 49 | for i in 0..expectation.len() { | 73 | let from = from as usize; |
| 50 | assert_eq!(self.mem[offset as usize + i], expectation[i], "Index {}", i); | 74 | let to = to as usize; |
| 75 | assert!(from % ERASE_SIZE == 0); | ||
| 76 | assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); | ||
| 77 | for i in from..to { | ||
| 78 | self.mem[i] = 0xFF; | ||
| 51 | } | 79 | } |
| 80 | Ok(()) | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn program(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> { | ||
| 84 | let offset = offset as usize; | ||
| 85 | assert!(bytes.len() % WRITE_SIZE == 0); | ||
| 86 | assert!(offset % WRITE_SIZE == 0); | ||
| 87 | assert!(offset + bytes.len() <= SIZE); | ||
| 88 | |||
| 89 | self.mem[offset..offset + bytes.len()].copy_from_slice(bytes); | ||
| 90 | |||
| 91 | Ok(()) | ||
| 52 | } | 92 | } |
| 53 | } | 93 | } |
| 54 | 94 | ||
| @@ -78,9 +118,7 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNo | |||
| 78 | const READ_SIZE: usize = 1; | 118 | const READ_SIZE: usize = 1; |
| 79 | 119 | ||
| 80 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | 120 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { |
| 81 | let len = bytes.len(); | 121 | self.read(offset, bytes) |
| 82 | bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); | ||
| 83 | Ok(()) | ||
| 84 | } | 122 | } |
| 85 | 123 | ||
| 86 | fn capacity(&self) -> usize { | 124 | fn capacity(&self) -> usize { |
| @@ -94,44 +132,12 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFla | |||
| 94 | const WRITE_SIZE: usize = WRITE_SIZE; | 132 | const WRITE_SIZE: usize = WRITE_SIZE; |
| 95 | const ERASE_SIZE: usize = ERASE_SIZE; | 133 | const ERASE_SIZE: usize = ERASE_SIZE; |
| 96 | 134 | ||
| 97 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | ||
| 98 | let from = from as usize; | ||
| 99 | let to = to as usize; | ||
| 100 | assert!(from % ERASE_SIZE == 0); | ||
| 101 | assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); | ||
| 102 | for i in from..to { | ||
| 103 | self.mem[i] = 0xFF; | ||
| 104 | } | ||
| 105 | Ok(()) | ||
| 106 | } | ||
| 107 | |||
| 108 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { | 135 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { |
| 109 | let offset = offset as usize; | 136 | self.write(offset, bytes) |
| 110 | assert!(bytes.len() % WRITE_SIZE == 0); | 137 | } |
| 111 | assert!(offset % WRITE_SIZE == 0); | ||
| 112 | assert!(offset + bytes.len() <= SIZE); | ||
| 113 | |||
| 114 | if let Some(pending_successes) = self.pending_write_successes { | ||
| 115 | if pending_successes > 0 { | ||
| 116 | self.pending_write_successes = Some(pending_successes - 1); | ||
| 117 | } else { | ||
| 118 | return Err(MemFlashError); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | for ((offset, mem_byte), new_byte) in self | ||
| 123 | .mem | ||
| 124 | .iter_mut() | ||
| 125 | .enumerate() | ||
| 126 | .skip(offset) | ||
| 127 | .take(bytes.len()) | ||
| 128 | .zip(bytes) | ||
| 129 | { | ||
| 130 | assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); | ||
| 131 | *mem_byte = *new_byte; | ||
| 132 | } | ||
| 133 | 138 | ||
| 134 | Ok(()) | 139 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { |
| 140 | self.erase(from, to) | ||
| 135 | } | 141 | } |
| 136 | } | 142 | } |
| 137 | 143 | ||
| @@ -142,11 +148,11 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncR | |||
| 142 | const READ_SIZE: usize = 1; | 148 | const READ_SIZE: usize = 1; |
| 143 | 149 | ||
| 144 | async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | 150 | async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { |
| 145 | <Self as ReadNorFlash>::read(self, offset, bytes) | 151 | self.read(offset, bytes) |
| 146 | } | 152 | } |
| 147 | 153 | ||
| 148 | fn capacity(&self) -> usize { | 154 | fn capacity(&self) -> usize { |
| 149 | <Self as ReadNorFlash>::capacity(self) | 155 | SIZE |
| 150 | } | 156 | } |
| 151 | } | 157 | } |
| 152 | 158 | ||
| @@ -157,11 +163,11 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncN | |||
| 157 | const WRITE_SIZE: usize = WRITE_SIZE; | 163 | const WRITE_SIZE: usize = WRITE_SIZE; |
| 158 | const ERASE_SIZE: usize = ERASE_SIZE; | 164 | const ERASE_SIZE: usize = ERASE_SIZE; |
| 159 | 165 | ||
| 160 | async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | 166 | async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { |
| 161 | <Self as NorFlash>::erase(self, from, to) | 167 | self.write(offset, bytes) |
| 162 | } | 168 | } |
| 163 | 169 | ||
| 164 | async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { | 170 | async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { |
| 165 | <Self as NorFlash>::write(self, offset, bytes) | 171 | self.erase(from, to) |
| 166 | } | 172 | } |
| 167 | } | 173 | } |
diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs deleted file mode 100644 index 7b56a8240..000000000 --- a/embassy-boot/boot/src/partition.rs +++ /dev/null | |||
| @@ -1,144 +0,0 @@ | |||
| 1 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 2 | #[cfg(feature = "nightly")] | ||
| 3 | use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; | ||
| 4 | |||
| 5 | /// A region in flash used by the bootloader. | ||
| 6 | #[derive(Copy, Clone, Debug)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | pub struct Partition { | ||
| 9 | /// The offset into the flash where the partition starts. | ||
| 10 | pub from: u32, | ||
| 11 | /// The offset into the flash where the partition ends. | ||
| 12 | pub to: u32, | ||
| 13 | } | ||
| 14 | |||
| 15 | impl Partition { | ||
| 16 | /// Create a new partition with the provided range | ||
| 17 | pub const fn new(from: u32, to: u32) -> Self { | ||
| 18 | Self { from, to } | ||
| 19 | } | ||
| 20 | |||
| 21 | /// Return the size of the partition | ||
| 22 | pub const fn size(&self) -> u32 { | ||
| 23 | self.to - self.from | ||
| 24 | } | ||
| 25 | |||
| 26 | /// Read from the partition on the provided flash | ||
| 27 | #[cfg(feature = "nightly")] | ||
| 28 | pub async fn read<F: AsyncReadNorFlash>( | ||
| 29 | &self, | ||
| 30 | flash: &mut F, | ||
| 31 | offset: u32, | ||
| 32 | bytes: &mut [u8], | ||
| 33 | ) -> Result<(), F::Error> { | ||
| 34 | let offset = self.from as u32 + offset; | ||
| 35 | flash.read(offset, bytes).await | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Write to the partition on the provided flash | ||
| 39 | #[cfg(feature = "nightly")] | ||
| 40 | pub async fn write<F: AsyncNorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { | ||
| 41 | let offset = self.from as u32 + offset; | ||
| 42 | flash.write(offset, bytes).await?; | ||
| 43 | trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); | ||
| 44 | Ok(()) | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Erase part of the partition on the provided flash | ||
| 48 | #[cfg(feature = "nightly")] | ||
| 49 | pub async fn erase<F: AsyncNorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { | ||
| 50 | let from = self.from as u32 + from; | ||
| 51 | let to = self.from as u32 + to; | ||
| 52 | flash.erase(from, to).await?; | ||
| 53 | trace!("Erased from 0x{:x} to 0x{:x}", from, to); | ||
| 54 | Ok(()) | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Erase the entire partition | ||
| 58 | #[cfg(feature = "nightly")] | ||
| 59 | pub(crate) async fn wipe<F: AsyncNorFlash>(&self, flash: &mut F) -> Result<(), F::Error> { | ||
| 60 | let from = self.from as u32; | ||
| 61 | let to = self.to as u32; | ||
| 62 | flash.erase(from, to).await?; | ||
| 63 | trace!("Wiped from 0x{:x} to 0x{:x}", from, to); | ||
| 64 | Ok(()) | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Read from the partition on the provided flash | ||
| 68 | pub fn read_blocking<F: ReadNorFlash>(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> { | ||
| 69 | let offset = self.from as u32 + offset; | ||
| 70 | flash.read(offset, bytes) | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Write to the partition on the provided flash | ||
| 74 | pub fn write_blocking<F: NorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { | ||
| 75 | let offset = self.from as u32 + offset; | ||
| 76 | flash.write(offset, bytes)?; | ||
| 77 | trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); | ||
| 78 | Ok(()) | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Erase part of the partition on the provided flash | ||
| 82 | pub fn erase_blocking<F: NorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { | ||
| 83 | let from = self.from as u32 + from; | ||
| 84 | let to = self.from as u32 + to; | ||
| 85 | flash.erase(from, to)?; | ||
| 86 | trace!("Erased from 0x{:x} to 0x{:x}", from, to); | ||
| 87 | Ok(()) | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Erase the entire partition | ||
| 91 | pub(crate) fn wipe_blocking<F: NorFlash>(&self, flash: &mut F) -> Result<(), F::Error> { | ||
| 92 | let from = self.from as u32; | ||
| 93 | let to = self.to as u32; | ||
| 94 | flash.erase(from, to)?; | ||
| 95 | trace!("Wiped from 0x{:x} to 0x{:x}", from, to); | ||
| 96 | Ok(()) | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | #[cfg(test)] | ||
| 101 | mod tests { | ||
| 102 | use crate::mem_flash::MemFlash; | ||
| 103 | use crate::Partition; | ||
| 104 | |||
| 105 | #[test] | ||
| 106 | fn can_erase() { | ||
| 107 | let mut flash = MemFlash::<1024, 64, 4>::new(0x00); | ||
| 108 | let partition = Partition::new(256, 512); | ||
| 109 | |||
| 110 | partition.erase_blocking(&mut flash, 64, 192).unwrap(); | ||
| 111 | |||
| 112 | for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) { | ||
| 113 | assert_eq!(0x00, byte, "Index {}", index); | ||
| 114 | } | ||
| 115 | |||
| 116 | for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) { | ||
| 117 | assert_eq!(0xFF, byte, "Index {}", index); | ||
| 118 | } | ||
| 119 | |||
| 120 | for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) { | ||
| 121 | assert_eq!(0x00, byte, "Index {}", index); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | #[test] | ||
| 126 | fn can_wipe() { | ||
| 127 | let mut flash = MemFlash::<1024, 64, 4>::new(0x00); | ||
| 128 | let partition = Partition::new(256, 512); | ||
| 129 | |||
| 130 | partition.wipe_blocking(&mut flash).unwrap(); | ||
| 131 | |||
| 132 | for (index, byte) in flash.mem.iter().copied().enumerate().take(256) { | ||
| 133 | assert_eq!(0x00, byte, "Index {}", index); | ||
| 134 | } | ||
| 135 | |||
| 136 | for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) { | ||
| 137 | assert_eq!(0xFF, byte, "Index {}", index); | ||
| 138 | } | ||
| 139 | |||
| 140 | for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) { | ||
| 141 | assert_eq!(0x00, byte, "Index {}", index); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | } | ||
diff --git a/embassy-boot/boot/src/test_flash/asynch.rs b/embassy-boot/boot/src/test_flash/asynch.rs new file mode 100644 index 000000000..3ac9e71ab --- /dev/null +++ b/embassy-boot/boot/src/test_flash/asynch.rs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | use embassy_embedded_hal::flash::partition::Partition; | ||
| 2 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 3 | use embassy_sync::mutex::Mutex; | ||
| 4 | use embedded_storage_async::nor_flash::NorFlash; | ||
| 5 | |||
| 6 | use crate::BootLoaderConfig; | ||
| 7 | |||
| 8 | pub struct AsyncTestFlash<ACTIVE, DFU, STATE> | ||
| 9 | where | ||
| 10 | ACTIVE: NorFlash, | ||
| 11 | DFU: NorFlash, | ||
| 12 | STATE: NorFlash, | ||
| 13 | { | ||
| 14 | active: Mutex<NoopRawMutex, ACTIVE>, | ||
| 15 | dfu: Mutex<NoopRawMutex, DFU>, | ||
| 16 | state: Mutex<NoopRawMutex, STATE>, | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<ACTIVE, DFU, STATE> AsyncTestFlash<ACTIVE, DFU, STATE> | ||
| 20 | where | ||
| 21 | ACTIVE: NorFlash, | ||
| 22 | DFU: NorFlash, | ||
| 23 | STATE: NorFlash, | ||
| 24 | { | ||
| 25 | pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self { | ||
| 26 | Self { | ||
| 27 | active: Mutex::new(config.active), | ||
| 28 | dfu: Mutex::new(config.dfu), | ||
| 29 | state: Mutex::new(config.state), | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn active(&self) -> Partition<NoopRawMutex, ACTIVE> { | ||
| 34 | Self::create_partition(&self.active) | ||
| 35 | } | ||
| 36 | |||
| 37 | pub fn dfu(&self) -> Partition<NoopRawMutex, DFU> { | ||
| 38 | Self::create_partition(&self.dfu) | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn state(&self) -> Partition<NoopRawMutex, STATE> { | ||
| 42 | Self::create_partition(&self.state) | ||
| 43 | } | ||
| 44 | |||
| 45 | fn create_partition<T: NorFlash>(mutex: &Mutex<NoopRawMutex, T>) -> Partition<NoopRawMutex, T> { | ||
| 46 | Partition::new(mutex, 0, mutex.try_lock().unwrap().capacity() as u32) | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | impl<ACTIVE, DFU, STATE> AsyncTestFlash<ACTIVE, DFU, STATE> | ||
| 51 | where | ||
| 52 | ACTIVE: NorFlash + embedded_storage::nor_flash::NorFlash, | ||
| 53 | DFU: NorFlash + embedded_storage::nor_flash::NorFlash, | ||
| 54 | STATE: NorFlash + embedded_storage::nor_flash::NorFlash, | ||
| 55 | { | ||
| 56 | pub fn into_blocking(self) -> super::BlockingTestFlash<ACTIVE, DFU, STATE> { | ||
| 57 | let config = BootLoaderConfig { | ||
| 58 | active: self.active.into_inner(), | ||
| 59 | dfu: self.dfu.into_inner(), | ||
| 60 | state: self.state.into_inner(), | ||
| 61 | }; | ||
| 62 | super::BlockingTestFlash::new(config) | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/embassy-boot/boot/src/test_flash/blocking.rs b/embassy-boot/boot/src/test_flash/blocking.rs new file mode 100644 index 000000000..ba33c9208 --- /dev/null +++ b/embassy-boot/boot/src/test_flash/blocking.rs | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | |||
| 3 | use embassy_embedded_hal::flash::partition::BlockingPartition; | ||
| 4 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 5 | use embassy_sync::blocking_mutex::Mutex; | ||
| 6 | use embedded_storage::nor_flash::NorFlash; | ||
| 7 | |||
| 8 | use crate::BootLoaderConfig; | ||
| 9 | |||
| 10 | pub struct BlockingTestFlash<ACTIVE, DFU, STATE> | ||
| 11 | where | ||
| 12 | ACTIVE: NorFlash, | ||
| 13 | DFU: NorFlash, | ||
| 14 | STATE: NorFlash, | ||
| 15 | { | ||
| 16 | active: Mutex<NoopRawMutex, RefCell<ACTIVE>>, | ||
| 17 | dfu: Mutex<NoopRawMutex, RefCell<DFU>>, | ||
| 18 | state: Mutex<NoopRawMutex, RefCell<STATE>>, | ||
| 19 | } | ||
| 20 | |||
| 21 | impl<ACTIVE, DFU, STATE> BlockingTestFlash<ACTIVE, DFU, STATE> | ||
| 22 | where | ||
| 23 | ACTIVE: NorFlash, | ||
| 24 | DFU: NorFlash, | ||
| 25 | STATE: NorFlash, | ||
| 26 | { | ||
| 27 | pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self { | ||
| 28 | Self { | ||
| 29 | active: Mutex::new(RefCell::new(config.active)), | ||
| 30 | dfu: Mutex::new(RefCell::new(config.dfu)), | ||
| 31 | state: Mutex::new(RefCell::new(config.state)), | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | pub fn active(&self) -> BlockingPartition<NoopRawMutex, ACTIVE> { | ||
| 36 | Self::create_partition(&self.active) | ||
| 37 | } | ||
| 38 | |||
| 39 | pub fn dfu(&self) -> BlockingPartition<NoopRawMutex, DFU> { | ||
| 40 | Self::create_partition(&self.dfu) | ||
| 41 | } | ||
| 42 | |||
| 43 | pub fn state(&self) -> BlockingPartition<NoopRawMutex, STATE> { | ||
| 44 | Self::create_partition(&self.state) | ||
| 45 | } | ||
| 46 | |||
| 47 | pub fn create_partition<T: NorFlash>( | ||
| 48 | mutex: &Mutex<NoopRawMutex, RefCell<T>>, | ||
| 49 | ) -> BlockingPartition<NoopRawMutex, T> { | ||
| 50 | BlockingPartition::new(mutex, 0, mutex.lock(|f| f.borrow().capacity()) as u32) | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | #[cfg(feature = "nightly")] | ||
| 55 | impl<ACTIVE, DFU, STATE> BlockingTestFlash<ACTIVE, DFU, STATE> | ||
| 56 | where | ||
| 57 | ACTIVE: NorFlash + embedded_storage_async::nor_flash::NorFlash, | ||
| 58 | DFU: NorFlash + embedded_storage_async::nor_flash::NorFlash, | ||
| 59 | STATE: NorFlash + embedded_storage_async::nor_flash::NorFlash, | ||
| 60 | { | ||
| 61 | pub fn into_async(self) -> super::AsyncTestFlash<ACTIVE, DFU, STATE> { | ||
| 62 | let config = BootLoaderConfig { | ||
| 63 | active: self.active.into_inner().into_inner(), | ||
| 64 | dfu: self.dfu.into_inner().into_inner(), | ||
| 65 | state: self.state.into_inner().into_inner(), | ||
| 66 | }; | ||
| 67 | super::AsyncTestFlash::new(config) | ||
| 68 | } | ||
| 69 | } | ||
diff --git a/embassy-boot/boot/src/test_flash/mod.rs b/embassy-boot/boot/src/test_flash/mod.rs new file mode 100644 index 000000000..a0672322e --- /dev/null +++ b/embassy-boot/boot/src/test_flash/mod.rs | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #[cfg(feature = "nightly")] | ||
| 2 | mod asynch; | ||
| 3 | mod blocking; | ||
| 4 | |||
| 5 | #[cfg(feature = "nightly")] | ||
| 6 | pub(crate) use asynch::AsyncTestFlash; | ||
| 7 | pub(crate) use blocking::BlockingTestFlash; | ||
diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 710798bdb..bb702073c 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs | |||
| @@ -3,74 +3,37 @@ | |||
| 3 | #![doc = include_str!("../README.md")] | 3 | #![doc = include_str!("../README.md")] |
| 4 | mod fmt; | 4 | mod fmt; |
| 5 | 5 | ||
| 6 | pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig}; | 6 | #[cfg(feature = "nightly")] |
| 7 | pub use embassy_boot::FirmwareUpdater; | ||
| 8 | pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig}; | ||
| 7 | use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; | 9 | use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; |
| 8 | use embassy_nrf::peripherals::WDT; | 10 | use embassy_nrf::peripherals::WDT; |
| 9 | use embassy_nrf::wdt; | 11 | use embassy_nrf::wdt; |
| 10 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; | 12 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; |
| 11 | 13 | ||
| 12 | /// A bootloader for nRF devices. | 14 | /// A bootloader for nRF devices. |
| 13 | pub struct BootLoader<const BUFFER_SIZE: usize = PAGE_SIZE> { | 15 | pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = PAGE_SIZE> { |
| 14 | boot: embassy_boot::BootLoader, | 16 | boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>, |
| 15 | aligned_buf: AlignedBuffer<BUFFER_SIZE>, | 17 | aligned_buf: AlignedBuffer<BUFFER_SIZE>, |
| 16 | } | 18 | } |
| 17 | 19 | ||
| 18 | #[cfg(target_os = "none")] | 20 | impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> |
| 19 | impl Default for BootLoader<PAGE_SIZE> { | 21 | BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE> |
| 20 | /// Create a new bootloader instance using parameters from linker script | 22 | { |
| 21 | fn default() -> Self { | ||
| 22 | extern "C" { | ||
| 23 | static __bootloader_state_start: u32; | ||
| 24 | static __bootloader_state_end: u32; | ||
| 25 | static __bootloader_active_start: u32; | ||
| 26 | static __bootloader_active_end: u32; | ||
| 27 | static __bootloader_dfu_start: u32; | ||
| 28 | static __bootloader_dfu_end: u32; | ||
| 29 | } | ||
| 30 | |||
| 31 | let active = unsafe { | ||
| 32 | Partition::new( | ||
| 33 | &__bootloader_active_start as *const u32 as u32, | ||
| 34 | &__bootloader_active_end as *const u32 as u32, | ||
| 35 | ) | ||
| 36 | }; | ||
| 37 | let dfu = unsafe { | ||
| 38 | Partition::new( | ||
| 39 | &__bootloader_dfu_start as *const u32 as u32, | ||
| 40 | &__bootloader_dfu_end as *const u32 as u32, | ||
| 41 | ) | ||
| 42 | }; | ||
| 43 | let state = unsafe { | ||
| 44 | Partition::new( | ||
| 45 | &__bootloader_state_start as *const u32 as u32, | ||
| 46 | &__bootloader_state_end as *const u32 as u32, | ||
| 47 | ) | ||
| 48 | }; | ||
| 49 | |||
| 50 | trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to); | ||
| 51 | trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); | ||
| 52 | trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); | ||
| 53 | |||
| 54 | Self::new(active, dfu, state) | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> { | ||
| 59 | /// Create a new bootloader instance using the supplied partitions for active, dfu and state. | 23 | /// Create a new bootloader instance using the supplied partitions for active, dfu and state. |
| 60 | pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { | 24 | pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self { |
| 61 | Self { | 25 | Self { |
| 62 | boot: embassy_boot::BootLoader::new(active, dfu, state), | 26 | boot: embassy_boot::BootLoader::new(config), |
| 63 | aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), | 27 | aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), |
| 64 | } | 28 | } |
| 65 | } | 29 | } |
| 66 | 30 | ||
| 67 | /// Inspect the bootloader state and perform actions required before booting, such as swapping | 31 | /// Inspect the bootloader state and perform actions required before booting, such as swapping |
| 68 | /// firmware. | 32 | /// firmware. |
| 69 | pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize { | 33 | pub fn prepare(&mut self) { |
| 70 | match self.boot.prepare_boot(flash, &mut self.aligned_buf.0) { | 34 | self.boot |
| 71 | Ok(_) => self.boot.boot_address(), | 35 | .prepare_boot(&mut self.aligned_buf.0) |
| 72 | Err(_) => panic!("boot prepare error!"), | 36 | .expect("Boot prepare error"); |
| 73 | } | ||
| 74 | } | 37 | } |
| 75 | 38 | ||
| 76 | /// Boots the application without softdevice mechanisms. | 39 | /// Boots the application without softdevice mechanisms. |
| @@ -79,10 +42,12 @@ impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> { | |||
| 79 | /// | 42 | /// |
| 80 | /// This modifies the stack pointer and reset vector and will run code placed in the active partition. | 43 | /// This modifies the stack pointer and reset vector and will run code placed in the active partition. |
| 81 | #[cfg(not(feature = "softdevice"))] | 44 | #[cfg(not(feature = "softdevice"))] |
| 82 | pub unsafe fn load(&mut self, start: usize) -> ! { | 45 | pub unsafe fn load(self, start: u32) -> ! { |
| 46 | core::mem::drop(self.boot); | ||
| 47 | |||
| 83 | let mut p = cortex_m::Peripherals::steal(); | 48 | let mut p = cortex_m::Peripherals::steal(); |
| 84 | p.SCB.invalidate_icache(); | 49 | p.SCB.invalidate_icache(); |
| 85 | p.SCB.vtor.write(start as u32); | 50 | p.SCB.vtor.write(start); |
| 86 | cortex_m::asm::bootload(start as *const u32) | 51 | cortex_m::asm::bootload(start as *const u32) |
| 87 | } | 52 | } |
| 88 | 53 | ||
| @@ -92,7 +57,7 @@ impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> { | |||
| 92 | /// | 57 | /// |
| 93 | /// This modifies the stack pointer and reset vector and will run code placed in the active partition. | 58 | /// This modifies the stack pointer and reset vector and will run code placed in the active partition. |
| 94 | #[cfg(feature = "softdevice")] | 59 | #[cfg(feature = "softdevice")] |
| 95 | pub unsafe fn load(&mut self, _app: usize) -> ! { | 60 | pub unsafe fn load(&mut self, _app: u32) -> ! { |
| 96 | use nrf_softdevice_mbr as mbr; | 61 | use nrf_softdevice_mbr as mbr; |
| 97 | const NRF_SUCCESS: u32 = 0; | 62 | const NRF_SUCCESS: u32 = 0; |
| 98 | 63 | ||
diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index fb9bc3242..25329f9e9 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs | |||
| @@ -3,7 +3,9 @@ | |||
| 3 | #![doc = include_str!("../README.md")] | 3 | #![doc = include_str!("../README.md")] |
| 4 | mod fmt; | 4 | mod fmt; |
| 5 | 5 | ||
| 6 | pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; | 6 | #[cfg(feature = "nightly")] |
| 7 | pub use embassy_boot::FirmwareUpdater; | ||
| 8 | pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; | ||
| 7 | use embassy_rp::flash::{Flash, ERASE_SIZE}; | 9 | use embassy_rp::flash::{Flash, ERASE_SIZE}; |
| 8 | use embassy_rp::peripherals::{FLASH, WATCHDOG}; | 10 | use embassy_rp::peripherals::{FLASH, WATCHDOG}; |
| 9 | use embassy_rp::watchdog::Watchdog; | 11 | use embassy_rp::watchdog::Watchdog; |
| @@ -11,27 +13,28 @@ use embassy_time::Duration; | |||
| 11 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; | 13 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; |
| 12 | 14 | ||
| 13 | /// A bootloader for RP2040 devices. | 15 | /// A bootloader for RP2040 devices. |
| 14 | pub struct BootLoader<const BUFFER_SIZE: usize = ERASE_SIZE> { | 16 | pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = ERASE_SIZE> { |
| 15 | boot: embassy_boot::BootLoader, | 17 | boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>, |
| 16 | aligned_buf: AlignedBuffer<BUFFER_SIZE>, | 18 | aligned_buf: AlignedBuffer<BUFFER_SIZE>, |
| 17 | } | 19 | } |
| 18 | 20 | ||
| 19 | impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> { | 21 | impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> |
| 22 | BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE> | ||
| 23 | { | ||
| 20 | /// Create a new bootloader instance using the supplied partitions for active, dfu and state. | 24 | /// Create a new bootloader instance using the supplied partitions for active, dfu and state. |
| 21 | pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { | 25 | pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self { |
| 22 | Self { | 26 | Self { |
| 23 | boot: embassy_boot::BootLoader::new(active, dfu, state), | 27 | boot: embassy_boot::BootLoader::new(config), |
| 24 | aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), | 28 | aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), |
| 25 | } | 29 | } |
| 26 | } | 30 | } |
| 27 | 31 | ||
| 28 | /// Inspect the bootloader state and perform actions required before booting, such as swapping | 32 | /// Inspect the bootloader state and perform actions required before booting, such as swapping |
| 29 | /// firmware. | 33 | /// firmware. |
| 30 | pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize { | 34 | pub fn prepare(&mut self) { |
| 31 | match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { | 35 | self.boot |
| 32 | Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(), | 36 | .prepare_boot(self.aligned_buf.as_mut()) |
| 33 | Err(_) => panic!("boot prepare error!"), | 37 | .expect("Boot prepare error"); |
| 34 | } | ||
| 35 | } | 38 | } |
| 36 | 39 | ||
| 37 | /// Boots the application. | 40 | /// Boots the application. |
| @@ -39,58 +42,20 @@ impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> { | |||
| 39 | /// # Safety | 42 | /// # Safety |
| 40 | /// | 43 | /// |
| 41 | /// This modifies the stack pointer and reset vector and will run code placed in the active partition. | 44 | /// This modifies the stack pointer and reset vector and will run code placed in the active partition. |
| 42 | pub unsafe fn load(&mut self, start: usize) -> ! { | 45 | pub unsafe fn load(self, start: u32) -> ! { |
| 46 | core::mem::drop(self.boot); | ||
| 47 | |||
| 43 | trace!("Loading app at 0x{:x}", start); | 48 | trace!("Loading app at 0x{:x}", start); |
| 44 | #[allow(unused_mut)] | 49 | #[allow(unused_mut)] |
| 45 | let mut p = cortex_m::Peripherals::steal(); | 50 | let mut p = cortex_m::Peripherals::steal(); |
| 46 | #[cfg(not(armv6m))] | 51 | #[cfg(not(armv6m))] |
| 47 | p.SCB.invalidate_icache(); | 52 | p.SCB.invalidate_icache(); |
| 48 | p.SCB.vtor.write(start as u32); | 53 | p.SCB.vtor.write(start); |
| 49 | 54 | ||
| 50 | cortex_m::asm::bootload(start as *const u32) | 55 | cortex_m::asm::bootload(start as *const u32) |
| 51 | } | 56 | } |
| 52 | } | 57 | } |
| 53 | 58 | ||
| 54 | #[cfg(target_os = "none")] | ||
| 55 | impl Default for BootLoader<ERASE_SIZE> { | ||
| 56 | /// Create a new bootloader instance using parameters from linker script | ||
| 57 | fn default() -> Self { | ||
| 58 | extern "C" { | ||
| 59 | static __bootloader_state_start: u32; | ||
| 60 | static __bootloader_state_end: u32; | ||
| 61 | static __bootloader_active_start: u32; | ||
| 62 | static __bootloader_active_end: u32; | ||
| 63 | static __bootloader_dfu_start: u32; | ||
| 64 | static __bootloader_dfu_end: u32; | ||
| 65 | } | ||
| 66 | |||
| 67 | let active = unsafe { | ||
| 68 | Partition::new( | ||
| 69 | &__bootloader_active_start as *const u32 as u32, | ||
| 70 | &__bootloader_active_end as *const u32 as u32, | ||
| 71 | ) | ||
| 72 | }; | ||
| 73 | let dfu = unsafe { | ||
| 74 | Partition::new( | ||
| 75 | &__bootloader_dfu_start as *const u32 as u32, | ||
| 76 | &__bootloader_dfu_end as *const u32 as u32, | ||
| 77 | ) | ||
| 78 | }; | ||
| 79 | let state = unsafe { | ||
| 80 | Partition::new( | ||
| 81 | &__bootloader_state_start as *const u32 as u32, | ||
| 82 | &__bootloader_state_end as *const u32 as u32, | ||
| 83 | ) | ||
| 84 | }; | ||
| 85 | |||
| 86 | trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to); | ||
| 87 | trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); | ||
| 88 | trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); | ||
| 89 | |||
| 90 | Self::new(active, dfu, state) | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | /// A flash implementation that will feed a watchdog when touching flash. | 59 | /// A flash implementation that will feed a watchdog when touching flash. |
| 95 | pub struct WatchdogFlash<'d, const SIZE: usize> { | 60 | pub struct WatchdogFlash<'d, const SIZE: usize> { |
| 96 | flash: Flash<'d, FLASH, SIZE>, | 61 | flash: Flash<'d, FLASH, SIZE>, |
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index ccf136c74..069de0d1a 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs | |||
| @@ -3,30 +3,34 @@ | |||
| 3 | #![doc = include_str!("../README.md")] | 3 | #![doc = include_str!("../README.md")] |
| 4 | mod fmt; | 4 | mod fmt; |
| 5 | 5 | ||
| 6 | pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; | 6 | #[cfg(feature = "nightly")] |
| 7 | pub use embassy_boot::FirmwareUpdater; | ||
| 8 | pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; | ||
| 9 | use embedded_storage::nor_flash::NorFlash; | ||
| 7 | 10 | ||
| 8 | /// A bootloader for STM32 devices. | 11 | /// A bootloader for STM32 devices. |
| 9 | pub struct BootLoader<const BUFFER_SIZE: usize> { | 12 | pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> { |
| 10 | boot: embassy_boot::BootLoader, | 13 | boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>, |
| 11 | aligned_buf: AlignedBuffer<BUFFER_SIZE>, | 14 | aligned_buf: AlignedBuffer<BUFFER_SIZE>, |
| 12 | } | 15 | } |
| 13 | 16 | ||
| 14 | impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> { | 17 | impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> |
| 18 | BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE> | ||
| 19 | { | ||
| 15 | /// Create a new bootloader instance using the supplied partitions for active, dfu and state. | 20 | /// Create a new bootloader instance using the supplied partitions for active, dfu and state. |
| 16 | pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { | 21 | pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self { |
| 17 | Self { | 22 | Self { |
| 18 | boot: embassy_boot::BootLoader::new(active, dfu, state), | 23 | boot: embassy_boot::BootLoader::new(config), |
| 19 | aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), | 24 | aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), |
| 20 | } | 25 | } |
| 21 | } | 26 | } |
| 22 | 27 | ||
| 23 | /// Inspect the bootloader state and perform actions required before booting, such as swapping | 28 | /// Inspect the bootloader state and perform actions required before booting, such as swapping |
| 24 | /// firmware. | 29 | /// firmware. |
| 25 | pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize { | 30 | pub fn prepare(&mut self) { |
| 26 | match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { | 31 | self.boot |
| 27 | Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(), | 32 | .prepare_boot(self.aligned_buf.as_mut()) |
| 28 | Err(_) => panic!("boot prepare error!"), | 33 | .expect("Boot prepare error"); |
| 29 | } | ||
| 30 | } | 34 | } |
| 31 | 35 | ||
| 32 | /// Boots the application. | 36 | /// Boots the application. |
| @@ -34,54 +38,16 @@ impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> { | |||
| 34 | /// # Safety | 38 | /// # Safety |
| 35 | /// | 39 | /// |
| 36 | /// This modifies the stack pointer and reset vector and will run code placed in the active partition. | 40 | /// This modifies the stack pointer and reset vector and will run code placed in the active partition. |
| 37 | pub unsafe fn load(&mut self, start: usize) -> ! { | 41 | pub unsafe fn load(self, start: u32) -> ! { |
| 42 | core::mem::drop(self.boot); | ||
| 43 | |||
| 38 | trace!("Loading app at 0x{:x}", start); | 44 | trace!("Loading app at 0x{:x}", start); |
| 39 | #[allow(unused_mut)] | 45 | #[allow(unused_mut)] |
| 40 | let mut p = cortex_m::Peripherals::steal(); | 46 | let mut p = cortex_m::Peripherals::steal(); |
| 41 | #[cfg(not(armv6m))] | 47 | #[cfg(not(armv6m))] |
| 42 | p.SCB.invalidate_icache(); | 48 | p.SCB.invalidate_icache(); |
| 43 | p.SCB.vtor.write(start as u32); | 49 | p.SCB.vtor.write(start); |
| 44 | 50 | ||
| 45 | cortex_m::asm::bootload(start as *const u32) | 51 | cortex_m::asm::bootload(start as *const u32) |
| 46 | } | 52 | } |
| 47 | } | 53 | } |
| 48 | |||
| 49 | #[cfg(target_os = "none")] | ||
| 50 | impl<const BUFFER_SIZE: usize> Default for BootLoader<BUFFER_SIZE> { | ||
| 51 | /// Create a new bootloader instance using parameters from linker script | ||
| 52 | fn default() -> Self { | ||
| 53 | extern "C" { | ||
| 54 | static __bootloader_state_start: u32; | ||
| 55 | static __bootloader_state_end: u32; | ||
| 56 | static __bootloader_active_start: u32; | ||
| 57 | static __bootloader_active_end: u32; | ||
| 58 | static __bootloader_dfu_start: u32; | ||
| 59 | static __bootloader_dfu_end: u32; | ||
| 60 | } | ||
| 61 | |||
| 62 | let active = unsafe { | ||
| 63 | Partition::new( | ||
| 64 | &__bootloader_active_start as *const u32 as u32, | ||
| 65 | &__bootloader_active_end as *const u32 as u32, | ||
| 66 | ) | ||
| 67 | }; | ||
| 68 | let dfu = unsafe { | ||
| 69 | Partition::new( | ||
| 70 | &__bootloader_dfu_start as *const u32 as u32, | ||
| 71 | &__bootloader_dfu_end as *const u32 as u32, | ||
| 72 | ) | ||
| 73 | }; | ||
| 74 | let state = unsafe { | ||
| 75 | Partition::new( | ||
| 76 | &__bootloader_state_start as *const u32 as u32, | ||
| 77 | &__bootloader_state_end as *const u32 as u32, | ||
| 78 | ) | ||
| 79 | }; | ||
| 80 | |||
| 81 | trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to); | ||
| 82 | trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); | ||
| 83 | trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); | ||
| 84 | |||
| 85 | Self::new(active, dfu, state) | ||
| 86 | } | ||
| 87 | } | ||
diff --git a/embassy-embedded-hal/src/flash/partition/asynch.rs b/embassy-embedded-hal/src/flash/partition/asynch.rs index 141e0d9fc..5920436dd 100644 --- a/embassy-embedded-hal/src/flash/partition/asynch.rs +++ b/embassy-embedded-hal/src/flash/partition/asynch.rs | |||
| @@ -30,6 +30,16 @@ impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> { | |||
| 30 | } | 30 | } |
| 31 | Self { flash, offset, size } | 31 | Self { flash, offset, size } |
| 32 | } | 32 | } |
| 33 | |||
| 34 | /// Get the partition offset within the flash | ||
| 35 | pub const fn offset(&self) -> u32 { | ||
| 36 | self.offset | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Get the partition size | ||
| 40 | pub const fn size(&self) -> u32 { | ||
| 41 | self.size | ||
| 42 | } | ||
| 33 | } | 43 | } |
| 34 | 44 | ||
| 35 | impl<M: RawMutex, T: NorFlash> ErrorType for Partition<'_, M, T> { | 45 | impl<M: RawMutex, T: NorFlash> ErrorType for Partition<'_, M, T> { |
diff --git a/embassy-embedded-hal/src/flash/partition/blocking.rs b/embassy-embedded-hal/src/flash/partition/blocking.rs index dc52e292a..2ddbe3de0 100644 --- a/embassy-embedded-hal/src/flash/partition/blocking.rs +++ b/embassy-embedded-hal/src/flash/partition/blocking.rs | |||
| @@ -31,6 +31,16 @@ impl<'a, M: RawMutex, T: NorFlash> BlockingPartition<'a, M, T> { | |||
| 31 | } | 31 | } |
| 32 | Self { flash, offset, size } | 32 | Self { flash, offset, size } |
| 33 | } | 33 | } |
| 34 | |||
| 35 | /// Get the partition offset within the flash | ||
| 36 | pub const fn offset(&self) -> u32 { | ||
| 37 | self.offset | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Get the partition size | ||
| 41 | pub const fn size(&self) -> u32 { | ||
| 42 | self.size | ||
| 43 | } | ||
| 34 | } | 44 | } |
| 35 | 45 | ||
| 36 | impl<M: RawMutex, T: NorFlash> ErrorType for BlockingPartition<'_, M, T> { | 46 | impl<M: RawMutex, T: NorFlash> ErrorType for BlockingPartition<'_, M, T> { |
diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 090a05b23..06c237781 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs | |||
| @@ -3,12 +3,13 @@ | |||
| 3 | #![macro_use] | 3 | #![macro_use] |
| 4 | #![feature(type_alias_impl_trait)] | 4 | #![feature(type_alias_impl_trait)] |
| 5 | 5 | ||
| 6 | use embassy_boot_nrf::FirmwareUpdater; | 6 | use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; |
| 7 | use embassy_embedded_hal::adapter::BlockingAsync; | 7 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; | 9 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; |
| 10 | use embassy_nrf::nvmc::Nvmc; | 10 | use embassy_nrf::nvmc::Nvmc; |
| 11 | use embassy_nrf::wdt::{self, Watchdog}; | 11 | use embassy_nrf::wdt::{self, Watchdog}; |
| 12 | use embassy_sync::mutex::Mutex; | ||
| 12 | use panic_reset as _; | 13 | use panic_reset as _; |
| 13 | 14 | ||
| 14 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 15 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| @@ -45,9 +46,10 @@ async fn main(_spawner: Spawner) { | |||
| 45 | }; | 46 | }; |
| 46 | 47 | ||
| 47 | let nvmc = Nvmc::new(p.NVMC); | 48 | let nvmc = Nvmc::new(p.NVMC); |
| 48 | let mut nvmc = BlockingAsync::new(nvmc); | 49 | let nvmc = Mutex::new(BlockingAsync::new(nvmc)); |
| 49 | 50 | ||
| 50 | let mut updater = FirmwareUpdater::default(); | 51 | let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc); |
| 52 | let mut updater = FirmwareUpdater::new(config); | ||
| 51 | loop { | 53 | loop { |
| 52 | led.set_low(); | 54 | led.set_low(); |
| 53 | button.wait_for_any_edge().await; | 55 | button.wait_for_any_edge().await; |
| @@ -56,11 +58,11 @@ async fn main(_spawner: Spawner) { | |||
| 56 | for chunk in APP_B.chunks(4096) { | 58 | for chunk in APP_B.chunks(4096) { |
| 57 | let mut buf: [u8; 4096] = [0; 4096]; | 59 | let mut buf: [u8; 4096] = [0; 4096]; |
| 58 | buf[..chunk.len()].copy_from_slice(chunk); | 60 | buf[..chunk.len()].copy_from_slice(chunk); |
| 59 | updater.write_firmware(offset, &buf, &mut nvmc, 4096).await.unwrap(); | 61 | updater.write_firmware(offset, &buf).await.unwrap(); |
| 60 | offset += chunk.len(); | 62 | offset += chunk.len(); |
| 61 | } | 63 | } |
| 62 | let mut magic = [0; 4]; | 64 | let mut magic = [0; 4]; |
| 63 | updater.mark_updated(&mut nvmc, &mut magic).await.unwrap(); | 65 | updater.mark_updated(&mut magic).await.unwrap(); |
| 64 | led.set_high(); | 66 | led.set_high(); |
| 65 | cortex_m::peripheral::SCB::sys_reset(); | 67 | cortex_m::peripheral::SCB::sys_reset(); |
| 66 | } | 68 | } |
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 64c2b8925..4a2c5dd8f 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml | |||
| @@ -20,6 +20,7 @@ embedded-hal = { version = "0.2.6" } | |||
| 20 | 20 | ||
| 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 23 | embedded-storage = "0.3.0" | ||
| 23 | 24 | ||
| 24 | [features] | 25 | [features] |
| 25 | default = ["panic-reset"] | 26 | default = ["panic-reset"] |
diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 47f1d16d8..69850069b 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs | |||
| @@ -2,13 +2,17 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use core::cell::RefCell; | ||
| 6 | |||
| 5 | use defmt_rtt as _; | 7 | use defmt_rtt as _; |
| 6 | use embassy_boot_rp::*; | 8 | use embassy_boot_rp::*; |
| 7 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::flash::Flash; | 10 | use embassy_rp::flash::Flash; |
| 9 | use embassy_rp::gpio::{Level, Output}; | 11 | use embassy_rp::gpio::{Level, Output}; |
| 10 | use embassy_rp::watchdog::Watchdog; | 12 | use embassy_rp::watchdog::Watchdog; |
| 13 | use embassy_sync::blocking_mutex::Mutex; | ||
| 11 | use embassy_time::{Duration, Timer}; | 14 | use embassy_time::{Duration, Timer}; |
| 15 | use embedded_storage::nor_flash::NorFlash; | ||
| 12 | #[cfg(feature = "panic-probe")] | 16 | #[cfg(feature = "panic-probe")] |
| 13 | use panic_probe as _; | 17 | use panic_probe as _; |
| 14 | #[cfg(feature = "panic-reset")] | 18 | #[cfg(feature = "panic-reset")] |
| @@ -26,9 +30,11 @@ async fn main(_s: Spawner) { | |||
| 26 | let mut watchdog = Watchdog::new(p.WATCHDOG); | 30 | let mut watchdog = Watchdog::new(p.WATCHDOG); |
| 27 | watchdog.start(Duration::from_secs(8)); | 31 | watchdog.start(Duration::from_secs(8)); |
| 28 | 32 | ||
| 29 | let mut flash: Flash<_, FLASH_SIZE> = Flash::new_blocking(p.FLASH); | 33 | let flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); |
| 34 | let flash = Mutex::new(RefCell::new(flash)); | ||
| 30 | 35 | ||
| 31 | let mut updater = FirmwareUpdater::default(); | 36 | let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); |
| 37 | let mut updater = BlockingFirmwareUpdater::new(config); | ||
| 32 | 38 | ||
| 33 | Timer::after(Duration::from_secs(5)).await; | 39 | Timer::after(Duration::from_secs(5)).await; |
| 34 | watchdog.feed(); | 40 | watchdog.feed(); |
| @@ -36,22 +42,20 @@ async fn main(_s: Spawner) { | |||
| 36 | let mut offset = 0; | 42 | let mut offset = 0; |
| 37 | let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); | 43 | let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); |
| 38 | defmt::info!("preparing update"); | 44 | defmt::info!("preparing update"); |
| 39 | let mut writer = updater | 45 | let writer = updater |
| 40 | .prepare_update_blocking(&mut flash) | 46 | .prepare_update() |
| 41 | .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) | 47 | .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) |
| 42 | .unwrap(); | 48 | .unwrap(); |
| 43 | defmt::info!("writer created, starting write"); | 49 | defmt::info!("writer created, starting write"); |
| 44 | for chunk in APP_B.chunks(4096) { | 50 | for chunk in APP_B.chunks(4096) { |
| 45 | buf.0[..chunk.len()].copy_from_slice(chunk); | 51 | buf.0[..chunk.len()].copy_from_slice(chunk); |
| 46 | defmt::info!("writing block at offset {}", offset); | 52 | defmt::info!("writing block at offset {}", offset); |
| 47 | writer | 53 | writer.write(offset, &buf.0[..]).unwrap(); |
| 48 | .write_block_blocking(offset, &buf.0[..], &mut flash, 256) | 54 | offset += chunk.len() as u32; |
| 49 | .unwrap(); | ||
| 50 | offset += chunk.len(); | ||
| 51 | } | 55 | } |
| 52 | watchdog.feed(); | 56 | watchdog.feed(); |
| 53 | defmt::info!("firmware written, marking update"); | 57 | defmt::info!("firmware written, marking update"); |
| 54 | updater.mark_updated_blocking(&mut flash, &mut buf.0[..1]).unwrap(); | 58 | updater.mark_updated(&mut buf.0[..1]).unwrap(); |
| 55 | Timer::after(Duration::from_secs(2)).await; | 59 | Timer::after(Duration::from_secs(2)).await; |
| 56 | led.set_low(); | 60 | led.set_low(); |
| 57 | defmt::info!("update marked, resetting"); | 61 | defmt::info!("update marked, resetting"); |
diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 5db1dbb57..c94676f09 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs | |||
| @@ -4,12 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 6 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; | 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | 8 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::exti::ExtiInput; | 10 | use embassy_stm32::exti::ExtiInput; |
| 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; | 11 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 12 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| 13 | use embassy_sync::mutex::Mutex; | ||
| 13 | use panic_reset as _; | 14 | use panic_reset as _; |
| 14 | 15 | ||
| 15 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 16 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| @@ -18,7 +19,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); | |||
| 18 | async fn main(_spawner: Spawner) { | 19 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_stm32::init(Default::default()); | 20 | let p = embassy_stm32::init(Default::default()); |
| 20 | let flash = Flash::new_blocking(p.FLASH); | 21 | let flash = Flash::new_blocking(p.FLASH); |
| 21 | let mut flash = BlockingAsync::new(flash); | 22 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 22 | 23 | ||
| 23 | let button = Input::new(p.PC13, Pull::Up); | 24 | let button = Input::new(p.PC13, Pull::Up); |
| 24 | let mut button = ExtiInput::new(button, p.EXTI13); | 25 | let mut button = ExtiInput::new(button, p.EXTI13); |
| @@ -26,17 +27,18 @@ async fn main(_spawner: Spawner) { | |||
| 26 | let mut led = Output::new(p.PA5, Level::Low, Speed::Low); | 27 | let mut led = Output::new(p.PA5, Level::Low, Speed::Low); |
| 27 | led.set_high(); | 28 | led.set_high(); |
| 28 | 29 | ||
| 29 | let mut updater = FirmwareUpdater::default(); | 30 | let config = FirmwareUpdaterConfig::from_linkerfile(&flash); |
| 31 | let mut updater = FirmwareUpdater::new(config); | ||
| 30 | button.wait_for_falling_edge().await; | 32 | button.wait_for_falling_edge().await; |
| 31 | let mut offset = 0; | 33 | let mut offset = 0; |
| 32 | for chunk in APP_B.chunks(2048) { | 34 | for chunk in APP_B.chunks(2048) { |
| 33 | let mut buf: [u8; 2048] = [0; 2048]; | 35 | let mut buf: [u8; 2048] = [0; 2048]; |
| 34 | buf[..chunk.len()].copy_from_slice(chunk); | 36 | buf[..chunk.len()].copy_from_slice(chunk); |
| 35 | updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); | 37 | updater.write_firmware(offset, &buf).await.unwrap(); |
| 36 | offset += chunk.len(); | 38 | offset += chunk.len(); |
| 37 | } | 39 | } |
| 38 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | 40 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); |
| 39 | updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); | 41 | updater.mark_updated(magic.as_mut()).await.unwrap(); |
| 40 | led.set_low(); | 42 | led.set_low(); |
| 41 | cortex_m::peripheral::SCB::sys_reset(); | 43 | cortex_m::peripheral::SCB::sys_reset(); |
| 42 | } | 44 | } |
diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 5d586445c..fc2702c91 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 6 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; | 7 | use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::exti::ExtiInput; | 9 | use embassy_stm32::exti::ExtiInput; |
| 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; | 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| @@ -16,7 +16,8 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); | |||
| 16 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 17 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 18 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 19 | let mut flash = Flash::new_blocking(p.FLASH); | 19 | let flash = Flash::new_blocking(p.FLASH); |
| 20 | let flash = Mutex::new(RefCell::new(flash)); | ||
| 20 | 21 | ||
| 21 | let button = Input::new(p.PC13, Pull::Down); | 22 | let button = Input::new(p.PC13, Pull::Down); |
| 22 | let mut button = ExtiInput::new(button, p.EXTI13); | 23 | let mut button = ExtiInput::new(button, p.EXTI13); |
| @@ -24,20 +25,19 @@ async fn main(_spawner: Spawner) { | |||
| 24 | let mut led = Output::new(p.PB7, Level::Low, Speed::Low); | 25 | let mut led = Output::new(p.PB7, Level::Low, Speed::Low); |
| 25 | led.set_high(); | 26 | led.set_high(); |
| 26 | 27 | ||
| 27 | let mut updater = FirmwareUpdater::default(); | 28 | let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); |
| 28 | let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); | 29 | let mut updater = BlockingFirmwareUpdater::new(config); |
| 30 | let mut writer = updater.prepare_update().unwrap(); | ||
| 29 | button.wait_for_rising_edge().await; | 31 | button.wait_for_rising_edge().await; |
| 30 | let mut offset = 0; | 32 | let mut offset = 0; |
| 31 | let mut buf = AlignedBuffer([0; 4096]); | 33 | let mut buf = AlignedBuffer([0; 4096]); |
| 32 | for chunk in APP_B.chunks(4096) { | 34 | for chunk in APP_B.chunks(4096) { |
| 33 | buf.as_mut()[..chunk.len()].copy_from_slice(chunk); | 35 | buf.as_mut()[..chunk.len()].copy_from_slice(chunk); |
| 34 | writer | 36 | writer.write(offset, buf.as_ref()).unwrap(); |
| 35 | .write_block_blocking(offset, buf.as_ref(), &mut flash, chunk.len()) | ||
| 36 | .unwrap(); | ||
| 37 | offset += chunk.len(); | 37 | offset += chunk.len(); |
| 38 | } | 38 | } |
| 39 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | 39 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); |
| 40 | updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); | 40 | updater.mark_updated(magic.as_mut()).unwrap(); |
| 41 | led.set_low(); | 41 | led.set_low(); |
| 42 | cortex_m::peripheral::SCB::sys_reset(); | 42 | cortex_m::peripheral::SCB::sys_reset(); |
| 43 | } | 43 | } |
diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 202220223..1a54464d0 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 6 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; | 7 | use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::exti::ExtiInput; | 9 | use embassy_stm32::exti::ExtiInput; |
| 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; | 10 | use embassy_stm32::flash::{Flash, WRITE_SIZE}; |
| @@ -16,7 +16,8 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); | |||
| 16 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 17 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 18 | let p = embassy_stm32::init(Default::default()); | 18 | let p = embassy_stm32::init(Default::default()); |
| 19 | let mut flash = Flash::new_blocking(p.FLASH); | 19 | let flash = Flash::new_blocking(p.FLASH); |
| 20 | let flash = Mutex::new(RefCell::new(flash)); | ||
| 20 | 21 | ||
| 21 | let button = Input::new(p.PC13, Pull::Down); | 22 | let button = Input::new(p.PC13, Pull::Down); |
| 22 | let mut button = ExtiInput::new(button, p.EXTI13); | 23 | let mut button = ExtiInput::new(button, p.EXTI13); |
| @@ -24,21 +25,19 @@ async fn main(_spawner: Spawner) { | |||
| 24 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); | 25 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); |
| 25 | led.set_high(); | 26 | led.set_high(); |
| 26 | 27 | ||
| 27 | let mut updater = FirmwareUpdater::default(); | 28 | let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); |
| 28 | 29 | let mut updater = BlockingFirmwareUpdater::new(config); | |
| 29 | let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); | 30 | let mut writer = updater.prepare_update().unwrap(); |
| 30 | button.wait_for_rising_edge().await; | 31 | button.wait_for_rising_edge().await; |
| 31 | let mut offset = 0; | 32 | let mut offset = 0; |
| 32 | let mut buf = AlignedBuffer([0; 4096]); | 33 | let mut buf = AlignedBuffer([0; 4096]); |
| 33 | for chunk in APP_B.chunks(4096) { | 34 | for chunk in APP_B.chunks(4096) { |
| 34 | buf.as_mut()[..chunk.len()].copy_from_slice(chunk); | 35 | buf.as_mut()[..chunk.len()].copy_from_slice(chunk); |
| 35 | writer | 36 | writer.write(offset, buf.as_ref()).unwrap(); |
| 36 | .write_block_blocking(offset, buf.as_ref(), &mut flash, 4096) | ||
| 37 | .unwrap(); | ||
| 38 | offset += chunk.len(); | 37 | offset += chunk.len(); |
| 39 | } | 38 | } |
| 40 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | 39 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); |
| 41 | updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); | 40 | updater.mark_updated(magic.as_mut()).unwrap(); |
| 42 | led.set_low(); | 41 | led.set_low(); |
| 43 | cortex_m::peripheral::SCB::sys_reset(); | 42 | cortex_m::peripheral::SCB::sys_reset(); |
| 44 | } | 43 | } |
diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index 4033ac590..00ddda636 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 6 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; | 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | 8 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::exti::ExtiInput; | 10 | use embassy_stm32::exti::ExtiInput; |
| @@ -19,7 +19,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); | |||
| 19 | async fn main(_spawner: Spawner) { | 19 | async fn main(_spawner: Spawner) { |
| 20 | let p = embassy_stm32::init(Default::default()); | 20 | let p = embassy_stm32::init(Default::default()); |
| 21 | let flash = Flash::new_blocking(p.FLASH); | 21 | let flash = Flash::new_blocking(p.FLASH); |
| 22 | let mut flash = BlockingAsync::new(flash); | 22 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 23 | 23 | ||
| 24 | let button = Input::new(p.PB2, Pull::Up); | 24 | let button = Input::new(p.PB2, Pull::Up); |
| 25 | let mut button = ExtiInput::new(button, p.EXTI2); | 25 | let mut button = ExtiInput::new(button, p.EXTI2); |
| @@ -28,18 +28,19 @@ async fn main(_spawner: Spawner) { | |||
| 28 | 28 | ||
| 29 | led.set_high(); | 29 | led.set_high(); |
| 30 | 30 | ||
| 31 | let mut updater = FirmwareUpdater::default(); | 31 | let config = FirmwareUpdaterConfig::from_linkerfile(&flash); |
| 32 | let mut updater = FirmwareUpdater::new(config); | ||
| 32 | button.wait_for_falling_edge().await; | 33 | button.wait_for_falling_edge().await; |
| 33 | let mut offset = 0; | 34 | let mut offset = 0; |
| 34 | for chunk in APP_B.chunks(128) { | 35 | for chunk in APP_B.chunks(128) { |
| 35 | let mut buf: [u8; 128] = [0; 128]; | 36 | let mut buf: [u8; 128] = [0; 128]; |
| 36 | buf[..chunk.len()].copy_from_slice(chunk); | 37 | buf[..chunk.len()].copy_from_slice(chunk); |
| 37 | updater.write_firmware(offset, &buf, &mut flash, 128).await.unwrap(); | 38 | updater.write_firmware(offset, &buf).await.unwrap(); |
| 38 | offset += chunk.len(); | 39 | offset += chunk.len(); |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | 42 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); |
| 42 | updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); | 43 | updater.mark_updated(magic.as_mut()).await.unwrap(); |
| 43 | led.set_low(); | 44 | led.set_low(); |
| 44 | Timer::after(Duration::from_secs(1)).await; | 45 | Timer::after(Duration::from_secs(1)).await; |
| 45 | cortex_m::peripheral::SCB::sys_reset(); | 46 | cortex_m::peripheral::SCB::sys_reset(); |
diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 141d82afd..54579e4ac 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt-rtt")] | 5 | #[cfg(feature = "defmt-rtt")] |
| 6 | use defmt_rtt::*; | 6 | use defmt_rtt::*; |
| 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; | 7 | use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; |
| 8 | use embassy_embedded_hal::adapter::BlockingAsync; | 8 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::exti::ExtiInput; | 10 | use embassy_stm32::exti::ExtiInput; |
| @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); | |||
| 18 | async fn main(_spawner: Spawner) { | 18 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_stm32::init(Default::default()); | 19 | let p = embassy_stm32::init(Default::default()); |
| 20 | let flash = Flash::new_blocking(p.FLASH); | 20 | let flash = Flash::new_blocking(p.FLASH); |
| 21 | let mut flash = BlockingAsync::new(flash); | 21 | let flash = Mutex::new(BlockingAsync::new(flash)); |
| 22 | 22 | ||
| 23 | let button = Input::new(p.PC13, Pull::Up); | 23 | let button = Input::new(p.PC13, Pull::Up); |
| 24 | let mut button = ExtiInput::new(button, p.EXTI13); | 24 | let mut button = ExtiInput::new(button, p.EXTI13); |
| @@ -26,13 +26,14 @@ async fn main(_spawner: Spawner) { | |||
| 26 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); | 26 | let mut led = Output::new(p.PB14, Level::Low, Speed::Low); |
| 27 | led.set_high(); | 27 | led.set_high(); |
| 28 | 28 | ||
| 29 | let mut updater = FirmwareUpdater::default(); | 29 | let config = FirmwareUpdaterConfig::from_linkerfile(&flash); |
| 30 | let mut updater = FirmwareUpdater::new(config); | ||
| 30 | button.wait_for_falling_edge().await; | 31 | button.wait_for_falling_edge().await; |
| 31 | let mut offset = 0; | 32 | let mut offset = 0; |
| 32 | for chunk in APP_B.chunks(2048) { | 33 | for chunk in APP_B.chunks(2048) { |
| 33 | let mut buf: [u8; 2048] = [0; 2048]; | 34 | let mut buf: [u8; 2048] = [0; 2048]; |
| 34 | buf[..chunk.len()].copy_from_slice(chunk); | 35 | buf[..chunk.len()].copy_from_slice(chunk); |
| 35 | updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); | 36 | updater.write_firmware(offset, &buf).await.unwrap(); |
| 36 | offset += chunk.len(); | 37 | offset += chunk.len(); |
| 37 | } | 38 | } |
| 38 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | 39 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); |
diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 5f48dbe51..0c6fa05f9 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs | |||
| @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); | |||
| 18 | async fn main(_spawner: Spawner) { | 18 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_stm32::init(Default::default()); | 19 | let p = embassy_stm32::init(Default::default()); |
| 20 | let flash = Flash::new_blocking(p.FLASH); | 20 | let flash = Flash::new_blocking(p.FLASH); |
| 21 | let mut flash = BlockingAsync::new(flash); | 21 | let mut flash = Mutex::new(BlockingAsync::new(flash)); |
| 22 | 22 | ||
| 23 | let button = Input::new(p.PA0, Pull::Up); | 23 | let button = Input::new(p.PA0, Pull::Up); |
| 24 | let mut button = ExtiInput::new(button, p.EXTI0); | 24 | let mut button = ExtiInput::new(button, p.EXTI0); |
| @@ -26,7 +26,8 @@ async fn main(_spawner: Spawner) { | |||
| 26 | let mut led = Output::new(p.PB9, Level::Low, Speed::Low); | 26 | let mut led = Output::new(p.PB9, Level::Low, Speed::Low); |
| 27 | led.set_high(); | 27 | led.set_high(); |
| 28 | 28 | ||
| 29 | let mut updater = FirmwareUpdater::default(); | 29 | let config = FirmwareUpdaterConfig::from_linkerfile(&flash); |
| 30 | let mut updater = FirmwareUpdater::new(config); | ||
| 30 | button.wait_for_falling_edge().await; | 31 | button.wait_for_falling_edge().await; |
| 31 | //defmt::info!("Starting update"); | 32 | //defmt::info!("Starting update"); |
| 32 | let mut offset = 0; | 33 | let mut offset = 0; |
| @@ -34,11 +35,11 @@ async fn main(_spawner: Spawner) { | |||
| 34 | let mut buf: [u8; 2048] = [0; 2048]; | 35 | let mut buf: [u8; 2048] = [0; 2048]; |
| 35 | buf[..chunk.len()].copy_from_slice(chunk); | 36 | buf[..chunk.len()].copy_from_slice(chunk); |
| 36 | // defmt::info!("Writing chunk at 0x{:x}", offset); | 37 | // defmt::info!("Writing chunk at 0x{:x}", offset); |
| 37 | updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); | 38 | updater.write_firmware(offset, &buf).await.unwrap(); |
| 38 | offset += chunk.len(); | 39 | offset += chunk.len(); |
| 39 | } | 40 | } |
| 40 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); | 41 | let mut magic = AlignedBuffer([0; WRITE_SIZE]); |
| 41 | updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); | 42 | updater.mark_updated(magic.as_mut()).await.unwrap(); |
| 42 | //defmt::info!("Marked as updated"); | 43 | //defmt::info!("Marked as updated"); |
| 43 | led.set_low(); | 44 | led.set_low(); |
| 44 | cortex_m::peripheral::SCB::sys_reset(); | 45 | cortex_m::peripheral::SCB::sys_reset(); |
diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 8c2fb4c5f..40656f359 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml | |||
| @@ -12,6 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } | |||
| 12 | embassy-nrf = { path = "../../../../embassy-nrf", features = ["nightly"] } | 12 | embassy-nrf = { path = "../../../../embassy-nrf", features = ["nightly"] } |
| 13 | embassy-boot-nrf = { path = "../../../../embassy-boot/nrf" } | 13 | embassy-boot-nrf = { path = "../../../../embassy-boot/nrf" } |
| 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 15 | embassy-sync = { path = "../../../../embassy-sync" } | ||
| 15 | cortex-m-rt = { version = "0.7" } | 16 | cortex-m-rt = { version = "0.7" } |
| 16 | cfg-if = "1.0.0" | 17 | cfg-if = "1.0.0" |
| 17 | 18 | ||
diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 8818a23b8..72c95c02a 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs | |||
| @@ -1,12 +1,15 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::cell::RefCell; | ||
| 5 | |||
| 4 | use cortex_m_rt::{entry, exception}; | 6 | use cortex_m_rt::{entry, exception}; |
| 5 | #[cfg(feature = "defmt")] | 7 | #[cfg(feature = "defmt")] |
| 6 | use defmt_rtt as _; | 8 | use defmt_rtt as _; |
| 7 | use embassy_boot_nrf::*; | 9 | use embassy_boot_nrf::*; |
| 8 | use embassy_nrf::nvmc::Nvmc; | 10 | use embassy_nrf::nvmc::Nvmc; |
| 9 | use embassy_nrf::wdt; | 11 | use embassy_nrf::wdt; |
| 12 | use embassy_sync::blocking_mutex::Mutex; | ||
| 10 | 13 | ||
| 11 | #[entry] | 14 | #[entry] |
| 12 | fn main() -> ! { | 15 | fn main() -> ! { |
| @@ -20,19 +23,21 @@ fn main() -> ! { | |||
| 20 | } | 23 | } |
| 21 | */ | 24 | */ |
| 22 | 25 | ||
| 23 | let mut bl = BootLoader::default(); | ||
| 24 | |||
| 25 | let mut wdt_config = wdt::Config::default(); | 26 | let mut wdt_config = wdt::Config::default(); |
| 26 | wdt_config.timeout_ticks = 32768 * 5; // timeout seconds | 27 | wdt_config.timeout_ticks = 32768 * 5; // timeout seconds |
| 27 | wdt_config.run_during_sleep = true; | 28 | wdt_config.run_during_sleep = true; |
| 28 | wdt_config.run_during_debug_halt = false; | 29 | wdt_config.run_during_debug_halt = false; |
| 29 | 30 | ||
| 30 | let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::new(WatchdogFlash::start( | 31 | let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config); |
| 31 | Nvmc::new(p.NVMC), | 32 | let flash = Mutex::new(RefCell::new(flash)); |
| 32 | p.WDT, | 33 | |
| 33 | wdt_config, | 34 | let config = BootLoaderConfig::from_linkerfile_blocking(&flash); |
| 34 | )))); | 35 | let active_offset = config.active.offset(); |
| 35 | unsafe { bl.load(start) } | 36 | let mut bl: BootLoader<_, _, _> = BootLoader::new(config); |
| 37 | |||
| 38 | bl.prepare(); | ||
| 39 | |||
| 40 | unsafe { bl.load(active_offset) } | ||
| 36 | } | 41 | } |
| 37 | 42 | ||
| 38 | #[no_mangle] | 43 | #[no_mangle] |
diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index bf9226993..8d60f18be 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml | |||
| @@ -11,6 +11,7 @@ defmt-rtt = { version = "0.4", optional = true } | |||
| 11 | 11 | ||
| 12 | embassy-rp = { path = "../../../../embassy-rp", features = ["nightly"] } | 12 | embassy-rp = { path = "../../../../embassy-rp", features = ["nightly"] } |
| 13 | embassy-boot-rp = { path = "../../../../embassy-boot/rp" } | 13 | embassy-boot-rp = { path = "../../../../embassy-boot/rp" } |
| 14 | embassy-sync = { path = "../../../../embassy-sync" } | ||
| 14 | embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } | 15 | embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } |
| 15 | 16 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 17 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 8129591fa..6a81db804 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs | |||
| @@ -1,10 +1,13 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::cell::RefCell; | ||
| 5 | |||
| 4 | use cortex_m_rt::{entry, exception}; | 6 | use cortex_m_rt::{entry, exception}; |
| 5 | #[cfg(feature = "defmt")] | 7 | #[cfg(feature = "defmt")] |
| 6 | use defmt_rtt as _; | 8 | use defmt_rtt as _; |
| 7 | use embassy_boot_rp::*; | 9 | use embassy_boot_rp::*; |
| 10 | use embassy_sync::blocking_mutex::Mutex; | ||
| 8 | use embassy_time::Duration; | 11 | use embassy_time::Duration; |
| 9 | 12 | ||
| 10 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | 13 | const FLASH_SIZE: usize = 2 * 1024 * 1024; |
| @@ -21,13 +24,16 @@ fn main() -> ! { | |||
| 21 | } | 24 | } |
| 22 | */ | 25 | */ |
| 23 | 26 | ||
| 24 | let mut bl: BootLoader = BootLoader::default(); | ||
| 25 | let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); | 27 | let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); |
| 26 | let mut flash = BootFlash::new(flash); | 28 | let flash = Mutex::new(RefCell::new(flash)); |
| 27 | let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); | 29 | |
| 28 | core::mem::drop(flash); | 30 | let config = BootLoaderConfig::from_linkerfile_blocking(&flash); |
| 31 | let active_offset = config.active.offset(); | ||
| 32 | let mut bl: BootLoader<_, _, _> = BootLoader::new(config); | ||
| 33 | |||
| 34 | bl.prepare(); | ||
| 29 | 35 | ||
| 30 | unsafe { bl.load(start) } | 36 | unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) } |
| 31 | } | 37 | } |
| 32 | 38 | ||
| 33 | #[no_mangle] | 39 | #[no_mangle] |
diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index fbc80b34c..6436f2fee 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml | |||
| @@ -12,6 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } | |||
| 12 | embassy-stm32 = { path = "../../../../embassy-stm32", features = ["nightly"] } | 12 | embassy-stm32 = { path = "../../../../embassy-stm32", features = ["nightly"] } |
| 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32" } | 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32" } |
| 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 15 | embassy-sync = { path = "../../../../embassy-sync" } | ||
| 15 | cortex-m-rt = { version = "0.7" } | 16 | cortex-m-rt = { version = "0.7" } |
| 16 | embedded-storage = "0.3.0" | 17 | embedded-storage = "0.3.0" |
| 17 | embedded-storage-async = "0.4.0" | 18 | embedded-storage-async = "0.4.0" |
diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index f81fdbc5f..262eed200 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs | |||
| @@ -1,11 +1,14 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::cell::RefCell; | ||
| 5 | |||
| 4 | use cortex_m_rt::{entry, exception}; | 6 | use cortex_m_rt::{entry, exception}; |
| 5 | #[cfg(feature = "defmt")] | 7 | #[cfg(feature = "defmt")] |
| 6 | use defmt_rtt as _; | 8 | use defmt_rtt as _; |
| 7 | use embassy_boot_stm32::*; | 9 | use embassy_boot_stm32::*; |
| 8 | use embassy_stm32::flash::Flash; | 10 | use embassy_stm32::flash::{Flash, BANK1_REGION}; |
| 11 | use embassy_sync::blocking_mutex::Mutex; | ||
| 9 | 12 | ||
| 10 | #[entry] | 13 | #[entry] |
| 11 | fn main() -> ! { | 14 | fn main() -> ! { |
| @@ -19,12 +22,16 @@ fn main() -> ! { | |||
| 19 | } | 22 | } |
| 20 | */ | 23 | */ |
| 21 | 24 | ||
| 22 | let mut bl: BootLoader<2048> = BootLoader::default(); | ||
| 23 | let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); | 25 | let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); |
| 24 | let mut flash = BootFlash::new(layout.bank1_region); | 26 | let flash = Mutex::new(RefCell::new(layout.bank1_region)); |
| 25 | let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); | 27 | |
| 26 | core::mem::drop(flash); | 28 | let config = BootLoaderConfig::from_linkerfile_blocking(&flash); |
| 27 | unsafe { bl.load(start) } | 29 | let active_offset = config.active.offset(); |
| 30 | let mut bl: BootLoader<_, _, _, 2048> = BootLoader::new(config); | ||
| 31 | |||
| 32 | bl.prepare(); | ||
| 33 | |||
| 34 | unsafe { bl.load(BANK1_REGION.base + active_offset) } | ||
| 28 | } | 35 | } |
| 29 | 36 | ||
| 30 | #[no_mangle] | 37 | #[no_mangle] |
