1use core::time::Duration;
4
5#[cfg(test)]
6use super::PreparedUpdate;
7use crate::drive::{self, Error, Transport};
8use crate::sense::{self, Triplet};
9use crate::{cdb, layout};
10
11pub struct FlashTransport<'a, R, W> {
14 pub reads: &'a mut R,
16 pub writes: &'a mut W,
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum Stage {
24 Commit,
26 Settled,
28}
29
30#[derive(Debug)]
32pub enum Status<'a, E> {
33 Sense(Triplet),
35 Unparseable(usize),
37 Failed(&'a Error<E>),
39}
40
41pub trait FlashRuntime<E> {
43 fn operation_progress(&mut self, _progress: crate::operation::Progress) {}
45 fn elapsed(&self) -> Duration;
48 fn sleep(&mut self, duration: Duration);
50 fn starting(&mut self, _bytes: usize) {}
52 fn progress(&mut self, _written: usize, _total: usize) {}
54 fn recovery_entry_failed(&mut self, _error: &Error<E>) {}
58 fn commit_failed(&mut self, _error: &Error<E>) {}
62 fn commit_not_ready(&mut self, _error: &Error<E>) {}
64 fn status(&mut self, _stage: Stage, _status: Status<'_, E>) {}
66 fn uploaded(&mut self, _bytes: usize) {}
68 fn poll_failed(&mut self, _elapsed: Duration, _error: &Error<E>) {}
70 fn verifying(&mut self) {}
72 fn unverified(&mut self, _offset: usize, _length: usize, _error: &Error<E>) {}
74}
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub struct Verified {
79 pub checked: usize,
81 pub ranges: usize,
83}
84
85#[derive(Debug)]
89#[non_exhaustive]
90pub enum FlashError<E> {
91 Entry(Error<E>),
94 Transfer {
96 offset: usize,
98 length: usize,
100 source: Error<E>,
102 },
103 Fault {
105 stage: Stage,
107 sense: Triplet,
109 },
110 ReadyTimeout(Error<E>),
113 Startup(super::StartupError<E>),
115 Mismatch {
117 address: usize,
119 read: u8,
121 wrote: u8,
123 differing: usize,
125 checked: usize,
127 },
128 Incomplete {
130 unverified: usize,
132 checked: usize,
134 },
135}
136
137impl<E: core::fmt::Debug> core::fmt::Display for FlashError<E> {
138 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
139 match self {
140 Self::Entry(e) => write!(f, "update entry failed before any firmware data: {e}"),
141 Self::Transfer {
142 offset,
143 length,
144 source,
145 } => write!(
146 f,
147 "firmware write at {offset:#x}, length {length}: {source}"
148 ),
149 Self::Fault { stage, sense } => write!(
150 f,
151 "{stage:?} sense {:02X}/{:02X}/{:02X} reports a flash fault",
152 sense.0, sense.1, sense.2
153 ),
154 Self::ReadyTimeout(e) => write!(f, "drive did not settle after the burn: {e}"),
155 Self::Startup(error) => write!(f, "target startup was not confirmed: {error}"),
156 Self::Mismatch {
157 address,
158 read,
159 wrote,
160 differing,
161 checked,
162 } => write!(
163 f,
164 "read-back differs at {address:#08x} (read {read:#04x}, wrote {wrote:#04x}); \
165 {differing} of {checked} protected bytes differ"
166 ),
167 Self::Incomplete {
168 unverified,
169 checked,
170 } => write!(
171 f,
172 "{unverified} protected chunks unreadable; {checked} protected bytes verified"
173 ),
174 }
175 }
176}
177
178impl<E: core::fmt::Debug + 'static> std::error::Error for FlashError<E> {
179 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
180 match self {
181 Self::Entry(source) | Self::ReadyTimeout(source) => Some(source),
182 Self::Transfer { source, .. } => Some(source),
183 Self::Startup(source) => Some(source),
184 _ => None,
185 }
186 }
187}
188
189fn request_sense<T: Transport>(t: &mut T) -> Result<(usize, Option<Triplet>), Error<T::Error>> {
192 let mut buf = [0; cdb::REQUEST_SENSE_LEN];
193 let n = drive::read(t, &cdb::request_sense(), &mut buf, 0)?;
194 Ok((n, sense::parse(&buf[..n])))
195}
196
197fn status<T: Transport, H: FlashRuntime<T::Error>>(
199 t: &mut T,
200 runtime: &mut H,
201 stage: Stage,
202) -> Result<(), FlashError<T::Error>> {
203 match request_sense(t) {
204 Ok((_, Some(sense))) if sense::is_flash_fault(sense.0) => {
205 Err(FlashError::Fault { stage, sense })
206 }
207 Ok((_, Some(sense))) => {
208 runtime.status(stage, Status::Sense(sense));
209 Ok(())
210 }
211 Ok((n, None)) => {
212 runtime.status(stage, Status::Unparseable(n));
213 Ok(())
214 }
215 Err(error) => {
216 runtime.status(stage, Status::Failed(&error));
217 Ok(())
218 }
219 }
220}
221
222#[cfg(test)]
223impl PreparedUpdate {
224 pub(crate) fn flash<R, W, H>(
233 &self,
234 io: FlashTransport<'_, R, W>,
235 runtime: &mut H,
236 ) -> Result<Verified, FlashError<W::Error>>
237 where
238 W: Transport,
239 R: Transport<Error = W::Error>,
240 H: FlashRuntime<W::Error>,
241 {
242 Payload {
243 class: self.class,
244 image: &self.image,
245 transfer: self.transfer(),
246 }
247 .flash(io, runtime, ExecutionMode::Flash, cdb::Prepare::ModeOne)
248 }
249}
250
251pub(crate) struct Payload<'a> {
253 pub(crate) class: crate::DriveClass,
254 pub(crate) image: &'a [u8],
255 pub(crate) transfer: &'a [u8],
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
261pub(crate) enum ExecutionMode {
262 Flash,
263 Recovery,
264}
265impl Payload<'_> {
266 pub(crate) fn flash<R, W, H>(
267 &self,
268 io: FlashTransport<'_, R, W>,
269 runtime: &mut H,
270 mode: ExecutionMode,
271 prepare: cdb::Prepare,
272 ) -> Result<Verified, FlashError<W::Error>>
273 where
274 W: Transport,
275 R: Transport<Error = W::Error>,
276 H: FlashRuntime<W::Error>,
277 {
278 let transfer = self.transfer;
279 runtime.starting(transfer.len());
280 runtime.operation_progress(crate::operation::Progress::waiting(
281 crate::operation::Phase::Preparing,
282 crate::operation::Component::Firmware,
283 ));
284 let mut session = match mode {
285 ExecutionMode::Recovery => {
286 drive::enter_recovery_reporting(io.writes, prepare, |error| {
287 runtime.recovery_entry_failed(error)
288 })
289 }
290 ExecutionMode::Flash => {
291 drive::enter_update_command(io.writes, prepare).map_err(FlashError::Entry)?
292 }
293 };
294 let chunk = self.class.chunk();
295 for (index, piece) in transfer.chunks(chunk).enumerate() {
296 let offset = index * chunk;
297 session
298 .write(offset as u32, piece)
299 .map_err(|source| FlashError::Transfer {
300 offset,
301 length: piece.len(),
302 source,
303 })?;
304 runtime.progress(offset + piece.len(), transfer.len());
305 runtime.operation_progress(crate::operation::Progress::bytes(
306 crate::operation::Phase::Writing,
307 crate::operation::Component::Firmware,
308 offset + piece.len(),
309 transfer.len(),
310 ));
311 }
312 if let Err(error) = session.finish() {
313 runtime.commit_failed(&error);
314 }
315 if let Err(error) = drive::test_unit_ready(io.reads) {
316 runtime.commit_not_ready(&error);
317 }
318 status(io.reads, runtime, Stage::Commit)?;
319
320 runtime.uploaded(transfer.len());
321 runtime.operation_progress(crate::operation::Progress::waiting(
322 crate::operation::Phase::Restarting,
323 crate::operation::Component::Firmware,
324 ));
325 let start = runtime.elapsed();
326 while let Err(error) = drive::test_unit_ready(io.reads) {
327 let waited = runtime.elapsed().saturating_sub(start);
328 runtime.poll_failed(waited, &error);
329 if waited >= self.class.ready_timeout() {
330 return Err(FlashError::ReadyTimeout(error));
331 }
332 runtime.sleep(self.class.poll_interval());
333 }
334 status(io.reads, runtime, Stage::Settled)?;
335
336 runtime.verifying();
337 self.verify(io.reads, runtime)
338 }
339
340 pub(crate) fn flash_completed<R, W, H>(
342 &self,
343 io: FlashTransport<'_, R, W>,
344 runtime: &mut H,
345 mode: ExecutionMode,
346 prepare: cdb::Prepare,
347 ) -> Result<super::Completion, FlashError<W::Error>>
348 where
349 W: Transport,
350 R: Transport<Error = W::Error>,
351 H: FlashRuntime<W::Error>,
352 {
353 let result = self.flash(
354 FlashTransport {
355 reads: io.reads,
356 writes: io.writes,
357 },
358 runtime,
359 mode,
360 prepare,
361 );
362 super::completion::complete(io.reads, self.image, result)
363 }
364
365 fn verify<R: Transport, H: FlashRuntime<R::Error>>(
367 &self,
368 reads: &mut R,
369 runtime: &mut H,
370 ) -> Result<Verified, FlashError<R::Error>> {
371 let ranges = self.class.verification_ranges(self.image);
372 let protected = |at: usize| ranges.iter().any(|r| r.contains(&at));
373 let (mut checked, mut differing, mut unverified) = (0, 0, 0);
374 let mut first: Option<(usize, u8, u8)> = None;
375 let chunk = self.class.chunk();
376 let mut buf = vec![0; chunk];
377 for (index, piece) in self.image.chunks(chunk).enumerate() {
378 runtime.operation_progress(crate::operation::Progress::bytes(
379 crate::operation::Phase::Verifying,
380 crate::operation::Component::Firmware,
381 index * chunk,
382 self.image.len(),
383 ));
384 let offset = index * chunk;
385 let got = &mut buf[..piece.len()];
386 match drive::read_memory(reads, offset as u32, got) {
387 Ok(()) => {
388 for (i, (&read, &wrote)) in got.iter().zip(piece).enumerate() {
389 if !protected(offset + i) {
390 continue;
391 }
392 checked += 1;
393 if read != wrote {
394 differing += 1;
395 first.get_or_insert((offset + i, read, wrote));
396 }
397 }
398 }
399 Err(error) => {
400 if (offset..offset + piece.len()).any(protected) {
401 unverified += 1;
402 runtime.unverified(offset, piece.len(), &error);
403 }
404 }
405 }
406 }
407 runtime.operation_progress(crate::operation::Progress::bytes(
408 crate::operation::Phase::Verifying,
409 crate::operation::Component::Firmware,
410 self.image.len(),
411 self.image.len(),
412 ));
413 if let Some((address, read, wrote)) = first {
414 return Err(FlashError::Mismatch {
415 address,
416 read,
417 wrote,
418 differing,
419 checked,
420 });
421 }
422 if ranges.is_empty() || unverified > 0 {
423 return Err(FlashError::Incomplete {
424 unverified,
425 checked,
426 });
427 }
428 debug_assert_eq!(self.image.len(), layout::IMAGE_SIZE);
429 Ok(Verified {
430 checked,
431 ranges: ranges.len(),
432 })
433 }
434}