mirror of
https://github.com/edera-dev/sprout.git
synced 2025-12-19 22:10:17 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
5e4e86857c
|
|||
|
d3cad25749
|
|||
|
22780e6102
|
|||
|
c52d61b07f
|
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -66,7 +66,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "edera-sprout-boot"
|
name = "edera-sprout-boot"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"edera-sprout-build",
|
"edera-sprout-build",
|
||||||
@@ -82,18 +82,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "edera-sprout-build"
|
name = "edera-sprout-build"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "edera-sprout-config"
|
name = "edera-sprout-config"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "edera-sprout-eficore"
|
name = "edera-sprout-eficore"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
@@ -309,9 +309,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uefi"
|
name = "uefi"
|
||||||
version = "0.36.0"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f123e69767fc287c44d70ee19af3b39d1bfb735dbaff5090e95b5b13cd656d16"
|
checksum = "71fe9058b73ee2b6559524af9e33199c13b2485ddbf3ad1181b68051cdc50c17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -336,9 +336,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uefi-raw"
|
name = "uefi-raw"
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8aff2f4f2b556a36a201d335a1e0a57754967a96857b1f47a52d5a23825cac84"
|
checksum = "7f64fe59e11af447d12fd60a403c74106eb104309f34b4c6dbce6e927d97da9d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"uguid",
|
"uguid",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ resolver = "3"
|
|||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
homepage = "https://sprout.edera.dev"
|
homepage = "https://sprout.edera.dev"
|
||||||
repository = "https://github.com/edera-dev/sprout"
|
repository = "https://github.com/edera-dev/sprout"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
@@ -18,7 +18,7 @@ edition = "2024"
|
|||||||
bitflags = "2.10.0"
|
bitflags = "2.10.0"
|
||||||
log = "0.4.28"
|
log = "0.4.28"
|
||||||
spin = "0.10.0"
|
spin = "0.10.0"
|
||||||
uefi-raw = "0.12.0"
|
uefi-raw = "0.13.0"
|
||||||
|
|
||||||
[workspace.dependencies.anyhow]
|
[workspace.dependencies.anyhow]
|
||||||
version = "1.0.100"
|
version = "1.0.100"
|
||||||
@@ -48,7 +48,7 @@ default-features = false
|
|||||||
features = ["serde", "parse"]
|
features = ["serde", "parse"]
|
||||||
|
|
||||||
[workspace.dependencies.uefi]
|
[workspace.dependencies.uefi]
|
||||||
version = "0.36.0"
|
version = "0.36.1"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["alloc", "global_allocator", "panic_handler"]
|
features = ["alloc", "global_allocator", "panic_handler"]
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ use alloc::rc::Rc;
|
|||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use edera_sprout_config::actions::chainload::ChainloadConfiguration;
|
use edera_sprout_config::actions::chainload::ChainloadConfiguration;
|
||||||
use eficore::bootloader_interface::BootloaderInterface;
|
use eficore::bootloader_interface::BootloaderInterface;
|
||||||
|
use eficore::loader::source::ImageSource;
|
||||||
|
use eficore::loader::{ImageLoadRequest, ImageLoader};
|
||||||
use eficore::media_loader::MediaLoaderHandle;
|
use eficore::media_loader::MediaLoaderHandle;
|
||||||
use eficore::media_loader::constants::linux::LINUX_EFI_INITRD_MEDIA_GUID;
|
use eficore::media_loader::constants::linux::LINUX_EFI_INITRD_MEDIA_GUID;
|
||||||
use eficore::shim::{ShimInput, ShimSupport};
|
|
||||||
use log::error;
|
use log::error;
|
||||||
use uefi::CString16;
|
use uefi::CString16;
|
||||||
use uefi::proto::loaded_image::LoadedImage;
|
use uefi::proto::loaded_image::LoadedImage;
|
||||||
@@ -24,13 +25,17 @@ pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfigurat
|
|||||||
)
|
)
|
||||||
.context("unable to resolve chainload path")?;
|
.context("unable to resolve chainload path")?;
|
||||||
|
|
||||||
// Load the image to chainload using the shim support integration.
|
// Create a new image load request with the current image and the resolved path.
|
||||||
|
let request = ImageLoadRequest::new(sprout_image, ImageSource::ResolvedPath(&resolved));
|
||||||
|
|
||||||
|
// Load the image to chainload using the image loader support module.
|
||||||
// It will determine if the image needs to be loaded via the shim or can be loaded directly.
|
// 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))?;
|
let image = ImageLoader::load(request)?;
|
||||||
|
|
||||||
// Open the LoadedImage protocol of the image to chainload.
|
// Open the LoadedImage protocol of the image to chainload.
|
||||||
let mut loaded_image_protocol = uefi::boot::open_protocol_exclusive::<LoadedImage>(image)
|
let mut loaded_image_protocol =
|
||||||
.context("unable to open loaded image protocol")?;
|
uefi::boot::open_protocol_exclusive::<LoadedImage>(*image.handle())
|
||||||
|
.context("unable to open loaded image protocol")?;
|
||||||
|
|
||||||
// Stamp and combine the options to pass to the image.
|
// Stamp and combine the options to pass to the image.
|
||||||
let options =
|
let options =
|
||||||
@@ -87,7 +92,7 @@ pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfigurat
|
|||||||
// This call might return, or it may pass full control to another image that will never return.
|
// This call might return, or it may pass full control to another image that will never return.
|
||||||
// Capture the result to ensure we can return an error if the image fails to start, but only
|
// Capture the result to ensure we can return an error if the image fails to start, but only
|
||||||
// after the optional initrd has been unregistered.
|
// after the optional initrd has been unregistered.
|
||||||
let result = uefi::boot::start_image(image);
|
let result = uefi::boot::start_image(*image.handle());
|
||||||
|
|
||||||
// Unregister the initrd if it was registered.
|
// Unregister the initrd if it was registered.
|
||||||
if let Some(initrd_handle) = initrd_handle
|
if let Some(initrd_handle) = initrd_handle
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ use alloc::rc::Rc;
|
|||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use edera_sprout_config::drivers::DriverDeclaration;
|
use edera_sprout_config::drivers::DriverDeclaration;
|
||||||
use eficore::shim::{ShimInput, ShimSupport};
|
use eficore::loader::source::ImageSource;
|
||||||
|
use eficore::loader::{ImageLoadRequest, ImageLoader};
|
||||||
use log::info;
|
use log::info;
|
||||||
use uefi::boot::SearchType;
|
use uefi::boot::SearchType;
|
||||||
|
|
||||||
@@ -21,14 +22,17 @@ fn load_driver(context: Rc<SproutContext>, driver: &DriverDeclaration) -> Result
|
|||||||
)
|
)
|
||||||
.context("unable to resolve path to driver")?;
|
.context("unable to resolve path to driver")?;
|
||||||
|
|
||||||
// Load the driver image using the shim support integration.
|
// 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.
|
||||||
// It will determine if the image needs to be loaded via the shim or can be loaded directly.
|
// 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))?;
|
let image = ImageLoader::load(request)?;
|
||||||
|
|
||||||
// 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.
|
||||||
uefi::boot::start_image(image).context("unable to start driver image")?;
|
uefi::boot::start_image(*image.handle()).context("unable to start driver image")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ extern crate alloc;
|
|||||||
/// EFI handle helpers.
|
/// EFI handle helpers.
|
||||||
pub mod handle;
|
pub mod handle;
|
||||||
|
|
||||||
|
/// Load and start EFI images.
|
||||||
|
pub mod loader;
|
||||||
|
|
||||||
/// Logging support for EFI applications.
|
/// Logging support for EFI applications.
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
|
|
||||||
|
|||||||
141
crates/eficore/src/loader.rs
Normal file
141
crates/eficore/src/loader.rs
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
use crate::loader::source::ImageSource;
|
||||||
|
use crate::secure::SecureBoot;
|
||||||
|
use crate::shim::hook::SecurityHook;
|
||||||
|
use crate::shim::{ShimInput, ShimSupport};
|
||||||
|
use anyhow::{Context, Result, bail};
|
||||||
|
use log::warn;
|
||||||
|
use uefi::Handle;
|
||||||
|
use uefi::boot::LoadImageSource;
|
||||||
|
|
||||||
|
/// Represents EFI image sources generically.
|
||||||
|
pub mod source;
|
||||||
|
|
||||||
|
/// Handle to a loaded EFI image.
|
||||||
|
pub struct ImageHandle {
|
||||||
|
/// Handle to the loaded image.
|
||||||
|
handle: Handle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageHandle {
|
||||||
|
/// Create a new image handle based on a handle from the UEFI stack.
|
||||||
|
pub fn new(handle: Handle) -> Self {
|
||||||
|
Self { handle }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve the underlying handle.
|
||||||
|
pub fn handle(&self) -> &Handle {
|
||||||
|
&self.handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request to load an image from a source, with support for additional validation features.
|
||||||
|
pub struct ImageLoadRequest<'source> {
|
||||||
|
/// Handle to the current image.
|
||||||
|
current_image: Handle,
|
||||||
|
/// Source of the image to load.
|
||||||
|
source: ImageSource<'source>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'source> ImageLoadRequest<'source> {
|
||||||
|
/// Create a new image load request with a current image and a source.
|
||||||
|
pub fn new(current_image: Handle, source: ImageSource<'source>) -> Self {
|
||||||
|
Self {
|
||||||
|
current_image,
|
||||||
|
source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve the current image.
|
||||||
|
pub fn current_image(&self) -> &Handle {
|
||||||
|
&self.current_image
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve the source of the image to load.
|
||||||
|
pub fn source(&'source self) -> &'source ImageSource<'source> {
|
||||||
|
&self.source
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the request into a source.
|
||||||
|
pub fn into_source(self) -> ImageSource<'source> {
|
||||||
|
self.source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// EFI image loader.
|
||||||
|
pub struct ImageLoader;
|
||||||
|
|
||||||
|
impl ImageLoader {
|
||||||
|
/// Load an image using the image `request` which allows
|
||||||
|
pub fn load(request: ImageLoadRequest) -> Result<ImageHandle> {
|
||||||
|
// Determine whether Secure Boot is enabled.
|
||||||
|
let secure_boot =
|
||||||
|
SecureBoot::enabled().context("unable to determine if secure boot is enabled")?;
|
||||||
|
|
||||||
|
// Determine whether the shim is loaded.
|
||||||
|
let shim_loaded = ShimSupport::loaded().context("unable to determine if shim is loaded")?;
|
||||||
|
|
||||||
|
// Determine whether the shim loader is available.
|
||||||
|
let shim_loader_available = ShimSupport::loader_available()
|
||||||
|
.context("unable to determine if shim loader is available")?;
|
||||||
|
|
||||||
|
// Determines whether LoadImage in Boot Services must be patched.
|
||||||
|
// Version 16 of the shim doesn't require extra effort to load Secure Boot binaries.
|
||||||
|
// If the image loader is installed, we can skip over the security hook.
|
||||||
|
let requires_security_hook = secure_boot && shim_loaded && !shim_loader_available;
|
||||||
|
|
||||||
|
// If the security hook is required, we will bail for now.
|
||||||
|
if requires_security_hook {
|
||||||
|
// Install the security hook, if possible. If it's not, this is necessary to continue,
|
||||||
|
// so we should bail.
|
||||||
|
let installed = SecurityHook::install().context("unable to install security hook")?;
|
||||||
|
if !installed {
|
||||||
|
bail!("unable to install security hook required for this platform");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the shim is loaded, we will need to retain the shim protocol to allow
|
||||||
|
// loading multiple images.
|
||||||
|
if shim_loaded {
|
||||||
|
// Retain the shim protocol after loading the image.
|
||||||
|
ShimSupport::retain()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone the current image handle to use for loading the image.
|
||||||
|
let current_image = *request.current_image();
|
||||||
|
|
||||||
|
// Converts the source to a shim input with an owned data buffer.
|
||||||
|
let input = ShimInput::from(request.into_source())
|
||||||
|
.into_owned_data_buffer()
|
||||||
|
.context("unable to convert input to loaded data buffer")?;
|
||||||
|
|
||||||
|
// Constructs a LoadImageSource from the input.
|
||||||
|
let source = LoadImageSource::FromBuffer {
|
||||||
|
buffer: input.buffer().context("unable to get buffer from input")?,
|
||||||
|
file_path: input.file_path(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loads the image using Boot Services LoadImage function.
|
||||||
|
let result = uefi::boot::load_image(current_image, source).context("unable to load image");
|
||||||
|
|
||||||
|
// If the security override is required, we will uninstall the security hook.
|
||||||
|
if requires_security_hook {
|
||||||
|
let uninstall_result = crate::shim::hook::SecurityHook::uninstall();
|
||||||
|
// Ensure we don't mask load image errors if uninstalling fails.
|
||||||
|
if result.is_err()
|
||||||
|
&& let Err(uninstall_error) = &uninstall_result
|
||||||
|
{
|
||||||
|
// Warn on the error since the load image error is more important.
|
||||||
|
warn!("unable to uninstall security hook: {}", uninstall_error);
|
||||||
|
} else {
|
||||||
|
// Otherwise, ensure we handle the original uninstallation result.
|
||||||
|
uninstall_result?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert the result and grab the handle.
|
||||||
|
let handle = result?;
|
||||||
|
|
||||||
|
// Retrieve the handle from the result and make a new image handle.
|
||||||
|
Ok(ImageHandle::new(handle))
|
||||||
|
}
|
||||||
|
}
|
||||||
25
crates/eficore/src/loader/source.rs
Normal file
25
crates/eficore/src/loader/source.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use crate::path::ResolvedPath;
|
||||||
|
use crate::shim::ShimInput;
|
||||||
|
|
||||||
|
/// Represents a source of an EFI image.
|
||||||
|
pub enum ImageSource<'source> {
|
||||||
|
/// The image is located at the specified path that has been resolved.
|
||||||
|
ResolvedPath(&'source ResolvedPath),
|
||||||
|
/// The image is located in a buffer.
|
||||||
|
DataBuffer {
|
||||||
|
/// Optional path to the image.
|
||||||
|
path: Option<&'source ResolvedPath>,
|
||||||
|
/// Buffer containing the image.
|
||||||
|
buffer: &'source [u8],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement conversion from `ImageSource` to `ShimInput`, which is used by the shim support code.
|
||||||
|
impl<'source> From<ImageSource<'source>> for ShimInput<'source> {
|
||||||
|
fn from(value: ImageSource<'source>) -> Self {
|
||||||
|
match value {
|
||||||
|
ImageSource::ResolvedPath(path) => ShimInput::ResolvedPath(path),
|
||||||
|
ImageSource::DataBuffer { path, buffer } => ShimInput::DataBuffer(path, buffer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -161,14 +161,31 @@ impl MediaLoaderHandle {
|
|||||||
|
|
||||||
// Install a protocol interface for the device path.
|
// Install a protocol interface for the device path.
|
||||||
// This ensures it can be located by other EFI programs.
|
// This ensures it can be located by other EFI programs.
|
||||||
let primary_handle = unsafe {
|
let primary_handle = match unsafe {
|
||||||
uefi::boot::install_protocol_interface(
|
uefi::boot::install_protocol_interface(
|
||||||
None,
|
None,
|
||||||
&DevicePathProtocol::GUID,
|
&DevicePathProtocol::GUID,
|
||||||
path.as_ffi_ptr() as *mut c_void,
|
path.as_ffi_ptr() as *mut c_void,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.context("unable to install media loader device path handle")?;
|
.context("unable to install media loader device path handle")
|
||||||
|
{
|
||||||
|
// Acquiring the primary handle succeeded, so we can return the handle.
|
||||||
|
Ok(handle) => handle,
|
||||||
|
// If acquiring the primary handle failed, we free the device path and return the error.
|
||||||
|
Err(error) => {
|
||||||
|
// SAFETY: We know that the device path is leaked,
|
||||||
|
// so we can safely take a reference to it again.
|
||||||
|
// The UEFI stack failed to install the protocol interface
|
||||||
|
// if we reach here, so the path is no longer in use.
|
||||||
|
let path = unsafe { Box::from_raw(path) };
|
||||||
|
// Explicitly drop the path to clarify the lifetime.
|
||||||
|
drop(path);
|
||||||
|
|
||||||
|
// Return the original error.
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Leak the data we need to pass to the UEFI stack.
|
// Leak the data we need to pass to the UEFI stack.
|
||||||
let data = Box::leak(data);
|
let data = Box::leak(data);
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use crate::path::ResolvedPath;
|
use crate::path::ResolvedPath;
|
||||||
use crate::secure::SecureBoot;
|
|
||||||
use crate::shim::hook::SecurityHook;
|
|
||||||
use crate::variables::{VariableClass, VariableController};
|
use crate::variables::{VariableClass, VariableController};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
@@ -8,9 +6,6 @@ use alloc::vec::Vec;
|
|||||||
use anyhow::{Context, Result, anyhow, bail};
|
use anyhow::{Context, Result, anyhow, bail};
|
||||||
use core::ffi::c_void;
|
use core::ffi::c_void;
|
||||||
use core::pin::Pin;
|
use core::pin::Pin;
|
||||||
use log::warn;
|
|
||||||
use uefi::Handle;
|
|
||||||
use uefi::boot::LoadImageSource;
|
|
||||||
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
|
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
|
||||||
use uefi::proto::device_path::{DevicePath, FfiDevicePath};
|
use uefi::proto::device_path::{DevicePath, FfiDevicePath};
|
||||||
use uefi::proto::unsafe_protocol;
|
use uefi::proto::unsafe_protocol;
|
||||||
@@ -18,7 +13,7 @@ use uefi_raw::table::runtime::VariableVendor;
|
|||||||
use uefi_raw::{Guid, Status, guid};
|
use uefi_raw::{Guid, Status, guid};
|
||||||
|
|
||||||
/// Security hook support.
|
/// Security hook support.
|
||||||
mod hook;
|
pub mod hook;
|
||||||
|
|
||||||
/// Support for the shim loader application for Secure Boot.
|
/// Support for the shim loader application for Secure Boot.
|
||||||
pub struct ShimSupport;
|
pub struct ShimSupport;
|
||||||
@@ -237,72 +232,6 @@ impl ShimSupport {
|
|||||||
.unwrap_or(ShimVerificationOutput::VerifiedDataNotLoaded))
|
.unwrap_or(ShimVerificationOutput::VerifiedDataNotLoaded))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load the image specified by the `input` and returns an image handle.
|
|
||||||
pub fn load(current_image: Handle, input: ShimInput) -> Result<Handle> {
|
|
||||||
// Determine whether Secure Boot is enabled.
|
|
||||||
let secure_boot =
|
|
||||||
SecureBoot::enabled().context("unable to determine if secure boot is enabled")?;
|
|
||||||
|
|
||||||
// Determine whether the shim is loaded.
|
|
||||||
let shim_loaded = Self::loaded().context("unable to determine if shim is loaded")?;
|
|
||||||
|
|
||||||
// Determine whether the shim loader is available.
|
|
||||||
let shim_loader_available =
|
|
||||||
Self::loader_available().context("unable to determine if shim loader is available")?;
|
|
||||||
|
|
||||||
// Determines whether LoadImage in Boot Services must be patched.
|
|
||||||
// Version 16 of the shim doesn't require extra effort to load Secure Boot binaries.
|
|
||||||
// If the image loader is installed, we can skip over the security hook.
|
|
||||||
let requires_security_hook = secure_boot && shim_loaded && !shim_loader_available;
|
|
||||||
|
|
||||||
// If the security hook is required, we will bail for now.
|
|
||||||
if requires_security_hook {
|
|
||||||
// Install the security hook, if possible. If it's not, this is necessary to continue,
|
|
||||||
// so we should bail.
|
|
||||||
let installed = SecurityHook::install().context("unable to install security hook")?;
|
|
||||||
if !installed {
|
|
||||||
bail!("unable to install security hook required for this platform");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the shim is loaded, we will need to retain the shim protocol to allow
|
|
||||||
// loading multiple images.
|
|
||||||
if shim_loaded {
|
|
||||||
// Retain the shim protocol after loading the image.
|
|
||||||
Self::retain()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts the shim input to an owned data buffer.
|
|
||||||
let input = input
|
|
||||||
.into_owned_data_buffer()
|
|
||||||
.context("unable to convert input to loaded data buffer")?;
|
|
||||||
|
|
||||||
// Constructs a LoadImageSource from the input.
|
|
||||||
let source = LoadImageSource::FromBuffer {
|
|
||||||
buffer: input.buffer().context("unable to get buffer from input")?,
|
|
||||||
file_path: input.file_path(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Loads the image using Boot Services LoadImage function.
|
|
||||||
let result = uefi::boot::load_image(current_image, source).context("unable to load image");
|
|
||||||
|
|
||||||
// If the security override is required, we will uninstall the security hook.
|
|
||||||
if requires_security_hook {
|
|
||||||
let uninstall_result = SecurityHook::uninstall();
|
|
||||||
// Ensure we don't mask load image errors if uninstalling fails.
|
|
||||||
if result.is_err()
|
|
||||||
&& let Err(uninstall_error) = &uninstall_result
|
|
||||||
{
|
|
||||||
// Warn on the error since the load image error is more important.
|
|
||||||
warn!("unable to uninstall security hook: {}", uninstall_error);
|
|
||||||
} else {
|
|
||||||
// Otherwise, ensure we handle the original uninstallation result.
|
|
||||||
uninstall_result?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the ShimRetainProtocol variable to indicate that shim should retain the protocols
|
/// Set the ShimRetainProtocol variable to indicate that shim should retain the protocols
|
||||||
/// for the full lifetime of boot services.
|
/// for the full lifetime of boot services.
|
||||||
pub fn retain() -> Result<()> {
|
pub fn retain() -> Result<()> {
|
||||||
|
|||||||
Reference in New Issue
Block a user