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