opentitanlib/rescue/
mod.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, ensure};
6use clap::{Args, ValueEnum};
7use thiserror::Error;
8
9use crate::app::TransportWrapper;
10use crate::chip::boot_log::BootLog;
11use crate::chip::boot_svc::{BootSlot, BootSvc, OwnershipActivateRequest, OwnershipUnlockRequest};
12use crate::chip::device_id::DeviceId;
13use crate::io::uart::UartParams;
14use crate::with_unknown;
15
16pub mod serial;
17pub mod xmodem;
18
19pub use serial::RescueSerial;
20
21#[derive(Debug, Error)]
22pub enum RescueError {
23    #[error("bad mode: {0}")]
24    BadMode(String),
25    #[error("configuration error: {0}")]
26    Configuration(String),
27}
28
29#[derive(ValueEnum, Default, Debug, Clone, Copy, PartialEq)]
30pub enum RescueProtocol {
31    #[default]
32    Xmodem,
33    UsbDfu,
34    SpiDfu,
35}
36
37#[derive(ValueEnum, Default, Debug, Clone, Copy, PartialEq)]
38pub enum RescueTrigger {
39    #[default]
40    SerialBreak,
41    Gpio,
42    Strap,
43}
44
45#[derive(Clone, Default, Debug, Args)]
46pub struct RescueParams {
47    /// Rescue Protocol
48    #[arg(short, long, value_enum, default_value_t = RescueProtocol::Xmodem)]
49    pub protocol: RescueProtocol,
50    #[arg(short, long, value_enum, default_value_t = RescueTrigger::SerialBreak)]
51    pub trigger: RescueTrigger,
52    #[arg(short, long, default_value = "")]
53    pub value: String,
54    #[command(flatten)]
55    uart: UartParams,
56}
57
58impl RescueParams {
59    pub fn create(&self, transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
60        match self.protocol {
61            RescueProtocol::Xmodem => self.create_serial(transport),
62            RescueProtocol::UsbDfu => self.create_usbdfu(transport),
63            RescueProtocol::SpiDfu => self.create_spidfu(transport),
64        }
65    }
66    fn create_serial(&self, transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
67        ensure!(
68            self.trigger == RescueTrigger::SerialBreak,
69            RescueError::Configuration(format!(
70                "Xmodem does not support trigger {:?}",
71                self.trigger
72            ))
73        );
74        ensure!(
75            self.value.is_empty(),
76            RescueError::Configuration(format!(
77                "Xmodem does not support trigger value {:?}",
78                self.value
79            ))
80        );
81
82        Ok(Box::new(RescueSerial::new(self.uart.create(transport)?)))
83    }
84    fn create_usbdfu(&self, _transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
85        unimplemented!()
86    }
87    fn create_spidfu(&self, _transport: &TransportWrapper) -> Result<Box<dyn Rescue>> {
88        unimplemented!()
89    }
90}
91
92with_unknown! {
93pub enum RescueMode: u32 {
94    Rescue = u32::from_be_bytes(*b"RESQ"),
95    RescueB = u32::from_be_bytes(*b"RESB"),
96    BootLog = u32::from_be_bytes(*b"BLOG"),
97    BootSvcReq = u32::from_be_bytes(*b"BREQ"),
98    BootSvcRsp = u32::from_be_bytes(*b"BRSP"),
99    OwnerBlock = u32::from_be_bytes(*b"OWNR"),
100    GetOwnerPage0 = u32::from_be_bytes(*b"OPG0"),
101    GetOwnerPage1 = u32::from_be_bytes(*b"OPG1"),
102    DeviceId = u32::from_be_bytes(*b"OTID"),
103    EraseOwner = u32::from_be_bytes(*b"KLBR"),
104}
105}
106
107pub trait Rescue {
108    fn enter(&self, transport: &TransportWrapper, reset_target: bool) -> Result<()>;
109    fn set_mode(&self, mode: RescueMode) -> Result<()>;
110    fn send(&self, data: &[u8]) -> Result<()>;
111    fn recv(&self) -> Result<Vec<u8>>;
112
113    // Not supported by all backends
114    fn set_speed(&self, speed: u32) -> Result<u32>;
115    fn wait(&self) -> Result<()>;
116    fn reboot(&self) -> Result<()>;
117
118    fn get_raw(&self, mode: RescueMode) -> Result<Vec<u8>> {
119        self.set_mode(mode)?;
120        self.recv()
121    }
122
123    fn set_raw(&self, mode: RescueMode, data: &[u8]) -> Result<()> {
124        self.set_mode(mode)?;
125        self.send(data)
126    }
127
128    fn update_firmware(&self, slot: BootSlot, image: &[u8]) -> Result<()> {
129        let mode = if slot == BootSlot::SlotB {
130            RescueMode::RescueB
131        } else {
132            RescueMode::Rescue
133        };
134        self.set_raw(mode, image)
135    }
136
137    fn get_boot_log(&self) -> Result<BootLog> {
138        let blog = self.get_raw(RescueMode::BootLog)?;
139        Ok(BootLog::try_from(blog.as_slice())?)
140    }
141
142    fn get_boot_svc(&self) -> Result<BootSvc> {
143        let bsvc = self.get_raw(RescueMode::BootSvcRsp)?;
144        Ok(BootSvc::try_from(bsvc.as_slice())?)
145    }
146
147    fn get_device_id(&self) -> Result<DeviceId> {
148        let id = self.get_raw(RescueMode::DeviceId)?;
149        DeviceId::read(&mut std::io::Cursor::new(&id))
150    }
151
152    fn set_next_bl0_slot(&self, primary: BootSlot, next: BootSlot) -> Result<()> {
153        let message = BootSvc::next_boot_bl0_slot(primary, next);
154        self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
155    }
156
157    fn ownership_unlock(&self, unlock: OwnershipUnlockRequest) -> Result<()> {
158        let message = BootSvc::ownership_unlock(unlock);
159        self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
160    }
161
162    fn ownership_activate(&self, activate: OwnershipActivateRequest) -> Result<()> {
163        let message = BootSvc::ownership_activate(activate);
164        self.set_raw(RescueMode::BootSvcReq, &message.to_bytes()?)
165    }
166
167    fn set_owner_config(&self, data: &[u8]) -> Result<()> {
168        self.set_raw(RescueMode::OwnerBlock, data)
169    }
170
171    fn erase_owner(&self) -> Result<()> {
172        self.set_raw(RescueMode::EraseOwner, &[])
173    }
174}