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, Serialize};
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 RescueType: u32 [default = Self::None] {
19        None = 0,
20        Xmodem = u32::from_le_bytes(*b"XMDM"),
21    }
22
23    pub enum CommandTag: u32 [default = Self::Unknown] {
24        Unknown = 0,
25        Empty = BootSvcKind::EmptyRequest.0,
26        MinBl0SecVerRequest = BootSvcKind::MinBl0SecVerRequest.0,
27        NextBl0SlotRequest = BootSvcKind::NextBl0SlotRequest.0,
28        OwnershipUnlockRequest = BootSvcKind::OwnershipUnlockRequest.0,
29        OwnershipActivateRequest =   BootSvcKind::OwnershipActivateRequest.0,
30
31        // The rescue protocol-level commands are represented in big-endian order.
32        Rescue = u32::from_be_bytes(*b"RESQ"),
33        RescueB = u32::from_be_bytes(*b"RESB"),
34        Reboot = u32::from_be_bytes(*b"REBO"),
35        GetBootLog = u32::from_be_bytes(*b"BLOG"),
36        BootSvcReq = u32::from_be_bytes(*b"BREQ"),
37        BootSvcRsp = u32::from_be_bytes(*b"BRSP"),
38        OwnerBlock = u32::from_be_bytes(*b"OWNR"),
39        GetOwnerPage0 = u32::from_be_bytes(*b"OPG0"),
40        GetOwnerPage1 = u32::from_be_bytes(*b"OPG1"),
41        GetDeviceId = u32::from_be_bytes(*b"OTID"),
42        Wait = u32::from_be_bytes(*b"WAIT"),
43    }
44}
45
46/// Describes the configuration of the rescue feature of the ROM_EXT.
47#[derive(Debug, Serialize, Deserialize, Annotate)]
48pub struct OwnerRescueConfig {
49    /// Header identifying this struct.
50    #[serde(
51        skip_serializing_if = "GlobalFlags::not_debug",
52        default = "OwnerRescueConfig::default_header"
53    )]
54    pub header: TlvHeader,
55    /// The type of rescue protocol to use (ie: Xmodem).
56    pub rescue_type: RescueType,
57    /// The start of the rescue flash region (in pages).
58    pub start: u16,
59    /// The size of the rescue flash region (in pages).
60    pub size: u16,
61    /// An allow-list of commands permitted to be executed by the rescue module.
62    pub command_allow: Vec<CommandTag>,
63}
64
65impl Default for OwnerRescueConfig {
66    fn default() -> Self {
67        Self {
68            header: Self::default_header(),
69            rescue_type: RescueType::default(),
70            start: 0u16,
71            size: 0u16,
72            command_allow: Vec::new(),
73        }
74    }
75}
76
77impl OwnerRescueConfig {
78    const BASE_SIZE: usize = 16;
79    pub fn default_header() -> TlvHeader {
80        TlvHeader::new(TlvTag::Rescue, 0, "0.0")
81    }
82    pub fn read(src: &mut impl Read, header: TlvHeader) -> Result<Self> {
83        let rescue_type = RescueType(src.read_u32::<LittleEndian>()?);
84        let start = src.read_u16::<LittleEndian>()?;
85        let size = src.read_u16::<LittleEndian>()?;
86        let allow_len = (header.length - Self::BASE_SIZE) / std::mem::size_of::<u32>();
87        let mut command_allow = Vec::new();
88        for _ in 0..allow_len {
89            command_allow.push(CommandTag(src.read_u32::<LittleEndian>()?));
90        }
91        Ok(Self {
92            header,
93            rescue_type,
94            start,
95            size,
96            command_allow,
97        })
98    }
99    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
100        let header = TlvHeader::new(
101            TlvTag::Rescue,
102            Self::BASE_SIZE + self.command_allow.len() * std::mem::size_of::<u32>(),
103            "0.0",
104        );
105        header.write(dest)?;
106        dest.write_u32::<LittleEndian>(u32::from(self.rescue_type))?;
107        dest.write_u16::<LittleEndian>(self.start)?;
108        dest.write_u16::<LittleEndian>(self.size)?;
109        for allow in self.command_allow.iter() {
110            dest.write_u32::<LittleEndian>(u32::from(*allow))?;
111        }
112        Ok(())
113    }
114
115    pub fn all() -> Self {
116        OwnerRescueConfig {
117            rescue_type: RescueType::Xmodem,
118            // Start after the ROM_EXT.
119            start: 32,
120            // End at the end of the partition.
121            size: 224,
122            command_allow: vec![
123                CommandTag::Empty,
124                CommandTag::MinBl0SecVerRequest,
125                CommandTag::NextBl0SlotRequest,
126                CommandTag::OwnershipUnlockRequest,
127                CommandTag::OwnershipActivateRequest,
128                CommandTag::Rescue,
129                CommandTag::RescueB,
130                CommandTag::Reboot,
131                CommandTag::GetBootLog,
132                CommandTag::BootSvcReq,
133                CommandTag::BootSvcRsp,
134                CommandTag::OwnerBlock,
135                CommandTag::GetOwnerPage0,
136                CommandTag::GetOwnerPage1,
137                CommandTag::GetDeviceId,
138                CommandTag::Wait,
139            ],
140            ..Default::default()
141        }
142    }
143}
144
145#[cfg(test)]
146mod test {
147    use super::*;
148    use crate::util::hexdump::{hexdump_parse, hexdump_string};
149
150    const OWNER_RESCUE_CONFIG_BIN: &str = "\
15100000000: 52 45 53 51 4c 00 00 00 58 4d 44 4d 20 00 64 00  RESQL...XMDM .d.\n\
15200000010: 45 4d 50 54 4d 53 45 43 4e 45 58 54 55 4e 4c 4b  EMPTMSECNEXTUNLK\n\
15300000020: 41 43 54 56 51 53 45 52 42 53 45 52 47 4f 4c 42  ACTVQSERBSERGOLB\n\
15400000030: 51 45 52 42 50 53 52 42 52 4e 57 4f 30 47 50 4f  QERBPSRBRNWO0GPO\n\
15500000040: 31 47 50 4f 44 49 54 4f 54 49 41 57              1GPODITOTIAW\n\
156";
157    const OWNER_RESCUE_CONFIG_JSON: &str = r#"{
158  rescue_type: "Xmodem",
159  start: 32,
160  size: 100,
161  command_allow: [
162    "Empty",
163    "MinBl0SecVerRequest",
164    "NextBl0SlotRequest",
165    "OwnershipUnlockRequest",
166    "OwnershipActivateRequest",
167    "Rescue",
168    "RescueB",
169    "GetBootLog",
170    "BootSvcReq",
171    "BootSvcRsp",
172    "OwnerBlock",
173    "GetOwnerPage0",
174    "GetOwnerPage1",
175    "GetDeviceId",
176    "Wait"
177  ]
178}"#;
179
180    #[test]
181    fn test_owner_rescue_config_write() -> Result<()> {
182        let orc = OwnerRescueConfig {
183            header: TlvHeader::default(),
184            rescue_type: RescueType::Xmodem,
185            start: 32,
186            size: 100,
187            command_allow: vec![
188                CommandTag::Empty,
189                CommandTag::MinBl0SecVerRequest,
190                CommandTag::NextBl0SlotRequest,
191                CommandTag::OwnershipUnlockRequest,
192                CommandTag::OwnershipActivateRequest,
193                CommandTag::Rescue,
194                CommandTag::RescueB,
195                CommandTag::GetBootLog,
196                CommandTag::BootSvcReq,
197                CommandTag::BootSvcRsp,
198                CommandTag::OwnerBlock,
199                CommandTag::GetOwnerPage0,
200                CommandTag::GetOwnerPage1,
201                CommandTag::GetDeviceId,
202                CommandTag::Wait,
203            ],
204        };
205
206        let mut bin = Vec::new();
207        orc.write(&mut bin)?;
208        eprintln!("{}", hexdump_string(&bin)?);
209        assert_eq!(hexdump_string(&bin)?, OWNER_RESCUE_CONFIG_BIN);
210        Ok(())
211    }
212
213    #[test]
214    fn test_owner_rescue_config_read() -> Result<()> {
215        let buf = hexdump_parse(OWNER_RESCUE_CONFIG_BIN)?;
216        let mut cur = std::io::Cursor::new(&buf);
217        let header = TlvHeader::read(&mut cur)?;
218        let orc = OwnerRescueConfig::read(&mut cur, header)?;
219        let doc = serde_annotate::serialize(&orc)?.to_json5().to_string();
220        eprintln!("{}", doc);
221        assert_eq!(doc, OWNER_RESCUE_CONFIG_JSON);
222        Ok(())
223    }
224}