diff --git a/src/actions/chainload.rs b/src/actions/chainload.rs index 830552d..15afb1a 100644 --- a/src/actions/chainload.rs +++ b/src/actions/chainload.rs @@ -1,7 +1,7 @@ use crate::context::SproutContext; use crate::utils; use crate::utils::media_loader::MediaLoaderHandle; -use crate::utils::media_loader::constants::LINUX_EFI_INITRD_MEDIA_GUID; +use crate::utils::media_loader::constants::linux::LINUX_EFI_INITRD_MEDIA_GUID; use anyhow::{Context, Result, bail}; use log::{error, info}; use serde::{Deserialize, Serialize}; @@ -74,10 +74,10 @@ pub fn chainload(context: Rc, configuration: &ChainloadConfigurat let initrd_path = context.stamp(linux_initrd); let content = utils::read_file_contents(context.root().loaded_image_path()?, &initrd_path) .context("unable to read linux initrd")?; - initrd_handle = Some( + let handle = MediaLoaderHandle::register(LINUX_EFI_INITRD_MEDIA_GUID, content.into_boxed_slice()) - .context("unable to register linux initrd")?, - ); + .context("unable to register linux initrd")?; + initrd_handle = Some(handle); } let (base, size) = loaded_image_protocol.info(); diff --git a/src/actions/edera.rs b/src/actions/edera.rs index 0aa50c5..1ab837c 100644 --- a/src/actions/edera.rs +++ b/src/actions/edera.rs @@ -12,7 +12,7 @@ use crate::{ self, media_loader::{ MediaLoaderHandle, - constants::{ + constants::xen::{ XEN_EFI_CONFIG_MEDIA_GUID, XEN_EFI_KERNEL_MEDIA_GUID, XEN_EFI_RAMDISK_MEDIA_GUID, }, }, diff --git a/src/utils/media_loader.rs b/src/utils/media_loader.rs index ddfd30f..19894ab 100644 --- a/src/utils/media_loader.rs +++ b/src/utils/media_loader.rs @@ -13,7 +13,7 @@ pub mod constants; #[derive(Debug)] #[repr(C)] -pub struct MediaLoaderProtocol { +struct MediaLoaderProtocol { pub load_file: unsafe extern "efiapi" fn( this: *mut MediaLoaderProtocol, file_path: *const DevicePathProtocol, @@ -25,14 +25,24 @@ pub struct MediaLoaderProtocol { pub length: usize, } +/// Represents a media loader which has been registered in the UEFI stack. +/// You MUST call [MediaLoaderHandle::unregister] when ready to unregister. +/// [Drop] is not implemented for this type. pub struct MediaLoaderHandle { - pub guid: Guid, - pub handle: Handle, - pub protocol: *mut MediaLoaderProtocol, - pub path: *mut DevicePath, + guid: Guid, + handle: Handle, + protocol: *mut MediaLoaderProtocol, + path: *mut DevicePath, } impl MediaLoaderHandle { + /// The behavior of this function is derived from how Linux calls it. + /// + /// Linux calls this function by first passing a NULL [buffer]. + /// We must set the size of the buffer it should allocate in [buffer_size]. + /// The next call will pass a buffer of the right size, and we should copy + /// data into that buffer, checking whether it is safe to copy based on + /// the buffer size. unsafe extern "efiapi" fn load_file( this: *mut MediaLoaderProtocol, file_path: *const DevicePathProtocol, @@ -44,10 +54,14 @@ impl MediaLoaderHandle { return Status::INVALID_PARAMETER; } + // Boot policy must not be true, as that's special behavior that is irrelevant + // for the media loader concept. if boot_policy == Boolean::TRUE { return Status::UNSUPPORTED; } + // SAFETY: Validated as safe because this is checked to be non-null. It is the caller's + // responsibility to ensure that the right pointer is passed for [this]. unsafe { if (*this).length == 0 || (*this).address.is_null() { return Status::NOT_FOUND; @@ -65,7 +79,7 @@ impl MediaLoaderHandle { Status::SUCCESS } - pub fn device_path(guid: Guid) -> Box { + fn device_path(guid: Guid) -> Box { let mut path = Vec::new(); let path = DevicePathBuilder::with_vec(&mut path) .push(&Vendor { @@ -167,8 +181,15 @@ impl MediaLoaderHandle { ) .context("unable to uninstall media loader load file handle")?; - let _path = Box::from_raw(self.path); - let _protocol = Box::from_raw(self.protocol); + let path = Box::from_raw(self.path); + let protocol = Box::from_raw(self.protocol); + let slice = + std::ptr::slice_from_raw_parts_mut(protocol.address as *mut u8, protocol.length); + let data = Box::from_raw(slice); + + drop(path); + drop(protocol); + drop(data); } Ok(()) diff --git a/src/utils/media_loader/constants.rs b/src/utils/media_loader/constants.rs index 33322ce..3b094a3 100644 --- a/src/utils/media_loader/constants.rs +++ b/src/utils/media_loader/constants.rs @@ -1,7 +1,19 @@ -use uefi::{Guid, guid}; +/// These GUIDs are specific to Linux itself. +pub mod linux { + use uefi::{Guid, guid}; -pub const LINUX_EFI_INITRD_MEDIA_GUID: Guid = guid!("5568e427-68fc-4f3d-ac74-ca555231cc68"); + /// The device path GUID for the Linux EFI initrd. + pub const LINUX_EFI_INITRD_MEDIA_GUID: Guid = guid!("5568e427-68fc-4f3d-ac74-ca555231cc68"); +} -pub const XEN_EFI_CONFIG_MEDIA_GUID: Guid = guid!("bf61f458-a28e-46cd-93d7-07dac5e8cd66"); -pub const XEN_EFI_KERNEL_MEDIA_GUID: Guid = guid!("4010c8bf-6ced-40f5-a53f-e820aee8f34b"); -pub const XEN_EFI_RAMDISK_MEDIA_GUID: Guid = guid!("5db1fd01-c3cb-4812-b2ba-8791e52d4a89"); +/// These GUIDs were created by Edera to support Xen loading data +/// from Sprout and other EFI bootloaders. +pub mod xen { + use uefi::{Guid, guid}; + + /// The device path GUID for the Xen EFI config. + pub const XEN_EFI_CONFIG_MEDIA_GUID: Guid = guid!("bf61f458-a28e-46cd-93d7-07dac5e8cd66"); + /// The device path GUID for the Xen EFI config. + pub const XEN_EFI_KERNEL_MEDIA_GUID: Guid = guid!("4010c8bf-6ced-40f5-a53f-e820aee8f34b"); + pub const XEN_EFI_RAMDISK_MEDIA_GUID: Guid = guid!("5db1fd01-c3cb-4812-b2ba-8791e52d4a89"); +}