Skip to content

Commit fcfe676

Browse files
authored
Merge pull request #1300 from nicholasbishop/bishop-protocol-management
Add freestanding {install,reinstall,uninstall}_protocol_interface functions
2 parents 50b5141 + e06b7fd commit fcfe676

File tree

2 files changed

+134
-2
lines changed

2 files changed

+134
-2
lines changed

uefi-test-runner/src/boot/misc.rs

+44-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use uefi::table::boot::{
99
Tpl,
1010
};
1111
use uefi::table::{Boot, SystemTable};
12-
use uefi::{boot, guid, Event, Guid, Identify};
12+
use uefi::{boot, guid, Event, Guid, Identify, Status};
1313

1414
pub fn test(st: &SystemTable<Boot>) {
1515
let bt = st.boot_services();
@@ -25,6 +25,7 @@ pub fn test(st: &SystemTable<Boot>) {
2525
test_watchdog(bt);
2626
info!("Testing protocol handler services...");
2727
test_register_protocol_notify(bt);
28+
test_protocol_interface_management();
2829
test_install_protocol_interface(bt);
2930
test_reinstall_protocol_interface(bt);
3031
test_uninstall_protocol_interface(bt);
@@ -142,6 +143,48 @@ fn test_register_protocol_notify(bt: &BootServices) {
142143
.expect("Failed to register protocol notify fn");
143144
}
144145

146+
fn test_protocol_interface_management() {
147+
let mut interface = TestProtocol { data: 123 };
148+
let interface_ptr: *mut _ = &mut interface;
149+
150+
// Install the protocol.
151+
let handle = unsafe {
152+
boot::install_protocol_interface(None, &TestProtocol::GUID, interface_ptr.cast())
153+
}
154+
.unwrap();
155+
156+
// Verify the handle was installed.
157+
assert_eq!(
158+
&*boot::locate_handle_buffer(SearchType::from_proto::<TestProtocol>()).unwrap(),
159+
[handle]
160+
);
161+
162+
// Re-install the protocol.
163+
unsafe {
164+
boot::reinstall_protocol_interface(
165+
handle,
166+
&TestProtocol::GUID,
167+
interface_ptr.cast(),
168+
interface_ptr.cast(),
169+
)
170+
}
171+
.unwrap();
172+
173+
// Uninstall the protocol.
174+
unsafe {
175+
boot::uninstall_protocol_interface(handle, &TestProtocol::GUID, interface_ptr.cast())
176+
}
177+
.unwrap();
178+
179+
// Verify the protocol was uninstalled.
180+
assert_eq!(
181+
boot::locate_handle_buffer(SearchType::from_proto::<TestProtocol>())
182+
.unwrap_err()
183+
.status(),
184+
Status::NOT_FOUND
185+
);
186+
}
187+
145188
fn test_install_protocol_interface(bt: &BootServices) {
146189
info!("Installing TestProtocol");
147190

uefi/src/boot.rs

+90-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use core::ops::{Deref, DerefMut};
1111
use core::ptr::{self, NonNull};
1212
use core::sync::atomic::{AtomicPtr, Ordering};
1313
use core::{mem, slice};
14-
use uefi::{table, Char16, Event, Handle, Result, Status, StatusExt};
14+
use uefi::{table, Char16, Event, Guid, Handle, Result, Status, StatusExt};
15+
use uefi_raw::table::boot::InterfaceType;
1516

1617
#[cfg(doc)]
1718
use {
@@ -371,6 +372,94 @@ pub fn disconnect_controller(
371372
.to_result_with_err(|_| ())
372373
}
373374

375+
/// Installs a protocol interface on a device handle.
376+
///
377+
/// When a protocol interface is installed, firmware will call all functions
378+
/// that have registered to wait for that interface to be installed.
379+
///
380+
/// If `handle` is `None`, a new handle will be created and returned.
381+
///
382+
/// # Safety
383+
///
384+
/// The caller is responsible for ensuring that they pass a valid `Guid` for `protocol`.
385+
///
386+
/// # Errors
387+
///
388+
/// * [`Status::OUT_OF_RESOURCES`]: failed to allocate a new handle.
389+
/// * [`Status::INVALID_PARAMETER`]: this protocol is already installed on the handle.
390+
pub unsafe fn install_protocol_interface(
391+
handle: Option<Handle>,
392+
protocol: &Guid,
393+
interface: *const c_void,
394+
) -> Result<Handle> {
395+
let bt = boot_services_raw_panicking();
396+
let bt = unsafe { bt.as_ref() };
397+
398+
let mut handle = Handle::opt_to_ptr(handle);
399+
((bt.install_protocol_interface)(
400+
&mut handle,
401+
protocol,
402+
InterfaceType::NATIVE_INTERFACE,
403+
interface,
404+
))
405+
.to_result_with_val(|| Handle::from_ptr(handle).unwrap())
406+
}
407+
408+
/// Reinstalls a protocol interface on a device handle. `old_interface` is replaced with `new_interface`.
409+
/// These interfaces may be the same, in which case the registered protocol notifications occur for the handle
410+
/// without replacing the interface.
411+
///
412+
/// As with `install_protocol_interface`, any process that has registered to wait for the installation of
413+
/// the interface is notified.
414+
///
415+
/// # Safety
416+
///
417+
/// The caller is responsible for ensuring that there are no references to the `old_interface` that is being
418+
/// removed.
419+
///
420+
/// # Errors
421+
///
422+
/// * [`Status::NOT_FOUND`]: the old interface was not found on the handle.
423+
/// * [`Status::ACCESS_DENIED`]: the old interface is still in use and cannot be uninstalled.
424+
pub unsafe fn reinstall_protocol_interface(
425+
handle: Handle,
426+
protocol: &Guid,
427+
old_interface: *const c_void,
428+
new_interface: *const c_void,
429+
) -> Result<()> {
430+
let bt = boot_services_raw_panicking();
431+
let bt = unsafe { bt.as_ref() };
432+
433+
(bt.reinstall_protocol_interface)(handle.as_ptr(), protocol, old_interface, new_interface)
434+
.to_result()
435+
}
436+
437+
/// Removes a protocol interface from a device handle.
438+
///
439+
/// # Safety
440+
///
441+
/// The caller is responsible for ensuring that there are no references to a protocol interface
442+
/// that has been removed. Some protocols may not be able to be removed as there is no information
443+
/// available regarding the references. This includes Console I/O, Block I/O, Disk I/o, and handles
444+
/// to device protocols.
445+
///
446+
/// The caller is responsible for ensuring that they pass a valid `Guid` for `protocol`.
447+
///
448+
/// # Errors
449+
///
450+
/// * [`Status::NOT_FOUND`]: the interface was not found on the handle.
451+
/// * [`Status::ACCESS_DENIED`]: the interface is still in use and cannot be uninstalled.
452+
pub unsafe fn uninstall_protocol_interface(
453+
handle: Handle,
454+
protocol: &Guid,
455+
interface: *const c_void,
456+
) -> Result<()> {
457+
let bt = boot_services_raw_panicking();
458+
let bt = unsafe { bt.as_ref() };
459+
460+
(bt.uninstall_protocol_interface)(handle.as_ptr(), protocol, interface).to_result()
461+
}
462+
374463
/// Returns an array of handles that support the requested protocol in a
375464
/// pool-allocated buffer.
376465
///

0 commit comments

Comments
 (0)