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