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