1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! This library provides APIs for receiving, processing and replying to fastboot commands. To use
16 //! the library:
17 //!
18 //! 1. Provide a transport backend by implementing the `Transport` trait.
19 //!
20 //! ```
21 //!
22 //! struct FastbootTransport {}
23 //!
24 //! impl Transport<MyErrorType> for TestTransport {
25 //! fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize, TransportError> {
26 //! todo!();
27 //! }
28 //!
29 //! fn send_packet(&mut self, packet: &[u8]) -> Result<(), TransportError> {
30 //! todo!();
31 //! }
32 //! }
33 //! ```
34 //!
35 //! 2. Provide a fastboot command backend by implementing the `FastbootImplementation` trait.
36 //! i.e.
37 //!
38 //! ```
39 //!
40 //! struct FastbootCommand {}
41 //!
42 //! impl FastbootImplementation for FastbootTest {
43 //! fn get_var(
44 //! &mut self,
45 //! var: &str,
46 //! args: Split<char>,
47 //! out: &mut [u8],
48 //! ) -> CommandResult<usize> {
49 //! todo!();
50 //! }
51 //!
52 //! ...
53 //! }
54 //!```
55 //!
56 //! 3. Construct a `Fastboot` object with a given download buffer. Pass the transport, command
57 //! implementation and call the `run()` method:
58 //!
59 //! ```
60 //! let mut fastboot_impl: FastbootCommand = ...;
61 //! let mut transport: TestTransport = ...;
62 //! let download_buffer: &mut [u8] = ...;
63 //! let mut fastboot = Fastboot::new();
64 //! let result = run(&mut transport, &mut fastboot_impl, &[]);
65 //! ```
66
67 #![cfg_attr(not(test), no_std)]
68 #![allow(async_fn_in_trait)]
69
70 use core::{
71 cmp::min,
72 ffi::CStr,
73 fmt::{Debug, Display, Formatter, Write},
74 str::{from_utf8, Split},
75 };
76 use gbl_async::{block_on, yield_now};
77 use liberror::{Error, Result};
78
79 /// Maximum packet size that can be accepted from the host.
80 ///
81 /// The transport layer may have its own size limits that reduce the packet size further.
82 pub const MAX_COMMAND_SIZE: usize = 4096;
83 /// Maximum packet size that will be sent to the host.
84 ///
85 /// The `fastboot` host tool originally had a 64-byte packet size max, but this was increased
86 /// to 256 in 2020, so any reasonably recent host binary should be able to support 256.
87 ///
88 /// The transport layer may have its own size limits that reduce the packet size further.
89 pub const MAX_RESPONSE_SIZE: usize = 256;
90
91 /// Trait to provide the transport layer for a fastboot implementation.
92 ///
93 /// Fastboot supports these transports:
94 /// * USB
95 /// * TCP
96 /// * UDP
97 pub trait Transport {
98 /// Fetches the next fastboot packet into `out`.
99 ///
100 /// Returns the actual size of the packet on success.
101 ///
102 /// TODO(b/322540167): In the future, we may want to support using `[MaybeUninit<u8>]` as the
103 /// download buffer to avoid expensive initialization at the beginning. This would require an
104 /// interface where the implementation provides the buffer for us to copy instead of us.
receive_packet(&mut self, out: &mut [u8]) -> Result<usize>105 async fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize>;
106
107 /// Sends a fastboot packet.
108 ///
109 /// The method assumes `packet` is sent or at least copied to queue after it returns, where
110 /// the buffer can go out of scope without affecting anything.
send_packet(&mut self, packet: &[u8]) -> Result<()>111 async fn send_packet(&mut self, packet: &[u8]) -> Result<()>;
112 }
113
114 /// For now, we hardcode the expected version, until we need to distinguish between multiple
115 /// versions.
116 const TCP_HANDSHAKE_MESSAGE: &[u8] = b"FB01";
117
118 /// A trait representing a TCP stream reader/writer. Fastboot over TCP has additional handshake
119 /// process and uses a length-prefixed wire message format. It is recommended that caller
120 /// implements this trait instead of `Transport`, and uses the API `Fastboot::run_tcp_session()`
121 /// to perform fastboot over TCP. It internally handles handshake and wire message parsing.
122 pub trait TcpStream {
123 /// Reads to `out` for exactly `out.len()` number bytes from the TCP connection.
read_exact(&mut self, out: &mut [u8]) -> Result<()>124 async fn read_exact(&mut self, out: &mut [u8]) -> Result<()>;
125
126 /// Sends exactly `data.len()` number bytes from `data` to the TCP connection.
write_exact(&mut self, data: &[u8]) -> Result<()>127 async fn write_exact(&mut self, data: &[u8]) -> Result<()>;
128 }
129
130 /// Implements [Transport] on a [TcpStream].
131 pub struct TcpTransport<'a, T: TcpStream>(&'a mut T);
132
133 impl<'a, T: TcpStream> TcpTransport<'a, T> {
134 /// Creates an instance from a newly connected TcpStream and performs handshake.
new_and_handshake(tcp_stream: &'a mut T) -> Result<Self>135 pub fn new_and_handshake(tcp_stream: &'a mut T) -> Result<Self> {
136 let mut handshake = [0u8; 4];
137 block_on(tcp_stream.write_exact(TCP_HANDSHAKE_MESSAGE))?;
138 block_on(tcp_stream.read_exact(&mut handshake[..]))?;
139 match handshake == *TCP_HANDSHAKE_MESSAGE {
140 true => Ok(Self(tcp_stream)),
141 _ => Err(Error::InvalidHandshake),
142 }
143 }
144 }
145
146 impl<'a, T: TcpStream> Transport for TcpTransport<'a, T> {
receive_packet(&mut self, out: &mut [u8]) -> Result<usize>147 async fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize> {
148 let mut length_prefix = [0u8; 8];
149 self.0.read_exact(&mut length_prefix[..]).await?;
150 let packet_size: usize = u64::from_be_bytes(length_prefix).try_into()?;
151 match out.len() < packet_size {
152 true => Err(Error::InvalidInput),
153 _ => {
154 self.0.read_exact(&mut out[..packet_size]).await?;
155 Ok(packet_size)
156 }
157 }
158 }
159
send_packet(&mut self, packet: &[u8]) -> Result<()>160 async fn send_packet(&mut self, packet: &[u8]) -> Result<()> {
161 self.0.write_exact(&mut u64::try_from(packet.len())?.to_be_bytes()[..]).await?;
162 self.0.write_exact(packet).await
163 }
164 }
165
166 const COMMAND_ERROR_LENGTH: usize = MAX_RESPONSE_SIZE - 4;
167
168 /// `CommandError` is the return error type for methods in trait `FastbootImplementation` when
169 /// they fail. It will be converted into string and sent as fastboot error message "FAIL<string>".
170 ///
171 /// Any type that implements `Display` trait can be converted into it. However, because fastboot
172 /// response message is limited to `MAX_RESPONSE_SIZE`. If the final displayed string length
173 /// exceeds it, the rest of the content is ignored.
174 pub struct CommandError(FormattedBytes<[u8; COMMAND_ERROR_LENGTH]>);
175
176 impl CommandError {
177 /// Converts to string.
to_str(&self) -> &str178 pub fn to_str(&self) -> &str {
179 from_utf8(&self.0 .0[..self.0 .1]).unwrap_or("")
180 }
181
182 /// Clones the error.
clone(&self) -> Self183 pub fn clone(&self) -> Self {
184 self.to_str().into()
185 }
186 }
187
188 impl Debug for CommandError {
fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result189 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
190 write!(f, "{}", self.to_str())
191 }
192 }
193
194 impl<T: Display> From<T> for CommandError {
from(val: T) -> Self195 fn from(val: T) -> Self {
196 let mut res = CommandError(FormattedBytes([0u8; COMMAND_ERROR_LENGTH], 0));
197 write!(res.0, "{}", val).unwrap();
198 res
199 }
200 }
201
202 /// Type alias for Result that wraps a CommandError
203 pub type CommandResult<T> = core::result::Result<T, CommandError>;
204
205 /// Fastboot reboot mode
206 #[derive(Debug, Copy, Clone, PartialEq)]
207 pub enum RebootMode {
208 /// "fastboot reboot". Normal reboot.
209 Normal,
210 /// "fastboot reboot-bootloader". Reboot to bootloader.
211 Bootloader,
212 /// "fastboot reboot-fastboot". Reboot to userspace fastboot.
213 Fastboot,
214 /// "fastboot reboot-recovery". Reboot to recovery.
215 Recovery,
216 }
217
218 /// Implementation for Fastboot command backends.
219 pub trait FastbootImplementation {
220 /// Backend for `fastboot getvar ...`
221 ///
222 /// Gets the value of a variable specified by name and configuration represented by list of
223 /// additional arguments in `args`.
224 ///
225 /// Variable `max-download-size`, `version` are reserved by the library.
226 ///
227 /// # Args
228 ///
229 /// * `var`: Name of the variable.
230 /// * `args`: Additional arguments.
231 /// * `out`: Output buffer for storing the variable value.
232 /// * `responder`: An instance of `InfoSender`.
233 ///
234 /// TODO(b/322540167): Figure out other reserved variables.
get_var( &mut self, var: &CStr, args: impl Iterator<Item = &'_ CStr> + Clone, out: &mut [u8], responder: impl InfoSender, ) -> CommandResult<usize>235 async fn get_var(
236 &mut self,
237 var: &CStr,
238 args: impl Iterator<Item = &'_ CStr> + Clone,
239 out: &mut [u8],
240 responder: impl InfoSender,
241 ) -> CommandResult<usize>;
242
243 /// A helper API for getting the value of a fastboot variable and decoding it into string.
get_var_as_str<'s>( &mut self, var: &CStr, args: impl Iterator<Item = &'_ CStr> + Clone, responder: impl InfoSender, out: &'s mut [u8], ) -> CommandResult<&'s str>244 async fn get_var_as_str<'s>(
245 &mut self,
246 var: &CStr,
247 args: impl Iterator<Item = &'_ CStr> + Clone,
248 responder: impl InfoSender,
249 out: &'s mut [u8],
250 ) -> CommandResult<&'s str> {
251 let size = self.get_var(var, args, out, responder).await?;
252 Ok(from_utf8(out.get(..size).ok_or("Invalid variable size")?)
253 .map_err(|_| "Value is not string")?)
254 }
255
256 /// Backend for `fastboot getvar all`.
257 ///
258 /// Iterates all combinations of fastboot variable, configurations and values that need to be
259 /// included in the response to `fastboot getvar all`.
260 ///
261 /// # Args
262 ///
263 /// * `responder`: An implementation VarInfoSender. Implementation should call
264 /// `VarInfoSender::send` for all combinations of Fastboot variable/argument/value that needs
265 /// to be included in the response to `fastboot getvarl all`:
266 ///
267 /// async fn get_var_all(&mut self, f: F, resp: impl VarInfoSender)
268 /// -> CommandResult<()> {
269 /// resp.send("partition-size", &["boot_a"], /* size of boot_a */).await?;
270 /// resp.send("partition-size", &["boot_b"], /* size of boot_b */).await?;
271 /// resp.send("partition-size", &["init_boot_a"], /* size of init_boot_a */).await?;
272 /// resp.send("partition-size", &["init_boot_b"], /* size of init_boot_b */).await?;
273 /// Ok(())
274 /// }
275 ///
276 /// will generates the following outputs for `fastboot getvar all`:
277 ///
278 /// ...
279 /// (bootloader) partition-size:boot_a: <size of boot_a>
280 /// (bootloader) partition-size:boot_b: <size of boot_b>
281 /// (bootloader) partition-size:init_boot_a: <size of init_boot_a>
282 /// (bootloader) partition-size:init_boot_b: <size of init_boot_b>
283 /// ...
284 ///
285 /// TODO(b/322540167): This and `get_var()` contain duplicated logic. Investigate if there can
286 /// be better solutions for doing the combination traversal.
get_var_all(&mut self, responder: impl VarInfoSender) -> CommandResult<()>287 async fn get_var_all(&mut self, responder: impl VarInfoSender) -> CommandResult<()>;
288
289 /// Backend for getting download buffer
get_download_buffer(&mut self) -> &mut [u8]290 async fn get_download_buffer(&mut self) -> &mut [u8];
291
292 /// Called when a download is completed.
download_complete( &mut self, download_size: usize, responder: impl InfoSender, ) -> CommandResult<()>293 async fn download_complete(
294 &mut self,
295 download_size: usize,
296 responder: impl InfoSender,
297 ) -> CommandResult<()>;
298
299 /// Backend for `fastboot flash ...`
300 ///
301 /// # Args
302 ///
303 /// * `part`: Name of the partition.
304 /// * `responder`: An instance of `InfoSender`.
flash(&mut self, part: &str, responder: impl InfoSender) -> CommandResult<()>305 async fn flash(&mut self, part: &str, responder: impl InfoSender) -> CommandResult<()>;
306
307 /// Backend for `fastboot erase ...`
308 ///
309 /// # Args
310 ///
311 /// * `part`: Name of the partition.
312 /// * `responder`: An instance of `InfoSender`.
erase(&mut self, part: &str, responder: impl InfoSender) -> CommandResult<()>313 async fn erase(&mut self, part: &str, responder: impl InfoSender) -> CommandResult<()>;
314
315 /// Backend for `fastboot get_staged ...`
316 ///
317 /// # Args
318 ///
319 /// * `responder`: An instance of `UploadBuilder + InfoSender` for initiating and uploading
320 /// data. For example:
321 ///
322 /// ```
323 /// async fn upload(
324 /// &mut self,
325 /// responder: impl UploadBuilder + InfoSender,
326 /// ) -> CommandResult<()> {
327 /// let data = ..;
328 /// // Sends a total of 1024 bytes data.
329 /// responder.send_info("About to upload...").await?;
330 /// let mut uploader = responder.initiate_upload(1024).await?;
331 /// // Can upload in multiple batches.
332 /// uploader.upload(&data[..512]).await?;
333 /// uploader.upload(&data[512..]).await?;
334 /// Ok(())
335 /// }
336 /// ```
337 ///
338 /// If implementation fails to upload enough, or attempts to upload more than expected data
339 /// with `Uploader::upload()`, an error will be returned.
upload(&mut self, responder: impl UploadBuilder + InfoSender) -> CommandResult<()>340 async fn upload(&mut self, responder: impl UploadBuilder + InfoSender) -> CommandResult<()>;
341
342 /// Backend for `fastboot fetch ...`
343 ///
344 /// # Args
345 ///
346 /// * `part`: The partition name.
347 /// * `offset`: The offset into the partition for upload.
348 /// * `size`: The number of bytes to upload.
349 /// * `responder`: An instance of `UploadBuilder + InfoSender` for initiating and uploading data.
fetch( &mut self, part: &str, offset: u64, size: u64, responder: impl UploadBuilder + InfoSender, ) -> CommandResult<()>350 async fn fetch(
351 &mut self,
352 part: &str,
353 offset: u64,
354 size: u64,
355 responder: impl UploadBuilder + InfoSender,
356 ) -> CommandResult<()>;
357
358 /// Backend for `fastboot reboot/reboot-bootloader/reboot-fastboot/reboot-recovery`
359 ///
360 /// # Args
361 ///
362 /// * `mode`: An `RebootMode` specifying the reboot mode.
363 /// * `responder`: An instance of `InfoSender + OkaySender`. Implementation should call
364 /// `responder.send_okay("")` right before reboot to notify the remote host that the
365 /// operation is successful.
366 ///
367 /// # Returns
368 ///
369 /// * The method is not expected to return if reboot is successful.
370 /// * Returns `Err(e)` on error.
reboot( &mut self, mode: RebootMode, responder: impl InfoSender + OkaySender, ) -> CommandError371 async fn reboot(
372 &mut self,
373 mode: RebootMode,
374 responder: impl InfoSender + OkaySender,
375 ) -> CommandError;
376
377 /// Method for handling `fastboot continue` clean up.
378 ///
379 /// `run()` and `run_tcp_session()` exit after receiving `fastboot continue.` The method is for
380 /// implementation to perform necessary clean up.
381 ///
382 /// # Args
383 ///
384 /// * `responder`: An instance of `InfoSender`.
385 async fn r#continue(&mut self, responder: impl InfoSender) -> CommandResult<()>;
386
387 /// Backend for `fastboot set_active`.
set_active(&mut self, slot: &str, responder: impl InfoSender) -> CommandResult<()>388 async fn set_active(&mut self, slot: &str, responder: impl InfoSender) -> CommandResult<()>;
389
390 /// Backend for `fastboot boot`
391 ///
392 /// # Args
393 ///
394 /// * `responder`: An instance of `InfoSender + OkaySender`. Implementation should call
395 /// `responder.send_okay("")` right before boot to notify the remote host that the
396 /// operation is successful.
397 ///
398 /// # Returns
399 ///
400 /// * The method is always return OK to let fastboot continue.
401 /// * Returns `Err(e)` on error.
boot(&mut self, responder: impl InfoSender + OkaySender) -> CommandResult<()>402 async fn boot(&mut self, responder: impl InfoSender + OkaySender) -> CommandResult<()>;
403
404 /// Backend for `fastboot oem ...`.
405 ///
406 /// # Args
407 ///
408 /// * `cmd`: The OEM command string that comes after "oem ".
409 /// * `responder`: An instance of `InfoSender`.
410 /// * `res`: The responder buffer. Upon success, implementation can use the buffer to
411 /// construct a valid UTF8 string which will be sent as "OKAY<string>"
412 ///
413 /// # Returns
414 ///
415 /// On success, returns the portion of `res` used by the construction of string message.
oem<'a>( &mut self, cmd: &str, responder: impl InfoSender, res: &'a mut [u8], ) -> CommandResult<&'a [u8]>416 async fn oem<'a>(
417 &mut self,
418 cmd: &str,
419 responder: impl InfoSender,
420 res: &'a mut [u8],
421 ) -> CommandResult<&'a [u8]>;
422
423 // TODO(b/322540167): Add methods for other commands.
424 }
425
426 /// An internal convenient macro helper for `fastboot_okay`, `fastboot_fail` and `fastboot_info`.
427 macro_rules! fastboot_msg {
428 ( $arr:expr, $msg_type:expr, $( $x:expr ),* $(,)? ) => {
429 {
430 let mut formatted_bytes = FormattedBytes::new(&mut $arr[..]);
431 write!(formatted_bytes, $msg_type).unwrap();
432 write!(formatted_bytes, $($x,)*).unwrap();
433 let size = formatted_bytes.size();
434 &mut $arr[..size]
435 }
436 };
437 }
438
439 /// An internal convenient macro that constructs a formatted fastboot OKAY message.
440 macro_rules! fastboot_okay {
441 ( $arr:expr, $( $x:expr ),* $(,)?) => { fastboot_msg!($arr, "OKAY", $($x,)*) };
442 }
443
444 /// An internal convenient macro that constructs a formatted fastboot FAIL message.
445 macro_rules! fastboot_fail {
446 ( $arr:expr, $( $x:expr ),* $(,)?) => { fastboot_msg!($arr, "FAIL", $($x,)*) };
447 }
448
449 /// `VarInfoSender` provide an interface for sending variable/args/value combination during the
450 /// processing of `fastboot getvar all`
451 pub trait VarInfoSender {
452 /// Send a combination of variable name, arguments and value.
453 ///
454 /// The method sends a fastboot message "INFO<var>:<args>:<val>" to the host.
455 ///
456 /// # Args
457 ///
458 /// * `name`: Name of the fastboot variable.
459 /// * `args`: An iterator to additional arguments.
460 /// * `val`: Value of the variable.
send_var_info( &mut self, name: &str, args: impl IntoIterator<Item = &'_ str>, val: &str, ) -> Result<()>461 async fn send_var_info(
462 &mut self,
463 name: &str,
464 args: impl IntoIterator<Item = &'_ str>,
465 val: &str,
466 ) -> Result<()>;
467 }
468
469 /// Provides an API for sending fastboot INFO messages.
470 pub trait InfoSender {
471 /// Sends formatted INFO message.
472 ///
473 /// # Args:
474 ///
475 /// * `cb`: A closure provided by the caller for constructing the formatted messagae.
send_formatted_info<F: FnOnce(&mut dyn Write)>(&mut self, cb: F) -> Result<()>476 async fn send_formatted_info<F: FnOnce(&mut dyn Write)>(&mut self, cb: F) -> Result<()>;
477
478 /// Sends a Fastboot "INFO<`msg`>" packet.
send_info(&mut self, msg: &str) -> Result<()>479 async fn send_info(&mut self, msg: &str) -> Result<()> {
480 self.send_formatted_info(|w| write!(w, "{}", msg).unwrap()).await
481 }
482 }
483
484 /// Provides an API for sending fastboot OKAY messages.
485 pub trait OkaySender {
486 /// Sends formatted Okay message.
487 ///
488 /// # Args:
489 ///
490 /// * `cb`: A closure provided by the caller for constructing the formatted messagae.
send_formatted_okay<F: FnOnce(&mut dyn Write)>(self, cb: F) -> Result<()>491 async fn send_formatted_okay<F: FnOnce(&mut dyn Write)>(self, cb: F) -> Result<()>;
492
493 /// Sends a fastboot OKAY<msg> packet. `Self` is consumed.
send_okay(self, msg: &str) -> Result<()> where Self: Sized,494 async fn send_okay(self, msg: &str) -> Result<()>
495 where
496 Self: Sized,
497 {
498 self.send_formatted_okay(|w| write!(w, "{}", msg).unwrap()).await
499 }
500 }
501
502 /// `UploadBuilder` provides API for initiating a fastboot upload.
503 pub trait UploadBuilder {
504 /// Starts the upload.
505 ///
506 /// In a real fastboot context, the method should send `DATA0xXXXXXXXX` to the remote host to
507 /// start the download. An `Uploader` implementation should be returned for uploading payload.
initiate_upload(self, data_size: u64) -> Result<impl Uploader>508 async fn initiate_upload(self, data_size: u64) -> Result<impl Uploader>;
509 }
510
511 /// `UploadBuilder` provides API for uploading payload.
512 pub trait Uploader {
513 /// Uploads data to the Fastboot host.
upload(&mut self, data: &[u8]) -> Result<()>514 async fn upload(&mut self, data: &[u8]) -> Result<()>;
515 }
516
517 /// `Responder` implements APIs for fastboot backend to send fastboot messages and uploading data.
518 struct Responder<'a, T: Transport> {
519 buffer: [u8; MAX_RESPONSE_SIZE],
520 transport: &'a mut T,
521 transport_error: Result<()>,
522 remaining_upload: u64,
523 }
524
525 impl<'a, T: Transport> Responder<'a, T> {
new(transport: &'a mut T) -> Self526 fn new(transport: &'a mut T) -> Self {
527 Self {
528 buffer: [0u8; MAX_RESPONSE_SIZE],
529 transport,
530 transport_error: Ok(()),
531 remaining_upload: 0,
532 }
533 }
534
535 /// A helper for sending a fastboot message in the buffer.
send_buffer(&mut self, size: usize) -> Result<()>536 async fn send_buffer(&mut self, size: usize) -> Result<()> {
537 self.transport_error?;
538 assert!(size < self.buffer.len());
539 self.transport_error = self.transport.send_packet(&self.buffer[..size]).await;
540 Ok(self.transport_error?)
541 }
542
543 /// Helper for sending a formatted fastboot message.
544 ///
545 /// # Args:
546 ///
547 /// * `cb`: A closure provided by the caller for constructing the formatted messagae.
send_formatted_msg<F: FnOnce(&mut dyn Write)>( &mut self, msg_type: &str, cb: F, ) -> Result<()>548 async fn send_formatted_msg<F: FnOnce(&mut dyn Write)>(
549 &mut self,
550 msg_type: &str,
551 cb: F,
552 ) -> Result<()> {
553 let mut formatted_bytes = FormattedBytes::new(&mut self.buffer);
554 write!(formatted_bytes, "{}", msg_type).unwrap();
555 cb(&mut formatted_bytes);
556 let size = formatted_bytes.size();
557 self.send_buffer(size).await
558 }
559
560 /// Sends a fastboot DATA message.
send_data_message(&mut self, data_size: u64) -> Result<()>561 async fn send_data_message(&mut self, data_size: u64) -> Result<()> {
562 self.send_formatted_msg("DATA", |v| write!(v, "{:08x}", data_size).unwrap()).await
563 }
564 }
565
566 impl<'a, T: Transport> VarInfoSender for &mut Responder<'a, T> {
send_var_info( &mut self, name: &str, args: impl IntoIterator<Item = &'_ str>, val: &str, ) -> Result<()>567 async fn send_var_info(
568 &mut self,
569 name: &str,
570 args: impl IntoIterator<Item = &'_ str>,
571 val: &str,
572 ) -> Result<()> {
573 // Sends a "INFO<var>:<':'-separated args>:<val>" packet to the host.
574 Ok(self
575 .send_formatted_msg("INFO", |v| {
576 write!(v, "{}", name).unwrap();
577 args.into_iter().for_each(|arg| write!(v, ":{}", arg).unwrap());
578 write!(v, ": {}", val).unwrap();
579 })
580 .await?)
581 }
582 }
583
584 /// An internal convenient macro that sends a formatted fastboot OKAY message via a `Responder`
585 macro_rules! reply_okay {
586 ( $resp:expr, $( $x:expr ),* $(,)?) => {
587 {
588 let len = fastboot_okay!($resp.buffer, $($x,)*).len();
589 $resp.send_buffer(len).await
590 }
591 };
592 }
593
594 /// An internal convenient macro that sends a formatted fastboot FAIL message via a `Responder`
595 macro_rules! reply_fail {
596 ( $resp:expr, $( $x:expr ),* $(,)?) => {
597 {
598 let len = fastboot_fail!($resp.buffer, $($x,)*).len();
599 $resp.send_buffer(len).await
600 }
601 };
602 }
603
604 impl<T: Transport> InfoSender for &mut Responder<'_, T> {
send_formatted_info<F: FnOnce(&mut dyn Write)>(&mut self, cb: F) -> Result<()>605 async fn send_formatted_info<F: FnOnce(&mut dyn Write)>(&mut self, cb: F) -> Result<()> {
606 Ok(self.send_formatted_msg("INFO", cb).await?)
607 }
608 }
609
610 impl<T: Transport> OkaySender for &mut Responder<'_, T> {
send_formatted_okay<F: FnOnce(&mut dyn Write)>(self, cb: F) -> Result<()>611 async fn send_formatted_okay<F: FnOnce(&mut dyn Write)>(self, cb: F) -> Result<()> {
612 Ok(self.send_formatted_msg("OKAY", cb).await?)
613 }
614 }
615
616 impl<'a, T: Transport> UploadBuilder for &mut Responder<'a, T> {
initiate_upload(self, data_size: u64) -> Result<impl Uploader>617 async fn initiate_upload(self, data_size: u64) -> Result<impl Uploader> {
618 self.send_data_message(data_size).await?;
619 self.remaining_upload = data_size;
620 Ok(self)
621 }
622 }
623
624 impl<'a, T: Transport> Uploader for &mut Responder<'a, T> {
625 /// Uploads data. Returns error if accumulative amount exceeds `data_size` passed to
626 /// `UploadBuilder::start()`.
upload(&mut self, data: &[u8]) -> Result<()>627 async fn upload(&mut self, data: &[u8]) -> Result<()> {
628 self.transport_error?;
629 self.remaining_upload = self
630 .remaining_upload
631 .checked_sub(data.len().try_into().map_err(|_| "")?)
632 .ok_or(Error::Other(Some("Invalid size of upload data")))?;
633 self.transport_error = self.transport.send_packet(data).await;
634 Ok(())
635 }
636 }
637
638 pub mod test_utils {
639 //! Test utilities to help users of this library write unit tests.
640
641 use crate::{InfoSender, UploadBuilder, Uploader};
642 use core::fmt::Write;
643 use liberror::Error;
644
645 /// A test implementation of `UploadBuilder` for unittesting
646 /// `FastbootImplementation::upload()`.
647 ///
648 /// The test uploader simply uploads to a user provided buffer.
649 pub struct TestUploadBuilder<'a>(pub &'a mut [u8]);
650
651 impl<'a> UploadBuilder for TestUploadBuilder<'a> {
initiate_upload(self, _: u64) -> Result<impl Uploader, Error>652 async fn initiate_upload(self, _: u64) -> Result<impl Uploader, Error> {
653 Ok(TestUploader(0, self.0))
654 }
655 }
656
657 impl<'a> InfoSender for TestUploadBuilder<'a> {
send_formatted_info<F: FnOnce(&mut dyn Write)>( &mut self, _: F, ) -> Result<(), Error>658 async fn send_formatted_info<F: FnOnce(&mut dyn Write)>(
659 &mut self,
660 _: F,
661 ) -> Result<(), Error> {
662 // Not needed currently.
663 Ok(())
664 }
665 }
666
667 // (Bytes sent, upload buffer)
668 struct TestUploader<'a>(usize, &'a mut [u8]);
669
670 impl Uploader for TestUploader<'_> {
upload(&mut self, data: &[u8]) -> Result<(), Error>671 async fn upload(&mut self, data: &[u8]) -> Result<(), Error> {
672 self.1[self.0..][..data.len()].clone_from_slice(data);
673 self.0 = self.0.checked_add(data.len()).unwrap();
674 Ok(())
675 }
676 }
677 }
678
679 const MAX_DOWNLOAD_SIZE_NAME: &'static str = "max-download-size";
680
681 /// Converts a null-terminated command line string where arguments are separated by ':' into an
682 /// iterator of individual argument as CStr.
cmd_to_c_string_args(cmd: &mut [u8]) -> impl Iterator<Item = &CStr> + Clone683 fn cmd_to_c_string_args(cmd: &mut [u8]) -> impl Iterator<Item = &CStr> + Clone {
684 let end = cmd.iter().position(|v| *v == 0).unwrap();
685 // Replace ':' with NULL.
686 cmd.iter_mut().filter(|v| **v == b':').for_each(|v| *v = 0);
687 cmd[..end + 1].split_inclusive(|v| *v == 0).map(|v| CStr::from_bytes_until_nul(v).unwrap())
688 }
689
690 /// Helper for handling "fastboot getvar ..."
get_var( cmd: &mut [u8], transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>691 async fn get_var(
692 cmd: &mut [u8],
693 transport: &mut impl Transport,
694 fb_impl: &mut impl FastbootImplementation,
695 ) -> Result<()> {
696 let mut resp = Responder::new(transport);
697 let mut args = cmd_to_c_string_args(cmd).skip(1);
698 let Some(var) = args.next() else {
699 return reply_fail!(resp, "Missing variable");
700 };
701
702 match var.to_str()? {
703 "all" => return get_var_all(transport, fb_impl).await,
704 MAX_DOWNLOAD_SIZE_NAME => {
705 return reply_okay!(resp, "{:#x}", fb_impl.get_download_buffer().await.len());
706 }
707 _ => {
708 let mut val = [0u8; MAX_RESPONSE_SIZE];
709 match fb_impl.get_var_as_str(var, args, &mut resp, &mut val[..]).await {
710 Ok(s) => reply_okay!(resp, "{}", s),
711 Err(e) => reply_fail!(resp, "{}", e.to_str()),
712 }
713 }
714 }
715 }
716
717 /// A wrapper of `get_var_all()` that first iterates reserved variables.
get_var_all_with_native( fb_impl: &mut impl FastbootImplementation, mut sender: impl VarInfoSender, ) -> CommandResult<()>718 async fn get_var_all_with_native(
719 fb_impl: &mut impl FastbootImplementation,
720 mut sender: impl VarInfoSender,
721 ) -> CommandResult<()> {
722 // Process the built-in MAX_DOWNLOAD_SIZE_NAME variable.
723 let mut size_str = [0u8; 32];
724 let size_str = snprintf!(size_str, "{:#x}", fb_impl.get_download_buffer().await.len());
725 sender.send_var_info(MAX_DOWNLOAD_SIZE_NAME, [], size_str).await?;
726 fb_impl.get_var_all(sender).await
727 }
728
729 /// Method for handling "fastboot getvar all"
get_var_all( transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>730 async fn get_var_all(
731 transport: &mut impl Transport,
732 fb_impl: &mut impl FastbootImplementation,
733 ) -> Result<()> {
734 let mut resp = Responder::new(transport);
735 // Don't allow custom INFO messages because variable values are sent as INFO messages.
736 let get_res = get_var_all_with_native(fb_impl, &mut resp).await;
737 match get_res {
738 Ok(()) => reply_okay!(resp, ""),
739 Err(e) => reply_fail!(resp, "{}", e.to_str()),
740 }
741 }
742
743 /// Helper for handling "fastboot download:...".
download( mut args: Split<'_, char>, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>744 async fn download(
745 mut args: Split<'_, char>,
746 transport: &mut impl Transport,
747 fb_impl: &mut impl FastbootImplementation,
748 ) -> Result<()> {
749 let mut resp = Responder::new(transport);
750 let total_download_size = match (|| -> CommandResult<usize> {
751 usize::try_from(next_arg_u64(&mut args)?.ok_or("Not enough argument")?)
752 .map_err(|_| "Download size overflow".into())
753 })() {
754 Err(e) => return reply_fail!(resp, "{}", e.to_str()),
755 Ok(v) => v,
756 };
757 let download_buffer = &mut fb_impl.get_download_buffer().await;
758 if total_download_size > download_buffer.len() {
759 return reply_fail!(resp, "Download size is too big");
760 } else if total_download_size == 0 {
761 return reply_fail!(resp, "Zero download size");
762 }
763
764 // Starts the download
765 let download_buffer = &mut download_buffer[..total_download_size];
766 // `total_download_size` is parsed from `next_arg_u64` and thus should fit into u64.
767 resp.send_data_message(u64::try_from(total_download_size).unwrap()).await?;
768 let mut downloaded = 0;
769 while downloaded < total_download_size {
770 let (_, remains) = &mut download_buffer.split_at_mut(downloaded);
771 match resp.transport.receive_packet(remains).await? {
772 0 => yield_now().await,
773 v => match downloaded.checked_add(v) {
774 Some(v) if v > total_download_size => {
775 return reply_fail!(resp, "More data received then expected");
776 }
777 Some(v) => downloaded = v,
778 _ => return Err(Error::Other(Some("Invalid read size from transport"))),
779 },
780 };
781 }
782 match fb_impl.download_complete(downloaded, &mut resp).await {
783 Ok(()) => reply_okay!(resp, ""),
784 Err(e) => reply_fail!(resp, "{}", e.to_str()),
785 }
786 }
787
788 /// Helper for handling "fastboot flash ...".
flash( cmd: &str, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>789 async fn flash(
790 cmd: &str,
791 transport: &mut impl Transport,
792 fb_impl: &mut impl FastbootImplementation,
793 ) -> Result<()> {
794 let mut resp = Responder::new(transport);
795 let flash_res =
796 match cmd.strip_prefix("flash:").ok_or::<CommandError>("Missing partition".into()) {
797 Ok(part) => fb_impl.flash(part, &mut resp).await,
798 Err(e) => Err(e),
799 };
800 match flash_res {
801 Err(e) => reply_fail!(resp, "{}", e.to_str()),
802 _ => reply_okay!(resp, ""),
803 }
804 }
805
806 /// Helper for handling "fastboot erase ...".
erase( cmd: &str, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>807 async fn erase(
808 cmd: &str,
809 transport: &mut impl Transport,
810 fb_impl: &mut impl FastbootImplementation,
811 ) -> Result<()> {
812 let mut resp = Responder::new(transport);
813 let flash_res =
814 match cmd.strip_prefix("erase:").ok_or::<CommandError>("Missing partition".into()) {
815 Ok(part) => fb_impl.erase(part, &mut resp).await,
816 Err(e) => Err(e),
817 };
818 match flash_res {
819 Err(e) => reply_fail!(resp, "{}", e.to_str()),
820 _ => reply_okay!(resp, ""),
821 }
822 }
823
824 /// Helper for handling "fastboot get_staged ...".
upload( transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>825 async fn upload(
826 transport: &mut impl Transport,
827 fb_impl: &mut impl FastbootImplementation,
828 ) -> Result<()> {
829 let mut resp = Responder::new(transport);
830 let upload_res = fb_impl.upload(&mut resp).await;
831 match resp.remaining_upload > 0 {
832 true => return Err(Error::InvalidInput),
833 _ => match upload_res {
834 Err(e) => reply_fail!(resp, "{}", e.to_str()),
835 _ => reply_okay!(resp, ""),
836 },
837 }
838 }
839
840 /// Helper for handling "fastboot fetch ...".
fetch( cmd: &str, args: Split<'_, char>, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>841 async fn fetch(
842 cmd: &str,
843 args: Split<'_, char>,
844 transport: &mut impl Transport,
845 fb_impl: &mut impl FastbootImplementation,
846 ) -> Result<()> {
847 let mut resp = Responder::new(transport);
848 let fetch_res = async {
849 let cmd = cmd.strip_prefix("fetch:").ok_or::<CommandError>("Missing arguments".into())?;
850 if args.clone().count() < 3 {
851 return Err("Not enough argments".into());
852 }
853 // Parses backward. Parses size, offset first and treats the remaining string as
854 // partition name. This allows ":" in partition name.
855 let mut rev = args.clone().rev();
856 let sz = next_arg(&mut rev).ok_or("Missing size")?;
857 let off = next_arg(&mut rev).ok_or("Invalid offset")?;
858 let part = &cmd[..cmd.len() - (off.len() + sz.len() + 2)];
859 fb_impl.fetch(part, hex_to_u64(off)?, hex_to_u64(sz)?, &mut resp).await
860 }
861 .await;
862 match resp.remaining_upload > 0 {
863 true => return Err(Error::InvalidInput),
864 _ => match fetch_res {
865 Err(e) => reply_fail!(resp, "{}", e.to_str()),
866 _ => reply_okay!(resp, ""),
867 },
868 }
869 }
870
871 // Handles `fastboot reboot*`
reboot( mode: RebootMode, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>872 async fn reboot(
873 mode: RebootMode,
874 transport: &mut impl Transport,
875 fb_impl: &mut impl FastbootImplementation,
876 ) -> Result<()> {
877 let mut resp = Responder::new(transport);
878 let e = fb_impl.reboot(mode, &mut resp).await;
879 reply_fail!(resp, "{}", e.to_str())
880 }
881
882 // Handles `fastboot boot`
boot( transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>883 async fn boot(
884 transport: &mut impl Transport,
885 fb_impl: &mut impl FastbootImplementation,
886 ) -> Result<()> {
887 let mut resp = Responder::new(transport);
888 let boot_res = async { fb_impl.boot(&mut resp).await }.await;
889 match boot_res {
890 Err(e) => reply_fail!(resp, "{}", e.to_str()),
891 _ => reply_okay!(resp, "boot_command"),
892 }
893 }
894
895 // Handles `fastboot continue`
896 async fn r#continue(
897 transport: &mut impl Transport,
898 fb_impl: &mut impl FastbootImplementation,
899 ) -> Result<()> {
900 let mut resp = Responder::new(transport);
901 match fb_impl.r#continue(&mut resp).await {
902 Ok(_) => reply_okay!(resp, ""),
903 Err(e) => reply_fail!(resp, "{}", e.to_str()),
904 }
905 }
906
907 // Handles `fastboot set_active`
set_active( mut args: Split<'_, char>, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>908 async fn set_active(
909 mut args: Split<'_, char>,
910 transport: &mut impl Transport,
911 fb_impl: &mut impl FastbootImplementation,
912 ) -> Result<()> {
913 let mut resp = Responder::new(transport);
914 let res = async {
915 let slot = next_arg(&mut args).ok_or("Missing slot")?;
916 fb_impl.set_active(slot, &mut resp).await
917 };
918 match res.await {
919 Ok(_) => reply_okay!(resp, ""),
920 Err(e) => reply_fail!(resp, "{}", e.to_str()),
921 }
922 }
923
924 /// Helper for handling "fastboot oem ...".
oem( cmd: &str, transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>925 async fn oem(
926 cmd: &str,
927 transport: &mut impl Transport,
928 fb_impl: &mut impl FastbootImplementation,
929 ) -> Result<()> {
930 let mut resp = Responder::new(transport);
931 let mut oem_out = [0u8; MAX_RESPONSE_SIZE - 4];
932 let oem_res = fb_impl.oem(cmd, &mut resp, &mut oem_out[..]).await;
933 match oem_res {
934 Ok(msg) => match from_utf8(msg) {
935 Ok(s) => reply_okay!(resp, "{}", s),
936 Err(e) => reply_fail!(resp, "Invalid return string {}", e),
937 },
938 Err(e) => reply_fail!(resp, "{}", e.to_str()),
939 }
940 }
941
942 /// Process the next Fastboot command from the transport.
943 ///
944 /// # Returns
945 ///
946 /// * Returns Ok(is_continue) on success where `is_continue` is true if command is
947 /// `fastboot continue`.
948 /// * Returns Err() on errors.
process_next_command( transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<bool>949 pub async fn process_next_command(
950 transport: &mut impl Transport,
951 fb_impl: &mut impl FastbootImplementation,
952 ) -> Result<bool> {
953 let mut packet = [0u8; MAX_COMMAND_SIZE + 1];
954 let cmd_size = match transport.receive_packet(&mut packet[..MAX_COMMAND_SIZE]).await? {
955 0 => return Ok(false),
956 v => v,
957 };
958 let Ok(cmd_str) = from_utf8(&packet[..cmd_size]) else {
959 transport.send_packet(fastboot_fail!(packet, "Invalid Command")).await?;
960 return Ok(false);
961 };
962 let mut args = cmd_str.split(':');
963 let Some(cmd) = args.next() else {
964 return transport.send_packet(fastboot_fail!(packet, "No command")).await.map(|_| false);
965 };
966 match cmd {
967 "boot" => {
968 boot(transport, fb_impl).await?;
969 return Ok(true);
970 }
971 "continue" => {
972 r#continue(transport, fb_impl).await?;
973 return Ok(true);
974 }
975 "download" => download(args, transport, fb_impl).await,
976 "erase" => erase(cmd_str, transport, fb_impl).await,
977 "fetch" => fetch(cmd_str, args, transport, fb_impl).await,
978 "flash" => flash(cmd_str, transport, fb_impl).await,
979 "getvar" => get_var(&mut packet[..], transport, fb_impl).await,
980 "reboot" => reboot(RebootMode::Normal, transport, fb_impl).await,
981 "reboot-bootloader" => reboot(RebootMode::Bootloader, transport, fb_impl).await,
982 "reboot-fastboot" => reboot(RebootMode::Fastboot, transport, fb_impl).await,
983 "reboot-recovery" => reboot(RebootMode::Recovery, transport, fb_impl).await,
984 "set_active" => set_active(args, transport, fb_impl).await,
985 "upload" => upload(transport, fb_impl).await,
986 _ if cmd_str.starts_with("oem ") => oem(&cmd_str[4..], transport, fb_impl).await,
987 _ => transport.send_packet(fastboot_fail!(packet, "Command not found")).await,
988 }?;
989 Ok(false)
990 }
991
992 /// Keeps polling and processing fastboot commands from the transport.
993 ///
994 /// # Returns
995 ///
996 /// * Returns Ok(()) if "fastboot continue" is received.
997 /// * Returns Err() on errors.
run( transport: &mut impl Transport, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>998 pub async fn run(
999 transport: &mut impl Transport,
1000 fb_impl: &mut impl FastbootImplementation,
1001 ) -> Result<()> {
1002 while !process_next_command(transport, fb_impl).await? {}
1003 Ok(())
1004 }
1005
1006 /// Runs a fastboot over TCP session.
1007 ///
1008 /// The method performs fastboot over TCP handshake and then call `run(...)`.
1009 ///
1010 /// Returns Ok(()) if "fastboot continue" is received.
run_tcp_session( tcp_stream: &mut impl TcpStream, fb_impl: &mut impl FastbootImplementation, ) -> Result<()>1011 pub async fn run_tcp_session(
1012 tcp_stream: &mut impl TcpStream,
1013 fb_impl: &mut impl FastbootImplementation,
1014 ) -> Result<()> {
1015 run(&mut TcpTransport::new_and_handshake(tcp_stream)?, fb_impl).await
1016 }
1017
1018 /// A helper data structure for writing formatted string to fixed size bytes array.
1019 #[derive(Debug)]
1020 pub struct FormattedBytes<T: AsMut<[u8]>>(T, usize);
1021
1022 impl<T: AsMut<[u8]>> FormattedBytes<T> {
1023 /// Create an instance.
new(buf: T) -> Self1024 pub fn new(buf: T) -> Self {
1025 Self(buf, 0)
1026 }
1027
1028 /// Get the size of content.
size(&self) -> usize1029 pub fn size(&self) -> usize {
1030 self.1
1031 }
1032
1033 /// Appends the given `bytes` to the contents.
1034 ///
1035 /// If `bytes` exceeds the remaining buffer space, any excess bytes are discarded.
1036 ///
1037 /// Returns the resulting contents.
append(&mut self, bytes: &[u8]) -> &mut [u8]1038 pub fn append(&mut self, bytes: &[u8]) -> &mut [u8] {
1039 let buf = &mut self.0.as_mut()[self.1..];
1040 // Only write as much as the size of the bytes buffer. Additional write is silently
1041 // ignored.
1042 let to_write = min(buf.len(), bytes.len());
1043 buf[..to_write].clone_from_slice(&bytes[..to_write]);
1044 self.1 += to_write;
1045 &mut self.0.as_mut()[..self.1]
1046 }
1047 }
1048
1049 impl<T: AsMut<[u8]>> core::fmt::Write for FormattedBytes<T> {
write_str(&mut self, s: &str) -> core::fmt::Result1050 fn write_str(&mut self, s: &str) -> core::fmt::Result {
1051 self.append(s.as_bytes());
1052 Ok(())
1053 }
1054 }
1055
1056 /// A convenient macro that behaves similar to snprintf in C.
1057 #[macro_export]
1058 macro_rules! snprintf {
1059 ( $arr:expr, $( $x:expr ),* ) => {
1060 {
1061 let mut formatted_bytes = FormattedBytes::new(&mut $arr[..]);
1062 write!(formatted_bytes, $($x,)*).unwrap();
1063 let size = formatted_bytes.size();
1064 from_utf8(&$arr[..size]).unwrap()
1065 }
1066 };
1067 }
1068
1069 /// A helper to convert a hex string into u64.
hex_to_u64(s: &str) -> CommandResult<u64>1070 pub(crate) fn hex_to_u64(s: &str) -> CommandResult<u64> {
1071 Ok(u64::from_str_radix(s.strip_prefix("0x").unwrap_or(s), 16)?)
1072 }
1073
1074 /// A helper to check and fetch the next non-empty argument.
1075 ///
1076 /// # Args
1077 ///
1078 /// args: A string iterator.
next_arg<'a, T: Iterator<Item = &'a str>>(args: &mut T) -> Option<&'a str>1079 pub fn next_arg<'a, T: Iterator<Item = &'a str>>(args: &mut T) -> Option<&'a str> {
1080 args.next().filter(|v| *v != "")
1081 }
1082
1083 /// A helper to check and fetch the next argument as a u64 hex string.
1084 ///
1085 /// # Args
1086 ///
1087 /// args: A string iterator.
1088 ///
1089 ///
1090 /// # Returns
1091 ///
1092 /// * Returns Ok(Some(v)) is next argument is available and a valid u64 hex.
1093 /// * Returns Ok(None) is next argument is not available
1094 /// * Returns Err() if next argument is present but not a valid u64 hex.
next_arg_u64<'a, T: Iterator<Item = &'a str>>(args: &mut T) -> CommandResult<Option<u64>>1095 pub fn next_arg_u64<'a, T: Iterator<Item = &'a str>>(args: &mut T) -> CommandResult<Option<u64>> {
1096 match next_arg(args) {
1097 Some(v) => Ok(Some(hex_to_u64(v)?)),
1098 _ => Ok(None),
1099 }
1100 }
1101
1102 #[cfg(test)]
1103 mod test {
1104 use super::*;
1105 use std::collections::{BTreeMap, VecDeque};
1106
1107 #[derive(Default)]
1108 struct FastbootTest {
1109 // A mapping from (variable name, argument) to variable value.
1110 vars: BTreeMap<(&'static str, &'static [&'static str]), &'static str>,
1111 // The partition arg from Fastboot flash command
1112 flash_partition: String,
1113 // The partition arg from Fastboot erase command
1114 erase_partition: String,
1115 // Upload size, batches of data to upload,
1116 upload_config: (u64, Vec<Vec<u8>>),
1117 // A map from partition name to (upload size override, partition data)
1118 fetch_data: BTreeMap<&'static str, (u64, Vec<u8>)>,
1119 // result string, INFO strings.
1120 oem_output: (String, Vec<String>),
1121 oem_command: String,
1122 download_buffer: Vec<u8>,
1123 downloaded_size: usize,
1124 reboot_mode: Option<RebootMode>,
1125 active_slot: Option<String>,
1126 }
1127
1128 impl FastbootImplementation for FastbootTest {
get_var( &mut self, var: &CStr, args: impl Iterator<Item = &'_ CStr> + Clone, out: &mut [u8], _: impl InfoSender, ) -> CommandResult<usize>1129 async fn get_var(
1130 &mut self,
1131 var: &CStr,
1132 args: impl Iterator<Item = &'_ CStr> + Clone,
1133 out: &mut [u8],
1134 _: impl InfoSender,
1135 ) -> CommandResult<usize> {
1136 let args = args.map(|v| v.to_str().unwrap()).collect::<Vec<_>>();
1137 match self.vars.get(&(var.to_str()?, &args[..])) {
1138 Some(v) => {
1139 out[..v.len()].clone_from_slice(v.as_bytes());
1140 Ok(v.len())
1141 }
1142 _ => Err("Not Found".into()),
1143 }
1144 }
1145
get_var_all(&mut self, mut responder: impl VarInfoSender) -> CommandResult<()>1146 async fn get_var_all(&mut self, mut responder: impl VarInfoSender) -> CommandResult<()> {
1147 for ((var, config), value) in &self.vars {
1148 responder.send_var_info(var, config.iter().copied(), value).await?;
1149 }
1150 Ok(())
1151 }
1152
get_download_buffer(&mut self) -> &mut [u8]1153 async fn get_download_buffer(&mut self) -> &mut [u8] {
1154 self.download_buffer.as_mut_slice()
1155 }
1156
download_complete( &mut self, download_size: usize, _: impl InfoSender, ) -> CommandResult<()>1157 async fn download_complete(
1158 &mut self,
1159 download_size: usize,
1160 _: impl InfoSender,
1161 ) -> CommandResult<()> {
1162 self.downloaded_size = download_size;
1163 Ok(())
1164 }
1165
flash(&mut self, part: &str, _: impl InfoSender) -> CommandResult<()>1166 async fn flash(&mut self, part: &str, _: impl InfoSender) -> CommandResult<()> {
1167 self.flash_partition = part.into();
1168 Ok(())
1169 }
1170
erase(&mut self, part: &str, _: impl InfoSender) -> CommandResult<()>1171 async fn erase(&mut self, part: &str, _: impl InfoSender) -> CommandResult<()> {
1172 self.erase_partition = part.into();
1173 Ok(())
1174 }
1175
upload(&mut self, responder: impl UploadBuilder) -> CommandResult<()>1176 async fn upload(&mut self, responder: impl UploadBuilder) -> CommandResult<()> {
1177 let (size, batches) = &self.upload_config;
1178 let mut uploader = responder.initiate_upload(*size).await?;
1179 for ele in batches {
1180 uploader.upload(&ele[..]).await?;
1181 }
1182 Ok(())
1183 }
1184
fetch( &mut self, part: &str, offset: u64, size: u64, responder: impl UploadBuilder + InfoSender, ) -> CommandResult<()>1185 async fn fetch(
1186 &mut self,
1187 part: &str,
1188 offset: u64,
1189 size: u64,
1190 responder: impl UploadBuilder + InfoSender,
1191 ) -> CommandResult<()> {
1192 let (size_override, data) = self.fetch_data.get(part).ok_or("Not Found")?;
1193 let mut uploader = responder.initiate_upload(*size_override).await?;
1194 Ok(uploader
1195 .upload(&data[offset.try_into().unwrap()..][..size.try_into().unwrap()])
1196 .await?)
1197 }
1198
boot(&mut self, mut responder: impl InfoSender + OkaySender) -> CommandResult<()>1199 async fn boot(&mut self, mut responder: impl InfoSender + OkaySender) -> CommandResult<()> {
1200 Ok(responder.send_info("Boot to boot.img...").await?)
1201 }
1202
reboot( &mut self, mode: RebootMode, responder: impl InfoSender + OkaySender, ) -> CommandError1203 async fn reboot(
1204 &mut self,
1205 mode: RebootMode,
1206 responder: impl InfoSender + OkaySender,
1207 ) -> CommandError {
1208 responder.send_okay("").await.unwrap();
1209 self.reboot_mode = Some(mode);
1210 "reboot-return".into()
1211 }
1212
1213 async fn r#continue(&mut self, mut responder: impl InfoSender) -> CommandResult<()> {
1214 Ok(responder.send_info("Continuing to boot...").await?)
1215 }
1216
set_active(&mut self, slot: &str, _: impl InfoSender) -> CommandResult<()>1217 async fn set_active(&mut self, slot: &str, _: impl InfoSender) -> CommandResult<()> {
1218 self.active_slot = Some(slot.into());
1219 Ok(())
1220 }
1221
oem<'b>( &mut self, cmd: &str, mut responder: impl InfoSender, res: &'b mut [u8], ) -> CommandResult<&'b [u8]>1222 async fn oem<'b>(
1223 &mut self,
1224 cmd: &str,
1225 mut responder: impl InfoSender,
1226 res: &'b mut [u8],
1227 ) -> CommandResult<&'b [u8]> {
1228 let (res_str, infos) = &mut self.oem_output;
1229 self.oem_command = cmd.into();
1230 for ele in infos {
1231 responder.send_info(ele.as_str()).await?;
1232 }
1233 Ok(snprintf!(res, "{}", *res_str).as_bytes())
1234 }
1235 }
1236
1237 struct TestTransport {
1238 in_queue: VecDeque<Vec<u8>>,
1239 out_queue: VecDeque<Vec<u8>>,
1240 }
1241
1242 impl TestTransport {
new() -> Self1243 fn new() -> Self {
1244 Self { in_queue: VecDeque::new(), out_queue: VecDeque::new() }
1245 }
1246
add_input(&mut self, packet: &[u8])1247 fn add_input(&mut self, packet: &[u8]) {
1248 self.in_queue.push_back(packet.into());
1249 }
1250 }
1251
1252 impl Transport for TestTransport {
receive_packet(&mut self, out: &mut [u8]) -> Result<usize>1253 async fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize> {
1254 match self.in_queue.pop_front() {
1255 Some(v) => {
1256 let size = min(out.len(), v.len());
1257 out[..size].clone_from_slice(&v[..size]);
1258 // Returns the input length so that we can test bogus download size.
1259 Ok(v.len())
1260 }
1261 _ => Err(Error::Other(Some("No more data"))),
1262 }
1263 }
1264
send_packet(&mut self, packet: &[u8]) -> Result<()>1265 async fn send_packet(&mut self, packet: &[u8]) -> Result<()> {
1266 self.out_queue.push_back(packet.into());
1267 Ok(())
1268 }
1269 }
1270
1271 #[derive(Default)]
1272 struct TestTcpStream {
1273 in_queue: VecDeque<u8>,
1274 out_queue: VecDeque<u8>,
1275 }
1276
1277 impl TestTcpStream {
1278 /// Adds bytes to input stream.
add_input(&mut self, data: &[u8])1279 fn add_input(&mut self, data: &[u8]) {
1280 data.iter().for_each(|v| self.in_queue.push_back(*v));
1281 }
1282
1283 /// Adds a length pre-fixed bytes stream.
add_length_prefixed_input(&mut self, data: &[u8])1284 fn add_length_prefixed_input(&mut self, data: &[u8]) {
1285 self.add_input(&(data.len() as u64).to_be_bytes());
1286 self.add_input(data);
1287 }
1288 }
1289
1290 impl TcpStream for TestTcpStream {
read_exact(&mut self, out: &mut [u8]) -> Result<()>1291 async fn read_exact(&mut self, out: &mut [u8]) -> Result<()> {
1292 for ele in out {
1293 *ele = self.in_queue.pop_front().ok_or(Error::OperationProhibited)?;
1294 }
1295 Ok(())
1296 }
1297
write_exact(&mut self, data: &[u8]) -> Result<()>1298 async fn write_exact(&mut self, data: &[u8]) -> Result<()> {
1299 data.iter().for_each(|v| self.out_queue.push_back(*v));
1300 Ok(())
1301 }
1302 }
1303
1304 #[test]
test_boot()1305 fn test_boot() {
1306 let mut fastboot_impl: FastbootTest = Default::default();
1307 fastboot_impl.download_buffer = vec![0u8; 1024];
1308 let mut transport = TestTransport::new();
1309 transport.add_input(b"boot");
1310 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1311 assert_eq!(
1312 transport.out_queue,
1313 VecDeque::<Vec<u8>>::from([
1314 b"INFOBoot to boot.img...".into(),
1315 b"OKAYboot_command".into()
1316 ])
1317 );
1318 }
1319
1320 #[test]
test_non_exist_command()1321 fn test_non_exist_command() {
1322 let mut fastboot_impl: FastbootTest = Default::default();
1323 fastboot_impl.download_buffer = vec![0u8; 1024];
1324 let mut transport = TestTransport::new();
1325 transport.add_input(b"non_exist");
1326 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1327 assert_eq!(transport.out_queue, [b"FAILCommand not found"]);
1328 }
1329
1330 #[test]
test_non_ascii_command_string()1331 fn test_non_ascii_command_string() {
1332 let mut fastboot_impl: FastbootTest = Default::default();
1333 fastboot_impl.download_buffer = vec![0u8; 1024];
1334 let mut transport = TestTransport::new();
1335 transport.add_input(b"\xff\xff\xff");
1336 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1337 assert_eq!(transport.out_queue, [b"FAILInvalid Command"]);
1338 }
1339
1340 #[test]
test_get_var_max_download_size()1341 fn test_get_var_max_download_size() {
1342 let mut fastboot_impl: FastbootTest = Default::default();
1343 fastboot_impl.download_buffer = vec![0u8; 1024];
1344 let mut transport = TestTransport::new();
1345 transport.add_input(b"getvar:max-download-size");
1346 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1347 assert_eq!(transport.out_queue, [b"OKAY0x400"]);
1348 }
1349
1350 #[test]
test_get_var()1351 fn test_get_var() {
1352 let mut fastboot_impl: FastbootTest = Default::default();
1353 let vars: [((&str, &[&str]), &str); 4] = [
1354 (("var_0", &[]), "val_0"),
1355 (("var_1", &["a", "b"]), "val_1_a_b"),
1356 (("var_1", &["c", "d"]), "val_1_c_d"),
1357 (("var_2", &["e", "f"]), "val_2_e_f"),
1358 ];
1359 fastboot_impl.vars = BTreeMap::from(vars);
1360
1361 fastboot_impl.download_buffer = vec![0u8; 1024];
1362 let mut transport = TestTransport::new();
1363 transport.add_input(b"getvar:var_0");
1364 transport.add_input(b"getvar:var_1:a:b");
1365 transport.add_input(b"getvar:var_1:c:d");
1366 transport.add_input(b"getvar:var_1"); // Not Found
1367 transport.add_input(b"getvar:var_2:e:f");
1368 transport.add_input(b"getvar:var_3"); // Not Found
1369 transport.add_input(b"getvar"); // Not Found
1370
1371 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1372 assert_eq!(
1373 transport.out_queue,
1374 VecDeque::<Vec<u8>>::from([
1375 b"OKAYval_0".into(),
1376 b"OKAYval_1_a_b".into(),
1377 b"OKAYval_1_c_d".into(),
1378 b"FAILNot Found".into(),
1379 b"OKAYval_2_e_f".into(),
1380 b"FAILNot Found".into(),
1381 b"FAILMissing variable".into(),
1382 ])
1383 );
1384 }
1385
1386 #[test]
test_get_var_all()1387 fn test_get_var_all() {
1388 let mut fastboot_impl: FastbootTest = Default::default();
1389 let vars: [((&str, &[&str]), &str); 4] = [
1390 (("var_0", &[]), "val_0"),
1391 (("var_1", &["a", "b"]), "val_1_a_b"),
1392 (("var_1", &["c", "d"]), "val_1_c_d"),
1393 (("var_2", &["e", "f"]), "val_2_e_f"),
1394 ];
1395 fastboot_impl.vars = BTreeMap::from(vars);
1396
1397 fastboot_impl.download_buffer = vec![0u8; 1024];
1398 let mut transport = TestTransport::new();
1399 transport.add_input(b"getvar:all");
1400 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1401 assert_eq!(
1402 transport.out_queue,
1403 VecDeque::<Vec<u8>>::from([
1404 b"INFOmax-download-size: 0x400".into(),
1405 b"INFOvar_0: val_0".into(),
1406 b"INFOvar_1:a:b: val_1_a_b".into(),
1407 b"INFOvar_1:c:d: val_1_c_d".into(),
1408 b"INFOvar_2:e:f: val_2_e_f".into(),
1409 b"OKAY".into(),
1410 ])
1411 );
1412 }
1413
1414 #[test]
test_download()1415 fn test_download() {
1416 let mut fastboot_impl: FastbootTest = Default::default();
1417 fastboot_impl.download_buffer = vec![0u8; 1024];
1418 let download_content: Vec<u8> =
1419 (0..fastboot_impl.download_buffer.len()).into_iter().map(|v| v as u8).collect();
1420 let mut transport = TestTransport::new();
1421 // Splits download into two batches.
1422 let (first, second) = download_content.as_slice().split_at(download_content.len() / 2);
1423 transport.add_input(format!("download:{:#x}", download_content.len()).as_bytes());
1424 transport.add_input(first);
1425 transport.add_input(second);
1426 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1427 assert_eq!(
1428 transport.out_queue,
1429 VecDeque::<Vec<u8>>::from([b"DATA00000400".into(), b"OKAY".into(),])
1430 );
1431 assert_eq!(fastboot_impl.downloaded_size, download_content.len());
1432 assert_eq!(fastboot_impl.download_buffer, download_content);
1433 }
1434
1435 #[test]
test_download_not_enough_args()1436 fn test_download_not_enough_args() {
1437 let mut fastboot_impl: FastbootTest = Default::default();
1438 fastboot_impl.download_buffer = vec![0u8; 1024];
1439 let mut transport = TestTransport::new();
1440 transport.add_input(b"download");
1441 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1442 assert_eq!(transport.out_queue, [b"FAILNot enough argument"]);
1443 }
1444
1445 #[test]
test_download_invalid_hex_string()1446 fn test_download_invalid_hex_string() {
1447 let mut fastboot_impl: FastbootTest = Default::default();
1448 fastboot_impl.download_buffer = vec![0u8; 1024];
1449 let mut transport = TestTransport::new();
1450 transport.add_input(b"download:hhh");
1451 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1452 assert_eq!(transport.out_queue.len(), 1);
1453 assert!(transport.out_queue[0].starts_with(b"FAIL"));
1454 }
1455
test_download_size(download_buffer_size: usize, download_size: usize, msg: &str)1456 fn test_download_size(download_buffer_size: usize, download_size: usize, msg: &str) {
1457 let mut fastboot_impl: FastbootTest = Default::default();
1458 fastboot_impl.download_buffer = vec![0u8; download_buffer_size];
1459 let mut transport = TestTransport::new();
1460 transport.add_input(format!("download:{:#x}", download_size).as_bytes());
1461 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1462 assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([msg.as_bytes().into()]));
1463 }
1464
1465 #[test]
test_download_download_size_too_big()1466 fn test_download_download_size_too_big() {
1467 test_download_size(1024, 1025, "FAILDownload size is too big");
1468 }
1469
1470 #[test]
test_download_zero_download_size()1471 fn test_download_zero_download_size() {
1472 test_download_size(1024, 0, "FAILZero download size");
1473 }
1474
1475 #[test]
test_download_more_than_expected()1476 fn test_download_more_than_expected() {
1477 let mut fastboot_impl: FastbootTest = Default::default();
1478 fastboot_impl.download_buffer = vec![0u8; 1024];
1479 let download_content: Vec<u8> = vec![0u8; fastboot_impl.download_buffer.len()];
1480 let mut transport = TestTransport::new();
1481 transport.add_input(format!("download:{:#x}", download_content.len() - 1).as_bytes());
1482 transport.add_input(&download_content[..]);
1483 // State should be reset to command state.
1484 transport.add_input(b"getvar:max-download-size");
1485 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1486 assert_eq!(
1487 transport.out_queue,
1488 VecDeque::<Vec<u8>>::from([
1489 b"DATA000003ff".into(),
1490 b"FAILMore data received then expected".into(),
1491 b"OKAY0x400".into(),
1492 ])
1493 );
1494 }
1495
1496 #[test]
test_oem_cmd()1497 fn test_oem_cmd() {
1498 let mut fastboot_impl: FastbootTest = Default::default();
1499 fastboot_impl.download_buffer = vec![0u8; 2048];
1500 let mut transport = TestTransport::new();
1501 transport.add_input(b"oem oem-command");
1502 fastboot_impl.oem_output =
1503 ("oem-return".into(), vec!["oem-info-1".into(), "oem-info-2".into()]);
1504 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1505 assert_eq!(fastboot_impl.oem_command, "oem-command");
1506 assert_eq!(
1507 transport.out_queue,
1508 VecDeque::<Vec<u8>>::from([
1509 b"INFOoem-info-1".into(),
1510 b"INFOoem-info-2".into(),
1511 b"OKAYoem-return".into(),
1512 ])
1513 );
1514 }
1515
1516 #[test]
test_flash()1517 fn test_flash() {
1518 let mut fastboot_impl: FastbootTest = Default::default();
1519 fastboot_impl.download_buffer = vec![0u8; 2048];
1520 let mut transport = TestTransport::new();
1521 transport.add_input(b"flash:boot_a:0::");
1522 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1523 assert_eq!(fastboot_impl.flash_partition, "boot_a:0::");
1524 assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([b"OKAY".into()]));
1525 }
1526
1527 #[test]
test_flash_missing_partition()1528 fn test_flash_missing_partition() {
1529 let mut fastboot_impl: FastbootTest = Default::default();
1530 let mut transport = TestTransport::new();
1531 transport.add_input(b"flash");
1532 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1533 assert_eq!(transport.out_queue, [b"FAILMissing partition"]);
1534 }
1535
1536 #[test]
test_erase()1537 fn test_erase() {
1538 let mut fastboot_impl: FastbootTest = Default::default();
1539 fastboot_impl.download_buffer = vec![0u8; 2048];
1540 let mut transport = TestTransport::new();
1541 transport.add_input(b"erase:boot_a:0::");
1542 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1543 assert_eq!(fastboot_impl.erase_partition, "boot_a:0::");
1544 assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([b"OKAY".into()]));
1545 }
1546
1547 #[test]
test_erase_missing_partition()1548 fn test_erase_missing_partition() {
1549 let mut fastboot_impl: FastbootTest = Default::default();
1550 let mut transport = TestTransport::new();
1551 transport.add_input(b"erase");
1552 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1553 assert_eq!(transport.out_queue, [b"FAILMissing partition"]);
1554 }
1555
1556 #[test]
test_upload()1557 fn test_upload() {
1558 let mut fastboot_impl: FastbootTest = Default::default();
1559 let upload_content: Vec<u8> = (0..1024).into_iter().map(|v| v as u8).collect();
1560 let mut transport = TestTransport::new();
1561 transport.add_input(b"upload");
1562 fastboot_impl.upload_config = (
1563 upload_content.len().try_into().unwrap(),
1564 vec![
1565 upload_content[..upload_content.len() / 2].to_vec(),
1566 upload_content[upload_content.len() / 2..].to_vec(),
1567 ],
1568 );
1569 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1570 assert_eq!(
1571 transport.out_queue,
1572 VecDeque::<Vec<u8>>::from([
1573 b"DATA00000400".into(),
1574 upload_content[..upload_content.len() / 2].to_vec(),
1575 upload_content[upload_content.len() / 2..].to_vec(),
1576 b"OKAY".into(),
1577 ])
1578 );
1579 }
1580
1581 #[test]
test_upload_not_enough_data()1582 fn test_upload_not_enough_data() {
1583 let mut fastboot_impl: FastbootTest = Default::default();
1584 fastboot_impl.download_buffer = vec![0u8; 2048];
1585 let mut transport = TestTransport::new();
1586 transport.add_input(b"upload");
1587 fastboot_impl.upload_config = (0x400, vec![vec![0u8; 0x400 - 1]]);
1588 assert!(block_on(run(&mut transport, &mut fastboot_impl)).is_err());
1589 }
1590
1591 #[test]
test_upload_more_data()1592 fn test_upload_more_data() {
1593 let mut fastboot_impl: FastbootTest = Default::default();
1594 fastboot_impl.download_buffer = vec![0u8; 2048];
1595 let mut transport = TestTransport::new();
1596 transport.add_input(b"upload");
1597 fastboot_impl.upload_config = (0x400, vec![vec![0u8; 0x400 + 1]]);
1598 assert!(block_on(run(&mut transport, &mut fastboot_impl)).is_err());
1599 }
1600
1601 #[test]
test_fetch()1602 fn test_fetch() {
1603 let mut fastboot_impl: FastbootTest = Default::default();
1604 fastboot_impl.download_buffer = vec![0u8; 2048];
1605 let mut transport = TestTransport::new();
1606 transport.add_input(b"fetch:boot_a:0:::200:400");
1607 fastboot_impl
1608 .fetch_data
1609 .insert("boot_a:0::", (0x400, vec![vec![0u8; 0x200], vec![1u8; 0x400]].concat()));
1610 block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1611 assert_eq!(
1612 transport.out_queue,
1613 VecDeque::<Vec<u8>>::from([
1614 b"DATA00000400".into(),
1615 [1u8; 0x400].to_vec(),
1616 b"OKAY".into(),
1617 ])
1618 );
1619 }
1620
1621 #[test]
test_fetch_not_enough_data()1622 fn test_fetch_not_enough_data() {
1623 let mut fastboot_impl: FastbootTest = Default::default();
1624 fastboot_impl.download_buffer = vec![0u8; 2048];
1625 let mut transport = TestTransport::new();
1626 transport.add_input(b"fetch:boot_a:0:::200:400");
1627 fastboot_impl
1628 .fetch_data
1629 .insert("boot_a:0::", (0x400 - 1, vec![vec![0u8; 0x200], vec![1u8; 0x400]].concat()));
1630 assert!(block_on(process_next_command(&mut transport, &mut fastboot_impl)).is_err());
1631 }
1632
1633 #[test]
test_fetch_more_data()1634 fn test_fetch_more_data() {
1635 let mut fastboot_impl: FastbootTest = Default::default();
1636 fastboot_impl.download_buffer = vec![0u8; 2048];
1637 let mut transport = TestTransport::new();
1638 transport.add_input(b"fetch:boot_a:0:::200:400");
1639 fastboot_impl
1640 .fetch_data
1641 .insert("boot_a:0::", (0x400 + 1, vec![vec![0u8; 0x200], vec![1u8; 0x400]].concat()));
1642 assert!(block_on(process_next_command(&mut transport, &mut fastboot_impl)).is_err());
1643 }
1644
1645 #[test]
test_fetch_invalid_args()1646 fn test_fetch_invalid_args() {
1647 let mut fastboot_impl: FastbootTest = Default::default();
1648 fastboot_impl.download_buffer = vec![0u8; 2048];
1649 let mut transport = TestTransport::new();
1650 transport.add_input(b"fetch");
1651 transport.add_input(b"fetch:");
1652 transport.add_input(b"fetch:boot_a");
1653 transport.add_input(b"fetch:boot_a:200");
1654 transport.add_input(b"fetch:boot_a::400");
1655 transport.add_input(b"fetch:boot_a::");
1656 transport.add_input(b"fetch:boot_a:xxx:400");
1657 transport.add_input(b"fetch:boot_a:200:xxx");
1658 let _ = block_on(run(&mut transport, &mut fastboot_impl));
1659 assert!(transport.out_queue.iter().all(|v| v.starts_with(b"FAIL")));
1660 }
1661
1662 #[test]
test_fastboot_tcp()1663 fn test_fastboot_tcp() {
1664 let mut fastboot_impl: FastbootTest = Default::default();
1665 fastboot_impl.download_buffer = vec![0u8; 1024];
1666 let download_content: Vec<u8> =
1667 (0..fastboot_impl.download_buffer.len()).into_iter().map(|v| v as u8).collect();
1668 let mut tcp_stream: TestTcpStream = Default::default();
1669 tcp_stream.add_input(TCP_HANDSHAKE_MESSAGE);
1670 // Add two commands and verify both are executed.
1671 tcp_stream.add_length_prefixed_input(b"getvar:max-download-size");
1672 tcp_stream.add_length_prefixed_input(
1673 format!("download:{:#x}", download_content.len()).as_bytes(),
1674 );
1675 tcp_stream.add_length_prefixed_input(&download_content[..]);
1676 let _ = block_on(run_tcp_session(&mut tcp_stream, &mut fastboot_impl));
1677 let expected: &[&[u8]] = &[
1678 b"FB01",
1679 b"\x00\x00\x00\x00\x00\x00\x00\x09OKAY0x400",
1680 b"\x00\x00\x00\x00\x00\x00\x00\x0cDATA00000400",
1681 b"\x00\x00\x00\x00\x00\x00\x00\x04OKAY",
1682 ];
1683 assert_eq!(tcp_stream.out_queue, VecDeque::from(expected.concat()));
1684 assert_eq!(fastboot_impl.download_buffer, download_content);
1685 }
1686
1687 #[test]
test_fastboot_tcp_invalid_handshake()1688 fn test_fastboot_tcp_invalid_handshake() {
1689 let mut fastboot_impl: FastbootTest = Default::default();
1690 let mut tcp_stream: TestTcpStream = Default::default();
1691 tcp_stream.add_input(b"ABCD");
1692 assert_eq!(
1693 block_on(run_tcp_session(&mut tcp_stream, &mut fastboot_impl)).unwrap_err(),
1694 Error::InvalidHandshake
1695 );
1696 }
1697
1698 #[test]
test_fastboot_tcp_packet_size_exceeds_maximum()1699 fn test_fastboot_tcp_packet_size_exceeds_maximum() {
1700 let mut fastboot_impl: FastbootTest = Default::default();
1701 let mut tcp_stream: TestTcpStream = Default::default();
1702 tcp_stream.add_input(TCP_HANDSHAKE_MESSAGE);
1703 tcp_stream.add_input(&(MAX_COMMAND_SIZE + 1).to_be_bytes());
1704 assert_eq!(
1705 block_on(run_tcp_session(&mut tcp_stream, &mut fastboot_impl)).unwrap_err(),
1706 Error::InvalidInput
1707 );
1708 }
1709
1710 #[test]
test_reboot()1711 fn test_reboot() {
1712 let mut fastboot_impl: FastbootTest = Default::default();
1713 let mut transport = TestTransport::new();
1714 transport.add_input(b"reboot");
1715 block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1716 assert_eq!(fastboot_impl.reboot_mode, Some(RebootMode::Normal));
1717 assert_eq!(transport.out_queue[0], b"OKAY");
1718 // Failure is expected here because test reboot implementation always returns, which
1719 // automatically generates a fastboot failure packet.
1720 assert!(transport.out_queue[1].starts_with(b"FAIL"));
1721 }
1722
1723 #[test]
test_reboot_bootloader()1724 fn test_reboot_bootloader() {
1725 let mut fastboot_impl: FastbootTest = Default::default();
1726 let mut transport = TestTransport::new();
1727 transport.add_input(b"reboot-bootloader");
1728 block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1729 assert_eq!(fastboot_impl.reboot_mode, Some(RebootMode::Bootloader));
1730 assert_eq!(transport.out_queue[0], b"OKAY");
1731 // Failure is expected here because test reboot implementation always returns, which
1732 // automatically generates a fastboot failure packet.
1733 assert!(transport.out_queue[1].starts_with(b"FAIL"));
1734 }
1735
1736 #[test]
test_reboot_fastboot()1737 fn test_reboot_fastboot() {
1738 let mut fastboot_impl: FastbootTest = Default::default();
1739 let mut transport = TestTransport::new();
1740 transport.add_input(b"reboot-fastboot");
1741 block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1742 assert_eq!(fastboot_impl.reboot_mode, Some(RebootMode::Fastboot));
1743 assert_eq!(transport.out_queue[0], b"OKAY");
1744 // Failure is expected here because test reboot implementation always returns, which
1745 // automatically generates a fastboot failure packet.
1746 assert!(transport.out_queue[1].starts_with(b"FAIL"));
1747 }
1748
1749 #[test]
test_reboot_recovery()1750 fn test_reboot_recovery() {
1751 let mut fastboot_impl: FastbootTest = Default::default();
1752 let mut transport = TestTransport::new();
1753 transport.add_input(b"reboot-recovery");
1754 block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1755 assert_eq!(fastboot_impl.reboot_mode, Some(RebootMode::Recovery));
1756 assert_eq!(transport.out_queue[0], b"OKAY");
1757 // Failure is expected here because test reboot implementation always returns, which
1758 // automatically generates a fastboot failure packet.
1759 assert!(transport.out_queue[1].starts_with(b"FAIL"));
1760 }
1761
1762 #[test]
test_continue()1763 fn test_continue() {
1764 let mut fastboot_impl: FastbootTest = Default::default();
1765 fastboot_impl.download_buffer = vec![0u8; 1024];
1766 let mut transport = TestTransport::new();
1767 transport.add_input(b"getvar:max-download-size");
1768 transport.add_input(b"continue");
1769 transport.add_input(b"getvar:max-download-size");
1770 block_on(run(&mut transport, &mut fastboot_impl)).unwrap();
1771 assert_eq!(
1772 transport.out_queue,
1773 VecDeque::<Vec<u8>>::from([
1774 b"OKAY0x400".into(),
1775 b"INFOContinuing to boot...".into(),
1776 b"OKAY".into()
1777 ])
1778 );
1779 }
1780
1781 #[test]
test_continue_run_tcp()1782 fn test_continue_run_tcp() {
1783 let mut fastboot_impl: FastbootTest = Default::default();
1784 let mut tcp_stream: TestTcpStream = Default::default();
1785 tcp_stream.add_input(TCP_HANDSHAKE_MESSAGE);
1786 tcp_stream.add_length_prefixed_input(b"continue");
1787 block_on(run_tcp_session(&mut tcp_stream, &mut fastboot_impl)).unwrap();
1788 }
1789
1790 #[test]
test_set_active()1791 fn test_set_active() {
1792 let mut fastboot_impl: FastbootTest = Default::default();
1793 fastboot_impl.download_buffer = vec![0u8; 1024];
1794 let mut transport = TestTransport::new();
1795 transport.add_input(b"set_active:a");
1796 block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1797 assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([b"OKAY".into()]));
1798 assert_eq!(fastboot_impl.active_slot, Some("a".into()));
1799 }
1800
1801 #[test]
test_set_active_missing_slot()1802 fn test_set_active_missing_slot() {
1803 let mut fastboot_impl: FastbootTest = Default::default();
1804 fastboot_impl.download_buffer = vec![0u8; 1024];
1805 let mut transport = TestTransport::new();
1806 transport.add_input(b"set_active");
1807 block_on(process_next_command(&mut transport, &mut fastboot_impl)).unwrap();
1808 assert_eq!(transport.out_queue, VecDeque::<Vec<u8>>::from([b"FAILMissing slot".into()]));
1809 }
1810 }
1811