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