• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Android boot support.
16 
17 use crate::{
18     constants::{FDT_ALIGNMENT, KERNEL_ALIGNMENT},
19     device_tree::{DeviceTreeComponentSource, DeviceTreeComponentsRegistry},
20     fastboot::{
21         run_gbl_fastboot, run_gbl_fastboot_stack, BufferPool, GblFastbootResult, GblTcpStream,
22         GblUsbTransport, LoadedImageInfo, PinFutContainer, Shared,
23     },
24     gbl_print, gbl_println,
25     ops::RebootReason,
26     GblOps, Result,
27 };
28 use bootparams::commandline::CommandlineBuilder;
29 use core::{array::from_fn, ffi::CStr};
30 use dttable::DtTableImage;
31 use fastboot::local_session::LocalSession;
32 use fdt::{Fdt, FdtHeader};
33 use gbl_async::block_on;
34 use liberror::Error;
35 use libutils::{aligned_offset, aligned_subslice};
36 use misc::{AndroidBootMode, BootloaderMessage};
37 use safemath::SafeNum;
38 
39 mod vboot;
40 use vboot::{avb_verify_slot, PartitionsToVerify};
41 
42 pub(crate) mod load;
43 use load::split_chunks;
44 pub use load::{android_load_verify, LoadedImages};
45 
46 /// Device tree bootargs property to store kernel command line.
47 pub const BOOTARGS_PROP: &CStr = c"bootargs";
48 
49 /// A helper to convert a bytes slice containing a null-terminated string to `str`
cstr_bytes_to_str(data: &[u8]) -> core::result::Result<&str, Error>50 fn cstr_bytes_to_str(data: &[u8]) -> core::result::Result<&str, Error> {
51     Ok(CStr::from_bytes_until_nul(data)?.to_str()?)
52 }
53 
54 /// Loads Android images from the given slot on disk and fixes up bootconfig, commandline, and FDT.
55 ///
56 /// On success, returns a tuple of (ramdisk, fdt, kernel, unused buffer).
android_load_verify_fixup<'a, 'b, 'c>( ops: &mut impl GblOps<'b, 'c>, slot: u8, is_recovery: bool, load: &'a mut [u8], ) -> Result<(&'a mut [u8], &'a mut [u8], &'a mut [u8], &'a mut [u8])>57 pub fn android_load_verify_fixup<'a, 'b, 'c>(
58     ops: &mut impl GblOps<'b, 'c>,
59     slot: u8,
60     is_recovery: bool,
61     load: &'a mut [u8],
62 ) -> Result<(&'a mut [u8], &'a mut [u8], &'a mut [u8], &'a mut [u8])> {
63     let load_addr = load.as_ptr() as usize;
64     let images = android_load_verify(ops, slot, is_recovery, load)?;
65 
66     let mut components = DeviceTreeComponentsRegistry::new();
67     let fdt_load = &mut images.unused[..];
68     // TODO(b/353272981): Remove get_custom_device_tree
69     let (fdt_load, base, overlays) = match ops.get_custom_device_tree() {
70         Some(v) => (fdt_load, v, &[][..]),
71         _ => {
72             let mut remains = match images.dtbo.len() > 0 {
73                 // TODO(b/384964561, b/374336105): Investigate if we can avoid additional copy.
74                 true => {
75                     gbl_println!(ops, "Handling overlays from dtbo");
76                     components.append_from_dttable(
77                         DeviceTreeComponentSource::Dtbo,
78                         &DtTableImage::from_bytes(images.dtbo)?,
79                         fdt_load,
80                     )?
81                 }
82                 _ => fdt_load,
83             };
84 
85             if images.dtb.len() > 0 {
86                 gbl_println!(ops, "Handling device tree from boot/vendor_boot");
87                 remains = if FdtHeader::from_bytes_ref(images.dtb).is_ok() {
88                     gbl_println!(ops, "Device tree found in boot/vendor_boot");
89                     components.append(ops, DeviceTreeComponentSource::Boot, images.dtb, remains)?
90                 } else if let Ok(table) = DtTableImage::from_bytes(images.dtb) {
91                     gbl_println!(
92                         ops,
93                         "Dttable with {} entries found in boot/vendor_boot",
94                         table.entries_count()
95                     );
96                     components.append_from_dttable(
97                         DeviceTreeComponentSource::Boot,
98                         &table,
99                         remains,
100                     )?
101                 } else {
102                     return Err(Error::Other(Some(
103                         "Invalid or unrecognized device tree format in boot/vendor_boot",
104                     ))
105                     .into());
106                 }
107             }
108 
109             if images.dtb_part.len() > 0 {
110                 gbl_println!(ops, "Handling device trees from dtb");
111                 let dttable = DtTableImage::from_bytes(images.dtb_part)?;
112                 remains = components.append_from_dttable(
113                     DeviceTreeComponentSource::Dtb,
114                     &dttable,
115                     remains,
116                 )?;
117             }
118 
119             gbl_println!(ops, "Selecting device tree components");
120             ops.select_device_trees(&mut components)?;
121             let (base, overlays) = components.selected()?;
122             (remains, base, overlays)
123         }
124     };
125     let fdt_load = aligned_subslice(fdt_load, FDT_ALIGNMENT)?;
126     let mut fdt = Fdt::new_from_init(&mut fdt_load[..], base)?;
127 
128     // Adds ramdisk range to FDT
129     let ramdisk_addr: u64 = (images.ramdisk.as_ptr() as usize).try_into().map_err(Error::from)?;
130     let ramdisk_end: u64 = ramdisk_addr + u64::try_from(images.ramdisk.len()).unwrap();
131     fdt.set_property("chosen", c"linux,initrd-start", &ramdisk_addr.to_be_bytes())?;
132     fdt.set_property("chosen", c"linux,initrd-end", &ramdisk_end.to_be_bytes())?;
133     gbl_println!(ops, "linux,initrd-start: {:#x}", ramdisk_addr);
134     gbl_println!(ops, "linux,initrd-end: {:#x}", ramdisk_end);
135 
136     // Updates the FDT commandline.
137     let device_tree_commandline_length = match fdt.get_property("chosen", BOOTARGS_PROP) {
138         Ok(val) => CStr::from_bytes_until_nul(val).map_err(Error::from)?.to_bytes().len(),
139         Err(_) => 0,
140     };
141 
142     // Reserves 1024 bytes for separators and fixup.
143     let final_commandline_len = device_tree_commandline_length
144         + images.boot_cmdline.len()
145         + images.vendor_cmdline.len()
146         + 1024;
147     let final_commandline_buffer =
148         fdt.set_property_placeholder("chosen", BOOTARGS_PROP, final_commandline_len)?;
149     let mut commandline_builder =
150         CommandlineBuilder::new_from_prefix(&mut final_commandline_buffer[..])?;
151     commandline_builder.add(images.boot_cmdline)?;
152     commandline_builder.add(images.vendor_cmdline)?;
153 
154     // TODO(b/353272981): Handle buffer too small
155     commandline_builder.add_with(|current, out| {
156         // TODO(b/353272981): Verify provided command line and fail here.
157         Ok(ops.fixup_os_commandline(current, out)?.map(|fixup| fixup.len()).unwrap_or(0))
158     })?;
159     gbl_println!(ops, "final cmdline: \"{}\"", commandline_builder.as_str());
160 
161     gbl_println!(ops, "Applying {} overlays", overlays.len());
162     fdt.multioverlay_apply(overlays)?;
163     gbl_println!(ops, "Overlays applied");
164     // `DeviceTreeComponentsRegistry` internally uses ArrayVec which causes it to have a default
165     // life time equal to the scope it lives in. This is unnecessarily strict and prevents us from
166     // accessing `load` buffer.
167     drop(components);
168 
169     // Make sure we provide an actual device tree size, so FW can calculate amount of space
170     // available for fixup.
171     fdt.shrink_to_fit()?;
172     // TODO(b/353272981): Make a copy of current device tree and verify provided fixup.
173     // TODO(b/353272981): Handle buffer too small
174     ops.fixup_device_tree(fdt.as_mut())?;
175     fdt.shrink_to_fit()?;
176 
177     // Moves the kernel forward to reserve as much space as possible. This is in case there is not
178     // enough memory after `load`, i.e. the memory after it is not mapped or is reserved.
179     let ramdisk_off = usize::try_from(ramdisk_addr).unwrap() - load_addr;
180     let fdt_len = fdt.header_ref()?.actual_size();
181     let fdt_off = fdt_load.as_ptr() as usize - load_addr;
182     let kernel_off = images.kernel.as_ptr() as usize - load_addr;
183     let kernel_len = images.kernel.len();
184     let mut kernel_new = (SafeNum::from(fdt_off) + fdt_len).try_into().map_err(Error::from)?;
185     kernel_new += aligned_offset(&mut load[kernel_new..], KERNEL_ALIGNMENT)?;
186     load.copy_within(kernel_off..kernel_off + kernel_len, kernel_new);
187     let ([_, ramdisk, fdt, kernel], unused) =
188         split_chunks(load, &[ramdisk_off, fdt_off - ramdisk_off, kernel_new - fdt_off, kernel_len]);
189     let ramdisk = &mut ramdisk[..usize::try_from(ramdisk_end - ramdisk_addr).unwrap()];
190     Ok((ramdisk, fdt, kernel, unused))
191 }
192 
193 /// Gets the target slot to boot.
194 ///
195 /// * If GBL is slotless (`GblOps::get_current_slot()` returns `Error::Unsupported`), the API
196 ///   behaves the same as `GblOps::get_next_slot()`.
197 /// * If GBL is slotted, the API behaves the same as `GblOps::get_current_slot()` and
198 ///   `mark_boot_attempt` is ignored.
199 /// * Default to A slot if slotting backend is not implemented on the platform.
get_boot_slot<'a, 'b, 'c>( ops: &mut impl GblOps<'a, 'b>, mark_boot_attempt: bool, ) -> Result<char>200 pub(crate) fn get_boot_slot<'a, 'b, 'c>(
201     ops: &mut impl GblOps<'a, 'b>,
202     mark_boot_attempt: bool,
203 ) -> Result<char> {
204     let slot = match ops.get_current_slot() {
205         // Slotless bootloader
206         Err(Error::Unsupported) => {
207             gbl_println!(ops, "GBL is Slotless.");
208             ops.get_next_slot(mark_boot_attempt)
209         }
210         v => v,
211     };
212     match slot {
213         Ok(slot) => Ok(slot.suffix.0),
214         Err(Error::Unsupported) => {
215             // Default to slot A if slotting is not supported.
216             // Slotless partition name is currently not supported. Revisit if this causes problems.
217             gbl_println!(ops, "Slotting is not supported. Choose A slot by default");
218             Ok('a')
219         }
220         Err(e) => {
221             gbl_println!(ops, "Failed to get boot slot: {e}");
222             Err(e.into())
223         }
224     }
225 }
226 
227 /// Provides methods to run GBL fastboot.
228 pub struct GblFastbootEntry<'d, G> {
229     ops: &'d mut G,
230     load: &'d mut [u8],
231     result: &'d mut GblFastbootResult,
232 }
233 
234 impl<'a, 'd, 'e, G> GblFastbootEntry<'d, G>
235 where
236     G: GblOps<'a, 'e>,
237 {
238     /// Runs GBL fastboot with the given buffer pool, tasks container, and usb/tcp/local transport
239     /// channels.
240     ///
241     /// # Args
242     ///
243     /// * `buffer_pool`: An implementation of `BufferPool` wrapped in `Shared` for allocating
244     ///    download buffers.
245     /// * `tasks`: An implementation of `PinFutContainer` used as task container for GBL fastboot to
246     // /   schedule dynamically spawned async tasks.
247     /// * `local`: An implementation of `LocalSession` which exchanges fastboot packet from platform
248     ///   specific channels i.e. UX.
249     /// * `usb`: An implementation of `GblUsbTransport` that represents USB channel.
250     /// * `tcp`: An implementation of `GblTcpStream` that represents TCP channel.
run<'b: 'c, 'c>( self, buffer_pool: &'b Shared<impl BufferPool>, tasks: impl PinFutContainer<'c> + 'c, local: Option<impl LocalSession>, usb: Option<impl GblUsbTransport>, tcp: Option<impl GblTcpStream>, ) where 'a: 'c, 'd: 'c,251     pub async fn run<'b: 'c, 'c>(
252         self,
253         buffer_pool: &'b Shared<impl BufferPool>,
254         tasks: impl PinFutContainer<'c> + 'c,
255         local: Option<impl LocalSession>,
256         usb: Option<impl GblUsbTransport>,
257         tcp: Option<impl GblTcpStream>,
258     ) where
259         'a: 'c,
260         'd: 'c,
261     {
262         *self.result =
263             run_gbl_fastboot(self.ops, buffer_pool, tasks, local, usb, tcp, self.load).await;
264     }
265 
266     /// Runs fastboot with N pre-allocated async worker tasks.
267     ///
268     /// Comparing  to `Self::run()`, this API   simplifies the input by handling the implementation of
269     /// `BufferPool` and `PinFutContainer` internally . However it only supports up to N parallel
270     /// tasks where N is determined at build time. The download buffer will be split into N chunks
271     /// evenly.
272     ///
273     /// The choice of N depends on the level of parallelism the platform can support. For platform
274     /// with `n` storage devices that can independently perform non-blocking IO, it will required
275     /// `N = n + 1` in order to achieve parallel flashing to all storages plus a parallel download.
276     /// However, it is common for partitions that need to be flashed to be on the same block device
277     /// so flashing of them becomes sequential, in which case N can be smaller. Caller should take
278     /// into consideration usage pattern for determining N. If platform only has one physical disk
279     /// or does not expect disks to be parallelizable, a common choice is N=2 which allows
280     /// downloading and flashing to be performed in parallel.
run_n<const N: usize>( self, download: &mut [u8], local: Option<impl LocalSession>, usb: Option<impl GblUsbTransport>, tcp: Option<impl GblTcpStream>, )281     pub fn run_n<const N: usize>(
282         self,
283         download: &mut [u8],
284         local: Option<impl LocalSession>,
285         usb: Option<impl GblUsbTransport>,
286         tcp: Option<impl GblTcpStream>,
287     ) {
288         if N < 1 {
289             return self.run_n::<1>(download, local, usb, tcp);
290         }
291         // Splits into N download buffers.
292         let mut arr: [_; N] = from_fn(|_| Default::default());
293         for (i, v) in download.chunks_exact_mut(download.len() / N).enumerate() {
294             arr[i] = v;
295         }
296         let bufs = &mut arr[..];
297         *self.result =
298             block_on(run_gbl_fastboot_stack::<N>(self.ops, bufs, local, usb, tcp, self.load));
299     }
300 }
301 
302 /// Runs full Android bootloader bootflow before kernel handoff.
303 ///
304 /// The API performs slot selection, handles boot mode, fastboot and loads and verifies Android from
305 /// disk.
306 ///
307 /// # Args:
308 ///
309 /// * `ops`: An implementation of `GblOps`.
310 /// * `load`: Buffer for loading various Android images.
311 /// * `run_fastboot`: A closure for running GBL fastboot. The closure is passed a
312 ///   `GblFastbootEntry` type which provides methods for running GBL fastboot. The caller is
313 ///   responsible for preparing the required inputs and calling the method in the closure. See
314 ///   `GblFastbootEntry` for more details.
315 ///
316 /// On success, returns a tuple of slices corresponding to `(ramdisk, FDT, kernel, unused)`
android_main<'a, 'b, 'c, G: GblOps<'a, 'b>>( ops: &mut G, load: &'c mut [u8], run_fastboot: impl FnOnce(GblFastbootEntry<'_, G>), ) -> Result<(&'c mut [u8], &'c mut [u8], &'c mut [u8], &'c mut [u8])>317 pub fn android_main<'a, 'b, 'c, G: GblOps<'a, 'b>>(
318     ops: &mut G,
319     load: &'c mut [u8],
320     run_fastboot: impl FnOnce(GblFastbootEntry<'_, G>),
321 ) -> Result<(&'c mut [u8], &'c mut [u8], &'c mut [u8], &'c mut [u8])> {
322     let (bcb_buffer, _) = load
323         .split_at_mut_checked(BootloaderMessage::SIZE_BYTES)
324         .ok_or(Error::BufferTooSmall(Some(BootloaderMessage::SIZE_BYTES)))
325         .inspect_err(|e| gbl_println!(ops, "Buffer too small for reading misc. {e}"))?;
326     ops.read_from_partition_sync("misc", 0, bcb_buffer)
327         .inspect_err(|e| gbl_println!(ops, "Failed to read misc partition {e}"))?;
328     let bcb = BootloaderMessage::from_bytes_ref(bcb_buffer)
329         .inspect_err(|e| gbl_println!(ops, "Failed to parse bootloader messgae {e}"))?;
330     let boot_mode = bcb
331         .boot_mode()
332         .inspect_err(|e| gbl_println!(ops, "Failed to parse BCB boot mode {e}. Ignored"))
333         .unwrap_or(AndroidBootMode::Normal);
334     gbl_println!(ops, "Boot mode from BCB: {}", boot_mode);
335 
336     if matches!(boot_mode, AndroidBootMode::BootloaderBootOnce) {
337         let mut zeroed_command = [0u8; misc::COMMAND_FIELD_SIZE];
338         ops.write_to_partition_sync(
339             "misc",
340             misc::COMMAND_FIELD_OFFSET.try_into().unwrap(),
341             &mut zeroed_command,
342         )?;
343     }
344 
345     // Checks platform reboot reason.
346     let reboot_reason = ops
347         .get_reboot_reason()
348         .inspect_err(|e| {
349             gbl_println!(ops, "Failed to get reboot reason from platform: {e}. Ignored.")
350         })
351         .unwrap_or(RebootReason::Normal);
352     gbl_println!(ops, "Reboot reason from platform: {reboot_reason:?}");
353 
354     // Checks and enters fastboot.
355     let result = &mut Default::default();
356     if matches!(reboot_reason, RebootReason::Bootloader)
357         || matches!(boot_mode, AndroidBootMode::BootloaderBootOnce)
358         || ops
359             .should_stop_in_fastboot()
360             .inspect_err(|e| {
361                 gbl_println!(ops, "Warning: error while checking fastboot trigger ({:?})", e);
362                 gbl_println!(ops, "Ignoring error and continuing with normal boot");
363             })
364             .unwrap_or(false)
365     {
366         gbl_println!(ops, "Entering fastboot mode...");
367         run_fastboot(GblFastbootEntry { ops, load: &mut load[..], result });
368         gbl_println!(ops, "Leaving fastboot mode...");
369     }
370 
371     // Checks if "fastboot boot" has loaded an android image.
372     match &result.loaded_image_info {
373         Some(LoadedImageInfo::Android { .. }) => {
374             gbl_println!(ops, "Booting from \"fastboot boot\"");
375             return Ok(result.split_loaded_android(load).unwrap());
376         }
377         _ => {}
378     }
379 
380     // Checks whether fastboot has set a different active slot. Reboot if it does.
381     let slot_suffix = get_boot_slot(ops, true)?;
382     if result.last_set_active_slot.unwrap_or(slot_suffix) != slot_suffix {
383         gbl_println!(ops, "Active slot changed by \"fastboot set_active\". Reset..");
384         ops.reboot();
385         return Err(Error::UnexpectedReturn.into());
386     }
387 
388     // Currently we assume slot suffix only takes value within 'a' to 'z'. Revisit if this
389     // is not the case.
390     //
391     // It's a little awkward to convert suffix char to integer which will then be converted
392     // back to char by the API. Consider passing in the char bytes directly.
393     let slot_idx = (u64::from(slot_suffix) - u64::from('a')).try_into().unwrap();
394 
395     let is_recovery = matches!(reboot_reason, RebootReason::Recovery)
396         || matches!(boot_mode, AndroidBootMode::Recovery);
397     android_load_verify_fixup(ops, slot_idx, is_recovery, load)
398 }
399 
400 #[cfg(test)]
401 pub(crate) mod tests {
402     use super::*;
403     use crate::{
404         fastboot::test::{make_expected_usb_out, SharedTestListener, TestLocalSession},
405         gbl_avb::state::KeyValidationStatus,
406         ops::test::{slot, FakeGblOps, FakeGblOpsStorage},
407         tests::AlignedBuffer,
408     };
409     use load::tests::{
410         check_ramdisk, make_expected_bootconfig, read_test_data, read_test_data_as_str,
411         AvbResultBootconfigBuilder, MakeExpectedBootconfigInclude, TEST_DEFAULT_BUILD_ID,
412         TEST_PUBLIC_KEY_DIGEST, TEST_VENDOR_BOOTCONFIG,
413     };
414     use std::{collections::HashMap, ffi::CString};
415 
416     const TEST_ROLLBACK_INDEX_LOCATION: usize = 1;
417 
418     /// Helper for testing `android_load_verify_fixup` given a partition layout, target slot and
419     /// custom device tree.
test_android_load_verify_fixup( slot: u8, partitions: &[(CString, String)], expected_kernel: &[u8], expected_ramdisk: &[u8], expected_bootconfig: &[u8], expected_bootargs: &str, expected_fdt_property: &[(&str, &CStr, Option<&[u8]>)], )420     fn test_android_load_verify_fixup(
421         slot: u8,
422         partitions: &[(CString, String)],
423         expected_kernel: &[u8],
424         expected_ramdisk: &[u8],
425         expected_bootconfig: &[u8],
426         expected_bootargs: &str,
427         expected_fdt_property: &[(&str, &CStr, Option<&[u8]>)],
428     ) {
429         let mut storage = FakeGblOpsStorage::default();
430         for (part, file) in partitions {
431             storage.add_raw_device(part, read_test_data(file));
432         }
433         let mut ops = FakeGblOps::new(&storage);
434         ops.avb_ops.unlock_state = Ok(false);
435         ops.avb_ops.rollbacks = HashMap::from([(TEST_ROLLBACK_INDEX_LOCATION, Ok(0))]);
436         let mut out_color = None;
437         let mut handler = |color,
438                            _: Option<&CStr>,
439                            _: Option<&[u8]>,
440                            _: Option<&[u8]>,
441                            _: Option<&[u8]>,
442                            _: Option<&[u8]>,
443                            _: Option<&[u8]>,
444                            _: Option<&[u8]>| {
445             out_color = Some(color);
446             Ok(())
447         };
448         ops.avb_handle_verification_result = Some(&mut handler);
449         ops.avb_key_validation_status = Some(Ok(KeyValidationStatus::Valid));
450 
451         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
452         let (ramdisk, fdt, kernel, _) =
453             android_load_verify_fixup(&mut ops, slot, false, &mut load_buffer).unwrap();
454         assert_eq!(kernel, expected_kernel);
455         check_ramdisk(ramdisk, expected_ramdisk, expected_bootconfig);
456 
457         let fdt = Fdt::new(fdt).unwrap();
458         // "linux,initrd-start/end" are updated.
459         assert_eq!(
460             fdt.get_property("/chosen", c"linux,initrd-start").unwrap(),
461             (ramdisk.as_ptr() as usize).to_be_bytes(),
462         );
463         assert_eq!(
464             fdt.get_property("/chosen", c"linux,initrd-end").unwrap(),
465             (ramdisk.as_ptr() as usize + ramdisk.len()).to_be_bytes(),
466         );
467 
468         // Commandlines are updated.
469         assert_eq!(
470             CStr::from_bytes_until_nul(fdt.get_property("/chosen", c"bootargs").unwrap()).unwrap(),
471             CString::new(expected_bootargs).unwrap().as_c_str(),
472         );
473 
474         // Fixup is applied.
475         assert_eq!(fdt.get_property("/chosen", c"fixup").unwrap(), &[1]);
476 
477         // Other FDT properties are as expected.
478         for (path, property, res) in expected_fdt_property {
479             assert_eq!(
480                 fdt.get_property(&path, &property).ok(),
481                 res.clone(),
482                 "{path}:{property:?} value doesn't match"
483             );
484         }
485     }
486 
487     /// Helper for testing `android_load_verify_fixup` for v2 boot image or lower.
test_android_load_verify_fixup_v2_or_lower( ver: u8, slot: char, additional_parts: &[(&CStr, &str)], additional_expected_fdt_properties: &[(&str, &CStr, Option<&[u8]>)], )488     fn test_android_load_verify_fixup_v2_or_lower(
489         ver: u8,
490         slot: char,
491         additional_parts: &[(&CStr, &str)],
492         additional_expected_fdt_properties: &[(&str, &CStr, Option<&[u8]>)],
493     ) {
494         let dtb =
495             additional_parts.iter().any(|(name, _)| name.to_str().unwrap().starts_with("dtb_"));
496         let dtbo =
497             additional_parts.iter().any(|(name, _)| name.to_str().unwrap().starts_with("dtbo_"));
498         let vbmeta = format!("vbmeta_v{ver}_{slot}.img");
499         let mut parts: Vec<(CString, String)> = vec![
500             (CString::new(format!("boot_{slot}")).unwrap(), format!("boot_v{ver}_{slot}.img")),
501             (CString::new(format!("vbmeta_{slot}")).unwrap(), vbmeta.clone()),
502         ];
503         for (part, file) in additional_parts.iter().cloned() {
504             parts.push((part.into(), file.into()));
505         }
506 
507         test_android_load_verify_fixup(
508             (u64::from(slot) - ('a' as u64)).try_into().unwrap(),
509             &parts,
510             &read_test_data(format!("kernel_{slot}.img")),
511             &read_test_data(format!("generic_ramdisk_{slot}.img")),
512             &make_expected_bootconfig(&vbmeta, slot, "",
513                 MakeExpectedBootconfigInclude {dtb, dtbo, ..Default::default() }
514             ),
515             "existing_arg_1=existing_val_1 existing_arg_2=existing_val_2 cmd_key_1=cmd_val_1,cmd_key_2=cmd_val_2",
516             additional_expected_fdt_properties,
517         )
518     }
519 
520     #[test]
test_android_load_verify_fixup_v0_slot_a()521     fn test_android_load_verify_fixup_v0_slot_a() {
522         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"dtb_slot", Some(b"a\0"))];
523         // V0 image doesn't have built-in dtb. We need to provide from dtb partition.
524         let parts = &[(c"dtb_a", "dtb_a.img")];
525         test_android_load_verify_fixup_v2_or_lower(0, 'a', parts, fdt_prop);
526     }
527 
528     #[test]
test_android_load_verify_fixup_v0_dtbo_slot_a()529     fn test_android_load_verify_fixup_v0_dtbo_slot_a() {
530         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
531             ("/chosen", c"dtb_slot", Some(b"a\0")),
532             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
533         ];
534         let parts = &[(c"dtbo_a", "dtbo_a.img"), (c"dtb_a", "dtb_a.img")];
535         test_android_load_verify_fixup_v2_or_lower(0, 'a', parts, fdt_prop);
536     }
537 
538     #[test]
test_android_load_verify_fixup_v0_slot_b()539     fn test_android_load_verify_fixup_v0_slot_b() {
540         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"dtb_slot", Some(b"b\0"))];
541         let parts = &[(c"dtb_b", "dtb_b.img")];
542         test_android_load_verify_fixup_v2_or_lower(0, 'b', parts, fdt_prop);
543     }
544 
545     #[test]
test_android_load_verify_fixup_v0_dtbo_slot_b()546     fn test_android_load_verify_fixup_v0_dtbo_slot_b() {
547         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
548             ("/chosen", c"dtb_slot", Some(b"b\0")),
549             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
550         ];
551         let parts = &[(c"dtbo_b", "dtbo_b.img"), (c"dtb_b", "dtb_b.img")];
552         test_android_load_verify_fixup_v2_or_lower(0, 'b', parts, fdt_prop);
553     }
554 
555     #[test]
test_android_load_verify_fixup_v1_slot_a()556     fn test_android_load_verify_fixup_v1_slot_a() {
557         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"dtb_slot", Some(b"a\0"))];
558         // V1 image doesn't have built-in dtb. We need to provide from dtb partition.
559         let parts = &[(c"dtb_a", "dtb_a.img")];
560         test_android_load_verify_fixup_v2_or_lower(1, 'a', parts, fdt_prop);
561     }
562 
563     #[test]
test_android_load_verify_fixup_v1_dtbo_slot_a()564     fn test_android_load_verify_fixup_v1_dtbo_slot_a() {
565         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
566             ("/chosen", c"dtb_slot", Some(b"a\0")),
567             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
568         ];
569         let parts = &[(c"dtbo_a", "dtbo_a.img"), (c"dtb_a", "dtb_a.img")];
570         test_android_load_verify_fixup_v2_or_lower(1, 'a', parts, fdt_prop);
571     }
572 
573     #[test]
test_android_load_verify_fixup_v1_slot_b()574     fn test_android_load_verify_fixup_v1_slot_b() {
575         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"dtb_slot", Some(b"b\0"))];
576         let parts = &[(c"dtb_b", "dtb_b.img")];
577         test_android_load_verify_fixup_v2_or_lower(1, 'b', parts, fdt_prop);
578     }
579 
580     #[test]
test_android_load_verify_fixup_v1_dtbo_slot_b()581     fn test_android_load_verify_fixup_v1_dtbo_slot_b() {
582         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
583             ("/chosen", c"dtb_slot", Some(b"b\0")),
584             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
585         ];
586         let parts = &[(c"dtbo_b", "dtbo_b.img"), (c"dtb_b", "dtb_b.img")];
587         test_android_load_verify_fixup_v2_or_lower(1, 'b', parts, fdt_prop);
588     }
589 
590     #[test]
test_android_load_verify_fixup_v2_slot_a()591     fn test_android_load_verify_fixup_v2_slot_a() {
592         // V2 image has built-in dtb. We don't need to provide custom device tree.
593         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
594         test_android_load_verify_fixup_v2_or_lower(2, 'a', &[], fdt_prop);
595     }
596 
597     #[test]
test_android_load_verify_fixup_v2_dtbo_slot_a()598     fn test_android_load_verify_fixup_v2_dtbo_slot_a() {
599         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
600             ("/chosen", c"builtin", Some(&[1])),
601             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
602         ];
603         let parts = &[(c"dtbo_a".into(), "dtbo_a.img".into())];
604         test_android_load_verify_fixup_v2_or_lower(2, 'a', parts, fdt_prop);
605     }
606 
607     #[test]
test_android_load_verify_fixup_v2_slot_b()608     fn test_android_load_verify_fixup_v2_slot_b() {
609         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
610         test_android_load_verify_fixup_v2_or_lower(2, 'b', &[], fdt_prop);
611     }
612 
613     #[test]
test_android_load_verify_fixup_v2_dtbo_slot_b()614     fn test_android_load_verify_fixup_v2_dtbo_slot_b() {
615         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
616             ("/chosen", c"builtin", Some(&[1])),
617             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
618         ];
619         let parts = &[(c"dtbo_b".into(), "dtbo_b.img".into())];
620         test_android_load_verify_fixup_v2_or_lower(2, 'b', parts, fdt_prop);
621     }
622 
623     /// Common helper for testing `android_load_verify_fixup` for v3/v4 boot image.
test_android_load_verify_fixup_v3_or_v4( slot: char, partitions: &[(CString, String)], vbmeta_file: &str, expected_vendor_bootconfig: &str, additional_expected_fdt_properties: &[(&str, &CStr, Option<&[u8]>)], )624     fn test_android_load_verify_fixup_v3_or_v4(
625         slot: char,
626         partitions: &[(CString, String)],
627         vbmeta_file: &str,
628         expected_vendor_bootconfig: &str,
629         additional_expected_fdt_properties: &[(&str, &CStr, Option<&[u8]>)],
630     ) {
631         let dtbo = partitions
632             .iter()
633             .any(|(name, _)| name.clone().into_string().unwrap().starts_with("dtbo_"));
634         let expected_ramdisk = [
635             read_test_data(format!("vendor_ramdisk_{slot}.img")),
636             read_test_data(format!("generic_ramdisk_{slot}.img")),
637         ]
638         .concat();
639         test_android_load_verify_fixup(
640             (u64::from(slot) - ('a' as u64)).try_into().unwrap(),
641             &partitions,
642             &read_test_data(format!("kernel_{slot}.img")),
643             &expected_ramdisk,
644             &make_expected_bootconfig(&vbmeta_file, slot, expected_vendor_bootconfig,
645                 MakeExpectedBootconfigInclude { dtbo, dtb: false, ..Default::default() },
646                 ),
647             "existing_arg_1=existing_val_1 existing_arg_2=existing_val_2 cmd_key_1=cmd_val_1,cmd_key_2=cmd_val_2 cmd_vendor_key_1=cmd_vendor_val_1,cmd_vendor_key_2=cmd_vendor_val_2",
648             additional_expected_fdt_properties,
649         )
650     }
651 
652     /// Helper for testing `android_load_verify_fixup` for v3/v4 boot image without init_boot.
test_android_load_verify_fixup_v3_or_v4_no_init_boot( boot_ver: u32, vendor_ver: u32, slot: char, expected_vendor_bootconfig: &str, additional_parts: &[(CString, String)], additional_expected_fdt_properties: &[(&str, &CStr, Option<&[u8]>)], )653     fn test_android_load_verify_fixup_v3_or_v4_no_init_boot(
654         boot_ver: u32,
655         vendor_ver: u32,
656         slot: char,
657         expected_vendor_bootconfig: &str,
658         additional_parts: &[(CString, String)],
659         additional_expected_fdt_properties: &[(&str, &CStr, Option<&[u8]>)],
660     ) {
661         let vbmeta = format!("vbmeta_v{boot_ver}_v{vendor_ver}_{slot}.img");
662         let mut parts: Vec<(CString, String)> = vec![
663             (CString::new(format!("boot_{slot}")).unwrap(), format!("boot_v{boot_ver}_{slot}.img")),
664             (
665                 CString::new(format!("vendor_boot_{slot}")).unwrap(),
666                 format!("vendor_boot_v{vendor_ver}_{slot}.img"),
667             ),
668             (CString::new(format!("vbmeta_{slot}")).unwrap(), vbmeta.clone()),
669         ];
670         parts.extend_from_slice(additional_parts);
671         test_android_load_verify_fixup_v3_or_v4(
672             slot,
673             &parts,
674             &vbmeta,
675             expected_vendor_bootconfig,
676             additional_expected_fdt_properties,
677         );
678     }
679 
680     #[test]
test_android_load_verify_fixup_v3_v3_no_init_boot_slot_a()681     fn test_android_load_verify_fixup_v3_v3_no_init_boot_slot_a() {
682         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
683         test_android_load_verify_fixup_v3_or_v4_no_init_boot(3, 3, 'a', "", &[], fdt_prop);
684     }
685 
686     #[test]
test_android_load_verify_fixup_v3_v3_no_init_boot_dtbo_slot_a()687     fn test_android_load_verify_fixup_v3_v3_no_init_boot_dtbo_slot_a() {
688         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
689             ("/chosen", c"builtin", Some(&[1])),
690             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
691         ];
692         let parts = &[(c"dtbo_a".into(), "dtbo_a.img".into())];
693         test_android_load_verify_fixup_v3_or_v4_no_init_boot(3, 3, 'a', "", parts, fdt_prop);
694     }
695 
696     #[test]
test_android_load_verify_fixup_v3_v3_no_init_boot_slot_b()697     fn test_android_load_verify_fixup_v3_v3_no_init_boot_slot_b() {
698         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
699         test_android_load_verify_fixup_v3_or_v4_no_init_boot(3, 3, 'a', "", &[], fdt_prop);
700     }
701 
702     #[test]
test_android_load_verify_fixup_v3_v3_no_init_boot_dtbo_slot_b()703     fn test_android_load_verify_fixup_v3_v3_no_init_boot_dtbo_slot_b() {
704         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
705             ("/chosen", c"builtin", Some(&[1])),
706             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
707         ];
708         let parts = &[(c"dtbo_b".into(), "dtbo_b.img".into())];
709         test_android_load_verify_fixup_v3_or_v4_no_init_boot(3, 3, 'b', "", parts, fdt_prop);
710     }
711 
712     #[test]
test_android_load_verify_fixup_v4_v3_no_init_boot_slot_a()713     fn test_android_load_verify_fixup_v4_v3_no_init_boot_slot_a() {
714         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
715         test_android_load_verify_fixup_v3_or_v4_no_init_boot(4, 3, 'a', "", &[], fdt_prop);
716     }
717 
718     #[test]
test_android_load_verify_fixup_v4_v3_no_init_boot_dtbo_slot_a()719     fn test_android_load_verify_fixup_v4_v3_no_init_boot_dtbo_slot_a() {
720         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
721             ("/chosen", c"builtin", Some(&[1])),
722             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
723         ];
724         let parts = &[(c"dtbo_a".into(), "dtbo_a.img".into())];
725         test_android_load_verify_fixup_v3_or_v4_no_init_boot(4, 3, 'a', "", parts, fdt_prop);
726     }
727 
728     #[test]
test_android_load_verify_fixup_v4_v3_no_init_boot_slot_b()729     fn test_android_load_verify_fixup_v4_v3_no_init_boot_slot_b() {
730         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
731         test_android_load_verify_fixup_v3_or_v4_no_init_boot(4, 3, 'a', "", &[], fdt_prop);
732     }
733 
734     #[test]
test_android_load_verify_fixup_v4_v3_no_init_boot_dtbo_slot_b()735     fn test_android_load_verify_fixup_v4_v3_no_init_boot_dtbo_slot_b() {
736         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
737             ("/chosen", c"builtin", Some(&[1])),
738             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
739         ];
740         let parts = &[(c"dtbo_b".into(), "dtbo_b.img".into())];
741         test_android_load_verify_fixup_v3_or_v4_no_init_boot(4, 3, 'b', "", parts, fdt_prop);
742     }
743 
744     #[test]
test_android_load_verify_fixup_v3_v4_no_init_boot_slot_a()745     fn test_android_load_verify_fixup_v3_v4_no_init_boot_slot_a() {
746         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
747         let config = TEST_VENDOR_BOOTCONFIG;
748         test_android_load_verify_fixup_v3_or_v4_no_init_boot(3, 4, 'a', config, &[], fdt_prop);
749     }
750 
751     #[test]
test_android_load_verify_fixup_v3_v4_no_init_boot_dtbo_slot_a()752     fn test_android_load_verify_fixup_v3_v4_no_init_boot_dtbo_slot_a() {
753         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
754             ("/chosen", c"builtin", Some(&[1])),
755             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
756         ];
757         let parts = &[(c"dtbo_a".into(), "dtbo_a.img".into())];
758         let config = TEST_VENDOR_BOOTCONFIG;
759         test_android_load_verify_fixup_v3_or_v4_no_init_boot(3, 4, 'a', config, parts, fdt_prop);
760     }
761 
762     #[test]
test_android_load_verify_fixup_v3_v4_no_init_boot_slot_b()763     fn test_android_load_verify_fixup_v3_v4_no_init_boot_slot_b() {
764         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
765         let config = TEST_VENDOR_BOOTCONFIG;
766         test_android_load_verify_fixup_v3_or_v4_no_init_boot(3, 4, 'a', config, &[], fdt_prop);
767     }
768 
769     #[test]
test_android_load_verify_fixup_v3_v4_no_init_boot_dtbo_slot_b()770     fn test_android_load_verify_fixup_v3_v4_no_init_boot_dtbo_slot_b() {
771         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
772             ("/chosen", c"builtin", Some(&[1])),
773             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
774         ];
775         let parts = &[(c"dtbo_b".into(), "dtbo_b.img".into())];
776         let config = TEST_VENDOR_BOOTCONFIG;
777         test_android_load_verify_fixup_v3_or_v4_no_init_boot(3, 4, 'b', config, parts, fdt_prop);
778     }
779 
780     #[test]
test_android_load_verify_fixup_v4_v4_no_init_boot_slot_a()781     fn test_android_load_verify_fixup_v4_v4_no_init_boot_slot_a() {
782         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
783         let config = TEST_VENDOR_BOOTCONFIG;
784         test_android_load_verify_fixup_v3_or_v4_no_init_boot(4, 4, 'a', config, &[], fdt_prop);
785     }
786 
787     #[test]
test_android_load_verify_fixup_v4_v4_no_init_boot_dtbo_slot_a()788     fn test_android_load_verify_fixup_v4_v4_no_init_boot_dtbo_slot_a() {
789         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
790             ("/chosen", c"builtin", Some(&[1])),
791             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
792         ];
793         let parts = &[(c"dtbo_a".into(), "dtbo_a.img".into())];
794         let config = TEST_VENDOR_BOOTCONFIG;
795         test_android_load_verify_fixup_v3_or_v4_no_init_boot(4, 4, 'a', config, parts, fdt_prop);
796     }
797 
798     #[test]
test_android_load_verify_fixup_v4_v4_no_init_boot_slot_b()799     fn test_android_load_verify_fixup_v4_v4_no_init_boot_slot_b() {
800         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
801         let config = TEST_VENDOR_BOOTCONFIG;
802         test_android_load_verify_fixup_v3_or_v4_no_init_boot(4, 4, 'a', config, &[], fdt_prop);
803     }
804 
805     #[test]
test_android_load_verify_fixup_v4_v4_no_init_boot_dtbo_slot_b()806     fn test_android_load_verify_fixup_v4_v4_no_init_boot_dtbo_slot_b() {
807         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
808             ("/chosen", c"builtin", Some(&[1])),
809             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
810         ];
811         let parts = &[(c"dtbo_b".into(), "dtbo_b.img".into())];
812         let config = TEST_VENDOR_BOOTCONFIG;
813         test_android_load_verify_fixup_v3_or_v4_no_init_boot(4, 4, 'b', config, parts, fdt_prop);
814     }
815 
816     /// Helper for testing `android_load_verify_fixup` with dttable vendor_boot
test_android_load_verify_fixup_v4_vendor_boot_dttable( slot: char, expected_vendor_bootconfig: &str, additional_parts: &[(CString, String)], additional_expected_fdt_properties: &[(&str, &CStr, Option<&[u8]>)], )817     fn test_android_load_verify_fixup_v4_vendor_boot_dttable(
818         slot: char,
819         expected_vendor_bootconfig: &str,
820         additional_parts: &[(CString, String)],
821         additional_expected_fdt_properties: &[(&str, &CStr, Option<&[u8]>)],
822     ) {
823         let vbmeta = format!("vbmeta_v4_dttable_{slot}.img");
824         let mut parts: Vec<(CString, String)> = vec![
825             (CString::new(format!("boot_{slot}")).unwrap(), format!("boot_v4_{slot}.img")),
826             (
827                 CString::new(format!("vendor_boot_{slot}")).unwrap(),
828                 format!("vendor_boot_v4_dttable_{slot}.img"),
829             ),
830             (CString::new(format!("vbmeta_{slot}")).unwrap(), vbmeta.clone()),
831         ];
832         parts.extend_from_slice(additional_parts);
833         test_android_load_verify_fixup_v3_or_v4(
834             slot,
835             &parts,
836             &vbmeta,
837             expected_vendor_bootconfig,
838             additional_expected_fdt_properties,
839         );
840     }
841 
842     #[test]
test_android_load_verify_fixup_v4_v4_no_init_boot_slot_dttable_vendor_boot_a()843     fn test_android_load_verify_fixup_v4_v4_no_init_boot_slot_dttable_vendor_boot_a() {
844         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
845         let config = TEST_VENDOR_BOOTCONFIG;
846         test_android_load_verify_fixup_v4_vendor_boot_dttable('a', config, &[], fdt_prop);
847     }
848 
849     #[test]
test_android_load_verify_fixup_v4_v4_no_init_boot_slot_dttable_vendor_boot_b()850     fn test_android_load_verify_fixup_v4_v4_no_init_boot_slot_dttable_vendor_boot_b() {
851         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
852         let config = TEST_VENDOR_BOOTCONFIG;
853         test_android_load_verify_fixup_v4_vendor_boot_dttable('b', config, &[], fdt_prop);
854     }
855 
856     /// Helper for testing `android_load_verify_fixup` for v3/v4 boot image with init_boot.
test_android_load_verify_fixup_v3_or_v4_init_boot( boot_ver: u32, vendor_ver: u32, slot: char, expected_vendor_bootconfig: &str, additional_parts: &[(CString, String)], additional_expected_fdt_properties: &[(&str, &CStr, Option<&[u8]>)], )857     fn test_android_load_verify_fixup_v3_or_v4_init_boot(
858         boot_ver: u32,
859         vendor_ver: u32,
860         slot: char,
861         expected_vendor_bootconfig: &str,
862         additional_parts: &[(CString, String)],
863         additional_expected_fdt_properties: &[(&str, &CStr, Option<&[u8]>)],
864     ) {
865         let vbmeta = format!("vbmeta_v{boot_ver}_v{vendor_ver}_init_boot_{slot}.img");
866         let mut parts: Vec<(CString, String)> = vec![
867             (
868                 CString::new(format!("boot_{slot}")).unwrap(),
869                 format!("boot_no_ramdisk_v{boot_ver}_{slot}.img"),
870             ),
871             (
872                 CString::new(format!("vendor_boot_{slot}")).unwrap(),
873                 format!("vendor_boot_v{vendor_ver}_{slot}.img"),
874             ),
875             (CString::new(format!("init_boot_{slot}")).unwrap(), format!("init_boot_{slot}.img")),
876             (CString::new(format!("vbmeta_{slot}")).unwrap(), vbmeta.clone()),
877         ];
878         parts.extend_from_slice(additional_parts);
879         test_android_load_verify_fixup_v3_or_v4(
880             slot,
881             &parts,
882             &vbmeta,
883             expected_vendor_bootconfig,
884             additional_expected_fdt_properties,
885         );
886     }
887 
888     #[test]
test_android_load_verify_fixup_v3_v3_init_boot_slot_a()889     fn test_android_load_verify_fixup_v3_v3_init_boot_slot_a() {
890         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
891         test_android_load_verify_fixup_v3_or_v4_init_boot(3, 3, 'a', "", &[], fdt_prop);
892     }
893 
894     #[test]
test_android_load_verify_fixup_v3_v3_init_boot_dtbo_slot_a()895     fn test_android_load_verify_fixup_v3_v3_init_boot_dtbo_slot_a() {
896         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
897             ("/chosen", c"builtin", Some(&[1])),
898             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
899         ];
900         let parts = &[(c"dtbo_a".into(), "dtbo_a.img".into())];
901         test_android_load_verify_fixup_v3_or_v4_init_boot(3, 3, 'a', "", parts, fdt_prop);
902     }
903 
904     #[test]
test_android_load_verify_fixup_v3_v3_init_boot_slot_b()905     fn test_android_load_verify_fixup_v3_v3_init_boot_slot_b() {
906         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
907         test_android_load_verify_fixup_v3_or_v4_init_boot(3, 3, 'a', "", &[], fdt_prop);
908     }
909 
910     #[test]
test_android_load_verify_fixup_v3_v3_init_boot_dtbo_slot_b()911     fn test_android_load_verify_fixup_v3_v3_init_boot_dtbo_slot_b() {
912         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
913             ("/chosen", c"builtin", Some(&[1])),
914             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
915         ];
916         let parts = &[(c"dtbo_b".into(), "dtbo_b.img".into())];
917         test_android_load_verify_fixup_v3_or_v4_init_boot(3, 3, 'b', "", parts, fdt_prop);
918     }
919 
920     #[test]
test_android_load_verify_fixup_v4_v3_init_boot_slot_a()921     fn test_android_load_verify_fixup_v4_v3_init_boot_slot_a() {
922         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
923         test_android_load_verify_fixup_v3_or_v4_init_boot(4, 3, 'a', "", &[], fdt_prop);
924     }
925 
926     #[test]
test_android_load_verify_fixup_v4_v3_init_boot_dtbo_slot_a()927     fn test_android_load_verify_fixup_v4_v3_init_boot_dtbo_slot_a() {
928         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
929             ("/chosen", c"builtin", Some(&[1])),
930             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
931         ];
932         let parts = &[(c"dtbo_a".into(), "dtbo_a.img".into())];
933         test_android_load_verify_fixup_v3_or_v4_init_boot(4, 3, 'a', "", parts, fdt_prop);
934     }
935 
936     #[test]
test_android_load_verify_fixup_v4_v3_init_boot_slot_b()937     fn test_android_load_verify_fixup_v4_v3_init_boot_slot_b() {
938         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
939         test_android_load_verify_fixup_v3_or_v4_init_boot(4, 3, 'a', "", &[], fdt_prop);
940     }
941 
942     #[test]
test_android_load_verify_fixup_v4_v3_init_boot_dtbo_slot_b()943     fn test_android_load_verify_fixup_v4_v3_init_boot_dtbo_slot_b() {
944         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
945             ("/chosen", c"builtin", Some(&[1])),
946             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
947         ];
948         let parts = &[(c"dtbo_b".into(), "dtbo_b.img".into())];
949         test_android_load_verify_fixup_v3_or_v4_init_boot(4, 3, 'b', "", parts, fdt_prop);
950     }
951 
952     #[test]
test_android_load_verify_fixup_v3_v4_init_boot_slot_a()953     fn test_android_load_verify_fixup_v3_v4_init_boot_slot_a() {
954         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
955         let config = TEST_VENDOR_BOOTCONFIG;
956         test_android_load_verify_fixup_v3_or_v4_init_boot(3, 4, 'a', config, &[], fdt_prop);
957     }
958 
959     #[test]
test_android_load_verify_fixup_v3_v4_init_boot_dtbo_slot_a()960     fn test_android_load_verify_fixup_v3_v4_init_boot_dtbo_slot_a() {
961         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
962             ("/chosen", c"builtin", Some(&[1])),
963             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
964         ];
965         let parts = &[(c"dtbo_a".into(), "dtbo_a.img".into())];
966         let config = TEST_VENDOR_BOOTCONFIG;
967         test_android_load_verify_fixup_v3_or_v4_init_boot(3, 4, 'a', config, parts, fdt_prop);
968     }
969 
970     #[test]
test_android_load_verify_fixup_v3_v4_init_boot_slot_b()971     fn test_android_load_verify_fixup_v3_v4_init_boot_slot_b() {
972         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
973         let config = TEST_VENDOR_BOOTCONFIG;
974         test_android_load_verify_fixup_v3_or_v4_init_boot(3, 4, 'a', config, &[], fdt_prop);
975     }
976 
977     #[test]
test_android_load_verify_fixup_v3_v4_init_boot_dtbo_slot_b()978     fn test_android_load_verify_fixup_v3_v4_init_boot_dtbo_slot_b() {
979         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
980             ("/chosen", c"builtin", Some(&[1])),
981             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
982         ];
983         let parts = &[(c"dtbo_b".into(), "dtbo_b.img".into())];
984         let config = TEST_VENDOR_BOOTCONFIG;
985         test_android_load_verify_fixup_v3_or_v4_init_boot(3, 4, 'b', config, parts, fdt_prop);
986     }
987 
988     #[test]
test_android_load_verify_fixup_v4_v4_init_boot_slot_a()989     fn test_android_load_verify_fixup_v4_v4_init_boot_slot_a() {
990         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
991         let config = TEST_VENDOR_BOOTCONFIG;
992         test_android_load_verify_fixup_v3_or_v4_init_boot(4, 4, 'a', config, &[], fdt_prop);
993     }
994 
995     #[test]
test_android_load_verify_fixup_v4_v4_init_boot_dtbo_slot_a()996     fn test_android_load_verify_fixup_v4_v4_init_boot_dtbo_slot_a() {
997         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
998             ("/chosen", c"builtin", Some(&[1])),
999             ("/chosen", c"overlay_a_property", Some(b"overlay_a_val\0")),
1000         ];
1001         let parts = &[(c"dtbo_a".into(), "dtbo_a.img".into())];
1002         let config = TEST_VENDOR_BOOTCONFIG;
1003         test_android_load_verify_fixup_v3_or_v4_init_boot(4, 4, 'a', config, parts, fdt_prop);
1004     }
1005 
1006     #[test]
test_android_load_verify_fixup_v4_v4_init_boot_slot_b()1007     fn test_android_load_verify_fixup_v4_v4_init_boot_slot_b() {
1008         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[("/chosen", c"builtin", Some(&[1]))];
1009         let config = TEST_VENDOR_BOOTCONFIG;
1010         test_android_load_verify_fixup_v3_or_v4_init_boot(4, 4, 'a', config, &[], fdt_prop);
1011     }
1012 
1013     #[test]
test_android_load_verify_fixup_v4_v4_init_boot_dtbo_slot_b()1014     fn test_android_load_verify_fixup_v4_v4_init_boot_dtbo_slot_b() {
1015         let fdt_prop: &[(&str, &CStr, Option<&[u8]>)] = &[
1016             ("/chosen", c"builtin", Some(&[1])),
1017             ("/chosen", c"overlay_b_property", Some(b"overlay_b_val\0")),
1018         ];
1019         let parts = &[(c"dtbo_b".into(), "dtbo_b.img".into())];
1020         let config = TEST_VENDOR_BOOTCONFIG;
1021         test_android_load_verify_fixup_v3_or_v4_init_boot(4, 4, 'b', config, parts, fdt_prop);
1022     }
1023 
1024     /// Helper for checking V2 image loaded from slot A and in normal mode.
checks_loaded_v2_slot_a_normal_mode(ramdisk: &[u8], kernel: &[u8])1025     pub(crate) fn checks_loaded_v2_slot_a_normal_mode(ramdisk: &[u8], kernel: &[u8]) {
1026         let expected_bootconfig = AvbResultBootconfigBuilder::new()
1027             .vbmeta_size(read_test_data("vbmeta_v2_a.img").len())
1028             .digest(read_test_data_as_str("vbmeta_v2_a.digest.txt").strip_suffix("\n").unwrap())
1029             .partition_digest(
1030                 "boot",
1031                 read_test_data_as_str("vbmeta_v2_a.boot.digest.txt").strip_suffix("\n").unwrap(),
1032             )
1033             .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
1034             .extra("androidboot.force_normal_boot=1\n")
1035             .extra(format!("androidboot.slot_suffix=_a\n"))
1036             .extra("androidboot.gbl.version=0\n")
1037             .extra(format!("androidboot.gbl.build_number={TEST_DEFAULT_BUILD_ID}\n"))
1038             .extra(FakeGblOps::GBL_TEST_BOOTCONFIG)
1039             .build();
1040         check_ramdisk(ramdisk, &read_test_data("generic_ramdisk_a.img"), &expected_bootconfig);
1041         assert_eq!(kernel, read_test_data("kernel_a.img"));
1042     }
1043 
1044     /// Helper for checking V2 image loaded from slot A and in recovery mode.
checks_loaded_v2_slot_a_recovery_mode(ramdisk: &[u8], kernel: &[u8])1045     fn checks_loaded_v2_slot_a_recovery_mode(ramdisk: &[u8], kernel: &[u8]) {
1046         let expected_bootconfig = AvbResultBootconfigBuilder::new()
1047             .vbmeta_size(read_test_data("vbmeta_v2_a.img").len())
1048             .digest(read_test_data_as_str("vbmeta_v2_a.digest.txt").strip_suffix("\n").unwrap())
1049             .partition_digest(
1050                 "boot",
1051                 read_test_data_as_str("vbmeta_v2_a.boot.digest.txt").strip_suffix("\n").unwrap(),
1052             )
1053             .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
1054             .extra(format!("androidboot.slot_suffix=_a\n"))
1055             .extra("androidboot.gbl.version=0\n")
1056             .extra(format!("androidboot.gbl.build_number={TEST_DEFAULT_BUILD_ID}\n"))
1057             .extra(FakeGblOps::GBL_TEST_BOOTCONFIG)
1058             .build();
1059         check_ramdisk(ramdisk, &read_test_data("generic_ramdisk_a.img"), &expected_bootconfig);
1060         assert_eq!(kernel, read_test_data("kernel_a.img"));
1061     }
1062 
1063     /// Helper for getting default FakeGblOps for tests.
default_test_gbl_ops(storage: &FakeGblOpsStorage) -> FakeGblOps1064     pub(crate) fn default_test_gbl_ops(storage: &FakeGblOpsStorage) -> FakeGblOps {
1065         let mut ops = FakeGblOps::new(&storage);
1066         ops.avb_ops.unlock_state = Ok(false);
1067         ops.avb_ops.rollbacks = HashMap::from([(TEST_ROLLBACK_INDEX_LOCATION, Ok(0))]);
1068         ops.avb_key_validation_status = Some(Ok(KeyValidationStatus::Valid));
1069         ops.current_slot = Some(Ok(slot('a')));
1070         ops.reboot_reason = Some(Ok(RebootReason::Normal));
1071         ops
1072     }
1073 
1074     #[test]
test_android_load_verify_fixup_recovery_mode()1075     fn test_android_load_verify_fixup_recovery_mode() {
1076         // Recovery mode is specified by the absence of bootconfig arg
1077         // "androidboot.force_normal_boot=1\n" and therefore independent of image versions. We can
1078         // pick any image version for test. Use v2 for simplicity.
1079         let mut storage = FakeGblOpsStorage::default();
1080         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1081         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1082 
1083         let mut ops = default_test_gbl_ops(&storage);
1084         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1085         let (ramdisk, _, kernel, _) =
1086             android_load_verify_fixup(&mut ops, 0, true, &mut load_buffer).unwrap();
1087         checks_loaded_v2_slot_a_recovery_mode(ramdisk, kernel)
1088     }
1089 
1090     #[test]
test_android_main_bcb_normal_mode()1091     fn test_android_main_bcb_normal_mode() {
1092         let mut storage = FakeGblOpsStorage::default();
1093         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1094         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1095         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1096 
1097         let mut ops = default_test_gbl_ops(&storage);
1098         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1099         let (ramdisk, _, kernel, _) = android_main(&mut ops, &mut load_buffer, |_| {}).unwrap();
1100         checks_loaded_v2_slot_a_normal_mode(ramdisk, kernel)
1101     }
1102 
1103     #[test]
test_android_main_bcb_recovery_mode()1104     fn test_android_main_bcb_recovery_mode() {
1105         let mut storage = FakeGblOpsStorage::default();
1106         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1107         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1108         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1109 
1110         let mut ops = default_test_gbl_ops(&storage);
1111         ops.write_to_partition_sync("misc", 0, &mut b"boot-recovery".to_vec()).unwrap();
1112         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1113         let (ramdisk, _, kernel, _) = android_main(&mut ops, &mut load_buffer, |_| {}).unwrap();
1114         checks_loaded_v2_slot_a_recovery_mode(ramdisk, kernel)
1115     }
1116 
1117     #[test]
test_android_main_reboot_reason_recovery_mode()1118     fn test_android_main_reboot_reason_recovery_mode() {
1119         let mut storage = FakeGblOpsStorage::default();
1120         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1121         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1122         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1123 
1124         let mut ops = default_test_gbl_ops(&storage);
1125         ops.reboot_reason = Some(Ok(RebootReason::Recovery));
1126         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1127         let (ramdisk, _, kernel, _) = android_main(&mut ops, &mut load_buffer, |_| {}).unwrap();
1128         checks_loaded_v2_slot_a_recovery_mode(ramdisk, kernel)
1129     }
1130 
1131     /// Helper for checking V2 image loaded from slot B and in normal mode.
checks_loaded_v2_slot_b_normal_mode(ramdisk: &[u8], kernel: &[u8])1132     pub(crate) fn checks_loaded_v2_slot_b_normal_mode(ramdisk: &[u8], kernel: &[u8]) {
1133         let expected_bootconfig = AvbResultBootconfigBuilder::new()
1134             .vbmeta_size(read_test_data("vbmeta_v2_b.img").len())
1135             .digest(read_test_data_as_str("vbmeta_v2_b.digest.txt").strip_suffix("\n").unwrap())
1136             .partition_digest(
1137                 "boot",
1138                 read_test_data_as_str("vbmeta_v2_b.boot.digest.txt").strip_suffix("\n").unwrap(),
1139             )
1140             .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
1141             .extra("androidboot.force_normal_boot=1\n")
1142             .extra(format!("androidboot.slot_suffix=_b\n"))
1143             .extra("androidboot.gbl.version=0\n")
1144             .extra(format!("androidboot.gbl.build_number={TEST_DEFAULT_BUILD_ID}\n"))
1145             .extra(FakeGblOps::GBL_TEST_BOOTCONFIG)
1146             .build();
1147         check_ramdisk(ramdisk, &read_test_data("generic_ramdisk_b.img"), &expected_bootconfig);
1148         assert_eq!(kernel, read_test_data("kernel_b.img"));
1149     }
1150 
1151     #[test]
test_android_main_slotted_gbl_slot_a()1152     fn test_android_main_slotted_gbl_slot_a() {
1153         let mut storage = FakeGblOpsStorage::default();
1154         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1155         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1156         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1157 
1158         let mut ops = default_test_gbl_ops(&storage);
1159         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1160         let (ramdisk, _, kernel, _) = android_main(&mut ops, &mut load_buffer, |_| {}).unwrap();
1161         assert_eq!(ops.mark_boot_attempt_called, 0);
1162         checks_loaded_v2_slot_a_normal_mode(ramdisk, kernel)
1163     }
1164 
1165     #[test]
test_android_main_slotless_gbl_slot_a()1166     fn test_android_main_slotless_gbl_slot_a() {
1167         let mut storage = FakeGblOpsStorage::default();
1168         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1169         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1170         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1171 
1172         let mut ops = default_test_gbl_ops(&storage);
1173         ops.current_slot = Some(Err(Error::Unsupported));
1174         ops.next_slot = Some(Ok(slot('a')));
1175         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1176         let (ramdisk, _, kernel, _) = android_main(&mut ops, &mut load_buffer, |_| {}).unwrap();
1177         assert_eq!(ops.mark_boot_attempt_called, 1);
1178         checks_loaded_v2_slot_a_normal_mode(ramdisk, kernel)
1179     }
1180 
1181     #[test]
test_android_main_slotted_gbl_slot_b()1182     fn test_android_main_slotted_gbl_slot_b() {
1183         let mut storage = FakeGblOpsStorage::default();
1184         storage.add_raw_device(c"boot_b", read_test_data("boot_v2_b.img"));
1185         storage.add_raw_device(c"vbmeta_b", read_test_data("vbmeta_v2_b.img"));
1186         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1187 
1188         let mut ops = default_test_gbl_ops(&storage);
1189         ops.current_slot = Some(Ok(slot('b')));
1190 
1191         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1192         let (ramdisk, _, kernel, _) = android_main(&mut ops, &mut load_buffer, |_| {}).unwrap();
1193         assert_eq!(ops.mark_boot_attempt_called, 0);
1194         checks_loaded_v2_slot_b_normal_mode(ramdisk, kernel)
1195     }
1196 
1197     #[test]
test_android_main_slotless_gbl_slot_b()1198     fn test_android_main_slotless_gbl_slot_b() {
1199         let mut storage = FakeGblOpsStorage::default();
1200         storage.add_raw_device(c"boot_b", read_test_data("boot_v2_b.img"));
1201         storage.add_raw_device(c"vbmeta_b", read_test_data("vbmeta_v2_b.img"));
1202         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1203 
1204         let mut ops = default_test_gbl_ops(&storage);
1205         ops.current_slot = Some(Err(Error::Unsupported));
1206         ops.next_slot = Some(Ok(slot('b')));
1207         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1208         let (ramdisk, _, kernel, _) = android_main(&mut ops, &mut load_buffer, |_| {}).unwrap();
1209         assert_eq!(ops.mark_boot_attempt_called, 1);
1210         checks_loaded_v2_slot_b_normal_mode(ramdisk, kernel);
1211     }
1212 
1213     #[test]
test_android_main_unsupported_slot_default_to_a()1214     fn test_android_main_unsupported_slot_default_to_a() {
1215         let mut storage = FakeGblOpsStorage::default();
1216         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1217         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1218         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1219 
1220         let mut ops = default_test_gbl_ops(&storage);
1221         ops.current_slot = Some(Err(Error::Unsupported));
1222         ops.next_slot = Some(Err(Error::Unsupported));
1223         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1224         let (ramdisk, _, kernel, _) = android_main(&mut ops, &mut load_buffer, |_| {}).unwrap();
1225         checks_loaded_v2_slot_a_normal_mode(ramdisk, kernel)
1226     }
1227 
1228     /// Helper for testing that fastboot mode is triggered.
test_fastboot_is_triggered<'a, 'b>(ops: &mut impl GblOps<'a, 'b>)1229     fn test_fastboot_is_triggered<'a, 'b>(ops: &mut impl GblOps<'a, 'b>) {
1230         let listener: SharedTestListener = Default::default();
1231         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1232         let (ramdisk, _, kernel, _) = android_main(ops, &mut load_buffer, |fb| {
1233             listener.add_usb_input(b"getvar:max-fetch-size");
1234             listener.add_usb_input(b"continue");
1235             fb.run_n::<2>(
1236                 &mut vec![0u8; 256 * 1024],
1237                 Some(&mut TestLocalSession::default()),
1238                 Some(&listener),
1239                 Some(&listener),
1240             )
1241         })
1242         .unwrap();
1243 
1244         assert_eq!(
1245             listener.usb_out_queue(),
1246             make_expected_usb_out(
1247                 &[b"OKAY0xffffffffffffffff", b"INFOSyncing storage...", b"OKAY",]
1248             ),
1249             "\nActual USB output:\n{}",
1250             listener.dump_usb_out_queue()
1251         );
1252 
1253         checks_loaded_v2_slot_a_normal_mode(ramdisk, kernel);
1254     }
1255 
1256     #[test]
test_android_main_bootonce_bootloader_bcb_command_is_cleared()1257     fn test_android_main_bootonce_bootloader_bcb_command_is_cleared() {
1258         let mut storage = FakeGblOpsStorage::default();
1259         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1260         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1261         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1262         let mut ops = default_test_gbl_ops(&storage);
1263         ops.write_to_partition_sync("misc", 0, &mut b"bootonce-bootloader".to_vec()).unwrap();
1264         test_fastboot_is_triggered(&mut ops);
1265 
1266         let mut bcb_buffer = [0u8; BootloaderMessage::SIZE_BYTES];
1267         ops.read_from_partition_sync("misc", 0, &mut bcb_buffer[..]).unwrap();
1268         let bcb = BootloaderMessage::from_bytes_ref(&bcb_buffer).unwrap();
1269         assert_eq!(
1270             bcb.boot_mode().unwrap(),
1271             AndroidBootMode::Normal,
1272             "BCB mode is expected to be cleared after bootonce-bootloader is handled"
1273         );
1274     }
1275 
1276     #[test]
test_android_main_enter_fastboot_via_bcb()1277     fn test_android_main_enter_fastboot_via_bcb() {
1278         let mut storage = FakeGblOpsStorage::default();
1279         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1280         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1281         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1282         let mut ops = default_test_gbl_ops(&storage);
1283         ops.write_to_partition_sync("misc", 0, &mut b"bootonce-bootloader".to_vec()).unwrap();
1284         test_fastboot_is_triggered(&mut ops);
1285     }
1286 
1287     #[test]
test_android_main_enter_fastboot_via_reboot_reason()1288     fn test_android_main_enter_fastboot_via_reboot_reason() {
1289         let mut storage = FakeGblOpsStorage::default();
1290         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1291         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1292         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1293         let mut ops = default_test_gbl_ops(&storage);
1294         ops.reboot_reason = Some(Ok(RebootReason::Bootloader));
1295         test_fastboot_is_triggered(&mut ops);
1296     }
1297 
1298     #[test]
test_android_main_enter_fastboot_via_should_stop_in_fastboot()1299     fn test_android_main_enter_fastboot_via_should_stop_in_fastboot() {
1300         let mut storage = FakeGblOpsStorage::default();
1301         storage.add_raw_device(c"boot_a", read_test_data("boot_v2_a.img"));
1302         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1303         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1304         let mut ops = default_test_gbl_ops(&storage);
1305         ops.stop_in_fastboot = Some(Ok(true));
1306         test_fastboot_is_triggered(&mut ops);
1307     }
1308 
1309     #[test]
test_android_main_fastboot_boot()1310     fn test_android_main_fastboot_boot() {
1311         let mut storage = FakeGblOpsStorage::default();
1312         storage.add_raw_device(c"vbmeta_a", read_test_data("vbmeta_v2_a.img"));
1313         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1314         let mut ops = default_test_gbl_ops(&storage);
1315         ops.stop_in_fastboot = Some(Ok(true));
1316         ops.current_slot = Some(Ok(slot('a')));
1317 
1318         let listener: SharedTestListener = Default::default();
1319         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1320         let (ramdisk, _, kernel, _) = android_main(&mut ops, &mut load_buffer, |fb| {
1321             let data = read_test_data(format!("boot_v2_a.img"));
1322             listener.add_usb_input(format!("download:{:#x}", data.len()).as_bytes());
1323             listener.add_usb_input(&data);
1324             listener.add_usb_input(b"boot");
1325             listener.add_usb_input(b"continue");
1326             fb.run_n::<2>(
1327                 &mut vec![0u8; 256 * 1024],
1328                 Some(&mut TestLocalSession::default()),
1329                 Some(&listener),
1330                 Some(&listener),
1331             )
1332         })
1333         .unwrap();
1334 
1335         assert_eq!(
1336             listener.usb_out_queue(),
1337             make_expected_usb_out(&[b"DATA00004000", b"OKAY", b"OKAYboot_command",]),
1338             "\nActual USB output:\n{}",
1339             listener.dump_usb_out_queue()
1340         );
1341 
1342         checks_loaded_v2_slot_a_normal_mode(ramdisk, kernel);
1343     }
1344 
1345     #[test]
test_android_main_reboot_if_set_active_to_different_slot()1346     fn test_android_main_reboot_if_set_active_to_different_slot() {
1347         let mut storage = FakeGblOpsStorage::default();
1348         storage.add_raw_device(c"misc", vec![0u8; 4 * 1024 * 1024]);
1349         let mut ops = default_test_gbl_ops(&storage);
1350         ops.stop_in_fastboot = Some(Ok(true));
1351         ops.current_slot = Some(Ok(slot('a')));
1352 
1353         let listener: SharedTestListener = Default::default();
1354         let mut load_buffer = AlignedBuffer::new(8 * 1024 * 1024, KERNEL_ALIGNMENT);
1355         assert_eq!(
1356             android_main(&mut ops, &mut load_buffer, |fb| {
1357                 listener.add_usb_input(b"set_active:b");
1358                 listener.add_usb_input(b"continue");
1359                 fb.run_n::<2>(
1360                     &mut vec![0u8; 256 * 1024],
1361                     Some(&mut TestLocalSession::default()),
1362                     Some(&listener),
1363                     Some(&listener),
1364                 )
1365             })
1366             .unwrap_err(),
1367             Error::UnexpectedReturn.into()
1368         );
1369 
1370         assert_eq!(
1371             listener.usb_out_queue(),
1372             make_expected_usb_out(&[b"OKAY", b"INFOSyncing storage...", b"OKAY",]),
1373             "\nActual USB output:\n{}",
1374             listener.dump_usb_out_queue()
1375         );
1376     }
1377 }
1378