2025-10-12 22:39:56 -07:00
|
|
|
use crate::context::SproutContext;
|
2025-11-03 02:04:21 -05:00
|
|
|
use alloc::collections::BTreeMap;
|
|
|
|
|
use alloc::format;
|
|
|
|
|
use alloc::rc::Rc;
|
|
|
|
|
use alloc::string::String;
|
2026-10-16 12:33:35 -07:00
|
|
|
use alloc::vec::Vec;
|
2025-10-12 22:39:56 -07:00
|
|
|
use anyhow::{Context, Result};
|
2026-10-16 12:33:35 -07:00
|
|
|
use edera_sprout_config::drivers::{DriverDeclaration, DriverProvides};
|
2025-11-06 11:52:00 -05:00
|
|
|
use eficore::loader::source::ImageSource;
|
|
|
|
|
use eficore::loader::{ImageLoadRequest, ImageLoader};
|
2025-10-27 00:20:42 -04:00
|
|
|
use log::info;
|
2025-10-12 22:39:56 -07:00
|
|
|
use uefi::boot::SearchType;
|
2026-10-16 12:33:35 -07:00
|
|
|
use uefi::proto::media::block::BlockIO;
|
|
|
|
|
use uefi::proto::media::disk::{DiskIo, DiskIo2};
|
|
|
|
|
use uefi::{Handle, Identify};
|
2025-10-12 22:39:56 -07:00
|
|
|
|
2025-10-24 15:54:58 -07:00
|
|
|
/// Loads the driver specified by the `driver` declaration.
|
2025-10-12 22:39:56 -07:00
|
|
|
fn load_driver(context: Rc<SproutContext>, driver: &DriverDeclaration) -> Result<()> {
|
2025-10-19 23:03:28 -07:00
|
|
|
// Acquire the handle and device path of the loaded image.
|
2025-10-12 22:39:56 -07:00
|
|
|
let sprout_image = uefi::boot::image_handle();
|
|
|
|
|
|
2025-10-30 21:38:49 -04:00
|
|
|
// Resolve the path to the driver image.
|
2025-11-03 20:47:21 -05:00
|
|
|
let resolved = eficore::path::resolve_path(
|
2025-10-30 22:56:01 -04:00
|
|
|
Some(context.root().loaded_image_path()?),
|
2026-01-26 20:13:52 -08:00
|
|
|
context.stamp(&driver.path),
|
2025-10-12 22:39:56 -07:00
|
|
|
)
|
2025-10-30 21:38:49 -04:00
|
|
|
.context("unable to resolve path to driver")?;
|
|
|
|
|
|
2025-11-06 11:52:00 -05:00
|
|
|
// Create an image load request with the current image and the resolved path.
|
|
|
|
|
let request = ImageLoadRequest::new(sprout_image, ImageSource::ResolvedPath(&resolved));
|
|
|
|
|
|
|
|
|
|
// Load the driver image using the image loader support module.
|
2025-10-30 21:38:49 -04:00
|
|
|
// It will determine if the image needs to be loaded via the shim or can be loaded directly.
|
2025-11-06 11:52:00 -05:00
|
|
|
let image = ImageLoader::load(request)?;
|
2025-10-12 22:39:56 -07:00
|
|
|
|
2025-10-19 23:03:28 -07:00
|
|
|
// Start the driver image, this is expected to return control to sprout.
|
|
|
|
|
// There is no guarantee that the driver will actually return control as it is
|
|
|
|
|
// just a standard EFI image.
|
2025-11-06 11:52:00 -05:00
|
|
|
uefi::boot::start_image(*image.handle()).context("unable to start driver image")?;
|
2025-10-12 22:39:56 -07:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-16 12:33:35 -07:00
|
|
|
/// Locates the handles that are disks or partitions.
|
|
|
|
|
/// These are the only controllers that filesystem drivers can bind to.
|
|
|
|
|
fn locate_storage_handles() -> Result<Vec<Handle>> {
|
|
|
|
|
let mut storage = Vec::new();
|
|
|
|
|
for guid in [&BlockIO::GUID, &DiskIo::GUID, &DiskIo2::GUID] {
|
|
|
|
|
// Not finding any handles for a protocol is not an error.
|
|
|
|
|
let Ok(handles) = uefi::boot::locate_handle_buffer(SearchType::ByProtocol(guid)) else {
|
|
|
|
|
continue;
|
|
|
|
|
};
|
|
|
|
|
for handle in handles.iter() {
|
|
|
|
|
if !storage.contains(handle) {
|
|
|
|
|
storage.push(*handle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(storage)
|
|
|
|
|
}
|
2025-10-12 22:39:56 -07:00
|
|
|
|
2026-10-16 12:33:35 -07:00
|
|
|
/// Reconnects handles to their controllers.
|
|
|
|
|
/// This is effectively a UEFI stack reload in a sense.
|
|
|
|
|
/// After we load all the drivers, we need to reconnect their handles
|
|
|
|
|
/// so that filesystems are recognized again.
|
|
|
|
|
/// When `provides` is [`DriverProvides::Filesystems`], only disk and partition handles are reconnected.
|
|
|
|
|
fn reconnect(provides: DriverProvides) -> Result<()> {
|
|
|
|
|
// Locate the handles to reconnect in the UEFI stack.
|
|
|
|
|
let handles = match provides {
|
|
|
|
|
DriverProvides::Any => uefi::boot::locate_handle_buffer(SearchType::AllHandles)
|
|
|
|
|
.context("unable to locate handles buffer")?
|
|
|
|
|
.iter()
|
|
|
|
|
.copied()
|
|
|
|
|
.collect(),
|
|
|
|
|
DriverProvides::Filesystems => locate_storage_handles()?,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for handle in handles {
|
2025-10-19 23:03:28 -07:00
|
|
|
// Ignore the result as there is nothing we can do if reconnecting a controller fails.
|
|
|
|
|
// This is also likely to fail in some cases but should fail safely.
|
2026-10-16 12:33:35 -07:00
|
|
|
let _ = uefi::boot::connect_controller(handle, &[], None, true);
|
2025-10-12 22:39:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-19 23:03:28 -07:00
|
|
|
/// Load all the drivers specified in `drivers`.
|
2026-10-16 12:33:35 -07:00
|
|
|
/// There is no driver order currently. This will reconnect the controllers
|
|
|
|
|
/// if at least one driver was loaded. Every controller is reconnected unless all
|
|
|
|
|
/// the drivers only provide filesystems, in which case only storage is reconnected.
|
2025-10-12 22:39:56 -07:00
|
|
|
pub fn load(
|
|
|
|
|
context: Rc<SproutContext>,
|
|
|
|
|
drivers: &BTreeMap<String, DriverDeclaration>,
|
|
|
|
|
) -> Result<()> {
|
2025-10-19 23:03:28 -07:00
|
|
|
// If there are no drivers, we don't need to do anything.
|
2025-10-12 22:39:56 -07:00
|
|
|
if drivers.is_empty() {
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-27 00:20:42 -04:00
|
|
|
info!("loading drivers");
|
2025-10-19 23:03:28 -07:00
|
|
|
|
|
|
|
|
// Load all the drivers in no particular order.
|
2025-10-12 22:39:56 -07:00
|
|
|
for (name, driver) in drivers {
|
2025-10-14 12:47:33 -07:00
|
|
|
load_driver(context.clone(), driver).context(format!("unable to load driver: {}", name))?;
|
2025-10-12 22:39:56 -07:00
|
|
|
}
|
|
|
|
|
|
2026-10-16 12:33:35 -07:00
|
|
|
// Reconnect everything if any driver can provide anything, otherwise only storage.
|
|
|
|
|
let provides = if drivers
|
|
|
|
|
.values()
|
|
|
|
|
.all(|driver| driver.provides == DriverProvides::Filesystems)
|
|
|
|
|
{
|
|
|
|
|
DriverProvides::Filesystems
|
|
|
|
|
} else {
|
|
|
|
|
DriverProvides::Any
|
|
|
|
|
};
|
|
|
|
|
reconnect(provides).context("unable to reconnect drivers")?;
|
2025-10-27 00:20:42 -04:00
|
|
|
info!("loaded drivers");
|
2025-10-19 23:03:28 -07:00
|
|
|
|
|
|
|
|
// We've now loaded all the drivers, so we can return.
|
2025-10-12 22:39:56 -07:00
|
|
|
Ok(())
|
|
|
|
|
}
|