1use std::ops::RangeInclusive;
25
26use crate::image::{cmac, detect_chip, family, is_drive_read, Family};
27use crate::{cdb, layout, Chip, DriveClass, Identity};
28
29#[cfg(feature = "drive")]
30mod flash;
31#[cfg(feature = "drive")]
32pub use flash::{FlashError, FlashRuntime, FlashTransport, Stage, Status, Verified};
33
34#[derive(Clone, Debug, PartialEq, Eq)]
36#[non_exhaustive]
37pub enum PrepareError {
38 Size(usize),
40 Integrity,
42 DriveRead,
45 UnknownChip,
47 UnknownDriveChip,
49 ChipMismatch {
51 image: Chip,
53 drive: Chip,
55 },
56 InstalledMismatch,
59 UnknownInstalledFamily,
61 UnknownTargetFamily,
63 FamilyMismatch {
65 installed: Family,
67 target: Family,
69 },
70}
71
72impl core::fmt::Display for PrepareError {
73 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 match self {
75 Self::Size(n) => write!(
76 f,
77 "image is {n} bytes; an update image is exactly {} bytes",
78 layout::IMAGE_SIZE
79 ),
80 Self::Integrity => f.write_str("image fails its AES-CMAC integrity check"),
81 Self::DriveRead => f.write_str(
82 "image is a raw drive read (its boot page is the read-back copy), not a \
83 flashable image",
84 ),
85 Self::UnknownChip => f.write_str("image names no MT1959 or MT1939 chip"),
86 Self::UnknownDriveChip => f.write_str(
87 "could not read this drive's controller generation (MT1959/MT1939) from its \
88 boot banner",
89 ),
90 Self::ChipMismatch { image, drive } => write!(
91 f,
92 "image is {image} firmware but this drive is {drive} silicon — refusing to \
93 flash across chip generations"
94 ),
95 Self::InstalledMismatch => f.write_str(
96 "installed firmware is not a complete image of the live drive (size, identity \
97 descriptor or boot banner differ)",
98 ),
99 Self::UnknownInstalledFamily => {
100 f.write_str("could not determine the installed firmware family")
101 }
102 Self::UnknownTargetFamily => {
103 f.write_str("could not determine the target firmware family")
104 }
105 Self::FamilyMismatch { installed, target } => write!(
106 f,
107 "family mismatch: installed firmware is family {installed}, target is family \
108 {target}"
109 ),
110 }
111 }
112}
113
114impl std::error::Error for PrepareError {}
115
116#[derive(Clone, Copy, Debug, PartialEq, Eq)]
118pub enum Direction {
119 In,
121 Out,
123 None,
125}
126
127#[derive(Clone, Copy, Debug, PartialEq, Eq)]
129pub struct Step {
130 pub label: &'static str,
132 pub cdb: [u8; 12],
134 pub direction: Direction,
136 pub length: usize,
138}
139
140impl DriveClass {
141 pub fn steps(self) -> Vec<Step> {
145 let chunk = self.chunk();
146 let step = |label, cdb, direction, length| Step {
147 label,
148 cdb,
149 direction,
150 length,
151 };
152 let mut steps = vec![
153 step("PROBE", cdb::probe(), Direction::In, cdb::PROBE_LEN),
154 step("READY", cdb::test_unit_ready(), Direction::None, 0),
155 step("PREPARE", cdb::enter_update(), Direction::Out, 0),
156 ];
157 steps.extend((0..layout::IMAGE_SIZE).step_by(chunk).map(|offset| {
158 step(
159 "STREAM",
160 cdb::transfer(offset as u32, chunk as u16),
161 Direction::Out,
162 chunk,
163 )
164 }));
165 steps.extend([
166 step("COMMIT", cdb::finish(), Direction::Out, 0),
167 step("READY", cdb::test_unit_ready(), Direction::None, 0),
168 step(
169 "STATUS",
170 cdb::request_sense(),
171 Direction::In,
172 cdb::REQUEST_SENSE_LEN,
173 ),
174 ]);
175 steps
176 }
177
178 pub fn verification_ranges(self, image: &[u8]) -> Vec<RangeInclusive<usize>> {
182 let Ok(entries) = cmac::parse_table(image) else {
183 return Vec::new();
184 };
185 entries
186 .iter()
187 .filter(|entry| entry.is_active())
188 .filter_map(|entry| {
189 let start = (entry.start as usize).max(self.verify_floor());
190 let end = entry.end as usize;
191 (start <= end).then_some(start..=end)
192 })
193 .collect()
194 }
195}
196
197#[derive(Clone, Debug)]
199pub struct InstalledFirmware {
200 identity: Identity,
201 family: Option<Family>,
202}
203
204impl InstalledFirmware {
205 pub fn new(identity: Identity, image: &[u8]) -> Result<Self, PrepareError> {
213 if image.len() != layout::IMAGE_SIZE {
214 return Err(PrepareError::InstalledMismatch);
215 }
216 let descriptor = identity
217 .has_descriptor()
218 .then(|| layout::DESCRIPTOR.of(image) == Some(&identity.descriptor_bytes()[..]));
219 let banner = (!identity.banner().is_empty())
220 .then(|| layout::BANNER.of(image) == Some(&identity.banner_bytes()[..]));
221 match (descriptor, banner) {
222 (None, None) | (Some(false), _) | (_, Some(false)) => {
223 Err(PrepareError::InstalledMismatch)
224 }
225 _ => Ok(Self {
226 family: family(image),
227 identity,
228 }),
229 }
230 }
231
232 pub fn identity(&self) -> &Identity {
234 &self.identity
235 }
236
237 pub fn family(&self) -> Option<Family> {
239 self.family
240 }
241}
242
243#[derive(Clone, Debug)]
245pub struct PreparedUpdate {
246 class: DriveClass,
247 family: Family,
248 image: Vec<u8>,
249 transfer: Option<Vec<u8>>,
250}
251
252impl PreparedUpdate {
253 pub fn check(image: &[u8], drive: &Identity) -> Result<Family, PrepareError> {
260 Self::checked(image, drive).map(|(_, family)| family)
261 }
262
263 fn checked(image: &[u8], drive: &Identity) -> Result<(DriveClass, Family), PrepareError> {
265 if image.len() != layout::IMAGE_SIZE {
266 return Err(PrepareError::Size(image.len()));
267 }
268 if !cmac::verify(image) {
269 return Err(PrepareError::Integrity);
270 }
271 if is_drive_read(image) {
272 return Err(PrepareError::DriveRead);
273 }
274 let image_chip = detect_chip(image).map_err(|_| PrepareError::UnknownChip)?;
275 let drive_chip =
279 Chip::from_banner(drive.banner_bytes()).ok_or(PrepareError::UnknownDriveChip)?;
280 if image_chip.family != drive_chip {
281 return Err(PrepareError::ChipMismatch {
282 image: image_chip.family,
283 drive: drive_chip,
284 });
285 }
286 let target = family(image).ok_or(PrepareError::UnknownTargetFamily)?;
287 Ok((DriveClass::of(drive_chip), target))
288 }
289
290 pub fn prepare(image: &[u8], installed: &InstalledFirmware) -> Result<Self, PrepareError> {
294 let (class, target) = Self::checked(image, installed.identity())?;
295 let installed_family = installed
296 .family()
297 .ok_or(PrepareError::UnknownInstalledFamily)?;
298 if installed_family != target {
299 return Err(PrepareError::FamilyMismatch {
300 installed: installed_family,
301 target,
302 });
303 }
304 Ok(Self {
305 class,
306 family: target,
307 image: image.to_vec(),
308 transfer: None,
309 })
310 }
311
312 #[cfg(feature = "enc")]
315 pub fn encrypted(mut self) -> Self {
316 let mut transfer = self.image.clone();
317 crate::enc::encrypt(&mut transfer).expect("an update image is whole AES blocks");
318 self.transfer = Some(transfer);
319 self
320 }
321
322 pub fn class(&self) -> DriveClass {
324 self.class
325 }
326
327 pub fn family(&self) -> Family {
329 self.family
330 }
331
332 pub fn image(&self) -> &[u8] {
334 &self.image
335 }
336
337 pub fn transfer(&self) -> &[u8] {
339 self.transfer.as_deref().unwrap_or(&self.image)
340 }
341
342 pub fn steps(&self) -> Vec<Step> {
344 self.class.steps()
345 }
346
347 pub fn verification_ranges(&self) -> Vec<RangeInclusive<usize>> {
350 self.class.verification_ranges(&self.image)
351 }
352}
353
354#[cfg(test)]
355#[path = "update_tests.rs"]
356mod tests;