• 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 use super::{avb_verify_slot, cstr_bytes_to_str};
16 use crate::{
17     android_boot::PartitionsToVerify,
18     constants::{FDT_ALIGNMENT, KERNEL_ALIGNMENT, PAGE_SIZE},
19     decompress::decompress_kernel,
20     gbl_print, gbl_println,
21     ops::GblOps,
22     partition::RAW_PARTITION_NAME_LEN,
23     IntegrationError,
24 };
25 use arrayvec::ArrayString;
26 use bootimg::{defs::*, BootImage, VendorImageHeader};
27 use bootparams::bootconfig::BootConfigBuilder;
28 use core::{
29     array,
30     ffi::CStr,
31     fmt::Write,
32     ops::{Deref, Range},
33 };
34 use liberror::Error;
35 use libutils::aligned_subslice;
36 use safemath::SafeNum;
37 use zerocopy::{IntoBytes, Ref};
38 
39 const DEFAULT_BUILD_ID: &str = "eng.build";
40 
41 // Represents a slot suffix.
42 struct SlotSuffix([u8; 3]);
43 
44 impl SlotSuffix {
45     // Creates a new instance.
new(slot: u8) -> Result<Self, Error>46     fn new(slot: u8) -> Result<Self, Error> {
47         let suffix = u32::from(slot) + u32::from(b'a');
48         match char::from_u32(suffix).map(|v| v.is_ascii_lowercase()) {
49             Some(true) => Ok(Self([b'_', suffix.try_into().unwrap(), 0])),
50             _ => Err(Error::Other(Some("Invalid slot index"))),
51         }
52     }
53 
54     // Casts as CStr.
as_cstr(&self) -> &CStr55     fn as_cstr(&self) -> &CStr {
56         CStr::from_bytes_with_nul(&self.0[..]).unwrap()
57     }
58 }
59 
60 impl Deref for SlotSuffix {
61     type Target = str;
62 
deref(&self) -> &Self::Target63     fn deref(&self) -> &Self::Target {
64         self.as_cstr().to_str().unwrap()
65     }
66 }
67 
68 /// Returns a slotted partition name.
slotted_part(part: &str, slot: u8) -> Result<ArrayString<RAW_PARTITION_NAME_LEN>, Error>69 fn slotted_part(part: &str, slot: u8) -> Result<ArrayString<RAW_PARTITION_NAME_LEN>, Error> {
70     let mut res = ArrayString::new_const();
71     write!(res, "{}{}", part, &SlotSuffix::new(slot)? as &str).unwrap();
72     Ok(res)
73 }
74 
75 // Helper for constructing a range that ends at a page aligned boundary. Specifically, it returns
76 // `start..round_up(start + sz, page_size)`
page_aligned_range( start: impl Into<SafeNum>, sz: impl Into<SafeNum>, page_size: impl Into<SafeNum>, ) -> Result<Range<usize>, Error>77 fn page_aligned_range(
78     start: impl Into<SafeNum>,
79     sz: impl Into<SafeNum>,
80     page_size: impl Into<SafeNum>,
81 ) -> Result<Range<usize>, Error> {
82     let start = start.into();
83     Ok(start.try_into()?..(start + sz.into()).round_up(page_size.into()).try_into()?)
84 }
85 
86 /// Represents a loaded boot image of version 2 and lower.
87 ///
88 /// TODO(b/384964561): Investigate if the APIs are better suited for bootimg.rs. The issue
89 /// is that it uses `Error` and `SafeNum` from GBL.
90 struct BootImageV2Info<'a> {
91     cmdline: &'a str,
92     page_size: usize,
93     kernel_range: Range<usize>,
94     ramdisk_range: Range<usize>,
95     dtb_range: Range<usize>,
96     // Actual dtb size without padding.
97     //
98     // We need to know the exact size because the fdt buffer will be passed to
99     // `DeviceTreeComponentsRegistry::append` which assumes that the buffer contains concatenated
100     // device trees and will try to parse for additional device trees if the preivous one doesn't
101     // consume all buffer.
102     dtb_sz: usize,
103     image_size: usize,
104 }
105 
106 impl<'a> BootImageV2Info<'a> {
107     /// Creates a new instance.
new(buffer: &'a [u8]) -> Result<Self, Error>108     fn new(buffer: &'a [u8]) -> Result<Self, Error> {
109         let header = BootImage::parse(buffer)?;
110         if matches!(header, BootImage::V3(_) | BootImage::V4(_)) {
111             return Err(Error::InvalidInput);
112         }
113         // This is valid since v1/v2 are superset of v0.
114         let v0 = Ref::into_ref(Ref::<_, boot_img_hdr_v0>::from_prefix(&buffer[..]).unwrap().0);
115         let page_size: usize = v0.page_size.try_into()?;
116         let cmdline = cstr_bytes_to_str(&v0.cmdline[..])?;
117         let kernel_range = page_aligned_range(page_size, v0.kernel_size, page_size)?;
118         let ramdisk_range = page_aligned_range(kernel_range.end, v0.ramdisk_size, page_size)?;
119         let second_range = page_aligned_range(ramdisk_range.end, v0.second_size, page_size)?;
120 
121         let start = u64::try_from(second_range.end)?;
122         let (off, sz) = match header {
123             BootImage::V1(v) => (v.recovery_dtbo_offset, v.recovery_dtbo_size),
124             BootImage::V2(v) => (v._base.recovery_dtbo_offset, v._base.recovery_dtbo_size),
125             _ => (start, 0),
126         };
127         let recovery_dtb_range = match off >= start {
128             true => page_aligned_range(off, sz, page_size)?,
129             _ if off == 0 => page_aligned_range(start, 0, page_size)?,
130             _ => return Err(Error::Other(Some("Unexpected recovery_dtbo_offset"))),
131         };
132         let dtb_sz: usize = match header {
133             BootImage::V2(v) => v.dtb_size.try_into().unwrap(),
134             _ => 0,
135         };
136         let dtb_range = page_aligned_range(recovery_dtb_range.end, dtb_sz, page_size)?;
137         let image_size = dtb_range.end;
138         Ok(Self { cmdline, page_size, kernel_range, ramdisk_range, dtb_range, dtb_sz, image_size })
139     }
140 }
141 
142 // Contains information of a V3/V4 boot image.
143 struct BootImageV3Info {
144     kernel_range: Range<usize>,
145     ramdisk_range: Range<usize>,
146     image_size: usize,
147 }
148 
149 impl BootImageV3Info {
150     /// Creates a new instance.
new(buffer: &[u8]) -> Result<Self, Error>151     fn new(buffer: &[u8]) -> Result<Self, Error> {
152         let header = BootImage::parse(buffer)?;
153         if !matches!(header, BootImage::V3(_) | BootImage::V4(_)) {
154             return Err(Error::InvalidInput);
155         }
156         let v3 = Self::v3(buffer);
157         let kernel_range = page_aligned_range(PAGE_SIZE, v3.kernel_size, PAGE_SIZE)?;
158         let ramdisk_range = page_aligned_range(kernel_range.end, v3.ramdisk_size, PAGE_SIZE)?;
159         let sz = match header {
160             BootImage::V4(v) => v.signature_size,
161             _ => 0,
162         };
163         let signature_range = page_aligned_range(ramdisk_range.end, sz, PAGE_SIZE)?;
164         let image_size = signature_range.end;
165 
166         Ok(Self { kernel_range, ramdisk_range, image_size })
167     }
168 
169     /// Gets the v3 base header.
v3(buffer: &[u8]) -> &boot_img_hdr_v3170     fn v3(buffer: &[u8]) -> &boot_img_hdr_v3 {
171         // This is valid since v4 is superset of v3.
172         Ref::into_ref(Ref::from_prefix(&buffer[..]).unwrap().0)
173     }
174 
175     // Decodes the kernel cmdline
cmdline(buffer: &[u8]) -> Result<&str, Error>176     fn cmdline(buffer: &[u8]) -> Result<&str, Error> {
177         cstr_bytes_to_str(&Self::v3(buffer).cmdline[..])
178     }
179 }
180 
181 /// Contains vendor boot image information.
182 struct VendorBootImageInfo {
183     header_size: usize,
184     ramdisk_range: Range<usize>,
185     dtb_range: Range<usize>,
186     // Actual dtb size without padding.
187     //
188     // We need to know the exact size because the fdt buffer will be passed to
189     // `DeviceTreeComponentsRegistry::append` which assumes that the buffer contains concatenated
190     // device trees and will try to parse for additional device trees if the preivous one doesn't
191     // consume all buffer.
192     dtb_sz: usize,
193     bootconfig_range: Range<usize>,
194     image_size: usize,
195 }
196 
197 impl VendorBootImageInfo {
198     /// Creates a new instance.
new(buffer: &[u8]) -> Result<Self, Error>199     fn new(buffer: &[u8]) -> Result<Self, Error> {
200         let header = VendorImageHeader::parse(buffer)?;
201         let v3 = Self::v3(buffer);
202         let page_size = v3.page_size;
203         let header_size = match header {
204             VendorImageHeader::V3(hdr) => SafeNum::from(hdr.as_bytes().len()),
205             VendorImageHeader::V4(hdr) => SafeNum::from(hdr.as_bytes().len()),
206         }
207         .round_up(page_size)
208         .try_into()?;
209         let ramdisk_range = page_aligned_range(header_size, v3.vendor_ramdisk_size, page_size)?;
210         let dtb_sz: usize = v3.dtb_size.try_into().unwrap();
211         let dtb_range = page_aligned_range(ramdisk_range.end, dtb_sz, page_size)?;
212 
213         let (table_sz, bootconfig_sz) = match header {
214             VendorImageHeader::V4(hdr) => (hdr.vendor_ramdisk_table_size, hdr.bootconfig_size),
215             _ => (0, 0),
216         };
217         let table = page_aligned_range(dtb_range.end, table_sz, page_size)?;
218         let bootconfig_range = table.end..(table.end + usize::try_from(bootconfig_sz)?);
219         let image_size = SafeNum::from(bootconfig_range.end).round_up(page_size).try_into()?;
220         Ok(Self { header_size, ramdisk_range, dtb_range, dtb_sz, bootconfig_range, image_size })
221     }
222 
223     /// Gets the v3 base header.
v3(buffer: &[u8]) -> &vendor_boot_img_hdr_v3224     fn v3(buffer: &[u8]) -> &vendor_boot_img_hdr_v3 {
225         Ref::into_ref(Ref::<_, _>::from_prefix(&buffer[..]).unwrap().0)
226     }
227 
228     // Decodes the vendor cmdline
cmdline(buffer: &[u8]) -> Result<&str, Error>229     fn cmdline(buffer: &[u8]) -> Result<&str, Error> {
230         cstr_bytes_to_str(&Self::v3(buffer).cmdline[..])
231     }
232 }
233 
234 /// Contains various loaded image components by `android_load_verify`
235 pub struct LoadedImages<'a> {
236     /// dtbo image.
237     pub dtbo: &'a mut [u8],
238     /// Kernel commandline.
239     pub boot_cmdline: &'a str,
240     /// Vendor commandline,
241     pub vendor_cmdline: &'a str,
242     /// DTB.
243     pub dtb: &'a mut [u8],
244     /// DTB from partition.
245     pub dtb_part: &'a mut [u8],
246     /// Kernel image.
247     pub kernel: &'a mut [u8],
248     /// Ramdisk image.
249     pub ramdisk: &'a mut [u8],
250     /// Unused portion. Can be used by the caller to construct FDT.
251     pub unused: &'a mut [u8],
252 }
253 
254 impl<'a> Default for LoadedImages<'a> {
default() -> LoadedImages<'a>255     fn default() -> LoadedImages<'a> {
256         LoadedImages {
257             dtbo: &mut [][..],
258             boot_cmdline: "",
259             vendor_cmdline: "",
260             dtb: &mut [][..],
261             dtb_part: &mut [][..],
262             kernel: &mut [][..],
263             ramdisk: &mut [][..],
264             unused: &mut [][..],
265         }
266     }
267 }
268 
269 /// Loads and verifies Android images of the given slot.
android_load_verify<'a, 'b, 'c>( ops: &mut impl GblOps<'a, 'b>, slot: u8, is_recovery: bool, load: &'c mut [u8], ) -> Result<LoadedImages<'c>, IntegrationError>270 pub fn android_load_verify<'a, 'b, 'c>(
271     ops: &mut impl GblOps<'a, 'b>,
272     slot: u8,
273     is_recovery: bool,
274     load: &'c mut [u8],
275 ) -> Result<LoadedImages<'c>, IntegrationError> {
276     let mut res = LoadedImages::default();
277 
278     let slot_suffix = SlotSuffix::new(slot)?;
279     // Additional partitions loaded before loading standard boot images.
280     let mut partitions = PartitionsToVerify::default();
281 
282     // Loads dtbo.
283     let dtbo_part = slotted_part("dtbo", slot)?;
284     let (dtbo, remains) = load_entire_part(ops, &dtbo_part, &mut load[..])?;
285     if dtbo.len() > 0 {
286         partitions.try_push_preloaded(c"dtbo", &dtbo[..])?;
287     }
288 
289     // Loads dtb.
290     let remains = aligned_subslice(remains, FDT_ALIGNMENT)?;
291     let dtb_part = slotted_part("dtb", slot)?;
292     let (dtb, remains) = load_entire_part(ops, &dtb_part, &mut remains[..])?;
293     if dtb.len() > 0 {
294         partitions.try_push_preloaded(c"dtb", &dtb[..])?;
295     }
296 
297     let add = |v: &mut BootConfigBuilder| {
298         if !is_recovery {
299             v.add("androidboot.force_normal_boot=1\n")?;
300         }
301         write!(v, "androidboot.slot_suffix={}\n", &slot_suffix as &str)?;
302 
303         // Placeholder value for now. Userspace can use this value to tell if device is booted with GBL.
304         // TODO(yochiang): Generate useful value like version, build_incremental in the bootconfig.
305         v.add("androidboot.gbl.version=0\n")?;
306 
307         let build_number = match option_env!("BUILD_NUMBER") {
308             None | Some("") => DEFAULT_BUILD_ID,
309             Some(build_number) => build_number,
310         };
311         write!(v, "androidboot.gbl.build_number={}\n", build_number)?;
312         Ok(())
313     };
314 
315     // Loads boot image header and inspect version
316     ops.read_from_partition_sync(&slotted_part("boot", slot)?, 0, &mut remains[..PAGE_SIZE])?;
317     match BootImage::parse(&remains[..]).map_err(Error::from)? {
318         BootImage::V3(_) | BootImage::V4(_) => {
319             load_verify_v3_and_v4(ops, slot, &partitions, add, &mut res, remains)?
320         }
321         _ => load_verify_v2_and_lower(ops, slot, &partitions, add, &mut res, remains)?,
322     };
323 
324     drop(partitions);
325     res.dtbo = dtbo;
326     res.dtb_part = dtb;
327     Ok(res)
328 }
329 
330 /// Loads and verifies android boot images of version 0, 1 and 2.
331 ///
332 /// * Both kernel and ramdisk come from the boot image.
333 /// * vendor_boot, init_boot are irrelevant.
334 ///
335 /// # Args
336 ///
337 /// * `ops`: An implementation of [GblOps].
338 /// * `slot`: slot index.
339 /// * `additional_partitions`: Additional partitions for verification.
340 /// * `out`: A `&mut LoadedImages` for output.
341 /// * `load`: The load buffer. The boot header must be preloaded into this buffer.
load_verify_v2_and_lower<'a, 'b, 'c>( ops: &mut impl GblOps<'a, 'b>, slot: u8, additional_partitions: &PartitionsToVerify, add_additional_bootconfig: impl FnOnce(&mut BootConfigBuilder) -> Result<(), Error>, out: &mut LoadedImages<'c>, load: &'c mut [u8], ) -> Result<(), IntegrationError>342 fn load_verify_v2_and_lower<'a, 'b, 'c>(
343     ops: &mut impl GblOps<'a, 'b>,
344     slot: u8,
345     additional_partitions: &PartitionsToVerify,
346     add_additional_bootconfig: impl FnOnce(&mut BootConfigBuilder) -> Result<(), Error>,
347     out: &mut LoadedImages<'c>,
348     load: &'c mut [u8],
349 ) -> Result<(), IntegrationError> {
350     gbl_println!(ops, "Android loading v2 or lower");
351     // Loads boot image.
352     let boot_size = BootImageV2Info::new(load).unwrap().image_size;
353     let boot_part = slotted_part("boot", slot)?;
354     let (boot, remains) = split(load, boot_size)?;
355     ops.read_from_partition_sync(&boot_part, 0, boot)?;
356 
357     // Performs libavb verification.
358 
359     // Prepares a BootConfigBuilder to add avb generated bootconfig.
360     let mut bootconfig_builder = BootConfigBuilder::new(remains)?;
361     // Puts in a subscope for auto dropping `to_verify`, so that the slices it
362     // borrows can be released.
363     {
364         let mut to_verify = PartitionsToVerify::default();
365         to_verify.try_push_preloaded(c"boot", &boot[..])?;
366         to_verify.try_extend_preloaded(additional_partitions)?;
367         avb_verify_slot(ops, slot, &to_verify, &mut bootconfig_builder)?;
368     }
369 
370     add_additional_bootconfig(&mut bootconfig_builder)?;
371     // Adds platform-specific bootconfig.
372     bootconfig_builder.add_with(|bytes, out| {
373         Ok(ops.fixup_bootconfig(&bytes, out)?.map(|slice| slice.len()).unwrap_or(0))
374     })?;
375     let bootconfig_size = bootconfig_builder.config_bytes().len();
376 
377     // We now have the following layout:
378     //
379     // | boot_hdr | kernel | ramdisk | second | recovery_dtb | dtb | bootconfig | remains |
380     // |------------------------------`boot_ex`---------------------------------|
381     //
382     // We need to:
383     // 1. move bootconfig to after ramdisk.
384     // 2. relocate the kernel to the tail so that all memory after it can be used as scratch memory.
385     //    It is observed that riscv kernel reaches into those memory and overwrites data.
386     //
387     // TODO(b/384964561): Investigate if `second`, `recovery_dtb` needs to be kept.
388     let (boot_ex, remains) = load.split_at_mut(boot_size + bootconfig_size);
389     let boot_img = BootImageV2Info::new(boot_ex).unwrap();
390     let page_size = boot_img.page_size;
391     let dtb_sz = boot_img.dtb_sz;
392     // Relocates kernel to tail.
393     let kernel_range = boot_img.kernel_range;
394     let kernel = boot_ex.get(kernel_range.clone()).unwrap();
395     let (remains, _, kernel_sz) = relocate_kernel(ops, kernel, remains)?;
396     // Relocates dtb to tail.
397     let dtb_range = boot_img.dtb_range;
398     let (_, dtb) = split_aligned_tail(remains, dtb_range.len(), FDT_ALIGNMENT)?;
399     dtb[..dtb_range.len()].clone_from_slice(boot_ex.get(dtb_range).unwrap());
400     // Move ramdisk forward and bootconfig following it.
401     let ramdisk_range = boot_img.ramdisk_range;
402     boot_ex.copy_within(ramdisk_range.start..ramdisk_range.end, kernel_range.start);
403     boot_ex.copy_within(boot_size.., kernel_range.start + ramdisk_range.len());
404 
405     // We now have the following layout:
406     // | boot_hdr | ramdisk + bootconfig | unused | dtb | kernel |
407     let ramdisk_sz = ramdisk_range.len() + bootconfig_size;
408     let unused_sz = slice_offset(dtb, boot_ex) - page_size - ramdisk_sz;
409     let dtb_padding = dtb.len() - dtb_sz;
410     let hdr;
411     ([hdr, out.ramdisk, out.unused, out.dtb, _, out.kernel], _) =
412         split_chunks(load, &[page_size, ramdisk_sz, unused_sz, dtb_sz, dtb_padding, kernel_sz]);
413     out.boot_cmdline = BootImageV2Info::new(hdr).unwrap().cmdline;
414     Ok(())
415 }
416 
417 /// Loads and verifies android boot images of version 3 and 4.
418 ///
419 /// V3, V4 images have the following characteristics:
420 ///
421 /// * Kernel comes from "boot_a/b" partition.
422 /// * Generic ramdisk may come from either "boot_a/b" or "init_boot_a/b" partitions.
423 /// * Vendor ramdisk comes from "vendor_boot_a/b" partition.
424 /// * V4 vendor_boot contains additional bootconfig.
425 ///
426 /// From the perspective of Android versions:
427 ///
428 /// Android 11:
429 ///
430 /// * Can use v3 header.
431 /// * Generic ramdisk is in the "boot_a/b" partitions.
432 ///
433 /// Android 12:
434 ///
435 /// * Can use v3 or v4 header.
436 /// * Generic ramdisk is in the "boot_a/b" partitions.
437 ///
438 /// Android 13:
439 ///
440 /// * Can use v3 or v4 header.
441 /// * Generic ramdisk is in the "init_boot_a/b" partitions.
442 ///
443 /// # References
444 ///
445 /// https://source.android.com/docs/core/architecture/bootloader/boot-image-header
446 /// https://source.android.com/docs/core/architecture/partitions/vendor-boot-partitions
447 /// https://source.android.com/docs/core/architecture/partitions/generic-boot
448 ///
449 /// # Args
450 ///
451 /// * `ops`: An implementation of [GblOps].
452 /// * `slot`: slot index.
453 /// * `additional_partitions`: Additional partitions for verification.
454 /// * `out`: A `&mut LoadedImages` for output.
455 /// * `load`: The load buffer. The boot header must be preloaded into this buffer.
load_verify_v3_and_v4<'a, 'b, 'c>( ops: &mut impl GblOps<'a, 'b>, slot: u8, additional_partitions: &PartitionsToVerify, add_additional_bootconfig: impl FnOnce(&mut BootConfigBuilder) -> Result<(), Error>, out: &mut LoadedImages<'c>, load: &'c mut [u8], ) -> Result<(), IntegrationError>456 fn load_verify_v3_and_v4<'a, 'b, 'c>(
457     ops: &mut impl GblOps<'a, 'b>,
458     slot: u8,
459     additional_partitions: &PartitionsToVerify,
460     add_additional_bootconfig: impl FnOnce(&mut BootConfigBuilder) -> Result<(), Error>,
461     out: &mut LoadedImages<'c>,
462     load: &'c mut [u8],
463 ) -> Result<(), IntegrationError> {
464     gbl_println!(ops, "Android loading v3 or higher");
465     // Creates a `start` marker for `slice_offset()` to compute absolute slice offset later.
466     let (start, load) = load.split_at_mut(0);
467 
468     let boot_part = slotted_part("boot", slot)?;
469     let vendor_boot_part = slotted_part("vendor_boot", slot)?;
470     let init_boot_part = slotted_part("init_boot", slot)?;
471 
472     let boot_img_info = BootImageV3Info::new(load).unwrap();
473 
474     // Loads vendor boot image.
475     ops.read_from_partition_sync(&vendor_boot_part, 0, &mut load[..PAGE_SIZE])?;
476     let vendor_boot_info = VendorBootImageInfo::new(&load[..PAGE_SIZE])?;
477     let (vendor_boot, remains) = split(&mut load[..], vendor_boot_info.image_size)?;
478     ops.read_from_partition_sync(&vendor_boot_part, 0, vendor_boot)?;
479 
480     // Loads boot image.
481     let (boot, remains) = split(remains, boot_img_info.image_size)?;
482     ops.read_from_partition_sync(&boot_part, 0, boot)?;
483 
484     // Loads init_boot image if boot doesn't contain a ramdisk.
485     let (init_boot, remains, init_boot_info) = match boot_img_info.ramdisk_range.len() > 0 {
486         false => {
487             ops.read_from_partition_sync(&init_boot_part, 0, &mut remains[..PAGE_SIZE])?;
488             let init_boot_info = BootImageV3Info::new(&remains[..])?;
489             let (out, remains) = split(remains, init_boot_info.image_size)?;
490             ops.read_from_partition_sync(&init_boot_part, 0, out)?;
491             (out, remains, Some(init_boot_info))
492         }
493         _ => (&mut [][..], remains, None),
494     };
495 
496     // Performs libavb verification.
497 
498     // Prepares a BootConfigBuilder to add avb generated bootconfig.
499     let mut bootconfig_builder = BootConfigBuilder::new(remains)?;
500     // Puts in a subscope for auto dropping `to_verify`, so that the slices it
501     // borrows can be released.
502     {
503         let mut to_verify = PartitionsToVerify::default();
504         to_verify.try_push_preloaded(c"boot", &boot)?;
505         to_verify.try_push_preloaded(c"vendor_boot", &vendor_boot)?;
506         if init_boot.len() > 0 {
507             to_verify.try_push_preloaded(c"init_boot", &init_boot)?;
508         }
509         to_verify.try_extend_preloaded(additional_partitions)?;
510         avb_verify_slot(ops, slot, &to_verify, &mut bootconfig_builder)?;
511     }
512 
513     add_additional_bootconfig(&mut bootconfig_builder)?;
514     // Adds platform-specific bootconfig.
515     bootconfig_builder.add_with(|bytes, out| {
516         Ok(ops.fixup_bootconfig(&bytes, out)?.map(|slice| slice.len()).unwrap_or(0))
517     })?;
518 
519     // We now have the following layout:
520     //
521     // +------------------------+
522     // | vendor boot header     |
523     // +------------------------+
524     // | vendor ramdisk         |
525     // +------------------------+
526     // | dtb                    |
527     // +------------------------+
528     // | vendor ramdisk table   |
529     // +------------------------+
530     // | vendor bootconfig      |
531     // +------------------------+    +------------------------+
532     // | boot hdr               |    | boot hdr               |
533     // +------------------------+    +------------------------+
534     // | kernel                 |    | kernel                 |
535     // +------------------------+    +------------------------+
536     // |                        |    | boot signature         |
537     // |                        | or +------------------------+
538     // | generic ramdisk        |    | init_boot hdr          |
539     // |                        |    +------------------------+
540     // |                        |    | generic ramdisk        |
541     // +------------------------+    +------------------------+
542     // | boot signature         |    | boot signature         |
543     // +------------------------+    +------------------------+
544     // | avb + board bootconfig |
545     // +------------------------+
546     // | unused                 |
547     // +------------------------+
548     //
549     // We need to:
550     // * Relocate kernel to the tail of the load buffer to reserve all memory after it for scratch.
551     // * Relocates dtb, boot hdr to elsewhere.
552     // * Move generic ramdisk to follow vendor ramdisk.
553     // * Move vendor bootconfig, avb + board bootconfig to follow generic ramdisk.
554 
555     // Appends vendor bootconfig so that the section can be discarded.
556     let vendor_bootconfig = vendor_boot.get(vendor_boot_info.bootconfig_range).unwrap();
557     bootconfig_builder.add_with(|_, out| {
558         out.get_mut(..vendor_bootconfig.len())
559             .ok_or(Error::BufferTooSmall(Some(vendor_bootconfig.len())))?
560             .clone_from_slice(vendor_bootconfig);
561         Ok(vendor_bootconfig.len())
562     })?;
563     let bootconfig_size = bootconfig_builder.config_bytes().len();
564     let (bootconfig, remains) = remains.split_at_mut(bootconfig_size);
565 
566     // Relocates kernel to tail.
567     let kernel = boot.get(boot_img_info.kernel_range.clone()).unwrap();
568     let (remains, kernel, kernel_sz) = relocate_kernel(ops, kernel, remains)?;
569     let kernel_buf_len = kernel.len();
570 
571     // Relocates boot header to tail.
572     let (remains, boot_hdr) = split_aligned_tail(remains, PAGE_SIZE, 1)?;
573     boot_hdr.clone_from_slice(&boot[..PAGE_SIZE]);
574     let boot_hdr_sz = boot_hdr.len();
575 
576     // Relocates dtb to tail.
577     let dtb = vendor_boot.get(vendor_boot_info.dtb_range).unwrap();
578     let (_, dtb_reloc) = split_aligned_tail(remains, dtb.len(), FDT_ALIGNMENT)?;
579     dtb_reloc[..dtb.len()].clone_from_slice(dtb);
580     let dtb_sz = vendor_boot_info.dtb_sz;
581     let dtb_pad = dtb_reloc.len() - dtb_sz;
582 
583     // Moves generic ramdisk and bootconfig forward
584     let generic_ramdisk_range = match init_boot_info {
585         Some(v) => offset_range(v.ramdisk_range, slice_offset(init_boot, start)),
586         _ => offset_range(boot_img_info.ramdisk_range, slice_offset(boot, start)),
587     };
588     let vendor_ramdisk_range = vendor_boot_info.ramdisk_range;
589     let bootconfig_range = offset_range(0..bootconfig_size, slice_offset(bootconfig, start));
590     load.copy_within(generic_ramdisk_range.clone(), vendor_ramdisk_range.end);
591     load.copy_within(bootconfig_range, vendor_ramdisk_range.end + generic_ramdisk_range.len());
592     let ramdisk_sz = vendor_ramdisk_range.len() + generic_ramdisk_range.len() + bootconfig_size;
593 
594     // We now have the following layout:
595     //
596     // +------------------------+
597     // | vendor boot header     |
598     // +------------------------+
599     // | vendor ramdisk         |
600     // +------------------------+
601     // | generic ramdisk        |
602     // +------------------------+
603     // | vendor bootconfig      |
604     // +------------------------+
605     // | avb + board bootconfig |
606     // +------------------------+
607     // | unused                 |
608     // +------------------------+
609     // | dtb                    |
610     // +------------------------+
611     // | boot hdr               |
612     // +------------------------+
613     // | kernel                 |
614     // +------------------------+
615     //
616     // Splits out the images and returns.
617     let vendor_hdr_sz = vendor_boot_info.header_size;
618     let unused_sz =
619         load.len() - vendor_hdr_sz - ramdisk_sz - boot_hdr_sz - dtb_sz - dtb_pad - kernel_buf_len;
620     let (vendor_hdr, boot_hdr);
621     ([vendor_hdr, out.ramdisk, out.unused, out.dtb, _, boot_hdr, out.kernel], _) = split_chunks(
622         load,
623         &[vendor_hdr_sz, ramdisk_sz, unused_sz, dtb_sz, dtb_pad, boot_hdr_sz, kernel_sz],
624     );
625     out.boot_cmdline = BootImageV3Info::cmdline(boot_hdr)?;
626     out.vendor_cmdline = VendorBootImageInfo::cmdline(vendor_hdr)?;
627     Ok(())
628 }
629 
630 // A helper for calculating the relative offset of `buf` to `src`.
slice_offset(buf: &[u8], src: &[u8]) -> usize631 fn slice_offset(buf: &[u8], src: &[u8]) -> usize {
632     (buf.as_ptr() as usize).checked_sub(src.as_ptr() as usize).unwrap()
633 }
634 
635 /// Wrapper of `split_at_mut_checked` with error conversion.
split(buffer: &mut [u8], size: usize) -> Result<(&mut [u8], &mut [u8]), Error>636 fn split(buffer: &mut [u8], size: usize) -> Result<(&mut [u8], &mut [u8]), Error> {
637     buffer.split_at_mut_checked(size).ok_or(Error::BufferTooSmall(Some(size)))
638 }
639 
640 /// Calculates the offset from the start of the buffer to obtain an aligned tail
641 /// that can fit at least `size` bytes with the given alignment.
642 ///
643 /// Returns the starting offset of the aligned tail slice.
aligned_tail_offset(buffer: &[u8], size: usize, align: usize) -> Result<usize, Error>644 fn aligned_tail_offset(buffer: &[u8], size: usize, align: usize) -> Result<usize, Error> {
645     let off = SafeNum::from(buffer.len()) - size;
646     let rem = buffer[off.try_into()?..].as_ptr() as usize % align;
647     Ok(usize::try_from(off - rem)?)
648 }
649 
650 /// Split buffer from the tail with the given alignment such that the buffer is at least `size`
651 /// bytes.
split_aligned_tail( buffer: &mut [u8], size: usize, align: usize, ) -> Result<(&mut [u8], &mut [u8]), Error>652 fn split_aligned_tail(
653     buffer: &mut [u8],
654     size: usize,
655     align: usize,
656 ) -> Result<(&mut [u8], &mut [u8]), Error> {
657     split(buffer, aligned_tail_offset(buffer, size, align)?)
658 }
659 
660 /// Splits a buffer into multiple chunks of the given sizes.
661 ///
662 /// Returns an array of slices corresponding to the given sizes and the remaining slice.
split_chunks<'a, const N: usize>( buf: &'a mut [u8], sizes: &[usize; N], ) -> ([&'a mut [u8]; N], &'a mut [u8])663 pub(super) fn split_chunks<'a, const N: usize>(
664     buf: &'a mut [u8],
665     sizes: &[usize; N],
666 ) -> ([&'a mut [u8]; N], &'a mut [u8]) {
667     let mut chunks: [_; N] = array::from_fn(|_| &mut [][..]);
668     let mut remains = buf;
669     for (i, ele) in sizes.iter().enumerate() {
670         (chunks[i], remains) = remains.split_at_mut(*ele);
671     }
672     (chunks, remains)
673 }
674 
675 /// Helper for loading entire partition.
676 ///
677 /// * Returns the loaded slice and the remaining slice.
678 /// * If the partition doesn't exist, an empty loaded slice is returned.
load_entire_part<'a, 'b, 'c>( ops: &mut impl GblOps<'a, 'b>, part: &str, load: &'c mut [u8], ) -> Result<(&'c mut [u8], &'c mut [u8]), Error>679 fn load_entire_part<'a, 'b, 'c>(
680     ops: &mut impl GblOps<'a, 'b>,
681     part: &str,
682     load: &'c mut [u8],
683 ) -> Result<(&'c mut [u8], &'c mut [u8]), Error> {
684     match ops.partition_size(&part)? {
685         Some(sz) => {
686             let sz = sz.try_into()?;
687             gbl_println!(ops, "Found {} partition.", &part);
688             let (out, remains) = split(load, sz)?;
689             ops.read_from_partition_sync(&part, 0, out)?;
690             Ok((out, remains))
691         }
692         _ => {
693             gbl_println!(ops, "Partition {} doesn't exist. Skip loading.", &part);
694             Ok((&mut [][..], &mut load[..]))
695         }
696     }
697 }
698 
699 /// A helper function for relocating and decompressing kernel to a different buffer.
700 ///
701 /// The relocated kernel will be place at the tail.
702 ///
703 /// Returns the leading unused slice, the relocated slice and the actual kernel size without
704 /// alignment padding.
relocate_kernel<'a, 'b, 'c>( ops: &mut impl GblOps<'a, 'b>, kernel: &[u8], dst: &'c mut [u8], ) -> Result<(&'c mut [u8], &'c mut [u8], usize), Error>705 fn relocate_kernel<'a, 'b, 'c>(
706     ops: &mut impl GblOps<'a, 'b>,
707     kernel: &[u8],
708     dst: &'c mut [u8],
709 ) -> Result<(&'c mut [u8], &'c mut [u8], usize), Error> {
710     let decompressed_size = decompress_kernel(ops, kernel, dst)?;
711     let aligned_tail_off = aligned_tail_offset(dst, decompressed_size, KERNEL_ALIGNMENT)?;
712     dst.copy_within(0..decompressed_size, aligned_tail_off);
713     let (prefix, tail) = split(dst, aligned_tail_off)?;
714     Ok((prefix, tail, decompressed_size))
715 }
716 
717 // Adds offset to a given range i.e. [start+off, end+off)
offset_range(lhs: Range<usize>, off: usize) -> Range<usize>718 fn offset_range(lhs: Range<usize>, off: usize) -> Range<usize> {
719     lhs.start.checked_add(off).unwrap()..lhs.end.checked_add(off).unwrap()
720 }
721 
722 #[cfg(test)]
723 pub(crate) mod tests {
724     use super::*;
725     use crate::{
726         gbl_avb::state::{BootStateColor, KeyValidationStatus},
727         ops::test::{FakeGblOps, FakeGblOpsStorage},
728         tests::AlignedBuffer,
729     };
730     use bootparams::bootconfig::BOOTCONFIG_TRAILER_SIZE;
731     use std::{
732         ascii::escape_default, collections::HashMap, ffi::CString, fmt, fs, path::Path,
733         string::String,
734     };
735 
736     /// Export DEFAULT_BUILD_ID for other test modules.
737     pub const TEST_DEFAULT_BUILD_ID: &str = DEFAULT_BUILD_ID;
738 
739     // See libgbl/testdata/gen_test_data.py for test data generation.
740     const TEST_ROLLBACK_INDEX_LOCATION: usize = 1;
741 
742     // The commandline in the generated vendor boot image.
743     // See libgbl/testdata/gen_test_data.py for test data generation.
744     const TEST_VENDOR_CMDLINE: &str =
745         "cmd_vendor_key_1=cmd_vendor_val_1,cmd_vendor_key_2=cmd_vendor_val_2";
746     // The vendor bootconfig in the generated vendor boot image.
747     // See libgbl/testdata/gen_test_data.py for test data generation.
748     pub(crate) const TEST_VENDOR_BOOTCONFIG: &str =
749         "androidboot.config_1=val_1\x0aandroidboot.config_2=val_2\x0a";
750 
751     /// Digest of public key used to execute AVB.
752     pub(crate) const TEST_PUBLIC_KEY_DIGEST: &str =
753         "7ec02ee1be696366f3fa91240a8ec68125c4145d698f597aa2b3464b59ca7fc3";
754 
755     // Test data path
756     const TEST_DATA_PATH: &str = "external/gbl/libgbl/testdata/android";
757 
758     /// Reads a data file under libgbl/testdata/
read_test_data(file: impl AsRef<str>) -> Vec<u8>759     pub(crate) fn read_test_data(file: impl AsRef<str>) -> Vec<u8> {
760         println!("reading file: {}", file.as_ref());
761         fs::read(Path::new(format!("{TEST_DATA_PATH}/{}", file.as_ref()).as_str())).unwrap()
762     }
763 
764     /// Reads a data file as string under libgbl/testdata/
read_test_data_as_str(file: impl AsRef<str>) -> String765     pub(crate) fn read_test_data_as_str(file: impl AsRef<str>) -> String {
766         fs::read_to_string(Path::new(format!("{TEST_DATA_PATH}/{}", file.as_ref()).as_str()))
767             .unwrap()
768     }
769 
770     // Returns the test dtb
test_dtb() -> Vec<u8>771     fn test_dtb() -> Vec<u8> {
772         read_test_data("device_tree.dtb")
773     }
774 
775     /// Generates a readable string for a bootconfig bytes.
dump_bootconfig(data: &[u8]) -> String776     pub(crate) fn dump_bootconfig(data: &[u8]) -> String {
777         let s = data.iter().map(|v| escape_default(*v).to_string()).collect::<Vec<_>>().concat();
778         let s = s.split("\\\\").collect::<Vec<_>>().join("\\");
779         s.split("\\n").collect::<Vec<_>>().join("\n")
780     }
781 
782     /// A helper for assert checking ramdisk binary and bootconfig separately.
783     pub(crate) fn check_ramdisk(ramdisk: &[u8], expected_bin: &[u8], expected_bootconfig: &[u8]) {
784         let (ramdisk, bootconfig) = ramdisk.split_at(expected_bin.len());
785         assert_eq!(ramdisk, expected_bin);
786         assert_eq!(
787             bootconfig,
788             expected_bootconfig,
789             "\nexpect: \n{}\nactual: \n{}\n",
790             dump_bootconfig(expected_bootconfig),
791             dump_bootconfig(bootconfig),
792         );
793     }
794 
795     /// Helper for testing load/verify and assert verfiication success.
796     fn test_android_load_verify_success(
797         slot: u8,
798         partitions: &[(CString, String)],
799         expected_kernel: &[u8],
800         expected_ramdisk: &[u8],
801         expected_bootconfig: &[u8],
802         expected_dtb: &[u8],
803         expected_dtbo: &[u8],
804         expected_vendor_cmdline: &str,
805     ) {
806         let mut storage = FakeGblOpsStorage::default();
807         for (part, file) in partitions {
808             storage.add_raw_device(part, read_test_data(file));
809         }
810         let mut ops = FakeGblOps::new(&storage);
811         ops.avb_ops.unlock_state = Ok(false);
812         ops.avb_ops.rollbacks = HashMap::from([(TEST_ROLLBACK_INDEX_LOCATION, Ok(0))]);
813         let mut load_buffer = AlignedBuffer::new(64 * 1024 * 1024, KERNEL_ALIGNMENT);
814         let mut out_color = None;
815         let mut handler = |color,
816                            _: Option<&CStr>,
817                            _: Option<&[u8]>,
818                            _: Option<&[u8]>,
819                            _: Option<&[u8]>,
820                            _: Option<&[u8]>,
821                            _: Option<&[u8]>,
822                            _: Option<&[u8]>| {
823             out_color = Some(color);
824             Ok(())
825         };
826         ops.avb_handle_verification_result = Some(&mut handler);
827         ops.avb_key_validation_status = Some(Ok(KeyValidationStatus::Valid));
828         let loaded = android_load_verify(&mut ops, slot, false, &mut load_buffer).unwrap();
829 
830         assert_eq!(loaded.dtb, expected_dtb);
831         assert_eq!(out_color, Some(BootStateColor::Green));
832         assert_eq!(loaded.boot_cmdline, "cmd_key_1=cmd_val_1,cmd_key_2=cmd_val_2");
833         assert_eq!(loaded.vendor_cmdline, expected_vendor_cmdline);
834         assert_eq!(loaded.kernel, expected_kernel);
835         assert_eq!(loaded.kernel.as_ptr() as usize % KERNEL_ALIGNMENT, 0);
836         assert_eq!(loaded.dtbo, expected_dtbo);
837         check_ramdisk(loaded.ramdisk, expected_ramdisk, expected_bootconfig);
838     }
839 
840     /// A helper for generating avb bootconfig with the given parameters.
841     pub(crate) struct AvbResultBootconfigBuilder {
842         vbmeta_size: usize,
843         digest: String,
844         boot_digest: Option<String>,
845         init_boot_digest: Option<String>,
846         dtb_digest: Option<String>,
847         dtbo_digest: Option<String>,
848         vendor_boot_digest: Option<String>,
849         public_key_digest: String,
850         color: BootStateColor,
851         unlocked: bool,
852         extra: String,
853     }
854 
855     impl AvbResultBootconfigBuilder {
856         pub(crate) fn new() -> Self {
857             Self {
858                 vbmeta_size: 0,
859                 digest: String::new(),
860                 boot_digest: None,
861                 init_boot_digest: None,
862                 dtb_digest: None,
863                 dtbo_digest: None,
864                 vendor_boot_digest: None,
865                 public_key_digest: String::new(),
866                 color: BootStateColor::Green,
867                 unlocked: false,
868                 extra: String::new(),
869             }
870         }
871 
872         pub(crate) fn vbmeta_size(mut self, size: usize) -> Self {
873             self.vbmeta_size = size;
874             self
875         }
876 
877         pub(crate) fn digest(mut self, digest: impl Into<String>) -> Self {
878             self.digest = digest.into();
879             self
880         }
881 
882         pub(crate) fn partition_digest(mut self, name: &str, digest: impl Into<String>) -> Self {
883             let digest = Some(digest.into());
884             match name {
885                 "boot" => self.boot_digest = digest,
886                 "init_boot" => self.init_boot_digest = digest,
887                 "vendor_boot" => self.vendor_boot_digest = digest,
888                 "dtb" => self.dtb_digest = digest,
889                 "dtbo" => self.dtbo_digest = digest,
890                 _ => panic!("unknown digest name requested"),
891             };
892             self
893         }
894 
895         pub(crate) fn public_key_digest(mut self, pk_digest: impl Into<String>) -> Self {
896             self.public_key_digest = pk_digest.into();
897             self
898         }
899 
900         pub(crate) fn color(mut self, color: BootStateColor) -> Self {
901             self.color = color;
902             self
903         }
904 
905         pub(crate) fn unlocked(mut self, unlocked: bool) -> Self {
906             self.unlocked = unlocked;
907             self
908         }
909 
910         pub(crate) fn extra(mut self, extra: impl Into<String>) -> Self {
911             self.extra += &extra.into();
912             self
913         }
914 
915         pub(crate) fn build_string(self) -> String {
916             let device_state = match self.unlocked {
917                 true => "unlocked",
918                 false => "locked",
919             };
920 
921             let mut boot_digests = String::new();
922             for (name, maybe_digest) in [
923                 ("boot", &self.boot_digest),
924                 ("dtb", &self.dtb_digest),
925                 ("dtbo", &self.dtbo_digest),
926                 ("init_boot", &self.init_boot_digest),
927                 ("vendor_boot", &self.vendor_boot_digest),
928             ] {
929                 if let Some(digest) = maybe_digest {
930                     boot_digests += format!(
931                         "androidboot.vbmeta.{name}.hash_alg=sha256
932 androidboot.vbmeta.{name}.digest={digest}\n"
933                     )
934                     .as_str()
935                 }
936             }
937 
938             format!(
939                 "androidboot.vbmeta.device=PARTUUID=00000000-0000-0000-0000-000000000000
940 androidboot.vbmeta.public_key_digest={}
941 androidboot.vbmeta.avb_version=1.3
942 androidboot.vbmeta.device_state={}
943 androidboot.vbmeta.hash_alg=sha512
944 androidboot.vbmeta.size={}
945 androidboot.vbmeta.digest={}
946 androidboot.vbmeta.invalidate_on_error=yes
947 androidboot.veritymode=enforcing
948 {}androidboot.verifiedbootstate={}
949 {}",
950                 self.public_key_digest,
951                 device_state,
952                 self.vbmeta_size,
953                 self.digest,
954                 boot_digests.as_str(),
955                 self.color,
956                 self.extra
957             )
958         }
959 
960         pub(crate) fn build(self) -> Vec<u8> {
961             make_bootconfig(self.build_string())
962         }
963     }
964 
965     // A helper for generating expected bootconfig.
966     pub(crate) fn make_bootconfig(bootconfig: impl AsRef<str>) -> Vec<u8> {
967         let bootconfig = bootconfig.as_ref();
968         let mut buffer = vec![0u8; bootconfig.len() + BOOTCONFIG_TRAILER_SIZE];
969         let mut res = BootConfigBuilder::new(&mut buffer).unwrap();
970         res.add_with(|_, out| {
971             out[..bootconfig.len()].clone_from_slice(bootconfig.as_bytes());
972             Ok(bootconfig.as_bytes().len())
973         })
974         .unwrap();
975         res.config_bytes().to_vec()
976     }
977 
978     pub(crate) struct MakeExpectedBootconfigInclude {
979         pub boot: bool,
980         pub init_boot: bool,
981         pub vendor_boot: bool,
982         pub dtb: bool,
983         pub dtbo: bool,
984     }
985 
986     impl MakeExpectedBootconfigInclude {
987         fn is_include_str(&self, name: &str) -> bool {
988             match name {
989                 "boot" => self.boot,
990                 "init_boot" => self.init_boot,
991                 "vendor_boot" => self.vendor_boot,
992                 "dtb" => self.dtb,
993                 "dtbo" => self.dtbo,
994                 _ => false,
995             }
996         }
997     }
998 
999     impl Default for MakeExpectedBootconfigInclude {
1000         fn default() -> MakeExpectedBootconfigInclude {
1001             MakeExpectedBootconfigInclude {
1002                 boot: true,
1003                 init_boot: true,
1004                 vendor_boot: true,
1005                 dtb: true,
1006                 dtbo: true,
1007             }
1008         }
1009     }
1010 
1011     /// Helper for generating expected bootconfig after load and verification.
1012     pub(crate) fn make_expected_bootconfig(
1013         vbmeta_file: &str,
1014         slot: char,
1015         vendor_config: &str,
1016         include: MakeExpectedBootconfigInclude,
1017     ) -> Vec<u8> {
1018         let vbmeta_file = Path::new(vbmeta_file);
1019         let vbmeta_digest = vbmeta_file.with_extension("digest.txt");
1020         let vbmeta_digest = vbmeta_digest.to_str().unwrap();
1021         let mut builder = AvbResultBootconfigBuilder::new()
1022             .vbmeta_size(read_test_data(vbmeta_file.to_str().unwrap()).len())
1023             .digest(read_test_data_as_str(vbmeta_digest).strip_suffix("\n").unwrap())
1024             .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
1025             .extra("androidboot.force_normal_boot=1\n")
1026             .extra(format!("androidboot.slot_suffix=_{slot}\n"))
1027             .extra("androidboot.gbl.version=0\n")
1028             .extra(format!("androidboot.gbl.build_number={TEST_DEFAULT_BUILD_ID}\n"))
1029             .extra(FakeGblOps::GBL_TEST_BOOTCONFIG)
1030             .extra(vendor_config);
1031 
1032         for name in ["boot", "vendor_boot", "init_boot", "dtbo", "dtb"].iter() {
1033             let file = vbmeta_file.with_extension(format!("{name}.digest.txt"));
1034             println!("{file:?}");
1035             if include.is_include_str(name)
1036                 && Path::new(format!("{TEST_DATA_PATH}/{}", file.to_str().unwrap()).as_str())
1037                     .exists()
1038             {
1039                 builder = builder.partition_digest(
1040                     name,
1041                     read_test_data_as_str(file.to_str().unwrap()).strip_suffix("\n").unwrap(),
1042                 );
1043             }
1044         }
1045 
1046         builder.build()
1047     }
1048 
1049     /// Helper for testing load/verify for a/b slot v0,1,2 image with dtbo partition.
1050     ///
1051     /// # Args
1052     ///
1053     /// * `ver`: Boot image version.
1054     /// * `slot`: Target slot to boot.
1055     /// * `additional_part`: A list of pair `(partition name, file name)` representing additional
1056     ///   partitions for creating boot storage.
1057     /// * `expected_dtb`: The expected DTB.
1058     /// * `expected_dtbo`: The expected DTBO.
1059     fn test_android_load_verify_v2_and_lower_slot(
1060         ver: u8,
1061         slot: char,
1062         additional_part: &[(CString, String)],
1063         expected_dtb: &[u8],
1064         expected_dtbo: &[u8],
1065     ) {
1066         let dtbo =
1067             additional_part.iter().any(|(name, _)| name.to_str().unwrap().starts_with("dtbo_"));
1068         let vbmeta = format!("vbmeta_v{ver}_{slot}.img");
1069         let boot = format!("boot_v{ver}_{slot}.img");
1070         let mut parts: Vec<(CString, String)> = vec![
1071             (CString::new(format!("boot_{slot}")).unwrap(), boot.clone()),
1072             (CString::new(format!("vbmeta_{slot}")).unwrap(), vbmeta.clone()),
1073         ];
1074         parts.extend_from_slice(additional_part);
1075 
1076         test_android_load_verify_success(
1077             (u64::from(slot) - ('a' as u64)).try_into().unwrap(),
1078             &parts,
1079             &read_test_data(format!("kernel_{slot}.img")),
1080             &read_test_data(format!("generic_ramdisk_{slot}.img")),
1081             &make_expected_bootconfig(
1082                 &vbmeta,
1083                 slot,
1084                 "",
1085                 MakeExpectedBootconfigInclude { dtbo, dtb: false, ..Default::default() },
1086             ),
1087             expected_dtb,
1088             expected_dtbo,
1089             "",
1090         );
1091     }
1092 
1093     #[test]
1094     fn test_android_load_verify_v0_slot_a() {
1095         test_android_load_verify_v2_and_lower_slot(0, 'a', &[], &[], &[])
1096     }
1097 
1098     #[test]
1099     fn test_android_load_verify_v0_slot_b() {
1100         test_android_load_verify_v2_and_lower_slot(0, 'b', &[], &[], &[]);
1101     }
1102 
1103     #[test]
1104     fn test_android_load_verify_v1_slot_a() {
1105         test_android_load_verify_v2_and_lower_slot(1, 'a', &[], &[], &[])
1106     }
1107 
1108     #[test]
1109     fn test_android_load_verify_v1_slot_b() {
1110         test_android_load_verify_v2_and_lower_slot(1, 'b', &[], &[], &[]);
1111     }
1112 
1113     #[test]
1114     fn test_android_load_verify_v2_slot_a() {
1115         test_android_load_verify_v2_and_lower_slot(2, 'a', &[], &test_dtb(), &[])
1116     }
1117 
1118     #[test]
1119     fn test_android_load_verify_v2_slot_b() {
1120         test_android_load_verify_v2_and_lower_slot(2, 'b', &[], &test_dtb(), &[]);
1121     }
1122 
1123     fn test_android_load_verify_v2_and_lower_slot_with_dtbo(
1124         ver: u8,
1125         slot: char,
1126         expected_dtb: &[u8],
1127     ) {
1128         let dtbo = read_test_data(format!("dtbo_{slot}.img"));
1129         let parts: Vec<(CString, String)> =
1130             vec![(CString::new(format!("dtbo_{slot}")).unwrap(), format!("dtbo_{slot}.img"))];
1131         test_android_load_verify_v2_and_lower_slot(ver, slot, &parts, expected_dtb, &dtbo);
1132     }
1133 
1134     #[test]
1135     fn test_android_load_verify_v0_slot_a_with_dtbo() {
1136         test_android_load_verify_v2_and_lower_slot_with_dtbo(0, 'a', &[])
1137     }
1138 
1139     #[test]
1140     fn test_android_load_verify_v0_slot_b_with_dtbo() {
1141         test_android_load_verify_v2_and_lower_slot_with_dtbo(0, 'b', &[]);
1142     }
1143 
1144     #[test]
1145     fn test_android_load_verify_v1_slot_a_with_dtbo() {
1146         test_android_load_verify_v2_and_lower_slot_with_dtbo(1, 'a', &[])
1147     }
1148 
1149     #[test]
1150     fn test_android_load_verify_v1_slot_b_with_dtbo() {
1151         test_android_load_verify_v2_and_lower_slot_with_dtbo(1, 'b', &[]);
1152     }
1153 
1154     #[test]
1155     fn test_android_load_verify_v2_slot_a_with_dtbo() {
1156         test_android_load_verify_v2_and_lower_slot_with_dtbo(2, 'a', &test_dtb())
1157     }
1158 
1159     #[test]
1160     fn test_android_load_verify_v2_slot_b_with_dtbo() {
1161         test_android_load_verify_v2_and_lower_slot_with_dtbo(2, 'b', &test_dtb());
1162     }
1163 
1164     /// Helper for testing load/verify for v3/v4 boot/vendor_boot images.
1165     ///
1166     /// # Args
1167     ///
1168     /// * `partitions`: A list of pair `(partition name, file name)` for creating boot storage.
1169     /// * `vbmeta_file`: The vbmeta file for the storage. Used for constructing expected bootconfig.
1170     /// * `expected_kernel`: The expected kernel.
1171     /// * `expected_digest`: The expected digest outputed by vbmeta.
1172     /// * `expected_vendor_bootconfig`: The expected vendor_boot_config.
1173     fn test_android_load_verify_v3_and_v4(
1174         slot: char,
1175         partitions: &[(CString, String)],
1176         vbmeta: &str,
1177         expected_kernel: &[u8],
1178         expected_vendor_bootconfig: &str,
1179         expected_dtbo: &[u8],
1180     ) {
1181         let dtbo = partitions.iter().any(|(name, _)| name.to_str().unwrap().starts_with("dtbo_"));
1182         test_android_load_verify_success(
1183             (u64::from(slot) - ('a' as u64)).try_into().unwrap(),
1184             partitions,
1185             expected_kernel,
1186             &[
1187                 read_test_data(format!("vendor_ramdisk_{slot}.img")),
1188                 read_test_data(format!("generic_ramdisk_{slot}.img")),
1189             ]
1190             .concat(),
1191             &make_expected_bootconfig(
1192                 &vbmeta,
1193                 slot,
1194                 expected_vendor_bootconfig,
1195                 MakeExpectedBootconfigInclude { dtbo, dtb: false, ..Default::default() },
1196             ),
1197             &test_dtb(),
1198             expected_dtbo,
1199             TEST_VENDOR_CMDLINE,
1200         );
1201     }
1202 
1203     /// Helper for testing v3/v4 boot image without init_boot partition.
1204     fn test_android_load_verify_boot_v3_v4_slot_no_init_boot(
1205         slot: char,
1206         boot_ver: u32,
1207         vendor_ver: u32,
1208         additional_part: &[(CString, String)],
1209         expected_vendor_bootconfig: &str,
1210         expected_dtbo: &[u8],
1211     ) {
1212         let vbmeta = format!("vbmeta_v{boot_ver}_v{vendor_ver}_{slot}.img");
1213         let mut parts: Vec<(CString, String)> = vec![
1214             (CString::new(format!("boot_{slot}")).unwrap(), format!("boot_v{boot_ver}_{slot}.img")),
1215             (
1216                 CString::new(format!("vendor_boot_{slot}")).unwrap(),
1217                 format!("vendor_boot_v{vendor_ver}_{slot}.img"),
1218             ),
1219             (CString::new(format!("vbmeta_{slot}")).unwrap(), vbmeta.clone()),
1220         ];
1221         parts.extend_from_slice(additional_part);
1222         test_android_load_verify_v3_and_v4(
1223             slot,
1224             &parts[..],
1225             &vbmeta,
1226             &read_test_data(format!("kernel_{slot}.img")),
1227             expected_vendor_bootconfig,
1228             expected_dtbo,
1229         );
1230     }
1231 
1232     /// Helper for testing v3/v4 boot image with init_boot partition.
1233     fn test_android_load_verify_boot_v3_v4_slot_init_boot(
1234         slot: char,
1235         boot_ver: u32,
1236         vendor_ver: u32,
1237         additional_part: &[(CString, String)],
1238         expected_vendor_bootconfig: &str,
1239         expected_dtbo: &[u8],
1240     ) {
1241         let vbmeta = format!("vbmeta_v{boot_ver}_v{vendor_ver}_init_boot_{slot}.img");
1242         let mut parts: Vec<(CString, String)> = vec![
1243             (
1244                 CString::new(format!("boot_{slot}")).unwrap(),
1245                 format!("boot_no_ramdisk_v{boot_ver}_{slot}.img"),
1246             ),
1247             (
1248                 CString::new(format!("vendor_boot_{slot}")).unwrap(),
1249                 format!("vendor_boot_v{vendor_ver}_{slot}.img"),
1250             ),
1251             (CString::new(format!("init_boot_{slot}")).unwrap(), format!("init_boot_{slot}.img")),
1252             (CString::new(format!("vbmeta_{slot}")).unwrap(), vbmeta.clone()),
1253         ];
1254         parts.extend_from_slice(additional_part);
1255         test_android_load_verify_v3_and_v4(
1256             slot,
1257             &parts[..],
1258             &vbmeta,
1259             &read_test_data(format!("kernel_{slot}.img")),
1260             expected_vendor_bootconfig,
1261             expected_dtbo,
1262         );
1263     }
1264 
1265     enum KernelCompression {
1266         LZ4,
1267         GZIP,
1268     }
1269 
1270     impl fmt::Display for KernelCompression {
1271         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1272             match self {
1273                 KernelCompression::LZ4 => write!(f, "lz4"),
1274                 KernelCompression::GZIP => write!(f, "gz"),
1275             }
1276         }
1277     }
1278 
1279     /// Helper for testing v4 boot image with different kernel compression.
1280     fn test_android_load_verify_boot_v4_compression_slot(
1281         compression: KernelCompression,
1282         slot: char,
1283         expected_vendor_bootconfig: &str,
1284         expected_dtbo: &[u8],
1285     ) {
1286         let vbmeta = format!("vbmeta_v4_{compression}_{slot}.img");
1287         let parts: Vec<(CString, String)> = vec![
1288             (
1289                 CString::new(format!("boot_{slot}")).unwrap(),
1290                 format!("boot_v4_{compression}_{slot}.img"),
1291             ),
1292             (
1293                 CString::new(format!("vendor_boot_{slot}")).unwrap(),
1294                 format!("vendor_boot_v4_{slot}.img"),
1295             ),
1296             (CString::new(format!("vbmeta_{slot}")).unwrap(), vbmeta.clone()),
1297         ];
1298         test_android_load_verify_v3_and_v4(
1299             slot,
1300             &parts[..],
1301             &vbmeta,
1302             &read_test_data(format!("gki_boot_{compression}_kernel_uncompressed")),
1303             expected_vendor_bootconfig,
1304             expected_dtbo,
1305         );
1306     }
1307 
1308     #[test]
1309     fn test_android_load_verify_boot_v3_vendor_v3_no_init_boot_slot_a() {
1310         test_android_load_verify_boot_v3_v4_slot_no_init_boot('a', 3, 3, &[], "", &[])
1311     }
1312 
1313     #[test]
1314     fn test_android_load_verify_boot_v3_vendor_v3_no_init_boot_slot_b() {
1315         test_android_load_verify_boot_v3_v4_slot_no_init_boot('b', 3, 3, &[], "", &[])
1316     }
1317 
1318     #[test]
1319     fn test_android_load_verify_boot_v3_vendor_v3_init_boot_slot_a() {
1320         test_android_load_verify_boot_v3_v4_slot_init_boot('a', 3, 3, &[], "", &[])
1321     }
1322 
1323     #[test]
1324     fn test_android_load_verify_boot_v3_vendor_v3_init_boot_slot_b() {
1325         test_android_load_verify_boot_v3_v4_slot_init_boot('b', 3, 3, &[], "", &[])
1326     }
1327 
1328     #[test]
1329     fn test_android_load_verify_boot_v3_vendor_v4_no_init_boot_slot_a() {
1330         test_android_load_verify_boot_v3_v4_slot_no_init_boot(
1331             'a',
1332             3,
1333             4,
1334             &[],
1335             TEST_VENDOR_BOOTCONFIG,
1336             &[],
1337         )
1338     }
1339 
1340     #[test]
1341     fn test_android_load_verify_boot_v3_vendor_v4_no_init_boot_slot_b() {
1342         test_android_load_verify_boot_v3_v4_slot_no_init_boot(
1343             'b',
1344             3,
1345             4,
1346             &[],
1347             TEST_VENDOR_BOOTCONFIG,
1348             &[],
1349         )
1350     }
1351 
1352     #[test]
1353     fn test_android_load_verify_boot_v3_vendor_v4_init_boot_slot_a() {
1354         test_android_load_verify_boot_v3_v4_slot_init_boot(
1355             'a',
1356             3,
1357             4,
1358             &[],
1359             TEST_VENDOR_BOOTCONFIG,
1360             &[],
1361         )
1362     }
1363 
1364     #[test]
1365     fn test_android_load_verify_boot_v3_vendor_v4_init_boot_slot_b() {
1366         test_android_load_verify_boot_v3_v4_slot_init_boot(
1367             'b',
1368             3,
1369             4,
1370             &[],
1371             TEST_VENDOR_BOOTCONFIG,
1372             &[],
1373         )
1374     }
1375 
1376     #[test]
1377     fn test_android_load_verify_boot_v4_vendor_v3_no_init_boot_slot_a() {
1378         test_android_load_verify_boot_v3_v4_slot_no_init_boot('a', 4, 3, &[], "", &[])
1379     }
1380 
1381     #[test]
1382     fn test_android_load_verify_boot_v4_vendor_v3_no_init_boot_slot_b() {
1383         test_android_load_verify_boot_v3_v4_slot_no_init_boot('b', 4, 3, &[], "", &[])
1384     }
1385 
1386     #[test]
1387     fn test_android_load_verify_boot_v4_vendor_v3_init_boot_slot_a() {
1388         test_android_load_verify_boot_v3_v4_slot_init_boot('a', 4, 3, &[], "", &[])
1389     }
1390 
1391     #[test]
1392     fn test_android_load_verify_boot_v4_vendor_v3_init_boot_slot_b() {
1393         test_android_load_verify_boot_v3_v4_slot_init_boot('b', 4, 3, &[], "", &[])
1394     }
1395 
1396     #[test]
1397     fn test_android_load_verify_boot_v4_vendor_v4_no_init_boot_slot_a() {
1398         test_android_load_verify_boot_v3_v4_slot_no_init_boot(
1399             'a',
1400             4,
1401             4,
1402             &[],
1403             TEST_VENDOR_BOOTCONFIG,
1404             &[],
1405         )
1406     }
1407 
1408     #[test]
1409     fn test_android_load_verify_boot_v4_vendor_v4_no_init_boot_slot_b() {
1410         test_android_load_verify_boot_v3_v4_slot_no_init_boot(
1411             'b',
1412             4,
1413             4,
1414             &[],
1415             TEST_VENDOR_BOOTCONFIG,
1416             &[],
1417         )
1418     }
1419 
1420     #[test]
1421     fn test_android_load_verify_boot_v4_vendor_v4_init_boot_slot_a() {
1422         test_android_load_verify_boot_v3_v4_slot_init_boot(
1423             'a',
1424             4,
1425             4,
1426             &[],
1427             TEST_VENDOR_BOOTCONFIG,
1428             &[],
1429         )
1430     }
1431 
1432     #[test]
1433     fn test_android_load_verify_boot_v4_vendor_v4_init_boot_slot_b() {
1434         test_android_load_verify_boot_v3_v4_slot_init_boot(
1435             'b',
1436             4,
1437             4,
1438             &[],
1439             TEST_VENDOR_BOOTCONFIG,
1440             &[],
1441         )
1442     }
1443 
1444     /// Same as `test_android_load_verify_boot_v3_v4_slot_no_init_boot` but with dtbo partition.
1445     fn test_android_load_verify_boot_v3_v4_slot_no_init_boot_with_dtbo(
1446         slot: char,
1447         boot_ver: u32,
1448         vendor_ver: u32,
1449         expected_vendor_bootconfig: &str,
1450     ) {
1451         let dtbo = read_test_data(format!("dtbo_{slot}.img"));
1452         let parts: Vec<(CString, String)> =
1453             vec![(CString::new(format!("dtbo_{slot}")).unwrap(), format!("dtbo_{slot}.img"))];
1454         test_android_load_verify_boot_v3_v4_slot_no_init_boot(
1455             slot,
1456             boot_ver,
1457             vendor_ver,
1458             &parts,
1459             expected_vendor_bootconfig,
1460             &dtbo,
1461         );
1462     }
1463 
1464     /// Same as `test_android_load_verify_boot_v3_v4_slot_init_boot` but with dtbo partition.
1465     fn test_android_load_verify_boot_v3_v4_slot_init_boot_with_dtbo(
1466         slot: char,
1467         boot_ver: u32,
1468         vendor_ver: u32,
1469         expected_vendor_bootconfig: &str,
1470     ) {
1471         let dtbo = read_test_data(format!("dtbo_{slot}.img"));
1472         let parts: Vec<(CString, String)> =
1473             vec![(CString::new(format!("dtbo_{slot}")).unwrap(), format!("dtbo_{slot}.img"))];
1474         test_android_load_verify_boot_v3_v4_slot_init_boot(
1475             slot,
1476             boot_ver,
1477             vendor_ver,
1478             &parts,
1479             expected_vendor_bootconfig,
1480             &dtbo,
1481         );
1482     }
1483 
1484     #[test]
1485     fn test_android_load_verify_boot_v3_vendor_v3_no_init_boot_slot_a_with_dtbo() {
1486         test_android_load_verify_boot_v3_v4_slot_no_init_boot_with_dtbo('a', 3, 3, "")
1487     }
1488 
1489     #[test]
1490     fn test_android_load_verify_boot_v3_vendor_v3_no_init_boot_slot_b_with_dtbo() {
1491         test_android_load_verify_boot_v3_v4_slot_no_init_boot_with_dtbo('b', 3, 3, "")
1492     }
1493 
1494     #[test]
1495     fn test_android_load_verify_boot_v3_vendor_v3_init_boot_slot_a_with_dtbo() {
1496         test_android_load_verify_boot_v3_v4_slot_init_boot_with_dtbo('a', 3, 3, "")
1497     }
1498 
1499     #[test]
1500     fn test_android_load_verify_boot_v3_vendor_v3_init_boot_slot_b_with_dtbo() {
1501         test_android_load_verify_boot_v3_v4_slot_init_boot_with_dtbo('b', 3, 3, "")
1502     }
1503 
1504     #[test]
1505     fn test_android_load_verify_boot_v3_vendor_v4_no_init_boot_slot_a_with_dtbo() {
1506         test_android_load_verify_boot_v3_v4_slot_no_init_boot_with_dtbo(
1507             'a',
1508             3,
1509             4,
1510             TEST_VENDOR_BOOTCONFIG,
1511         )
1512     }
1513 
1514     #[test]
1515     fn test_android_load_verify_boot_v3_vendor_v4_no_init_boot_slot_b_with_dtbo() {
1516         test_android_load_verify_boot_v3_v4_slot_no_init_boot_with_dtbo(
1517             'b',
1518             3,
1519             4,
1520             TEST_VENDOR_BOOTCONFIG,
1521         )
1522     }
1523 
1524     #[test]
1525     fn test_android_load_verify_boot_v3_vendor_v4_init_boot_slot_a_with_dtbo() {
1526         test_android_load_verify_boot_v3_v4_slot_init_boot_with_dtbo(
1527             'a',
1528             3,
1529             4,
1530             TEST_VENDOR_BOOTCONFIG,
1531         )
1532     }
1533 
1534     #[test]
1535     fn test_android_load_verify_boot_v3_vendor_v4_init_boot_slot_b_with_dtbo() {
1536         test_android_load_verify_boot_v3_v4_slot_init_boot_with_dtbo(
1537             'b',
1538             3,
1539             4,
1540             TEST_VENDOR_BOOTCONFIG,
1541         )
1542     }
1543 
1544     #[test]
1545     fn test_android_load_verify_boot_v4_vendor_v3_no_init_boot_slot_a_with_dtbo() {
1546         test_android_load_verify_boot_v3_v4_slot_no_init_boot_with_dtbo('a', 4, 3, "")
1547     }
1548 
1549     #[test]
1550     fn test_android_load_verify_boot_v4_vendor_v3_no_init_boot_slot_b_with_dtbo() {
1551         test_android_load_verify_boot_v3_v4_slot_no_init_boot_with_dtbo('b', 4, 3, "")
1552     }
1553 
1554     #[test]
1555     fn test_android_load_verify_boot_v4_vendor_v3_init_boot_slot_a_with_dtbo() {
1556         test_android_load_verify_boot_v3_v4_slot_init_boot_with_dtbo('a', 4, 3, "")
1557     }
1558 
1559     #[test]
1560     fn test_android_load_verify_boot_v4_vendor_v3_init_boot_slot_b_with_dtbo() {
1561         test_android_load_verify_boot_v3_v4_slot_init_boot_with_dtbo('b', 4, 3, "")
1562     }
1563 
1564     #[test]
1565     fn test_android_load_verify_boot_v4_vendor_v4_no_init_boot_slot_a_with_dtbo() {
1566         test_android_load_verify_boot_v3_v4_slot_no_init_boot_with_dtbo(
1567             'a',
1568             4,
1569             4,
1570             TEST_VENDOR_BOOTCONFIG,
1571         )
1572     }
1573 
1574     #[test]
1575     fn test_android_load_verify_boot_v4_vendor_v4_no_init_boot_slot_b_with_dtbo() {
1576         test_android_load_verify_boot_v3_v4_slot_no_init_boot_with_dtbo(
1577             'b',
1578             4,
1579             4,
1580             TEST_VENDOR_BOOTCONFIG,
1581         )
1582     }
1583 
1584     #[test]
1585     fn test_android_load_verify_boot_v4_vendor_v4_init_boot_slot_a_with_dtbo() {
1586         test_android_load_verify_boot_v3_v4_slot_init_boot_with_dtbo(
1587             'a',
1588             4,
1589             4,
1590             TEST_VENDOR_BOOTCONFIG,
1591         )
1592     }
1593 
1594     #[test]
1595     fn test_android_load_verify_boot_v4_vendor_v4_init_boot_slot_b_with_dtbo() {
1596         test_android_load_verify_boot_v3_v4_slot_init_boot_with_dtbo(
1597             'b',
1598             4,
1599             4,
1600             TEST_VENDOR_BOOTCONFIG,
1601         )
1602     }
1603 
1604     #[test]
1605     fn test_android_load_verify_gzip_boot_v4_vendor_v4_slot_a() {
1606         test_android_load_verify_boot_v4_compression_slot(
1607             KernelCompression::GZIP,
1608             'a',
1609             TEST_VENDOR_BOOTCONFIG,
1610             &[],
1611         )
1612     }
1613 
1614     #[test]
1615     fn test_android_load_verify_lz4_boot_v4_vendor_v4_slot_a() {
1616         test_android_load_verify_boot_v4_compression_slot(
1617             KernelCompression::LZ4,
1618             'a',
1619             TEST_VENDOR_BOOTCONFIG,
1620             &[],
1621         )
1622     }
1623 }
1624