opentitanlib/rescue/
serial.rs1use anyhow::Result;
6use std::rc::Rc;
7use std::time::Duration;
8
9use crate::app::{TransportWrapper, UartRx};
10use crate::chip::boot_log::BootLog;
11use crate::chip::boot_svc::{BootSlot, BootSvc, OwnershipActivateRequest, OwnershipUnlockRequest};
12use crate::chip::device_id::DeviceId;
13use crate::io::console::ConsoleExt;
14use crate::io::console::ext::{PassFail, PassFailResult};
15use crate::io::uart::Uart;
16use crate::regex;
17use crate::rescue::RescueError;
18use crate::rescue::xmodem::Xmodem;
19
20pub struct RescueSerial {
21 uart: Rc<dyn Uart>,
22 reset_delay: Duration,
23 enter_delay: Duration,
24}
25
26impl RescueSerial {
27 const ONE_SECOND: Duration = Duration::from_secs(1);
28 pub const RESCUE: [u8; 4] = *b"RESQ";
29 pub const RESCUE_B: [u8; 4] = *b"RESB";
30 pub const REBOOT: [u8; 4] = *b"REBO";
31 pub const BAUD: [u8; 4] = *b"BAUD";
32 pub const BOOT_LOG: [u8; 4] = *b"BLOG";
33 pub const BOOT_SVC_REQ: [u8; 4] = *b"BREQ";
34 pub const BOOT_SVC_RSP: [u8; 4] = *b"BRSP";
35 pub const OWNER_BLOCK: [u8; 4] = *b"OWNR";
36 pub const GET_OWNER_PAGE0: [u8; 4] = *b"OPG0";
37 pub const GET_OWNER_PAGE1: [u8; 4] = *b"OPG1";
38 pub const OT_ID: [u8; 4] = *b"OTID";
39 pub const ERASE_OWNER: [u8; 4] = *b"KLBR";
40 pub const WAIT: [u8; 4] = *b"WAIT";
41
42 const BAUD_115K: [u8; 4] = *b"115K";
43 const BAUD_230K: [u8; 4] = *b"230K";
44 const BAUD_460K: [u8; 4] = *b"460K";
45 const BAUD_921K: [u8; 4] = *b"921K";
46 const BAUD_1M33: [u8; 4] = *b"1M33";
47 const BAUD_1M50: [u8; 4] = *b"1M50";
48
49 pub fn new(uart: Rc<dyn Uart>) -> Self {
50 RescueSerial {
51 uart,
52 reset_delay: Duration::from_millis(50),
53 enter_delay: Duration::from_secs(5),
54 }
55 }
56
57 pub fn enter(&self, transport: &TransportWrapper, reset_target: bool) -> Result<()> {
58 log::info!("Setting serial break to trigger rescue mode.");
59 self.uart.set_break(true)?;
60 if reset_target {
61 transport.reset_with_delay(UartRx::Clear, self.reset_delay)?;
62 }
63 (&self.uart)
64 .logged()
65 .wait_for_line("rescue:", self.enter_delay)?;
66 log::info!("Rescue triggered. clearing serial break.");
67 self.uart.set_break(false)?;
68 let _ = (&self.uart)
71 .logged()
72 .wait_for_line(PassFail("ok:", "error:"), Self::ONE_SECOND);
73 Ok(())
74 }
75
76 pub fn set_baud(&self, baud: u32) -> Result<()> {
77 let symbol = match baud {
79 115200 => Self::BAUD_115K,
80 230400 => Self::BAUD_230K,
81 460800 => Self::BAUD_460K,
82 921600 => Self::BAUD_921K,
83 1333333 => Self::BAUD_1M33,
84 1500000 => Self::BAUD_1M50,
85 _ => return Err(RescueError::BadMode(format!("Unsupported badrate {baud}")).into()),
86 };
87
88 self.uart.write(&Self::BAUD)?;
91 self.uart.write(b"\r")?;
92 if let PassFailResult::Fail(result) = (&self.uart)
93 .logged()
94 .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
95 {
96 return Err(RescueError::BadMode(result[0].clone()).into());
97 }
98
99 self.uart.write(&symbol)?;
101 if let PassFailResult::Fail(result) = (&self.uart)
102 .logged()
103 .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
104 {
105 return Err(RescueError::BadMode(result[0].clone()).into());
106 }
107 self.uart.set_baudrate(baud)?;
109 Ok(())
110 }
111
112 pub fn set_mode(&self, mode: [u8; 4]) -> Result<()> {
113 self.uart.write(&mode)?;
114 let enter = b'\r';
115 self.uart.write(std::slice::from_ref(&enter))?;
116 let mode = std::str::from_utf8(&mode)?;
117 (&self.uart)
118 .logged()
119 .wait_for_line(format!("mode: {mode}").as_str(), Self::ONE_SECOND)?;
120 if let PassFailResult::Fail(result) = (&self.uart)
121 .logged()
122 .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
123 {
124 return Err(RescueError::BadMode(result[0].clone()).into());
125 }
126 Ok(())
127 }
128
129 pub fn wait(&self) -> Result<()> {
130 self.set_mode(Self::WAIT)?;
131 Ok(())
132 }
133
134 pub fn reboot(&self) -> Result<()> {
135 self.set_mode(Self::REBOOT)?;
136 Ok(())
137 }
138
139 pub fn update_firmware(&self, slot: BootSlot, image: &[u8]) -> Result<()> {
140 self.set_mode(if slot == BootSlot::SlotB {
141 Self::RESCUE_B
142 } else {
143 Self::RESCUE
144 })?;
145 let xm = Xmodem::new();
146 xm.send(&*self.uart, image)?;
147 Ok(())
148 }
149
150 pub fn get_raw(&self, mode: [u8; 4]) -> Result<Vec<u8>> {
151 self.set_mode(mode)?;
152 let mut data = Vec::new();
153 let xm = Xmodem::new();
154 xm.receive(&*self.uart, &mut data)?;
155 Ok(data)
156 }
157
158 pub fn get_boot_log(&self) -> Result<BootLog> {
159 let blog = self.get_raw(Self::BOOT_LOG)?;
160 Ok(BootLog::try_from(blog.as_slice())?)
161 }
162
163 pub fn get_boot_svc(&self) -> Result<BootSvc> {
164 let bsvc = self.get_raw(Self::BOOT_SVC_RSP)?;
165 Ok(BootSvc::try_from(bsvc.as_slice())?)
166 }
167
168 pub fn get_device_id(&self) -> Result<DeviceId> {
169 let id = self.get_raw(Self::OT_ID)?;
170 DeviceId::read(&mut std::io::Cursor::new(&id))
171 }
172
173 pub fn set_boot_svc_raw(&self, data: &[u8]) -> Result<()> {
174 self.set_mode(Self::BOOT_SVC_REQ)?;
175 let xm = Xmodem::new();
176 xm.send(&*self.uart, data)?;
177 Ok(())
178 }
179
180 pub fn set_next_bl0_slot(&self, primary: BootSlot, next: BootSlot) -> Result<()> {
181 let message = BootSvc::next_boot_bl0_slot(primary, next);
182 let data = message.to_bytes()?;
183 self.set_boot_svc_raw(&data)
184 }
185
186 pub fn ownership_unlock(&self, unlock: OwnershipUnlockRequest) -> Result<()> {
187 let message = BootSvc::ownership_unlock(unlock);
188 let data = message.to_bytes()?;
189 self.set_boot_svc_raw(&data)
190 }
191
192 pub fn ownership_activate(&self, activate: OwnershipActivateRequest) -> Result<()> {
193 let message = BootSvc::ownership_activate(activate);
194 let data = message.to_bytes()?;
195 self.set_boot_svc_raw(&data)
196 }
197
198 pub fn set_owner_config(&self, data: &[u8]) -> Result<()> {
199 self.set_mode(Self::OWNER_BLOCK)?;
200 let xm = Xmodem::new();
201 xm.send(&*self.uart, data)?;
202 Ok(())
203 }
204
205 pub fn erase_owner(&self) -> Result<()> {
206 self.set_mode(Self::ERASE_OWNER)?;
207 Ok(())
208 }
209}