mirror of
https://github.com/edera-dev/sprout.git
synced 2026-11-04 19:52:22 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed226afb7e
|
||
|
|
6cdf5362a4
|
+81
-12
@@ -2,20 +2,20 @@ use crate::context::SproutContext;
|
|||||||
use alloc::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
use alloc::string::String;
|
use alloc::string::{String, ToString};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use edera_sprout_config::drivers::{DriverDeclaration, DriverProvides};
|
use edera_sprout_config::drivers::{DriverDeclaration, DriverProvides};
|
||||||
use eficore::loader::source::ImageSource;
|
use eficore::loader::source::ImageSource;
|
||||||
use eficore::loader::{ImageLoadRequest, ImageLoader};
|
use eficore::loader::{ImageLoadRequest, ImageLoader};
|
||||||
use log::info;
|
use log::info;
|
||||||
use uefi::boot::SearchType;
|
use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, SearchType};
|
||||||
use uefi::proto::media::block::BlockIO;
|
use uefi::proto::media::block::BlockIO;
|
||||||
use uefi::proto::media::disk::{DiskIo, DiskIo2};
|
use uefi::proto::media::disk::{DiskIo, DiskIo2};
|
||||||
use uefi::{Handle, Identify};
|
use uefi::{Handle, Identify};
|
||||||
|
|
||||||
/// Loads the driver specified by the `driver` declaration.
|
/// Loads the driver specified by the `driver` declaration.
|
||||||
fn load_driver(context: Rc<SproutContext>, driver: &DriverDeclaration) -> Result<()> {
|
fn load_driver(context: Rc<SproutContext>, driver: &DriverDeclaration) -> Result<Handle> {
|
||||||
// Acquire the handle and device path of the loaded image.
|
// Acquire the handle and device path of the loaded image.
|
||||||
let sprout_image = uefi::boot::image_handle();
|
let sprout_image = uefi::boot::image_handle();
|
||||||
|
|
||||||
@@ -36,13 +36,31 @@ fn load_driver(context: Rc<SproutContext>, driver: &DriverDeclaration) -> Result
|
|||||||
// Start the driver image, this is expected to return control to sprout.
|
// 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
|
// There is no guarantee that the driver will actually return control as it is
|
||||||
// just a standard EFI image.
|
// just a standard EFI image.
|
||||||
|
info!("starting driver image");
|
||||||
uefi::boot::start_image(*image.handle()).context("unable to start driver image")?;
|
uefi::boot::start_image(*image.handle()).context("unable to start driver image")?;
|
||||||
|
info!("driver image returned");
|
||||||
|
|
||||||
Ok(())
|
Ok(*image.handle())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether the handle is a logical partition rather than a whole disk.
|
||||||
|
fn is_partition(handle: Handle) -> bool {
|
||||||
|
let params = OpenProtocolParams {
|
||||||
|
handle,
|
||||||
|
agent: uefi::boot::image_handle(),
|
||||||
|
controller: None,
|
||||||
|
};
|
||||||
|
// Opening with GetProtocol does not disturb the existing protocol users.
|
||||||
|
let block = unsafe {
|
||||||
|
uefi::boot::open_protocol::<BlockIO>(params, OpenProtocolAttributes::GetProtocol)
|
||||||
|
};
|
||||||
|
block.is_ok_and(|block| block.media().is_logical_partition())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Locates the handles that are disks or partitions.
|
/// Locates the handles that are disks or partitions.
|
||||||
/// These are the only controllers that filesystem drivers can bind to.
|
/// Filesystem drivers bind to partitions, so whole disks are only returned when
|
||||||
|
/// there are no partitions. Reconnecting a whole disk makes the firmware rebuild
|
||||||
|
/// its partition handles, which hangs on some NVMe firmware.
|
||||||
fn locate_storage_handles() -> Result<Vec<Handle>> {
|
fn locate_storage_handles() -> Result<Vec<Handle>> {
|
||||||
let mut storage = Vec::new();
|
let mut storage = Vec::new();
|
||||||
for guid in [&BlockIO::GUID, &DiskIo::GUID, &DiskIo2::GUID] {
|
for guid in [&BlockIO::GUID, &DiskIo::GUID, &DiskIo2::GUID] {
|
||||||
@@ -56,7 +74,32 @@ fn locate_storage_handles() -> Result<Vec<Handle>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(storage)
|
let partitions: Vec<Handle> = storage
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|handle| is_partition(*handle))
|
||||||
|
.collect();
|
||||||
|
if partitions.is_empty() {
|
||||||
|
return Ok(storage);
|
||||||
|
}
|
||||||
|
Ok(partitions)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes a handle by its device path, for diagnosing hangs.
|
||||||
|
fn describe(handle: Handle) -> String {
|
||||||
|
use uefi::proto::device_path::DevicePath;
|
||||||
|
|
||||||
|
let params = OpenProtocolParams {
|
||||||
|
handle,
|
||||||
|
agent: uefi::boot::image_handle(),
|
||||||
|
controller: None,
|
||||||
|
};
|
||||||
|
match unsafe {
|
||||||
|
uefi::boot::open_protocol::<DevicePath>(params, OpenProtocolAttributes::GetProtocol)
|
||||||
|
} {
|
||||||
|
Ok(path) => path.to_string(),
|
||||||
|
Err(_) => String::from("<no device path>"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reconnects handles to their controllers.
|
/// Reconnects handles to their controllers.
|
||||||
@@ -64,7 +107,7 @@ fn locate_storage_handles() -> Result<Vec<Handle>> {
|
|||||||
/// After we load all the drivers, we need to reconnect their handles
|
/// After we load all the drivers, we need to reconnect their handles
|
||||||
/// so that filesystems are recognized again.
|
/// so that filesystems are recognized again.
|
||||||
/// When `provides` is [`DriverProvides::Filesystems`], only disk and partition handles are reconnected.
|
/// When `provides` is [`DriverProvides::Filesystems`], only disk and partition handles are reconnected.
|
||||||
fn reconnect(provides: DriverProvides) -> Result<()> {
|
fn reconnect(provides: DriverProvides, drivers: &[Handle]) -> Result<()> {
|
||||||
// Locate the handles to reconnect in the UEFI stack.
|
// Locate the handles to reconnect in the UEFI stack.
|
||||||
let handles = match provides {
|
let handles = match provides {
|
||||||
DriverProvides::Any => uefi::boot::locate_handle_buffer(SearchType::AllHandles)
|
DriverProvides::Any => uefi::boot::locate_handle_buffer(SearchType::AllHandles)
|
||||||
@@ -72,13 +115,35 @@ fn reconnect(provides: DriverProvides) -> Result<()> {
|
|||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.collect(),
|
.collect(),
|
||||||
DriverProvides::Filesystems => locate_storage_handles()?,
|
DriverProvides::Filesystems => {
|
||||||
|
// Probe in reverse so the first partition is the last to be tried.
|
||||||
|
let mut storage = locate_storage_handles()?;
|
||||||
|
storage.reverse();
|
||||||
|
storage
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for handle in handles {
|
// The driver list passed to the firmware must end with a null entry.
|
||||||
|
let driver_list: Vec<Option<Handle>> = drivers
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.map(Some)
|
||||||
|
.chain(core::iter::once(None))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let total = handles.len();
|
||||||
|
for (index, handle) in handles.into_iter().enumerate() {
|
||||||
|
info!("reconnecting {}/{}: {}", index + 1, total, describe(handle));
|
||||||
// Ignore the result as there is nothing we can do if reconnecting a controller fails.
|
// 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.
|
// This is also likely to fail in some cases but should fail safely.
|
||||||
let _ = uefi::boot::connect_controller(handle, &[], None, true);
|
// Filesystem drivers are connected directly so the firmware stack is not rebuilt.
|
||||||
|
let _ = match provides {
|
||||||
|
DriverProvides::Any => uefi::boot::connect_controller(handle, &[], None, true),
|
||||||
|
DriverProvides::Filesystems => {
|
||||||
|
uefi::boot::connect_controller(handle, &driver_list, None, false)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
info!("reconnected {}/{}", index + 1, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -100,8 +165,12 @@ pub fn load(
|
|||||||
info!("loading drivers");
|
info!("loading drivers");
|
||||||
|
|
||||||
// Load all the drivers in no particular order.
|
// Load all the drivers in no particular order.
|
||||||
|
let mut loaded = Vec::new();
|
||||||
for (name, driver) in drivers {
|
for (name, driver) in drivers {
|
||||||
load_driver(context.clone(), driver).context(format!("unable to load driver: {}", name))?;
|
loaded.push(
|
||||||
|
load_driver(context.clone(), driver)
|
||||||
|
.context(format!("unable to load driver: {}", name))?,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconnect everything if any driver can provide anything, otherwise only storage.
|
// Reconnect everything if any driver can provide anything, otherwise only storage.
|
||||||
@@ -113,7 +182,7 @@ pub fn load(
|
|||||||
} else {
|
} else {
|
||||||
DriverProvides::Any
|
DriverProvides::Any
|
||||||
};
|
};
|
||||||
reconnect(provides).context("unable to reconnect drivers")?;
|
reconnect(provides, &loaded).context("unable to reconnect drivers")?;
|
||||||
info!("loaded drivers");
|
info!("loaded drivers");
|
||||||
|
|
||||||
// We've now loaded all the drivers, so we can return.
|
// We've now loaded all the drivers, so we can return.
|
||||||
|
|||||||
Reference in new issue
Block a user