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).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            .logged()
89            .wait_for_line(PassFail("ok:", "error:"), Self::ONE_SECOND);
90
91        if version < Self::VERSION_1_0 {
92            // Make the older version of the protocol compliant with the current expectations of
93            // the client.  Since the rescue client now expects the chip to wait after transfers
94            // rather than automatically reset, we put older implementations into WAIT mode.
95            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        // Make sure the requested rate is a known rate.
109        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        // Request to change rates.  We don't use `set_mode` here because changing
120        // rates isn't a "mode" request and doesn't respond the same way.
121        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        // Send the new rate and check for success.
131        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        // Change our side of the connection to the new rate.
139        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}