1 // Copyright 2023, 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 //! # Generic Boot Loader (gbl) Library
16 //!
17 //! TODO: b/312610098 - add documentation.
18 //!
19 //! The intended users of this library are firmware, bootloader, and bring-up teams at OEMs and SOC
20 //! Vendors
21 //!
22 //! # Features
23 //! * `alloc` - enables AVB ops related logic that relies on allocation and depends on allocation.
24
25 // This code is intended for use in bootloaders that typically will not support
26 // the Rust standard library
27 #![cfg_attr(not(any(test, android_dylib)), no_std)]
28 // TODO: b/312610985 - return warning for unused partitions
29 #![allow(unused_variables, dead_code)]
30 // TODO: b/312608163 - Adding ZBI library usage to check dependencies
31 extern crate avb;
32 extern crate core;
33 extern crate cstr;
34 extern crate gbl_storage;
35 extern crate spin;
36 extern crate zbi;
37
38 use avb::{HashtreeErrorMode, SlotVerifyData, SlotVerifyError, SlotVerifyFlags, SlotVerifyResult};
39 use core::ffi::CStr;
40 use core::fmt::Debug;
41 use cstr::cstr;
42 use gbl_storage::AsMultiBlockDevices;
43 use spin::Mutex;
44
45 pub mod boot_mode;
46 pub mod boot_reason;
47 pub mod error;
48 pub mod fastboot;
49 pub mod ops;
50 mod overlap;
51
52 /// The 'slots' module, containing types and traits for
53 /// querying and modifying slotted boot behavior.
54 pub mod slots;
55
56 use slots::{BootTarget, BootToken, Cursor, Manager, OneShot, SuffixBytes, UnbootableReason};
57
58 pub use avb::Descriptor;
59 pub use boot_mode::BootMode;
60 pub use boot_reason::KnownBootReason;
61 pub use error::{Error, IntegrationError, Result};
62 pub use ops::{
63 AndroidBootImages, BootImages, DefaultGblOps, FuchsiaBootImages, GblOps, GblOpsError,
64 };
65
66 use ops::GblUtils;
67 use overlap::is_overlap;
68
69 // TODO: b/312607649 - Replace placeholders with actual structures: https://r.android.com/2721974, etc
70 /// TODO: b/312607649 - placeholder type
71 pub struct Partition {}
72 /// TODO: b/312607649 - placeholder type
73 pub struct InfoStruct {}
74
75 /// Data structure holding verified slot data.
76 #[derive(Debug)]
77 pub struct VerifiedData<'a>(SlotVerifyData<'a>);
78
79 /// Structure representing partition and optional address it is required to be loaded.
80 /// If no address is provided GBL will use default one.
81 pub struct PartitionRamMap<'b, 'c> {
82 /// Partition details
83 pub partition: &'b Partition,
84
85 /// Optional memory region to load partitions.
86 /// If it's not provided default values will be used.
87 pub address: Option<&'c mut [u8]>,
88
89 loaded: bool,
90 verified: bool,
91 }
92
93 /// Boot Image in memory
94 pub struct BootImage<'a>(&'a mut [u8]);
95
96 /// Vendor Boot Image in memory
97 pub struct VendorBootImage<'a>(&'a mut [u8]);
98
99 /// Init Boot Image in memory
100 pub struct InitBootImage<'a>(&'a mut [u8]);
101
102 /// Kernel Image in memory
103 pub struct KernelImage<'a>(&'a mut [u8]);
104
105 /// Ramdisk in memory
106 pub struct Ramdisk<'a>(&'a mut [u8]);
107 /// Bootconfig in memory
108 pub struct Bootconfig<'a>(&'a mut [u8]);
109 /// DTB in memory
110 pub struct Dtb<'a>(&'a mut [u8]);
111
112 /// Create Boot Image from corresponding partition for `partitions_ram_map` and `avb_descriptors`
113 /// lists
get_boot_image<'a: 'b, 'b: 'c, 'c, 'd>( verified_data: &mut VerifiedData<'d>, partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> (Option<BootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>])114 pub fn get_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
115 verified_data: &mut VerifiedData<'d>,
116 partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
117 ) -> (Option<BootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
118 match partitions_ram_map.len() {
119 0 => (None, partitions_ram_map),
120 _ => {
121 let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
122 (partition_map.address.take().map(BootImage), tail)
123 }
124 }
125 }
126
127 /// Create Vendor Boot Image from corresponding partition for `partitions_ram_map` and
128 /// `avb_descriptors` lists
get_vendor_boot_image<'a: 'b, 'b: 'c, 'c, 'd>( verified_data: &mut VerifiedData<'d>, partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> (Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>])129 pub fn get_vendor_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
130 verified_data: &mut VerifiedData<'d>,
131 partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
132 ) -> (Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
133 match partitions_ram_map.len() {
134 0 => (None, partitions_ram_map),
135 _ => {
136 let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
137 (partition_map.address.take().map(VendorBootImage), tail)
138 }
139 }
140 }
141
142 /// Create Init Boot Image from corresponding partition for `partitions` and `avb_descriptors` lists
get_init_boot_image<'a: 'b, 'b: 'c, 'c, 'd>( verified_data: &mut VerifiedData<'d>, partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> (Option<InitBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>])143 pub fn get_init_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
144 verified_data: &mut VerifiedData<'d>,
145 partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
146 ) -> (Option<InitBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
147 match partitions_ram_map.len() {
148 0 => (None, partitions_ram_map),
149 _ => {
150 let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
151 (partition_map.address.take().map(InitBootImage), tail)
152 }
153 }
154 }
155
156 /// Create separate image types from [avb::Descriptor]
get_images<'a: 'b, 'b: 'c, 'c, 'd>( verified_data: &mut VerifiedData<'d>, partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> ( Option<BootImage<'c>>, Option<InitBootImage<'c>>, Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>], )157 pub fn get_images<'a: 'b, 'b: 'c, 'c, 'd>(
158 verified_data: &mut VerifiedData<'d>,
159 partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
160 ) -> (
161 Option<BootImage<'c>>,
162 Option<InitBootImage<'c>>,
163 Option<VendorBootImage<'c>>,
164 &'a mut [PartitionRamMap<'b, 'c>],
165 ) {
166 let (boot_image, partitions_ram_map) = get_boot_image(verified_data, partitions_ram_map);
167 let (init_boot_image, partitions_ram_map) =
168 get_init_boot_image(verified_data, partitions_ram_map);
169 let (vendor_boot_image, partitions_ram_map) =
170 get_vendor_boot_image(verified_data, partitions_ram_map);
171 (boot_image, init_boot_image, vendor_boot_image, partitions_ram_map)
172 }
173
174 static BOOT_TOKEN: Mutex<Option<BootToken>> = Mutex::new(Some(BootToken(())));
175
176 type AvbVerifySlot = for<'b> fn(
177 ops: &mut dyn avb::Ops<'b>,
178 requested_partitions: &[&CStr],
179 ab_suffix: Option<&CStr>,
180 flags: SlotVerifyFlags,
181 hashtree_error_mode: HashtreeErrorMode,
182 ) -> SlotVerifyResult<'b, SlotVerifyData<'b>>;
183
184 /// GBL object that provides implementation of helpers for boot process.
185 ///
186 /// To create this object use [GblBuilder].
187 pub struct Gbl<'a, G>
188 where
189 G: GblOps,
190 {
191 ops: &'a mut G,
192 verify_slot: AvbVerifySlot,
193 }
194
195 impl<'a, G> Gbl<'a, G>
196 where
197 G: GblOps,
198 {
199 /// Verify + Load Image Into memory
200 ///
201 /// Load from disk, validate with AVB
202 ///
203 /// # Arguments
204 /// * `avb_ops` - implementation for `avb::Ops` that would be borrowed in result to prevent
205 /// changes to partitions until it is out of scope.
206 /// * `partitions_ram_map` - Partitions to verify with optional address to load image to.
207 /// * `slot_verify_flags` - AVB slot verification flags
208 /// * `boot_target` - [Optional] Boot Target
209 ///
210 /// # Returns
211 ///
212 /// * `Ok(&[avb_descriptor])` - Array of AVB Descriptors - AVB return codes, partition name,
213 /// image load address, image size, AVB Footer contents (version details, etc.)
214 /// * `Err(Error)` - on failure
load_and_verify_image<'b>( &mut self, avb_ops: &mut impl avb::Ops<'b>, partitions_ram_map: &mut [PartitionRamMap], slot_verify_flags: SlotVerifyFlags, boot_target: Option<BootTarget>, ) -> Result<VerifiedData<'b>>215 pub fn load_and_verify_image<'b>(
216 &mut self,
217 avb_ops: &mut impl avb::Ops<'b>,
218 partitions_ram_map: &mut [PartitionRamMap],
219 slot_verify_flags: SlotVerifyFlags,
220 boot_target: Option<BootTarget>,
221 ) -> Result<VerifiedData<'b>> {
222 let bytes: SuffixBytes =
223 if let Some(tgt) = boot_target { tgt.suffix().into() } else { Default::default() };
224
225 let requested_partitions = [cstr!("")];
226 let avb_suffix = CStr::from_bytes_until_nul(&bytes)?;
227
228 let verified_data = VerifiedData(
229 (self.verify_slot)(
230 avb_ops,
231 &requested_partitions,
232 Some(avb_suffix),
233 slot_verify_flags,
234 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
235 )
236 .map_err(|v| v.without_verify_data())?,
237 );
238
239 Ok(verified_data)
240 }
241
242 /// Load Slot Manager Interface
243 ///
244 /// The default implementation loads from the `durable_boot` partition
245 /// and writes changes back on the destruction of the cursor.
246 ///
247 /// # Returns
248 ///
249 /// * `Ok(Cursor)` - Cursor object that manages a Manager
250 /// * `Err(Error)` - on failure
load_slot_interface<'b, B: gbl_storage::AsBlockDevice, M: Manager>( &mut self, block_device: &'b mut B, ) -> Result<Cursor<'b, B, M>>251 pub fn load_slot_interface<'b, B: gbl_storage::AsBlockDevice, M: Manager>(
252 &mut self,
253 block_device: &'b mut B,
254 ) -> Result<Cursor<'b, B, M>> {
255 let boot_token = BOOT_TOKEN.lock().take().ok_or(Error::OperationProhibited)?;
256 self.ops
257 .load_slot_interface::<B, M>(block_device, boot_token)
258 .map_err(|_| Error::OperationProhibited.into())
259 }
260
261 /// Info Load
262 ///
263 /// Unpack boot image in RAM
264 ///
265 /// # Arguments
266 /// * `boot_image_buffer` - Buffer that contains (Optionally Verified) Boot Image
267 /// * `boot_mode` - Boot Mode
268 /// * `boot_target` - [Optional] Boot Target
269 ///
270 /// # Returns
271 ///
272 /// * `Ok(InfoStruct)` - Info Struct (Concatenated kernel commandline - includes slot,
273 /// bootconfig selection, normal_mode, Concatenated bootconfig) on success
274 /// * `Err(Error)` - on failure
unpack_boot_image( &self, boot_image_buffer: &BootImage, boot_target: Option<BootTarget>, ) -> Result<InfoStruct>275 pub fn unpack_boot_image(
276 &self,
277 boot_image_buffer: &BootImage,
278 boot_target: Option<BootTarget>,
279 ) -> Result<InfoStruct> {
280 unimplemented!();
281 }
282
283 /// Kernel Load
284 ///
285 /// Prepare kernel in RAM for booting
286 ///
287 /// # Arguments
288 /// * `info` - Info Struct from Info Load
289 /// * `image_buffer` - Buffer that contains (Verified) Boot Image
290 /// * `load_buffer` - Kernel Load buffer
291 ///
292 /// # Returns
293 ///
294 /// * `Ok(())` - on success
295 /// * `Err(Error)` - on failure
kernel_load<'b>( &self, info: &InfoStruct, image_buffer: BootImage, load_buffer: &'b mut [u8], ) -> Result<KernelImage<'b>>296 pub fn kernel_load<'b>(
297 &self,
298 info: &InfoStruct,
299 image_buffer: BootImage,
300 load_buffer: &'b mut [u8],
301 ) -> Result<KernelImage<'b>> {
302 unimplemented!();
303 }
304
305 /// Ramdisk + Bootconfig Load
306 ///
307 /// Kernel Load
308 /// (Could break this into a RD and Bootconfig specific function each, TBD)
309 /// Prepare ramdisk/bootconfig in RAM for booting
310 ///
311 /// # Arguments
312 /// * `info` - Info Struct from Info Load
313 /// * `vendor_boot_image` - Buffer that contains (Verified) Vendor Boot Image
314 /// * `init_boot_image` - Buffer that contains (Verified) Init Boot Image
315 /// * `ramdisk_load_buffer` - Ramdisk Load buffer (not compressed). It will be filled with
316 /// a concatenation of `vendor_boot_image`, `init_boot_image` and bootconfig at the end.
317 ///
318 /// # Returns
319 ///
320 /// * `Ok(&str)` - on success returns Kernel command line
321 /// * `Err(Error)` - on failure
ramdisk_bootconfig_load( &self, info: &InfoStruct, vendor_boot_image: &VendorBootImage, init_boot_image: &InitBootImage, ramdisk: &mut Ramdisk, ) -> Result<&'static str>322 pub fn ramdisk_bootconfig_load(
323 &self,
324 info: &InfoStruct,
325 vendor_boot_image: &VendorBootImage,
326 init_boot_image: &InitBootImage,
327 ramdisk: &mut Ramdisk,
328 ) -> Result<&'static str> {
329 unimplemented!();
330 }
331
332 /// DTB Update And Load
333 ///
334 /// Prepare DTB in RAM for booting
335 ///
336 /// # Arguments
337 /// * `info` - Info Struct from Info Load
338 /// * `vendor_boot_image_buffer` - Buffer that contains (Verified) Vendor Boot Image
339 ///
340 /// # Returns
341 ///
342 /// * `Ok()` - on success
343 /// * `Err(Error)` - on failure
dtb_update_and_load( &self, info: &InfoStruct, vendor_boot_image_buffer: VendorBootImage, ) -> Result<Dtb>344 pub fn dtb_update_and_load(
345 &self,
346 info: &InfoStruct,
347 vendor_boot_image_buffer: VendorBootImage,
348 ) -> Result<Dtb> {
349 unimplemented!();
350 }
351
352 /// Kernel Jump
353 ///
354 ///
355 /// # Arguments
356 /// * `kernel_load_buffer` - Kernel Load buffer
357 /// * `ramdisk_bootconfi_load_buffer` - Concatenated Ramdisk, (Bootconfig if present) Load
358 /// buffer
359 /// * `dtb_load_buffer` - DTB Load buffer
360 /// * `boot_token` - Consumable boot token
361 ///
362 /// # Returns
363 ///
364 /// * doesn't return on success
365 /// * `Err(Error)` - on failure
366 // Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812
kernel_jump( &self, kernel_load_buffer: KernelImage, ramdisk_load_buffer: Ramdisk, dtb_load_buffer: Dtb, boot_token: BootToken, ) -> Result<()>367 pub fn kernel_jump(
368 &self,
369 kernel_load_buffer: KernelImage,
370 ramdisk_load_buffer: Ramdisk,
371 dtb_load_buffer: Dtb,
372 boot_token: BootToken,
373 ) -> Result<()> {
374 unimplemented!();
375 }
376
377 /// Load, verify, and boot
378 ///
379 /// Wrapper around the above functions for devices that don't need custom behavior between each
380 /// step
381 ///
382 /// Warning: If the call to load_verify_boot fails, the device MUST
383 /// be restarted in order to make forward boot progress.
384 /// Callers MAY log the error, enter an interactive mode,
385 /// or take other actions before rebooting.
386 ///
387 ///
388 /// # Arguments
389 /// * `avb_ops` - implementation for `avb::Ops` that would be borrowed in result to prevent
390 /// changes to partitions until it is out of scope.
391 /// * `partitions_ram_map` - Partitions to verify and optional address for them to be loaded.
392 /// * `slot_verify_flags` - AVB slot verification flags
393 /// * `slot_cursor` - Cursor object that manages interactions with boot slot management
394 /// * `kernel_load_buffer` - Buffer for loading the kernel.
395 /// * `ramdisk_load_buffer` - Buffer for loading the ramdisk.
396 /// * `fdt` - Buffer containing a flattened device tree blob.
397 ///
398 /// # Returns
399 ///
400 /// * doesn't return on success
401 /// * `Err(Error)` - on failure
402 // Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812
403 #[allow(clippy::too_many_arguments)]
load_verify_boot<'b: 'c, 'c, 'd: 'b, B: gbl_storage::AsBlockDevice>( &mut self, avb_ops: &mut impl avb::Ops<'b>, partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>], slot_verify_flags: SlotVerifyFlags, slot_cursor: Cursor<B, impl Manager>, kernel_load_buffer: &mut [u8], ramdisk_load_buffer: &mut [u8], fdt: &mut [u8], ) -> Result<()>404 pub fn load_verify_boot<'b: 'c, 'c, 'd: 'b, B: gbl_storage::AsBlockDevice>(
405 &mut self,
406 avb_ops: &mut impl avb::Ops<'b>,
407 partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>],
408 slot_verify_flags: SlotVerifyFlags,
409 slot_cursor: Cursor<B, impl Manager>,
410 kernel_load_buffer: &mut [u8],
411 ramdisk_load_buffer: &mut [u8],
412 fdt: &mut [u8],
413 ) -> Result<()> {
414 let dtb = Dtb(&mut fdt[..]);
415 let mut ramdisk = Ramdisk(ramdisk_load_buffer);
416
417 // Call the inner method which consumes the cursor
418 // in order to properly manager cursor lifetime
419 // and cleanup.
420 let (kernel_image, token) = self.lvb_inner(
421 avb_ops,
422 &mut ramdisk,
423 kernel_load_buffer,
424 partitions_ram_map,
425 slot_verify_flags,
426 slot_cursor,
427 )?;
428
429 self.kernel_jump(kernel_image, ramdisk, dtb, token)
430 }
431
is_unrecoverable_error(error: &IntegrationError) -> bool432 fn is_unrecoverable_error(error: &IntegrationError) -> bool {
433 // Note: these ifs are nested instead of chained because multiple
434 // expressions in an if-let is an unstable features
435 if let IntegrationError::AvbSlotVerifyError(ref avb_error) = error {
436 // These are the AVB errors that are not recoverable on a subsequent attempt.
437 // If necessary in the future, this helper function can be moved to the GblOps trait
438 // and customized for platform specific behavior.
439 if matches!(
440 avb_error,
441 SlotVerifyError::Verification(_)
442 | SlotVerifyError::PublicKeyRejected
443 | SlotVerifyError::RollbackIndex
444 ) {
445 return true;
446 }
447 }
448 false
449 }
450
lvb_inner<'b: 'c, 'c, 'd: 'b, 'e, B: gbl_storage::AsBlockDevice>( &mut self, avb_ops: &mut impl avb::Ops<'b>, ramdisk: &mut Ramdisk, kernel_load_buffer: &'e mut [u8], partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>], slot_verify_flags: SlotVerifyFlags, mut slot_cursor: Cursor<B, impl Manager>, ) -> Result<(KernelImage<'e>, BootToken)>451 fn lvb_inner<'b: 'c, 'c, 'd: 'b, 'e, B: gbl_storage::AsBlockDevice>(
452 &mut self,
453 avb_ops: &mut impl avb::Ops<'b>,
454 ramdisk: &mut Ramdisk,
455 kernel_load_buffer: &'e mut [u8],
456 partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>],
457 slot_verify_flags: SlotVerifyFlags,
458 mut slot_cursor: Cursor<B, impl Manager>,
459 ) -> Result<(KernelImage<'e>, BootToken)> {
460 let mut oneshot_status = slot_cursor.ctx.get_oneshot_status();
461 slot_cursor.ctx.clear_oneshot_status();
462
463 if oneshot_status == Some(OneShot::Bootloader) {
464 match self.ops.do_fastboot(&mut slot_cursor) {
465 Ok(_) => oneshot_status = slot_cursor.ctx.get_oneshot_status(),
466 Err(IntegrationError::GblNativeError(Error::NotImplemented)) => (),
467 Err(e) => return Err(e),
468 }
469 }
470
471 let boot_target = match oneshot_status {
472 None | Some(OneShot::Bootloader) => slot_cursor.ctx.get_boot_target(),
473 Some(OneShot::Continue(recovery)) => BootTarget::Recovery(recovery),
474 };
475
476 let mut verify_data = self
477 .load_and_verify_image(
478 avb_ops,
479 partitions_ram_map,
480 slot_verify_flags,
481 Some(boot_target),
482 )
483 .map_err(|e: IntegrationError| {
484 if let BootTarget::NormalBoot(slot) = boot_target {
485 if Self::is_unrecoverable_error(&e) {
486 let _ = slot_cursor.ctx.set_slot_unbootable(
487 slot.suffix,
488 UnbootableReason::VerificationFailure,
489 );
490 } else {
491 // Note: the call to mark_boot_attempt will fail if any of the following occur:
492 // * the target was already Unbootable before the call to load_and_verify_image
493 // * policy, I/O, or other errors in mark_boot_attempt
494 //
495 // We don't really care about those circumstances.
496 // The call here is a best effort attempt to decrement tries remaining.
497 let _ = slot_cursor.ctx.mark_boot_attempt();
498 }
499 }
500 e
501 })?;
502
503 let (boot_image, init_boot_image, vendor_boot_image, _) =
504 get_images(&mut verify_data, partitions_ram_map);
505 let boot_image = boot_image.ok_or(Error::MissingImage)?;
506 let vendor_boot_image = vendor_boot_image.ok_or(Error::MissingImage)?;
507 let init_boot_image = init_boot_image.ok_or(Error::MissingImage)?;
508
509 if is_overlap(&[
510 boot_image.0,
511 vendor_boot_image.0,
512 init_boot_image.0,
513 &ramdisk.0,
514 kernel_load_buffer,
515 ]) {
516 return Err(IntegrationError::GblNativeError(Error::BufferOverlap));
517 }
518
519 let info_struct = self.unpack_boot_image(&boot_image, Some(boot_target))?;
520
521 let kernel_image = self.kernel_load(&info_struct, boot_image, kernel_load_buffer)?;
522
523 let cmd_line = self.ramdisk_bootconfig_load(
524 &info_struct,
525 &vendor_boot_image,
526 &init_boot_image,
527 ramdisk,
528 )?;
529
530 self.dtb_update_and_load(&info_struct, vendor_boot_image)?;
531
532 let token = slot_cursor.ctx.mark_boot_attempt().map_err(|_| Error::OperationProhibited)?;
533
534 Ok((kernel_image, token))
535 }
536
537 /// Loads and boots a Zircon kernel according to ABR + AVB.
zircon_load_and_boot(&mut self, load_buffer: &mut [u8]) -> Result<()>538 pub fn zircon_load_and_boot(&mut self, load_buffer: &mut [u8]) -> Result<()> {
539 let (mut block_devices, load_buffer) = GblUtils::new(self.ops, load_buffer)?;
540 block_devices.sync_gpt_all(&mut |_, _, _| {});
541 // TODO(b/334962583): Implement zircon ABR + AVB.
542 // The following are place holder for test of invocation in the integration test only.
543 let ptn_size = block_devices
544 .find_partition("zircon_a")?
545 .size()
546 .map_err(|e: gbl_storage::StorageError| IntegrationError::StorageError(e))?
547 .try_into()
548 .or(Err(Error::ArithmeticOverflow))?;
549 let (kernel, remains) = load_buffer.split_at_mut(ptn_size);
550 block_devices.read_gpt_partition("zircon_a", 0, kernel)?;
551 self.ops.boot(BootImages::Fuchsia(FuchsiaBootImages {
552 zbi_kernel: kernel,
553 zbi_items: &mut [],
554 }))?;
555 Err(Error::BootFailed.into())
556 }
557 }
558
559 /// Builder for GBL object
560 #[derive(Debug)]
561 pub struct GblBuilder<'a, G>
562 where
563 G: GblOps,
564 {
565 ops: &'a mut G,
566 verify_slot: AvbVerifySlot,
567 }
568
569 impl<'a, G> GblBuilder<'a, G>
570 where
571 G: GblOps,
572 {
573 /// Start Gbl object creation, with default GblOps implementation
new(ops: &'a mut G) -> Self574 pub fn new(ops: &'a mut G) -> Self {
575 GblBuilder { ops, verify_slot: avb::slot_verify }
576 }
577
578 // Override [avb::slot_verify] for testing only
579 #[cfg(test)]
verify_slot(mut self, verify_slot: AvbVerifySlot) -> Self580 fn verify_slot(mut self, verify_slot: AvbVerifySlot) -> Self {
581 self.verify_slot = verify_slot;
582 self
583 }
584
585 /// Finish Gbl object construction and return it as the result
build(self) -> Gbl<'a, G>586 pub fn build(self) -> Gbl<'a, G> {
587 Gbl { ops: self.ops, verify_slot: self.verify_slot }
588 }
589 }
590
591 #[cfg(test)]
592 mod tests {
593 extern crate avb_sysdeps;
594 extern crate avb_test;
595 use super::*;
596 use avb::IoError;
597 use avb::IoResult as AvbIoResult;
598 use avb::PublicKeyForPartitionInfo;
599 use avb_test::{FakeVbmetaKey, TestOps};
600 use std::{fs, path::Path};
601
602 struct AvbOpsUnimplemented {}
603 impl avb::Ops<'_> for AvbOpsUnimplemented {
validate_vbmeta_public_key(&mut self, _: &[u8], _: Option<&[u8]>) -> AvbIoResult<bool>604 fn validate_vbmeta_public_key(&mut self, _: &[u8], _: Option<&[u8]>) -> AvbIoResult<bool> {
605 Err(IoError::NotImplemented)
606 }
read_from_partition(&mut self, _: &CStr, _: i64, _: &mut [u8]) -> AvbIoResult<usize>607 fn read_from_partition(&mut self, _: &CStr, _: i64, _: &mut [u8]) -> AvbIoResult<usize> {
608 Err(IoError::NotImplemented)
609 }
read_rollback_index(&mut self, _: usize) -> AvbIoResult<u64>610 fn read_rollback_index(&mut self, _: usize) -> AvbIoResult<u64> {
611 Err(IoError::NotImplemented)
612 }
write_rollback_index(&mut self, _: usize, _: u64) -> AvbIoResult<()>613 fn write_rollback_index(&mut self, _: usize, _: u64) -> AvbIoResult<()> {
614 Err(IoError::NotImplemented)
615 }
read_is_device_unlocked(&mut self) -> AvbIoResult<bool>616 fn read_is_device_unlocked(&mut self) -> AvbIoResult<bool> {
617 Err(IoError::NotImplemented)
618 }
619 #[cfg(feature = "uuid")]
get_unique_guid_for_partition(&mut self, partition: &CStr) -> AvbIoResult<uuid::Uuid>620 fn get_unique_guid_for_partition(&mut self, partition: &CStr) -> AvbIoResult<uuid::Uuid> {
621 Err(IoError::NotImplemented)
622 }
get_size_of_partition(&mut self, partition: &CStr) -> AvbIoResult<u64>623 fn get_size_of_partition(&mut self, partition: &CStr) -> AvbIoResult<u64> {
624 Err(IoError::NotImplemented)
625 }
read_persistent_value(&mut self, name: &CStr, value: &mut [u8]) -> AvbIoResult<usize>626 fn read_persistent_value(&mut self, name: &CStr, value: &mut [u8]) -> AvbIoResult<usize> {
627 Err(IoError::NotImplemented)
628 }
write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> AvbIoResult<()>629 fn write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> AvbIoResult<()> {
630 Err(IoError::NotImplemented)
631 }
erase_persistent_value(&mut self, name: &CStr) -> AvbIoResult<()>632 fn erase_persistent_value(&mut self, name: &CStr) -> AvbIoResult<()> {
633 Err(IoError::NotImplemented)
634 }
validate_public_key_for_partition( &mut self, partition: &CStr, public_key: &[u8], public_key_metadata: Option<&[u8]>, ) -> AvbIoResult<PublicKeyForPartitionInfo>635 fn validate_public_key_for_partition(
636 &mut self,
637 partition: &CStr,
638 public_key: &[u8],
639 public_key_metadata: Option<&[u8]>,
640 ) -> AvbIoResult<PublicKeyForPartitionInfo> {
641 Err(IoError::NotImplemented)
642 }
643 }
644
645 #[test]
test_load_and_verify_image_avb_io_error()646 fn test_load_and_verify_image_avb_io_error() {
647 let mut gbl_ops = DefaultGblOps {};
648 let mut gbl = GblBuilder::new(&mut gbl_ops).build();
649 let mut avb_ops = AvbOpsUnimplemented {};
650 let mut partitions_ram_map: [PartitionRamMap; 0] = [];
651 let res = gbl.load_and_verify_image(
652 &mut avb_ops,
653 &mut partitions_ram_map,
654 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
655 None,
656 );
657 assert_eq!(res.unwrap_err(), IntegrationError::AvbSlotVerifyError(SlotVerifyError::Io));
658 }
659
660 const TEST_ZIRCON_PARTITION_NAME: &str = "zircon_a";
661 const TEST_ZIRCON_IMAGE_PATH: &str = "zircon_a.bin";
662 const TEST_ZIRCON_VBMETA_PATH: &str = "zircon_a.vbmeta";
663 const TEST_PUBLIC_KEY_PATH: &str = "testkey_rsa4096_pub.bin";
664 const TEST_VBMETA_ROLLBACK_LOCATION: usize = 0; // Default value, we don't explicitly set this.
665
666 /// Returns the contents of a test data file.
667 ///
668 /// Panicks if the requested file cannot be read.
669 ///
670 /// # Arguments
671 /// * `path`: file path relative to libgbl's `testdata/` directory.
testdata(path: &str) -> Vec<u8>672 fn testdata(path: &str) -> Vec<u8> {
673 let full_path = Path::new("external/gbl/libgbl/testdata").join(path);
674 fs::read(full_path).unwrap()
675 }
676
677 #[test]
test_load_and_verify_image_stub()678 fn test_load_and_verify_image_stub() {
679 let mut gbl_ops = DefaultGblOps {};
680 let mut gbl = GblBuilder::new(&mut gbl_ops).build();
681 let mut avb_ops = TestOps::default();
682
683 avb_ops.add_partition(TEST_ZIRCON_PARTITION_NAME, testdata(TEST_ZIRCON_IMAGE_PATH));
684 avb_ops.add_partition("vbmeta", testdata(TEST_ZIRCON_VBMETA_PATH));
685 avb_ops.default_vbmeta_key = Some(FakeVbmetaKey::Avb {
686 public_key: testdata(TEST_PUBLIC_KEY_PATH),
687 public_key_metadata: None,
688 });
689 avb_ops.rollbacks.insert(TEST_VBMETA_ROLLBACK_LOCATION, 0);
690 avb_ops.unlock_state = Ok(false);
691
692 let mut partitions_ram_map: [PartitionRamMap; 0] = [];
693 let res = gbl.load_and_verify_image(
694 &mut avb_ops,
695 &mut partitions_ram_map,
696 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
697 None,
698 );
699 assert!(res.is_ok());
700 }
701
702 #[test]
test_load_and_verify_image_avb_error()703 fn test_load_and_verify_image_avb_error() {
704 const TEST_ERROR: SlotVerifyError<'static> = SlotVerifyError::Verification(None);
705 let expected_error = SlotVerifyError::Verification(None);
706 let mut gbl_ops = DefaultGblOps {};
707 let mut gbl =
708 GblBuilder::new(&mut gbl_ops).verify_slot(|_, _, _, _, _| Err(TEST_ERROR)).build();
709 let mut avb_ops = AvbOpsUnimplemented {};
710 let mut partitions_ram_map: [PartitionRamMap; 0] = [];
711 let res = gbl.load_and_verify_image(
712 &mut avb_ops,
713 &mut partitions_ram_map,
714 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
715 None,
716 );
717 assert_eq!(res.unwrap_err(), IntegrationError::AvbSlotVerifyError(TEST_ERROR));
718 }
719 }
720