From 7f9d2ddcb57c4c22e6d58fb2de419365420d6cef Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Tue, 30 Jul 2024 23:03:25 -0400 Subject: [PATCH 1/4] boot: Add freestanding version of load_image --- uefi/src/boot.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/uefi/src/boot.rs b/uefi/src/boot.rs index d8a3b4917..49eb2887a 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,77 @@ 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(), + ) + } +} + /// A buffer returned by [`locate_handle_buffer`] that contains an array of /// [`Handle`]s that support the requested protocol. #[derive(Debug, Eq, PartialEq)] From ac56349204b6723cf56890f51ed179c7dfe469fb Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Tue, 30 Jul 2024 23:06:44 -0400 Subject: [PATCH 2/4] boot: Add freestanding version of unload_image --- uefi/src/boot.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/uefi/src/boot.rs b/uefi/src/boot.rs index 49eb2887a..666c9feb8 100644 --- a/uefi/src/boot.rs +++ b/uefi/src/boot.rs @@ -264,7 +264,7 @@ pub fn open_protocol_exclusive( /// 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`. +/// [`unload_image`]. /// /// # Errors /// @@ -321,6 +321,19 @@ pub fn load_image(parent_image_handle: Handle, source: LoadImageSource) -> Resul } } +/// 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() +} + /// A buffer returned by [`locate_handle_buffer`] that contains an array of /// [`Handle`]s that support the requested protocol. #[derive(Debug, Eq, PartialEq)] From 450c4e62e5e3348dc14520ea4a5d00e01c888cce Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Tue, 30 Jul 2024 23:08:59 -0400 Subject: [PATCH 3/4] boot: Add freestanding version of start_image --- uefi/src/boot.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/uefi/src/boot.rs b/uefi/src/boot.rs index 666c9feb8..8bc961a63 100644 --- a/uefi/src/boot.rs +++ b/uefi/src/boot.rs @@ -263,7 +263,7 @@ pub fn open_protocol_exclusive( /// /// 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 +/// image can be started with [`start_image`] and unloaded with /// [`unload_image`]. /// /// # Errors @@ -334,6 +334,27 @@ pub fn unload_image(image_handle: Handle) -> Result { 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)] From 42160522d1da7ad37bb929c1ba1048986dfa2e64 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Tue, 30 Jul 2024 23:10:08 -0400 Subject: [PATCH 4/4] test-runner: Use freestanding boot functions in shell_launcher Use boot::load_image and boot::start_image in shell_launcher. Drop the `efi_main` args as they are no longer needed. --- uefi-test-runner/src/bin/shell_launcher.rs | 27 +++++++++------------- 1 file changed, 11 insertions(+), 16 deletions(-) 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 }