diff --git a/uefi-test-runner/src/bin/shell_launcher.rs b/uefi-test-runner/src/bin/shell_launcher.rs index 5146bac46..4b3310eda 100644 --- a/uefi-test-runner/src/bin/shell_launcher.rs +++ b/uefi-test-runner/src/bin/shell_launcher.rs @@ -13,12 +13,11 @@ extern crate alloc; use alloc::vec::Vec; use log::info; -use uefi::boot; +use uefi::boot::{self, LoadImageSource}; use uefi::prelude::*; use uefi::proto::device_path::build::{self, DevicePathBuilder}; use uefi::proto::device_path::{DevicePath, DeviceSubType, DeviceType, LoadedImageDevicePath}; use uefi::proto::loaded_image::LoadedImage; -use uefi::table::boot::LoadImageSource; /// Get the device path of the shell app. This is the same as the /// currently-loaded image's device path, but with the file path part changed. @@ -43,23 +42,21 @@ fn get_shell_app_device_path(storage: &mut Vec) -> &DevicePath { } #[entry] -fn efi_main(image: Handle, st: SystemTable) -> Status { +fn efi_main() -> Status { uefi::helpers::init().unwrap(); - let boot_services = st.boot_services(); let mut storage = Vec::new(); let shell_image_path = get_shell_app_device_path(&mut storage); // Load the shell app. - let shell_image_handle = boot_services - .load_image( - image, - LoadImageSource::FromDevicePath { - device_path: shell_image_path, - from_boot_manager: false, - }, - ) - .expect("failed to load shell app"); + let shell_image_handle = boot::load_image( + boot::image_handle(), + LoadImageSource::FromDevicePath { + device_path: shell_image_path, + from_boot_manager: false, + }, + ) + .expect("failed to load shell app"); // Set the command line passed to the shell app so that it will run the // test-runner app. This automatically turns off the five-second delay. @@ -74,9 +71,7 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { } info!("launching the shell app"); - boot_services - .start_image(shell_image_handle) - .expect("failed to launch the shell app"); + boot::start_image(shell_image_handle).expect("failed to launch the shell app"); Status::SUCCESS } diff --git a/uefi/src/boot.rs b/uefi/src/boot.rs index d8a3b4917..8bc961a63 100644 --- a/uefi/src/boot.rs +++ b/uefi/src/boot.rs @@ -11,7 +11,15 @@ use core::slice; use core::sync::atomic::{AtomicPtr, Ordering}; use uefi::{table, Handle, Result, Status, StatusExt}; -pub use uefi::table::boot::{AllocateType, OpenProtocolAttributes, OpenProtocolParams, SearchType}; +#[cfg(doc)] +use { + crate::proto::device_path::LoadedImageDevicePath, crate::proto::loaded_image::LoadedImage, + crate::proto::media::fs::SimpleFileSystem, +}; + +pub use uefi::table::boot::{ + AllocateType, LoadImageSource, OpenProtocolAttributes, OpenProtocolParams, SearchType, +}; pub use uefi_raw::table::boot::MemoryType; /// Global image handle. This is only set by [`set_image_handle`], and it is @@ -242,6 +250,111 @@ pub fn open_protocol_exclusive( } } +/// Loads a UEFI image into memory and return a [`Handle`] to the image. +/// +/// There are two ways to load the image: by copying raw image data +/// from a source buffer, or by loading the image via the +/// [`SimpleFileSystem`] protocol. See [`LoadImageSource`] for more +/// details of the `source` parameter. +/// +/// The `parent_image_handle` is used to initialize the +/// `parent_handle` field of the [`LoadedImage`] protocol for the +/// image. +/// +/// If the image is successfully loaded, a [`Handle`] supporting the +/// [`LoadedImage`] and [`LoadedImageDevicePath`] protocols is returned. The +/// image can be started with [`start_image`] and unloaded with +/// [`unload_image`]. +/// +/// # Errors +/// +/// * [`Status::INVALID_PARAMETER`]: `source` contains an invalid value. +/// * [`Status::UNSUPPORTED`]: the image type is not supported. +/// * [`Status::OUT_OF_RESOURCES`]: insufficient resources to load the image. +/// * [`Status::LOAD_ERROR`]: the image is invalid. +/// * [`Status::DEVICE_ERROR`]: failed to load image due to a read error. +/// * [`Status::ACCESS_DENIED`]: failed to load image due to a security policy. +/// * [`Status::SECURITY_VIOLATION`]: a security policy specifies that the image +/// should not be started. +pub fn load_image(parent_image_handle: Handle, source: LoadImageSource) -> Result { + let bt = boot_services_raw_panicking(); + let bt = unsafe { bt.as_ref() }; + + let boot_policy; + let device_path; + let source_buffer; + let source_size; + match source { + LoadImageSource::FromBuffer { buffer, file_path } => { + // Boot policy is ignored when loading from source buffer. + boot_policy = 0; + + device_path = file_path.map(|p| p.as_ffi_ptr()).unwrap_or(ptr::null()); + source_buffer = buffer.as_ptr(); + source_size = buffer.len(); + } + LoadImageSource::FromDevicePath { + device_path: file_path, + from_boot_manager, + } => { + boot_policy = u8::from(from_boot_manager); + device_path = file_path.as_ffi_ptr(); + source_buffer = ptr::null(); + source_size = 0; + } + }; + + let mut image_handle = ptr::null_mut(); + unsafe { + (bt.load_image)( + boot_policy, + parent_image_handle.as_ptr(), + device_path.cast(), + source_buffer, + source_size, + &mut image_handle, + ) + .to_result_with_val( + // OK to unwrap: image handle is non-null for Status::SUCCESS. + || Handle::from_ptr(image_handle).unwrap(), + ) + } +} + +/// Unloads a UEFI image. +/// +/// # Errors +/// +/// * [`Status::UNSUPPORTED`]: the image has been started, and does not support unload. +/// * [`Status::INVALID_PARAMETER`]: `image_handle` is not valid. +pub fn unload_image(image_handle: Handle) -> Result { + let bt = boot_services_raw_panicking(); + let bt = unsafe { bt.as_ref() }; + + unsafe { (bt.unload_image)(image_handle.as_ptr()) }.to_result() +} + +/// Transfers control to a loaded image's entry point. +/// +/// # Errors +/// +/// * [`Status::INVALID_PARAMETER`]: `image_handle` is not valid, or the image +/// has already been initialized with `start_image`. +/// * [`Status::SECURITY_VIOLATION`]: a security policy specifies that the image +/// should not be started. +pub fn start_image(image_handle: Handle) -> Result { + let bt = boot_services_raw_panicking(); + let bt = unsafe { bt.as_ref() }; + + // TODO: implement returning exit data to the caller. + let mut exit_data_size: usize = 0; + let mut exit_data: *mut u16 = ptr::null_mut(); + + unsafe { + (bt.start_image)(image_handle.as_ptr(), &mut exit_data_size, &mut exit_data).to_result() + } +} + /// A buffer returned by [`locate_handle_buffer`] that contains an array of /// [`Handle`]s that support the requested protocol. #[derive(Debug, Eq, PartialEq)]