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