blob: 057cfa4de9b7cb5531d4213e96b2343810b0c2bb [file] [log] [blame]
//! `LoadedImage` protocol.
use crate::data_types::FromSliceWithNulError;
use crate::mem::memory_map::MemoryType;
use crate::proto::device_path::DevicePath;
use crate::proto::unsafe_protocol;
use crate::util::usize_from_u32;
use crate::{CStr16, Handle, Status};
use core::ffi::c_void;
use core::{mem, slice};
use uefi_raw::protocol::loaded_image::LoadedImageProtocol;
/// The LoadedImage protocol. This can be opened on any image handle using the `HandleProtocol` boot service.
#[derive(Debug)]
#[repr(transparent)]
#[unsafe_protocol(LoadedImageProtocol::GUID)]
pub struct LoadedImage(LoadedImageProtocol);
/// Errors that can be raised during parsing of the load options.
#[derive(Debug)]
pub enum LoadOptionsError {
/// Load options are not set.
NotSet,
/// The start and/or length of the load options is not [`u16`]-aligned.
NotAligned,
/// Not a valid null-terminated UCS-2 string.
InvalidString(FromSliceWithNulError),
}
impl LoadedImage {
/// Returns a handle to the storage device on which the image is located.
#[must_use]
pub fn device(&self) -> Option<Handle> {
unsafe { Handle::from_ptr(self.0.device_handle) }
}
/// Get a reference to the `file_path` portion of the DeviceHandle that the
/// EFI image was loaded from.
///
/// For a full device path, consider using the [`LoadedImageDevicePath`]
/// protocol.
///
/// Returns `None` if `file_path` is null.
///
/// [`LoadedImageDevicePath`]: crate::proto::device_path::LoadedImageDevicePath
#[must_use]
pub fn file_path(&self) -> Option<&DevicePath> {
if self.0.file_path.is_null() {
None
} else {
unsafe { Some(DevicePath::from_ffi_ptr(self.0.file_path.cast())) }
}
}
/// Get the load options of the image as a [`&CStr16`].
///
/// Load options are typically used to pass command-line options as
/// a null-terminated UCS-2 string. This format is not required
/// though; use [`load_options_as_bytes`] to access the raw bytes.
///
/// [`&CStr16`]: `CStr16`
/// [`load_options_as_bytes`]: `Self::load_options_as_bytes`
pub fn load_options_as_cstr16(&self) -> Result<&CStr16, LoadOptionsError> {
let load_options_size = usize_from_u32(self.0.load_options_size);
if self.0.load_options.is_null() {
Err(LoadOptionsError::NotSet)
} else if (load_options_size % mem::size_of::<u16>() != 0)
|| (((self.0.load_options as usize) % mem::align_of::<u16>()) != 0)
{
Err(LoadOptionsError::NotAligned)
} else {
let s = unsafe {
slice::from_raw_parts(
self.0.load_options.cast::<u16>(),
load_options_size / mem::size_of::<u16>(),
)
};
CStr16::from_u16_with_nul(s).map_err(LoadOptionsError::InvalidString)
}
}
/// Get the load options of the image as raw bytes.
///
/// UEFI allows arbitrary binary data in load options, but typically
/// the data is a null-terminated UCS-2 string. Use
/// [`load_options_as_cstr16`] to more conveniently access the load
/// options as a string.
///
/// Returns `None` if load options are not set.
///
/// [`load_options_as_cstr16`]: `Self::load_options_as_cstr16`
#[must_use]
pub fn load_options_as_bytes(&self) -> Option<&[u8]> {
if self.0.load_options.is_null() {
None
} else {
unsafe {
Some(slice::from_raw_parts(
self.0.load_options.cast(),
usize_from_u32(self.0.load_options_size),
))
}
}
}
/// Set the image data address and size.
///
/// This is useful in the following scenario:
/// 1. Secure boot is enabled, so images loaded with `LoadImage` must be
/// signed with an appropriate key known to the firmware.
/// 2. The bootloader has its own key embedded, and uses that key to
/// verify the next stage. This key is not known to the firmware, so
/// the next stage's image can't be loaded with `LoadImage`.
/// 3. Since image handles are created by `LoadImage`, which we can't
/// call, we have to make use of an existing image handle -- the one
/// passed into the bootloader's entry function. By modifying that
/// image handle (after appropriately verifying the signature of the
/// new data), we can repurpose the image handle for the next stage.
///
/// See [shim] for an example of this scenario.
///
/// # Safety
///
/// This function takes `data` as a raw pointer because the data is not
/// owned by `LoadedImage`. The caller must ensure that the memory lives
/// long enough.
///
/// [shim]: https://github.com/rhboot/shim/blob/4d64389c6c941d21548b06423b8131c872e3c3c7/pe.c#L1143
pub unsafe fn set_image(&mut self, data: *const c_void, size: u64) {
self.0.image_base = data;
self.0.image_size = size;
}
/// Set the callback handler to unload the image.
///
/// Drivers that wish to support unloading have to register their unload handler
/// using this protocol. It is responsible for cleaning up any resources the
/// image is using before returning. Unloading a driver is done with
/// [`boot::unload_image`].
///
/// # Safety
///
/// Only the driver that this [`LoadedImage`] is attached to should register an
/// unload handler.
///
/// [`boot::unload_image`]: crate::boot::unload_image
pub unsafe fn set_unload(
&mut self,
unload: extern "efiapi" fn(image_handle: Handle) -> Status,
) {
type RawFn = unsafe extern "efiapi" fn(image_handle: uefi_raw::Handle) -> uefi_raw::Status;
let unload: RawFn = mem::transmute(unload);
self.0.unload = Some(unload);
}
/// Set the load options for the image. This can be used prior to
/// calling [`boot::start_image`] to control the command line
/// passed to the image.
///
/// `size` is in bytes.
///
/// # Safety
///
/// This function takes `options` as a raw pointer because the
/// load options data is not owned by `LoadedImage`. The caller
/// must ensure that the memory lives long enough.
///
/// [`boot::start_image`]: crate::boot::start_image
pub unsafe fn set_load_options(&mut self, options: *const u8, size: u32) {
self.0.load_options = options.cast();
self.0.load_options_size = size;
}
/// Returns the base address and the size in bytes of the loaded image.
#[must_use]
pub const fn info(&self) -> (*const c_void, u64) {
(self.0.image_base, self.0.image_size)
}
/// Get the memory type of the image's code sections.
///
/// Normally the returned value is one of:
/// - `MemoryType::LOADER_CODE` for UEFI applications
/// - `MemoryType::BOOT_SERVICES_CODE` for UEFI boot drivers
/// - `MemoryType::RUNTIME_SERVICES_CODE` for UEFI runtime drivers
#[must_use]
pub const fn code_type(&self) -> MemoryType {
self.0.image_code_type
}
/// Get the memory type of the image's data sections.
///
/// Normally the returned value is one of:
/// - `MemoryType::LOADER_DATA` for UEFI applications
/// - `MemoryType::BOOT_SERVICES_DATA` for UEFI boot drivers
/// - `MemoryType::RUNTIME_SERVICES_DATA` for UEFI runtime drivers
#[must_use]
pub const fn data_type(&self) -> MemoryType {
self.0.image_data_type
}
}