begin documenting some functions and align error messages

This commit is contained in:
2025-10-14 12:47:33 -07:00
parent 7a0c32c191
commit e90b45f58d
15 changed files with 148 additions and 101 deletions

View File

@@ -28,7 +28,7 @@ pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfigurat
context.root().loaded_image_path()?, context.root().loaded_image_path()?,
&context.stamp(&configuration.path), &context.stamp(&configuration.path),
) )
.context("failed to resolve chainload path")?; .context("unable to resolve chainload path")?;
let image = uefi::boot::load_image( let image = uefi::boot::load_image(
sprout_image, sprout_image,
@@ -37,7 +37,7 @@ pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfigurat
boot_policy: uefi::proto::BootPolicy::ExactMatch, boot_policy: uefi::proto::BootPolicy::ExactMatch,
}, },
) )
.context("failed to load image")?; .context("unable to load image")?;
let mut loaded_image_protocol = uefi::boot::open_protocol_exclusive::<LoadedImage>(image) let mut loaded_image_protocol = uefi::boot::open_protocol_exclusive::<LoadedImage>(image)
.context("unable to open loaded image protocol")?; .context("unable to open loaded image protocol")?;
@@ -76,20 +76,20 @@ pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfigurat
if let Some(ref linux_initrd) = configuration.linux_initrd { if let Some(ref linux_initrd) = configuration.linux_initrd {
let initrd_path = context.stamp(linux_initrd); let initrd_path = context.stamp(linux_initrd);
let content = utils::read_file_contents(context.root().loaded_image_path()?, &initrd_path) let content = utils::read_file_contents(context.root().loaded_image_path()?, &initrd_path)
.context("failed to read linux initrd")?; .context("unable to read linux initrd")?;
initrd_handle = Some( initrd_handle = Some(
register_linux_initrd(content.into_boxed_slice()) register_linux_initrd(content.into_boxed_slice())
.context("failed to register linux initrd")?, .context("unable to register linux initrd")?,
); );
} }
let (base, size) = loaded_image_protocol.info(); let (base, size) = loaded_image_protocol.info();
info!("loaded image: base={:#x} size={:#x}", base.addr(), size); info!("loaded image: base={:#x} size={:#x}", base.addr(), size);
let result = uefi::boot::start_image(image).context("failed to start image"); let result = uefi::boot::start_image(image).context("unable to start image");
if let Some(initrd_handle) = initrd_handle { if let Some(initrd_handle) = initrd_handle {
unregister_linux_initrd(initrd_handle).context("failed to unregister linux initrd")?; unregister_linux_initrd(initrd_handle).context("unable to unregister linux initrd")?;
} }
result.context("failed to start image")?; result.context("unable to start image")?;
drop(options_holder); drop(options_holder);
Ok(()) Ok(())
} }

View File

@@ -25,9 +25,9 @@ pub fn default_splash_time() -> u32 {
fn setup_graphics() -> Result<ScopedProtocol<GraphicsOutput>> { fn setup_graphics() -> Result<ScopedProtocol<GraphicsOutput>> {
let gop_handle = uefi::boot::get_handle_for_protocol::<GraphicsOutput>() let gop_handle = uefi::boot::get_handle_for_protocol::<GraphicsOutput>()
.context("failed to get graphics output")?; .context("unable to get graphics output")?;
uefi::boot::open_protocol_exclusive::<GraphicsOutput>(gop_handle) uefi::boot::open_protocol_exclusive::<GraphicsOutput>(gop_handle)
.context("failed to open graphics output") .context("unable to open graphics output")
} }
fn fit_to_frame(image: &DynamicImage, frame: Rect) -> Rect { fn fit_to_frame(image: &DynamicImage, frame: Rect) -> Rect {
@@ -99,7 +99,7 @@ pub fn splash(context: Rc<SproutContext>, configuration: &SplashConfiguration) -
let image = read_file_contents(context.root().loaded_image_path()?, &image)?; let image = read_file_contents(context.root().loaded_image_path()?, &image)?;
let image = ImageReader::with_format(Cursor::new(image), ImageFormat::Png) let image = ImageReader::with_format(Cursor::new(image), ImageFormat::Png)
.decode() .decode()
.context("failed to decode splash image")?; .context("unable to decode splash image")?;
draw(image)?; draw(image)?;
std::thread::sleep(Duration::from_secs(configuration.time as u64)); std::thread::sleep(Duration::from_secs(configuration.time as u64));
Ok(()) Ok(())

View File

@@ -1,7 +1,9 @@
use crate::actions::ActionDeclaration; use crate::actions::ActionDeclaration;
use crate::drivers::DriverDeclaration; use crate::drivers::DriverDeclaration;
use crate::entries::EntryDeclaration;
use crate::extractors::ExtractorDeclaration; use crate::extractors::ExtractorDeclaration;
use crate::generators::GeneratorDeclaration; use crate::generators::GeneratorDeclaration;
use crate::phases::PhasesConfiguration;
use crate::utils; use crate::utils;
use anyhow::Context; use anyhow::Context;
use anyhow::Result; use anyhow::Result;
@@ -30,37 +32,14 @@ pub struct RootConfiguration {
pub phases: PhasesConfiguration, pub phases: PhasesConfiguration,
} }
#[derive(Serialize, Deserialize, Default, Clone)] pub fn latest_version() -> u32 {
pub struct EntryDeclaration { 1
pub title: String,
#[serde(default)]
pub actions: Vec<String>,
#[serde(default)]
pub values: BTreeMap<String, String>,
}
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct PhasesConfiguration {
#[serde(default)]
pub early: Vec<PhaseConfiguration>,
#[serde(default)]
pub startup: Vec<PhaseConfiguration>,
#[serde(default)]
pub late: Vec<PhaseConfiguration>,
}
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct PhaseConfiguration {
#[serde(default)]
pub actions: Vec<String>,
#[serde(default)]
pub values: BTreeMap<String, String>,
} }
pub fn load() -> Result<RootConfiguration> { pub fn load() -> Result<RootConfiguration> {
let current_image_device_path_protocol = let current_image_device_path_protocol =
uefi::boot::open_protocol_exclusive::<LoadedImageDevicePath>(uefi::boot::image_handle()) uefi::boot::open_protocol_exclusive::<LoadedImageDevicePath>(uefi::boot::image_handle())
.context("failed to get loaded image device path")?; .context("unable to get loaded image device path")?;
let path = current_image_device_path_protocol.deref().to_boxed(); let path = current_image_device_path_protocol.deref().to_boxed();
let content = utils::read_file_contents(&path, "sprout.toml") let content = utils::read_file_contents(&path, "sprout.toml")
@@ -69,7 +48,3 @@ pub fn load() -> Result<RootConfiguration> {
toml::from_slice(&content).context("unable to parse sprout.toml file")?; toml::from_slice(&content).context("unable to parse sprout.toml file")?;
Ok(config) Ok(config)
} }
pub fn latest_version() -> u32 {
1
}

View File

@@ -34,16 +34,16 @@ fn load_driver(context: Rc<SproutContext>, driver: &DriverDeclaration) -> Result
boot_policy: uefi::proto::BootPolicy::ExactMatch, boot_policy: uefi::proto::BootPolicy::ExactMatch,
}, },
) )
.context("failed to load image")?; .context("unable to load image")?;
uefi::boot::start_image(image).context("failed to start driver image")?; uefi::boot::start_image(image).context("unable to start driver image")?;
Ok(()) Ok(())
} }
fn reconnect() -> Result<()> { fn reconnect() -> Result<()> {
let handles = uefi::boot::locate_handle_buffer(SearchType::AllHandles) let handles = uefi::boot::locate_handle_buffer(SearchType::AllHandles)
.context("failed to locate handles buffer")?; .context("unable to locate handles buffer")?;
for handle in handles.iter() { for handle in handles.iter() {
// ignore result as there is nothing we can do if it doesn't work. // ignore result as there is nothing we can do if it doesn't work.
@@ -63,10 +63,10 @@ pub fn load(
info!("loading drivers"); info!("loading drivers");
for (name, driver) in drivers { for (name, driver) in drivers {
load_driver(context.clone(), driver).context(format!("failed to load driver: {}", name))?; load_driver(context.clone(), driver).context(format!("unable to load driver: {}", name))?;
} }
reconnect().context("failed to reconnect drivers")?; reconnect().context("unable to reconnect drivers")?;
info!("loaded drivers"); info!("loaded drivers");
Ok(()) Ok(())
} }

11
src/entries.rs Normal file
View File

@@ -0,0 +1,11 @@
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct EntryDeclaration {
pub title: String,
#[serde(default)]
pub actions: Vec<String>,
#[serde(default)]
pub values: BTreeMap<String, String>,
}

View File

@@ -32,7 +32,7 @@ pub fn extract(
extractor: &FilesystemDeviceMatchExtractor, extractor: &FilesystemDeviceMatchExtractor,
) -> Result<String> { ) -> Result<String> {
let handles = uefi::boot::find_handles::<SimpleFileSystem>() let handles = uefi::boot::find_handles::<SimpleFileSystem>()
.context("failed to find filesystem handles")?; .context("unable to find filesystem handles")?;
for handle in handles { for handle in handles {
let mut has_match = false; let mut has_match = false;
@@ -55,7 +55,7 @@ pub fn extract(
{ {
None None
} else { } else {
Err(error).context("failed to open filesystem partition info")?; Err(error).context("unable to open filesystem partition info")?;
None None
} }
} }
@@ -66,7 +66,7 @@ pub fn extract(
&& let Some(ref has_partition_uuid) = extractor.has_partition_uuid && let Some(ref has_partition_uuid) = extractor.has_partition_uuid
{ {
let parsed_uuid = Guid::from_str(has_partition_uuid) let parsed_uuid = Guid::from_str(has_partition_uuid)
.map_err(|e| anyhow!("failed to parse has-partition-uuid: {}", e))?; .map_err(|e| anyhow!("unable to parse has-partition-uuid: {}", e))?;
if partition_uuid != parsed_uuid { if partition_uuid != parsed_uuid {
continue; continue;
} }
@@ -77,7 +77,7 @@ pub fn extract(
&& let Some(ref has_partition_type_uuid) = extractor.has_partition_type_uuid && let Some(ref has_partition_type_uuid) = extractor.has_partition_type_uuid
{ {
let parsed_uuid = Guid::from_str(has_partition_type_uuid) let parsed_uuid = Guid::from_str(has_partition_type_uuid)
.map_err(|e| anyhow!("failed to parse has-partition-type-uuid: {}", e))?; .map_err(|e| anyhow!("unable to parse has-partition-type-uuid: {}", e))?;
if partition_type_guid != parsed_uuid { if partition_type_guid != parsed_uuid {
continue; continue;
} }
@@ -85,17 +85,17 @@ pub fn extract(
} }
let mut filesystem = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(handle) let mut filesystem = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(handle)
.context("failed to open filesystem protocol")?; .context("unable to open filesystem protocol")?;
if let Some(ref label) = extractor.has_label { if let Some(ref label) = extractor.has_label {
let want_label = CString16::try_from(context.stamp(label).as_str()) let want_label = CString16::try_from(context.stamp(label).as_str())
.context("failed to convert label to CString16")?; .context("unable to convert label to CString16")?;
let mut root = filesystem let mut root = filesystem
.open_volume() .open_volume()
.context("failed to open filesystem volume")?; .context("unable to open filesystem volume")?;
let label = root let label = root
.get_boxed_info::<FileSystemVolumeLabel>() .get_boxed_info::<FileSystemVolumeLabel>()
.context("failed to get filesystem volume label")?; .context("unable to get filesystem volume label")?;
if label.volume_label() != want_label { if label.volume_label() != want_label {
continue; continue;
@@ -105,7 +105,7 @@ pub fn extract(
if let Some(ref item) = extractor.has_item { if let Some(ref item) = extractor.has_item {
let want_item = CString16::try_from(context.stamp(item).as_str()) let want_item = CString16::try_from(context.stamp(item).as_str())
.context("failed to convert item to CString16")?; .context("unable to convert item to CString16")?;
let mut filesystem = FileSystem::new(filesystem); let mut filesystem = FileSystem::new(filesystem);
let metadata = filesystem.metadata(Path::new(&want_item)); let metadata = filesystem.metadata(Path::new(&want_item));
@@ -125,9 +125,9 @@ pub fn extract(
} }
let path = uefi::boot::open_protocol_exclusive::<DevicePath>(handle) let path = uefi::boot::open_protocol_exclusive::<DevicePath>(handle)
.context("failed to open filesystem device path")?; .context("unable to open filesystem device path")?;
let path = path.deref(); let path = path.deref();
return utils::device_path_root(path).context("failed to get device path root"); return utils::device_path_root(path).context("unable to get device path root");
} }
if let Some(fallback) = &extractor.fallback { if let Some(fallback) = &extractor.fallback {

View File

@@ -1,5 +1,5 @@
use crate::config::EntryDeclaration;
use crate::context::SproutContext; use crate::context::SproutContext;
use crate::entries::EntryDeclaration;
use crate::generators::bls::BlsConfiguration; use crate::generators::bls::BlsConfiguration;
use crate::generators::matrix::MatrixConfiguration; use crate::generators::matrix::MatrixConfiguration;
use anyhow::Result; use anyhow::Result;

View File

@@ -1,7 +1,5 @@
mod entry;
use crate::config::EntryDeclaration;
use crate::context::SproutContext; use crate::context::SproutContext;
use crate::entries::EntryDeclaration;
use crate::generators::bls::entry::BlsEntry; use crate::generators::bls::entry::BlsEntry;
use crate::utils; use crate::utils;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
@@ -13,6 +11,8 @@ use uefi::fs::{FileSystem, Path};
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly}; use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
use uefi::proto::media::fs::SimpleFileSystem; use uefi::proto::media::fs::SimpleFileSystem;
mod entry;
#[derive(Serialize, Deserialize, Default, Clone)] #[derive(Serialize, Deserialize, Default, Clone)]
pub struct BlsConfiguration { pub struct BlsConfiguration {
pub entry: EntryDeclaration, pub entry: EntryDeclaration,
@@ -32,19 +32,19 @@ pub fn generate(
let path = context.stamp(&bls.path); let path = context.stamp(&bls.path);
let resolved = utils::resolve_path(context.root().loaded_image_path()?, &path) let resolved = utils::resolve_path(context.root().loaded_image_path()?, &path)
.context("failed to resolve bls path")?; .context("unable to resolve bls path")?;
let fs = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(resolved.filesystem_handle) let fs = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(resolved.filesystem_handle)
.context("failed to open bls filesystem")?; .context("unable to open bls filesystem")?;
let mut fs = FileSystem::new(fs); let mut fs = FileSystem::new(fs);
let sub_text_path = resolved let sub_text_path = resolved
.sub_path .sub_path
.to_string(DisplayOnly(false), AllowShortcuts(false)) .to_string(DisplayOnly(false), AllowShortcuts(false))
.context("failed to convert subpath to string")?; .context("unable to convert subpath to string")?;
let entries_path = Path::new(&sub_text_path); let entries_path = Path::new(&sub_text_path);
let entries_iter = fs let entries_iter = fs
.read_dir(entries_path) .read_dir(entries_path)
.context("failed to read bls entries")?; .context("unable to read bls entries")?;
for entry in entries_iter { for entry in entries_iter {
let entry = entry?; let entry = entry?;
@@ -57,13 +57,13 @@ pub fn generate(
} }
let full_entry_path = CString16::try_from(format!("{}\\{}", sub_text_path, name).as_str()) let full_entry_path = CString16::try_from(format!("{}\\{}", sub_text_path, name).as_str())
.context("failed to construct full entry path")?; .context("unable to construct full entry path")?;
let full_entry_path = Path::new(&full_entry_path); let full_entry_path = Path::new(&full_entry_path);
let content = fs let content = fs
.read(full_entry_path) .read(full_entry_path)
.context("failed to read bls file")?; .context("unable to read bls file")?;
let content = String::from_utf8(content).context("failed to read bls entry as utf8")?; let content = String::from_utf8(content).context("unable to read bls entry as utf8")?;
let entry = BlsEntry::from_str(&content).context("failed to parse bls entry")?; let entry = BlsEntry::from_str(&content).context("unable to parse bls entry")?;
if !entry.is_valid() { if !entry.is_valid() {
continue; continue;

View File

@@ -1,5 +1,5 @@
use crate::config::EntryDeclaration;
use crate::context::SproutContext; use crate::context::SproutContext;
use crate::entries::EntryDeclaration;
use anyhow::Result; use anyhow::Result;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::BTreeMap; use std::collections::BTreeMap;

View File

@@ -1,12 +1,11 @@
#![feature(uefi_std)] #![feature(uefi_std)]
use crate::config::PhaseConfiguration;
use crate::context::{RootContext, SproutContext}; use crate::context::{RootContext, SproutContext};
use crate::phases::phase;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use log::info; use log::info;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc;
use uefi::proto::device_path::LoadedImageDevicePath; use uefi::proto::device_path::LoadedImageDevicePath;
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly}; use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
@@ -14,25 +13,13 @@ pub mod actions;
pub mod config; pub mod config;
pub mod context; pub mod context;
pub mod drivers; pub mod drivers;
pub mod entries;
pub mod extractors; pub mod extractors;
pub mod generators; pub mod generators;
pub mod phases;
pub mod setup; pub mod setup;
pub mod utils; pub mod utils;
fn phase(context: Rc<SproutContext>, phase: &[PhaseConfiguration]) -> Result<()> {
for item in phase {
let mut context = context.fork();
context.insert(&item.values);
let context = context.freeze();
for action in item.actions.iter() {
actions::execute(context.clone(), action)
.context(format!("failed to execute action '{}'", action))?;
}
}
Ok(())
}
fn main() -> Result<()> { fn main() -> Result<()> {
setup::init()?; setup::init()?;
@@ -46,7 +33,7 @@ fn main() -> Result<()> {
let current_image_device_path_protocol = uefi::boot::open_protocol_exclusive::< let current_image_device_path_protocol = uefi::boot::open_protocol_exclusive::<
LoadedImageDevicePath, LoadedImageDevicePath,
>(uefi::boot::image_handle()) >(uefi::boot::image_handle())
.context("failed to get loaded image device path")?; .context("unable to get loaded image device path")?;
let loaded_image_path = current_image_device_path_protocol.deref().to_boxed(); let loaded_image_path = current_image_device_path_protocol.deref().to_boxed();
info!( info!(
"loaded image path: {}", "loaded image path: {}",
@@ -61,14 +48,14 @@ fn main() -> Result<()> {
context.insert(&config.values); context.insert(&config.values);
let context = context.freeze(); let context = context.freeze();
phase(context.clone(), &config.phases.early).context("failed to execute early phase")?; phase(context.clone(), &config.phases.early).context("unable to execute early phase")?;
drivers::load(context.clone(), &config.drivers).context("failed to load drivers")?; drivers::load(context.clone(), &config.drivers).context("unable to load drivers")?;
let mut extracted = BTreeMap::new(); let mut extracted = BTreeMap::new();
for (name, extractor) in &config.extractors { for (name, extractor) in &config.extractors {
let value = extractors::extract(context.clone(), extractor) let value = extractors::extract(context.clone(), extractor)
.context(format!("failed to extract value {}", name))?; .context(format!("unable to extract value {}", name))?;
info!("extracted value {}: {}", name, value); info!("extracted value {}: {}", name, value);
extracted.insert(name.clone(), value); extracted.insert(name.clone(), value);
} }
@@ -76,7 +63,7 @@ fn main() -> Result<()> {
context.insert(&extracted); context.insert(&extracted);
let context = context.freeze(); let context = context.freeze();
phase(context.clone(), &config.phases.startup).context("failed to execute startup phase")?; phase(context.clone(), &config.phases.startup).context("unable to execute startup phase")?;
let mut all_entries = Vec::new(); let mut all_entries = Vec::new();
@@ -107,7 +94,7 @@ fn main() -> Result<()> {
info!(" entry {}: {}", index + 1, title); info!(" entry {}: {}", index + 1, title);
} }
phase(context.clone(), &config.phases.late).context("failed to execute late phase")?; phase(context.clone(), &config.phases.late).context("unable to execute late phase")?;
let index = 1; let index = 1;
@@ -116,7 +103,7 @@ fn main() -> Result<()> {
for action in &entry.actions { for action in &entry.actions {
let action = context.stamp(action); let action = context.stamp(action);
actions::execute(context.clone(), &action) actions::execute(context.clone(), &action)
.context(format!("failed to execute action '{}'", action))?; .context(format!("unable to execute action '{}'", action))?;
} }
Ok(()) Ok(())
} }

38
src/phases.rs Normal file
View File

@@ -0,0 +1,38 @@
use crate::actions;
use crate::context::SproutContext;
use anyhow::Context;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::rc::Rc;
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct PhasesConfiguration {
#[serde(default)]
pub early: Vec<PhaseConfiguration>,
#[serde(default)]
pub startup: Vec<PhaseConfiguration>,
#[serde(default)]
pub late: Vec<PhaseConfiguration>,
}
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct PhaseConfiguration {
#[serde(default)]
pub actions: Vec<String>,
#[serde(default)]
pub values: BTreeMap<String, String>,
}
pub fn phase(context: Rc<SproutContext>, phase: &[PhaseConfiguration]) -> anyhow::Result<()> {
for item in phase {
let mut context = context.fork();
context.insert(&item.values);
let context = context.freeze();
for action in item.actions.iter() {
actions::execute(context.clone(), action)
.context(format!("unable to execute action '{}'", action))?;
}
}
Ok(())
}

View File

@@ -1,6 +1,10 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::os::uefi as uefi_std; use std::os::uefi as uefi_std;
/// Initializes the UEFI environment.
///
/// This fetches the system table and current image handle from uefi_std and injects
/// them into the uefi crate.
pub fn init() -> Result<()> { pub fn init() -> Result<()> {
let system_table = uefi_std::env::system_table(); let system_table = uefi_std::env::system_table();
let image_handle = uefi_std::env::image_handle(); let image_handle = uefi_std::env::image_handle();
@@ -15,6 +19,6 @@ pub fn init() -> Result<()> {
uefi::boot::set_image_handle(handle); uefi::boot::set_image_handle(handle);
} }
uefi::helpers::init().context("failed to initialize uefi")?; uefi::helpers::init().context("unable to initialize uefi")?;
Ok(()) Ok(())
} }

View File

@@ -9,6 +9,8 @@ use uefi::{CString16, Handle};
pub mod framebuffer; pub mod framebuffer;
pub mod linux_media_initrd; pub mod linux_media_initrd;
/// Parses the input [path] as a [DevicePath].
/// Uses the [DevicePathFromText] protocol exclusively, and will fail if it cannot acquire the protocol.
pub fn text_to_device_path(path: &str) -> Result<PoolDevicePath> { pub fn text_to_device_path(path: &str) -> Result<PoolDevicePath> {
let path = CString16::try_from(path).context("unable to convert path to CString16")?; let path = CString16::try_from(path).context("unable to convert path to CString16")?;
let device_path_from_text = uefi::boot::open_protocol_exclusive::<DevicePathFromText>( let device_path_from_text = uefi::boot::open_protocol_exclusive::<DevicePathFromText>(
@@ -22,6 +24,9 @@ pub fn text_to_device_path(path: &str) -> Result<PoolDevicePath> {
.context("unable to convert text to device path") .context("unable to convert text to device path")
} }
/// Grabs the root part of the [path].
/// For example, given "PciRoot(0x0)/Pci(0x4,0x0)/NVMe(0x1,00-00-00-00-00-00-00-00)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)/\EFI\BOOT\BOOTX64.efi"
/// it will give "PciRoot(0x0)/Pci(0x4,0x0)/NVMe(0x1,00-00-00-00-00-00-00-00)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)"
pub fn device_path_root(path: &DevicePath) -> Result<String> { pub fn device_path_root(path: &DevicePath) -> Result<String> {
let mut path = path let mut path = path
.node_iter() .node_iter()
@@ -44,6 +49,9 @@ pub fn device_path_root(path: &DevicePath) -> Result<String> {
Ok(path) Ok(path)
} }
/// Grabs the part of the [path] after the root.
/// For example, given "PciRoot(0x0)/Pci(0x4,0x0)/NVMe(0x1,00-00-00-00-00-00-00-00)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)/\EFI\BOOT\BOOTX64.efi"
/// it will give "\EFI\BOOT\BOOTX64.efi"
pub fn device_path_subpath(path: &DevicePath) -> Result<String> { pub fn device_path_subpath(path: &DevicePath) -> Result<String> {
let path = path let path = path
.node_iter() .node_iter()
@@ -65,15 +73,27 @@ pub fn device_path_subpath(path: &DevicePath) -> Result<String> {
Ok(path) Ok(path)
} }
/// Represents the components of a resolved path.
pub struct ResolvedPath { pub struct ResolvedPath {
/// The root path of the resolved path. This is the device itself.
/// For example, "PciRoot(0x0)/Pci(0x4,0x0)/NVMe(0x1,00-00-00-00-00-00-00-00)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)/"
pub root_path: Box<DevicePath>, pub root_path: Box<DevicePath>,
/// The subpath of the resolved path. This is the path to the file.
/// For example, "\EFI\BOOT\BOOTX64.efi"
pub sub_path: Box<DevicePath>, pub sub_path: Box<DevicePath>,
/// The full path of the resolved path. This is the safest path to use.
/// For example, "PciRoot(0x0)/Pci(0x4,0x0)/NVMe(0x1,00-00-00-00-00-00-00-00)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)/\EFI\BOOT\BOOTX64.efi"
pub full_path: Box<DevicePath>, pub full_path: Box<DevicePath>,
/// The handle of the filesystem containing the path.
/// This can be used to acquire a [SimpleFileSystem] protocol to read the file.
pub filesystem_handle: Handle, pub filesystem_handle: Handle,
} }
/// Resolve a path specified by [input] to its various components.
/// Uses [default_root_path] as the base root if one is not specified in the path.
/// Returns [ResolvedPath] which contains the resolved components.
pub fn resolve_path(default_root_path: &DevicePath, input: &str) -> Result<ResolvedPath> { pub fn resolve_path(default_root_path: &DevicePath, input: &str) -> Result<ResolvedPath> {
let mut path = text_to_device_path(input).context("failed to convert text to path")?; let mut path = text_to_device_path(input).context("unable to convert text to path")?;
let path_has_device = path let path_has_device = path
.node_iter() .node_iter()
.next() .next()
@@ -91,21 +111,21 @@ pub fn resolve_path(default_root_path: &DevicePath, input: &str) -> Result<Resol
input.insert_str( input.insert_str(
0, 0,
device_path_root(default_root_path) device_path_root(default_root_path)
.context("failed to get loaded image device root")? .context("unable to get loaded image device root")?
.as_str(), .as_str(),
); );
path = text_to_device_path(input.as_str()).context("failed to convert text to path")?; path = text_to_device_path(input.as_str()).context("unable to convert text to path")?;
} }
let path = path.to_boxed(); let path = path.to_boxed();
let root = device_path_root(path.as_ref()).context("failed to convert root to path")?; let root = device_path_root(path.as_ref()).context("unable to convert root to path")?;
let root_path = text_to_device_path(root.as_str()) let root_path = text_to_device_path(root.as_str())
.context("failed to convert root to path")? .context("unable to convert root to path")?
.to_boxed(); .to_boxed();
let mut root_path = root_path.as_ref(); let mut root_path = root_path.as_ref();
let handle = uefi::boot::locate_device_path::<SimpleFileSystem>(&mut root_path) let handle = uefi::boot::locate_device_path::<SimpleFileSystem>(&mut root_path)
.context("failed to locate filesystem device path")?; .context("unable to locate filesystem device path")?;
let subpath = device_path_subpath(path.deref()).context("failed to get device subpath")?; let subpath = device_path_subpath(path.deref()).context("unable to get device subpath")?;
Ok(ResolvedPath { Ok(ResolvedPath {
root_path: root_path.to_boxed(), root_path: root_path.to_boxed(),
sub_path: text_to_device_path(subpath.as_str())?.to_boxed(), sub_path: text_to_device_path(subpath.as_str())?.to_boxed(),
@@ -114,6 +134,13 @@ pub fn resolve_path(default_root_path: &DevicePath, input: &str) -> Result<Resol
}) })
} }
/// Read the contents of a file at the location specified with the [input] path.
/// Internally, this uses [resolve_path] to resolve the path to its various components.
/// [resolve_path] is passed the [default_root_path] which should specify a base root.
///
/// This acquires exclusive protocol access to the [SimpleFileSystem] protocol of the resolved
/// filesystem handle, so care must be taken to call this function outside a scope with
/// the filesystem handle protocol acquired.
pub fn read_file_contents(default_root_path: &DevicePath, input: &str) -> Result<Vec<u8>> { pub fn read_file_contents(default_root_path: &DevicePath, input: &str) -> Result<Vec<u8>> {
let resolved = resolve_path(default_root_path, input)?; let resolved = resolve_path(default_root_path, input)?;
let fs = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(resolved.filesystem_handle) let fs = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(resolved.filesystem_handle)

View File

@@ -27,7 +27,7 @@ impl Framebuffer {
dest: (0, 0), dest: (0, 0),
dims: (self.width, self.height), dims: (self.width, self.height),
}) })
.context("failed to blit framebuffer")?; .context("unable to blit framebuffer")?;
Ok(()) Ok(())
} }
} }

View File

@@ -100,6 +100,9 @@ fn already_registered() -> Result<bool> {
Ok(false) Ok(false)
} }
/// Registers the provided [data] with the UEFI stack as a Linux initrd.
/// This uses a special device path that Linux EFI stub will look at
/// to load the initrd from.
pub fn register_linux_initrd(data: Box<[u8]>) -> Result<LinuxMediaInitrdHandle> { pub fn register_linux_initrd(data: Box<[u8]>) -> Result<LinuxMediaInitrdHandle> {
let path = LinuxMediaInitrdProtocol::device_path(); let path = LinuxMediaInitrdProtocol::device_path();
let path = Box::leak(path); let path = Box::leak(path);
@@ -149,6 +152,8 @@ pub fn register_linux_initrd(data: Box<[u8]>) -> Result<LinuxMediaInitrdHandle>
}) })
} }
/// Unregisters a Linux initrd from the UEFI stack.
/// This will free the memory allocated by the initrd.
pub fn unregister_linux_initrd(handle: LinuxMediaInitrdHandle) -> Result<()> { pub fn unregister_linux_initrd(handle: LinuxMediaInitrdHandle) -> Result<()> {
if !already_registered()? { if !already_registered()? {
return Ok(()); return Ok(());