1use anyhow::{Result, ensure};
6use clap::{Args, ValueEnum};
7use std::time::Duration;
8use thiserror::Error;
9
10use crate::app::TransportWrapper;
11use crate::chip::boot_log::BootLog;
12use crate::chip::boot_svc::{BootSlot, BootSvc, OwnershipActivateRequest, OwnershipUnlockRequest};
13use crate::chip::device_id::DeviceId;
14use crate::io::gpio::PinMode;
15use crate::io::spi::SpiParams;
16use crate::io::uart::UartParams;
17use crate::util::parse_int::ParseInt;
18use crate::with_unknown;
19
20pub mod dfu;
21pub mod serial;
22pub mod spidfu;
23pub mod usbdfu;
24pub mod xmodem;
25
26pub use serial::RescueSerial;
27pub use spidfu::SpiDfu;
28pub use usbdfu::UsbDfu;
29
30#[derive(Debug, Error)]
31pub enum RescueError {
32 #[error("bad mode: {0}")]
33 BadMode(String),
34 #[error("configuration error: {0}")]
35 Configuration(String),
36 #[error("bad protocol: {0}")]
37 BadProtocol(String),
38 #[error("not found: {0}")]
39 NotFound(String),
40}
41
42#[derive(ValueEnum, Default, Debug, Clone, Copy, PartialEq)]
43pub enum RescueProtocol {
44 #[default]
45 Xmodem,
46 UsbDfu,
47 SpiDfu,
48}
49
50#[derive(ValueEnum, Default, Debug, Clone, Copy, PartialEq)]
51pub enum RescueTrigger {
52 #[default]
53 SerialBreak,
54 Gpio,
55 Strap,
56}
57
58#[derive(Clone, Debug, Args)]
59pub struct RescueParams {
60 #[arg(short, long, value_enum, default_value_t = RescueProtocol::Xmodem)]
62 pub protocol: RescueProtocol,
63 #[arg(short, long, value_enum, default_value_t = RescueTrigger::SerialBreak)]
64 pub trigger: RescueTrigger,
65 #[arg(short, long, default_value = "")]
66 pub value: String,
67 #[command(flatten)]
68 pub spi: SpiParams,
69 #[command(flatten)]
70 pub uart: UartParams,
71 #[arg(long)]
72 pub usb_serial: Option<String>,
73 #[arg(long, value_parser = humantime::parse_duration, default_value = "15s")]
74 pub enter_delay: Duration,
75}
76
77impl RescueParams {
78 pub fn create(&self, transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
79 match self.protocol {
80 RescueProtocol::Xmodem => self.create_serial(transport),
81 RescueProtocol::UsbDfu => self.create_usbdfu(transport),
82 RescueProtocol::SpiDfu => self.create_spidfu(transport),
83 }
84 }
85
86 pub fn set_trigger(&self, transport: &TransportWrapper, trigger: bool) -> Result<()> {
87 match self.trigger {
88 RescueTrigger::SerialBreak => unimplemented!(),
89 RescueTrigger::Gpio => self.set_gpio(transport, trigger),
90 RescueTrigger::Strap => self.set_strap(transport, trigger),
91 }
92 }
93
94 fn create_serial(&self, transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
95 ensure!(
96 self.trigger == RescueTrigger::SerialBreak,
97 RescueError::Configuration(format!(
98 "Xmodem does not support trigger {:?}",
99 self.trigger
100 ))
101 );
102 ensure!(
103 self.value.is_empty(),
104 RescueError::Configuration(format!(
105 "Xmodem does not support trigger value {:?}",
106 self.value
107 ))
108 );
109 Ok(Box::new(RescueSerial::new(self.uart.create(transport)?)))
110 }
111
112 fn create_usbdfu(&self, _transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
113 ensure!(
114 self.trigger != RescueTrigger::SerialBreak,
115 RescueError::Configuration(format!(
116 "Usb-DFU does not support trigger {:?}",
117 self.trigger
118 ))
119 );
120 ensure!(
121 !self.value.is_empty(),
122 RescueError::Configuration("Usb-DFU requires a trigger value".into())
123 );
124 Ok(Box::new(UsbDfu::new(self.clone())))
125 }
126
127 fn create_spidfu(&self, transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
128 ensure!(
129 self.trigger != RescueTrigger::SerialBreak,
130 RescueError::Configuration(format!(
131 "Usb-DFU does not support trigger {:?}",
132 self.trigger
133 ))
134 );
135 ensure!(
136 !self.value.is_empty(),
137 RescueError::Configuration("Usb-DFU requires a trigger value".into())
138 );
139 Ok(Box::new(SpiDfu::new(
140 self.spi.create(transport, "BOOTSTRAP")?,
141 self.clone(),
142 )))
143 }
144
145 fn set_strap(&self, transport: &TransportWrapper, trigger: bool) -> Result<()> {
146 let mut value = if trigger {
147 u8::from_str(&self.value)?
148 } else {
149 0u8
150 };
151 for strap in ["SW_STRAP0", "SW_STRAP1", "SW_STRAP2"] {
152 let pin = transport.gpio_pin(strap)?;
153 match value & 3 {
154 0 => pin.write(false)?,
155 1 | 2 => log::error!("weak straps not supported yet"),
156 3 => pin.write(true)?,
157 _ => unreachable!(),
158 };
159 value >>= 2;
160 }
161 Ok(())
162 }
163
164 fn parse_pin(&self) -> Result<(&str, bool)> {
165 if let Some(pin) = self.value.strip_prefix('+') {
166 Ok((pin, true))
167 } else if let Some(pin) = self.value.strip_prefix('-') {
168 Ok((pin, false))
169 } else {
170 Ok((self.value.as_str(), true))
171 }
172 }
173
174 fn set_gpio(&self, transport: &TransportWrapper, trigger: bool) -> Result<()> {
175 let (name, mut value) = self.parse_pin()?;
176 if !trigger {
177 value = !value
178 };
179 let pin = transport.gpio_pin(name)?;
180 pin.set(Some(PinMode::PushPull), Some(value), None, None)?;
181 Ok(())
182 }
183}
184
185with_unknown! {
186pub enum RescueMode: u32 {
187 Rescue = u32::from_be_bytes(*b"RESQ"),
188 RescueB = u32::from_be_bytes(*b"RESB"),
189 BootLog = u32::from_be_bytes(*b"BLOG"),
190 BootSvcReq = u32::from_be_bytes(*b"BREQ"),
191 BootSvcRsp = u32::from_be_bytes(*b"BRSP"),
192 OwnerBlock = u32::from_be_bytes(*b"OWNR"),
193 GetOwnerPage0 = u32::from_be_bytes(*b"OPG0"),
194 GetOwnerPage1 = u32::from_be_bytes(*b"OPG1"),
195 DeviceId = u32::from_be_bytes(*b"OTID"),
196 EraseOwner = u32::from_be_bytes(*b"KLBR"),
197}
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
201pub enum EntryMode {
203 Reset,
205 Reboot,
207 None,
209}
210
211pub trait Rescue {
212 fn enter(&self, transport: &TransportWrapper, mode: EntryMode) -> Result<()>;
213 fn set_mode(&self, mode: RescueMode) -> Result<()>;
214 fn send(&self, data: &[u8]) -> Result<()>;
215 fn recv(&self) -> Result<Vec<u8>>;
216
217 fn set_speed(&self, speed: u32) -> Result<u32>;
219 fn reboot(&self) -> Result<()>;
220
221 fn get_raw(&self, mode: RescueMode) -> Result<Vec<u8>> {
222 self.set_mode(mode)?;
223 self.recv()
224 }
225
226 fn set_raw(&self, mode: RescueMode, data: &[u8]) -> Result<()> {
227 self.set_mode(mode)?;
228 self.send(data)
229 }
230
231 fn update_firmware(&self, slot: BootSlot, image: &[u8]) -> Result<()> {
232 let mode = if slot == BootSlot::SlotB {
233 RescueMode::RescueB
234 } else {
235 RescueMode::Rescue
236 };
237 self.set_raw(mode, image)
238 }
239
240 fn get_boot_log(&self) -> Result<BootLog> {
241 let blog = self.get_raw(RescueMode::BootLog)?;
242 Ok(BootLog::try_from(blog.as_slice())?)
243 }
244
245 fn get_boot_svc(&self) -> Result<BootSvc> {
246 let bsvc = self.get_raw(RescueMode::BootSvcRsp)?;
247 Ok(BootSvc::try_from(bsvc.as_slice())?)
248 }
249
250 fn get_device_id(&self) -> Result<DeviceId> {
251 let id = self.get_raw(RescueMode::DeviceId)?;
252 DeviceId::read(&mut std::io::Cursor::new(&id))
253 }
254
255 fn empty(&self, payload: &[u32]) -> Result<()> {
256 let message = BootSvc::empty(payload);
257 self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
258 }
259
260 fn set_min_bl0_sec_ver(&self, ver: u32) -> Result<()> {
261 let message = BootSvc::min_bl0_sec_ver(ver);
262 self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
263 }
264
265 fn set_next_bl0_slot(&self, primary: BootSlot, next: BootSlot) -> Result<()> {
266 let message = BootSvc::next_boot_bl0_slot(primary, next);
267 self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
268 }
269
270 fn ownership_unlock(&self, unlock: OwnershipUnlockRequest) -> Result<()> {
271 let message = BootSvc::ownership_unlock(unlock);
272 self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
273 }
274
275 fn ownership_activate(&self, activate: OwnershipActivateRequest) -> Result<()> {
276 let message = BootSvc::ownership_activate(activate);
277 self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
278 }
279
280 fn set_owner_config(&self, data: &[u8]) -> Result<()> {
281 self.set_raw(RescueMode::OwnerBlock, data)
282 }
283
284 fn erase_owner(&self) -> Result<()> {
285 self.set_raw(RescueMode::EraseOwner, &[])
286 }
287}