aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot
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
parent72ab04c45335ceb725076a38a252749e781a8cf6 (diff)
feat(boot): enhance firmware write functionality
Diffstat (limited to 'embassy-boot')
-rw-r--r--embassy-boot/src/firmware_updater/asynch.rs71
-rw-r--r--embassy-boot/src/firmware_updater/blocking.rs70
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> {
210pub struct FirmwareState<'d, STATE> { 258pub 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
215impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { 264impl<'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>
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.