aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot/src/firmware_updater/blocking.rs
diff options
context:
space:
mode:
authorBadr Bouslikhin <[email protected]>2024-02-11 20:02:28 +0100
committerBadr Bouslikhin <[email protected]>2024-02-11 20:16:17 +0100
commiteb3bd39b068ae34892520ec38f111704ea357355 (patch)
tree7014d70ee32db54af4b229c6edbf3818080064c0 /embassy-boot/src/firmware_updater/blocking.rs
parent72ab04c45335ceb725076a38a252749e781a8cf6 (diff)
feat(boot): enhance firmware write functionality
Diffstat (limited to 'embassy-boot/src/firmware_updater/blocking.rs')
-rw-r--r--embassy-boot/src/firmware_updater/blocking.rs70
1 files changed, 62 insertions, 8 deletions
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>
244pub struct BlockingFirmwareState<'d, STATE> { 293pub 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
249impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { 299impl<'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.