Skip to main content

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