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 anyhow::Result;
6use std::rc::Rc;
7use std::time::Duration;
8
9use crate::app::{TransportWrapper, UartRx};
10use crate::io::console::ConsoleExt;
11use crate::io::console::ext::{PassFail, PassFailResult};
12use crate::io::uart::Uart;
13use crate::regex;
14use crate::rescue::xmodem::Xmodem;
15use crate::rescue::{Rescue, RescueError, RescueMode};
16
17pub struct RescueSerial {
18    uart: Rc<dyn Uart>,
19    reset_delay: Duration,
20    enter_delay: Duration,
21}
22
23impl RescueSerial {
24    const ONE_SECOND: Duration = Duration::from_secs(1);
25    pub const REBOOT: RescueMode = RescueMode(u32::from_be_bytes(*b"REBO"));
26    pub const BAUD: RescueMode = RescueMode(u32::from_be_bytes(*b"BAUD"));
27    pub const WAIT: RescueMode = RescueMode(u32::from_be_bytes(*b"WAIT"));
28
29    const BAUD_115K: [u8; 4] = *b"115K";
30    const BAUD_230K: [u8; 4] = *b"230K";
31    const BAUD_460K: [u8; 4] = *b"460K";
32    const BAUD_921K: [u8; 4] = *b"921K";
33    const BAUD_1M33: [u8; 4] = *b"1M33";
34    const BAUD_1M50: [u8; 4] = *b"1M50";
35
36    pub fn new(uart: Rc<dyn Uart>) -> Self {
37        RescueSerial {
38            uart,
39            reset_delay: Duration::from_millis(50),
40            enter_delay: Duration::from_secs(5),
41        }
42    }
43}
44
45impl Rescue for RescueSerial {
46    fn enter(&self, transport: &TransportWrapper, reset_target: bool) -> Result<()> {
47        log::info!("Setting serial break to trigger rescue mode.");
48        self.uart.set_break(true)?;
49        if reset_target {
50            transport.reset_with_delay(UartRx::Clear, self.reset_delay)?;
51        }
52        (&self.uart)
53            .logged()
54            .wait_for_line("rescue:", self.enter_delay)?;
55        log::info!("Rescue triggered. clearing serial break.");
56        self.uart.set_break(false)?;
57        // Upon entry, rescue is going to tell us what mode it is.
58        // Consume and discard.
59        let _ = (&self.uart)
60            .logged()
61            .wait_for_line(PassFail("ok:", "error:"), Self::ONE_SECOND);
62        Ok(())
63    }
64
65    fn set_speed(&self, baud: u32) -> Result<u32> {
66        // Make sure the requested rate is a known rate.
67        let symbol = match baud {
68            115200 => Self::BAUD_115K,
69            230400 => Self::BAUD_230K,
70            460800 => Self::BAUD_460K,
71            921600 => Self::BAUD_921K,
72            1333333 => Self::BAUD_1M33,
73            1500000 => Self::BAUD_1M50,
74            _ => return Err(RescueError::BadMode(format!("Unsupported badrate {baud}")).into()),
75        };
76
77        // Request to change rates.  We don't use `set_mode` here because changing
78        // rates isn't a "mode" request and doesn't respond the same way.
79        self.uart.write(&Self::BAUD.0.to_be_bytes())?;
80        self.uart.write(b"\r")?;
81        if let PassFailResult::Fail(result) = (&self.uart)
82            .logged()
83            .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
84        {
85            return Err(RescueError::BadMode(result[0].clone()).into());
86        }
87
88        // Send the new rate and check for success.
89        self.uart.write(&symbol)?;
90        if let PassFailResult::Fail(result) = (&self.uart)
91            .logged()
92            .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
93        {
94            return Err(RescueError::BadMode(result[0].clone()).into());
95        }
96        // Change our side of the connection to the new rate.
97        let old = self.uart.get_baudrate()?;
98        self.uart.set_baudrate(baud)?;
99        Ok(old)
100    }
101
102    fn set_mode(&self, mode: RescueMode) -> Result<()> {
103        let mode = mode.0.to_be_bytes();
104        self.uart.write(&mode)?;
105        let enter = b'\r';
106        self.uart.write(std::slice::from_ref(&enter))?;
107        let mode = std::str::from_utf8(&mode)?;
108        (&self.uart)
109            .logged()
110            .wait_for_line(format!("mode: {mode}").as_str(), Self::ONE_SECOND)?;
111        if let PassFailResult::Fail(result) = (&self.uart)
112            .logged()
113            .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
114        {
115            return Err(RescueError::BadMode(result[0].clone()).into());
116        }
117        Ok(())
118    }
119
120    fn wait(&self) -> Result<()> {
121        self.set_mode(Self::WAIT)?;
122        Ok(())
123    }
124
125    fn reboot(&self) -> Result<()> {
126        self.set_mode(Self::REBOOT)?;
127        Ok(())
128    }
129
130    fn send(&self, data: &[u8]) -> Result<()> {
131        let xm = Xmodem::new();
132        xm.send(&*self.uart, data)?;
133        Ok(())
134    }
135
136    fn recv(&self) -> Result<Vec<u8>> {
137        let mut data = Vec::new();
138        let xm = Xmodem::new();
139        xm.receive(&*self.uart, &mut data)?;
140        Ok(data)
141    }
142}