1use 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 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
55fn is_default<T: Default + Eq>(val: &T) -> bool {
56 *val == T::default()
57}
58
59#[derive(Debug, Deserialize, Annotate)]
61pub struct OwnerRescueConfig {
62 #[serde(
64 skip_serializing_if = "GlobalFlags::not_debug",
65 default = "OwnerRescueConfig::default_header"
66 )]
67 pub header: TlvHeader,
68 #[serde(alias = "rescue_type")]
70 pub protocol: RescueProtocol,
71 pub trigger: RescueTrigger,
73 pub trigger_index: u8,
75 pub gpio_pull_en: bool,
77 pub gpio_value: bool,
79 #[serde(skip_serializing_if = "is_default")]
81 pub _enter_on_failure: bool,
82 #[serde(skip_serializing_if = "is_default")]
84 pub _timeout_enable: bool,
85 #[serde(skip_serializing_if = "is_default")]
87 pub _timeout: u8,
88 pub start: u16,
90 pub size: u16,
92 pub command_allow: Vec<CommandTag>,
94}
95
96impl Default for OwnerRescueConfig {
97 fn default() -> Self {
98 Self {
99 header: Self::default_header(),
100 protocol: RescueProtocol::default(),
101 gpio_pull_en: false,
102 gpio_value: false,
103 _enter_on_failure: false,
104 _timeout_enable: false,
105 _timeout: 0,
106 trigger: RescueTrigger::default(),
107 trigger_index: 0,
108 start: 0u16,
109 size: 0u16,
110 command_allow: Vec::new(),
111 }
112 }
113}
114
115impl OwnerRescueConfig {
116 const BASE_SIZE: usize = 16;
117 const GPIO_PULL_BIT: u8 = 0x02;
118 const GPIO_VALUE_BIT: u8 = 0x01;
119 const ENTER_ON_FAIL_BIT: u8 = 0x80;
120 const TIMEOUT_EN_BIT: u8 = 0x40;
121 const TIMEOUT_MASK: u8 = 0x3f;
122 const TRIGGER_SHIFT: u8 = 6;
123 const INDEX_MASK: u8 = 0x3f;
124
125 pub fn default_header() -> TlvHeader {
126 TlvHeader::new(TlvTag::Rescue, 0, "0.0")
127 }
128 pub fn read(src: &mut impl Read, header: TlvHeader) -> Result<Self> {
129 let protocol = RescueProtocol(src.read_u8()?);
130 let gpio = src.read_u8()?;
131 let timeout = src.read_u8()?;
132 let trigger = src.read_u8()?;
133 let start = src.read_u16::<LittleEndian>()?;
134 let size = src.read_u16::<LittleEndian>()?;
135 let allow_len = (header.length - Self::BASE_SIZE) / std::mem::size_of::<u32>();
136 let mut command_allow = Vec::new();
137 for _ in 0..allow_len {
138 command_allow.push(CommandTag(src.read_u32::<LittleEndian>()?));
139 }
140 Ok(Self {
141 header,
142 protocol,
143 gpio_pull_en: gpio & Self::GPIO_PULL_BIT != 0,
144 gpio_value: gpio & Self::GPIO_VALUE_BIT != 0,
145 _enter_on_failure: timeout & Self::ENTER_ON_FAIL_BIT != 0,
146 _timeout_enable: timeout & Self::TIMEOUT_EN_BIT != 0,
147 _timeout: timeout & Self::TIMEOUT_MASK,
148 trigger: RescueTrigger(trigger >> Self::TRIGGER_SHIFT),
149 trigger_index: trigger & Self::INDEX_MASK,
150 start,
151 size,
152 command_allow,
153 })
154 }
155 pub fn write(&self, dest: &mut impl Write) -> Result<()> {
156 let header = TlvHeader::new(
157 TlvTag::Rescue,
158 Self::BASE_SIZE + self.command_allow.len() * std::mem::size_of::<u32>(),
159 "0.0",
160 );
161 header.write(dest)?;
162 dest.write_u8(u8::from(self.protocol))?;
163 dest.write_u8(
164 if self.gpio_pull_en {
165 Self::GPIO_PULL_BIT
166 } else {
167 0
168 } | if self.gpio_value {
169 Self::GPIO_VALUE_BIT
170 } else {
171 0
172 },
173 )?;
174 dest.write_u8(
175 self._timeout & Self::TIMEOUT_MASK
176 | if self._timeout_enable {
177 Self::TIMEOUT_EN_BIT
178 } else {
179 0
180 }
181 | if self._enter_on_failure {
182 Self::ENTER_ON_FAIL_BIT
183 } else {
184 0
185 },
186 )?;
187 dest.write_u8(
188 self.trigger_index & Self::INDEX_MASK | u8::from(self.trigger) << Self::TRIGGER_SHIFT,
189 )?;
190 dest.write_u16::<LittleEndian>(self.start)?;
191 dest.write_u16::<LittleEndian>(self.size)?;
192 for allow in self.command_allow.iter() {
193 dest.write_u32::<LittleEndian>(u32::from(*allow))?;
194 }
195 Ok(())
196 }
197
198 pub fn all() -> Self {
199 OwnerRescueConfig {
200 protocol: RescueProtocol::Xmodem,
201 trigger: RescueTrigger::UartBreak,
202 start: 32,
204 size: 224,
206 command_allow: vec![
207 CommandTag::Empty,
208 CommandTag::MinBl0SecVerRequest,
209 CommandTag::NextBl0SlotRequest,
210 CommandTag::OwnershipUnlockRequest,
211 CommandTag::OwnershipActivateRequest,
212 CommandTag::Rescue,
213 CommandTag::RescueB,
214 CommandTag::Reboot,
215 CommandTag::GetBootLog,
216 CommandTag::BootSvcReq,
217 CommandTag::BootSvcRsp,
218 CommandTag::OwnerBlock,
219 CommandTag::GetOwnerPage0,
220 CommandTag::GetOwnerPage1,
221 CommandTag::GetDeviceId,
222 CommandTag::Wait,
223 ],
224 ..Default::default()
225 }
226 }
227}
228
229#[cfg(test)]
230mod test {
231 use super::*;
232 use crate::util::hexdump::{hexdump_parse, hexdump_string};
233
234 const OWNER_RESCUE_CONFIG_BIN: &str = "\
23500000000: 52 45 53 51 4c 00 00 00 58 00 00 40 20 00 64 00 RESQL...X..@ .d.\n\
23600000010: 45 4d 50 54 4d 53 45 43 4e 45 58 54 55 4e 4c 4b EMPTMSECNEXTUNLK\n\
23700000020: 41 43 54 56 51 53 45 52 42 53 45 52 47 4f 4c 42 ACTVQSERBSERGOLB\n\
23800000030: 51 45 52 42 50 53 52 42 52 4e 57 4f 30 47 50 4f QERBPSRBRNWO0GPO\n\
23900000040: 31 47 50 4f 44 49 54 4f 54 49 41 57 1GPODITOTIAW\n\
240";
241 const OWNER_RESCUE_CONFIG_JSON: &str = r#"{
242 protocol: "Xmodem",
243 trigger: "UartBreak",
244 trigger_index: 0,
245 gpio_pull_en: false,
246 gpio_value: false,
247 start: 32,
248 size: 100,
249 command_allow: [
250 "Empty",
251 "MinBl0SecVerRequest",
252 "NextBl0SlotRequest",
253 "OwnershipUnlockRequest",
254 "OwnershipActivateRequest",
255 "Rescue",
256 "RescueB",
257 "GetBootLog",
258 "BootSvcReq",
259 "BootSvcRsp",
260 "OwnerBlock",
261 "GetOwnerPage0",
262 "GetOwnerPage1",
263 "GetDeviceId",
264 "Wait"
265 ]
266}"#;
267
268 #[test]
269 fn test_owner_rescue_config_write() -> Result<()> {
270 let orc = OwnerRescueConfig {
271 header: TlvHeader::default(),
272 protocol: RescueProtocol::Xmodem,
273 trigger: RescueTrigger::UartBreak,
274 start: 32,
275 size: 100,
276 command_allow: vec![
277 CommandTag::Empty,
278 CommandTag::MinBl0SecVerRequest,
279 CommandTag::NextBl0SlotRequest,
280 CommandTag::OwnershipUnlockRequest,
281 CommandTag::OwnershipActivateRequest,
282 CommandTag::Rescue,
283 CommandTag::RescueB,
284 CommandTag::GetBootLog,
285 CommandTag::BootSvcReq,
286 CommandTag::BootSvcRsp,
287 CommandTag::OwnerBlock,
288 CommandTag::GetOwnerPage0,
289 CommandTag::GetOwnerPage1,
290 CommandTag::GetDeviceId,
291 CommandTag::Wait,
292 ],
293 ..Default::default()
294 };
295
296 let mut bin = Vec::new();
297 orc.write(&mut bin)?;
298 eprintln!("{}", hexdump_string(&bin)?);
299 assert_eq!(hexdump_string(&bin)?, OWNER_RESCUE_CONFIG_BIN);
300 Ok(())
301 }
302
303 #[test]
304 fn test_owner_rescue_config_read() -> Result<()> {
305 let buf = hexdump_parse(OWNER_RESCUE_CONFIG_BIN)?;
306 let mut cur = std::io::Cursor::new(&buf);
307 let header = TlvHeader::read(&mut cur)?;
308 let orc = OwnerRescueConfig::read(&mut cur, header)?;
309 let doc = serde_annotate::serialize(&orc)?.to_json5().to_string();
310 eprintln!("{}", doc);
311 assert_eq!(doc, OWNER_RESCUE_CONFIG_JSON);
312 Ok(())
313 }
314}