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).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 .logged()
89 .wait_for_line(PassFail("ok:", "error:"), Self::ONE_SECOND);
90
91 if version < Self::VERSION_1_0 {
92 self.set_mode(Self::WAIT)?;
96 }
97 if version >= Self::VERSION_2_0 {
98 Err(RescueError::BadProtocol(format!(
99 "This version of opentitantool does not support rescue protocol {version:04x}"
100 ))
101 .into())
102 } else {
103 Ok(())
104 }
105 }
106
107 fn set_speed(&self, baud: u32) -> Result<u32> {
108 let symbol = match baud {
110 115200 => Self::BAUD_115K,
111 230400 => Self::BAUD_230K,
112 460800 => Self::BAUD_460K,
113 921600 => Self::BAUD_921K,
114 1333333 => Self::BAUD_1M33,
115 1500000 => Self::BAUD_1M50,
116 _ => return Err(RescueError::BadMode(format!("Unsupported badrate {baud}")).into()),
117 };
118
119 self.uart.write(&Self::BAUD.0.to_be_bytes())?;
122 self.uart.write(b"\r")?;
123 if let PassFailResult::Fail(result) = (&self.uart)
124 .logged()
125 .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
126 {
127 return Err(RescueError::BadMode(result[0].clone()).into());
128 }
129
130 self.uart.write(&symbol)?;
132 if let PassFailResult::Fail(result) = (&self.uart)
133 .logged()
134 .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
135 {
136 return Err(RescueError::BadMode(result[0].clone()).into());
137 }
138 let old = self.uart.get_baudrate()?;
140 self.uart.set_baudrate(baud)?;
141 Ok(old)
142 }
143
144 fn set_mode(&self, mode: RescueMode) -> Result<()> {
145 let mode = mode.0.to_be_bytes();
146 self.uart.write(&mode)?;
147 let enter = b'\r';
148 self.uart.write(std::slice::from_ref(&enter))?;
149 let mode = std::str::from_utf8(&mode)?;
150 (&self.uart)
151 .logged()
152 .wait_for_line(format!("mode: {mode}").as_str(), Self::ONE_SECOND)?;
153 if let PassFailResult::Fail(result) = (&self.uart)
154 .logged()
155 .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
156 {
157 return Err(RescueError::BadMode(result[0].clone()).into());
158 }
159 Ok(())
160 }
161
162 fn reboot(&self) -> Result<()> {
163 self.set_mode(Self::REBOOT)?;
164 Ok(())
165 }
166
167 fn send(&self, data: &[u8]) -> Result<()> {
168 let xm = Xmodem::new();
169 xm.send(&*self.uart, data)?;
170 Ok(())
171 }
172
173 fn recv(&self) -> Result<Vec<u8>> {
174 let mut data = Vec::new();
175 let xm = Xmodem::new();
176 xm.receive(&*self.uart, &mut data)?;
177 Ok(data)
178 }
179}