From d311ea2305bba755917677a60b004f7b2727fd5b Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Sat, 28 Sep 2024 12:01:22 -0400 Subject: [PATCH 1/2] uefi: Delete the uefi::table::boot module --- uefi/CHANGELOG.md | 3 +- uefi/src/prelude.rs | 2 - uefi/src/proto/security/memory_protection.rs | 6 +- uefi/src/table/boot.rs | 1472 ------------------ uefi/src/table/mod.rs | 1 - uefi/src/table/system.rs | 7 - 6 files changed, 5 insertions(+), 1486 deletions(-) delete mode 100644 uefi/src/table/boot.rs diff --git a/uefi/CHANGELOG.md b/uefi/CHANGELOG.md index 140b82ac0..68342ea91 100644 --- a/uefi/CHANGELOG.md +++ b/uefi/CHANGELOG.md @@ -4,7 +4,8 @@ See [Deprecating SystemTable/BootServices/RuntimeServices][funcmigrate] for details of the deprecated items that were removed in this release. ## Changed -- **Breaking:** Deleted the deprecated `RuntimeServices` struct. +- **Breaking:** Deleted the deprecated `BootServices` and `RuntimeServices` + structs. - **Breaking:** Deleted deprecated functions `allocator::init`, `allocator::exit_boot_services`, `helpers::system_table`, and `table::system_table_runtime`. diff --git a/uefi/src/prelude.rs b/uefi/src/prelude.rs index 77dd0218f..a2ae5eb75 100644 --- a/uefi/src/prelude.rs +++ b/uefi/src/prelude.rs @@ -8,6 +8,4 @@ pub use crate::{ // Import the basic table types. #[allow(deprecated)] -pub use crate::table::boot::BootServices; -#[allow(deprecated)] pub use crate::table::{Boot, SystemTable}; diff --git a/uefi/src/proto/security/memory_protection.rs b/uefi/src/proto/security/memory_protection.rs index fa94452d2..df7fb000b 100644 --- a/uefi/src/proto/security/memory_protection.rs +++ b/uefi/src/proto/security/memory_protection.rs @@ -29,7 +29,7 @@ impl MemoryProtection { /// [`EXECUTE_PROTECT`]: MemoryAttribute::EXECUTE_PROTECT /// [`READ_ONLY`]: MemoryAttribute::READ_ONLY /// [`Status::NO_MAPPING`]: crate::Status::NO_MAPPING - /// [UEFI page size]: uefi::table::boot::PAGE_SIZE + /// [UEFI page size]: uefi::boot::PAGE_SIZE pub fn get_memory_attributes( &self, byte_region: Range, @@ -53,7 +53,7 @@ impl MemoryProtection { /// [`READ_PROTECT`]: MemoryAttribute::READ_PROTECT /// [`EXECUTE_PROTECT`]: MemoryAttribute::EXECUTE_PROTECT /// [`READ_ONLY`]: MemoryAttribute::READ_ONLY - /// [UEFI page size]: uefi::table::boot::PAGE_SIZE + /// [UEFI page size]: uefi::boot::PAGE_SIZE pub fn set_memory_attributes( &self, byte_region: Range, @@ -76,7 +76,7 @@ impl MemoryProtection { /// [`READ_PROTECT`]: MemoryAttribute::READ_PROTECT /// [`EXECUTE_PROTECT`]: MemoryAttribute::EXECUTE_PROTECT /// [`READ_ONLY`]: MemoryAttribute::READ_ONLY - /// [UEFI page size]: uefi::table::boot::PAGE_SIZE + /// [UEFI page size]: uefi::boot::PAGE_SIZE pub fn clear_memory_attributes( &self, byte_region: Range, diff --git a/uefi/src/table/boot.rs b/uefi/src/table/boot.rs deleted file mode 100644 index 9f491b1c6..000000000 --- a/uefi/src/table/boot.rs +++ /dev/null @@ -1,1472 +0,0 @@ -//! UEFI services available during boot. - -#![allow(deprecated)] - -pub use crate::boot::{ - AllocateType, EventNotifyFn, LoadImageSource, OpenProtocolAttributes, OpenProtocolParams, - ProtocolSearchKey, SearchType, TimerTrigger, -}; -pub use uefi_raw::table::boot::{ - EventType, InterfaceType, MemoryAttribute, MemoryDescriptor, MemoryType, Tpl, PAGE_SIZE, -}; - -use super::Revision; -use crate::data_types::PhysicalAddress; -use crate::mem::memory_map::*; -use crate::proto::device_path::DevicePath; -use crate::proto::loaded_image::LoadedImage; -use crate::proto::media::fs::SimpleFileSystem; -use crate::proto::{Protocol, ProtocolPointer}; -use crate::util::opt_nonnull_to_ptr; -use crate::{Char16, Error, Event, Guid, Handle, Result, Status, StatusExt}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -use core::cell::UnsafeCell; -use core::ffi::c_void; -use core::fmt::Debug; -use core::mem::{self, MaybeUninit}; -use core::ops::{Deref, DerefMut}; -use core::ptr::NonNull; -use core::{ptr, slice}; - -/// Contains pointers to all of the boot services. -/// -/// # Accessing `BootServices` -/// -/// A reference to `BootServices` can only be accessed by calling [`SystemTable::boot_services`]. -/// -/// [`SystemTable::boot_services`]: crate::table::SystemTable::boot_services -/// -/// # Accessing protocols -/// -/// Protocols can be opened using several methods of `BootServices`. Most -/// commonly, [`open_protocol_exclusive`] should be used. This ensures that -/// nothing else can use the protocol until it is closed, and returns a -/// [`ScopedProtocol`] that takes care of closing the protocol when it is -/// dropped. -/// -/// Other methods for opening protocols: -/// -/// * [`open_protocol`] -/// * [`get_image_file_system`] -/// -/// For protocol definitions, see the [`proto`] module. -/// -/// [`proto`]: crate::proto -/// [`open_protocol_exclusive`]: BootServices::open_protocol_exclusive -/// [`open_protocol`]: BootServices::open_protocol -/// [`get_image_file_system`]: BootServices::get_image_file_system -/// -/// ## Use of [`UnsafeCell`] for protocol references -/// -/// Some protocols require mutable access to themselves. For example, -/// most of the methods of the [`Output`] protocol take `&mut self`, -/// because the internal function pointers specified by UEFI for that -/// protocol take a mutable `*This` pointer. We don't want to directly -/// return a mutable reference to a protocol though because the lifetime -/// of the protocol is tied to `BootServices`. (That lifetime improves -/// safety by ensuring protocols aren't accessed after exiting boot -/// services.) If methods like [`open_protocol`] protocol took a mutable -/// reference to `BootServices` and returned a mutable reference to a -/// protocol it would prevent all other access to `BootServices` until -/// the protocol reference was dropped. To work around this, the -/// protocol reference is wrapped in an [`UnsafeCell`]. Callers can then -/// get a mutable reference to the protocol if needed. -/// -/// [`Output`]: crate::proto::console::text::Output -/// [`open_protocol`]: BootServices::open_protocol -#[deprecated = "Use the uefi::boot module instead. See https://github.com/rust-osdev/uefi-rs/blob/HEAD/docs/funcs_migration.md"] -#[derive(Debug)] -#[repr(transparent)] -pub struct BootServices(uefi_raw::table::boot::BootServices); - -impl BootServices { - /// Get the [`Handle`] of the currently-executing image. - #[must_use] - pub fn image_handle(&self) -> Handle { - uefi::boot::image_handle() - } - - /// Update the global image [`Handle`]. - /// - /// This is the same as calling [`uefi::boot::set_image_handle`]. See that - /// function for details. - /// - /// # Safety - /// - /// See [`uefi::boot::set_image_handle`] for safety requirements. - pub unsafe fn set_image_handle(&self, image_handle: Handle) { - uefi::boot::set_image_handle(image_handle) - } - - /// Raises a task's priority level and returns its previous level. - /// - /// The effect of calling `raise_tpl` with a `Tpl` that is below the current - /// one (which, sadly, cannot be queried) is undefined by the UEFI spec, - /// which also warns against remaining at high `Tpl`s for a long time. - /// - /// This function outputs an RAII guard that will automatically restore the - /// original `Tpl` when dropped. - /// - /// # Safety - /// - /// Raising a task's priority level can affect other running tasks and - /// critical processes run by UEFI. The highest priority level is the - /// most dangerous, since it disables interrupts. - #[must_use] - pub unsafe fn raise_tpl(&self, tpl: Tpl) -> TplGuard<'_> { - TplGuard { - boot_services: self, - old_tpl: (self.0.raise_tpl)(tpl), - } - } - - /// Allocates memory pages from the system. - /// - /// UEFI OS loaders should allocate memory of the type `LoaderData`. An `u64` - /// is returned even on 32-bit platforms because some hardware configurations - /// like Intel PAE enable 64-bit physical addressing on a 32-bit processor. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.AllocatePages()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::OUT_OF_RESOURCES`] - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::NOT_FOUND`] - pub fn allocate_pages( - &self, - ty: AllocateType, - mem_ty: MemoryType, - count: usize, - ) -> Result { - let (ty, mut addr) = match ty { - AllocateType::AnyPages => (0, 0), - AllocateType::MaxAddress(addr) => (1, addr), - AllocateType::Address(addr) => (2, addr), - }; - unsafe { (self.0.allocate_pages)(ty, mem_ty, count, &mut addr) }.to_result_with_val(|| addr) - } - - /// Frees memory pages allocated by UEFI. - /// - /// # Safety - /// - /// The caller must ensure that no references into the allocation remain, - /// and that the memory at the allocation is not used after it is freed. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.FreePages()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::NOT_FOUND`] - /// * [`uefi::Status::INVALID_PARAMETER`] - pub unsafe fn free_pages(&self, addr: PhysicalAddress, count: usize) -> Result { - unsafe { (self.0.free_pages)(addr, count) }.to_result() - } - - /// Stores the current UEFI memory map in an UEFI-heap allocated buffer - /// and returns a [`MemoryMapOwned`]. - /// - /// # Parameters - /// - /// - `mt`: The memory type for the backing memory on the UEFI heap. - /// Usually, this is [`MemoryType::LOADER_DATA`]. You can also use a - /// custom type. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.GetMemoryMap()` in the UEFI Specification - /// for more details. - /// - /// * [`uefi::Status::BUFFER_TOO_SMALL`] - /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn memory_map(&self, mt: MemoryType) -> Result { - let mut buffer = MemoryMapBackingMemory::new(mt)?; - - let meta = self.get_memory_map(buffer.as_mut_slice())?; - - Ok(MemoryMapOwned::from_initialized_mem(buffer, meta)) - } - - /// Calls the underlying `GetMemoryMap` function of UEFI. On success, - /// the buffer is mutated and contains the map. The map might be shorter - /// than the buffer, which is reflected by the return value. - pub(crate) fn get_memory_map(&self, buf: &mut [u8]) -> Result { - let mut map_size = buf.len(); - let map_buffer = buf.as_mut_ptr().cast::(); - let mut map_key = MemoryMapKey(0); - let mut desc_size = 0; - let mut desc_version = 0; - - assert_eq!( - (map_buffer as usize) % mem::align_of::(), - 0, - "Memory map buffers must be aligned like a MemoryDescriptor" - ); - - unsafe { - (self.0.get_memory_map)( - &mut map_size, - map_buffer, - &mut map_key.0, - &mut desc_size, - &mut desc_version, - ) - } - .to_result_with_val(|| MemoryMapMeta { - map_size, - desc_size, - map_key, - desc_version, - }) - } - - /// Allocates from a memory pool. The pointer will be 8-byte aligned. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.AllocatePool()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::OUT_OF_RESOURCES`] - /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn allocate_pool(&self, mem_ty: MemoryType, size: usize) -> Result> { - let mut buffer = ptr::null_mut(); - let ptr = unsafe { (self.0.allocate_pool)(mem_ty, size, &mut buffer) } - .to_result_with_val(|| buffer)?; - - Ok(NonNull::new(ptr) - .expect("UEFI should return error if an allocation failed but never a null pointer")) - } - - /// Frees memory allocated from a pool. - /// - /// # Safety - /// - /// The caller must ensure that no references into the allocation remain, - /// and that the memory at the allocation is not used after it is freed. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.FreePool()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - pub unsafe fn free_pool(&self, addr: *mut u8) -> Result { - unsafe { (self.0.free_pool)(addr) }.to_result() - } - - /// Creates an event - /// - /// This function creates a new event of the specified type and returns it. - /// - /// Events are created in a "waiting" state, and may switch to a "signaled" - /// state. If the event type has flag `NotifySignal` set, this will result in - /// a callback for the event being immediately enqueued at the `notify_tpl` - /// priority level. If the event type has flag `NotifyWait`, the notification - /// will be delivered next time `wait_for_event` or `check_event` is called. - /// In both cases, a `notify_fn` callback must be specified. - /// - /// # Safety - /// - /// This function is unsafe because callbacks must handle exit from boot - /// services correctly. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.CreateEvent()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::OUT_OF_RESOURCES`] - pub unsafe fn create_event( - &self, - event_ty: EventType, - notify_tpl: Tpl, - notify_fn: Option, - notify_ctx: Option>, - ) -> Result { - let mut event = ptr::null_mut(); - - // Safety: the argument types of the function pointers are defined - // differently, but are compatible and can be safely transmuted. - let notify_fn: Option = mem::transmute(notify_fn); - - let notify_ctx = opt_nonnull_to_ptr(notify_ctx); - - // Now we're ready to call UEFI - (self.0.create_event)(event_ty, notify_tpl, notify_fn, notify_ctx, &mut event) - .to_result_with_val( - // OK to unwrap: event is non-null for Status::SUCCESS. - || Event::from_ptr(event).unwrap(), - ) - } - - /// Creates a new `Event` of type `event_type`. The event's notification function, context, - /// and task priority are specified by `notify_fn`, `notify_ctx`, and `notify_tpl`, respectively. - /// The `Event` will be added to the group of `Event`s identified by `event_group`. - /// - /// If no group is specified by `event_group`, this function behaves as if the same parameters - /// had been passed to `create_event()`. - /// - /// Event groups are collections of events identified by a shared `Guid` where, when one member - /// event is signaled, all other events are signaled and their individual notification actions - /// are taken. All events are guaranteed to be signaled before the first notification action is - /// taken. All notification functions will be executed in the order specified by their `Tpl`. - /// - /// A single event can only be part of a single event group. An event may be removed from an - /// event group by using `close_event()`. - /// - /// The `EventType` of an event uses the same values as `create_event()`, except that - /// `EventType::SIGNAL_EXIT_BOOT_SERVICES` and `EventType::SIGNAL_VIRTUAL_ADDRESS_CHANGE` - /// are not valid. - /// - /// If `event_type` has `EventType::NOTIFY_SIGNAL` or `EventType::NOTIFY_WAIT`, then `notify_fn` - /// mus be `Some` and `notify_tpl` must be a valid task priority level, otherwise these parameters - /// are ignored. - /// - /// More than one event of type `EventType::TIMER` may be part of a single event group. However, - /// there is no mechanism for determining which of the timers was signaled. - /// - /// This operation is only supported starting with UEFI 2.0; earlier - /// versions will fail with [`Status::UNSUPPORTED`]. - /// - /// # Safety - /// - /// The caller must ensure they are passing a valid `Guid` as `event_group`, if applicable. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.CreateEventEx()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::OUT_OF_RESOURCES`] - pub unsafe fn create_event_ex( - &self, - event_type: EventType, - notify_tpl: Tpl, - notify_fn: Option, - notify_ctx: Option>, - event_group: Option>, - ) -> Result { - if self.0.header.revision < Revision::EFI_2_00 { - return Err(Status::UNSUPPORTED.into()); - } - - let mut event = ptr::null_mut(); - - // Safety: the argument types of the function pointers are defined - // differently, but are compatible and can be safely transmuted. - let notify_fn: Option = mem::transmute(notify_fn); - - (self.0.create_event_ex)( - event_type, - notify_tpl, - notify_fn, - opt_nonnull_to_ptr(notify_ctx), - opt_nonnull_to_ptr(event_group), - &mut event, - ) - .to_result_with_val( - // OK to unwrap: event is non-null for Status::SUCCESS. - || Event::from_ptr(event).unwrap(), - ) - } - - /// Sets the trigger for `EventType::TIMER` event. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.SetTimer()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn set_timer(&self, event: &Event, trigger_time: TimerTrigger) -> Result { - let (ty, time) = match trigger_time { - TimerTrigger::Cancel => (0, 0), - TimerTrigger::Periodic(hundreds_ns) => (1, hundreds_ns), - TimerTrigger::Relative(hundreds_ns) => (2, hundreds_ns), - }; - unsafe { (self.0.set_timer)(event.as_ptr(), ty, time) }.to_result() - } - - /// Stops execution until an event is signaled. - /// - /// This function must be called at priority level `Tpl::APPLICATION`. If an - /// attempt is made to call it at any other priority level, an `Unsupported` - /// error is returned. - /// - /// The input `Event` slice is repeatedly iterated from first to last until - /// an event is signaled or an error is detected. The following checks are - /// performed on each event: - /// - /// * If an event is of type `NotifySignal`, then an `InvalidParameter` - /// error is returned with the index of the event that caused the failure. - /// * If an event is in the signaled state, the signaled state is cleared - /// and the index of the event that was signaled is returned. - /// * If an event is not in the signaled state but does have a notification - /// function, the notification function is queued at the event's - /// notification task priority level. If the execution of the event's - /// notification function causes the event to be signaled, then the - /// signaled state is cleared and the index of the event that was signaled - /// is returned. - /// - /// To wait for a specified time, a timer event must be included in the - /// Event slice. - /// - /// To check if an event is signaled without waiting, an already signaled - /// event can be used as the last event in the slice being checked, or the - /// check_event() interface may be used. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.WaitForEvent()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::UNSUPPORTED`] - pub fn wait_for_event(&self, events: &mut [Event]) -> Result> { - let number_of_events = events.len(); - let events: *mut uefi_raw::Event = events.as_mut_ptr().cast(); - - let mut index = 0; - unsafe { (self.0.wait_for_event)(number_of_events, events, &mut index) }.to_result_with( - || index, - |s| { - if s == Status::INVALID_PARAMETER { - Some(index) - } else { - None - } - }, - ) - } - - /// Place 'event' in the signaled stated. If 'event' is already in the signaled state, - /// then nothing further occurs and `Status::SUCCESS` is returned. If `event` is of type - /// `EventType::NOTIFY_SIGNAL`, then the event's notification function is scheduled to - /// be invoked at the event's notification task priority level. - /// - /// This function may be invoked from any task priority level. - /// - /// If `event` is part of an event group, then all of the events in the event group are - /// also signaled and their notification functions are scheduled. - /// - /// When signaling an event group, it is possible to create an event in the group, signal - /// it, and then close the event to remove it from the group. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.SignalEvent()` in the UEFI Specification for more details. - /// - /// Currently, (as of UEFI Spec v2.9) this only returns `EFI_SUCCESS`. - pub fn signal_event(&self, event: &Event) -> Result { - // Safety: cloning this event should be safe, as we're directly passing it to firmware - // and not keeping the clone around. - unsafe { (self.0.signal_event)(event.as_ptr()).to_result() } - } - - /// Removes `event` from any event group to which it belongs and closes it. If `event` was - /// registered with `register_protocol_notify()`, then the corresponding registration will - /// be removed. It is safe to call this function within the corresponding notify function. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.CloseEvent()` in the UEFI Specification for more details. - /// - /// Note: The UEFI Specification v2.9 states that this may only return `EFI_SUCCESS`, but, - /// at least for application based on EDK2 (such as OVMF), it may also return `EFI_INVALID_PARAMETER`. - /// To be safe, ensure that error codes are handled properly. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn close_event(&self, event: Event) -> Result { - unsafe { (self.0.close_event)(event.as_ptr()).to_result() } - } - - /// Checks to see if an event is signaled, without blocking execution to wait for it. - /// - /// The returned value will be `true` if the event is in the signaled state, - /// otherwise `false` is returned. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.CheckEvent()` in the UEFI Specification for more details. - /// - /// Note: Instead of returning the `EFI_NOT_READY` error, as listed in the UEFI - /// Specification, this function will return `false`. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn check_event(&self, event: Event) -> Result { - let status = unsafe { (self.0.check_event)(event.as_ptr()) }; - match status { - Status::SUCCESS => Ok(true), - Status::NOT_READY => Ok(false), - _ => Err(status.into()), - } - } - - /// Installs a protocol interface on a device handle. If the inner `Option` in `handle` is `None`, - /// one will be created and added to the list of handles in the system and then returned. - /// - /// When a protocol interface is installed, firmware will call all functions that have registered - /// to wait for that interface to be installed. - /// - /// # Safety - /// - /// The caller is responsible for ensuring that they pass a valid `Guid` for `protocol`. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.InstallProtocolInterface()` in the UEFI Specification for - /// more details. - /// - /// * [`uefi::Status::OUT_OF_RESOURCES`] - /// * [`uefi::Status::INVALID_PARAMETER`] - pub unsafe fn install_protocol_interface( - &self, - handle: Option, - protocol: &Guid, - interface: *const c_void, - ) -> Result { - let mut handle = Handle::opt_to_ptr(handle); - ((self.0.install_protocol_interface)( - &mut handle, - protocol, - InterfaceType::NATIVE_INTERFACE, - interface, - )) - .to_result_with_val(|| Handle::from_ptr(handle).unwrap()) - } - - /// Reinstalls a protocol interface on a device handle. `old_interface` is replaced with `new_interface`. - /// These interfaces may be the same, in which case the registered protocol notifies occur for the handle - /// without replacing the interface. - /// - /// As with `install_protocol_interface`, any process that has registered to wait for the installation of - /// the interface is notified. - /// - /// # Safety - /// - /// The caller is responsible for ensuring that there are no references to the `old_interface` that is being - /// removed. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.ReinstallProtocolInterface()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::NOT_FOUND`] - /// * [`uefi::Status::ACCESS_DENIED`] - /// * [`uefi::Status::INVALID_PARAMETER`] - pub unsafe fn reinstall_protocol_interface( - &self, - handle: Handle, - protocol: &Guid, - old_interface: *const c_void, - new_interface: *const c_void, - ) -> Result<()> { - (self.0.reinstall_protocol_interface)( - handle.as_ptr(), - protocol, - old_interface, - new_interface, - ) - .to_result() - } - - /// Removes a protocol interface from a device handle. - /// - /// # Safety - /// - /// The caller is responsible for ensuring that there are no references to a protocol interface - /// that has been removed. Some protocols may not be able to be removed as there is no information - /// available regarding the references. This includes Console I/O, Block I/O, Disk I/o, and handles - /// to device protocols. - /// - /// The caller is responsible for ensuring that they pass a valid `Guid` for `protocol`. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.UninstallProtocolInterface()` in the UEFI Specification for - /// more details. - /// - /// * [`uefi::Status::NOT_FOUND`] - /// * [`uefi::Status::ACCESS_DENIED`] - /// * [`uefi::Status::INVALID_PARAMETER`] - pub unsafe fn uninstall_protocol_interface( - &self, - handle: Handle, - protocol: &Guid, - interface: *const c_void, - ) -> Result<()> { - (self.0.uninstall_protocol_interface)(handle.as_ptr(), protocol, interface).to_result() - } - - /// Registers `event` to be signalled whenever a protocol interface is registered for - /// `protocol` by `install_protocol_interface()` or `reinstall_protocol_interface()`. - /// - /// Once `event` has been signalled, `BootServices::locate_handle()` can be used to identify - /// the newly (re)installed handles that support `protocol`. The returned `SearchKey` on success - /// corresponds to the `search_key` parameter in `locate_handle()`. - /// - /// Events can be unregistered from protocol interface notification by calling `close_event()`. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.RegisterProtocolNotify()` in the UEFI Specification for - /// more details. - /// - /// * [`uefi::Status::OUT_OF_RESOURCES`] - /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn register_protocol_notify<'guid>( - &self, - protocol: &'guid Guid, - event: Event, - ) -> Result<(Event, SearchType<'guid>)> { - let mut key = ptr::null(); - // Safety: we clone `event` a couple times, but there will be only one left once we return. - unsafe { (self.0.register_protocol_notify)(protocol, event.as_ptr(), &mut key) } - // Safety: as long as this call is successful, `key` will be valid. - .to_result_with_val(|| unsafe { - ( - event.unsafe_clone(), - // OK to unwrap: key is non-null for Status::SUCCESS. - SearchType::ByRegisterNotify(ProtocolSearchKey( - NonNull::new(key.cast_mut()).unwrap(), - )), - ) - }) - } - - /// Enumerates all handles installed on the system which match a certain query. - /// - /// You should first call this function with `None` for the output buffer, - /// in order to retrieve the length of the buffer you need to allocate. - /// - /// The next call will fill the buffer with the requested data. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.LocateHandle()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::NOT_FOUND`] - /// * [`uefi::Status::BUFFER_TOO_SMALL`] - /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn locate_handle( - &self, - search_ty: SearchType, - output: Option<&mut [MaybeUninit]>, - ) -> Result { - let handle_size = mem::size_of::(); - - const NULL_BUFFER: *mut MaybeUninit = ptr::null_mut(); - - let (mut buffer_size, buffer) = match output { - Some(buffer) => (buffer.len() * handle_size, buffer.as_mut_ptr()), - None => (0, NULL_BUFFER), - }; - - // Obtain the needed data from the parameters. - let (ty, guid, key) = match search_ty { - SearchType::AllHandles => (0, ptr::null(), ptr::null()), - SearchType::ByRegisterNotify(registration) => { - (1, ptr::null(), registration.0.as_ptr().cast_const()) - } - SearchType::ByProtocol(guid) => (2, guid as *const Guid, ptr::null()), - }; - - let status = - unsafe { (self.0.locate_handle)(ty, guid, key, &mut buffer_size, buffer.cast()) }; - - // Must convert the returned size (in bytes) to length (number of elements). - let buffer_len = buffer_size / handle_size; - - match (buffer, status) { - (NULL_BUFFER, Status::BUFFER_TOO_SMALL) => Ok(buffer_len), - (_, other_status) => other_status.to_result_with_val(|| buffer_len), - } - } - - /// Locates the handle to a device on the device path that supports the specified protocol. - /// - /// The `device_path` is updated to point at the remaining part of the [`DevicePath`] after - /// the part that matched the protocol. For example, it can be used with a device path - /// that contains a file path to strip off the file system portion of the device path, - /// leaving the file path and handle to the file system driver needed to access the file. - /// - /// If the first node of `device_path` matches the - /// protocol, the `device_path` is advanced to the device path terminator node. If `device_path` - /// is a multi-instance device path, the function will operate on the first instance. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.LocateDevicePath()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::NOT_FOUND`] - /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn locate_device_path( - &self, - device_path: &mut &DevicePath, - ) -> Result { - let mut handle = ptr::null_mut(); - let mut device_path_ptr: *const uefi_raw::protocol::device_path::DevicePathProtocol = - device_path.as_ffi_ptr().cast(); - unsafe { - (self.0.locate_device_path)(&P::GUID, &mut device_path_ptr, &mut handle) - .to_result_with_val(|| { - *device_path = DevicePath::from_ffi_ptr(device_path_ptr.cast()); - // OK to unwrap: handle is non-null for Status::SUCCESS. - Handle::from_ptr(handle).unwrap() - }) - } - } - - /// Find an arbitrary handle that supports a particular - /// [`Protocol`]. Returns [`NOT_FOUND`] if no handles support the - /// protocol. - /// - /// This method is a convenient wrapper around - /// [`BootServices::locate_handle_buffer`] for getting just one - /// handle. This is useful when you don't care which handle the - /// protocol is opened on. For example, [`DevicePathToText`] isn't - /// tied to a particular device, so only a single handle is expected - /// to exist. - /// - /// [`NOT_FOUND`]: Status::NOT_FOUND - /// [`DevicePathToText`]: uefi::proto::device_path::text::DevicePathToText - /// - /// # Example - /// - /// ``` - /// use uefi::proto::device_path::text::DevicePathToText; - /// use uefi::table::boot::{BootServices, OpenProtocolAttributes, OpenProtocolParams}; - /// use uefi::Handle; - /// # use uefi::Result; - /// - /// # fn get_fake_val() -> T { todo!() } - /// # fn test() -> Result { - /// # let boot_services: &BootServices = get_fake_val(); - /// # let image_handle: Handle = get_fake_val(); - /// let handle = boot_services.get_handle_for_protocol::()?; - /// let device_path_to_text = boot_services.open_protocol_exclusive::(handle)?; - /// # Ok(()) - /// # } - /// ``` - /// - /// # Errors - /// - /// Returns [`NOT_FOUND`] if no handles support the requested protocol. - pub fn get_handle_for_protocol(&self) -> Result { - // Delegate to a non-generic function to potentially reduce code size. - self.get_handle_for_protocol_impl(&P::GUID) - } - - fn get_handle_for_protocol_impl(&self, guid: &Guid) -> Result { - self.locate_handle_buffer(SearchType::ByProtocol(guid))? - .first() - .cloned() - .ok_or_else(|| Status::NOT_FOUND.into()) - } - - /// Load an EFI 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`] or - /// unloaded with [`unload_image`]. - /// - /// [`LoadedImageDevicePath`]: crate::proto::device_path::LoadedImageDevicePath - /// [`start_image`]: BootServices::start_image - /// [`unload_image`]: BootServices::unload_image - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.LoadImage()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::NOT_FOUND`] - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::UNSUPPORTED`] - /// * [`uefi::Status::OUT_OF_RESOURCES`] - /// * [`uefi::Status::LOAD_ERROR`] - /// * [`uefi::Status::DEVICE_ERROR`] - /// * [`uefi::Status::ACCESS_DENIED`] - /// * [`uefi::Status::SECURITY_VIOLATION`] - pub fn load_image( - &self, - parent_image_handle: Handle, - source: LoadImageSource, - ) -> uefi::Result { - 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.into(), - 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(), - ) - } - } - - /// Unload an EFI image. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.UnloadImage()` in the UEFI Specification for more details. - /// - /// As this function can return an error code from the unloaded image, any error type - /// can be returned by this function. - /// - /// The following error codes can also be returned while unloading an image: - /// - /// * [`uefi::Status::UNSUPPORTED`] - /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn unload_image(&self, image_handle: Handle) -> Result { - unsafe { (self.0.unload_image)(image_handle.as_ptr()) }.to_result() - } - - /// Transfer control to a loaded image's entry point. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.StartImage()` in the UEFI Specification for more details. - /// - /// As this function can return an error code from the started image, any error type - /// can be returned by this function. - /// - /// The following error code can also be returned while starting an image: - /// - /// * [`uefi::Status::UNSUPPORTED`] - pub fn start_image(&self, image_handle: Handle) -> Result { - unsafe { - // TODO: implement returning exit data to the caller. - let mut exit_data_size: usize = 0; - let mut exit_data: *mut u16 = ptr::null_mut(); - (self.0.start_image)(image_handle.as_ptr(), &mut exit_data_size, &mut exit_data) - .to_result() - } - } - - /// Exits the UEFI application and returns control to the UEFI component - /// that started the UEFI application. - /// - /// # Safety - /// - /// This function is unsafe because it is up to the caller to ensure that - /// all resources allocated by the application is freed before invoking - /// exit and returning control to the UEFI component that started the UEFI - /// application. - pub unsafe fn exit( - &self, - image_handle: Handle, - exit_status: Status, - exit_data_size: usize, - exit_data: *mut Char16, - ) -> ! { - (self.0.exit)( - image_handle.as_ptr(), - exit_status, - exit_data_size, - exit_data.cast(), - ) - } - - /// Stalls the processor for an amount of time. - /// - /// The time is in microseconds. - pub fn stall(&self, time: usize) { - assert_eq!(unsafe { (self.0.stall)(time) }, Status::SUCCESS); - } - - /// Adds, updates, or removes a configuration table entry - /// from the EFI System Table. - /// - /// # Safety - /// - /// This relies on `table_ptr` being allocated in the - /// pool of type [`uefi::table::boot::MemoryType::RUNTIME_SERVICES_DATA`] - /// according to the specification. - /// Other memory types such as - /// [`uefi::table::boot::MemoryType::ACPI_RECLAIM`] - /// can be considered. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.InstallConfigurationTable()` in the UEFI - /// Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::NOT_FOUND`] - /// * [`uefi::Status::OUT_OF_RESOURCES`] - pub unsafe fn install_configuration_table( - &self, - guid_entry: &Guid, - table_ptr: *const c_void, - ) -> Result { - (self.0.install_configuration_table)(guid_entry, table_ptr).to_result() - } - - /// Set the watchdog timer. - /// - /// UEFI will start a 5-minute countdown after an UEFI image is loaded. - /// The image must either successfully load an OS and call `ExitBootServices` - /// in that time, or disable the watchdog. - /// - /// Otherwise, the firmware will log the event using the provided numeric - /// code and data, then reset the system. - /// - /// This function allows you to change the watchdog timer's timeout to a - /// certain amount of seconds or to disable the watchdog entirely. It also - /// allows you to change what will be logged when the timer expires. - /// - /// The watchdog codes from 0 to 0xffff (65535) are reserved for internal - /// firmware use. Higher values can be used freely by applications. - /// - /// If provided, the watchdog data must be a null-terminated string - /// optionally followed by other binary data. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.SetWatchdogTimer()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::UNSUPPORTED`] - /// * [`uefi::Status::DEVICE_ERROR`] - pub fn set_watchdog_timer( - &self, - timeout: usize, - watchdog_code: u64, - data: Option<&mut [u16]>, - ) -> Result { - assert!( - watchdog_code > 0xffff, - "Invalid use of a reserved firmware watchdog code" - ); - - let (data_len, data) = data - .map(|d| { - assert!( - d.contains(&0), - "Watchdog data must start with a null-terminated string" - ); - (d.len(), d.as_mut_ptr()) - }) - .unwrap_or((0, ptr::null_mut())); - - unsafe { (self.0.set_watchdog_timer)(timeout, watchdog_code, data_len, data) }.to_result() - } - - /// Connect one or more drivers to a controller. - /// - /// Usually one disconnects and then reconnects certain drivers - /// to make them rescan some state that changed, e.g. reconnecting - /// a `BlockIO` handle after your app changed the partitions somehow. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.ConnectController()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::NOT_FOUND`] - /// * [`uefi::Status::SECURITY_VIOLATION`] - pub fn connect_controller( - &self, - controller: Handle, - driver_image: Option, - remaining_device_path: Option<&DevicePath>, - recursive: bool, - ) -> Result { - unsafe { - (self.0.connect_controller)( - controller.as_ptr(), - Handle::opt_to_ptr(driver_image), - remaining_device_path - .map(|dp| dp.as_ffi_ptr()) - .unwrap_or(ptr::null()) - .cast(), - recursive, - ) - } - .to_result_with_err(|_| ()) - } - - /// Disconnect one or more drivers from a controller. - /// - /// See [`connect_controller`][Self::connect_controller]. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.DisconnectController()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::OUT_OF_RESOURCES`] - /// * [`uefi::Status::DEVICE_ERROR`] - pub fn disconnect_controller( - &self, - controller: Handle, - driver_image: Option, - child: Option, - ) -> Result { - unsafe { - (self.0.disconnect_controller)( - controller.as_ptr(), - Handle::opt_to_ptr(driver_image), - Handle::opt_to_ptr(child), - ) - } - .to_result_with_err(|_| ()) - } - - /// Open a protocol interface for a handle. - /// - /// See also [`open_protocol_exclusive`], which provides a safe - /// subset of this functionality. - /// - /// This function attempts to get the protocol implementation of a - /// handle, based on the protocol GUID. It is recommended that all - /// new drivers and applications use [`open_protocol_exclusive`] or - /// [`open_protocol`]. - /// - /// See [`OpenProtocolParams`] and [`OpenProtocolAttributes`] for - /// details of the input parameters. - /// - /// If successful, a [`ScopedProtocol`] is returned that will - /// automatically close the protocol interface when dropped. - /// - /// UEFI protocols are neither thread-safe nor reentrant, but the firmware - /// provides no mechanism to protect against concurrent usage. Such - /// protections must be implemented by user-level code, for example via a - /// global `HashSet`. - /// - /// # Safety - /// - /// This function is unsafe because it can be used to open a protocol in - /// ways that don't get tracked by the UEFI implementation. This could allow - /// the protocol to be removed from a handle, or for the handle to be - /// deleted entirely, while a reference to the protocol is still active. The - /// caller is responsible for ensuring that the handle and protocol remain - /// valid until the `ScopedProtocol` is dropped, and the caller must ensure - /// that there is never more than one mutable reference to the protocol. - /// - /// [`open_protocol`]: BootServices::open_protocol - /// [`open_protocol_exclusive`]: BootServices::open_protocol_exclusive - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.OpenProtocol()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::UNSUPPORTED`] - /// * [`uefi::Status::ACCESS_DENIED`] - /// * [`uefi::Status::ALREADY_STARTED`] - pub unsafe fn open_protocol( - &self, - params: OpenProtocolParams, - attributes: OpenProtocolAttributes, - ) -> Result> { - let mut interface = ptr::null_mut(); - (self.0.open_protocol)( - params.handle.as_ptr(), - &P::GUID, - &mut interface, - params.agent.as_ptr(), - Handle::opt_to_ptr(params.controller), - attributes as u32, - ) - .to_result_with_val(|| { - let interface = (!interface.is_null()).then(|| { - let interface = P::mut_ptr_from_ffi(interface) as *const UnsafeCell

; - &*interface - }); - - ScopedProtocol { - interface, - open_params: params, - boot_services: self, - } - }) - } - - /// Open a protocol interface for a handle in exclusive mode. - /// - /// If successful, a [`ScopedProtocol`] is returned that will - /// automatically close the protocol interface when dropped. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.OpenProtocol()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::UNSUPPORTED`] - /// * [`uefi::Status::ACCESS_DENIED`] - /// * [`uefi::Status::ALREADY_STARTED`] - pub fn open_protocol_exclusive( - &self, - handle: Handle, - ) -> Result> { - // Safety: opening in exclusive mode with the correct agent - // handle set ensures that the protocol cannot be modified or - // removed while it is open, so this usage is safe. - unsafe { - self.open_protocol::

( - OpenProtocolParams { - handle, - agent: self.image_handle(), - controller: None, - }, - OpenProtocolAttributes::Exclusive, - ) - } - } - - /// Test whether a handle supports a protocol. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.OpenProtocol()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::UNSUPPORTED`] - /// * [`uefi::Status::ACCESS_DENIED`] - /// * [`uefi::Status::ALREADY_STARTED`] - pub fn test_protocol( - &self, - params: OpenProtocolParams, - ) -> Result<()> { - const TEST_PROTOCOL: u32 = 0x04; - let mut interface = ptr::null_mut(); - unsafe { - (self.0.open_protocol)( - params.handle.as_ptr(), - &P::GUID, - &mut interface, - params.agent.as_ptr(), - Handle::opt_to_ptr(params.controller), - TEST_PROTOCOL, - ) - } - .to_result_with_val(|| ()) - } - - /// Get the list of protocol interface [`Guids`][Guid] that are installed - /// on a [`Handle`]. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.ProtocolsPerHandle()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::OUT_OF_RESOURCES`] - pub fn protocols_per_handle(&self, handle: Handle) -> Result { - let mut protocols = ptr::null_mut(); - let mut count = 0; - - let mut status = - unsafe { (self.0.protocols_per_handle)(handle.as_ptr(), &mut protocols, &mut count) }; - - if !status.is_error() { - // Ensure that protocols isn't null, and that none of the GUIDs - // returned are null. - if protocols.is_null() { - status = Status::OUT_OF_RESOURCES; - } else { - let protocols: &[*const Guid] = unsafe { slice::from_raw_parts(protocols, count) }; - if protocols.iter().any(|ptr| ptr.is_null()) { - status = Status::OUT_OF_RESOURCES; - } - } - } - - status.to_result_with_val(|| ProtocolsPerHandle { - boot_services: self, - protocols: protocols.cast::<&Guid>(), - count, - }) - } - - /// Returns an array of handles that support the requested protocol in a buffer allocated from - /// pool. - /// - /// # Errors - /// - /// See section `EFI_BOOT_SERVICES.LocateHandleBuffer()` in the UEFI Specification for more details. - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::NOT_FOUND`] - /// * [`uefi::Status::OUT_OF_RESOURCES`] - pub fn locate_handle_buffer(&self, search_ty: SearchType) -> Result { - let mut num_handles: usize = 0; - let mut buffer: *mut uefi_raw::Handle = ptr::null_mut(); - - // Obtain the needed data from the parameters. - let (ty, guid, key) = match search_ty { - SearchType::AllHandles => (0, ptr::null(), ptr::null()), - SearchType::ByRegisterNotify(registration) => { - (1, ptr::null(), registration.0.as_ptr().cast_const()) - } - SearchType::ByProtocol(guid) => (2, guid as *const _, ptr::null()), - }; - - unsafe { (self.0.locate_handle_buffer)(ty, guid, key, &mut num_handles, &mut buffer) } - .to_result_with_val(|| HandleBuffer { - boot_services: self, - count: num_handles, - buffer: buffer.cast(), - }) - } - - /// Retrieves a [`SimpleFileSystem`] protocol associated with the device the given - /// image was loaded from. - /// - /// # Errors - /// - /// This function can return errors from [`open_protocol_exclusive`] and - /// [`locate_device_path`]. See those functions for more details. - /// - /// [`open_protocol_exclusive`]: Self::open_protocol_exclusive - /// [`locate_device_path`]: Self::locate_device_path - /// - /// * [`uefi::Status::INVALID_PARAMETER`] - /// * [`uefi::Status::UNSUPPORTED`] - /// * [`uefi::Status::ACCESS_DENIED`] - /// * [`uefi::Status::ALREADY_STARTED`] - /// * [`uefi::Status::NOT_FOUND`] - pub fn get_image_file_system( - &self, - image_handle: Handle, - ) -> Result> { - let loaded_image = self.open_protocol_exclusive::(image_handle)?; - - let device_handle = loaded_image - .device() - .ok_or(Error::new(Status::UNSUPPORTED, ()))?; - let device_path = self.open_protocol_exclusive::(device_handle)?; - - let device_handle = self.locate_device_path::(&mut &*device_path)?; - - self.open_protocol_exclusive(device_handle) - } -} - -#[cfg(feature = "alloc")] -impl BootServices { - /// Returns all the handles implementing a certain protocol. - /// - /// # Errors - /// - /// All errors come from calls to [`locate_handle`]. - /// - /// [`locate_handle`]: Self::locate_handle - pub fn find_handles(&self) -> Result> { - // Search by protocol. - let search_type = SearchType::from_proto::

(); - - // Determine how much we need to allocate. - let buffer_size = self.locate_handle(search_type, None)?; - - // Allocate a large enough buffer without pointless initialization. - let mut handles = Vec::with_capacity(buffer_size); - let buffer = handles.spare_capacity_mut(); - - // Perform the search. - let buffer_size = self.locate_handle(search_type, Some(buffer))?; - - // Mark the returned number of elements as initialized. - unsafe { - handles.set_len(buffer_size); - } - - // Emit output, with warnings - Ok(handles) - } -} - -impl super::Table for BootServices { - const SIGNATURE: u64 = 0x5652_4553_544f_4f42; -} - -/// RAII guard for task priority level changes -/// -/// Will automatically restore the former task priority level when dropped. -#[deprecated = "Use uefi::boot::TplGuard instead. See https://github.com/rust-osdev/uefi-rs/blob/HEAD/docs/funcs_migration.md"] -#[derive(Debug)] -pub struct TplGuard<'boot> { - boot_services: &'boot BootServices, - old_tpl: Tpl, -} - -impl Drop for TplGuard<'_> { - fn drop(&mut self) { - unsafe { - (self.boot_services.0.restore_tpl)(self.old_tpl); - } - } -} - -/// An open protocol interface. Automatically closes the protocol -/// interface on drop. -/// -/// Most protocols have interface data associated with them. `ScopedProtocol` -/// implements [`Deref`] and [`DerefMut`] to access this data. A few protocols -/// (such as [`DevicePath`] and [`LoadedImageDevicePath`]) may be installed with -/// null interface data, in which case [`Deref`] and [`DerefMut`] will -/// panic. The [`get`] and [`get_mut`] methods may be used to access the -/// optional interface data without panicking. -/// -/// See also the [`BootServices`] documentation for details of how to open a -/// protocol and why [`UnsafeCell`] is used. -/// -/// [`LoadedImageDevicePath`]: crate::proto::device_path::LoadedImageDevicePath -/// [`get`]: ScopedProtocol::get -/// [`get_mut`]: ScopedProtocol::get_mut -#[deprecated = "Use uefi::boot::ScopedProtocol instead. See https://github.com/rust-osdev/uefi-rs/blob/HEAD/docs/funcs_migration.md"] -#[derive(Debug)] -pub struct ScopedProtocol<'a, P: Protocol + ?Sized> { - /// The protocol interface. - interface: Option<&'a UnsafeCell

>, - - open_params: OpenProtocolParams, - boot_services: &'a BootServices, -} - -impl<'a, P: Protocol + ?Sized> Drop for ScopedProtocol<'a, P> { - fn drop(&mut self) { - let status = unsafe { - (self.boot_services.0.close_protocol)( - self.open_params.handle.as_ptr(), - &P::GUID, - self.open_params.agent.as_ptr(), - Handle::opt_to_ptr(self.open_params.controller), - ) - }; - // All of the error cases for close_protocol boil down to - // calling it with a different set of parameters than what was - // passed to open_protocol. The public API prevents such errors, - // and the error can't be propagated out of drop anyway, so just - // assert success. - assert_eq!(status, Status::SUCCESS); - } -} - -impl<'a, P: Protocol + ?Sized> Deref for ScopedProtocol<'a, P> { - type Target = P; - - #[track_caller] - fn deref(&self) -> &Self::Target { - unsafe { &*self.interface.unwrap().get() } - } -} - -impl<'a, P: Protocol + ?Sized> DerefMut for ScopedProtocol<'a, P> { - #[track_caller] - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.interface.unwrap().get() } - } -} - -impl<'a, P: Protocol + ?Sized> ScopedProtocol<'a, P> { - /// Get the protocol interface data, or `None` if the open protocol's - /// interface is null. - #[must_use] - pub fn get(&self) -> Option<&P> { - self.interface.map(|p| unsafe { &*p.get() }) - } - - /// Get the protocol interface data, or `None` if the open protocol's - /// interface is null. - #[must_use] - pub fn get_mut(&self) -> Option<&mut P> { - self.interface.map(|p| unsafe { &mut *p.get() }) - } -} - -/// Protocol interface [`Guids`][Guid] that are installed on a [`Handle`] as -/// returned by [`BootServices::protocols_per_handle`]. -#[deprecated = "Use uefi::boot::ProtocolsPerHandle instead. See https://github.com/rust-osdev/uefi-rs/blob/HEAD/docs/funcs_migration.md"] -#[derive(Debug)] -pub struct ProtocolsPerHandle<'a> { - // The pointer returned by `protocols_per_handle` has to be free'd with - // `free_pool`, so keep a reference to boot services for that purpose. - boot_services: &'a BootServices, - - protocols: *mut &'a Guid, - count: usize, -} - -impl<'a> Drop for ProtocolsPerHandle<'a> { - fn drop(&mut self) { - // Ignore the result, we can't do anything about an error here. - let _ = unsafe { self.boot_services.free_pool(self.protocols.cast::()) }; - } -} - -impl<'a> Deref for ProtocolsPerHandle<'a> { - type Target = [&'a Guid]; - - fn deref(&self) -> &Self::Target { - unsafe { slice::from_raw_parts(self.protocols, self.count) } - } -} - -impl<'a> ProtocolsPerHandle<'a> { - /// Get the protocol interface [`Guids`][Guid] that are installed on the - /// [`Handle`]. - #[allow(clippy::missing_const_for_fn)] // Required until we bump the MSRV. - #[deprecated = "use Deref instead"] - #[must_use] - pub fn protocols<'b>(&'b self) -> &'b [&'a Guid] { - // convert raw pointer to slice here so that we can get - // appropriate lifetime of the slice. - unsafe { slice::from_raw_parts(self.protocols, self.count) } - } -} - -/// A buffer that contains an array of [`Handles`][Handle] that support the -/// requested protocol. Returned by [`BootServices::locate_handle_buffer`]. -#[deprecated = "Use uefi::boot::HandleBuffer instead. See https://github.com/rust-osdev/uefi-rs/blob/HEAD/docs/funcs_migration.md"] -#[derive(Debug)] -pub struct HandleBuffer<'a> { - // The pointer returned by `locate_handle_buffer` has to be freed with - // `free_pool`, so keep a reference to boot services for that purpose. - boot_services: &'a BootServices, - count: usize, - buffer: *mut Handle, -} - -impl<'a> Drop for HandleBuffer<'a> { - fn drop(&mut self) { - // Ignore the result, we can't do anything about an error here. - let _ = unsafe { self.boot_services.free_pool(self.buffer.cast::()) }; - } -} - -impl<'a> Deref for HandleBuffer<'a> { - type Target = [Handle]; - - fn deref(&self) -> &Self::Target { - unsafe { slice::from_raw_parts(self.buffer, self.count) } - } -} - -impl<'a> HandleBuffer<'a> { - /// Get an array of [`Handles`][Handle] that support the requested protocol. - #[allow(clippy::missing_const_for_fn)] // Required until we bump the MSRV. - #[deprecated = "use Deref instead"] - #[must_use] - pub fn handles(&self) -> &[Handle] { - // convert raw pointer to slice here so that we can get - // appropriate lifetime of the slice. - unsafe { slice::from_raw_parts(self.buffer, self.count) } - } -} diff --git a/uefi/src/table/mod.rs b/uefi/src/table/mod.rs index c991743c9..35b6f80b4 100644 --- a/uefi/src/table/mod.rs +++ b/uefi/src/table/mod.rs @@ -1,6 +1,5 @@ //! Standard UEFI tables. -pub mod boot; pub mod cfg; mod header; diff --git a/uefi/src/table/system.rs b/uefi/src/table/system.rs index 2d32219f1..8cdcd2adb 100644 --- a/uefi/src/table/system.rs +++ b/uefi/src/table/system.rs @@ -1,6 +1,5 @@ #![allow(deprecated)] -use super::boot::BootServices; use super::{cfg, Revision}; use crate::proto::console::text; use crate::CStr16; @@ -136,12 +135,6 @@ impl SystemTable { unsafe { &mut *(*self.table).stderr.cast() } } - /// Access boot services - #[must_use] - pub const fn boot_services(&self) -> &BootServices { - unsafe { &*(*self.table).boot_services.cast_const().cast() } - } - /// Clone this boot-time UEFI system table interface /// /// # Safety From b9c49446c7a2744484fb014e9160d1a971f02bd0 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Sat, 28 Sep 2024 12:04:57 -0400 Subject: [PATCH 2/2] uefi: Delete the uefi::table::system module --- uefi/CHANGELOG.md | 8 +- uefi/src/prelude.rs | 4 - uefi/src/table/mod.rs | 23 +----- uefi/src/table/system.rs | 157 --------------------------------------- 4 files changed, 5 insertions(+), 187 deletions(-) delete mode 100644 uefi/src/table/system.rs diff --git a/uefi/CHANGELOG.md b/uefi/CHANGELOG.md index 68342ea91..9f9fbaea3 100644 --- a/uefi/CHANGELOG.md +++ b/uefi/CHANGELOG.md @@ -4,11 +4,11 @@ See [Deprecating SystemTable/BootServices/RuntimeServices][funcmigrate] for details of the deprecated items that were removed in this release. ## Changed -- **Breaking:** Deleted the deprecated `BootServices` and `RuntimeServices` - structs. +- **Breaking:** Deleted the deprecated `BootServices`, `RuntimeServices`, and + `SystemTable` structs. - **Breaking:** Deleted deprecated functions `allocator::init`, - `allocator::exit_boot_services`, `helpers::system_table`, and - `table::system_table_runtime`. + `allocator::exit_boot_services`, `helpers::system_table`, + `table::system_table_boot`, and `table::system_table_runtime`. - **Breaking:** `FileSystem` no longer has a lifetime parameter, and the deprecated conversion from `uefi::table::boot::ScopedProtocol` has been removed. diff --git a/uefi/src/prelude.rs b/uefi/src/prelude.rs index a2ae5eb75..655b6fa39 100644 --- a/uefi/src/prelude.rs +++ b/uefi/src/prelude.rs @@ -5,7 +5,3 @@ pub use crate::{ boot, cstr16, cstr8, entry, runtime, system, Handle, ResultExt, Status, StatusExt, }; - -// Import the basic table types. -#[allow(deprecated)] -pub use crate::table::{Boot, SystemTable}; diff --git a/uefi/src/table/mod.rs b/uefi/src/table/mod.rs index 35b6f80b4..57f4d7c6a 100644 --- a/uefi/src/table/mod.rs +++ b/uefi/src/table/mod.rs @@ -3,11 +3,8 @@ pub mod cfg; mod header; -mod system; pub use header::Header; -#[allow(deprecated)] -pub use system::{Boot, SystemTable}; pub use uefi_raw::table::Revision; use core::ptr::{self, NonNull}; @@ -53,30 +50,12 @@ pub(crate) fn system_table_raw_panicking() -> NonNull Option> { - let st = SYSTEM_TABLE.load(Ordering::Acquire); - if st.is_null() { - return None; - } - - // SAFETY: the system table is valid per the requirements of `set_system_table`. - unsafe { - if (*st).boot_services.is_null() { - None - } else { - Some(SystemTable::::from_ptr(st.cast()).unwrap()) - } - } -} - /// Common trait implemented by all standard UEFI tables. pub trait Table { /// A unique number assigned by the UEFI specification diff --git a/uefi/src/table/system.rs b/uefi/src/table/system.rs deleted file mode 100644 index 8cdcd2adb..000000000 --- a/uefi/src/table/system.rs +++ /dev/null @@ -1,157 +0,0 @@ -#![allow(deprecated)] - -use super::{cfg, Revision}; -use crate::proto::console::text; -use crate::CStr16; -use core::ffi::c_void; -use core::marker::PhantomData; -use core::ptr::NonNull; -use core::slice; - -/// Marker trait used to provide different views of the UEFI System Table. -#[deprecated = "Use the uefi::system, uefi::boot, and uefi::runtime modules instead. See https://github.com/rust-osdev/uefi-rs/blob/HEAD/docs/funcs_migration.md"] -pub trait SystemTableView {} - -/// Marker struct associated with the boot view of the UEFI System Table. -#[deprecated = "Use the uefi::boot module instead. See https://github.com/rust-osdev/uefi-rs/blob/HEAD/docs/funcs_migration.md"] -#[derive(Debug)] -pub struct Boot; -impl SystemTableView for Boot {} - -/// UEFI System Table interface -/// -/// The UEFI System Table is the gateway to all UEFI services which an UEFI -/// application is provided access to on startup. However, not all UEFI services -/// will remain accessible forever. -/// -/// Some services, called "boot services", may only be called during a bootstrap -/// stage where the UEFI firmware still has control of the hardware, and will -/// become unavailable once the firmware hands over control of the hardware to -/// an operating system loader. Others, called "runtime services", may still be -/// used after that point, but require a rather specific CPU configuration which -/// an operating system loader is unlikely to preserve. -/// -/// We handle this state transition by providing two different views of the UEFI -/// system table, the "Boot" view and the "Runtime" view. An UEFI application -/// is initially provided with access to the "Boot" view, and may transition -/// to the "Runtime" view through the ExitBootServices mechanism that is -/// documented in the UEFI spec. At that point, the boot view of the system -/// table will be destroyed (which conveniently invalidates all references to -/// UEFI boot services in the eye of the Rust borrow checker) and a runtime view -/// will be provided to replace it -#[deprecated = "Use the uefi::system, uefi::boot, and uefi::runtime modules instead. See https://github.com/rust-osdev/uefi-rs/blob/HEAD/docs/funcs_migration.md"] -#[derive(Debug)] -#[repr(transparent)] -pub struct SystemTable { - table: *const uefi_raw::table::system::SystemTable, - _marker: PhantomData, -} - -// These parts of the UEFI System Table interface will always be available -impl SystemTable { - /// Return the firmware vendor string - #[must_use] - pub fn firmware_vendor(&self) -> &CStr16 { - unsafe { CStr16::from_ptr((*self.table).firmware_vendor.cast()) } - } - - /// Return the firmware revision - #[must_use] - pub const fn firmware_revision(&self) -> u32 { - unsafe { (*self.table).firmware_revision } - } - - /// Returns the revision of this table, which is defined to be - /// the revision of the UEFI specification implemented by the firmware. - #[must_use] - pub const fn uefi_revision(&self) -> Revision { - unsafe { (*self.table).header.revision } - } - - /// Returns the config table entries, a linear array of structures - /// pointing to other system-specific tables. - #[allow(clippy::missing_const_for_fn)] // Required until we bump the MSRV. - #[must_use] - pub fn config_table(&self) -> &[cfg::ConfigTableEntry] { - unsafe { - let table = &*self.table; - table - .configuration_table - .cast::() - .as_ref() - .map(|ptr| slice::from_raw_parts(ptr, table.number_of_configuration_table_entries)) - .unwrap_or(&[]) - } - } - - /// Creates a new `SystemTable` from a raw address. The address might - /// come from the Multiboot2 information structure or something similar. - /// - /// # Example - /// ```no_run - /// use core::ffi::c_void; - /// use uefi::prelude::{Boot, SystemTable}; - /// - /// let system_table_addr = 0xdeadbeef as *mut c_void; - /// - /// let mut uefi_system_table = unsafe { - /// SystemTable::::from_ptr(system_table_addr).expect("Pointer must not be null!") - /// }; - /// ``` - /// - /// # Safety - /// This function is unsafe because the caller must be sure that the pointer - /// is valid. Otherwise, further operations on the object might result in - /// undefined behaviour, even if the methods aren't marked as unsafe. - pub unsafe fn from_ptr(ptr: *mut c_void) -> Option { - NonNull::new(ptr.cast()).map(|ptr| Self { - table: ptr.as_ref(), - _marker: PhantomData, - }) - } - - /// Get the underlying raw pointer. - #[must_use] - pub const fn as_ptr(&self) -> *const c_void { - self.table.cast() - } -} - -// These parts of the UEFI System Table interface may only be used until boot -// services are exited and hardware control is handed over to the OS loader -impl SystemTable { - /// Returns the standard input protocol. - pub fn stdin(&mut self) -> &mut text::Input { - unsafe { &mut *(*self.table).stdin.cast() } - } - - /// Returns the standard output protocol. - pub fn stdout(&mut self) -> &mut text::Output { - unsafe { &mut *(*self.table).stdout.cast() } - } - - /// Returns the standard error protocol. - pub fn stderr(&mut self) -> &mut text::Output { - unsafe { &mut *(*self.table).stderr.cast() } - } - - /// Clone this boot-time UEFI system table interface - /// - /// # Safety - /// - /// This is unsafe because you must guarantee that the clone will not be - /// used after boot services are exited. However, the singleton-based - /// designs that Rust uses for memory allocation, logging, and panic - /// handling require taking this risk. - #[must_use] - pub const unsafe fn unsafe_clone(&self) -> Self { - Self { - table: self.table, - _marker: PhantomData, - } - } -} - -impl super::Table for SystemTable { - const SIGNATURE: u64 = 0x5453_5953_2049_4249; -}