diff options
| author | Badr Bouslikhin <[email protected]> | 2024-02-11 20:02:28 +0100 |
|---|---|---|
| committer | Badr Bouslikhin <[email protected]> | 2024-02-11 20:16:17 +0100 |
| commit | eb3bd39b068ae34892520ec38f111704ea357355 (patch) | |
| tree | 7014d70ee32db54af4b229c6edbf3818080064c0 /embassy-boot/src | |
| parent | 72ab04c45335ceb725076a38a252749e781a8cf6 (diff) | |
feat(boot): enhance firmware write functionality
Diffstat (limited to 'embassy-boot/src')
| -rw-r--r-- | embassy-boot/src/firmware_updater/asynch.rs | 71 | ||||
| -rw-r--r-- | embassy-boot/src/firmware_updater/blocking.rs | 70 |
2 files changed, 124 insertions, 17 deletions
diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index 668f16f16..99a3aa246 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs | |||
| @@ -172,21 +172,69 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 172 | self.state.mark_booted().await | 172 | self.state.mark_booted().await |
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | /// Write data to a flash page. | 175 | /// Writes firmware data to the device. |
| 176 | /// | 176 | /// |
| 177 | /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. | 177 | /// This function writes the given data to the firmware area starting at the specified offset. |
| 178 | /// It handles sector erasures and data writes while verifying the device is in a proper state | ||
| 179 | /// for firmware updates. The function ensures that only unerased sectors are erased before | ||
| 180 | /// writing and efficiently handles the writing process across sector boundaries and in | ||
| 181 | /// various configurations (data size, page size, etc.). | ||
| 178 | /// | 182 | /// |
| 179 | /// # Safety | 183 | /// # Arguments |
| 184 | /// | ||
| 185 | /// * `offset` - The starting offset within the firmware area where data writing should begin. | ||
| 186 | /// * `data` - A slice of bytes representing the firmware data to be written. It must be a | ||
| 187 | /// multiple of NorFlash WRITE_SIZE. | ||
| 188 | /// | ||
| 189 | /// # Returns | ||
| 190 | /// | ||
| 191 | /// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation. | ||
| 180 | /// | 192 | /// |
| 181 | /// Failing to meet alignment and size requirements may result in a panic. | 193 | /// # Errors |
| 194 | /// | ||
| 195 | /// This function will return an error if: | ||
| 196 | /// | ||
| 197 | /// - The device is not in a proper state to receive firmware updates (e.g., not booted). | ||
| 198 | /// - There is a failure erasing a sector before writing. | ||
| 199 | /// - There is a failure writing data to the device. | ||
| 182 | pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { | 200 | pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { |
| 183 | assert!(data.len() >= DFU::ERASE_SIZE); | 201 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 184 | |||
| 185 | self.state.verify_booted().await?; | 202 | self.state.verify_booted().await?; |
| 186 | 203 | ||
| 187 | self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; | 204 | // Initialize variables to keep track of the remaining data and the current offset. |
| 205 | let mut remaining_data = data; | ||
| 206 | let mut offset = offset; | ||
| 207 | |||
| 208 | // Continue writing as long as there is data left to write. | ||
| 209 | while !remaining_data.is_empty() { | ||
| 210 | // Compute the current sector and its boundaries. | ||
| 211 | let current_sector = offset / DFU::ERASE_SIZE; | ||
| 212 | let sector_start = current_sector * DFU::ERASE_SIZE; | ||
| 213 | let sector_end = sector_start + DFU::ERASE_SIZE; | ||
| 214 | // Determine if the current sector needs to be erased before writing. | ||
| 215 | let need_erase = self | ||
| 216 | .state | ||
| 217 | .last_erased_dfu_page_index | ||
| 218 | .map_or(true, |last_erased_sector| current_sector != last_erased_sector); | ||
| 219 | |||
| 220 | // If the sector needs to be erased, erase it and update the last erased sector index. | ||
| 221 | if need_erase { | ||
| 222 | self.dfu.erase(sector_start as u32, sector_end as u32).await?; | ||
| 223 | self.state.last_erased_dfu_page_index = Some(current_sector); | ||
| 224 | } | ||
| 225 | |||
| 226 | // Calculate the size of the data chunk that can be written in the current iteration. | ||
| 227 | let write_size = core::cmp::min(remaining_data.len(), sector_end - offset); | ||
| 228 | // Split the data to get the current chunk to be written and the remaining data. | ||
| 229 | let (data_chunk, rest) = remaining_data.split_at(write_size); | ||
| 230 | |||
| 231 | // Write the current data chunk. | ||
| 232 | self.dfu.write(offset as u32, data_chunk).await?; | ||
| 188 | 233 | ||
| 189 | self.dfu.write(offset as u32, data).await?; | 234 | // Update the offset and remaining data for the next iteration. |
| 235 | remaining_data = rest; | ||
| 236 | offset += write_size; | ||
| 237 | } | ||
| 190 | 238 | ||
| 191 | Ok(()) | 239 | Ok(()) |
| 192 | } | 240 | } |
| @@ -210,6 +258,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { | |||
| 210 | pub struct FirmwareState<'d, STATE> { | 258 | pub struct FirmwareState<'d, STATE> { |
| 211 | state: STATE, | 259 | state: STATE, |
| 212 | aligned: &'d mut [u8], | 260 | aligned: &'d mut [u8], |
| 261 | last_erased_dfu_page_index: Option<usize>, | ||
| 213 | } | 262 | } |
| 214 | 263 | ||
| 215 | impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | 264 | impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { |
| @@ -231,7 +280,11 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | |||
| 231 | /// and follow the alignment rules for the flash being read from and written to. | 280 | /// and follow the alignment rules for the flash being read from and written to. |
| 232 | pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { | 281 | pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { |
| 233 | assert_eq!(aligned.len(), STATE::WRITE_SIZE.max(STATE::READ_SIZE)); | 282 | assert_eq!(aligned.len(), STATE::WRITE_SIZE.max(STATE::READ_SIZE)); |
| 234 | Self { state, aligned } | 283 | Self { |
| 284 | state, | ||
| 285 | aligned, | ||
| 286 | last_erased_dfu_page_index: None, | ||
| 287 | } | ||
| 235 | } | 288 | } |
| 236 | 289 | ||
| 237 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | 290 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index 4044871f0..45ae966f3 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs | |||
| @@ -207,20 +207,69 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 207 | self.state.mark_booted() | 207 | self.state.mark_booted() |
| 208 | } | 208 | } |
| 209 | 209 | ||
| 210 | /// Write data to a flash page. | 210 | /// Writes firmware data to the device. |
| 211 | /// | 211 | /// |
| 212 | /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. | 212 | /// This function writes the given data to the firmware area starting at the specified offset. |
| 213 | /// It handles sector erasures and data writes while verifying the device is in a proper state | ||
| 214 | /// for firmware updates. The function ensures that only unerased sectors are erased before | ||
| 215 | /// writing and efficiently handles the writing process across sector boundaries and in | ||
| 216 | /// various configurations (data size, page size, etc.). | ||
| 213 | /// | 217 | /// |
| 214 | /// # Safety | 218 | /// # Arguments |
| 219 | /// | ||
| 220 | /// * `offset` - The starting offset within the firmware area where data writing should begin. | ||
| 221 | /// * `data` - A slice of bytes representing the firmware data to be written. It must be a | ||
| 222 | /// multiple of NorFlash WRITE_SIZE. | ||
| 223 | /// | ||
| 224 | /// # Returns | ||
| 225 | /// | ||
| 226 | /// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation. | ||
| 227 | /// | ||
| 228 | /// # Errors | ||
| 229 | /// | ||
| 230 | /// This function will return an error if: | ||
| 215 | /// | 231 | /// |
| 216 | /// Failing to meet alignment and size requirements may result in a panic. | 232 | /// - The device is not in a proper state to receive firmware updates (e.g., not booted). |
| 233 | /// - There is a failure erasing a sector before writing. | ||
| 234 | /// - There is a failure writing data to the device. | ||
| 217 | pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { | 235 | pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { |
| 218 | assert!(data.len() >= DFU::ERASE_SIZE); | 236 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 219 | self.state.verify_booted()?; | 237 | self.state.verify_booted()?; |
| 220 | 238 | ||
| 221 | self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; | 239 | // Initialize variables to keep track of the remaining data and the current offset. |
| 240 | let mut remaining_data = data; | ||
| 241 | let mut offset = offset; | ||
| 242 | |||
| 243 | // Continue writing as long as there is data left to write. | ||
| 244 | while !remaining_data.is_empty() { | ||
| 245 | // Compute the current sector and its boundaries. | ||
| 246 | let current_sector = offset / DFU::ERASE_SIZE; | ||
| 247 | let sector_start = current_sector * DFU::ERASE_SIZE; | ||
| 248 | let sector_end = sector_start + DFU::ERASE_SIZE; | ||
| 249 | // Determine if the current sector needs to be erased before writing. | ||
| 250 | let need_erase = self | ||
| 251 | .state | ||
| 252 | .last_erased_dfu_page_index | ||
| 253 | .map_or(true, |last_erased_sector| current_sector != last_erased_sector); | ||
| 254 | |||
| 255 | // If the sector needs to be erased, erase it and update the last erased sector index. | ||
| 256 | if need_erase { | ||
| 257 | self.dfu.erase(sector_start as u32, sector_end as u32)?; | ||
| 258 | self.state.last_erased_dfu_page_index = Some(current_sector); | ||
| 259 | } | ||
| 260 | |||
| 261 | // Calculate the size of the data chunk that can be written in the current iteration. | ||
| 262 | let write_size = core::cmp::min(remaining_data.len(), sector_end - offset); | ||
| 263 | // Split the data to get the current chunk to be written and the remaining data. | ||
| 264 | let (data_chunk, rest) = remaining_data.split_at(write_size); | ||
| 265 | |||
| 266 | // Write the current data chunk. | ||
| 267 | self.dfu.write(offset as u32, data_chunk)?; | ||
| 222 | 268 | ||
| 223 | self.dfu.write(offset as u32, data)?; | 269 | // Update the offset and remaining data for the next iteration. |
| 270 | remaining_data = rest; | ||
| 271 | offset += write_size; | ||
| 272 | } | ||
| 224 | 273 | ||
| 225 | Ok(()) | 274 | Ok(()) |
| 226 | } | 275 | } |
| @@ -244,6 +293,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> | |||
| 244 | pub struct BlockingFirmwareState<'d, STATE> { | 293 | pub struct BlockingFirmwareState<'d, STATE> { |
| 245 | state: STATE, | 294 | state: STATE, |
| 246 | aligned: &'d mut [u8], | 295 | aligned: &'d mut [u8], |
| 296 | last_erased_dfu_page_index: Option<usize>, | ||
| 247 | } | 297 | } |
| 248 | 298 | ||
| 249 | impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | 299 | impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { |
| @@ -265,7 +315,11 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 265 | /// and written to. | 315 | /// and written to. |
| 266 | pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { | 316 | pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { |
| 267 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); | 317 | assert_eq!(aligned.len(), STATE::WRITE_SIZE); |
| 268 | Self { state, aligned } | 318 | Self { |
| 319 | state, | ||
| 320 | aligned, | ||
| 321 | last_erased_dfu_page_index: None, | ||
| 322 | } | ||
| 269 | } | 323 | } |
| 270 | 324 | ||
| 271 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | 325 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
