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