Skip to main content

mediatek_optical/
recovery.rs

1//! Explicit recovery without installed-firmware, model, family or backup policy.
2//! Requires `drive`, `image` and `enc`. Identification and preparation are
3//! read-only; the caller owns consent and write authorization.
4use crate::update::{FlashError, FlashRuntime, FlashTransport, PrepareError, Verified};
5use crate::{
6    drive::{self, Transport},
7    image, layout, receiver, Chip, DriveClass,
8};
9
10/// A recognized receiver and its live controller generation.
11#[derive(Debug, Clone)]
12pub struct Receiver {
13    receiver: receiver::Receiver,
14    chip: Chip,
15    banner: [u8; layout::BANNER.len],
16}
17/// Recovery identification, preparation or execution failed.
18#[derive(Debug)]
19pub enum Error<E> {
20    /// Receiver identification or revalidation failed.
21    Receiver(receiver::Error<E>),
22    /// Explicit Current reference analysis or metadata binding failed.
23    Reference(receiver::reference::Error<E>),
24    /// A required device read failed.
25    Read(drive::Error<E>),
26    /// Image size, integrity, form or chip is unacceptable.
27    Image(PrepareError),
28    /// Transfer or protected-region verification failed.
29    Flash(FlashError<E>),
30    /// The live chip/banner changed after identification.
31    Changed,
32    /// The receiver did not start the selected firmware.
33    Startup,
34}
35impl<E: core::fmt::Debug> core::fmt::Display for Error<E> {
36    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37        match self {
38            Self::Receiver(e)=>write!(f,"{e}"), Self::Reference(e)=>write!(f,"{e}"), Self::Read(e)=>write!(f,"{e}"),
39            Self::Image(e)=>write!(f,"{e}"), Self::Flash(e)=>write!(f,"{e}"),
40            Self::Changed=>f.write_str("MediaTek boot banner changed; re-plan recovery"),
41            Self::Startup=>f.write_str("recovery did not confirm startup of the selected firmware; drive may still be in BOOT"),
42        }
43    }
44}
45impl<E: core::fmt::Debug + 'static> std::error::Error for Error<E> {
46    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
47        match self {
48            Self::Receiver(source) => Some(source),
49            Self::Reference(source) => Some(source),
50            Self::Read(source) => Some(source),
51            Self::Image(source) => Some(source),
52            Self::Flash(source) => Some(source),
53            _ => None,
54        }
55    }
56}
57
58impl Receiver {
59    /// Recognize a MediaTek receiver without MMC features or application metadata.
60    /// Returns `None` for a readable non-MediaTek banner. Reads no backup.
61    pub fn identify<T: Transport>(t: &mut T) -> Result<Option<Self>, Error<T::Error>> {
62        let mode = match receiver::detect_mode(t) {
63            Ok(mode) => mode,
64            Err(receiver::Error::InvalidIdentity) => return Ok(None),
65            Err(e) => return Err(Error::Receiver(e)),
66        };
67        if [&b"PIONEER "[..], &b"RENESAS "[..]].contains(&&mode.inquiry()[8..16]) {
68            return Ok(None);
69        }
70        let mut banner = [0; layout::BANNER.len];
71        if drive::read_memory(t, layout::BANNER.start as u32, &mut banner).is_err() {
72            return Ok(None);
73        }
74        let Some(chip) = Chip::from_banner(&banner) else {
75            return Ok(None);
76        };
77        let receiver = receiver::detect(t, &mode).map_err(Error::Receiver)?;
78        Ok(Some(Self {
79            receiver,
80            chip,
81            banner,
82        }))
83    }
84    /// Identify using explicit Current firmware when live code cannot be read.
85    /// Physical identity and build metadata bind the offline instruction evidence.
86    pub fn identify_reference<T: Transport>(
87        t: &mut T,
88        envelope: &crate::envelope::Envelope,
89    ) -> Result<Self, Error<T::Error>> {
90        let receiver = receiver::reference::detect(t, envelope).map_err(Error::Reference)?;
91        let mut banner = [0; layout::BANNER.len];
92        drive::read_memory(t, layout::BANNER.start as u32, &mut banner).map_err(Error::Read)?;
93        let chip = Chip::from_banner(&banner).ok_or(Error::Changed)?;
94        Ok(Self {
95            receiver,
96            chip,
97            banner,
98        })
99    }
100    /// Active operating mode.
101    pub fn mode(&self) -> receiver::Mode {
102        self.receiver.mode()
103    }
104    /// Automatically detected transfer format.
105    pub fn encoding(&self) -> receiver::Encoding {
106        self.receiver.encoding()
107    }
108    /// Controller generation from the live boot banner.
109    pub fn chip(&self) -> Chip {
110        self.chip
111    }
112    /// Verify identity, receiver instructions and chip/banner before a write.
113    pub fn revalidate<T: Transport>(&self, t: &mut T) -> Result<(), Error<T::Error>> {
114        self.receiver.revalidate(t).map_err(Error::Receiver)?;
115        let mut banner = [0; layout::BANNER.len];
116        drive::read_memory(t, layout::BANNER.start as u32, &mut banner).map_err(Error::Read)?;
117        if banner != self.banner {
118            return Err(Error::Changed);
119        }
120        Ok(())
121    }
122    /// Validate a recovery image, bypassing model/family and rollback policy.
123    /// No installed image, Current file or backup is needed.
124    pub fn prepare(&self, image: &[u8]) -> Result<PreparedRecovery, PrepareError> {
125        if image.len() != layout::IMAGE_SIZE {
126            return Err(PrepareError::Size(image.len()));
127        }
128        if !image::cmac::verify(image) {
129            return Err(PrepareError::Integrity);
130        }
131        if image::is_drive_read(image) {
132            return Err(PrepareError::DriveRead);
133        }
134        let chip = image::detect_chip(image)
135            .map_err(|_| PrepareError::UnknownChip)?
136            .family;
137        if chip != self.chip {
138            return Err(PrepareError::ChipMismatch {
139                image: chip,
140                drive: self.chip,
141            });
142        }
143        Ok(PreparedRecovery {
144            receiver: self.clone(),
145            plan: receiver::TransferPlan::new(
146                &self.receiver,
147                image,
148                DriveClass::of(self.chip),
149                crate::update::flash::ExecutionMode::Recovery,
150            )?,
151        })
152    }
153}
154
155/// A recovery target validated against a live receiver, without family policy.
156#[derive(Debug, Clone)]
157pub struct PreparedRecovery {
158    receiver: Receiver,
159    plan: receiver::TransferPlan,
160}
161impl PreparedRecovery {
162    /// Required wire encoding.
163    pub fn encoding(&self) -> receiver::Encoding {
164        self.receiver.encoding()
165    }
166    /// Receiver state at preparation.
167    pub fn mode(&self) -> receiver::Mode {
168        self.receiver.mode()
169    }
170    /// Bytes transmitted; the canonical image remains separate for verification.
171    pub fn transfer(&self) -> &[u8] {
172        self.plan.transfer()
173    }
174    /// Immutable receiver-derived transfer and PREPARE plan.
175    pub fn plan(&self) -> &receiver::TransferPlan {
176        &self.plan
177    }
178    /// Revalidate, PREPARE, stream and verify, then confirm the target started.
179    /// A PREPARE error is reported through `FlashRuntime::recovery_entry_failed`
180    /// and recovery continues once. STREAM errors remain fatal; no writes retry.
181    /// No backup, full pre-read, model or family gate is performed.
182    pub fn flash<R, W, H>(
183        &self,
184        io: FlashTransport<'_, R, W>,
185        runtime: &mut H,
186    ) -> Result<Verified, Error<W::Error>>
187    where
188        W: Transport,
189        R: Transport<Error = W::Error>,
190        H: FlashRuntime<W::Error>,
191    {
192        self.flash_completed(io, runtime)?
193            .into_verified()
194            .map_err(Error::Flash)
195    }
196
197    /// Execute recovery and require exact target startup, preserving limited
198    /// read-back as structured evidence for the host to report.
199    pub fn flash_completed<R, W, H>(
200        &self,
201        io: FlashTransport<'_, R, W>,
202        runtime: &mut H,
203    ) -> Result<crate::update::Completion, Error<W::Error>>
204    where
205        W: Transport,
206        R: Transport<Error = W::Error>,
207        H: FlashRuntime<W::Error>,
208    {
209        let mut banner = [0; layout::BANNER.len];
210        drive::read_memory(io.reads, layout::BANNER.start as u32, &mut banner)
211            .map_err(Error::Read)?;
212        if banner != self.receiver.banner {
213            return Err(Error::Changed);
214        }
215        self.plan
216            .execute_completed(io, runtime)
217            .map_err(|e| match e {
218                receiver::ExecuteError::Receiver(e) => Error::Receiver(e),
219                receiver::ExecuteError::Flash(e) => Error::Flash(e),
220            })
221    }
222}