2025-10-12 22:39:56 -07:00
|
|
|
use crate::context::SproutContext;
|
2025-10-30 21:38:49 -04:00
|
|
|
use crate::integrations::shim::{ShimInput, ShimSupport};
|
2025-10-12 22:39:56 -07:00
|
|
|
use crate::utils;
|
|
|
|
|
use anyhow::{Context, Result};
|
2025-10-27 00:20:42 -04:00
|
|
|
use log::info;
|
2025-10-12 22:39:56 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
use uefi::boot::SearchType;
|
|
|
|
|
|
2025-10-19 21:44:05 -07:00
|
|
|
/// Declares a driver configuration.
|
|
|
|
|
/// Drivers allow extending the functionality of Sprout.
|
|
|
|
|
/// Drivers are loaded at runtime and can provide extra functionality like filesystem support.
|
|
|
|
|
/// Drivers are loaded by their name, which is used to reference them in other concepts.
|
2025-10-27 03:37:09 -04:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
2025-10-12 22:39:56 -07:00
|
|
|
pub struct DriverDeclaration {
|
2025-10-19 21:44:05 -07:00
|
|
|
/// The filesystem path to the driver.
|
|
|
|
|
/// This file should be an EFI executable that can be located and executed.
|
2025-10-12 22:39:56 -07:00
|
|
|
pub path: String,
|
|
|
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
|
let resolved = utils::resolve_path(
|
2025-10-30 22:56:01 -04:00
|
|
|
Some(context.root().loaded_image_path()?),
|
2025-10-30 21:38:49 -04: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")?;
|
|
|
|
|
|
|
|
|
|
// Load the driver image using the shim support integration.
|
|
|
|
|
// It will determine if the image needs to be loaded via the shim or can be loaded directly.
|
|
|
|
|
let image = ShimSupport::load(sprout_image, ShimInput::ResolvedPath(&resolved))?;
|
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-10-14 12:47:33 -07:00
|
|
|
uefi::boot::start_image(image).context("unable to start driver image")?;
|
2025-10-12 22:39:56 -07:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-19 23:03:28 -07:00
|
|
|
/// Reconnects all handles to their controllers.
|
|
|
|
|
/// This is effectively a UEFI stack reload in a sense.
|
|
|
|
|
/// After we load all the drivers, we need to reconnect all of their handles
|
|
|
|
|
/// so that filesystems are recognized again.
|
2025-10-12 22:39:56 -07:00
|
|
|
fn reconnect() -> Result<()> {
|
2025-10-19 23:03:28 -07:00
|
|
|
// Locate all of the handles in the UEFI stack.
|
2025-10-12 22:39:56 -07:00
|
|
|
let handles = uefi::boot::locate_handle_buffer(SearchType::AllHandles)
|
2025-10-14 12:47:33 -07:00
|
|
|
.context("unable to locate handles buffer")?;
|
2025-10-12 22:39:56 -07:00
|
|
|
|
|
|
|
|
for handle in handles.iter() {
|
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.
|
2025-10-12 22:39:56 -07:00
|
|
|
let _ = uefi::boot::connect_controller(*handle, None, None, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-19 23:03:28 -07:00
|
|
|
/// Load all the drivers specified in `drivers`.
|
|
|
|
|
/// There is no driver order currently. This will reconnect all the controllers
|
|
|
|
|
/// to all handles if at least one driver was loaded.
|
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
|
|
|
}
|
|
|
|
|
|
2025-10-19 23:03:28 -07:00
|
|
|
// Reconnect all the controllers to all handles.
|
2025-10-14 12:47:33 -07:00
|
|
|
reconnect().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(())
|
|
|
|
|
}
|