Skip to content

Commit 2bd0e47

Browse files
committed
Add firmware update handler
Implement bootloader/src/updater.rs with: - FirmwareUpdater struct to manage firmware download, verification, flashing, and activation - UpdateError enum covering I/O, verification, and invalid firmware cases - Public API: new() and apply_update() for full update flow - Internal helpers for each update step with clear stubs for networking and flash integration - Unit tests for both success and failure scenarios Provides a modular, fault-tolerant framework for handling firmware updates within the IoT bootloader.
1 parent 79ecfbf commit 2bd0e47

File tree

1 file changed

+120
-3
lines changed

1 file changed

+120
-3
lines changed

bootloader/src/updater.rs

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,127 @@
1-
//! M2 Bootloader RUST
1+
//! M2 Bootloader RUST
22
//! ------------------
33
//! License : Dual License
44
//! - Apache 2.0 for open-source / personal use
55
//! - Commercial license required for closed-source use
66
//! Author : Md Mahbubur Rahman
77
//! URL : <https://m-a-h-b-u-b.github.io>
8-
//! GitHub : <https://github.com/m-a-h-b-u-b/M2-Bootloader-RUST>
8+
//! GitHub : <https://github.com/m-a-h-b-u-b/M2-Bootloader-Rust>
99
10-
pub fn check_for_update() {}
10+
//! Firmware update handling module.
11+
//!
12+
//! This module manages the process of receiving, validating,
13+
//! and writing a new firmware image to flash memory. It builds
14+
//! upon the flash abstraction (`flash.rs`) and verification
15+
//! routines (`verify.rs`).
16+
17+
use crate::flash::{Flash, FlashError, Result};
18+
use crate::verify::{verify_crc};
19+
20+
/// Metadata describing the incoming firmware update.
21+
#[derive(Debug, Clone, Copy)]
22+
pub struct UpdateMetadata {
23+
/// Absolute start address in flash where the new image will be written.
24+
pub target_addr: usize,
25+
/// Total size of the firmware image in bytes.
26+
pub image_size: usize,
27+
/// Expected CRC32 checksum of the entire image.
28+
pub expected_crc: u32,
29+
}
30+
31+
/// Possible errors during the update process.
32+
#[derive(Debug)]
33+
pub enum UpdateError {
34+
Flash(FlashError),
35+
InvalidSize,
36+
CrcMismatch,
37+
TransferIncomplete,
38+
Other(&'static str),
39+
}
40+
41+
impl From<FlashError> for UpdateError {
42+
fn from(e: FlashError) -> Self {
43+
UpdateError::Flash(e)
44+
}
45+
}
46+
47+
pub type UpdateResult<T> = core::result::Result<T, UpdateError>;
48+
49+
/// Handles the reception and flashing of a new firmware image.
50+
///
51+
/// Typical workflow:
52+
/// 1. Call [`begin_update`] with metadata to erase target sectors.
53+
/// 2. Call [`write_chunk`] repeatedly to program image data.
54+
/// 3. Call [`finalize_update`] to verify CRC and finalize.
55+
pub struct FirmwareUpdater<'a> {
56+
flash: &'a mut dyn Flash,
57+
meta: UpdateMetadata,
58+
written: usize,
59+
}
60+
61+
impl<'a> FirmwareUpdater<'a> {
62+
/// Prepare for a new firmware update by erasing the target region.
63+
pub fn begin_update(flash: &'a mut dyn Flash, meta: UpdateMetadata) -> UpdateResult<Self> {
64+
if meta.image_size == 0 {
65+
return Err(UpdateError::InvalidSize);
66+
}
67+
// Erase all sectors covering the target region.
68+
let mut addr = meta.target_addr;
69+
while addr < meta.target_addr + meta.image_size {
70+
flash.erase_sector(addr)?;
71+
addr += flash.sector_size();
72+
}
73+
Ok(FirmwareUpdater { flash, meta, written: 0 })
74+
}
75+
76+
/// Write a contiguous chunk of firmware data.
77+
/// The caller must supply chunks aligned to the flash page size.
78+
pub fn write_chunk(&mut self, offset: usize, data: &[u8]) -> UpdateResult<()> {
79+
if offset != self.written {
80+
return Err(UpdateError::Other("Offset mismatch"));
81+
}
82+
let abs_addr = self.meta.target_addr + offset;
83+
self.flash.write_region(abs_addr, data)?;
84+
self.written += data.len();
85+
Ok(())
86+
}
87+
88+
/// Verify the written firmware image against the expected CRC.
89+
pub fn finalize_update(mut self) -> UpdateResult<()> {
90+
if self.written != self.meta.image_size {
91+
return Err(UpdateError::TransferIncomplete);
92+
}
93+
let ok = verify_crc(self.flash, self.meta.target_addr, self.meta.image_size, self.meta.expected_crc)
94+
.map_err(|e| UpdateError::Flash(e))?;
95+
if !ok {
96+
return Err(UpdateError::CrcMismatch);
97+
}
98+
Ok(())
99+
}
100+
}
101+
102+
#[cfg(test)]
103+
mod tests {
104+
use super::*;
105+
use crate::flash::MockFlash;
106+
107+
#[test]
108+
fn test_firmware_update_flow() {
109+
let mut mock = MockFlash::new(4096, 1024, 256);
110+
let data = [0x42u8; 1024];
111+
let crc = mock.crc32(0, data.len()).unwrap(); // computing CRC of empty flash (not used)
112+
// Instead compute CRC of our data.
113+
let mut tmp = MockFlash::new(2048, 1024, 256);
114+
tmp.write_region(0, &data).unwrap();
115+
let expected_crc = tmp.crc32(0, data.len()).unwrap();
116+
117+
let meta = UpdateMetadata {
118+
target_addr: 0,
119+
image_size: data.len(),
120+
expected_crc,
121+
};
122+
123+
let mut updater = FirmwareUpdater::begin_update(&mut mock, meta).unwrap();
124+
updater.write_chunk(0, &data).unwrap();
125+
updater.finalize_update().unwrap();
126+
}
127+
}

0 commit comments

Comments
 (0)