fastboot_protocol/
protocol.rs

1use std::{fmt::Display, num::ParseIntError};
2use thiserror::Error;
3use tracing::trace;
4
5fn bytes_slice_null(bytes: &[u8]) -> &[u8] {
6    match bytes.iter().position(|&b| b == 0x00) {
7        Some(pos) => &bytes[..pos],
8        None => bytes,
9    }
10}
11
12/// Parse a hexadecimal 0x prefixed string e.g. 0x1234 into a u32
13pub fn parse_u32_hex(hex: &str) -> Result<u32, ParseIntError> {
14    // Can't create a custom ParseIntError; so if there is no 0x prefix, work around it providing
15    // an invalid hex string
16    let hex = hex.strip_prefix("0x").unwrap_or("invalid");
17    u32::from_str_radix(hex, 16)
18}
19
20/// Parse a hexadecimal 0x prefixed string e.g. 0x1234 into a u64
21pub fn parse_u64_hex(hex: &str) -> Result<u64, ParseIntError> {
22    // Can't create a custom ParseIntError; so if there is no 0x prefix, work around it providing
23    // an invalid hex string
24    let hex = hex.strip_prefix("0x").unwrap_or("invalid");
25    u64::from_str_radix(hex, 16)
26}
27
28/// Fastboot commands
29#[derive(Debug)]
30pub enum FastBootCommand<S> {
31    /// Get a variable value
32    GetVar(S),
33    /// Download a given length of data to the devices
34    Download(u32),
35    /// Verify
36    Verify(u32),
37    /// Flash downloaded to a partition
38    Flash(S),
39    /// Erase a partition
40    Erase(S),
41    /// Boot the downloaded data
42    Boot,
43    /// Continue booting
44    Continue,
45    /// Reboot the devices
46    Reboot,
47    /// Reboot into the bootloader
48    RebootBootloader,
49    /// Power off the device
50    Powerdown,
51}
52
53impl<S: Display> Display for FastBootCommand<S> {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        match self {
56            FastBootCommand::GetVar(var) => write!(f, "getvar:{var}"),
57            FastBootCommand::Download(size) => write!(f, "download:{size:08x}"),
58            FastBootCommand::Verify(part) => write!(f, "verity:{part}"),
59            FastBootCommand::Flash(part) => write!(f, "flash:{part}"),
60            FastBootCommand::Erase(part) => write!(f, "erase:{part}"),
61            FastBootCommand::Boot => write!(f, "boot"),
62            FastBootCommand::Continue => write!(f, "continue"),
63            FastBootCommand::Reboot => write!(f, "reboot"),
64            FastBootCommand::RebootBootloader => write!(f, "reboot-bootloader"),
65            FastBootCommand::Powerdown => write!(f, "powerdown"),
66        }
67    }
68}
69
70/// Parse errors for fastboot responses
71#[derive(Error, Debug, PartialEq, Eq)]
72pub enum FastBootResponseParseError {
73    /// Unknown response type
74    #[error("Unknown response type")]
75    UnknownReply,
76    /// Couldn't parse response type
77    #[error("Couldn't parse response type")]
78    ParseType,
79    /// Couldn't parse response payload
80    #[error("Couldn't parse response payload")]
81    ParsePayload,
82    /// Couldn't parse DATA length
83    #[error("Couldn't parse DATA length")]
84    DataLength,
85}
86
87/// Fastboot response
88#[derive(Debug, PartialEq, Eq)]
89pub enum FastBootResponse {
90    /// Command succeeded with value (depending on command)
91    Okay(String),
92    /// Information from the device
93    Info(String),
94    /// Text data from the device
95    Text(String),
96    /// Command failed with provided reason
97    Fail(String),
98    /// Device expected the amount of data to be sent
99    Data(u32),
100}
101
102impl<'a> FastBootResponse {
103    fn from_parts(resp: &str, data: &'a str) -> Result<Self, FastBootResponseParseError> {
104        trace!("Parsing Response: {} {}", resp, data);
105        match resp {
106            "OKAY" => Ok(Self::Okay(data.into())),
107            "INFO" => Ok(Self::Info(data.into())),
108            "TEXT" => Ok(Self::Text(data.into())),
109            "FAIL" => Ok(Self::Fail(data.into())),
110            "DATA" => {
111                let offset = u32::from_str_radix(data, 16)
112                    .or(Err(FastBootResponseParseError::DataLength))?;
113                Ok(Self::Data(offset))
114            }
115            _ => Err(FastBootResponseParseError::UnknownReply),
116        }
117    }
118
119    /// Parse a fastboot response from provided data
120    pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, FastBootResponseParseError> {
121        if bytes.len() < 4 {
122            Err(FastBootResponseParseError::UnknownReply)
123        } else {
124            let resp =
125                std::str::from_utf8(&bytes[0..4]).or(Err(FastBootResponseParseError::ParseType))?;
126            let data = std::str::from_utf8(bytes_slice_null(&bytes[4..]))
127                .or(Err(FastBootResponseParseError::ParsePayload))?;
128
129            Self::from_parts(resp, data)
130        }
131    }
132}
133
134#[cfg(test)]
135mod test {
136    use super::*;
137
138    #[test]
139    fn parse_valid_u32_hex() {
140        let hex = parse_u32_hex("0x123456").unwrap();
141        assert_eq!(0x123456, hex);
142
143        let hex = parse_u32_hex("0x0012abcd").unwrap();
144        assert_eq!(0x12abcd, hex);
145    }
146
147    #[test]
148    fn parse_valid_u64_hex() {
149        let hex = parse_u64_hex("0x123456").unwrap();
150        assert_eq!(0x123456, hex);
151
152        let hex = parse_u64_hex("0x0012abcd").unwrap();
153        assert_eq!(0x12abcd, hex);
154
155        let hex = parse_u64_hex("0x0000000134b72400").unwrap();
156        assert_eq!(0x134b72400, hex);
157    }
158
159    #[test]
160    fn parse_invalid_u32_hex() {
161        parse_u32_hex("123456").unwrap_err();
162    }
163
164    #[test]
165    fn response_parse_ok() {
166        let r = FastBootResponse::from_bytes(b"OKAYtest").unwrap();
167        assert_eq!(r, FastBootResponse::Okay("test".to_string()));
168    }
169
170    #[test]
171    fn response_parse_ok_with_null() {
172        let r = FastBootResponse::from_bytes(b"OKAYtest\0foo").unwrap();
173        assert_eq!(r, FastBootResponse::Okay("test".to_string()));
174    }
175
176    #[test]
177    fn response_parse_fail() {
178        let r = FastBootResponse::from_bytes(b"FAILtest").unwrap();
179        assert_eq!(r, FastBootResponse::Fail("test".to_string()));
180    }
181
182    #[test]
183    fn response_parse_fail_with_null() {
184        let r = FastBootResponse::from_bytes(b"FAILtest\0foo").unwrap();
185        assert_eq!(r, FastBootResponse::Fail("test".to_string()));
186    }
187
188    #[test]
189    fn response_parse_info() {
190        let r = FastBootResponse::from_bytes(b"INFOtest").unwrap();
191        assert_eq!(r, FastBootResponse::Info("test".to_string()));
192    }
193
194    #[test]
195    fn response_parse_info_with_null() {
196        let r = FastBootResponse::from_bytes(b"INFOtest\0foo").unwrap();
197        assert_eq!(r, FastBootResponse::Info("test".to_string()));
198    }
199
200    #[test]
201    fn response_parse_text() {
202        let r = FastBootResponse::from_bytes(b"TEXTtest").unwrap();
203        assert_eq!(r, FastBootResponse::Text("test".to_string()));
204    }
205
206    #[test]
207    fn response_parse_text_with_null() {
208        let r = FastBootResponse::from_bytes(b"TEXTtest\0foo").unwrap();
209        assert_eq!(r, FastBootResponse::Text("test".to_string()));
210    }
211
212    #[test]
213    fn response_parse_data() {
214        let r = FastBootResponse::from_bytes(b"DATA00123456").unwrap();
215        assert_eq!(r, FastBootResponse::Data(0x123456));
216    }
217
218    #[test]
219    fn response_parse_data_with_null() {
220        let r = FastBootResponse::from_bytes(b"DATA00123456\0foo").unwrap();
221        assert_eq!(r, FastBootResponse::Data(0x123456));
222    }
223
224    #[test]
225    fn response_parse_invalid() {
226        let e = FastBootResponse::from_bytes(b"UNKN").unwrap_err();
227        assert_eq!(e, FastBootResponseParseError::UnknownReply);
228    }
229
230    #[test]
231    fn response_parse_too_short() {
232        let e = FastBootResponse::from_bytes(b"UN").unwrap_err();
233        assert_eq!(e, FastBootResponseParseError::UnknownReply);
234    }
235}