vck_loader/chainload.rs
1// SPDX-FileCopyrightText: 2026 JC-Lab <joseph@jc-lab.net>
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! Chainloading the next EFI image.
6//!
7//! Loads and starts the next OS loader (e.g. the Windows Boot Manager,
8//! `msbootmgfw.os.efi`) using `LoadImage` / `StartImage`. See docs/architecture.md boot flow
9//! step 5.
10
11use alloc::format;
12
13use uefi::boot::{image_handle, load_image, start_image, LoadImageSource};
14use uefi::proto::BootPolicy;
15use vck_common::{VckError, VckResult};
16
17use crate::provider::DevicePath;
18
19/// Loads the EFI image at `next_loader` and transfers control to it.
20///
21/// On success this normally does not return (the next loader takes over);
22/// if `StartImage` returns, the started image exited and control comes back.
23///
24/// Block IO hooks must remain installed across the chainload so the next loader
25/// reads the OS volume transparently decrypted, but the loader's own resources
26/// should otherwise be cleaned up before handing off.
27pub fn chainload_next(next_loader: &DevicePath) -> VckResult<()> {
28 let image = load_image(
29 image_handle(),
30 LoadImageSource::FromDevicePath {
31 // `next_loader` is our owned `Box<DevicePath>`; deref to the borrowed
32 // `&uefi::proto::device_path::DevicePath` the FFI source expects.
33 device_path: next_loader,
34 boot_policy: BootPolicy::ExactMatch,
35 },
36 )
37 .map_err(|e| VckError::Io(format!("LoadImage(next loader) failed: {e:?}")))?;
38
39 start_image(image)
40 .map_err(|e| VckError::Io(format!("StartImage(next loader) failed: {e:?}")))?;
41 Ok(())
42}