• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! Fastboot backend for libgbl.
16 
17 use crate::{
18     android_boot::{android_load_verify_fixup, get_boot_slot},
19     fuchsia_boot::GblAbrOps,
20     gbl_print, gbl_println,
21     ops::RambootOps,
22     partition::{check_part_unique, GblDisk, PartitionIo},
23     GblOps,
24 };
25 pub use abr::{mark_slot_active, set_one_shot_bootloader, set_one_shot_recovery, SlotIndex};
26 use core::{
27     array::from_fn, cmp::min, ffi::CStr, fmt::Write, future::Future, marker::PhantomData,
28     mem::take, ops::DerefMut, ops::Range, pin::Pin, str::from_utf8,
29 };
30 use fastboot::{
31     local_session::LocalSession, next_arg, next_arg_u64, process_next_command, run_tcp_session,
32     CommandError, CommandResult, FastbootImplementation, InfoSender, OkaySender, RebootMode,
33     UploadBuilder, Uploader, VarInfoSender, MAX_COMMAND_SIZE,
34 };
35 use gbl_async::{join, yield_now};
36 use gbl_storage::{BlockIo, Disk, Gpt};
37 use liberror::Error;
38 use libutils::snprintf;
39 use libutils::FormattedBytes;
40 use safemath::SafeNum;
41 use zbi::{ZbiContainer, ZbiType};
42 
43 mod vars;
44 
45 pub(crate) mod sparse;
46 use sparse::is_sparse_image;
47 
48 mod shared;
49 pub use shared::Shared;
50 
51 mod buffer_pool;
52 pub use buffer_pool::BufferPool;
53 use buffer_pool::ScopedBuffer;
54 
55 mod pin_fut_container;
56 pub use pin_fut_container::PinFutContainer;
57 use pin_fut_container::{PinFutContainerTyped, PinFutSlice};
58 
59 // Re-exports dependency types
60 pub use fastboot::{TcpStream, Transport};
61 
62 /// Reserved name for indicating flashing GPT.
63 const FLASH_GPT_PART: &str = "gpt";
64 
65 /// Represents the workload of a GBL Fastboot async task.
66 enum TaskWorkload<'a, 'b, B: BlockIo, P: BufferPool> {
67     /// Image flashing task. (partition io, downloaded data, data size)
68     Flash(PartitionIo<'a, B>, ScopedBuffer<'b, P>, usize),
69     /// Sparse image flashing task. (partition io, downloaded data)
70     FlashSparse(PartitionIo<'a, B>, ScopedBuffer<'b, P>),
71     // Image erase task.
72     Erase(PartitionIo<'a, B>, ScopedBuffer<'b, P>),
73     None,
74 }
75 
76 impl<'a, 'b, B: BlockIo, P: BufferPool> TaskWorkload<'a, 'b, B, P> {
77     /// Runs the task and returns the result
run(self) -> Result<(), Error>78     async fn run(self) -> Result<(), Error> {
79         match self {
80             Self::Flash(mut io, mut data, sz) => io.write(0, &mut data[..sz]).await,
81             Self::FlashSparse(mut io, mut data) => io.write_sparse(0, &mut data).await,
82             Self::Erase(mut io, mut buffer) => io.zeroize(&mut buffer).await,
83             _ => Ok(()),
84         }
85     }
86 }
87 
88 /// Represents a GBL Fastboot async task.
89 struct Task<'a, 'b, B: BlockIo, P: BufferPool> {
90     workload: TaskWorkload<'a, 'b, B, P>,
91     context: [u8; MAX_COMMAND_SIZE],
92 }
93 
94 impl<'a, 'b, B: BlockIo, P: BufferPool> Task<'a, 'b, B, P> {
95     /// Creates a new instance with the given workload.
new(workload: TaskWorkload<'a, 'b, B, P>) -> Self96     fn new(workload: TaskWorkload<'a, 'b, B, P>) -> Self {
97         Self { workload, context: [0u8; MAX_COMMAND_SIZE] }
98     }
99 
100     /// Sets the context string.
set_context(&mut self, mut f: impl FnMut(&mut dyn Write) -> Result<(), core::fmt::Error>)101     fn set_context(&mut self, mut f: impl FnMut(&mut dyn Write) -> Result<(), core::fmt::Error>) {
102         let _ = f(&mut FormattedBytes::new(&mut self.context[..]));
103     }
104 
105     /// Runs the task and returns the result.
run_checked(self) -> Result<(), Error>106     async fn run_checked(self) -> Result<(), Error> {
107         self.workload.run().await
108     }
109 
110     /// Runs the task. Panics on error.
111     ///
112     /// The method is intended for use in the context of parallel/background async task where errors
113     /// can't be easily handled by the main routine.
run(self)114     async fn run(self) {
115         match self.workload.run().await {
116             Err(e) => panic!(
117                 "A Fastboot async task failed: {e:?}, context: {}",
118                 from_utf8(&self.context[..]).unwrap_or("")
119             ),
120             _ => {}
121         }
122     }
123 }
124 
125 impl<'a, 'b, B: BlockIo, P: BufferPool> Default for Task<'a, 'b, B, P> {
default() -> Self126     fn default() -> Self {
127         // Creates a noop task. This is mainly used for type inference for inline declaration of
128         // pre-allocated task pool.
129         Self::new(TaskWorkload::None)
130     }
131 }
132 
133 /// Contains the load buffer layout of images loaded by "fastboot boot".
134 #[derive(Debug, Clone)]
135 pub enum LoadedImageInfo {
136     /// Android loaded images.
137     Android {
138         /// Offset and length of ramdisk in `GblFastboot::load_buffer`.
139         ramdisk: Range<usize>,
140         /// Offset and length of fdt in `GblFastboot::load_buffer`.
141         fdt: Range<usize>,
142         /// Offset and length of kernel in `GblFastboot::load_buffer`.
143         kernel: Range<usize>,
144     },
145 }
146 
147 /// Contains result data returned by GBL Fastboot.
148 #[derive(Debug, Clone, Default)]
149 pub struct GblFastbootResult {
150     /// Buffer layout for images loaded by "fastboot boot"
151     pub loaded_image_info: Option<LoadedImageInfo>,
152     /// Slot suffix that was last set active by "fastboot set_active"
153     pub last_set_active_slot: Option<char>,
154 }
155 
156 impl GblFastbootResult {
157     /// Splits the given buffer into `(ramdisk, fdt, kernel, unused)` according to layout info in
158     ///  `Self::loaded_image_info` if it is a `Some(LoadedImageInfo::Android)`.
split_loaded_android<'a>( &self, load: &'a mut [u8], ) -> Option<(&'a mut [u8], &'a mut [u8], &'a mut [u8], &'a mut [u8])>159     pub fn split_loaded_android<'a>(
160         &self,
161         load: &'a mut [u8],
162     ) -> Option<(&'a mut [u8], &'a mut [u8], &'a mut [u8], &'a mut [u8])> {
163         let Some(LoadedImageInfo::Android { ramdisk, fdt, kernel }) = &self.loaded_image_info
164         else {
165             return None;
166         };
167         let (ramdisk_buf, rem) = load[ramdisk.start..].split_at_mut(ramdisk.len());
168         let (fdt_buf, rem) = rem[fdt.start - ramdisk.end..].split_at_mut(fdt.len());
169         let (kernel_buf, rem) = rem[kernel.start - fdt.end..].split_at_mut(kernel.len());
170         Some((ramdisk_buf, fdt_buf, kernel_buf, rem))
171     }
172 }
173 
174 /// `GblFastboot` implements fastboot commands in the GBL context.
175 ///
176 /// # Lifetimes
177 ///
178 /// * `'a`: [GblOps] and disks lifetime.
179 /// * `'b`: Lifetime for the buffer allocated by `P`.
180 /// * `'c`: Lifetime of the pinned [Future]s in task container `task`.
181 /// * `'d`: Lifetime of the `tasks` and `gbl_ops` objects borrowed.
182 /// * `'e`: Lifetime of the ImageBuffers returned by `get_image_buffer()`.
183 ///
184 /// # Generics
185 ///
186 /// * `G`: Type of `Self::gbl_ops` which implements [GblOps].
187 /// * `B`: Type that implements [BlockIo] in the [Disk] parameter of [GblDisk] for `Self::disks`.
188 /// * `S`: Type of scratch buffer in the [Disk] parameter of [GblDisk] for `Self::disks`.
189 /// * `T`: Type of gpt buffer in the [Gpt] parameter of [GblDisk] for `Self::disks`.
190 /// * `P`: Type of `Self::buffer_pool` which implements [BufferPool].
191 /// * `C`: Type of `Self::tasks` which implements [PinFutContainerTyped].
192 /// * `F`: Type of [Future] stored by `Self::Tasks`.
193 struct GblFastboot<'a, 'b, 'c, 'd, 'e, G, B, S, T, P, C, F>
194 where
195     G: GblOps<'a, 'e>,
196     B: BlockIo,
197     S: DerefMut<Target = [u8]>,
198     T: DerefMut<Target = [u8]>,
199     P: BufferPool,
200 {
201     pub(crate) gbl_ops: &'d mut G,
202     // We store the partition devices returned by `gbl_ops.disks()` directly instead of getting it
203     // from `gbl_ops` later because we need to establish to the compiler that the hidden type of
204     // [BlockIo] in `GblDisk<Disk<impl BlockIO...>...>` returned by `gbl_ops.disks()` will be the
205     // same as the [BlockIo] type (denoted as B) in the function pointer
206     // `task_mapper`: fn(Task<'a, 'b, B, P>) -> F`. Otherwise, compiler won't allow `fn flash()`
207     // to call `task_mapper` with a `Task` constructed from `GblDisk<Disk<impl BlockIO...>...>`.
208     disks: &'a [GblDisk<Disk<B, S>, Gpt<T>>],
209     buffer_pool: &'b Shared<P>,
210     task_mapper: fn(Task<'a, 'b, B, P>) -> F,
211     tasks: &'d Shared<C>,
212     current_download_buffer: Option<ScopedBuffer<'b, P>>,
213     current_download_size: usize,
214     enable_async_task: bool,
215     default_block: Option<usize>,
216     load_buffer: &'b mut [u8],
217     result: GblFastbootResult,
218     // Introduces marker type so that we can enforce constraint 'd <= min('b, 'c).
219     // The constraint is expressed in the implementation block for the `FastbootImplementation`
220     // trait.
221     _tasks_context_lifetime: PhantomData<&'c P>,
222     _get_image_buffer_lifetime: PhantomData<&'e ()>,
223 }
224 
225 // See definition of [GblFastboot] for docs on lifetimes and generics parameters.
226 impl<'a: 'c, 'b: 'c, 'c, 'd, 'e, G, B, S, T, P, C, F>
227     GblFastboot<'a, 'b, 'c, 'd, 'e, G, B, S, T, P, C, F>
228 where
229     G: GblOps<'a, 'e>,
230     B: BlockIo,
231     S: DerefMut<Target = [u8]>,
232     T: DerefMut<Target = [u8]>,
233     P: BufferPool,
234     C: PinFutContainerTyped<'c, F>,
235     F: Future<Output = ()> + 'c,
236 {
237     /// Creates a new [GblFastboot].
238     ///
239     /// # Args
240     ///
241     /// * `gbl_ops`: An implementation of `GblOps`.
242     /// * `disks`: The disk devices returned by `gbl_ops.disks()`. This is needed for expressing the
243     ///   property that the hidden [BlockIo] type is the same as that in `task_mapper`.
244     /// * `task_mapper`: A function pointer that maps `Task<'a, 'b, G, B>` to the target [Future]
245     ///   type `F` for input to `PinFutContainerTyped<F>::add_with()`.
246     /// * `tasks`: A shared instance of `PinFutContainerTyped<F>`.
247     /// * `buffer_pool`: A shared instance of `BufferPool`.
248     ///
249     /// The combination of `task_mapper` and `tasks` allows type `F`, which will be running the
250     /// async function `Task::run()`, to be defined at the callsite. This is necessary for the
251     /// usage of preallocated pinned futures (by `run_gbl_fastboot_stack()`) because the returned
252     /// type of a `async fn` is compiler-generated and can't be named. The only way to create a
253     /// preallocated slice of anonymous future is to keep the type generic and pass in the
254     /// anonymous future instance at the initialization callsite (aka defining use) and let compiler
255     /// infer and propagate it.
new( gbl_ops: &'d mut G, disks: &'a [GblDisk<Disk<B, S>, Gpt<T>>], task_mapper: fn(Task<'a, 'b, B, P>) -> F, tasks: &'d Shared<C>, buffer_pool: &'b Shared<P>, load_buffer: &'b mut [u8], ) -> Self256     fn new(
257         gbl_ops: &'d mut G,
258         disks: &'a [GblDisk<Disk<B, S>, Gpt<T>>],
259         task_mapper: fn(Task<'a, 'b, B, P>) -> F,
260         tasks: &'d Shared<C>,
261         buffer_pool: &'b Shared<P>,
262         load_buffer: &'b mut [u8],
263     ) -> Self {
264         Self {
265             gbl_ops,
266             disks,
267             task_mapper,
268             tasks,
269             buffer_pool,
270             current_download_buffer: None,
271             current_download_size: 0,
272             enable_async_task: false,
273             default_block: None,
274             load_buffer,
275             result: Default::default(),
276             _tasks_context_lifetime: PhantomData,
277             _get_image_buffer_lifetime: PhantomData,
278         }
279     }
280 
281     /// Returns the shared task container.
tasks(&self) -> &'d Shared<impl PinFutContainerTyped<'c, F>>282     fn tasks(&self) -> &'d Shared<impl PinFutContainerTyped<'c, F>> {
283         self.tasks
284     }
285 
286     /// Listens on the given USB, TCP, and local session channels and runs fastboot.
run( &mut self, mut local: Option<impl LocalSession>, mut usb: Option<impl GblUsbTransport>, mut tcp: Option<impl GblTcpStream>, )287     async fn run(
288         &mut self,
289         mut local: Option<impl LocalSession>,
290         mut usb: Option<impl GblUsbTransport>,
291         mut tcp: Option<impl GblTcpStream>,
292     ) {
293         if usb.is_none() && tcp.is_none() && local.is_none() {
294             gbl_println!(self.gbl_ops, "No USB, TCP, or local session found for GBL Fastboot");
295             return;
296         }
297         let tasks = self.tasks();
298         // The fastboot command loop task for interacting with the remote host.
299         let cmd_loop_end = Shared::from(false);
300         let cmd_loop_task = async {
301             loop {
302                 if let Some(ref mut l) = local {
303                     let res = match process_next_command(l, self).await {
304                         Ok(true) => break,
305                         l => l,
306                     };
307                     if res.is_err() {
308                         gbl_println!(self.gbl_ops, "GBL Fastboot local session error: {:?}", res);
309                     }
310                 }
311 
312                 if let Some(v) = usb.as_mut() {
313                     if v.has_packet() {
314                         let res = match process_next_command(v, self).await {
315                             Ok(true) => break,
316                             v => v,
317                         };
318                         if res.is_err() {
319                             gbl_println!(self.gbl_ops, "GBL Fastboot USB session error: {:?}", res);
320                         }
321                     }
322                 }
323 
324                 if let Some(v) = tcp.as_mut() {
325                     if v.accept_new() {
326                         let res = match run_tcp_session(v, self).await {
327                             Ok(()) => break,
328                             v => v,
329                         };
330                         if res.is_err_and(|e| e != Error::Disconnected) {
331                             gbl_println!(self.gbl_ops, "GBL Fastboot TCP session error: {:?}", res);
332                         }
333                     }
334                 }
335 
336                 yield_now().await;
337             }
338             *cmd_loop_end.borrow_mut() = true;
339         };
340 
341         // Schedules [Task] spawned by GBL fastboot.
342         let gbl_fb_tasks = async {
343             while tasks.borrow_mut().poll_all() > 0 || !*cmd_loop_end.borrow_mut() {
344                 yield_now().await;
345             }
346         };
347 
348         let _ = join(cmd_loop_task, gbl_fb_tasks).await;
349     }
350 
351     /// Extracts the next argument and verifies that it is a valid block device ID if present.
352     ///
353     /// # Returns
354     ///
355     /// * Returns `Ok(Some(blk_id))` if next argument is present and is a valid block device ID.
356     /// * Returns `None` if next argument is not available and there are more than one block
357     ///   devices.
358     /// * Returns `Err(())` if next argument is present but is an invalid block device ID.
check_next_arg_blk_id<'s>( &self, args: &mut impl Iterator<Item = &'s str>, ) -> CommandResult<Option<usize>>359     fn check_next_arg_blk_id<'s>(
360         &self,
361         args: &mut impl Iterator<Item = &'s str>,
362     ) -> CommandResult<Option<usize>> {
363         let devs = self.disks;
364         let blk_id = match next_arg_u64(args)? {
365             Some(v) => {
366                 let v = usize::try_from(v)?;
367                 // Checks out of range.
368                 devs.get(v).ok_or("Invalid block ID")?;
369                 Some(v)
370             }
371             _ => None,
372         };
373         let blk_id = blk_id.or(self.default_block);
374         let blk_id = blk_id.or((devs.len() == 1).then_some(0));
375         Ok(blk_id)
376     }
377 
378     /// Parses and checks the argument for "fastboot flash gpt/<blk_idx>/"resize".
379     ///
380     /// # Returns
381     ///
382     /// * Returns `Ok(Some((blk_idx, resize)))` if command is a GPT flashing command.
383     /// * Returns `Ok(None)` if command is not a GPT flashing command.
384     /// * Returns `Err()` otherwise.
parse_flash_gpt_args(&self, part: &str) -> CommandResult<Option<(usize, bool)>>385     pub(crate) fn parse_flash_gpt_args(&self, part: &str) -> CommandResult<Option<(usize, bool)>> {
386         // Syntax: flash gpt/<blk_idx>/"resize"
387         let mut args = part.split('/');
388         if next_arg(&mut args).filter(|v| *v == FLASH_GPT_PART).is_none() {
389             return Ok(None);
390         }
391         // Parses block device ID.
392         let blk_id = self
393             .check_next_arg_blk_id(&mut args)?
394             .ok_or("Block ID is required for flashing GPT")?;
395         // Parses resize option.
396         let resize = match next_arg(&mut args) {
397             Some("resize") => true,
398             Some(_) => return Err("Unknown argument".into()),
399             _ => false,
400         };
401         Ok(Some((blk_id, resize)))
402     }
403 
404     /// Parses and checks the partition argument and returns the partition name, block device
405     /// index, start offset and size.
parse_partition<'s>( &self, part: &'s str, ) -> CommandResult<(Option<&'s str>, usize, u64, u64)>406     pub(crate) fn parse_partition<'s>(
407         &self,
408         part: &'s str,
409     ) -> CommandResult<(Option<&'s str>, usize, u64, u64)> {
410         let devs = self.disks;
411         let mut args = part.split('/');
412         // Parses partition name.
413         let part = next_arg(&mut args);
414         // Parses block device ID.
415         let blk_id = self.check_next_arg_blk_id(&mut args)?;
416         // Parses sub window offset.
417         let window_offset = next_arg_u64(&mut args)?.unwrap_or(0);
418         // Parses sub window size.
419         let window_size = next_arg_u64(&mut args)?;
420         // Checks uniqueness of the partition and resolves its block device ID.
421         let find = |p: Option<&'s str>| match blk_id {
422             None => Ok((check_part_unique(devs, p.ok_or("Must provide a partition")?)?, p)),
423             Some(v) => Ok(((v, devs[v].find_partition(p)?), p)),
424         };
425         let ((blk_id, partition), actual) = match find(part) {
426             // Some legacy Fuchsia devices in the field uses name "fuchsia-fvm" for the standard
427             // "fvm" partition. However all of our infra uses the standard name "fvm" when flashing.
428             // Here we do a one off mapping if the device falls into this case. Once we have a
429             // solution for migrating those devices off the legacy name, we can remove this.
430             //
431             // If we run into more of such legacy aliases that we can't migrate, consider adding
432             // interfaces in GblOps for this.
433             Err(Error::NotFound) if part == Some("fvm") => find(Some("fuchsia-fvm"))?,
434             v => v?,
435         };
436         let part_sz = SafeNum::from(partition.size()?);
437         let window_size = window_size.unwrap_or((part_sz - window_offset).try_into()?);
438         u64::try_from(part_sz - window_size - window_offset)?;
439         Ok((actual, blk_id, window_offset, window_size))
440     }
441 
442     /// Takes the download data and resets download size.
take_download(&mut self) -> Option<(ScopedBuffer<'b, P>, usize)>443     fn take_download(&mut self) -> Option<(ScopedBuffer<'b, P>, usize)> {
444         Some((self.current_download_buffer.take()?, take(&mut self.current_download_size)))
445     }
446 
447     /// Waits until a Disk device is ready and get the [PartitionIo] for `part`.
wait_partition_io( &self, blk: usize, part: Option<&str>, ) -> CommandResult<PartitionIo<'a, B>>448     pub async fn wait_partition_io(
449         &self,
450         blk: usize,
451         part: Option<&str>,
452     ) -> CommandResult<PartitionIo<'a, B>> {
453         loop {
454             match self.disks[blk].partition_io(part) {
455                 Err(Error::NotReady) => yield_now().await,
456                 v => return Ok(v?),
457             }
458         }
459     }
460 
461     /// An internal helper for parsing a partition and getting the partition IO
parse_and_get_partition_io( &self, part: &str, ) -> CommandResult<(usize, PartitionIo<'a, B>)>462     async fn parse_and_get_partition_io(
463         &self,
464         part: &str,
465     ) -> CommandResult<(usize, PartitionIo<'a, B>)> {
466         let (part, blk_idx, start, sz) = self.parse_partition(part)?;
467         Ok((blk_idx, self.wait_partition_io(blk_idx, part).await?.sub(start, sz)?))
468     }
469 
470     /// Helper for scheduiling an async task.
471     ///
472     /// * If `Self::enable_async_task` is true, the method will add the task to the background task
473     ///   list. Otherwise it simply runs the task.
schedule_task( &mut self, task: Task<'a, 'b, B, P>, responder: &mut impl InfoSender, ) -> CommandResult<()>474     async fn schedule_task(
475         &mut self,
476         task: Task<'a, 'b, B, P>,
477         responder: &mut impl InfoSender,
478     ) -> CommandResult<()> {
479         Ok(match self.enable_async_task {
480             true => {
481                 let mut t = Some((self.task_mapper)(task));
482                 self.tasks.borrow_mut().add_with(|| t.take().unwrap());
483                 while t.is_some() {
484                     yield_now().await;
485                     self.tasks.borrow_mut().add_with(|| t.take().unwrap());
486                 }
487                 self.tasks.borrow_mut().poll_all();
488                 let info =
489                     "An async task is launched. To sync manually, run \"oem gbl-sync-tasks\".";
490                 responder.send_info(info).await?
491             }
492             _ => task.run_checked().await?,
493         })
494     }
495 
496     /// Waits for all block devices to be ready.
sync_all_blocks(&self) -> CommandResult<()>497     async fn sync_all_blocks(&self) -> CommandResult<()> {
498         for (idx, _) in self.disks.iter().enumerate() {
499             let _ = self.wait_partition_io(idx, None).await;
500         }
501         Ok(())
502     }
503 
504     /// Implementation for "fastboot oem gbl-sync-tasks".
oem_sync_tasks<'s>( &self, mut _responder: impl InfoSender, _res: &'s mut [u8], ) -> CommandResult<&'s [u8]>505     async fn oem_sync_tasks<'s>(
506         &self,
507         mut _responder: impl InfoSender,
508         _res: &'s mut [u8],
509     ) -> CommandResult<&'s [u8]> {
510         self.sync_all_blocks().await?;
511         Ok(b"")
512     }
513 
514     /// Syncs all storage devices and reboots.
sync_tasks_and_reboot( &mut self, mode: RebootMode, mut resp: impl InfoSender + OkaySender, ) -> CommandResult<()>515     async fn sync_tasks_and_reboot(
516         &mut self,
517         mode: RebootMode,
518         mut resp: impl InfoSender + OkaySender,
519     ) -> CommandResult<()> {
520         resp.send_info("Syncing storage...").await?;
521         self.sync_all_blocks().await?;
522         match mode {
523             RebootMode::Normal => {
524                 resp.send_info("Rebooting...").await?;
525                 resp.send_okay("").await?;
526                 self.gbl_ops.reboot();
527             }
528             RebootMode::Bootloader => {
529                 let f = self.gbl_ops.reboot_bootloader()?;
530                 resp.send_info("Rebooting to bootloader...").await?;
531                 resp.send_okay("").await?;
532                 f()
533             }
534             RebootMode::Recovery => {
535                 let f = self.gbl_ops.reboot_recovery()?;
536                 resp.send_info("Rebooting to recovery...").await?;
537                 resp.send_okay("").await?;
538                 f()
539             }
540             _ => return Err("Unsupported".into()),
541         }
542         Ok(())
543     }
544 
545     /// Appends a staged payload as bootloader file.
add_staged_bootloader_file(&mut self, file_name: &str) -> CommandResult<()>546     async fn add_staged_bootloader_file(&mut self, file_name: &str) -> CommandResult<()> {
547         let buffer = self
548             .gbl_ops
549             .get_zbi_bootloader_files_buffer_aligned()
550             .ok_or("No ZBI bootloader file buffer is provided")?;
551         let data = self.current_download_buffer.as_mut().ok_or("No file staged")?;
552         let data = &mut data[..self.current_download_size];
553         let mut zbi = match ZbiContainer::parse(&mut buffer[..]) {
554             Ok(v) => v,
555             _ => ZbiContainer::new(&mut buffer[..])?,
556         };
557         let next_payload = zbi.get_next_payload()?;
558         // Format: name length (1 byte) | name | file content.
559         let (name_len, rest) = next_payload.split_at_mut_checked(1).ok_or("Buffer too small")?;
560         let (name, rest) = rest.split_at_mut_checked(file_name.len()).ok_or("Buffer too small")?;
561         let file_content = rest.get_mut(..data.len()).ok_or("Buffer too small")?;
562         name_len[0] = file_name.len().try_into().map_err(|_| "File name length overflows 256")?;
563         name.clone_from_slice(file_name.as_bytes());
564         file_content.clone_from_slice(data);
565         // Creates the entry;
566         zbi.create_entry(
567             ZbiType::BootloaderFile,
568             0,
569             Default::default(),
570             1 + file_name.len() + data.len(),
571         )?;
572         Ok(())
573     }
574 
575     /// Sets active slot.
set_active_slot(&mut self, slot: &str) -> CommandResult<()>576     async fn set_active_slot(&mut self, slot: &str) -> CommandResult<()> {
577         self.sync_all_blocks().await?;
578         match self.gbl_ops.expected_os_is_fuchsia()? {
579             // TODO(b/374776896): Prioritizes platform specific `set_active_slot`  if available.
580             true => Ok(mark_slot_active(
581                 &mut GblAbrOps(self.gbl_ops),
582                 match slot {
583                     "a" => SlotIndex::A,
584                     "b" => SlotIndex::B,
585                     _ => return Err("Invalid slot index for Fuchsia A/B/R".into()),
586                 },
587             )?),
588             // We currently assume that slot indices are mapped to suffix 'a' to 'z' starting from
589             // 0. Revisit if we need to support arbitrary slot suffix to index mapping.
590             _ => Ok(self
591                 .gbl_ops
592                 .set_active_slot(u8::try_from(slot.chars().next().unwrap())? - b'a')?),
593         }
594     }
595 }
596 
597 // See definition of [GblFastboot] for docs on lifetimes and generics parameters.
598 impl<'a: 'c, 'b: 'c, 'c, 'e, G, B, S, T, P, C, F> FastbootImplementation
599     for GblFastboot<'a, 'b, 'c, '_, 'e, G, B, S, T, P, C, F>
600 where
601     G: GblOps<'a, 'e>,
602     B: BlockIo,
603     S: DerefMut<Target = [u8]>,
604     T: DerefMut<Target = [u8]>,
605     P: BufferPool,
606     C: PinFutContainerTyped<'c, F>,
607     F: Future<Output = ()> + 'c,
608 {
get_var( &mut self, var: &CStr, args: impl Iterator<Item = &'_ CStr> + Clone, out: &mut [u8], _: impl InfoSender, ) -> CommandResult<usize>609     async fn get_var(
610         &mut self,
611         var: &CStr,
612         args: impl Iterator<Item = &'_ CStr> + Clone,
613         out: &mut [u8],
614         _: impl InfoSender,
615     ) -> CommandResult<usize> {
616         Ok(self.get_var_internal(var, args, out)?.len())
617     }
618 
get_var_all(&mut self, mut resp: impl VarInfoSender) -> CommandResult<()>619     async fn get_var_all(&mut self, mut resp: impl VarInfoSender) -> CommandResult<()> {
620         self.get_var_all_internal(&mut resp).await
621     }
622 
get_download_buffer(&mut self) -> &mut [u8]623     async fn get_download_buffer(&mut self) -> &mut [u8] {
624         if self.current_download_buffer.is_none() {
625             self.current_download_buffer = Some(self.buffer_pool.allocate_async().await);
626         }
627         self.current_download_buffer.as_mut().unwrap()
628     }
629 
download_complete( &mut self, download_size: usize, _: impl InfoSender, ) -> CommandResult<()>630     async fn download_complete(
631         &mut self,
632         download_size: usize,
633         _: impl InfoSender,
634     ) -> CommandResult<()> {
635         self.current_download_size = download_size;
636         Ok(())
637     }
638 
flash(&mut self, part: &str, mut responder: impl InfoSender) -> CommandResult<()>639     async fn flash(&mut self, part: &str, mut responder: impl InfoSender) -> CommandResult<()> {
640         let disks = self.disks;
641 
642         // Checks if we are flashing new GPT partition table
643         if let Some((blk_idx, resize)) = self.parse_flash_gpt_args(part)? {
644             self.wait_partition_io(blk_idx, None).await?;
645             let (mut gpt, size) = self.take_download().ok_or("No GPT downloaded")?;
646             responder.send_info("Updating GPT...").await?;
647             return match disks[blk_idx].update_gpt(&mut gpt[..size], resize).await {
648                 Err(Error::NotReady) => panic!("Should not be busy"),
649                 Err(Error::Unsupported) => Err("Block device is not for GPT".into()),
650                 v => Ok(v?),
651             };
652         }
653 
654         let (_, part_io) = self.parse_and_get_partition_io(part).await?;
655         let (data, sz) = self.take_download().ok_or("No download")?;
656         let mut task = Task::new(match is_sparse_image(&data) {
657             Ok(v) => TaskWorkload::FlashSparse(part_io.sub(0, v.data_size())?, data),
658             _ => TaskWorkload::Flash(part_io.sub(0, sz.try_into().unwrap())?, data, sz),
659         });
660         task.set_context(|f| write!(f, "flash:{part}"));
661         Ok(self.schedule_task(task, &mut responder).await?)
662     }
663 
erase(&mut self, part: &str, mut responder: impl InfoSender) -> CommandResult<()>664     async fn erase(&mut self, part: &str, mut responder: impl InfoSender) -> CommandResult<()> {
665         let disks = self.disks;
666 
667         // Checks if we are erasing GPT partition table.
668         if let Some((blk_idx, _)) = self.parse_flash_gpt_args(part)? {
669             self.wait_partition_io(blk_idx, None).await?;
670             return match disks[blk_idx].erase_gpt().await {
671                 Err(Error::NotReady) => panic!("Should not be busy"),
672                 Err(Error::Unsupported) => Err("Block device is not for GPT".into()),
673                 v => Ok(v?),
674             };
675         }
676 
677         let (_, part_io) = self.parse_and_get_partition_io(part).await?;
678         self.get_download_buffer().await;
679         let mut task = Task::new(TaskWorkload::Erase(part_io, self.take_download().unwrap().0));
680         task.set_context(|f| write!(f, "erase:{part}"));
681         Ok(self.schedule_task(task, &mut responder).await?)
682     }
683 
upload(&mut self, _: impl UploadBuilder) -> CommandResult<()>684     async fn upload(&mut self, _: impl UploadBuilder) -> CommandResult<()> {
685         Err("Unimplemented".into())
686     }
687 
fetch( &mut self, part: &str, offset: u64, size: u64, mut responder: impl UploadBuilder + InfoSender, ) -> CommandResult<()>688     async fn fetch(
689         &mut self,
690         part: &str,
691         offset: u64,
692         size: u64,
693         mut responder: impl UploadBuilder + InfoSender,
694     ) -> CommandResult<()> {
695         let (_, mut part_io) = self.parse_and_get_partition_io(part).await?;
696         let buffer = self.get_download_buffer().await;
697         let end = u64::try_from(SafeNum::from(offset) + size)?;
698         let mut curr = offset;
699         responder
700             .send_formatted_info(|v| write!(v, "Uploading {} bytes...", size).unwrap())
701             .await?;
702         let mut uploader = responder.initiate_upload(size).await?;
703         while curr < end {
704             let to_send = min(usize::try_from(end - curr)?, buffer.len());
705             part_io.read(curr, &mut buffer[..to_send]).await?;
706             uploader.upload(&mut buffer[..to_send]).await?;
707             curr += u64::try_from(to_send)?;
708         }
709         Ok(())
710     }
711 
reboot( &mut self, mode: RebootMode, resp: impl InfoSender + OkaySender, ) -> CommandError712     async fn reboot(
713         &mut self,
714         mode: RebootMode,
715         resp: impl InfoSender + OkaySender,
716     ) -> CommandError {
717         match self.sync_tasks_and_reboot(mode, resp).await {
718             Err(e) => e,
719             _ => "Unknown".into(),
720         }
721     }
722 
723     async fn r#continue(&mut self, mut resp: impl InfoSender) -> CommandResult<()> {
724         resp.send_info("Syncing storage...").await?;
725         Ok(self.sync_all_blocks().await?)
726     }
727 
set_active(&mut self, slot: &str, _: impl InfoSender) -> CommandResult<()>728     async fn set_active(&mut self, slot: &str, _: impl InfoSender) -> CommandResult<()> {
729         if slot.len() > 1 {
730             return Err("Slot suffix must be one character".into());
731         }
732 
733         let slot_ch = slot.chars().next().ok_or("Invalid slot")?;
734         self.set_active_slot(slot).await?;
735         self.result.last_set_active_slot = Some(slot_ch);
736         Ok(())
737     }
738 
oem<'s>( &mut self, cmd: &str, mut responder: impl InfoSender, res: &'s mut [u8], ) -> CommandResult<&'s [u8]>739     async fn oem<'s>(
740         &mut self,
741         cmd: &str,
742         mut responder: impl InfoSender,
743         res: &'s mut [u8],
744     ) -> CommandResult<&'s [u8]> {
745         let mut args = cmd.split(' ');
746         let cmd = args.next().ok_or("Missing command")?;
747         match cmd {
748             "gbl-sync-tasks" => self.oem_sync_tasks(responder, res).await,
749             "gbl-enable-async-task" => {
750                 self.enable_async_task = true;
751                 Ok(b"")
752             }
753             "gbl-disable-async-task" => {
754                 self.enable_async_task = false;
755                 Ok(b"")
756             }
757             "gbl-unset-default-block" => {
758                 self.default_block = None;
759                 Ok(b"")
760             }
761             "gbl-set-default-block" => {
762                 let id = next_arg_u64(&mut args)?.ok_or("Missing block device ID")?;
763                 let id = usize::try_from(id)?;
764                 self.disks.get(id).ok_or("Out of range")?;
765                 self.default_block = Some(id.try_into()?);
766                 responder
767                     .send_formatted_info(|f| write!(f, "Default block device: {id:#x}").unwrap())
768                     .await?;
769                 Ok(b"")
770             }
771             "add-staged-bootloader-file" => {
772                 let file_name = next_arg(&mut args).ok_or("Missing file name")?;
773                 self.add_staged_bootloader_file(file_name).await?;
774                 Ok(b"")
775             }
776             _ => Err("Unknown oem command".into()),
777         }
778     }
779 
boot(&mut self, _: impl InfoSender + OkaySender) -> CommandResult<()>780     async fn boot(&mut self, _: impl InfoSender + OkaySender) -> CommandResult<()> {
781         let (mut data, sz) = self.take_download().ok_or("No boot image staged")?;
782         let bootimg_buffer = &mut data[..sz];
783         let load_buffer_addr = self.load_buffer.as_ptr() as usize;
784         let slot_suffix = get_boot_slot(self.gbl_ops, false)?;
785         let mut boot_part = [0u8; 16];
786         let boot_part = snprintf!(boot_part, "boot_{slot_suffix}");
787         // We still need to specify slot because other components such as vendor_boot, dtb, dtbo and
788         // vbmeta still come from the disk.
789         let slot_idx = (u64::from(slot_suffix) - u64::from('a')).try_into().unwrap();
790         let mut ramboot_ops =
791             RambootOps { ops: self.gbl_ops, preloaded_partitions: &[(boot_part, bootimg_buffer)] };
792         let (ramdisk, fdt, kernel, _) =
793             android_load_verify_fixup(&mut ramboot_ops, slot_idx, false, self.load_buffer)?;
794         self.result.loaded_image_info = Some(LoadedImageInfo::Android {
795             ramdisk: to_range(ramdisk.as_ptr() as usize - load_buffer_addr, ramdisk.len()),
796             fdt: to_range(fdt.as_ptr() as usize - load_buffer_addr, fdt.len()),
797             kernel: to_range(kernel.as_ptr() as usize - load_buffer_addr, kernel.len()),
798         });
799         Ok(())
800     }
801 }
802 
803 /// Helper to convert a offset and length to a range.
to_range(off: usize, len: usize) -> Range<usize>804 fn to_range(off: usize, len: usize) -> Range<usize> {
805     off..off.checked_add(len).unwrap()
806 }
807 
808 /// `GblUsbTransport` defines transport interfaces for running GBL fastboot over USB.
809 pub trait GblUsbTransport: Transport {
810     /// Checks whether there is a new USB packet.
has_packet(&mut self) -> bool811     fn has_packet(&mut self) -> bool;
812 }
813 
814 /// `GblTcpStream` defines transport interfaces for running GBL fastboot over TCP.
815 pub trait GblTcpStream: TcpStream {
816     /// Accepts a new TCP connection.
817     ///
818     /// If a connection is in progress, it should be aborted first.
819     ///
820     /// Returns true if a new connection is established, false otherwise.
accept_new(&mut self) -> bool821     fn accept_new(&mut self) -> bool;
822 }
823 
824 /// Runs GBL fastboot on the given USB/TCP channels.
825 ///
826 /// # Args:
827 ///
828 /// * `gbl_ops`: An instance of [GblOps].
829 /// * `buffer_pool`: An implementation of [BufferPool].
830 /// * `tasks`: An implementation of [PinFutContainer]
831 /// * `usb`: An optional implementation of [GblUsbTransport].
832 /// * `tcp`: An optional implementation of [GblTcpStream].
833 ///
834 /// # Lifetimes
835 /// * `'a`: Lifetime of [GblOps].
836 /// * `'b`: Lifetime of `download_buffers`.
837 /// * `'c`: Lifetime of `tasks`.
run_gbl_fastboot<'a: 'c, 'b: 'c, 'c, 'd>( gbl_ops: &mut impl GblOps<'a, 'd>, buffer_pool: &'b Shared<impl BufferPool>, tasks: impl PinFutContainer<'c> + 'c, local: Option<impl LocalSession>, usb: Option<impl GblUsbTransport>, tcp: Option<impl GblTcpStream>, load_buffer: &'b mut [u8], ) -> GblFastbootResult838 pub async fn run_gbl_fastboot<'a: 'c, 'b: 'c, 'c, 'd>(
839     gbl_ops: &mut impl GblOps<'a, 'd>,
840     buffer_pool: &'b Shared<impl BufferPool>,
841     tasks: impl PinFutContainer<'c> + 'c,
842     local: Option<impl LocalSession>,
843     usb: Option<impl GblUsbTransport>,
844     tcp: Option<impl GblTcpStream>,
845     load_buffer: &'b mut [u8],
846 ) -> GblFastbootResult {
847     let tasks = tasks.into();
848     let disks = gbl_ops.disks();
849     let mut fb = GblFastboot::new(gbl_ops, disks, Task::run, &tasks, buffer_pool, load_buffer);
850     fb.run(local, usb, tcp).await;
851     fb.result
852 }
853 
854 /// Runs GBL fastboot on the given USB/TCP channels with N stack allocated worker tasks.
855 ///
856 /// The choice of N depends on the level of parallelism the platform can support. For platform with
857 /// `n` storage devices that can independently perform non-blocking IO, it will required `N = n`
858 /// and a `buffer_pool` that can allocate at least n+1 buffers at the same time in order to achieve
859 /// parallel flashing to all storages plus a parallel downloading. However, it is common for
860 /// disks that need to be flashed to be on the same block deviece so flashing of them becomes
861 /// sequential, in which case N can be smaller. Caller should take into consideration usage pattern
862 /// for determining N.
863 ///
864 /// # Args:
865 ///
866 /// * `gbl_ops`: An instance of [GblOps].
867 /// * `buffer_pool`: An implementation of [BufferPool].
868 /// * `usb`: An optional implementation of [GblUsbTransport].
869 /// * `tcp`: An optional implementation of [GblTcpStream].
run_gbl_fastboot_stack<'a, 'b, const N: usize>( gbl_ops: &mut impl GblOps<'a, 'b>, buffer_pool: impl BufferPool, local: Option<impl LocalSession>, usb: Option<impl GblUsbTransport>, tcp: Option<impl GblTcpStream>, load_buffer: &mut [u8], ) -> GblFastbootResult870 pub async fn run_gbl_fastboot_stack<'a, 'b, const N: usize>(
871     gbl_ops: &mut impl GblOps<'a, 'b>,
872     buffer_pool: impl BufferPool,
873     local: Option<impl LocalSession>,
874     usb: Option<impl GblUsbTransport>,
875     tcp: Option<impl GblTcpStream>,
876     load_buffer: &mut [u8],
877 ) -> GblFastbootResult {
878     let buffer_pool = buffer_pool.into();
879     // Creates N worker tasks.
880     let mut tasks: [_; N] = from_fn(|_| Task::default().run());
881     // It is possible to avoid the use of the unsafe `Pin::new_unchecked` by delaring the array and
882     // manually pinning each element i.e.
883     //
884     // ```
885     // let mut tasks = [
886     //     core::pin::pin!(Task::None.run()),
887     //     core::pin::pin!(Task::None.run()),
888     //     core::pin::pin!(Task::None.run()),
889     // ];
890     // ```
891     //
892     // Parameterization of `N` will be an issue, but might be solvable with procedural macro.
893     // SAFETY: `tasks` is immediately shadowed and thus guaranteed not moved for the rest of its
894     // lifetime.
895     let mut tasks: [_; N] = tasks.each_mut().map(|v| unsafe { Pin::new_unchecked(v) });
896     let tasks = PinFutSlice::new(&mut tasks[..]).into();
897     let disks = gbl_ops.disks();
898     let mut fb = GblFastboot::new(gbl_ops, disks, Task::run, &tasks, &buffer_pool, load_buffer);
899     fb.run(local, usb, tcp).await;
900     fb.result
901 }
902 
903 /// Pre-generates a Fuchsia Fastboot MDNS service broadcast packet.
904 ///
905 /// Fuchsia ffx development flow can detect fastboot devices that broadcast a "_fastboot·_tcp·local"
906 /// MDNS service. This API generates the broadcast MDNS packet for Ipv6. Caller is reponsible for
907 /// sending this packet via UDP at the following address and port (defined by MDNS):
908 ///
909 /// * ipv6: ff02::fb
910 /// * port: 5353
911 ///
912 /// # Args
913 ///
914 /// * `node_name`: The Fuchsia node name for the service. Must be a 22 character ASCII string in the
915 ///   format "fuchsia-xxxx-xxxx-xxxx".
916 /// * `ipv6_addr`: The Ipv6 address bytes.
917 ///
918 /// The packet generated by the API contains the given IPv6 address and a fuchsia node name derived
919 /// from the given ethernet mac address `eth_mac`.
fuchsia_fastboot_mdns_packet(node_name: &str, ipv6_addr: &[u8]) -> Result<[u8; 140], Error>920 pub fn fuchsia_fastboot_mdns_packet(node_name: &str, ipv6_addr: &[u8]) -> Result<[u8; 140], Error> {
921     // Pre-generated Fuchsia fastboot MDNS service packet template.
922     // It contains the node name and ipv6 address. We simply replace with the device's node name and
923     // ipv6 address.
924     let mut packet: [u8; 140] = [
925         0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x09, 0x5f, 0x66,
926         0x61, 0x73, 0x74, 0x62, 0x6f, 0x6f, 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x6c, 0x6f,
927         0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x80, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x19, 0x16,
928         0x66, 0x75, 0x63, 0x68, 0x73, 0x69, 0x61, 0x2d, 0x34, 0x38, 0x32, 0x31, 0x2d, 0x30, 0x62,
929         0x33, 0x31, 0x2d, 0x65, 0x61, 0x66, 0x38, 0xc0, 0x0c, 0xc0, 0x2c, 0x00, 0x21, 0x80, 0x01,
930         0x00, 0x00, 0x00, 0x78, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x15, 0xb2, 0x16, 0x66, 0x75,
931         0x63, 0x68, 0x73, 0x69, 0x61, 0x2d, 0x34, 0x38, 0x32, 0x31, 0x2d, 0x30, 0x62, 0x33, 0x31,
932         0x2d, 0x65, 0x61, 0x66, 0x38, 0xc0, 0x1b, 0xc0, 0x57, 0x00, 0x1c, 0x80, 0x01, 0x00, 0x00,
933         0x00, 0x78, 0x00, 0x10, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x21, 0x0b,
934         0xff, 0xfe, 0x31, 0xea, 0xf8,
935     ];
936     // Offsets to the fuchsia node name field.
937     const NODE_NAME_OFFSETS: &[usize; 2] = &[45, 88];
938     // Offset to the IPv6 address field.
939     const IP6_ADDR_OFFSET: usize = 124;
940 
941     if node_name.as_bytes().len() != 22 {
942         return Err(Error::InvalidInput);
943     }
944 
945     for off in NODE_NAME_OFFSETS {
946         packet[*off..][..node_name.len()].clone_from_slice(node_name.as_bytes());
947     }
948     packet[IP6_ADDR_OFFSET..][..ipv6_addr.len()].clone_from_slice(ipv6_addr);
949     Ok(packet)
950 }
951 
952 #[cfg(test)]
953 pub(crate) mod test {
954     use super::*;
955     use crate::{
956         android_boot::{
957             load::tests::read_test_data,
958             tests::{
959                 checks_loaded_v2_slot_a_normal_mode, checks_loaded_v2_slot_b_normal_mode,
960                 default_test_gbl_ops,
961             },
962         },
963         constants::KiB,
964         constants::KERNEL_ALIGNMENT,
965         ops::test::{slot, FakeGblOps, FakeGblOpsStorage},
966         tests::AlignedBuffer,
967         Os,
968     };
969     use abr::{
970         get_and_clear_one_shot_bootloader, get_boot_slot, mark_slot_unbootable, ABR_DATA_SIZE,
971     };
972     use core::{
973         mem::size_of,
974         pin::{pin, Pin},
975         str::from_utf8,
976     };
977     use fastboot::{test_utils::TestUploadBuilder, MAX_RESPONSE_SIZE};
978     use gbl_async::{block_on, poll, poll_n_times};
979     use gbl_storage::GPT_GUID_LEN;
980     use liberror::Error;
981     use spin::{Mutex, MutexGuard};
982     use std::ffi::CString;
983     use std::{collections::VecDeque, io::Read};
984     use zerocopy::IntoBytes;
985 
986     /// A test implementation of [InfoSender] and [OkaySender].
987     #[derive(Default)]
988     struct TestResponder {
989         okay_sent: Mutex<bool>,
990         info_messages: Mutex<Vec<String>>,
991     }
992 
993     impl InfoSender for &TestResponder {
send_formatted_info<F: FnOnce(&mut dyn Write)>( &mut self, cb: F, ) -> Result<(), Error>994         async fn send_formatted_info<F: FnOnce(&mut dyn Write)>(
995             &mut self,
996             cb: F,
997         ) -> Result<(), Error> {
998             let mut msg: String = "".into();
999             cb(&mut msg);
1000             self.info_messages.try_lock().unwrap().push(msg);
1001             Ok(())
1002         }
1003     }
1004 
1005     impl OkaySender for &TestResponder {
1006         /// Sends a Fastboot "INFO<`msg`>" packet.
send_formatted_okay<F: FnOnce(&mut dyn Write)>(self, _: F) -> Result<(), Error>1007         async fn send_formatted_okay<F: FnOnce(&mut dyn Write)>(self, _: F) -> Result<(), Error> {
1008             *self.okay_sent.try_lock().unwrap() = true;
1009             Ok(())
1010         }
1011     }
1012 
1013     /// Helper to test fastboot variable value.
check_var(gbl_fb: &mut impl FastbootImplementation, var: &str, args: &str, expected: &str)1014     fn check_var(gbl_fb: &mut impl FastbootImplementation, var: &str, args: &str, expected: &str) {
1015         let resp: TestResponder = Default::default();
1016         let args_c = args.split(':').map(|v| CString::new(v).unwrap()).collect::<Vec<_>>();
1017         let args_c = args_c.iter().map(|v| v.as_c_str());
1018         let var_c = CString::new(var).unwrap();
1019         let mut out = vec![0u8; MAX_RESPONSE_SIZE];
1020         let val =
1021             block_on(gbl_fb.get_var_as_str(var_c.as_c_str(), args_c, &resp, &mut out[..])).unwrap();
1022         assert_eq!(val, expected, "var {}:{} = {} != {}", var, args, val, expected,);
1023     }
1024 
1025     /// A helper to set the download content.
set_download(gbl_fb: &mut impl FastbootImplementation, data: &[u8])1026     fn set_download(gbl_fb: &mut impl FastbootImplementation, data: &[u8]) {
1027         block_on(gbl_fb.get_download_buffer())[..data.len()].clone_from_slice(data);
1028         block_on(gbl_fb.download_complete(data.len(), &TestResponder::default())).unwrap();
1029     }
1030 
1031     impl<'a> PinFutContainer<'a> for Vec<Pin<Box<dyn Future<Output = ()> + 'a>>> {
add_with<F: Future<Output = ()> + 'a>(&mut self, f: impl FnOnce() -> F)1032         fn add_with<F: Future<Output = ()> + 'a>(&mut self, f: impl FnOnce() -> F) {
1033             self.push(Box::pin(f()));
1034         }
1035 
for_each_remove_if( &mut self, mut cb: impl FnMut(&mut Pin<&mut (dyn Future<Output = ()> + 'a)>) -> bool, )1036         fn for_each_remove_if(
1037             &mut self,
1038             mut cb: impl FnMut(&mut Pin<&mut (dyn Future<Output = ()> + 'a)>) -> bool,
1039         ) {
1040             for idx in (0..self.len()).rev() {
1041                 cb(&mut self[idx].as_mut()).then(|| self.swap_remove(idx));
1042             }
1043         }
1044     }
1045 
1046     #[test]
test_get_var_gbl()1047     fn test_get_var_gbl() {
1048         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 1]);
1049         let storage = FakeGblOpsStorage::default();
1050         let mut gbl_ops = FakeGblOps::new(&storage);
1051         let tasks = vec![].into();
1052         let parts = gbl_ops.disks();
1053         let mut gbl_fb =
1054             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1055         check_var(
1056             &mut gbl_fb,
1057             FakeGblOps::GBL_TEST_VAR,
1058             "arg",
1059             format!("{}:Some(\"arg\")", FakeGblOps::GBL_TEST_VAR_VAL).as_str(),
1060         );
1061     }
1062 
1063     #[test]
test_get_var_partition_info()1064     fn test_get_var_partition_info() {
1065         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 1]);
1066         let mut storage = FakeGblOpsStorage::default();
1067         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1068         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_2.bin"));
1069         storage.add_raw_device(c"raw_0", [0xaau8; KiB!(4)]);
1070         storage.add_raw_device(c"raw_1", [0x55u8; KiB!(8)]);
1071         let mut gbl_ops = FakeGblOps::new(&storage);
1072         let tasks = vec![].into();
1073         let parts = gbl_ops.disks();
1074         let mut gbl_fb =
1075             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1076 
1077         // Check different semantics
1078         check_var(&mut gbl_fb, "partition-size", "boot_a", "0x2000");
1079         check_var(&mut gbl_fb, "partition-size", "boot_a/", "0x2000");
1080         check_var(&mut gbl_fb, "partition-size", "boot_a//", "0x2000");
1081         check_var(&mut gbl_fb, "partition-size", "boot_a///", "0x2000");
1082         check_var(&mut gbl_fb, "partition-size", "boot_a/0", "0x2000");
1083         check_var(&mut gbl_fb, "partition-size", "boot_a/0/", "0x2000");
1084         check_var(&mut gbl_fb, "partition-size", "boot_a//0", "0x2000");
1085         check_var(&mut gbl_fb, "partition-size", "boot_a/0/0", "0x2000");
1086         check_var(&mut gbl_fb, "partition-size", "boot_a//0x1000", "0x1000");
1087 
1088         check_var(&mut gbl_fb, "partition-size", "boot_b/0", "0x3000");
1089         check_var(&mut gbl_fb, "partition-size", "vendor_boot_a/1", "0x1000");
1090         check_var(&mut gbl_fb, "partition-size", "vendor_boot_b/1", "0x1800");
1091         check_var(&mut gbl_fb, "partition-size", "boot_a//0x1000", "0x1000");
1092         check_var(&mut gbl_fb, "partition-size", "raw_0", "0x1000");
1093         check_var(&mut gbl_fb, "partition-size", "raw_1", "0x2000");
1094 
1095         let resp: TestResponder = Default::default();
1096         let mut out = vec![0u8; MAX_RESPONSE_SIZE];
1097         assert!(block_on(gbl_fb.get_var_as_str(
1098             c"partition",
1099             [c"non-existent"].into_iter(),
1100             &resp,
1101             &mut out[..],
1102         ))
1103         .is_err());
1104     }
1105 
1106     /// `TestVarSender` implements `TestVarSender`. It stores outputs in a vector of string.
1107     struct TestVarSender(Vec<String>);
1108 
1109     impl VarInfoSender for &mut TestVarSender {
send_var_info( &mut self, name: &str, args: impl IntoIterator<Item = &'_ str>, val: &str, ) -> Result<(), Error>1110         async fn send_var_info(
1111             &mut self,
1112             name: &str,
1113             args: impl IntoIterator<Item = &'_ str>,
1114             val: &str,
1115         ) -> Result<(), Error> {
1116             let args = args.into_iter().collect::<Vec<_>>();
1117             self.0.push(format!("{}:{}: {}", name, args.join(":"), val));
1118             Ok(())
1119         }
1120     }
1121 
1122     #[test]
test_get_var_all()1123     fn test_get_var_all() {
1124         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 1]);
1125         let mut storage = FakeGblOpsStorage::default();
1126         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1127         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_2.bin"));
1128         storage.add_raw_device(c"raw_0", [0xaau8; KiB!(4)]);
1129         storage.add_raw_device(c"raw_1", [0x55u8; KiB!(8)]);
1130         let mut gbl_ops = FakeGblOps::new(&storage);
1131         let tasks = vec![].into();
1132         let parts = gbl_ops.disks();
1133         let mut gbl_fb =
1134             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1135 
1136         let mut logger = TestVarSender(vec![]);
1137         block_on(gbl_fb.get_var_all(&mut logger)).unwrap();
1138         assert_eq!(
1139             logger.0,
1140             [
1141                 "version-bootloader:: 1.0",
1142                 "max-fetch-size:: 0xffffffffffffffff",
1143                 "block-device:0:total-blocks: 0x80",
1144                 "block-device:0:block-size: 0x200",
1145                 "block-device:0:status: idle",
1146                 "block-device:1:total-blocks: 0x100",
1147                 "block-device:1:block-size: 0x200",
1148                 "block-device:1:status: idle",
1149                 "block-device:2:total-blocks: 0x1000",
1150                 "block-device:2:block-size: 0x1",
1151                 "block-device:2:status: idle",
1152                 "block-device:3:total-blocks: 0x2000",
1153                 "block-device:3:block-size: 0x1",
1154                 "block-device:3:status: idle",
1155                 "gbl-default-block:: None",
1156                 "partition-size:boot_a/0: 0x2000",
1157                 "partition-type:boot_a/0: raw",
1158                 "partition-size:boot_b/0: 0x3000",
1159                 "partition-type:boot_b/0: raw",
1160                 "partition-size:vendor_boot_a/1: 0x1000",
1161                 "partition-type:vendor_boot_a/1: raw",
1162                 "partition-size:vendor_boot_b/1: 0x1800",
1163                 "partition-type:vendor_boot_b/1: raw",
1164                 "partition-size:raw_0/2: 0x1000",
1165                 "partition-type:raw_0/2: raw",
1166                 "partition-size:raw_1/3: 0x2000",
1167                 "partition-type:raw_1/3: raw",
1168                 format!("{}:1: {}:1", FakeGblOps::GBL_TEST_VAR, FakeGblOps::GBL_TEST_VAR_VAL)
1169                     .as_str(),
1170                 format!("{}:2: {}:2", FakeGblOps::GBL_TEST_VAR, FakeGblOps::GBL_TEST_VAR_VAL)
1171                     .as_str(),
1172             ]
1173         );
1174     }
1175 
1176     /// A helper for fetching partition from a `GblFastboot`
fetch<EOff: core::fmt::Debug, ESz: core::fmt::Debug>( fb: &mut impl FastbootImplementation, part: String, off: impl TryInto<u64, Error = EOff>, size: impl TryInto<u64, Error = ESz>, ) -> CommandResult<Vec<u8>>1177     fn fetch<EOff: core::fmt::Debug, ESz: core::fmt::Debug>(
1178         fb: &mut impl FastbootImplementation,
1179         part: String,
1180         off: impl TryInto<u64, Error = EOff>,
1181         size: impl TryInto<u64, Error = ESz>,
1182     ) -> CommandResult<Vec<u8>> {
1183         let off = off.try_into().unwrap();
1184         let size = size.try_into().unwrap();
1185         let mut upload_out = vec![0u8; usize::try_from(size).unwrap()];
1186         let test_uploader = TestUploadBuilder(&mut upload_out[..]);
1187         block_on(fb.fetch(part.as_str(), off, size, test_uploader))?;
1188         Ok(upload_out)
1189     }
1190 
1191     #[test]
test_fetch_invalid_partition_arg()1192     fn test_fetch_invalid_partition_arg() {
1193         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 1]);
1194         let mut storage = FakeGblOpsStorage::default();
1195         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1196         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_2.bin"));
1197         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_2.bin"));
1198         let mut gbl_ops = FakeGblOps::new(&storage);
1199         let tasks = vec![].into();
1200         let parts = gbl_ops.disks();
1201         let mut gbl_fb =
1202             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1203 
1204         // Missing mandatory block device ID for raw block partition.
1205         assert!(fetch(&mut gbl_fb, "//0/0".into(), 0, 0).is_err());
1206 
1207         // GPT partition does not exist.
1208         assert!(fetch(&mut gbl_fb, "non///".into(), 0, 0).is_err());
1209 
1210         // GPT Partition is not unique.
1211         assert!(fetch(&mut gbl_fb, "vendor_boot_a///".into(), 0, 0).is_err());
1212 
1213         // Offset overflows.
1214         assert!(fetch(&mut gbl_fb, "boot_a//0x2001/".into(), 0, 1).is_err());
1215         assert!(fetch(&mut gbl_fb, "boot_a".into(), 0x2000, 1).is_err());
1216 
1217         // Size overflows.
1218         assert!(fetch(&mut gbl_fb, "boot_a///0x2001".into(), 0, 0).is_err());
1219         assert!(fetch(&mut gbl_fb, "boot_a".into(), 0, 0x2001).is_err());
1220     }
1221 
1222     /// A helper for testing raw block upload. It verifies that data read from block device
1223     /// `blk_id` in range [`off`, `off`+`size`) is the same as `disk[off..][..size]`
check_blk_upload( fb: &mut impl FastbootImplementation, blk_id: u64, off: u64, size: u64, disk: &[u8], )1224     fn check_blk_upload(
1225         fb: &mut impl FastbootImplementation,
1226         blk_id: u64,
1227         off: u64,
1228         size: u64,
1229         disk: &[u8],
1230     ) {
1231         let expected = disk[off.try_into().unwrap()..][..size.try_into().unwrap()].to_vec();
1232         // offset/size as part of the partition string.
1233         let part = format!("/{:#x}/{:#x}/{:#x}", blk_id, off, size);
1234         assert_eq!(fetch(fb, part, 0, size).unwrap(), expected);
1235         // offset/size as separate fetch arguments.
1236         let part = format!("/{:#x}", blk_id);
1237         assert_eq!(fetch(fb, part, off, size).unwrap(), expected);
1238     }
1239 
1240     #[test]
test_fetch_raw_block()1241     fn test_fetch_raw_block() {
1242         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 1]);
1243         let mut storage = FakeGblOpsStorage::default();
1244         let disk_0 = include_bytes!("../../../libstorage/test/gpt_test_1.bin");
1245         let disk_1 = include_bytes!("../../../libstorage/test/gpt_test_2.bin");
1246         storage.add_gpt_device(disk_0);
1247         storage.add_gpt_device(disk_1);
1248         let mut gbl_ops = FakeGblOps::new(&storage);
1249         let tasks = vec![].into();
1250         let parts = gbl_ops.disks();
1251         let mut gbl_fb =
1252             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1253 
1254         let off = 512;
1255         let size = 512;
1256         check_blk_upload(&mut gbl_fb, 0, off, size, disk_0);
1257         check_blk_upload(&mut gbl_fb, 1, off, size, disk_1);
1258     }
1259 
1260     /// A helper for testing uploading GPT partition. It verifies that data read from GPT partition
1261     /// `part` at disk `blk_id` in range [`off`, `off`+`size`) is the same as
1262     /// `partition_data[off..][..size]`.
check_part_upload( fb: &mut impl FastbootImplementation, part: &str, off: u64, size: u64, blk_id: Option<u64>, partition_data: &[u8], )1263     fn check_part_upload(
1264         fb: &mut impl FastbootImplementation,
1265         part: &str,
1266         off: u64,
1267         size: u64,
1268         blk_id: Option<u64>,
1269         partition_data: &[u8],
1270     ) {
1271         let expected =
1272             partition_data[off.try_into().unwrap()..][..size.try_into().unwrap()].to_vec();
1273         let blk_id = blk_id.map_or("".to_string(), |v| format!("{:#x}", v));
1274         // offset/size as part of the partition string.
1275         let gpt_part = format!("{}/{}/{:#x}/{:#x}", part, blk_id, off, size);
1276         assert_eq!(fetch(fb, gpt_part, 0, size).unwrap(), expected);
1277         // offset/size as separate fetch arguments.
1278         let gpt_part = format!("{}/{}", part, blk_id);
1279         assert_eq!(fetch(fb, gpt_part, off, size).unwrap(), expected);
1280     }
1281 
1282     #[test]
test_fetch_partition()1283     fn test_fetch_partition() {
1284         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 1]);
1285         let mut storage = FakeGblOpsStorage::default();
1286         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1287         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_2.bin"));
1288         storage.add_raw_device(c"raw_0", [0xaau8; KiB!(4)]);
1289         storage.add_raw_device(c"raw_1", [0x55u8; KiB!(8)]);
1290         let mut gbl_ops = FakeGblOps::new(&storage);
1291         let tasks = vec![].into();
1292         let parts = gbl_ops.disks();
1293         let mut gbl_fb =
1294             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1295 
1296         let expect_boot_a = include_bytes!("../../../libstorage/test/boot_a.bin");
1297         let expect_boot_b = include_bytes!("../../../libstorage/test/boot_b.bin");
1298         let expect_vendor_boot_a = include_bytes!("../../../libstorage/test/vendor_boot_a.bin");
1299         let expect_vendor_boot_b = include_bytes!("../../../libstorage/test/vendor_boot_b.bin");
1300 
1301         let size = 512;
1302         let off = 512;
1303 
1304         check_part_upload(&mut gbl_fb, "boot_a", off, size, Some(0), expect_boot_a);
1305         check_part_upload(&mut gbl_fb, "boot_b", off, size, Some(0), expect_boot_b);
1306         check_part_upload(&mut gbl_fb, "vendor_boot_a", off, size, Some(1), expect_vendor_boot_a);
1307         check_part_upload(&mut gbl_fb, "vendor_boot_b", off, size, Some(1), expect_vendor_boot_b);
1308         check_part_upload(&mut gbl_fb, "raw_0", off, size, Some(2), &[0xaau8; KiB!(4)]);
1309         check_part_upload(&mut gbl_fb, "raw_1", off, size, Some(3), &[0x55u8; KiB!(8)]);
1310 
1311         // No block device id
1312         check_part_upload(&mut gbl_fb, "boot_a", off, size, None, expect_boot_a);
1313         check_part_upload(&mut gbl_fb, "boot_b", off, size, None, expect_boot_b);
1314         check_part_upload(&mut gbl_fb, "vendor_boot_a", off, size, None, expect_vendor_boot_a);
1315         check_part_upload(&mut gbl_fb, "vendor_boot_b", off, size, None, expect_vendor_boot_b);
1316         check_part_upload(&mut gbl_fb, "raw_0", off, size, None, &[0xaau8; KiB!(4)]);
1317         check_part_upload(&mut gbl_fb, "raw_1", off, size, None, &[0x55u8; KiB!(8)]);
1318     }
1319 
1320     /// A helper function to get a bit-flipped copy of the input data.
flipped_bits(data: &[u8]) -> Vec<u8>1321     fn flipped_bits(data: &[u8]) -> Vec<u8> {
1322         data.iter().map(|v| !(*v)).collect::<Vec<_>>()
1323     }
1324 
1325     /// A helper function to flash data to a partition
flash_part(fb: &mut impl FastbootImplementation, part: &str, data: &[u8])1326     fn flash_part(fb: &mut impl FastbootImplementation, part: &str, data: &[u8]) {
1327         // Prepare a download buffer.
1328         let dl_size = data.len();
1329         let download = data.to_vec();
1330         let resp: TestResponder = Default::default();
1331         set_download(fb, &download[..]);
1332         block_on(fb.flash(part, &resp)).unwrap();
1333         assert_eq!(fetch(fb, part.into(), 0, dl_size).unwrap(), download);
1334     }
1335 
1336     /// A helper for testing partition flashing.
check_flash_part(fb: &mut impl FastbootImplementation, part: &str, expected: &[u8])1337     fn check_flash_part(fb: &mut impl FastbootImplementation, part: &str, expected: &[u8]) {
1338         flash_part(fb, part, expected);
1339         // Also flashes bit-wise reversed version in case the initial content is the same.
1340         flash_part(fb, part, &flipped_bits(expected));
1341     }
1342 
1343     #[test]
test_flash_partition()1344     fn test_flash_partition() {
1345         let disk_0 = include_bytes!("../../../libstorage/test/gpt_test_1.bin");
1346         let disk_1 = include_bytes!("../../../libstorage/test/gpt_test_2.bin");
1347         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 1]);
1348         let mut storage = FakeGblOpsStorage::default();
1349         storage.add_gpt_device(disk_0);
1350         storage.add_gpt_device(disk_1);
1351         storage.add_raw_device(c"raw_0", [0xaau8; KiB!(4)]);
1352         storage.add_raw_device(c"raw_1", [0x55u8; KiB!(8)]);
1353         let mut gbl_ops = FakeGblOps::new(&storage);
1354         let tasks = vec![].into();
1355         let parts = gbl_ops.disks();
1356         let mut gbl_fb =
1357             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1358 
1359         let expect_boot_a = include_bytes!("../../../libstorage/test/boot_a.bin");
1360         let expect_boot_b = include_bytes!("../../../libstorage/test/boot_b.bin");
1361         check_flash_part(&mut gbl_fb, "boot_a", expect_boot_a);
1362         check_flash_part(&mut gbl_fb, "boot_b", expect_boot_b);
1363         check_flash_part(&mut gbl_fb, "raw_0", &[0xaau8; KiB!(4)]);
1364         check_flash_part(&mut gbl_fb, "raw_1", &[0x55u8; KiB!(8)]);
1365         check_flash_part(&mut gbl_fb, "/0", disk_0);
1366         check_flash_part(&mut gbl_fb, "/1", disk_1);
1367 
1368         // Partital flash
1369         let off = 0x200;
1370         let size = 1024;
1371         check_flash_part(&mut gbl_fb, "boot_a//200", &expect_boot_a[off..size]);
1372         check_flash_part(&mut gbl_fb, "boot_b//200", &expect_boot_b[off..size]);
1373         check_flash_part(&mut gbl_fb, "/0/200", &disk_0[off..size]);
1374         check_flash_part(&mut gbl_fb, "/1/200", &disk_1[off..size]);
1375     }
1376 
1377     #[test]
test_flash_partition_sparse()1378     fn test_flash_partition_sparse() {
1379         let raw = include_bytes!("../../testdata/sparse_test_raw.bin");
1380         let sparse = include_bytes!("../../testdata/sparse_test.bin");
1381         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 1]);
1382         let mut storage = FakeGblOpsStorage::default();
1383         storage.add_raw_device(c"raw", vec![0u8; raw.len()]);
1384         let mut gbl_ops = FakeGblOps::new(&storage);
1385         let tasks = vec![].into();
1386         let parts = gbl_ops.disks();
1387         let mut gbl_fb =
1388             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1389 
1390         let download = sparse.to_vec();
1391         let resp: TestResponder = Default::default();
1392         set_download(&mut gbl_fb, &download[..]);
1393         block_on(gbl_fb.flash("/0", &resp)).unwrap();
1394         assert_eq!(fetch(&mut gbl_fb, "/0".into(), 0, raw.len()).unwrap(), raw);
1395     }
1396 
1397     /// A helper to invoke OEM commands.
1398     ///
1399     /// Returns the result and INFO strings.
oem( fb: &mut impl FastbootImplementation, oem_cmd: &str, resp: impl InfoSender, ) -> CommandResult<String>1400     async fn oem(
1401         fb: &mut impl FastbootImplementation,
1402         oem_cmd: &str,
1403         resp: impl InfoSender,
1404     ) -> CommandResult<String> {
1405         let mut res = [0u8; MAX_RESPONSE_SIZE];
1406         fb.oem(oem_cmd, resp, &mut res[..]).await?;
1407         Ok(from_utf8(&mut res[..]).unwrap().into())
1408     }
1409 
1410     #[test]
test_async_flash()1411     fn test_async_flash() {
1412         // Creates two block devices for writing raw and sparse image.
1413         let sparse_raw = include_bytes!("../../testdata/sparse_test_raw.bin");
1414         let sparse = include_bytes!("../../testdata/sparse_test.bin");
1415         let mut storage = FakeGblOpsStorage::default();
1416         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1417         storage.add_gpt_device(vec![0u8; sparse_raw.len() + 67 * 512]);
1418         let mut gpt_builder = storage[1].gpt_builder().unwrap();
1419         gpt_builder.add("sparse", [1u8; GPT_GUID_LEN], [1u8; GPT_GUID_LEN], 0, None).unwrap();
1420         block_on(gpt_builder.persist()).unwrap();
1421         let mut gbl_ops = FakeGblOps::new(&storage);
1422         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 2]);
1423         let tasks = vec![].into();
1424         let parts = gbl_ops.disks();
1425         let mut gbl_fb =
1426             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1427         let tasks = gbl_fb.tasks();
1428         let resp: TestResponder = Default::default();
1429 
1430         // "oem gbl-sync-tasks" should return immediately when there is no pending IOs.
1431         assert!(poll(&mut pin!(oem(&mut gbl_fb, "gbl-sync-tasks", &resp))).unwrap().is_ok());
1432         // Enable async IO.
1433         assert!(poll(&mut pin!(oem(&mut gbl_fb, "gbl-enable-async-task", &resp))).unwrap().is_ok());
1434 
1435         // Flashes "boot_a".
1436         let expect_boot_a = flipped_bits(include_bytes!("../../../libstorage/test/boot_a.bin"));
1437         set_download(&mut gbl_fb, expect_boot_a.as_slice());
1438         block_on(gbl_fb.flash("boot_a", &resp)).unwrap();
1439         check_var(&mut gbl_fb, "block-device", "0:status", "IO pending");
1440 
1441         // Flashes the "sparse" partition on the different block device.
1442         set_download(&mut gbl_fb, sparse);
1443         block_on(gbl_fb.flash("sparse", &resp)).unwrap();
1444         check_var(&mut gbl_fb, "block-device", "1:status", "IO pending");
1445 
1446         {
1447             // "oem gbl-sync-tasks" should block.
1448             let oem_sync_blk_fut = &mut pin!(oem(&mut gbl_fb, "gbl-sync-tasks", &resp));
1449             assert!(poll(oem_sync_blk_fut).is_none());
1450             // Schedules the disk IO tasks to completion.
1451             tasks.borrow_mut().run();
1452             // "oem gbl-sync-tasks" should now be able to finish.
1453             assert!(poll(oem_sync_blk_fut).unwrap().is_ok());
1454         }
1455 
1456         // The two blocks should be in the idle state.
1457         check_var(&mut gbl_fb, "block-device", "0:status", "idle");
1458         check_var(&mut gbl_fb, "block-device", "1:status", "idle");
1459 
1460         // Verifies flashed image.
1461         assert_eq!(
1462             fetch(&mut gbl_fb, "boot_a".into(), 0, expect_boot_a.len()).unwrap(),
1463             expect_boot_a
1464         );
1465         assert_eq!(fetch(&mut gbl_fb, "sparse".into(), 0, sparse_raw.len()).unwrap(), sparse_raw);
1466     }
1467 
1468     #[test]
test_async_flash_block_on_busy_blk()1469     fn test_async_flash_block_on_busy_blk() {
1470         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 2]);
1471         let mut storage = FakeGblOpsStorage::default();
1472         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1473         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_2.bin"));
1474         let mut gbl_ops = FakeGblOps::new(&storage);
1475         let tasks = vec![].into();
1476         let parts = gbl_ops.disks();
1477         let mut gbl_fb =
1478             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1479         let tasks = gbl_fb.tasks();
1480         let resp: TestResponder = Default::default();
1481 
1482         // Enable async IO.
1483         assert!(poll(&mut pin!(oem(&mut gbl_fb, "gbl-enable-async-task", &resp))).unwrap().is_ok());
1484 
1485         // Flashes boot_a partition.
1486         let expect_boot_a = flipped_bits(include_bytes!("../../../libstorage/test/boot_a.bin"));
1487         set_download(&mut gbl_fb, expect_boot_a.as_slice());
1488         block_on(gbl_fb.flash("boot_a", &resp)).unwrap();
1489 
1490         // Flashes boot_b partition.
1491         let expect_boot_b = flipped_bits(include_bytes!("../../../libstorage/test/boot_b.bin"));
1492         set_download(&mut gbl_fb, expect_boot_b.as_slice());
1493         {
1494             let flash_boot_b_fut = &mut pin!(gbl_fb.flash("boot_b", &resp));
1495             // Previous IO has not completed. Block is busy.
1496             assert!(poll(flash_boot_b_fut).is_none());
1497             // There should only be the previous disk IO task for "boot_a".
1498             assert_eq!(tasks.borrow_mut().size(), 1);
1499             // Schedule the disk IO task for "flash boot_a" to completion.
1500             tasks.borrow_mut().run();
1501             // The blocked "flash boot_b" should now be able to finish.
1502             assert!(poll(flash_boot_b_fut).is_some());
1503             // There should be a disk IO task spawned for "flash boot_b".
1504             assert_eq!(tasks.borrow_mut().size(), 1);
1505             // Schedule the disk IO tasks for "flash boot_b" to completion.
1506             tasks.borrow_mut().run();
1507         }
1508 
1509         // Verifies flashed image.
1510         assert_eq!(
1511             fetch(&mut gbl_fb, "boot_a".into(), 0, expect_boot_a.len()).unwrap(),
1512             expect_boot_a
1513         );
1514         assert_eq!(
1515             fetch(&mut gbl_fb, "boot_b".into(), 0, expect_boot_b.len()).unwrap(),
1516             expect_boot_b
1517         );
1518     }
1519 
1520     #[test]
1521     #[should_panic(
1522         expected = "A Fastboot async task failed: Other(Some(\"test\")), context: flash:boot_a"
1523     )]
test_async_flash_error()1524     fn test_async_flash_error() {
1525         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 2]);
1526         let mut storage = FakeGblOpsStorage::default();
1527         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1528         let mut gbl_ops = FakeGblOps::new(&storage);
1529         // Injects an error.
1530         storage[0].partition_io(None).unwrap().dev().io().error =
1531             liberror::Error::Other(Some("test")).into();
1532         let tasks = vec![].into();
1533         let parts = gbl_ops.disks();
1534         let mut gbl_fb =
1535             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1536         let tasks = gbl_fb.tasks();
1537         let resp: TestResponder = Default::default();
1538 
1539         // Enable async IO.
1540         assert!(poll(&mut pin!(oem(&mut gbl_fb, "gbl-enable-async-task", &resp))).unwrap().is_ok());
1541         // Flashes boot_a partition.
1542         let expect_boot_a = flipped_bits(include_bytes!("../../../libstorage/test/boot_a.bin"));
1543         set_download(&mut gbl_fb, expect_boot_a.as_slice());
1544         block_on(gbl_fb.flash("boot_a", &resp)).unwrap();
1545         // Schedules the disk IO tasks to completion.
1546         tasks.borrow_mut().run();
1547     }
1548 
1549     #[test]
test_async_erase()1550     fn test_async_erase() {
1551         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 2]);
1552         let mut storage = FakeGblOpsStorage::default();
1553         storage.add_raw_device(c"raw_0", [0xaau8; 4096]);
1554         storage.add_raw_device(c"raw_1", [0x55u8; 4096]);
1555         let mut gbl_ops = FakeGblOps::new(&storage);
1556         let tasks = vec![].into();
1557         let parts = gbl_ops.disks();
1558         let mut gbl_fb =
1559             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1560         let tasks = gbl_fb.tasks();
1561         let resp: TestResponder = Default::default();
1562 
1563         // Enable async IO.
1564         assert!(poll(&mut pin!(oem(&mut gbl_fb, "gbl-enable-async-task", &resp))).unwrap().is_ok());
1565 
1566         // Erases "raw_0".
1567         block_on(gbl_fb.erase("raw_0", &resp)).unwrap();
1568         check_var(&mut gbl_fb, "block-device", "0:status", "IO pending");
1569 
1570         // Erases second half of "raw_1"
1571         block_on(gbl_fb.erase("raw_1//800", &resp)).unwrap();
1572         check_var(&mut gbl_fb, "block-device", "1:status", "IO pending");
1573 
1574         {
1575             // "oem gbl-sync-tasks" should block.
1576             let oem_sync_blk_fut = &mut pin!(oem(&mut gbl_fb, "gbl-sync-tasks", &resp));
1577             assert!(poll(oem_sync_blk_fut).is_none());
1578             // Schedules the disk IO tasks to completion.
1579             tasks.borrow_mut().run();
1580             // "oem gbl-sync-tasks" should now be able to finish.
1581             assert!(poll(oem_sync_blk_fut).unwrap().is_ok());
1582         }
1583 
1584         // The two blocks should be in the idle state.
1585         check_var(&mut gbl_fb, "block-device", "0:status", "idle");
1586         check_var(&mut gbl_fb, "block-device", "1:status", "idle");
1587 
1588         assert_eq!(storage[0].partition_io(None).unwrap().dev().io().storage, [0u8; 4096]);
1589         assert_eq!(
1590             storage[1].partition_io(None).unwrap().dev().io().storage,
1591             [[0x55u8; 2048], [0u8; 2048]].concat()
1592         );
1593     }
1594 
1595     #[test]
1596     #[should_panic(
1597         expected = "A Fastboot async task failed: Other(Some(\"test\")), context: erase:boot_a"
1598     )]
test_async_erase_error()1599     fn test_async_erase_error() {
1600         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 2]);
1601         let mut storage = FakeGblOpsStorage::default();
1602         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1603         let mut gbl_ops = FakeGblOps::new(&storage);
1604         // Injects an error.
1605         storage[0].partition_io(None).unwrap().dev().io().error =
1606             liberror::Error::Other(Some("test")).into();
1607         let tasks = vec![].into();
1608         let parts = gbl_ops.disks();
1609         let mut gbl_fb =
1610             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1611         let tasks = gbl_fb.tasks();
1612         let resp: TestResponder = Default::default();
1613 
1614         // Enable async IO.
1615         assert!(poll(&mut pin!(oem(&mut gbl_fb, "gbl-enable-async-task", &resp))).unwrap().is_ok());
1616         // Erases boot_a partition.
1617         block_on(gbl_fb.erase("boot_a", &resp)).unwrap();
1618         // Schedules the disk IO tasks to completion.
1619         tasks.borrow_mut().run();
1620     }
1621 
1622     #[test]
test_default_block()1623     fn test_default_block() {
1624         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 1]);
1625         let mut storage = FakeGblOpsStorage::default();
1626         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1627         let disk_dup = include_bytes!("../../../libstorage/test/gpt_test_2.bin");
1628         storage.add_gpt_device(disk_dup);
1629         storage.add_gpt_device(disk_dup);
1630         let raw_a = [0xaau8; KiB!(4)];
1631         let raw_b = [0x55u8; KiB!(8)];
1632         storage.add_raw_device(c"raw", raw_a);
1633         storage.add_raw_device(c"raw", raw_b);
1634         let mut gbl_ops = FakeGblOps::new(&storage);
1635         let tasks = vec![].into();
1636         let parts = gbl_ops.disks();
1637         let mut gbl_fb =
1638             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1639         let resp: TestResponder = Default::default();
1640 
1641         let boot_a = include_bytes!("../../../libstorage/test/boot_a.bin");
1642         // Flips the bits on partition "vendor_boot_a" on block device #2 to make it different from
1643         // block #1.
1644         let vendor_boot_a =
1645             flipped_bits(include_bytes!("../../../libstorage/test/vendor_boot_a.bin"));
1646         flash_part(&mut gbl_fb, "vendor_boot_a/2", &vendor_boot_a);
1647 
1648         let size = 512;
1649         let off = 512;
1650 
1651         check_var(&mut gbl_fb, "gbl-default-block", "", "None");
1652         // Sets default block to #2
1653         block_on(oem(&mut gbl_fb, "gbl-set-default-block 2", &resp)).unwrap();
1654         check_var(&mut gbl_fb, "gbl-default-block", "", "0x2");
1655         // The following fetch should succeed and fetch from "vendor_boot_a" on block 2.
1656         check_part_upload(&mut gbl_fb, "vendor_boot_a", off, size, None, &vendor_boot_a);
1657 
1658         // Sets default block to #4 (raw_b)
1659         block_on(oem(&mut gbl_fb, "gbl-set-default-block 4", &resp)).unwrap();
1660         check_var(&mut gbl_fb, "gbl-default-block", "", "0x4");
1661         // The following fetch should succeed and fetch from "raw" on block 4.
1662         check_part_upload(&mut gbl_fb, "raw", off, size, None, &raw_b);
1663 
1664         // Fetches with explicit storage ID shouldn't be affected.
1665         check_part_upload(&mut gbl_fb, "boot_a", off, size, Some(0), boot_a);
1666         check_part_upload(&mut gbl_fb, "raw", off, size, Some(3), &raw_a);
1667         check_blk_upload(&mut gbl_fb, 1, off, size, disk_dup);
1668 
1669         // Fetching without storage ID should use default ID and thus the following should fail.
1670         assert!(fetch(&mut gbl_fb, "boot_a".into(), 0, boot_a.len()).is_err());
1671 
1672         // Sets default block to #1 (unmodified `disk_dup`)
1673         block_on(oem(&mut gbl_fb, "gbl-set-default-block 1", &resp)).unwrap();
1674         check_var(&mut gbl_fb, "gbl-default-block", "", "0x1");
1675         // Fetches whole raw block but without block ID should use the default block.
1676         check_part_upload(&mut gbl_fb, "", off, size, None, disk_dup);
1677 
1678         // Unset default block
1679         block_on(oem(&mut gbl_fb, "gbl-unset-default-block", &resp)).unwrap();
1680         check_var(&mut gbl_fb, "gbl-default-block", "", "None");
1681         // Fetching non-unique partitions should now fail.
1682         assert!(fetch(&mut gbl_fb, "raw".into(), 0, raw_a.len()).is_err());
1683         assert!(fetch(&mut gbl_fb, "vendor_boot_a".into(), 0, vendor_boot_a.len()).is_err());
1684         assert!(fetch(&mut gbl_fb, "/".into(), 0, 512).is_err());
1685     }
1686 
1687     #[test]
test_set_default_block_invalid_arg()1688     fn test_set_default_block_invalid_arg() {
1689         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 2]);
1690         let storage = FakeGblOpsStorage::default();
1691         let mut gbl_ops = FakeGblOps::new(&storage);
1692         let tasks = vec![].into();
1693         let parts = gbl_ops.disks();
1694         let mut gbl_fb =
1695             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1696         let resp: TestResponder = Default::default();
1697         // Missing block device ID.
1698         assert!(block_on(oem(&mut gbl_fb, "gbl-set-default-block ", &resp)).is_err());
1699         // Invalid block device ID.
1700         assert!(block_on(oem(&mut gbl_fb, "gbl-set-default-block zzz", &resp)).is_err());
1701         // Out of range block device ID. (We've added no block device).
1702         assert!(block_on(oem(&mut gbl_fb, "gbl-set-default-block 0", &resp)).is_err());
1703     }
1704 
1705     #[test]
test_reboot_sync_all_blocks()1706     fn test_reboot_sync_all_blocks() {
1707         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 2]);
1708         let mut storage = FakeGblOpsStorage::default();
1709         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1710         let mut gbl_ops = FakeGblOps::new(&storage);
1711         let tasks = vec![].into();
1712         let parts = gbl_ops.disks();
1713         let mut gbl_fb =
1714             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1715         let tasks = gbl_fb.tasks();
1716         let resp: TestResponder = Default::default();
1717 
1718         block_on(oem(&mut gbl_fb, "gbl-enable-async-task", &resp)).unwrap();
1719 
1720         // Flashes "boot_a".
1721         let expect_boot_a = flipped_bits(include_bytes!("../../../libstorage/test/boot_a.bin"));
1722         set_download(&mut gbl_fb, expect_boot_a.as_slice());
1723         block_on(gbl_fb.flash("boot_a", &resp)).unwrap();
1724         // Checks initial state, okay_sent=false.
1725         assert!(!(*resp.okay_sent.try_lock().unwrap()));
1726         // Performs a reboot.
1727         let mut reboot_fut = pin!(gbl_fb.reboot(RebootMode::Normal, &resp));
1728         // There is a pending flash task. Reboot should wait.
1729         assert!(poll(&mut reboot_fut).is_none());
1730         assert!(!(*resp.okay_sent.try_lock().unwrap()));
1731         assert_eq!(resp.info_messages.try_lock().unwrap()[1], "Syncing storage...");
1732         // Schedules the disk IO tasks to completion.
1733         tasks.borrow_mut().run();
1734         // The reboot can now complete.
1735         assert!(poll(&mut reboot_fut).is_some());
1736         assert!((*resp.okay_sent.try_lock().unwrap()));
1737         assert_eq!(resp.info_messages.try_lock().unwrap()[2], "Rebooting...");
1738     }
1739 
1740     #[test]
test_continue_sync_all_blocks()1741     fn test_continue_sync_all_blocks() {
1742         let dl_buffers = Shared::from(vec![vec![0u8; KiB!(128)]; 2]);
1743         let mut storage = FakeGblOpsStorage::default();
1744         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
1745         let mut gbl_ops = FakeGblOps::new(&storage);
1746         let tasks = vec![].into();
1747         let parts = gbl_ops.disks();
1748         let mut gbl_fb =
1749             GblFastboot::new(&mut gbl_ops, parts, Task::run, &tasks, &dl_buffers, &mut []);
1750         let tasks = gbl_fb.tasks();
1751         let resp: TestResponder = Default::default();
1752 
1753         block_on(oem(&mut gbl_fb, "gbl-enable-async-task", &resp)).unwrap();
1754 
1755         // Flashes "boot_a".
1756         let expect_boot_a = flipped_bits(include_bytes!("../../../libstorage/test/boot_a.bin"));
1757         set_download(&mut gbl_fb, expect_boot_a.as_slice());
1758         block_on(gbl_fb.flash("boot_a", &resp)).unwrap();
1759         // Performs a continue.
1760         let mut continue_fut = pin!(gbl_fb.r#continue(&resp));
1761         // There is a pending flash task. Continue should wait.
1762         assert!(poll(&mut continue_fut).is_none());
1763         assert!(!(*resp.okay_sent.try_lock().unwrap()));
1764         assert_eq!(resp.info_messages.try_lock().unwrap()[1], "Syncing storage...");
1765         // Schedules the disk IO tasks to completion.
1766         tasks.borrow_mut().run();
1767         // The continue can now complete.
1768         assert!(poll(&mut continue_fut).is_some());
1769     }
1770 
1771     /// Generates a length prefixed byte sequence.
length_prefixed(data: &[u8]) -> Vec<u8>1772     fn length_prefixed(data: &[u8]) -> Vec<u8> {
1773         [&data.len().to_be_bytes()[..], data].concat()
1774     }
1775 
1776     /// Used for a test implementation of [GblUsbTransport] and [GblTcpStream].
1777     #[derive(Default)]
1778     struct TestListener {
1779         usb_in_queue: VecDeque<Vec<u8>>,
1780         usb_out_queue: VecDeque<Vec<u8>>,
1781 
1782         tcp_in_queue: VecDeque<u8>,
1783         tcp_out_queue: VecDeque<u8>,
1784     }
1785 
1786     /// A shared [TestListener].
1787     #[derive(Default)]
1788     pub(crate) struct SharedTestListener(Mutex<TestListener>);
1789 
1790     impl SharedTestListener {
1791         /// Locks the listener
lock(&self) -> MutexGuard<TestListener>1792         fn lock(&self) -> MutexGuard<TestListener> {
1793             self.0.try_lock().unwrap()
1794         }
1795 
1796         /// Adds packet to USB input
add_usb_input(&self, packet: &[u8])1797         pub(crate) fn add_usb_input(&self, packet: &[u8]) {
1798             self.lock().usb_in_queue.push_back(packet.into());
1799         }
1800 
1801         /// Adds bytes to input stream.
add_tcp_input(&self, data: &[u8])1802         pub(crate) fn add_tcp_input(&self, data: &[u8]) {
1803             self.lock().tcp_in_queue.append(&mut data.to_vec().into());
1804         }
1805 
1806         /// Adds a length pre-fixed bytes stream.
add_tcp_length_prefixed_input(&self, data: &[u8])1807         pub(crate) fn add_tcp_length_prefixed_input(&self, data: &[u8]) {
1808             self.add_tcp_input(&length_prefixed(data));
1809         }
1810 
1811         /// Gets a copy of `Self::usb_out_queue`.
usb_out_queue(&self) -> VecDeque<Vec<u8>>1812         pub(crate) fn usb_out_queue(&self) -> VecDeque<Vec<u8>> {
1813             self.lock().usb_out_queue.clone()
1814         }
1815 
1816         /// Gets a copy of `Self::tcp_out_queue`.
tcp_out_queue(&self) -> VecDeque<u8>1817         pub(crate) fn tcp_out_queue(&self) -> VecDeque<u8> {
1818             self.lock().tcp_out_queue.clone()
1819         }
1820 
1821         /// A helper for decoding USB output packets as a string
dump_usb_out_queue(&self) -> String1822         pub(crate) fn dump_usb_out_queue(&self) -> String {
1823             let mut res = String::from("");
1824             for v in self.lock().usb_out_queue.iter() {
1825                 let v = String::from_utf8(v.clone()).unwrap_or(format!("{:?}", v));
1826                 res += format!("b{:?},\n", v).as_str();
1827             }
1828             res
1829         }
1830 
1831         /// A helper for decoding TCP output data as strings
dump_tcp_out_queue(&self) -> String1832         pub(crate) fn dump_tcp_out_queue(&self) -> String {
1833             let mut data = self.lock();
1834             let mut v;
1835             let (_, mut remains) = data.tcp_out_queue.make_contiguous().split_at(4);
1836             let mut res = String::from("");
1837             while !remains.is_empty() {
1838                 // Parses length-prefixed payload.
1839                 let (len, rest) = remains.split_first_chunk::<{ size_of::<u64>() }>().unwrap();
1840                 (v, remains) = rest.split_at(u64::from_be_bytes(*len).try_into().unwrap());
1841                 let s = String::from_utf8(v.to_vec()).unwrap_or(format!("{:?}", v));
1842                 res += format!("b{:?},\n", s).as_str();
1843             }
1844             res
1845         }
1846     }
1847 
1848     impl Transport for &SharedTestListener {
receive_packet(&mut self, out: &mut [u8]) -> Result<usize, Error>1849         async fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize, Error> {
1850             match self.lock().usb_in_queue.pop_front() {
1851                 Some(v) => Ok((&v[..]).read(out).unwrap()),
1852                 _ => Err(Error::Other(Some("No more data"))),
1853             }
1854         }
1855 
send_packet(&mut self, packet: &[u8]) -> Result<(), Error>1856         async fn send_packet(&mut self, packet: &[u8]) -> Result<(), Error> {
1857             Ok(self.lock().usb_out_queue.push_back(packet.into()))
1858         }
1859     }
1860 
1861     impl GblUsbTransport for &SharedTestListener {
has_packet(&mut self) -> bool1862         fn has_packet(&mut self) -> bool {
1863             !self.lock().usb_in_queue.is_empty()
1864         }
1865     }
1866 
1867     impl TcpStream for &SharedTestListener {
read_exact(&mut self, out: &mut [u8]) -> Result<(), Error>1868         async fn read_exact(&mut self, out: &mut [u8]) -> Result<(), Error> {
1869             match self.lock().tcp_in_queue.read(out).unwrap() == out.len() {
1870                 true => Ok(()),
1871                 _ => Err(Error::Other(Some("No more data"))),
1872             }
1873         }
1874 
write_exact(&mut self, data: &[u8]) -> Result<(), Error>1875         async fn write_exact(&mut self, data: &[u8]) -> Result<(), Error> {
1876             Ok(self.lock().tcp_out_queue.append(&mut data.to_vec().into()))
1877         }
1878     }
1879 
1880     impl GblTcpStream for &SharedTestListener {
accept_new(&mut self) -> bool1881         fn accept_new(&mut self) -> bool {
1882             !self.lock().tcp_in_queue.is_empty()
1883         }
1884     }
1885 
1886     /// A helper to make an expected stream of USB output.
make_expected_usb_out(data: &[&[u8]]) -> VecDeque<Vec<u8>>1887     pub(crate) fn make_expected_usb_out(data: &[&[u8]]) -> VecDeque<Vec<u8>> {
1888         VecDeque::from(data.iter().map(|v| v.to_vec()).collect::<Vec<_>>())
1889     }
1890 
1891     /// A helper to make an expected stream of TCP output.
make_expected_tcp_out(data: &[&[u8]]) -> VecDeque<u8>1892     fn make_expected_tcp_out(data: &[&[u8]]) -> VecDeque<u8> {
1893         let mut res = VecDeque::<u8>::from(b"FB01".to_vec());
1894         data.iter().for_each(|v| res.append(&mut length_prefixed(v).into()));
1895         res
1896     }
1897 
1898     #[derive(Default)]
1899     pub(crate) struct TestLocalSession {
1900         requests: VecDeque<&'static str>,
1901         outgoing_packets: VecDeque<Vec<u8>>,
1902     }
1903 
1904     impl LocalSession for &mut TestLocalSession {
update(&mut self, buf: &mut [u8]) -> Result<usize, Error>1905         async fn update(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
1906             let Some(front) = self.requests.pop_front() else {
1907                 return Ok(0);
1908             };
1909             let front_len = front.len();
1910             if front_len >= buf.len() {
1911                 self.requests.push_front(front);
1912                 return Err(Error::BufferTooSmall(Some(front_len)));
1913             }
1914             buf[..front_len].copy_from_slice(front.as_bytes());
1915             buf[front_len] = b'\0';
1916             Ok(front_len)
1917         }
1918 
process_outgoing_packet(&mut self, buf: &[u8])1919         async fn process_outgoing_packet(&mut self, buf: &[u8]) {
1920             self.outgoing_packets.push_back(buf.into());
1921         }
1922     }
1923 
1924     impl From<&[&'static str]> for TestLocalSession {
from(elts: &[&'static str]) -> Self1925         fn from(elts: &[&'static str]) -> Self {
1926             let elts = elts.into_iter();
1927             let mut requests = VecDeque::with_capacity(elts.len());
1928             for e in elts {
1929                 requests.push_back(*e);
1930             }
1931             Self { requests, outgoing_packets: VecDeque::new() }
1932         }
1933     }
1934 
1935     #[test]
test_run_gbl_fastboot()1936     fn test_run_gbl_fastboot() {
1937         let storage = FakeGblOpsStorage::default();
1938         let buffers = vec![vec![0u8; KiB!(128)]; 2];
1939         let mut gbl_ops = FakeGblOps::new(&storage);
1940         let listener: SharedTestListener = Default::default();
1941         let (usb, tcp) = (&listener, &listener);
1942 
1943         listener.add_usb_input(b"getvar:version-bootloader");
1944         listener.add_tcp_input(b"FB01");
1945         listener.add_tcp_length_prefixed_input(b"getvar:max-download-size");
1946         listener.add_tcp_length_prefixed_input(b"continue");
1947         block_on(run_gbl_fastboot_stack::<3>(
1948             &mut gbl_ops,
1949             buffers,
1950             Some(&mut TestLocalSession::default()),
1951             Some(usb),
1952             Some(tcp),
1953             &mut [],
1954         ));
1955 
1956         assert_eq!(
1957             listener.usb_out_queue(),
1958             make_expected_usb_out(&[b"OKAY1.0"]),
1959             "\nActual USB output:\n{}",
1960             listener.dump_usb_out_queue()
1961         );
1962 
1963         assert_eq!(
1964             listener.tcp_out_queue(),
1965             make_expected_tcp_out(&[b"OKAY0x20000", b"INFOSyncing storage...", b"OKAY"]),
1966             "\nActual TCP output:\n{}",
1967             listener.dump_tcp_out_queue()
1968         );
1969     }
1970 
1971     #[test]
test_run_gbl_fastboot_local_session()1972     fn test_run_gbl_fastboot_local_session() {
1973         let storage = FakeGblOpsStorage::default();
1974         let buffers = vec![vec![0u8; KiB!(128)]; 2];
1975         let mut gbl_ops = FakeGblOps::new(&storage);
1976         let mut local = TestLocalSession::from(["reboot", "continue"].as_slice());
1977         block_on(run_gbl_fastboot_stack::<3>(
1978             &mut gbl_ops,
1979             buffers,
1980             Some(&mut local),
1981             None::<&SharedTestListener>,
1982             None::<&SharedTestListener>,
1983             &mut [],
1984         ));
1985 
1986         assert!(gbl_ops.rebooted);
1987     }
1988 
1989     #[test]
test_run_gbl_fastboot_parallel_task()1990     fn test_run_gbl_fastboot_parallel_task() {
1991         let mut storage = FakeGblOpsStorage::default();
1992         storage.add_raw_device(c"raw_0", [0u8; KiB!(4)]);
1993         storage.add_raw_device(c"raw_1", [0u8; KiB!(8)]);
1994         let buffers = vec![vec![0u8; KiB!(128)]; 2];
1995         let mut gbl_ops = FakeGblOps::new(&storage);
1996         let listener: SharedTestListener = Default::default();
1997         let (usb, tcp) = (&listener, &listener);
1998         let mut local = TestLocalSession::from(["getvar:all"].as_slice());
1999 
2000         // New scope to release reference on local
2001         {
2002             let mut fb_fut = pin!(run_gbl_fastboot_stack::<3>(
2003                 &mut gbl_ops,
2004                 buffers,
2005                 Some(&mut local),
2006                 Some(usb),
2007                 Some(tcp),
2008                 &mut []
2009             ));
2010 
2011             listener.add_usb_input(b"oem gbl-enable-async-task");
2012             listener.add_usb_input(format!("download:{:#x}", KiB!(4)).as_bytes());
2013             listener.add_usb_input(&[0x55u8; KiB!(4)]);
2014             listener.add_usb_input(b"flash:raw_0");
2015 
2016             listener.add_tcp_input(b"FB01");
2017             listener.add_tcp_length_prefixed_input(format!("download:{:#x}", KiB!(8)).as_bytes());
2018             listener.add_tcp_length_prefixed_input(&[0xaau8; KiB!(8)]);
2019             listener.add_tcp_length_prefixed_input(b"flash:raw_1");
2020 
2021             assert!(poll_n_times(&mut fb_fut, 100).is_none());
2022         }
2023 
2024         assert_eq!(
2025             listener.usb_out_queue(),
2026             make_expected_usb_out(&[
2027                 b"OKAY",
2028                 b"DATA00001000",
2029                 b"OKAY",
2030                 b"INFOAn async task is launched. To sync manually, run \"oem gbl-sync-tasks\".",
2031                 b"OKAY",
2032             ]),
2033             "\nActual USB output:\n{}",
2034             listener.dump_usb_out_queue()
2035         );
2036 
2037         assert_eq!(
2038             listener.tcp_out_queue(),
2039             make_expected_tcp_out(&[
2040                 b"DATA00002000",
2041                 b"OKAY",
2042                 b"INFOAn async task is launched. To sync manually, run \"oem gbl-sync-tasks\".",
2043                 b"OKAY",
2044             ]),
2045             "\nActual TCP output:\n{}",
2046             listener.dump_tcp_out_queue()
2047         );
2048 
2049         assert_eq!(
2050             local.outgoing_packets,
2051             VecDeque::from(vec![
2052                 Vec::from(b"INFOmax-download-size: 0x20000"),
2053                 Vec::from(b"INFOversion-bootloader: 1.0"),
2054                 Vec::from(b"INFOmax-fetch-size: 0xffffffffffffffff"),
2055                 Vec::from(b"INFOblock-device:0:total-blocks: 0x1000"),
2056                 Vec::from(b"INFOblock-device:0:block-size: 0x1"),
2057                 Vec::from(b"INFOblock-device:0:status: idle"),
2058                 Vec::from(b"INFOblock-device:1:total-blocks: 0x2000"),
2059                 Vec::from(b"INFOblock-device:1:block-size: 0x1"),
2060                 Vec::from(b"INFOblock-device:1:status: idle"),
2061                 Vec::from(b"INFOgbl-default-block: None"),
2062                 Vec::from(b"INFOpartition-size:raw_0/0: 0x1000"),
2063                 Vec::from(b"INFOpartition-type:raw_0/0: raw"),
2064                 Vec::from(b"INFOpartition-size:raw_1/1: 0x2000"),
2065                 Vec::from(b"INFOpartition-type:raw_1/1: raw"),
2066                 Vec::from(b"INFOgbl-test-var:1: gbl-test-var-val:1"),
2067                 Vec::from(b"INFOgbl-test-var:2: gbl-test-var-val:2"),
2068                 Vec::from(b"OKAY"),
2069             ])
2070         );
2071 
2072         // Verifies flashed image on raw_0.
2073         assert_eq!(storage[0].partition_io(None).unwrap().dev().io().storage, [0x55u8; KiB!(4)]);
2074 
2075         // Verifies flashed image on raw_1.
2076         assert_eq!(storage[1].partition_io(None).unwrap().dev().io().storage, [0xaau8; KiB!(8)]);
2077     }
2078 
2079     #[test]
test_oem_add_staged_bootloader_file()2080     fn test_oem_add_staged_bootloader_file() {
2081         let storage = FakeGblOpsStorage::default();
2082         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2083         let mut gbl_ops = FakeGblOps::new(&storage);
2084         gbl_ops.get_zbi_bootloader_files_buffer().unwrap().fill(0);
2085         let listener: SharedTestListener = Default::default();
2086         let (usb, tcp) = (&listener, &listener);
2087 
2088         // Stages two zbi files.
2089         listener.add_usb_input(format!("download:{:#x}", 3).as_bytes());
2090         listener.add_usb_input(b"foo");
2091         listener.add_usb_input(b"oem add-staged-bootloader-file file_1");
2092         listener.add_usb_input(format!("download:{:#x}", 3).as_bytes());
2093         listener.add_usb_input(b"bar");
2094         listener.add_usb_input(b"oem add-staged-bootloader-file file_2");
2095         listener.add_usb_input(b"continue");
2096 
2097         block_on(run_gbl_fastboot_stack::<3>(
2098             &mut gbl_ops,
2099             buffers,
2100             Some(&mut TestLocalSession::default()),
2101             Some(usb),
2102             Some(tcp),
2103             &mut [],
2104         ));
2105 
2106         let buffer = gbl_ops.get_zbi_bootloader_files_buffer_aligned().unwrap();
2107         let container = ZbiContainer::parse(&buffer[..]).unwrap();
2108         let mut iter = container.iter();
2109         assert_eq!(iter.next().unwrap().payload.as_bytes(), b"\x06file_1foo");
2110         assert_eq!(iter.next().unwrap().payload.as_bytes(), b"\x06file_2bar");
2111         assert!(iter.next().is_none());
2112     }
2113 
2114     #[test]
test_oem_add_staged_bootloader_file_missing_file_name()2115     fn test_oem_add_staged_bootloader_file_missing_file_name() {
2116         let storage = FakeGblOpsStorage::default();
2117         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2118         let mut gbl_ops = FakeGblOps::new(&storage);
2119         let listener: SharedTestListener = Default::default();
2120         let (usb, tcp) = (&listener, &listener);
2121 
2122         listener.add_usb_input(format!("download:{:#x}", 3).as_bytes());
2123         listener.add_usb_input(b"foo");
2124         listener.add_usb_input(b"oem add-staged-bootloader-file");
2125         listener.add_usb_input(b"continue");
2126 
2127         block_on(run_gbl_fastboot_stack::<3>(
2128             &mut gbl_ops,
2129             buffers,
2130             Some(&mut TestLocalSession::default()),
2131             Some(usb),
2132             Some(tcp),
2133             &mut [],
2134         ));
2135 
2136         assert_eq!(
2137             listener.usb_out_queue(),
2138             make_expected_usb_out(&[
2139                 b"DATA00000003",
2140                 b"OKAY",
2141                 b"FAILMissing file name",
2142                 b"INFOSyncing storage...",
2143                 b"OKAY",
2144             ]),
2145             "\nActual USB output:\n{}",
2146             listener.dump_usb_out_queue()
2147         )
2148     }
2149 
2150     #[test]
test_oem_add_staged_bootloader_file_missing_download()2151     fn test_oem_add_staged_bootloader_file_missing_download() {
2152         let storage = FakeGblOpsStorage::default();
2153         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2154         let mut gbl_ops = FakeGblOps::new(&storage);
2155         let listener: SharedTestListener = Default::default();
2156         let (usb, tcp) = (&listener, &listener);
2157 
2158         listener.add_usb_input(b"oem add-staged-bootloader-file file1");
2159         listener.add_usb_input(b"continue");
2160 
2161         block_on(run_gbl_fastboot_stack::<3>(
2162             &mut gbl_ops,
2163             buffers,
2164             Some(&mut TestLocalSession::default()),
2165             Some(usb),
2166             Some(tcp),
2167             &mut [],
2168         ));
2169 
2170         assert_eq!(
2171             listener.usb_out_queue(),
2172             make_expected_usb_out(&[b"FAILNo file staged", b"INFOSyncing storage...", b"OKAY",]),
2173             "\nActual USB output:\n{}",
2174             listener.dump_usb_out_queue()
2175         );
2176     }
2177 
2178     #[test]
test_fuchsia_fastboot_mdns_packet()2179     fn test_fuchsia_fastboot_mdns_packet() {
2180         let expected = [
2181             0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x09, 0x5f,
2182             0x66, 0x61, 0x73, 0x74, 0x62, 0x6f, 0x6f, 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05,
2183             0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x80, 0x01, 0x00, 0x00, 0x00, 0x78,
2184             0x00, 0x19, 0x16, 0x66, 0x75, 0x63, 0x68, 0x73, 0x69, 0x61, 0x2d, 0x35, 0x32, 0x35,
2185             0x34, 0x2d, 0x30, 0x30, 0x31, 0x32, 0x2d, 0x33, 0x34, 0x35, 0x36, 0xc0, 0x0c, 0xc0,
2186             0x2c, 0x00, 0x21, 0x80, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x1f, 0x00, 0x00, 0x00,
2187             0x00, 0x15, 0xb2, 0x16, 0x66, 0x75, 0x63, 0x68, 0x73, 0x69, 0x61, 0x2d, 0x35, 0x32,
2188             0x35, 0x34, 0x2d, 0x30, 0x30, 0x31, 0x32, 0x2d, 0x33, 0x34, 0x35, 0x36, 0xc0, 0x1b,
2189             0xc0, 0x57, 0x00, 0x1c, 0x80, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x10, 0xfe, 0x80,
2190             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x54, 0x00, 0xff, 0xfe, 0x12, 0x34, 0x56,
2191         ];
2192         let ip6_addr = &[
2193             0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x54, 0x00, 0xff, 0xfe, 0x12,
2194             0x34, 0x56,
2195         ];
2196         assert_eq!(
2197             fuchsia_fastboot_mdns_packet("fuchsia-5254-0012-3456", ip6_addr).unwrap(),
2198             expected
2199         );
2200     }
2201 
2202     #[test]
test_fuchsia_fastboot_mdns_packet_invalid_node_name()2203     fn test_fuchsia_fastboot_mdns_packet_invalid_node_name() {
2204         let ip6_addr = &[
2205             0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x54, 0x00, 0xff, 0xfe, 0x12,
2206             0x34, 0x56,
2207         ];
2208         assert!(fuchsia_fastboot_mdns_packet("fuchsia-5254-0012-345", ip6_addr).is_err());
2209         assert!(fuchsia_fastboot_mdns_packet("fuchsia-5254-0012-34567", ip6_addr).is_err());
2210     }
2211 
2212     #[test]
test_oem_update_gpt()2213     fn test_oem_update_gpt() {
2214         let disk_orig = include_bytes!("../../../libstorage/test/gpt_test_1.bin");
2215         // Erase the primary and secondary header.
2216         let mut disk = disk_orig.to_vec();
2217         disk[512..][..512].fill(0);
2218         disk.last_chunk_mut::<512>().unwrap().fill(0);
2219 
2220         let mut storage = FakeGblOpsStorage::default();
2221         storage.add_gpt_device(&disk);
2222         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_2.bin"));
2223         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2224         let mut gbl_ops = FakeGblOps::new(&storage);
2225         let listener: SharedTestListener = Default::default();
2226         let (usb, tcp) = (&listener, &listener);
2227 
2228         // Checks that there is no valid partitions for block #0.
2229         listener.add_usb_input(b"getvar:partition-size:boot_a");
2230         listener.add_usb_input(b"getvar:partition-size:boot_b");
2231         // No partitions on block #0 should show up in `getvar:all` despite being a GPT device,
2232         // since the GPTs are corrupted.
2233         listener.add_usb_input(b"getvar:all");
2234         // Download a GPT
2235         let gpt = &disk_orig[..34 * 512];
2236         listener.add_usb_input(format!("download:{:#x}", gpt.len()).as_bytes());
2237         listener.add_usb_input(gpt);
2238         listener.add_usb_input(b"flash:gpt/0");
2239         // Checks that we can get partition info now.
2240         listener.add_usb_input(b"getvar:partition-size:boot_a");
2241         listener.add_usb_input(b"getvar:partition-size:boot_b");
2242         listener.add_usb_input(b"getvar:all");
2243 
2244         listener.add_usb_input(b"continue");
2245 
2246         block_on(run_gbl_fastboot_stack::<3>(
2247             &mut gbl_ops,
2248             buffers,
2249             Some(&mut TestLocalSession::default()),
2250             Some(usb),
2251             Some(tcp),
2252             &mut [],
2253         ));
2254 
2255         assert_eq!(
2256             listener.usb_out_queue(),
2257             make_expected_usb_out(&[
2258                 b"FAILNotFound",
2259                 b"FAILNotFound",
2260                 b"INFOmax-download-size: 0x20000",
2261                 b"INFOversion-bootloader: 1.0",
2262                 b"INFOmax-fetch-size: 0xffffffffffffffff",
2263                 b"INFOblock-device:0:total-blocks: 0x80",
2264                 b"INFOblock-device:0:block-size: 0x200",
2265                 b"INFOblock-device:0:status: idle",
2266                 b"INFOblock-device:1:total-blocks: 0x100",
2267                 b"INFOblock-device:1:block-size: 0x200",
2268                 b"INFOblock-device:1:status: idle",
2269                 b"INFOgbl-default-block: None",
2270                 b"INFOpartition-size:vendor_boot_a/1: 0x1000",
2271                 b"INFOpartition-type:vendor_boot_a/1: raw",
2272                 b"INFOpartition-size:vendor_boot_b/1: 0x1800",
2273                 b"INFOpartition-type:vendor_boot_b/1: raw",
2274                 format!("INFO{}:1: {}:1", FakeGblOps::GBL_TEST_VAR, FakeGblOps::GBL_TEST_VAR_VAL)
2275                     .as_bytes(),
2276                 format!("INFO{}:2: {}:2", FakeGblOps::GBL_TEST_VAR, FakeGblOps::GBL_TEST_VAR_VAL)
2277                     .as_bytes(),
2278                 b"OKAY",
2279                 b"DATA00004400",
2280                 b"OKAY",
2281                 b"INFOUpdating GPT...",
2282                 b"OKAY",
2283                 b"OKAY0x2000",
2284                 b"OKAY0x3000",
2285                 b"INFOmax-download-size: 0x20000",
2286                 b"INFOversion-bootloader: 1.0",
2287                 b"INFOmax-fetch-size: 0xffffffffffffffff",
2288                 b"INFOblock-device:0:total-blocks: 0x80",
2289                 b"INFOblock-device:0:block-size: 0x200",
2290                 b"INFOblock-device:0:status: idle",
2291                 b"INFOblock-device:1:total-blocks: 0x100",
2292                 b"INFOblock-device:1:block-size: 0x200",
2293                 b"INFOblock-device:1:status: idle",
2294                 b"INFOgbl-default-block: None",
2295                 b"INFOpartition-size:boot_a/0: 0x2000",
2296                 b"INFOpartition-type:boot_a/0: raw",
2297                 b"INFOpartition-size:boot_b/0: 0x3000",
2298                 b"INFOpartition-type:boot_b/0: raw",
2299                 b"INFOpartition-size:vendor_boot_a/1: 0x1000",
2300                 b"INFOpartition-type:vendor_boot_a/1: raw",
2301                 b"INFOpartition-size:vendor_boot_b/1: 0x1800",
2302                 b"INFOpartition-type:vendor_boot_b/1: raw",
2303                 format!("INFO{}:1: {}:1", FakeGblOps::GBL_TEST_VAR, FakeGblOps::GBL_TEST_VAR_VAL)
2304                     .as_bytes(),
2305                 format!("INFO{}:2: {}:2", FakeGblOps::GBL_TEST_VAR, FakeGblOps::GBL_TEST_VAR_VAL)
2306                     .as_bytes(),
2307                 b"OKAY",
2308                 b"INFOSyncing storage...",
2309                 b"OKAY",
2310             ]),
2311             "\nActual USB output:\n{}",
2312             listener.dump_usb_out_queue()
2313         );
2314     }
2315 
2316     #[test]
test_oem_update_gpt_resize()2317     fn test_oem_update_gpt_resize() {
2318         let disk_orig = include_bytes!("../../../libstorage/test/gpt_test_1.bin");
2319         let mut disk = disk_orig.to_vec();
2320         // Doubles the size of the disk
2321         disk.resize(disk_orig.len() * 2, 0);
2322 
2323         let mut storage = FakeGblOpsStorage::default();
2324         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_2.bin"));
2325         storage.add_gpt_device(&disk);
2326         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2327         let mut gbl_ops = FakeGblOps::new(&storage);
2328         let listener: SharedTestListener = Default::default();
2329         let (usb, tcp) = (&listener, &listener);
2330 
2331         // Checks current size of last partition `boot_b`.
2332         listener.add_usb_input(b"getvar:partition-size:boot_b");
2333         // Sets a default block.
2334         listener.add_usb_input(b"oem gbl-set-default-block 1");
2335         let gpt = &disk_orig[..34 * 512];
2336         listener.add_usb_input(format!("download:{:#x}", gpt.len()).as_bytes());
2337         listener.add_usb_input(gpt);
2338         // No need to specify block device index
2339         listener.add_usb_input(b"flash:gpt//resize");
2340         // Checks updated size of last partition `boot_b`.
2341         listener.add_usb_input(b"getvar:partition-size:boot_b");
2342         listener.add_usb_input(b"continue");
2343 
2344         block_on(run_gbl_fastboot_stack::<3>(
2345             &mut gbl_ops,
2346             buffers,
2347             Some(&mut TestLocalSession::default()),
2348             Some(usb),
2349             Some(tcp),
2350             &mut [],
2351         ));
2352 
2353         assert_eq!(
2354             listener.usb_out_queue(),
2355             make_expected_usb_out(&[
2356                 b"OKAY0x3000",
2357                 b"INFODefault block device: 0x1",
2358                 b"OKAY",
2359                 b"DATA00004400",
2360                 b"OKAY",
2361                 b"INFOUpdating GPT...",
2362                 b"OKAY",
2363                 b"OKAY0x15a00",
2364                 b"INFOSyncing storage...",
2365                 b"OKAY",
2366             ]),
2367             "\nActual USB output:\n{}",
2368             listener.dump_usb_out_queue()
2369         );
2370     }
2371 
2372     #[test]
test_oem_update_gpt_no_downloaded_gpt()2373     fn test_oem_update_gpt_no_downloaded_gpt() {
2374         let disk = include_bytes!("../../../libstorage/test/gpt_test_1.bin");
2375         let mut storage = FakeGblOpsStorage::default();
2376         storage.add_gpt_device(&disk);
2377         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2378         let mut gbl_ops = FakeGblOps::new(&storage);
2379         let listener: SharedTestListener = Default::default();
2380         let (usb, tcp) = (&listener, &listener);
2381 
2382         listener.add_usb_input(b"flash:gpt/0");
2383         listener.add_usb_input(b"continue");
2384 
2385         block_on(run_gbl_fastboot_stack::<3>(
2386             &mut gbl_ops,
2387             buffers,
2388             Some(&mut TestLocalSession::default()),
2389             Some(usb),
2390             Some(tcp),
2391             &mut [],
2392         ));
2393 
2394         assert_eq!(
2395             listener.usb_out_queue(),
2396             make_expected_usb_out(&[b"FAILNo GPT downloaded", b"INFOSyncing storage...", b"OKAY",]),
2397             "\nActual USB output:\n{}",
2398             listener.dump_usb_out_queue()
2399         );
2400     }
2401 
2402     #[test]
test_oem_update_gpt_bad_gpt()2403     fn test_oem_update_gpt_bad_gpt() {
2404         let disk = include_bytes!("../../../libstorage/test/gpt_test_1.bin");
2405         let mut storage = FakeGblOpsStorage::default();
2406         storage.add_gpt_device(&disk);
2407         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2408         let mut gbl_ops = FakeGblOps::new(&storage);
2409         let listener: SharedTestListener = Default::default();
2410         let (usb, tcp) = (&listener, &listener);
2411         // Download a bad GPT.
2412         let mut gpt = disk[..34 * 512].to_vec();
2413         gpt[512] = !gpt[512];
2414         listener.add_usb_input(format!("download:{:#x}", gpt.len()).as_bytes());
2415         listener.add_usb_input(&gpt);
2416         listener.add_usb_input(b"flash:gpt/0");
2417         listener.add_usb_input(b"continue");
2418 
2419         block_on(run_gbl_fastboot_stack::<3>(
2420             &mut gbl_ops,
2421             buffers,
2422             Some(&mut TestLocalSession::default()),
2423             Some(usb),
2424             Some(tcp),
2425             &mut [],
2426         ));
2427 
2428         assert_eq!(
2429             listener.usb_out_queue(),
2430             make_expected_usb_out(&[
2431                 b"DATA00004400",
2432                 b"OKAY",
2433                 b"INFOUpdating GPT...",
2434                 b"FAILGptError(\n    IncorrectMagic(\n        6075990659671082682,\n    ),\n)",
2435                 b"INFOSyncing storage...",
2436                 b"OKAY",
2437             ]),
2438             "\nActual USB output:\n{}",
2439             listener.dump_usb_out_queue()
2440         );
2441     }
2442 
2443     #[test]
test_oem_update_gpt_invalid_input()2444     fn test_oem_update_gpt_invalid_input() {
2445         let disk_orig = include_bytes!("../../../libstorage/test/gpt_test_1.bin");
2446         let mut storage = FakeGblOpsStorage::default();
2447         storage.add_gpt_device(&disk_orig);
2448         storage.add_gpt_device(&disk_orig);
2449         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2450         let mut gbl_ops = FakeGblOps::new(&storage);
2451         let listener: SharedTestListener = Default::default();
2452         let (usb, tcp) = (&listener, &listener);
2453 
2454         let gpt = &disk_orig[..34 * 512];
2455         listener.add_usb_input(format!("download:{:#x}", gpt.len()).as_bytes());
2456         listener.add_usb_input(gpt);
2457         // Missing block device ID.
2458         listener.add_usb_input(b"flash:gpt");
2459         // Out of range block device ID.
2460         listener.add_usb_input(b"flash:gpt/2");
2461         // Invalid option.
2462         listener.add_usb_input(b"flash:gpt/0/invalid-arg");
2463         listener.add_usb_input(b"continue");
2464         block_on(run_gbl_fastboot_stack::<3>(
2465             &mut gbl_ops,
2466             buffers,
2467             Some(&mut TestLocalSession::default()),
2468             Some(usb),
2469             Some(tcp),
2470             &mut [],
2471         ));
2472 
2473         assert_eq!(
2474             listener.usb_out_queue(),
2475             make_expected_usb_out(&[
2476                 b"DATA00004400",
2477                 b"OKAY",
2478                 b"FAILBlock ID is required for flashing GPT",
2479                 b"FAILInvalid block ID",
2480                 b"FAILUnknown argument",
2481                 b"INFOSyncing storage...",
2482                 b"OKAY",
2483             ]),
2484             "\nActual USB output:\n{}",
2485             listener.dump_usb_out_queue()
2486         );
2487     }
2488 
2489     #[test]
test_oem_update_gpt_fail_on_raw_blk()2490     fn test_oem_update_gpt_fail_on_raw_blk() {
2491         let disk_orig = include_bytes!("../../../libstorage/test/gpt_test_1.bin");
2492         let mut storage = FakeGblOpsStorage::default();
2493         storage.add_raw_device(c"raw_0", [0u8; KiB!(1024)]);
2494         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2495         let mut gbl_ops = FakeGblOps::new(&storage);
2496         let listener: SharedTestListener = Default::default();
2497         let (usb, tcp) = (&listener, &listener);
2498 
2499         let gpt = &disk_orig[..34 * 512];
2500         listener.add_usb_input(format!("download:{:#x}", gpt.len()).as_bytes());
2501         listener.add_usb_input(gpt);
2502         listener.add_usb_input(b"flash:gpt/0");
2503         listener.add_usb_input(b"continue");
2504         block_on(run_gbl_fastboot_stack::<3>(
2505             &mut gbl_ops,
2506             buffers,
2507             Some(&mut TestLocalSession::default()),
2508             Some(usb),
2509             Some(tcp),
2510             &mut [],
2511         ));
2512 
2513         assert_eq!(
2514             listener.usb_out_queue(),
2515             make_expected_usb_out(&[
2516                 b"DATA00004400",
2517                 b"OKAY",
2518                 b"INFOUpdating GPT...",
2519                 b"FAILBlock device is not for GPT",
2520                 b"INFOSyncing storage...",
2521                 b"OKAY",
2522             ]),
2523             "\nActual USB output:\n{}",
2524             listener.dump_usb_out_queue()
2525         );
2526     }
2527 
2528     #[test]
test_oem_erase_gpt()2529     fn test_oem_erase_gpt() {
2530         let mut storage = FakeGblOpsStorage::default();
2531         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_1.bin"));
2532         storage.add_gpt_device(include_bytes!("../../../libstorage/test/gpt_test_2.bin"));
2533         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2534         let mut gbl_ops = FakeGblOps::new(&storage);
2535         let listener: SharedTestListener = Default::default();
2536         let (usb, tcp) = (&listener, &listener);
2537 
2538         // Erases the GPT on disk #0.
2539         listener.add_usb_input(b"erase:gpt/0");
2540         // Checks that we can no longer get partition info on disk #0.
2541         listener.add_usb_input(b"getvar:partition-size:boot_a");
2542         listener.add_usb_input(b"getvar:partition-size:boot_b");
2543         // Checks that we can still get partition info on disk #1.
2544         listener.add_usb_input(b"getvar:partition-size:vendor_boot_a");
2545         listener.add_usb_input(b"getvar:partition-size:vendor_boot_b");
2546         listener.add_usb_input(b"continue");
2547 
2548         block_on(run_gbl_fastboot_stack::<3>(
2549             &mut gbl_ops,
2550             buffers,
2551             Some(&mut TestLocalSession::default()),
2552             Some(usb),
2553             Some(tcp),
2554             &mut [],
2555         ));
2556 
2557         assert_eq!(
2558             listener.usb_out_queue(),
2559             make_expected_usb_out(&[
2560                 b"OKAY",
2561                 b"FAILNotFound",
2562                 b"FAILNotFound",
2563                 b"OKAY0x1000",
2564                 b"OKAY0x1800",
2565                 b"INFOSyncing storage...",
2566                 b"OKAY",
2567             ]),
2568             "\nActual USB output:\n{}",
2569             listener.dump_usb_out_queue()
2570         );
2571     }
2572 
2573     #[test]
test_oem_erase_gpt_fail_on_raw_blk()2574     fn test_oem_erase_gpt_fail_on_raw_blk() {
2575         let mut storage = FakeGblOpsStorage::default();
2576         storage.add_raw_device(c"raw_0", [0u8; KiB!(1024)]);
2577         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2578         let mut gbl_ops = FakeGblOps::new(&storage);
2579         let listener: SharedTestListener = Default::default();
2580         let (usb, tcp) = (&listener, &listener);
2581 
2582         listener.add_usb_input(b"erase:gpt/0");
2583         listener.add_usb_input(b"continue");
2584         block_on(run_gbl_fastboot_stack::<3>(
2585             &mut gbl_ops,
2586             buffers,
2587             Some(&mut TestLocalSession::default()),
2588             Some(usb),
2589             Some(tcp),
2590             &mut [],
2591         ));
2592 
2593         assert_eq!(
2594             listener.usb_out_queue(),
2595             make_expected_usb_out(&[
2596                 b"FAILBlock device is not for GPT",
2597                 b"INFOSyncing storage...",
2598                 b"OKAY",
2599             ]),
2600             "\nActual USB output:\n{}",
2601             listener.dump_usb_out_queue()
2602         );
2603     }
2604 
2605     /// Helper for testing fastboot set_active in fuchsia A/B/R mode.
test_run_gbl_fastboot_set_active_fuchsia_abr(slot_ch: char, slot: SlotIndex)2606     fn test_run_gbl_fastboot_set_active_fuchsia_abr(slot_ch: char, slot: SlotIndex) {
2607         let mut storage = FakeGblOpsStorage::default();
2608         storage.add_raw_device(c"durable_boot", [0x00u8; KiB!(4)]);
2609         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2610         let mut gbl_ops = FakeGblOps::new(&storage);
2611         gbl_ops.os = Some(Os::Fuchsia);
2612         let listener: SharedTestListener = Default::default();
2613         let (usb, tcp) = (&listener, &listener);
2614 
2615         mark_slot_unbootable(&mut GblAbrOps(&mut gbl_ops), SlotIndex::A).unwrap();
2616         mark_slot_unbootable(&mut GblAbrOps(&mut gbl_ops), SlotIndex::B).unwrap();
2617 
2618         // Flash some data to `durable_boot` after A/B/R metadata. This is for testing that sync
2619         // storage is done first.
2620         let data = vec![0x55u8; KiB!(4) - ABR_DATA_SIZE];
2621         listener.add_usb_input(b"oem gbl-enable-async-task");
2622         listener.add_usb_input(format!("download:{:#x}", KiB!(4) - ABR_DATA_SIZE).as_bytes());
2623         listener.add_usb_input(&data);
2624         listener.add_usb_input(format!("flash:durable_boot//{:#x}", ABR_DATA_SIZE).as_bytes());
2625         // Issues set_active commands
2626         listener.add_usb_input(format!("set_active:{slot_ch}").as_bytes());
2627         listener.add_usb_input(b"continue");
2628         let res = block_on(run_gbl_fastboot_stack::<3>(
2629             &mut gbl_ops,
2630             buffers,
2631             Some(&mut TestLocalSession::default()),
2632             Some(usb),
2633             Some(tcp),
2634             &mut [],
2635         ));
2636         assert_eq!(res.last_set_active_slot, Some(slot_ch));
2637 
2638         assert_eq!(
2639             listener.usb_out_queue(),
2640             make_expected_usb_out(&[
2641                 b"OKAY",
2642                 b"DATA00000fe0",
2643                 b"OKAY",
2644                 b"INFOAn async task is launched. To sync manually, run \"oem gbl-sync-tasks\".",
2645                 b"OKAY",
2646                 b"OKAY",
2647                 b"INFOSyncing storage...",
2648                 b"OKAY",
2649             ]),
2650             "\nActual USB output:\n{}",
2651             listener.dump_usb_out_queue()
2652         );
2653 
2654         assert_eq!(get_boot_slot(&mut GblAbrOps(&mut gbl_ops), true), (slot, false));
2655         // Verifies storage sync
2656         assert_eq!(
2657             storage[0].partition_io(None).unwrap().dev().io().storage[ABR_DATA_SIZE..],
2658             data
2659         );
2660     }
2661 
2662     #[test]
test_run_gbl_fastboot_set_active_fuchsia_abr_a()2663     fn test_run_gbl_fastboot_set_active_fuchsia_abr_a() {
2664         test_run_gbl_fastboot_set_active_fuchsia_abr('a', SlotIndex::A);
2665     }
2666 
2667     #[test]
test_run_gbl_fastboot_set_active_fuchsia_abr_b()2668     fn test_run_gbl_fastboot_set_active_fuchsia_abr_b() {
2669         test_run_gbl_fastboot_set_active_fuchsia_abr('b', SlotIndex::B);
2670     }
2671 
2672     #[test]
test_run_gbl_fastboot_set_active_fuchsia_abr_invalid_slot()2673     fn test_run_gbl_fastboot_set_active_fuchsia_abr_invalid_slot() {
2674         let mut storage = FakeGblOpsStorage::default();
2675         storage.add_raw_device(c"durable_boot", [0x00u8; KiB!(4)]);
2676         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2677         let mut gbl_ops = FakeGblOps::new(&storage);
2678         gbl_ops.os = Some(Os::Fuchsia);
2679         let listener: SharedTestListener = Default::default();
2680         let (usb, tcp) = (&listener, &listener);
2681 
2682         listener.add_usb_input(b"set_active:r");
2683         listener.add_usb_input(b"continue");
2684         block_on(run_gbl_fastboot_stack::<3>(
2685             &mut gbl_ops,
2686             buffers,
2687             Some(&mut TestLocalSession::default()),
2688             Some(usb),
2689             Some(tcp),
2690             &mut [],
2691         ));
2692 
2693         assert_eq!(
2694             listener.usb_out_queue(),
2695             make_expected_usb_out(&[
2696                 b"FAILInvalid slot index for Fuchsia A/B/R",
2697                 b"INFOSyncing storage...",
2698                 b"OKAY",
2699             ]),
2700             "\nActual USB output:\n{}",
2701             listener.dump_usb_out_queue()
2702         );
2703     }
2704 
2705     #[test]
test_run_gbl_fastboot_set_active_android()2706     fn test_run_gbl_fastboot_set_active_android() {
2707         let storage = FakeGblOpsStorage::default();
2708         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2709         let mut gbl_ops = FakeGblOps::new(&storage);
2710         gbl_ops.os = Some(Os::Android);
2711         let listener: SharedTestListener = Default::default();
2712         let (usb, tcp) = (&listener, &listener);
2713 
2714         listener.add_usb_input(b"set_active:b");
2715         listener.add_usb_input(b"continue");
2716         block_on(run_gbl_fastboot_stack::<2>(
2717             &mut gbl_ops,
2718             buffers,
2719             Some(&mut TestLocalSession::default()),
2720             Some(usb),
2721             Some(tcp),
2722             &mut [],
2723         ));
2724 
2725         assert_eq!(
2726             listener.usb_out_queue(),
2727             make_expected_usb_out(&[b"OKAY", b"INFOSyncing storage...", b"OKAY",]),
2728             "\nActual USB output:\n{}",
2729             listener.dump_usb_out_queue()
2730         );
2731         assert_eq!(gbl_ops.last_set_active_slot, Some(1));
2732     }
2733 
2734     #[test]
test_run_gbl_fastboot_set_active_multichar_slot()2735     fn test_run_gbl_fastboot_set_active_multichar_slot() {
2736         let storage = FakeGblOpsStorage::default();
2737         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2738         let mut gbl_ops = FakeGblOps::new(&storage);
2739         let listener: SharedTestListener = Default::default();
2740         let (usb, tcp) = (&listener, &listener);
2741         listener.add_usb_input(b"set_active:ab");
2742         listener.add_usb_input(b"continue");
2743         block_on(run_gbl_fastboot_stack::<3>(
2744             &mut gbl_ops,
2745             buffers,
2746             Some(&mut TestLocalSession::default()),
2747             Some(usb),
2748             Some(tcp),
2749             &mut [],
2750         ));
2751 
2752         assert_eq!(
2753             listener.usb_out_queue(),
2754             make_expected_usb_out(&[
2755                 b"FAILSlot suffix must be one character",
2756                 b"INFOSyncing storage...",
2757                 b"OKAY",
2758             ]),
2759             "\nActual USB output:\n{}",
2760             listener.dump_usb_out_queue()
2761         );
2762     }
2763 
2764     #[test]
test_run_gbl_fastboot_fuchsia_reboot_bootloader_abr()2765     fn test_run_gbl_fastboot_fuchsia_reboot_bootloader_abr() {
2766         let mut storage = FakeGblOpsStorage::default();
2767         storage.add_raw_device(c"durable_boot", [0x00u8; KiB!(4)]);
2768         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2769         let mut gbl_ops = FakeGblOps::new(&storage);
2770         gbl_ops.os = Some(Os::Fuchsia);
2771         let listener: SharedTestListener = Default::default();
2772         let (usb, tcp) = (&listener, &listener);
2773 
2774         listener.add_usb_input(b"reboot-bootloader");
2775         listener.add_usb_input(b"continue");
2776         block_on(run_gbl_fastboot_stack::<3>(
2777             &mut gbl_ops,
2778             buffers,
2779             Some(&mut TestLocalSession::default()),
2780             Some(usb),
2781             Some(tcp),
2782             &mut [],
2783         ));
2784 
2785         assert_eq!(
2786             listener.usb_out_queue(),
2787             make_expected_usb_out(&[
2788                 b"INFOSyncing storage...",
2789                 b"INFORebooting to bootloader...",
2790                 b"OKAY",
2791                 b"FAILUnknown",
2792                 b"INFOSyncing storage...",
2793                 b"OKAY",
2794             ]),
2795             "\nActual USB output:\n{}",
2796             listener.dump_usb_out_queue()
2797         );
2798 
2799         assert_eq!(get_and_clear_one_shot_bootloader(&mut GblAbrOps(&mut gbl_ops)), Ok(true));
2800     }
2801 
2802     #[test]
test_run_gbl_fastboot_fuchsia_reboot_recovery_abr()2803     fn test_run_gbl_fastboot_fuchsia_reboot_recovery_abr() {
2804         let mut storage = FakeGblOpsStorage::default();
2805         storage.add_raw_device(c"durable_boot", [0x00u8; KiB!(4)]);
2806         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2807         let mut gbl_ops = FakeGblOps::new(&storage);
2808         gbl_ops.os = Some(Os::Fuchsia);
2809         let listener: SharedTestListener = Default::default();
2810         let (usb, tcp) = (&listener, &listener);
2811 
2812         listener.add_usb_input(b"reboot-recovery");
2813         listener.add_usb_input(b"continue");
2814         block_on(run_gbl_fastboot_stack::<3>(
2815             &mut gbl_ops,
2816             buffers,
2817             Some(&mut TestLocalSession::default()),
2818             Some(usb),
2819             Some(tcp),
2820             &mut [],
2821         ));
2822 
2823         assert_eq!(
2824             listener.usb_out_queue(),
2825             make_expected_usb_out(&[
2826                 b"INFOSyncing storage...",
2827                 b"INFORebooting to recovery...",
2828                 b"OKAY",
2829                 b"FAILUnknown",
2830                 b"INFOSyncing storage...",
2831                 b"OKAY",
2832             ]),
2833             "\nActual USB output:\n{}",
2834             listener.dump_usb_out_queue()
2835         );
2836 
2837         // One shot recovery is set.
2838         assert_eq!(get_boot_slot(&mut GblAbrOps(&mut gbl_ops), true), (SlotIndex::R, false));
2839         assert_eq!(get_boot_slot(&mut GblAbrOps(&mut gbl_ops), true), (SlotIndex::A, false));
2840     }
2841 
2842     #[test]
test_legacy_fvm_partition_alias()2843     fn test_legacy_fvm_partition_alias() {
2844         let mut storage = FakeGblOpsStorage::default();
2845         storage.add_raw_device(c"fuchsia-fvm", [0x00u8; KiB!(4)]);
2846         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2847         let mut gbl_ops = FakeGblOps::new(&storage);
2848         gbl_ops.os = Some(Os::Fuchsia);
2849         let listener: SharedTestListener = Default::default();
2850         let (usb, tcp) = (&listener, &listener);
2851 
2852         listener.add_usb_input(format!("download:{:#x}", KiB!(4)).as_bytes());
2853         listener.add_usb_input(&[0xaau8; KiB!(4)]);
2854         listener.add_usb_input(b"flash:fvm");
2855         listener.add_usb_input(b"continue");
2856         block_on(run_gbl_fastboot_stack::<3>(
2857             &mut gbl_ops,
2858             buffers,
2859             Some(&mut TestLocalSession::default()),
2860             Some(usb),
2861             Some(tcp),
2862             &mut [],
2863         ));
2864 
2865         assert_eq!(
2866             listener.usb_out_queue(),
2867             make_expected_usb_out(&[
2868                 b"DATA00001000",
2869                 b"OKAY",
2870                 b"OKAY",
2871                 b"INFOSyncing storage...",
2872                 b"OKAY",
2873             ]),
2874             "\nActual USB output:\n{}",
2875             listener.dump_usb_out_queue()
2876         );
2877     }
2878 
2879     #[test]
test_async_flash_early_errors()2880     fn test_async_flash_early_errors() {
2881         let sparse_raw = include_bytes!("../../testdata/sparse_test_raw.bin");
2882         let sparse = include_bytes!("../../testdata/sparse_test.bin");
2883         let mut storage = FakeGblOpsStorage::default();
2884         storage.add_raw_device(c"raw", vec![0u8; sparse_raw.len() - 1]);
2885         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2886         let mut gbl_ops = FakeGblOps::new(&storage);
2887         let listener: SharedTestListener = Default::default();
2888         let (usb, tcp) = (&listener, &listener);
2889         listener.add_usb_input(b"oem gbl-enable-async-task");
2890         // Flashes an oversized image.
2891         listener.add_usb_input(format!("download:{:#x}", sparse_raw.len()).as_bytes());
2892         listener.add_usb_input(&vec![0xaau8; sparse_raw.len()]);
2893         listener.add_usb_input(b"flash:raw");
2894         // Flashes an oversized sparse image.
2895         listener.add_usb_input(format!("download:{:#x}", sparse.len()).as_bytes());
2896         listener.add_usb_input(sparse);
2897         listener.add_usb_input(b"flash:raw");
2898         listener.add_usb_input(b"continue");
2899         block_on(run_gbl_fastboot_stack::<3>(
2900             &mut gbl_ops,
2901             buffers,
2902             Some(&mut TestLocalSession::default()),
2903             Some(usb),
2904             Some(tcp),
2905             &mut [],
2906         ));
2907 
2908         // The out-of-range errors should be caught before async task is launched.
2909         assert_eq!(
2910             listener.usb_out_queue(),
2911             make_expected_usb_out(&[
2912                 b"OKAY",
2913                 b"DATA0000e000",
2914                 b"OKAY",
2915                 b"FAILOutOfRange",
2916                 b"DATA00006080",
2917                 b"OKAY",
2918                 b"FAILOutOfRange",
2919                 b"INFOSyncing storage...",
2920                 b"OKAY",
2921             ]),
2922             "\nActual USB output:\n{}",
2923             listener.dump_usb_out_queue()
2924         );
2925     }
2926 
test_fastboot_boot_slot( suffix: char, load_buffer: &mut [u8], ) -> (&mut [u8], &mut [u8], &mut [u8], &mut [u8])2927     fn test_fastboot_boot_slot(
2928         suffix: char,
2929         load_buffer: &mut [u8],
2930     ) -> (&mut [u8], &mut [u8], &mut [u8], &mut [u8]) {
2931         let mut storage = FakeGblOpsStorage::default();
2932         let vbmeta = CString::new(format!("vbmeta_{suffix}")).unwrap();
2933         let vbmeta_img = read_test_data(format!("vbmeta_v2_{suffix}.img"));
2934         storage.add_raw_device(&vbmeta, vbmeta_img);
2935         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2936         let mut gbl_ops = default_test_gbl_ops(&storage);
2937         gbl_ops.current_slot = Some(Ok(slot(suffix)));
2938         let listener: SharedTestListener = Default::default();
2939         let (usb, tcp) = (&listener, &listener);
2940 
2941         let data = read_test_data(format!("boot_v2_{suffix}.img"));
2942         listener.add_usb_input(format!("download:{:#x}", data.len()).as_bytes());
2943         listener.add_usb_input(&data);
2944         listener.add_usb_input(b"boot");
2945         listener.add_usb_input(b"continue");
2946 
2947         let res = block_on(run_gbl_fastboot_stack::<2>(
2948             &mut gbl_ops,
2949             buffers,
2950             Some(&mut TestLocalSession::default()),
2951             Some(usb),
2952             Some(tcp),
2953             &mut load_buffer[..],
2954         ));
2955 
2956         assert_eq!(
2957             listener.usb_out_queue(),
2958             make_expected_usb_out(&[b"DATA00004000", b"OKAY", b"OKAYboot_command",]),
2959             "\nActual USB output:\n{}",
2960             listener.dump_usb_out_queue()
2961         );
2962 
2963         res.split_loaded_android(&mut load_buffer[..]).unwrap()
2964     }
2965 
2966     #[test]
test_fastboot_boot_slot_a()2967     fn test_fastboot_boot_slot_a() {
2968         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
2969         let (ramdisk, _, kernel, _) = test_fastboot_boot_slot('a', &mut load_buffer);
2970         checks_loaded_v2_slot_a_normal_mode(ramdisk, kernel);
2971     }
2972 
2973     #[test]
test_fastboot_boot_slot_b()2974     fn test_fastboot_boot_slot_b() {
2975         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
2976         let (ramdisk, _, kernel, _) = test_fastboot_boot_slot('b', &mut load_buffer);
2977         checks_loaded_v2_slot_b_normal_mode(ramdisk, kernel);
2978     }
2979 
2980     #[test]
test_fastboot_no_channels()2981     fn test_fastboot_no_channels() {
2982         let storage = FakeGblOpsStorage::default();
2983         let buffers = vec![vec![0u8; KiB!(128)]; 2];
2984         let mut gbl_ops = default_test_gbl_ops(&storage);
2985 
2986         block_on(run_gbl_fastboot_stack::<2>(
2987             &mut gbl_ops,
2988             buffers,
2989             None::<&mut TestLocalSession>,
2990             None::<&SharedTestListener>,
2991             None::<&SharedTestListener>,
2992             &mut [],
2993         ));
2994     }
2995 }
2996