opentitanlib/rescue/
serial.rs1use std::cell::Cell;
6use std::rc::Rc;
7use std::time::Duration;
8
9use anyhow::Result;
10use regex::Regex;
11
12use crate::app::{TransportWrapper, UartRx};
13use crate::io::console::ConsoleExt;
14use crate::io::console::ext::{PassFail, PassFailResult};
15use crate::io::uart::Uart;
16use crate::regex;
17use crate::rescue::xmodem::Xmodem;
18use crate::rescue::{EntryMode, Rescue, RescueError, RescueMode};
19
20pub struct RescueSerial {
21 uart: Rc<dyn Uart>,
22 reset_delay: Duration,
23 enter_delay: Duration,
24 version: Cell<u16>,
25}
26
27impl RescueSerial {
28 const VERSION_1_0: u16 = 0x0100;
30 const VERSION_2_0: u16 = 0x0200;
31
32 const ONE_SECOND: Duration = Duration::from_secs(1);
33 pub const REBOOT: RescueMode = RescueMode(u32::from_be_bytes(*b"REBO"));
34 pub const BAUD: RescueMode = RescueMode(u32::from_be_bytes(*b"BAUD"));
35 pub const WAIT: RescueMode = RescueMode(u32::from_be_bytes(*b"WAIT"));
36
37 const BAUD_115K: [u8; 4] = *b"115K";
38 const BAUD_230K: [u8; 4] = *b"230K";
39 const BAUD_460K: [u8; 4] = *b"460K";
40 const BAUD_921K: [u8; 4] = *b"921K";
41 const BAUD_1M33: [u8; 4] = *b"1M33";
42 const BAUD_1M50: [u8; 4] = *b"1M50";
43
44 pub fn new(uart: Rc<dyn Uart>) -> Self {
45 RescueSerial {
46 uart,
47 reset_delay: Duration::from_millis(50),
48 enter_delay: Duration::from_secs(5),
49 version: Cell::default(),
50 }
51 }
52}
53
54impl Rescue for RescueSerial {
55 fn enter(&self, transport: &TransportWrapper, mode: EntryMode) -> Result<()> {
56 log::info!("Setting serial break to trigger rescue mode (entry via {mode:?}).");
57 match mode {
58 EntryMode::Reset => {
59 self.uart.set_break(true)?;
60 transport.reset_with_delay(UartRx::Clear, self.reset_delay)?;
61 }
62 EntryMode::Reboot => {
63 self.reboot()?;
64 self.uart.set_break(true)?;
65 }
66 EntryMode::None => {
67 self.uart.set_break(true)?;
68 }
69 }
70 let version = (&self.uart).coverage().logged().wait_for_line(
71 Regex::new(r"rescue:(?:(\d+)\.(\d+))?.*\r\n")?,
72 self.enter_delay,
73 )?;
74 let version = if !version[1].is_empty() && !version[2].is_empty() {
75 let major = version[1].parse::<u16>()?;
76 let minor = version[2].parse::<u16>()?;
77 (major << 8) | minor
78 } else {
79 0
80 };
81 self.version.set(version);
82 log::info!("Rescue triggered (version={version:04x}). Clearing serial break.");
83 self.uart.set_break(false)?;
84
85 let _ = (&self.uart)
88 .coverage()
89 .logged()
90 .wait_for_line(PassFail("ok:", "error:"), Self::ONE_SECOND);
91
92 if version < Self::VERSION_1_0 {
93 self.set_mode(Self::WAIT)?;
97 }
98 if version >= Self::VERSION_2_0 {
99 Err(RescueError::BadProtocol(format!(
100 "This version of opentitantool does not support rescue protocol {version:04x}"
101 ))
102 .into())
103 } else {
104 Ok(())
105 }
106 }
107
108 fn set_speed(&self, baud: u32) -> Result<u32> {
109 let symbol = match baud {
111 115200 => Self::BAUD_115K,
112 230400 => Self::BAUD_230K,
113 460800 => Self::BAUD_460K,
114 921600 => Self::BAUD_921K,
115 1333333 => Self::BAUD_1M33,
116 1500000 => Self::BAUD_1M50,
117 _ => return Err(RescueError::BadMode(format!("Unsupported badrate {baud}")).into()),
118 };
119
120 self.uart.write(&Self::BAUD.0.to_be_bytes())?;
123 self.uart.write(b"\r")?;
124 if let PassFailResult::Fail(result) = (&self.uart)
125 .coverage()
126 .logged()
127 .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
128 {
129 return Err(RescueError::BadMode(result[0].clone()).into());
130 }
131
132 self.uart.write(&symbol)?;
134 if let PassFailResult::Fail(result) = (&self.uart)
135 .coverage()
136 .logged()
137 .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
138 {
139 return Err(RescueError::BadMode(result[0].clone()).into());
140 }
141 let old = self.uart.get_baudrate()?;
143 self.uart.set_baudrate(baud)?;
144 Ok(old)
145 }
146
147 fn set_mode(&self, mode: RescueMode) -> Result<()> {
148 let mode = mode.0.to_be_bytes();
149 self.uart.write(&mode)?;
150 let enter = b'\r';
151 self.uart.write(std::slice::from_ref(&enter))?;
152 let mode = std::str::from_utf8(&mode)?;
153 (&self.uart)
154 .coverage()
155 .logged()
156 .wait_for_line(format!("mode: {mode}").as_str(), Self::ONE_SECOND)?;
157 if let PassFailResult::Fail(result) = (&self.uart)
158 .coverage()
159 .logged()
160 .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
161 {
162 return Err(RescueError::BadMode(format!("mode: {mode}\n{}", result[0])).into());
163 }
164 Ok(())
165 }
166
167 fn reboot(&self) -> Result<()> {
168 self.set_mode(Self::REBOOT)?;
169 Ok(())
170 }
171
172 fn send(&self, data: &[u8]) -> Result<()> {
173 let xm = Xmodem::new();
174 xm.send(&*self.uart, data)?;
175 Ok(())
176 }
177
178 fn recv(&self) -> Result<Vec<u8>> {
179 let mut data = Vec::new();
180 let xm = Xmodem::new();
181 xm.receive(&*self.uart, &mut data)?;
182 Ok(data)
183 }
184}