use alloc::string::String;
use log::error;
use thiserror::Error;
use uefi::{
CStr16, boot,
proto::{device_path::DevicePath, loaded_image::LoadedImage, media::file::FileInfo},
};
use crate::{
BootResult,
boot::secure_boot::shim::shim_load_image,
system::{
fs::UefiFileSystem,
helper::{get_path_cstr, join_to_device_path, str_to_cstr},
},
};
#[derive(Error, Debug)]
pub enum DriverError {
#[error("Unsupported EFI file: \"{0}\"")]
Unsupported(String),
}
fn load_driver(driver_path: &CStr16, file: &FileInfo, buf: &mut [u8]) -> BootResult<()> {
let handle_path = boot::open_protocol_exclusive::<DevicePath>(boot::image_handle())?;
let path_cstr = get_path_cstr(driver_path, file.file_name())?;
let path = join_to_device_path(&handle_path, &path_cstr, buf)?;
let src = boot::LoadImageSource::FromDevicePath {
device_path: &path,
boot_policy: uefi::proto::BootPolicy::ExactMatch,
};
let handle = shim_load_image(boot::image_handle(), src)?;
let image = boot::open_protocol_exclusive::<LoadedImage>(handle)?;
if image.code_type() != boot::MemoryType::BOOT_SERVICES_CODE
&& image.code_type() != boot::MemoryType::RUNTIME_SERVICES_CODE
{
return Err(DriverError::Unsupported(file.file_name().into()).into());
}
Ok(boot::start_image(handle)?)
}
pub(crate) fn load_drivers(driver_path: &str) -> BootResult<()> {
let driver_path = str_to_cstr(driver_path)?;
let mut fs = UefiFileSystem::from_image_fs()?;
let dir = fs.read_filtered_dir(&driver_path, ".efi");
let mut buf = [0; 2048];
let mut driver_loaded = false;
for file in dir {
if let Err(e) = load_driver(&driver_path, &file, &mut buf) {
error!("Failed to load driver {}: {e}", file.file_name());
} else {
driver_loaded = true;
}
}
if driver_loaded {
reconnect_drivers()?; }
Ok(())
}
fn reconnect_drivers() -> BootResult<()> {
let handles = boot::locate_handle_buffer(boot::SearchType::AllHandles)?;
for handle in handles.iter() {
let _ = boot::connect_controller(*handle, None, None, true);
}
Ok(())
}