diff --git a/uefi-test-runner/src/bin/shell_launcher.rs b/uefi-test-runner/src/bin/shell_launcher.rs index 4b3310eda..a087293ad 100644 --- a/uefi-test-runner/src/bin/shell_launcher.rs +++ b/uefi-test-runner/src/bin/shell_launcher.rs @@ -18,6 +18,7 @@ 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::proto::BootPolicy; /// 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. @@ -53,7 +54,7 @@ fn efi_main() -> Status { boot::image_handle(), LoadImageSource::FromDevicePath { device_path: shell_image_path, - from_boot_manager: false, + boot_policy: BootPolicy::ExactMatch, }, ) .expect("failed to load shell app"); diff --git a/uefi-test-runner/src/boot/mod.rs b/uefi-test-runner/src/boot/mod.rs index 86b524070..512b6ab98 100644 --- a/uefi-test-runner/src/boot/mod.rs +++ b/uefi-test-runner/src/boot/mod.rs @@ -3,6 +3,7 @@ use uefi::fs::FileSystem; use uefi::proto::console::text::Output; use uefi::proto::device_path::media::FilePath; use uefi::proto::device_path::{DevicePath, LoadedImageDevicePath}; +use uefi::proto::BootPolicy; use uefi::table::boot::{BootServices, LoadImageSource, SearchType}; use uefi::table::{Boot, SystemTable}; use uefi::{boot, CString16, Identify}; @@ -122,7 +123,7 @@ fn test_load_image(bt: &BootServices) { { let load_source = LoadImageSource::FromDevicePath { device_path: image_device_path, - from_boot_manager: false, + boot_policy: BootPolicy::ExactMatch, }; let _ = bt .load_image(bt.image_handle(), load_source) diff --git a/uefi/CHANGELOG.md b/uefi/CHANGELOG.md index 00f0cac26..2fa8275b5 100644 --- a/uefi/CHANGELOG.md +++ b/uefi/CHANGELOG.md @@ -43,6 +43,8 @@ details of a significant change to the API in this release. > use uefi::mem::memory_map::{MemoryMap, MemoryMapMut, MemoryType}; > use uefi::table::boot::BootServices; ``` +- **Breaking:** Added a new `BootPolicy` type which breaks existing usages + of `LoadImageSource`. [funcmigrate]: ../docs/funcs_migration.md diff --git a/uefi/src/boot.rs b/uefi/src/boot.rs index 013240309..5337e5ff8 100644 --- a/uefi/src/boot.rs +++ b/uefi/src/boot.rs @@ -2,37 +2,35 @@ //! //! These functions will panic if called after exiting boot services. +pub use crate::table::boot::{ + AllocateType, EventNotifyFn, LoadImageSource, OpenProtocolAttributes, OpenProtocolParams, + ProtocolSearchKey, SearchType, TimerTrigger, +}; +pub use uefi_raw::table::boot::{EventType, MemoryAttribute, MemoryDescriptor, MemoryType, Tpl}; + use crate::data_types::PhysicalAddress; use crate::mem::memory_map::{MemoryMapBackingMemory, MemoryMapKey, MemoryMapMeta, MemoryMapOwned}; use crate::polyfill::maybe_uninit_slice_assume_init_ref; use crate::proto::device_path::DevicePath; +#[cfg(doc)] +use crate::proto::device_path::LoadedImageDevicePath; use crate::proto::loaded_image::LoadedImage; use crate::proto::media::fs::SimpleFileSystem; use crate::proto::{Protocol, ProtocolPointer}; use crate::runtime::{self, ResetType}; use crate::table::Revision; use crate::util::opt_nonnull_to_ptr; +use crate::{table, Char16, Error, Event, Guid, Handle, Result, Status, StatusExt}; use core::ffi::c_void; use core::mem::MaybeUninit; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull}; use core::sync::atomic::{AtomicPtr, Ordering}; use core::{mem, slice}; -use uefi::{table, Char16, Error, Event, Guid, Handle, Result, Status, StatusExt}; use uefi_raw::table::boot::InterfaceType; - #[cfg(feature = "alloc")] use {alloc::vec::Vec, uefi::ResultExt}; -#[cfg(doc)] -use crate::proto::device_path::LoadedImageDevicePath; - -pub use uefi::table::boot::{ - AllocateType, EventNotifyFn, LoadImageSource, OpenProtocolAttributes, OpenProtocolParams, - ProtocolSearchKey, SearchType, TimerTrigger, -}; -pub use uefi_raw::table::boot::{EventType, MemoryAttribute, MemoryDescriptor, MemoryType, Tpl}; - /// Global image handle. This is only set by [`set_image_handle`], and it is /// only read by [`image_handle`]. static IMAGE_HANDLE: AtomicPtr = AtomicPtr::new(ptr::null_mut()); @@ -995,34 +993,12 @@ pub fn load_image(parent_image_handle: Handle, source: LoadImageSource) -> Resul 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 (boot_policy, device_path, source_buffer, source_size) = source.to_ffi_params(); let mut image_handle = ptr::null_mut(); unsafe { (bt.load_image)( - boot_policy, + boot_policy.into(), parent_image_handle.as_ptr(), device_path.cast(), source_buffer, diff --git a/uefi/src/proto/boot_policy.rs b/uefi/src/proto/boot_policy.rs new file mode 100644 index 000000000..d46edf2ff --- /dev/null +++ b/uefi/src/proto/boot_policy.rs @@ -0,0 +1,108 @@ +//! Module for the [`BootPolicy`] helper type. + +use core::fmt::{Display, Formatter}; + +/// Errors that can happen when working with [`BootPolicy`]. +#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Ord)] +pub enum BootPolicyError { + /// Only `0` and `1` are valid integers, all other values are undefined. + InvalidInteger(u8), +} + +impl Display for BootPolicyError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let s = match self { + Self::InvalidInteger(_) => { + "Only `0` and `1` are valid integers, all other values are undefined." + } + }; + f.write_str(s) + } +} + +#[cfg(feature = "unstable")] +impl core::error::Error for BootPolicyError {} + +/// The UEFI boot policy is a property that influences the behaviour of +/// various UEFI functions that load files (typically UEFI images). +/// +/// This type is not ABI compatible. On the ABI level, this is an UEFI +/// boolean. +#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] +pub enum BootPolicy { + /// Indicates that the request originates from the boot manager, and that + /// the boot manager is attempting to load the provided `file_path` as a + /// boot selection. + /// + /// Boot selection refers to what a user has chosen in the (GUI) boot menu. + /// + /// This corresponds to the `TRUE` value in the UEFI spec. + BootSelection, + /// The provided `file_path` must match an exact file to be loaded. + /// + /// This corresponds to the `FALSE` value in the UEFI spec. + #[default] + ExactMatch, +} + +impl From for bool { + fn from(value: BootPolicy) -> Self { + match value { + BootPolicy::BootSelection => true, + BootPolicy::ExactMatch => false, + } + } +} + +impl From for BootPolicy { + fn from(value: bool) -> Self { + match value { + true => Self::BootSelection, + false => Self::ExactMatch, + } + } +} + +impl From for u8 { + fn from(value: BootPolicy) -> Self { + match value { + BootPolicy::BootSelection => 1, + BootPolicy::ExactMatch => 0, + } + } +} + +impl TryFrom for BootPolicy { + type Error = BootPolicyError; + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Self::ExactMatch), + 1 => Ok(Self::BootSelection), + err => Err(Self::Error::InvalidInteger(err)), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn boot_policy() { + assert_eq!(bool::from(BootPolicy::ExactMatch), false); + assert_eq!(bool::from(BootPolicy::BootSelection), true); + + assert_eq!(BootPolicy::from(false), BootPolicy::ExactMatch); + assert_eq!(BootPolicy::from(true), BootPolicy::BootSelection); + + assert_eq!(u8::from(BootPolicy::ExactMatch), 0); + assert_eq!(u8::from(BootPolicy::BootSelection), 1); + + assert_eq!(BootPolicy::try_from(0), Ok(BootPolicy::ExactMatch)); + assert_eq!(BootPolicy::try_from(1), Ok(BootPolicy::BootSelection)); + assert_eq!( + BootPolicy::try_from(2), + Err(BootPolicyError::InvalidInteger(2)) + ); + } +} diff --git a/uefi/src/proto/mod.rs b/uefi/src/proto/mod.rs index 5919b7afc..218f3ffb0 100644 --- a/uefi/src/proto/mod.rs +++ b/uefi/src/proto/mod.rs @@ -9,6 +9,27 @@ //! //! [`BootServices`]: crate::table::boot::BootServices#accessing-protocols +pub mod console; +pub mod debug; +pub mod device_path; +pub mod driver; +pub mod loaded_image; +pub mod media; +pub mod misc; +pub mod network; +pub mod pi; +pub mod rng; +pub mod security; +pub mod shell_params; +pub mod shim; +pub mod string; +pub mod tcg; + +mod boot_policy; + +pub use boot_policy::{BootPolicy, BootPolicyError}; +pub use uefi_macros::unsafe_protocol; + use crate::Identify; use core::ffi::c_void; @@ -63,21 +84,3 @@ where ptr.cast::() } } - -pub use uefi_macros::unsafe_protocol; - -pub mod console; -pub mod debug; -pub mod device_path; -pub mod driver; -pub mod loaded_image; -pub mod media; -pub mod misc; -pub mod network; -pub mod pi; -pub mod rng; -pub mod security; -pub mod shell_params; -pub mod shim; -pub mod string; -pub mod tcg; diff --git a/uefi/src/table/boot.rs b/uefi/src/table/boot.rs index ed35ae922..eaca7f9fb 100644 --- a/uefi/src/table/boot.rs +++ b/uefi/src/table/boot.rs @@ -8,9 +8,10 @@ use super::Revision; use crate::data_types::PhysicalAddress; use crate::mem::memory_map::*; use crate::proto::device_path::DevicePath; +use crate::proto::device_path::FfiDevicePath; use crate::proto::loaded_image::LoadedImage; use crate::proto::media::fs::SimpleFileSystem; -use crate::proto::{Protocol, ProtocolPointer}; +use crate::proto::{BootPolicy, Protocol, ProtocolPointer}; use crate::util::opt_nonnull_to_ptr; use crate::{Char16, Error, Event, Guid, Handle, Result, Status, StatusExt}; #[cfg(feature = "alloc")] @@ -841,34 +842,12 @@ impl BootServices { parent_image_handle: Handle, source: LoadImageSource, ) -> uefi::Result { - 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 (boot_policy, device_path, source_buffer, source_size) = source.to_ffi_params(); let mut image_handle = ptr::null_mut(); unsafe { (self.0.load_image)( - boot_policy, + boot_policy.into(), parent_image_handle.as_ptr(), device_path.cast(), source_buffer, @@ -1403,9 +1382,10 @@ pub enum LoadImageSource<'a> { /// Load an image via the [`SimpleFileSystem`] protocol. If there is /// no instance of that protocol associated with the path then the - /// behavior depends on `from_boot_manager`. If `true`, attempt to - /// load via the `LoadFile` protocol. If `false`, attempt to load - /// via the `LoadFile2` protocol, then fall back to `LoadFile`. + /// behavior depends on [`BootPolicy`]. If [`BootPolicy::BootSelection`], + /// attempt to load via the `LoadFile` protocol. If + /// [`BootPolicy::ExactMatch`], attempt to load via the `LoadFile2` + /// protocol, then fall back to `LoadFile`. FromDevicePath { /// The full device path from which to load the image. /// @@ -1416,15 +1396,49 @@ pub enum LoadImageSource<'a> { /// and not just `\\EFI\\BOOT\\BOOTX64.EFI`. device_path: &'a DevicePath, - /// If there is no instance of [`SimpleFileSystem`] protocol associated - /// with the given device path, then this function will attempt to use - /// `LoadFileProtocol` (`from_boot_manager` is `true`) or - /// `LoadFile2Protocol`, and then `LoadFileProtocol` - /// (`from_boot_manager` is `false`). - from_boot_manager: bool, + /// The [`BootPolicy`] to use. + boot_policy: BootPolicy, }, } +impl<'a> LoadImageSource<'a> { + /// Returns the raw FFI parameters for `load_image`. + #[must_use] + pub(crate) fn to_ffi_params( + &self, + ) -> ( + BootPolicy, + *const FfiDevicePath, + *const u8, /* buffer */ + usize, /* buffer length */ + ) { + let boot_policy; + let device_path; + let source_buffer; + let source_size; + match self { + LoadImageSource::FromBuffer { buffer, file_path } => { + // Boot policy is ignored when loading from source buffer. + boot_policy = BootPolicy::default(); + + 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: d_path, + boot_policy: b_policy, + } => { + boot_policy = *b_policy; + device_path = d_path.as_ffi_ptr(); + source_buffer = ptr::null(); + source_size = 0; + } + }; + (boot_policy, device_path, source_buffer, source_size) + } +} + /// RAII guard for task priority level changes /// /// Will automatically restore the former task priority level when dropped.