add efi shell so that chainloading multiple items can be tested

This commit is contained in:
2025-10-02 00:24:19 -07:00
parent b8e1d11bed
commit 6bf745946e
8 changed files with 78 additions and 31 deletions

View File

@@ -16,6 +16,8 @@ pub struct ModuleConfiguration {
#[derive(Serialize, Deserialize, Default)]
pub struct ChainloaderConfiguration {
pub path: String,
#[serde(default)]
pub options: Vec<String>,
}
pub fn load() -> RootConfiguration {

View File

@@ -1,10 +1,8 @@
use crate::config::ChainloaderConfiguration;
use crate::utils::text_to_device_path;
use crate::utils;
use log::info;
use uefi::proto::device_path::{
DevicePath, LoadedImageDevicePath,
text::{AllowShortcuts, DisplayOnly},
};
use uefi::CString16;
use uefi::proto::device_path::LoadedImageDevicePath;
use uefi::proto::loaded_image::LoadedImage;
pub fn chainloader(configuration: ChainloaderConfiguration) {
@@ -13,28 +11,12 @@ pub fn chainloader(configuration: ChainloaderConfiguration) {
uefi::boot::open_protocol_exclusive::<LoadedImageDevicePath>(sprout_image)
.expect("unable to open loaded image device path protocol");
let image_device_path: &DevicePath = &image_device_path_protocol;
let mut full_path = image_device_path
.node_iter()
.filter_map(|item| {
let item = item
.to_string(DisplayOnly(false), AllowShortcuts(false))
.expect("unable to convert device path to string");
if item.to_string().contains("(") {
Some(item)
} else {
None
}
})
.map(|item| item.to_string())
.collect::<Vec<_>>()
.join("/");
full_path.push('/');
let mut full_path = utils::device_path_root(&image_device_path_protocol);
full_path.push_str(&configuration.path);
info!("path={}", full_path);
let device_path = text_to_device_path(&full_path);
let device_path = utils::text_to_device_path(&full_path);
let image = uefi::boot::load_image(
sprout_image,
@@ -45,10 +27,31 @@ pub fn chainloader(configuration: ChainloaderConfiguration) {
)
.expect("failed to load image");
let image_device_path_protocol = uefi::boot::open_protocol_exclusive::<LoadedImage>(image)
let mut loaded_image_protocol = uefi::boot::open_protocol_exclusive::<LoadedImage>(image)
.expect("unable to open loaded image protocol");
let (base, size) = image_device_path_protocol.info();
let options = configuration.options.join(" ");
if !options.is_empty() {
let options = Box::new(
CString16::try_from(&options[..])
.expect("unable to convert chainloader options to CString16"),
);
info!("options={}", options);
if options.num_bytes() > u32::MAX as usize {
panic!("chainloader options too large");
}
// SAFETY: options size is checked to validate it is safe to pass.
// Additionally, the pointer is allocated and retained on the heap which makes
// passing the options pointer safe to the next image.
unsafe {
loaded_image_protocol
.set_load_options(options.as_ptr() as *const u8, options.num_bytes() as u32);
}
}
let (base, size) = loaded_image_protocol.info();
info!("loaded image base={:#x} size={:#x}", base.addr(), size);
uefi::boot::start_image(image).expect("failed to start image");
}

View File

@@ -1,7 +1,7 @@
use uefi::CString16;
use uefi::fs::{FileSystem, Path};
use uefi::proto::device_path::PoolDevicePath;
use uefi::proto::device_path::text::DevicePathFromText;
use uefi::proto::device_path::text::{AllowShortcuts, DevicePathFromText, DisplayOnly};
use uefi::proto::device_path::{DevicePath, PoolDevicePath};
use uefi::proto::media::fs::SimpleFileSystem;
pub fn text_to_device_path(path: &str) -> PoolDevicePath {
@@ -17,6 +17,26 @@ pub fn text_to_device_path(path: &str) -> PoolDevicePath {
.expect("unable to convert text to device path")
}
pub fn device_path_root(path: &DevicePath) -> String {
let mut path = path
.node_iter()
.filter_map(|item| {
let item = item
.to_string(DisplayOnly(false), AllowShortcuts(false))
.expect("unable to convert device path to string");
if item.to_string().contains("(") {
Some(item)
} else {
None
}
})
.map(|item| item.to_string())
.collect::<Vec<_>>()
.join("/");
path.push('/');
path
}
pub fn read_file_contents(path: &str) -> Vec<u8> {
let fs = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(
uefi::boot::get_handle_for_protocol::<SimpleFileSystem>().expect("no filesystem protocol"),