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)]
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 pub enter_on_failure: bool,
81 pub timeout: u8,
83 pub start: u16,
85 pub size: u16,
87 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: 32,
191 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}