diff --git a/uefi-test-runner/src/boot/misc.rs b/uefi-test-runner/src/boot/misc.rs index 5768320e4..eacb31bfa 100644 --- a/uefi-test-runner/src/boot/misc.rs +++ b/uefi-test-runner/src/boot/misc.rs @@ -9,7 +9,7 @@ use uefi::table::boot::{ Tpl, }; use uefi::table::{Boot, SystemTable}; -use uefi::{boot, guid, Event, Guid, Identify}; +use uefi::{boot, guid, Event, Guid, Identify, Status}; pub fn test(st: &SystemTable) { let bt = st.boot_services(); @@ -24,6 +24,7 @@ pub fn test(st: &SystemTable) { test_watchdog(bt); info!("Testing protocol handler services..."); test_register_protocol_notify(bt); + test_protocol_interface_management(); test_install_protocol_interface(bt); test_reinstall_protocol_interface(bt); test_uninstall_protocol_interface(bt); @@ -133,6 +134,48 @@ fn test_register_protocol_notify(bt: &BootServices) { .expect("Failed to register protocol notify fn"); } +fn test_protocol_interface_management() { + let mut interface = TestProtocol { data: 123 }; + let interface_ptr: *mut _ = &mut interface; + + // Install the protocol. + let handle = unsafe { + boot::install_protocol_interface(None, &TestProtocol::GUID, interface_ptr.cast()) + } + .unwrap(); + + // Verify the handle was installed. + assert_eq!( + &*boot::locate_handle_buffer(SearchType::from_proto::()).unwrap(), + [handle] + ); + + // Re-install the protocol. + unsafe { + boot::reinstall_protocol_interface( + handle, + &TestProtocol::GUID, + interface_ptr.cast(), + interface_ptr.cast(), + ) + } + .unwrap(); + + // Uninstall the protocol. + unsafe { + boot::uninstall_protocol_interface(handle, &TestProtocol::GUID, interface_ptr.cast()) + } + .unwrap(); + + // Verify the protocol was uninstalled. + assert_eq!( + boot::locate_handle_buffer(SearchType::from_proto::()) + .unwrap_err() + .status(), + Status::NOT_FOUND + ); +} + fn test_install_protocol_interface(bt: &BootServices) { info!("Installing TestProtocol"); diff --git a/uefi/src/boot.rs b/uefi/src/boot.rs index a7be3d2b5..d230dc097 100644 --- a/uefi/src/boot.rs +++ b/uefi/src/boot.rs @@ -11,7 +11,8 @@ use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull}; use core::sync::atomic::{AtomicPtr, Ordering}; use core::{mem, slice}; -use uefi::{table, Char16, Event, Handle, Result, Status, StatusExt}; +use uefi::{table, Char16, Event, Guid, Handle, Result, Status, StatusExt}; +use uefi_raw::table::boot::InterfaceType; #[cfg(doc)] use { @@ -299,6 +300,94 @@ pub fn disconnect_controller( .to_result_with_err(|_| ()) } +/// Installs a protocol interface on a device handle. +/// +/// When a protocol interface is installed, firmware will call all functions +/// that have registered to wait for that interface to be installed. +/// +/// If `handle` is `None`, a new handle will be created and returned. +/// +/// # Safety +/// +/// The caller is responsible for ensuring that they pass a valid `Guid` for `protocol`. +/// +/// # Errors +/// +/// * [`Status::OUT_OF_RESOURCES`]: failed to allocate a new handle. +/// * [`Status::INVALID_PARAMETER`]: this protocol is already installed on the handle. +pub unsafe fn install_protocol_interface( + handle: Option, + protocol: &Guid, + interface: *const c_void, +) -> Result { + let bt = boot_services_raw_panicking(); + let bt = unsafe { bt.as_ref() }; + + let mut handle = Handle::opt_to_ptr(handle); + ((bt.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 notifications 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 +/// +/// * [`Status::NOT_FOUND`]: the old interface was not found on the handle. +/// * [`Status::ACCESS_DENIED`]: the old interface is still in use and cannot be uninstalled. +pub unsafe fn reinstall_protocol_interface( + handle: Handle, + protocol: &Guid, + old_interface: *const c_void, + new_interface: *const c_void, +) -> Result<()> { + let bt = boot_services_raw_panicking(); + let bt = unsafe { bt.as_ref() }; + + (bt.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 +/// +/// * [`Status::NOT_FOUND`]: the interface was not found on the handle. +/// * [`Status::ACCESS_DENIED`]: the interface is still in use and cannot be uninstalled. +pub unsafe fn uninstall_protocol_interface( + handle: Handle, + protocol: &Guid, + interface: *const c_void, +) -> Result<()> { + let bt = boot_services_raw_panicking(); + let bt = unsafe { bt.as_ref() }; + + (bt.uninstall_protocol_interface)(handle.as_ptr(), protocol, interface).to_result() +} + /// Returns an array of handles that support the requested protocol in a /// pool-allocated buffer. ///