opentitanlib/rescue/
serial.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use 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    // The version is encoded in a u16 with the major as the high byte and minor as the low byte.
29    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        // Upon entry, rescue is going to tell us what mode it is.
86        // Consume and discard.
87        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            // Make the older version of the protocol compliant with the current expectations of
94            // the client.  Since the rescue client now expects the chip to wait after transfers
95            // rather than automatically reset, we put older implementations into WAIT mode.
96            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        // Make sure the requested rate is a known rate.
110        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        // Request to change rates.  We don't use `set_mode` here because changing
121        // rates isn't a "mode" request and doesn't respond the same way.
122        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        // Send the new rate and check for success.
133        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        // Change our side of the connection to the new rate.
142        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}