Skip to content

Add freestanding {install,reinstall,uninstall}_protocol_interface functions #1300

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion uefi-test-runner/src/boot/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Boot>) {
let bt = st.boot_services();
Expand All @@ -24,6 +24,7 @@ pub fn test(st: &SystemTable<Boot>) {
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);
Expand Down Expand Up @@ -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::<TestProtocol>()).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::<TestProtocol>())
.unwrap_err()
.status(),
Status::NOT_FOUND
);
}

fn test_install_protocol_interface(bt: &BootServices) {
info!("Installing TestProtocol");

Expand Down
91 changes: 90 additions & 1 deletion uefi/src/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<Handle>,
protocol: &Guid,
interface: *const c_void,
) -> Result<Handle> {
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.
///
Expand Down