1use core::time::Duration;
4
5use super::PreparedUpdate;
6use crate::drive::{self, Error, Transport};
7use crate::sense::{self, Triplet};
8use crate::{cdb, layout};
9
10pub struct FlashTransport<'a, R, W> {
13 pub reads: &'a mut R,
15 pub writes: &'a mut W,
18}
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum Stage {
23 Commit,
25 Settled,
27}
28
29#[derive(Debug)]
31pub enum Status<'a, E> {
32 Sense(Triplet),
34 Unparseable(usize),
36 Failed(&'a Error<E>),
38}
39
40pub trait FlashRuntime<E> {
42 fn elapsed(&self) -> Duration;
45 fn sleep(&mut self, duration: Duration);
47 fn starting(&mut self, _bytes: usize) {}
49 fn progress(&mut self, _written: usize, _total: usize) {}
51 fn commit_failed(&mut self, _error: &Error<E>) {}
55 fn commit_not_ready(&mut self, _error: &Error<E>) {}
57 fn status(&mut self, _stage: Stage, _status: Status<'_, E>) {}
59 fn uploaded(&mut self, _bytes: usize) {}
61 fn poll_failed(&mut self, _elapsed: Duration, _error: &Error<E>) {}
63 fn verifying(&mut self) {}
65 fn unverified(&mut self, _offset: usize, _length: usize, _error: &Error<E>) {}
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub struct Verified {
72 pub checked: usize,
74 pub ranges: usize,
76}
77
78#[derive(Debug)]
82#[non_exhaustive]
83pub enum FlashError<E> {
84 Entry(Error<E>),
87 Transfer {
89 offset: usize,
91 length: usize,
93 source: Error<E>,
95 },
96 Fault {
98 stage: Stage,
100 sense: Triplet,
102 },
103 ReadyTimeout(Error<E>),
106 Mismatch {
108 address: usize,
110 read: u8,
112 wrote: u8,
114 differing: usize,
116 checked: usize,
118 },
119 Incomplete {
121 unverified: usize,
123 checked: usize,
125 },
126}
127
128impl<E: core::fmt::Debug> core::fmt::Display for FlashError<E> {
129 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
130 match self {
131 Self::Entry(e) => write!(f, "update entry failed before any firmware data: {e}"),
132 Self::Transfer {
133 offset,
134 length,
135 source,
136 } => write!(
137 f,
138 "firmware write at {offset:#x}, length {length}: {source}"
139 ),
140 Self::Fault { stage, sense } => write!(
141 f,
142 "{stage:?} sense {:02X}/{:02X}/{:02X} reports a flash fault",
143 sense.0, sense.1, sense.2
144 ),
145 Self::ReadyTimeout(e) => write!(f, "drive did not settle after the burn: {e}"),
146 Self::Mismatch {
147 address,
148 read,
149 wrote,
150 differing,
151 checked,
152 } => write!(
153 f,
154 "read-back differs at {address:#08x} (read {read:#04x}, wrote {wrote:#04x}); \
155 {differing} of {checked} protected bytes differ"
156 ),
157 Self::Incomplete {
158 unverified,
159 checked,
160 } => write!(
161 f,
162 "{unverified} protected chunks unreadable; {checked} protected bytes verified"
163 ),
164 }
165 }
166}
167
168impl<E: core::fmt::Debug + 'static> std::error::Error for FlashError<E> {}
169
170fn request_sense<T: Transport>(t: &mut T) -> Result<(usize, Option<Triplet>), Error<T::Error>> {
173 let mut buf = [0; cdb::REQUEST_SENSE_LEN];
174 let n = drive::read(t, &cdb::request_sense(), &mut buf, 0)?;
175 Ok((n, sense::parse(&buf[..n])))
176}
177
178fn status<T: Transport, H: FlashRuntime<T::Error>>(
180 t: &mut T,
181 runtime: &mut H,
182 stage: Stage,
183) -> Result<(), FlashError<T::Error>> {
184 match request_sense(t) {
185 Ok((_, Some(sense))) if sense::is_flash_fault(sense.0) => {
186 Err(FlashError::Fault { stage, sense })
187 }
188 Ok((_, Some(sense))) => {
189 runtime.status(stage, Status::Sense(sense));
190 Ok(())
191 }
192 Ok((n, None)) => {
193 runtime.status(stage, Status::Unparseable(n));
194 Ok(())
195 }
196 Err(error) => {
197 runtime.status(stage, Status::Failed(&error));
198 Ok(())
199 }
200 }
201}
202
203impl PreparedUpdate {
204 pub fn flash<R, W, H>(
213 &self,
214 io: FlashTransport<'_, R, W>,
215 runtime: &mut H,
216 ) -> Result<Verified, FlashError<W::Error>>
217 where
218 W: Transport,
219 R: Transport<Error = W::Error>,
220 H: FlashRuntime<W::Error>,
221 {
222 let transfer = self.transfer();
223 runtime.starting(transfer.len());
224 let mut session = drive::enter_update(io.writes).map_err(FlashError::Entry)?;
225 let chunk = self.class.chunk();
226 for (index, piece) in transfer.chunks(chunk).enumerate() {
227 let offset = index * chunk;
228 session
229 .write(offset as u32, piece)
230 .map_err(|source| FlashError::Transfer {
231 offset,
232 length: piece.len(),
233 source,
234 })?;
235 runtime.progress(offset + piece.len(), transfer.len());
236 }
237 if let Err(error) = session.finish() {
238 runtime.commit_failed(&error);
239 }
240 if let Err(error) = drive::test_unit_ready(io.reads) {
241 runtime.commit_not_ready(&error);
242 }
243 status(io.reads, runtime, Stage::Commit)?;
244
245 runtime.uploaded(transfer.len());
246 let start = runtime.elapsed();
247 while let Err(error) = drive::test_unit_ready(io.reads) {
248 let waited = runtime.elapsed().saturating_sub(start);
249 runtime.poll_failed(waited, &error);
250 if waited >= self.class.ready_timeout() {
251 return Err(FlashError::ReadyTimeout(error));
252 }
253 runtime.sleep(self.class.poll_interval());
254 }
255 status(io.reads, runtime, Stage::Settled)?;
256
257 runtime.verifying();
258 self.verify(io.reads, runtime)
259 }
260
261 fn verify<R: Transport, H: FlashRuntime<R::Error>>(
263 &self,
264 reads: &mut R,
265 runtime: &mut H,
266 ) -> Result<Verified, FlashError<R::Error>> {
267 let ranges = self.verification_ranges();
268 let protected = |at: usize| ranges.iter().any(|r| r.contains(&at));
269 let (mut checked, mut differing, mut unverified) = (0, 0, 0);
270 let mut first: Option<(usize, u8, u8)> = None;
271 let chunk = self.class.chunk();
272 let mut buf = vec![0; chunk];
273 for (index, piece) in self.image.chunks(chunk).enumerate() {
274 let offset = index * chunk;
275 let got = &mut buf[..piece.len()];
276 match drive::read_memory(reads, offset as u32, got) {
277 Ok(()) => {
278 for (i, (&read, &wrote)) in got.iter().zip(piece).enumerate() {
279 if !protected(offset + i) {
280 continue;
281 }
282 checked += 1;
283 if read != wrote {
284 differing += 1;
285 first.get_or_insert((offset + i, read, wrote));
286 }
287 }
288 }
289 Err(error) => {
290 if (offset..offset + piece.len()).any(protected) {
291 unverified += 1;
292 runtime.unverified(offset, piece.len(), &error);
293 }
294 }
295 }
296 }
297 if let Some((address, read, wrote)) = first {
298 return Err(FlashError::Mismatch {
299 address,
300 read,
301 wrote,
302 differing,
303 checked,
304 });
305 }
306 if !ranges.is_empty() && unverified > 0 {
307 return Err(FlashError::Incomplete {
308 unverified,
309 checked,
310 });
311 }
312 debug_assert_eq!(self.image.len(), layout::IMAGE_SIZE);
313 Ok(Verified {
314 checked,
315 ranges: ranges.len(),
316 })
317 }
318}