use core::cell::RefCell;
use crate::{
BootResult,
boot::{
devicetree::install_devicetree,
loader::{LoadError, get_efi},
secure_boot::shim::shim_load_image,
},
config::Config,
system::{
fs::UefiFileSystem,
helper::{join_to_device_path, str_to_cstr},
},
};
use uefi::{
CStr16, CString16, Handle,
boot::{self, ScopedProtocol},
proto::{device_path::DevicePath, loaded_image::LoadedImage},
};
static LOAD_OPTIONS: LoadOptions = LoadOptions {
options: RefCell::new(None),
};
struct LoadOptions {
options: RefCell<Option<CString16>>,
}
impl LoadOptions {
fn set(&self, s: &CStr16) {
let mut options = self.options.borrow_mut();
*options = Some(s.into());
}
fn get(&self) -> Option<*const u8> {
self.options
.borrow()
.as_ref()
.map(|x| x.as_ptr().cast::<u8>())
}
fn size(&self) -> usize {
self.options.borrow().as_ref().map_or(0, |x| x.num_bytes())
}
fn set_load_options(&self, image: &mut ScopedProtocol<LoadedImage>) {
if let Some(ptr) = self.get() {
let size = u32::try_from(self.size()).unwrap_or(0);
unsafe {
image.set_load_options(ptr, size);
}
}
}
}
unsafe impl Sync for LoadOptions {}
pub(crate) fn load_boot_option(config: &Config) -> BootResult<Handle> {
let handle = *config
.fs_handle
.ok_or_else(|| LoadError::ConfigMissingHandle(config.filename.clone()))?;
let mut fs = UefiFileSystem::from_handle(handle)?;
let file = get_efi(config)?;
let s = str_to_cstr(file)?;
let handle = load_image_from_path(handle, &s)?;
setup_image(&mut fs, handle, config)
}
fn load_image_from_path(handle: Handle, path: &CStr16) -> BootResult<Handle> {
let dev_path = boot::open_protocol_exclusive::<DevicePath>(handle)?;
let mut buf = [0; 2048]; let path = join_to_device_path(&dev_path, path, &mut buf)?;
let src = boot::LoadImageSource::FromDevicePath {
device_path: &path,
boot_policy: uefi::proto::BootPolicy::BootSelection,
};
shim_load_image(boot::image_handle(), src) }
fn setup_image(fs: &mut UefiFileSystem, handle: Handle, config: &Config) -> BootResult<Handle> {
if let Some(devicetree) = &config.devicetree_path {
install_devicetree(devicetree, fs)?;
}
let mut image = boot::open_protocol_exclusive::<LoadedImage>(handle)?;
if let Some(options) = config.options.as_deref() {
let load_options = &LOAD_OPTIONS;
load_options.set(&str_to_cstr(options)?);
load_options.set_load_options(&mut image);
}
Ok(handle)
}