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::chip::boot_log::BootLog;
11use crate::chip::boot_svc::{BootSlot, BootSvc, OwnershipActivateRequest, OwnershipUnlockRequest};
12use crate::chip::device_id::DeviceId;
13use crate::io::console::ConsoleExt;
14use crate::io::console::ext::{PassFail, PassFailResult};
15use crate::io::uart::Uart;
16use crate::regex;
17use crate::rescue::RescueError;
18use crate::rescue::xmodem::Xmodem;
19
20pub struct RescueSerial {
21    uart: Rc<dyn Uart>,
22    reset_delay: Duration,
23    enter_delay: Duration,
24}
25
26impl RescueSerial {
27    const ONE_SECOND: Duration = Duration::from_secs(1);
28    pub const RESCUE: [u8; 4] = *b"RESQ";
29    pub const RESCUE_B: [u8; 4] = *b"RESB";
30    pub const REBOOT: [u8; 4] = *b"REBO";
31    pub const BAUD: [u8; 4] = *b"BAUD";
32    pub const BOOT_LOG: [u8; 4] = *b"BLOG";
33    pub const BOOT_SVC_REQ: [u8; 4] = *b"BREQ";
34    pub const BOOT_SVC_RSP: [u8; 4] = *b"BRSP";
35    pub const OWNER_BLOCK: [u8; 4] = *b"OWNR";
36    pub const GET_OWNER_PAGE0: [u8; 4] = *b"OPG0";
37    pub const GET_OWNER_PAGE1: [u8; 4] = *b"OPG1";
38    pub const OT_ID: [u8; 4] = *b"OTID";
39    pub const ERASE_OWNER: [u8; 4] = *b"KLBR";
40    pub const WAIT: [u8; 4] = *b"WAIT";
41
42    const BAUD_115K: [u8; 4] = *b"115K";
43    const BAUD_230K: [u8; 4] = *b"230K";
44    const BAUD_460K: [u8; 4] = *b"460K";
45    const BAUD_921K: [u8; 4] = *b"921K";
46    const BAUD_1M33: [u8; 4] = *b"1M33";
47    const BAUD_1M50: [u8; 4] = *b"1M50";
48
49    pub fn new(uart: Rc<dyn Uart>) -> Self {
50        RescueSerial {
51            uart,
52            reset_delay: Duration::from_millis(50),
53            enter_delay: Duration::from_secs(5),
54        }
55    }
56
57    pub fn enter(&self, transport: &TransportWrapper, reset_target: bool) -> Result<()> {
58        log::info!("Setting serial break to trigger rescue mode.");
59        self.uart.set_break(true)?;
60        if reset_target {
61            transport.reset_with_delay(UartRx::Clear, self.reset_delay)?;
62        }
63        (&self.uart)
64            .logged()
65            .wait_for_line("rescue:", self.enter_delay)?;
66        log::info!("Rescue triggered. clearing serial break.");
67        self.uart.set_break(false)?;
68        // Upon entry, rescue is going to tell us what mode it is.
69        // Consume and discard.
70        let _ = (&self.uart)
71            .logged()
72            .wait_for_line(PassFail("ok:", "error:"), Self::ONE_SECOND);
73        Ok(())
74    }
75
76    pub fn set_baud(&self, baud: u32) -> Result<()> {
77        // Make sure the requested rate is a known rate.
78        let symbol = match baud {
79            115200 => Self::BAUD_115K,
80            230400 => Self::BAUD_230K,
81            460800 => Self::BAUD_460K,
82            921600 => Self::BAUD_921K,
83            1333333 => Self::BAUD_1M33,
84            1500000 => Self::BAUD_1M50,
85            _ => return Err(RescueError::BadMode(format!("Unsupported badrate {baud}")).into()),
86        };
87
88        // Request to change rates.  We don't use `set_mode` here because changing
89        // rates isn't a "mode" request and doesn't respond the same way.
90        self.uart.write(&Self::BAUD)?;
91        self.uart.write(b"\r")?;
92        if let PassFailResult::Fail(result) = (&self.uart)
93            .logged()
94            .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
95        {
96            return Err(RescueError::BadMode(result[0].clone()).into());
97        }
98
99        // Send the new rate and check for success.
100        self.uart.write(&symbol)?;
101        if let PassFailResult::Fail(result) = (&self.uart)
102            .logged()
103            .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
104        {
105            return Err(RescueError::BadMode(result[0].clone()).into());
106        }
107        // Change our side of the connection to the new rate.
108        self.uart.set_baudrate(baud)?;
109        Ok(())
110    }
111
112    pub fn set_mode(&self, mode: [u8; 4]) -> Result<()> {
113        self.uart.write(&mode)?;
114        let enter = b'\r';
115        self.uart.write(std::slice::from_ref(&enter))?;
116        let mode = std::str::from_utf8(&mode)?;
117        (&self.uart)
118            .logged()
119            .wait_for_line(format!("mode: {mode}").as_str(), Self::ONE_SECOND)?;
120        if let PassFailResult::Fail(result) = (&self.uart)
121            .logged()
122            .wait_for_line(PassFail("ok:", regex!("error:.*")), Self::ONE_SECOND)?
123        {
124            return Err(RescueError::BadMode(result[0].clone()).into());
125        }
126        Ok(())
127    }
128
129    pub fn wait(&self) -> Result<()> {
130        self.set_mode(Self::WAIT)?;
131        Ok(())
132    }
133
134    pub fn reboot(&self) -> Result<()> {
135        self.set_mode(Self::REBOOT)?;
136        Ok(())
137    }
138
139    pub fn update_firmware(&self, slot: BootSlot, image: &[u8]) -> Result<()> {
140        self.set_mode(if slot == BootSlot::SlotB {
141            Self::RESCUE_B
142        } else {
143            Self::RESCUE
144        })?;
145        let xm = Xmodem::new();
146        xm.send(&*self.uart, image)?;
147        Ok(())
148    }
149
150    pub fn get_raw(&self, mode: [u8; 4]) -> Result<Vec<u8>> {
151        self.set_mode(mode)?;
152        let mut data = Vec::new();
153        let xm = Xmodem::new();
154        xm.receive(&*self.uart, &mut data)?;
155        Ok(data)
156    }
157
158    pub fn get_boot_log(&self) -> Result<BootLog> {
159        let blog = self.get_raw(Self::BOOT_LOG)?;
160        Ok(BootLog::try_from(blog.as_slice())?)
161    }
162
163    pub fn get_boot_svc(&self) -> Result<BootSvc> {
164        let bsvc = self.get_raw(Self::BOOT_SVC_RSP)?;
165        Ok(BootSvc::try_from(bsvc.as_slice())?)
166    }
167
168    pub fn get_device_id(&self) -> Result<DeviceId> {
169        let id = self.get_raw(Self::OT_ID)?;
170        DeviceId::read(&mut std::io::Cursor::new(&id))
171    }
172
173    pub fn set_boot_svc_raw(&self, data: &[u8]) -> Result<()> {
174        self.set_mode(Self::BOOT_SVC_REQ)?;
175        let xm = Xmodem::new();
176        xm.send(&*self.uart, data)?;
177        Ok(())
178    }
179
180    pub fn set_next_bl0_slot(&self, primary: BootSlot, next: BootSlot) -> Result<()> {
181        let message = BootSvc::next_boot_bl0_slot(primary, next);
182        let data = message.to_bytes()?;
183        self.set_boot_svc_raw(&data)
184    }
185
186    pub fn ownership_unlock(&self, unlock: OwnershipUnlockRequest) -> Result<()> {
187        let message = BootSvc::ownership_unlock(unlock);
188        let data = message.to_bytes()?;
189        self.set_boot_svc_raw(&data)
190    }
191
192    pub fn ownership_activate(&self, activate: OwnershipActivateRequest) -> Result<()> {
193        let message = BootSvc::ownership_activate(activate);
194        let data = message.to_bytes()?;
195        self.set_boot_svc_raw(&data)
196    }
197
198    pub fn set_owner_config(&self, data: &[u8]) -> Result<()> {
199        self.set_mode(Self::OWNER_BLOCK)?;
200        let xm = Xmodem::new();
201        xm.send(&*self.uart, data)?;
202        Ok(())
203    }
204
205    pub fn erase_owner(&self) -> Result<()> {
206        self.set_mode(Self::ERASE_OWNER)?;
207        Ok(())
208    }
209}