opentitanlib/rescue/
mod.rs1use 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::uart::UartParams;
14use crate::with_unknown;
15
16pub mod serial;
17pub mod xmodem;
18
19pub use serial::RescueSerial;
20
21#[derive(Debug, Error)]
22pub enum RescueError {
23 #[error("bad mode: {0}")]
24 BadMode(String),
25 #[error("configuration error: {0}")]
26 Configuration(String),
27}
28
29#[derive(ValueEnum, Default, Debug, Clone, Copy, PartialEq)]
30pub enum RescueProtocol {
31 #[default]
32 Xmodem,
33 UsbDfu,
34 SpiDfu,
35}
36
37#[derive(ValueEnum, Default, Debug, Clone, Copy, PartialEq)]
38pub enum RescueTrigger {
39 #[default]
40 SerialBreak,
41 Gpio,
42 Strap,
43}
44
45#[derive(Clone, Default, Debug, Args)]
46pub struct RescueParams {
47 #[arg(short, long, value_enum, default_value_t = RescueProtocol::Xmodem)]
49 pub protocol: RescueProtocol,
50 #[arg(short, long, value_enum, default_value_t = RescueTrigger::SerialBreak)]
51 pub trigger: RescueTrigger,
52 #[arg(short, long, default_value = "")]
53 pub value: String,
54 #[command(flatten)]
55 uart: UartParams,
56}
57
58impl RescueParams {
59 pub fn create(&self, transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
60 match self.protocol {
61 RescueProtocol::Xmodem => self.create_serial(transport),
62 RescueProtocol::UsbDfu => self.create_usbdfu(transport),
63 RescueProtocol::SpiDfu => self.create_spidfu(transport),
64 }
65 }
66 fn create_serial(&self, transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
67 ensure!(
68 self.trigger == RescueTrigger::SerialBreak,
69 RescueError::Configuration(format!(
70 "Xmodem does not support trigger {:?}",
71 self.trigger
72 ))
73 );
74 ensure!(
75 self.value.is_empty(),
76 RescueError::Configuration(format!(
77 "Xmodem does not support trigger value {:?}",
78 self.value
79 ))
80 );
81
82 Ok(Box::new(RescueSerial::new(self.uart.create(transport)?)))
83 }
84 fn create_usbdfu(&self, _transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
85 unimplemented!()
86 }
87 fn create_spidfu(&self, _transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
88 unimplemented!()
89 }
90}
91
92with_unknown! {
93pub enum RescueMode: u32 {
94 Rescue = u32::from_be_bytes(*b"RESQ"),
95 RescueB = u32::from_be_bytes(*b"RESB"),
96 BootLog = u32::from_be_bytes(*b"BLOG"),
97 BootSvcReq = u32::from_be_bytes(*b"BREQ"),
98 BootSvcRsp = u32::from_be_bytes(*b"BRSP"),
99 OwnerBlock = u32::from_be_bytes(*b"OWNR"),
100 GetOwnerPage0 = u32::from_be_bytes(*b"OPG0"),
101 GetOwnerPage1 = u32::from_be_bytes(*b"OPG1"),
102 DeviceId = u32::from_be_bytes(*b"OTID"),
103 EraseOwner = u32::from_be_bytes(*b"KLBR"),
104}
105}
106
107pub trait Rescue {
108 fn enter(&self, transport: &TransportWrapper, reset_target: bool) -> Result<()>;
109 fn set_mode(&self, mode: RescueMode) -> Result<()>;
110 fn send(&self, data: &[u8]) -> Result<()>;
111 fn recv(&self) -> Result<Vec<u8>>;
112
113 fn set_speed(&self, speed: u32) -> Result<u32>;
115 fn wait(&self) -> Result<()>;
116 fn reboot(&self) -> Result<()>;
117
118 fn get_raw(&self, mode: RescueMode) -> Result<Vec<u8>> {
119 self.set_mode(mode)?;
120 self.recv()
121 }
122
123 fn set_raw(&self, mode: RescueMode, data: &[u8]) -> Result<()> {
124 self.set_mode(mode)?;
125 self.send(data)
126 }
127
128 fn update_firmware(&self, slot: BootSlot, image: &[u8]) -> Result<()> {
129 let mode = if slot == BootSlot::SlotB {
130 RescueMode::RescueB
131 } else {
132 RescueMode::Rescue
133 };
134 self.set_raw(mode, image)
135 }
136
137 fn get_boot_log(&self) -> Result<BootLog> {
138 let blog = self.get_raw(RescueMode::BootLog)?;
139 Ok(BootLog::try_from(blog.as_slice())?)
140 }
141
142 fn get_boot_svc(&self) -> Result<BootSvc> {
143 let bsvc = self.get_raw(RescueMode::BootSvcRsp)?;
144 Ok(BootSvc::try_from(bsvc.as_slice())?)
145 }
146
147 fn get_device_id(&self) -> Result<DeviceId> {
148 let id = self.get_raw(RescueMode::DeviceId)?;
149 DeviceId::read(&mut std::io::Cursor::new(&id))
150 }
151
152 fn set_next_bl0_slot(&self, primary: BootSlot, next: BootSlot) -> Result<()> {
153 let message = BootSvc::next_boot_bl0_slot(primary, next);
154 self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
155 }
156
157 fn ownership_unlock(&self, unlock: OwnershipUnlockRequest) -> Result<()> {
158 let message = BootSvc::ownership_unlock(unlock);
159 self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
160 }
161
162 fn ownership_activate(&self, activate: OwnershipActivateRequest) -> Result<()> {
163 let message = BootSvc::ownership_activate(activate);
164 self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
165 }
166
167 fn set_owner_config(&self, data: &[u8]) -> Result<()> {
168 self.set_raw(RescueMode::OwnerBlock, data)
169 }
170
171 fn erase_owner(&self) -> Result<()> {
172 self.set_raw(RescueMode::EraseOwner, &[])
173 }
174}