opentitanlib/ownership/
rescue.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 byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
7use serde::Deserialize;
8use serde_annotate::Annotate;
9use std::convert::TryFrom;
10use std::io::{Read, Write};
11
12use super::GlobalFlags;
13use super::misc::{TlvHeader, TlvTag};
14use crate::chip::boot_svc::BootSvcKind;
15use crate::with_unknown;
16
17with_unknown! {
18    pub enum RescueProtocol: u8 [default = Self::None] {
19        None = 0,
20        Xmodem = b'X',
21        UsbDfu = b'U',
22        SpiDfu = b'S',
23    }
24
25    pub enum RescueTrigger: u8 [default = Self::UartBreak] {
26        None = 0,
27        UartBreak = 1,
28        Strapping = 2,
29        Gpio = 3,
30    }
31
32    pub enum CommandTag: u32 [default = Self::Unknown] {
33        Unknown = 0,
34        Empty = BootSvcKind::EmptyRequest.0,
35        MinBl0SecVerRequest = BootSvcKind::MinBl0SecVerRequest.0,
36        NextBl0SlotRequest = BootSvcKind::NextBl0SlotRequest.0,
37        OwnershipUnlockRequest = BootSvcKind::OwnershipUnlockRequest.0,
38        OwnershipActivateRequest =   BootSvcKind::OwnershipActivateRequest.0,
39
40        // The rescue protocol-level commands are represented in big-endian order.
41        Rescue = u32::from_be_bytes(*b"RESQ"),
42        RescueB = u32::from_be_bytes(*b"RESB"),
43        Reboot = u32::from_be_bytes(*b"REBO"),
44        GetBootLog = u32::from_be_bytes(*b"BLOG"),
45        BootSvcReq = u32::from_be_bytes(*b"BREQ"),
46        BootSvcRsp = u32::from_be_bytes(*b"BRSP"),
47        OwnerBlock = u32::from_be_bytes(*b"OWNR"),
48        GetOwnerPage0 = u32::from_be_bytes(*b"OPG0"),
49        GetOwnerPage1 = u32::from_be_bytes(*b"OPG1"),
50        GetDeviceId = u32::from_be_bytes(*b"OTID"),
51        Wait = u32::from_be_bytes(*b"WAIT"),
52    }
53}
54
55/// Describes the configuration of the rescue feature of the ROM_EXT.
56#[derive(Debug, Deserialize, Annotate)]
57pub struct OwnerRescueConfig {
58    /// Header identifying this struct.
59    #[serde(
60        skip_serializing_if = "GlobalFlags::not_debug",
61        default = "OwnerRescueConfig::default_header"
62    )]
63    pub header: TlvHeader,
64    /// The type of rescue protocol to use (ie: Xmodem).
65    #[serde(alias = "rescue_type")]
66    pub protocol: RescueProtocol,
67    /// The rescue triggering mechanism (UartBreak, Strapping or Gpio).
68    #[serde(default)]
69    pub trigger: RescueTrigger,
70    /// The index of the trigger (e.g. Strapping combo or GPIO pin number).
71    #[serde(default)]
72    pub trigger_index: u8,
73    /// Whether or not to enable the GPIO pull resistor (only if trigger is GPIO).
74    #[serde(default)]
75    pub gpio_pull_en: bool,
76    /// The GPIO trigger value (only if trigger is GPIO).
77    #[serde(default)]
78    pub gpio_value: bool,
79    /// Enter rescue mode if boot fails.
80    pub enter_on_failure: bool,
81    /// The inactivity timeout in seconds (zero means disabled).
82    pub timeout: u8,
83    /// The start of the rescue flash region (in pages).
84    pub start: u16,
85    /// The size of the rescue flash region (in pages).
86    pub size: u16,
87    /// An allow-list of commands permitted to be executed by the rescue module.
88    pub command_allow: Vec<CommandTag>,
89}
90
91impl Default for OwnerRescueConfig {
92    fn default() -> Self {
93        Self {
94            header: Self::default_header(),
95            protocol: RescueProtocol::default(),
96            gpio_pull_en: false,
97            gpio_value: false,
98            enter_on_failure: false,
99            timeout: 0,
100            trigger: RescueTrigger::default(),
101            trigger_index: 0,
102            start: 0u16,
103            size: 0u16,
104            command_allow: Vec::new(),
105        }
106    }
107}
108
109impl OwnerRescueConfig {
110    const BASE_SIZE: usize = 16;
111    const GPIO_PULL_BIT: u8 = 0x02;
112    const GPIO_VALUE_BIT: u8 = 0x01;
113    const ENTER_ON_FAIL_BIT: u8 = 0x80;
114    const TIMEOUT_MASK: u8 = 0x7f;
115    const TRIGGER_SHIFT: u8 = 6;
116    const INDEX_MASK: u8 = 0x3f;
117
118    pub fn default_header() -> TlvHeader {
119        TlvHeader::new(TlvTag::Rescue, 0, "0.0")
120    }
121    pub fn read(src: &mut impl Read, header: TlvHeader) -> Result<Self> {
122        let protocol = RescueProtocol(src.read_u8()?);
123        let gpio = src.read_u8()?;
124        let timeout = src.read_u8()?;
125        let trigger = src.read_u8()?;
126        let start = src.read_u16::<LittleEndian>()?;
127        let size = src.read_u16::<LittleEndian>()?;
128        let allow_len = (header.length - Self::BASE_SIZE) / std::mem::size_of::<u32>();
129        let mut command_allow = Vec::new();
130        for _ in 0..allow_len {
131            command_allow.push(CommandTag(src.read_u32::<LittleEndian>()?));
132        }
133        Ok(Self {
134            header,
135            protocol,
136            gpio_pull_en: gpio & Self::GPIO_PULL_BIT != 0,
137            gpio_value: gpio & Self::GPIO_VALUE_BIT != 0,
138            enter_on_failure: timeout & Self::ENTER_ON_FAIL_BIT != 0,
139            timeout: timeout & Self::TIMEOUT_MASK,
140            trigger: RescueTrigger(trigger >> Self::TRIGGER_SHIFT),
141            trigger_index: trigger & Self::INDEX_MASK,
142            start,
143            size,
144            command_allow,
145        })
146    }
147    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
148        let header = TlvHeader::new(
149            TlvTag::Rescue,
150            Self::BASE_SIZE + self.command_allow.len() * std::mem::size_of::<u32>(),
151            "0.0",
152        );
153        header.write(dest)?;
154        dest.write_u8(u8::from(self.protocol))?;
155        dest.write_u8(
156            if self.gpio_pull_en {
157                Self::GPIO_PULL_BIT
158            } else {
159                0
160            } | if self.gpio_value {
161                Self::GPIO_VALUE_BIT
162            } else {
163                0
164            },
165        )?;
166        dest.write_u8(
167            self.timeout & Self::TIMEOUT_MASK
168                | if self.enter_on_failure {
169                    Self::ENTER_ON_FAIL_BIT
170                } else {
171                    0
172                },
173        )?;
174        dest.write_u8(
175            self.trigger_index & Self::INDEX_MASK | u8::from(self.trigger) << Self::TRIGGER_SHIFT,
176        )?;
177        dest.write_u16::<LittleEndian>(self.start)?;
178        dest.write_u16::<LittleEndian>(self.size)?;
179        for allow in self.command_allow.iter() {
180            dest.write_u32::<LittleEndian>(u32::from(*allow))?;
181        }
182        Ok(())
183    }
184
185    pub fn all() -> Self {
186        OwnerRescueConfig {
187            protocol: RescueProtocol::Xmodem,
188            trigger: RescueTrigger::UartBreak,
189            // Start after the ROM_EXT.
190            start: 32,
191            // End at the end of the partition.
192            size: 224,
193            command_allow: vec![
194                CommandTag::Empty,
195                CommandTag::MinBl0SecVerRequest,
196                CommandTag::NextBl0SlotRequest,
197                CommandTag::OwnershipUnlockRequest,
198                CommandTag::OwnershipActivateRequest,
199                CommandTag::Rescue,
200                CommandTag::RescueB,
201                CommandTag::Reboot,
202                CommandTag::GetBootLog,
203                CommandTag::BootSvcReq,
204                CommandTag::BootSvcRsp,
205                CommandTag::OwnerBlock,
206                CommandTag::GetOwnerPage0,
207                CommandTag::GetOwnerPage1,
208                CommandTag::GetDeviceId,
209                CommandTag::Wait,
210            ],
211            ..Default::default()
212        }
213    }
214}
215
216#[cfg(test)]
217mod test {
218    use super::*;
219    use crate::util::hexdump::{hexdump_parse, hexdump_string};
220
221    const OWNER_RESCUE_CONFIG_BIN: &str = "\
22200000000: 52 45 53 51 4c 00 00 00 58 00 00 40 20 00 64 00  RESQL...X..@ .d.\n\
22300000010: 45 4d 50 54 4d 53 45 43 4e 45 58 54 55 4e 4c 4b  EMPTMSECNEXTUNLK\n\
22400000020: 41 43 54 56 51 53 45 52 42 53 45 52 47 4f 4c 42  ACTVQSERBSERGOLB\n\
22500000030: 51 45 52 42 50 53 52 42 52 4e 57 4f 30 47 50 4f  QERBPSRBRNWO0GPO\n\
22600000040: 31 47 50 4f 44 49 54 4f 54 49 41 57              1GPODITOTIAW\n\
227";
228    const OWNER_RESCUE_CONFIG_JSON: &str = r#"{
229  protocol: "Xmodem",
230  trigger: "UartBreak",
231  trigger_index: 0,
232  gpio_pull_en: false,
233  gpio_value: false,
234  enter_on_failure: false,
235  timeout: 0,
236  start: 32,
237  size: 100,
238  command_allow: [
239    "Empty",
240    "MinBl0SecVerRequest",
241    "NextBl0SlotRequest",
242    "OwnershipUnlockRequest",
243    "OwnershipActivateRequest",
244    "Rescue",
245    "RescueB",
246    "GetBootLog",
247    "BootSvcReq",
248    "BootSvcRsp",
249    "OwnerBlock",
250    "GetOwnerPage0",
251    "GetOwnerPage1",
252    "GetDeviceId",
253    "Wait"
254  ]
255}"#;
256
257    #[test]
258    fn test_owner_rescue_config_write() -> Result<()> {
259        let orc = OwnerRescueConfig {
260            header: TlvHeader::default(),
261            protocol: RescueProtocol::Xmodem,
262            trigger: RescueTrigger::UartBreak,
263            start: 32,
264            size: 100,
265            command_allow: vec![
266                CommandTag::Empty,
267                CommandTag::MinBl0SecVerRequest,
268                CommandTag::NextBl0SlotRequest,
269                CommandTag::OwnershipUnlockRequest,
270                CommandTag::OwnershipActivateRequest,
271                CommandTag::Rescue,
272                CommandTag::RescueB,
273                CommandTag::GetBootLog,
274                CommandTag::BootSvcReq,
275                CommandTag::BootSvcRsp,
276                CommandTag::OwnerBlock,
277                CommandTag::GetOwnerPage0,
278                CommandTag::GetOwnerPage1,
279                CommandTag::GetDeviceId,
280                CommandTag::Wait,
281            ],
282            ..Default::default()
283        };
284
285        let mut bin = Vec::new();
286        orc.write(&mut bin)?;
287        eprintln!("{}", hexdump_string(&bin)?);
288        assert_eq!(hexdump_string(&bin)?, OWNER_RESCUE_CONFIG_BIN);
289        Ok(())
290    }
291
292    #[test]
293    fn test_owner_rescue_config_read() -> Result<()> {
294        let buf = hexdump_parse(OWNER_RESCUE_CONFIG_BIN)?;
295        let mut cur = std::io::Cursor::new(&buf);
296        let header = TlvHeader::read(&mut cur)?;
297        let orc = OwnerRescueConfig::read(&mut cur, header)?;
298        let doc = serde_annotate::serialize(&orc)?.to_json5().to_string();
299        eprintln!("{}", doc);
300        assert_eq!(doc, OWNER_RESCUE_CONFIG_JSON);
301        Ok(())
302    }
303}