Skip to content
Draft
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
138 changes: 130 additions & 8 deletions uefi-test-runner/src/boot/misc.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use alloc::boxed::Box;
use alloc::vec::Vec;
use core::ffi::c_void;
use core::ptr::{self, NonNull};

use uefi::boot::{
EventType, OpenProtocolAttributes, OpenProtocolParams, SearchType, TimerTrigger, Tpl,
};
use uefi::mem::memory_map::MemoryType;
use uefi::proto::unsafe_protocol;
use uefi::{Event, Guid, Identify, boot, guid, system};
use uefi::proto::device_path::build::DevicePathBuilder;
use uefi::proto::device_path::{DevicePath, build};
use uefi::proto::{ProtocolPointer, unsafe_protocol};
use uefi::{Event, Guid, Identify, ResultExt, boot, cstr16, guid, system};
use uefi_raw::Status;

pub fn test() {
test_tpl();
Expand All @@ -25,6 +30,8 @@ pub fn test() {
test_install_protocol_interface();
test_reinstall_protocol_interface();
test_uninstall_protocol_interface();
test_install_multiple_protocol_interface();
test_uninstall_multiple_protocol_interface();
test_install_configuration_table();
info!("Testing crc32...");
test_calculate_crc32();
Expand Down Expand Up @@ -145,12 +152,11 @@ fn test_register_protocol_notify() {
info!("in callback for test_register_protocol_notify")
}

let protocol = &TestProtocol::GUID;
let event = unsafe {
boot::create_event(EventType::NOTIFY_SIGNAL, Tpl::NOTIFY, Some(callback), None).unwrap()
};

boot::register_protocol_notify(protocol, &event)
boot::register_protocol_notify::<TestProtocol>(&event)
.expect("Failed to register protocol notify fn");
}

Expand All @@ -165,7 +171,7 @@ fn test_install_protocol_interface() {
unsafe { alloc.write(TestProtocol { data: 123 }) };

let _ = unsafe {
boot::install_protocol_interface(None, &TestProtocol::GUID, alloc.cast())
boot::install_protocol_interface::<TestProtocol>(None, alloc.cast())
.expect("Failed to install protocol interface")
};

Expand All @@ -179,9 +185,8 @@ fn test_reinstall_protocol_interface() {
.expect("Failed to find protocol to uninstall")[0];

unsafe {
let _ = boot::reinstall_protocol_interface(
let _ = boot::reinstall_protocol_interface::<TestProtocol>(
handle,
&TestProtocol::GUID,
ptr::null_mut(),
ptr::null_mut(),
);
Expand Down Expand Up @@ -212,13 +217,130 @@ fn test_uninstall_protocol_interface() {
&mut *sp
};

boot::uninstall_protocol_interface(handle, &TestProtocol::GUID, interface_ptr.cast())
boot::uninstall_protocol_interface::<TestProtocol>(handle, interface_ptr.cast())
.expect("Failed to uninstall protocol interface");

boot::free_pool(NonNull::new(interface_ptr.cast()).unwrap()).unwrap();
}
}

fn test_install_multiple_protocol_interface() {
info!("Installing multiple test protocols");

let alloc: *mut TestProtocol =
boot::allocate_pool(MemoryType::BOOT_SERVICES_DATA, size_of::<TestProtocol>())
.unwrap()
.cast()
.as_ptr();
unsafe { alloc.write(TestProtocol { data: 123 }) };

let dvp = {
let mut vec = Vec::new();
DevicePathBuilder::with_vec(&mut vec)
.push(&build::media::FilePath {
path_name: cstr16!("foobar"),
})
.unwrap()
.finalize()
.unwrap()
.to_boxed()
};
// Memory must stay valid as long as handle with interfaces lives:
// => so we leak the memory but will free it in the uninstall hook again.
let dvp = Box::leak(dvp);

let handle = unsafe {
boot::install_multiple_protocol_interface(
None,
&[
(&TestProtocol::GUID, alloc.cast()),
(&DevicePath::GUID, dvp.as_ffi_ptr().cast()),
],
)
.expect("Failed to install protocol interface")
};

// Test we indeed installed the protocols.
{
assert_eq!(
boot::test_protocol::<DevicePath>(OpenProtocolParams {
handle,
agent: boot::image_handle(),
controller: None,
}),
Ok(true)
);
}

// Test that installing the device path protocol multiple times results in
// EFI_ALREADY_STARTED
{
let res = unsafe {
boot::install_multiple_protocol_interface(
Some(handle),
&[(&DevicePath::GUID, dvp.as_ffi_ptr().cast())],
)
};
assert_eq!(res.status(), Status::ALREADY_STARTED);
}
}

fn test_uninstall_multiple_protocol_interface() {
info!("Uninstalling multiple test protocols");

let handles = boot::locate_handle_buffer(SearchType::from_proto::<TestProtocol>())
.expect("Failed to find protocol after it was installed");
let handle = *handles.first().unwrap();

let interface_test_protocol: *mut TestProtocol = unsafe {
let mut sp = boot::open_protocol::<TestProtocol>(
OpenProtocolParams {
handle,
agent: boot::image_handle(),
controller: None,
},
OpenProtocolAttributes::GetProtocol,
)
.unwrap();
assert_eq!(sp.data, 123);
&mut *sp
};

let interface_dvp: *mut DevicePath = unsafe {
let mut sp = boot::open_protocol::<DevicePath>(
OpenProtocolParams {
handle,
agent: boot::image_handle(),
controller: None,
},
OpenProtocolAttributes::GetProtocol,
)
.unwrap();
&mut *sp
};

unsafe {
boot::uninstall_multiple_protocol_interface(
handle,
&[
(&TestProtocol::GUID, interface_test_protocol.cast()),
(&DevicePath::GUID, interface_dvp.cast()),
],
)
}
.expect("should uninstall multiple protocols");

let dvp = unsafe {
DevicePath::mut_ptr_from_ffi(interface_dvp.cast())
.as_mut()
.expect("should be valid device path")
};

// Reconstruct the Rust box to ensure that the object is properly freed in
// the Rust global allocator
let _ = unsafe { Box::from_raw(dvp) };
}

fn test_install_configuration_table() {
// Get the current number of entries.
let initial_table_count = system::with_config_table(|t| t.len());
Expand Down
6 changes: 4 additions & 2 deletions uefi-test-runner/src/proto/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ impl CustomLoadFile2Protocol {

unsafe fn install_protocol(handle: Handle, guid: Guid, protocol: &mut CustomLoadFile2Protocol) {
unsafe {
boot::install_protocol_interface(Some(handle), &guid, addr_of!(*protocol).cast()).unwrap();
boot::install_protocol_interface_by_guid(Some(handle), &guid, addr_of!(*protocol).cast())
.unwrap();
}
}

unsafe fn uninstall_protocol(handle: Handle, guid: Guid, protocol: &mut CustomLoadFile2Protocol) {
unsafe {
boot::uninstall_protocol_interface(handle, &guid, addr_of!(*protocol).cast()).unwrap();
boot::uninstall_protocol_interface_by_guid(handle, &guid, addr_of!(*protocol).cast())
.unwrap();
}
}

Expand Down
11 changes: 11 additions & 0 deletions uefi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
- Added `proto::pci::root_bridge::PciRootBridgeIo::enumerate()`.
- Added `proto::nvme::pass_thru::NvmePassThru::broadcast()`.
- Added `proto::media::block::BlockIO2`.
- Added `boot::test_protocol_by_guid()`
- Added `boot::register_protocol_notify_by_guid()`
- Added `boot::[re_,un_]install_protocol_interface_by_guid()` functions.
- Added `boot::[un_]install_multiple_protocol_interface`. Currently, this
replicates the functionality of the EDK2 implementation rather than using it
due to Rusts limited support for variadic arguments.

## Changed
- Changed ordering of `proto::pci::PciIoAddress` to (bus -> dev -> fun -> reg -> ext_reg).
Expand All @@ -15,6 +21,11 @@
returns `Option<Event>` instead of `&Event`.
- `Http::get_mode_data` doesn't consume a parameter anymore and instead return
an owned value of type `HttpConfigData`
- **Breaking**: `boot::register_protocol_notify()` now follows our generic-based
API and now longer consumes a `&Guid` parameter.
- **Breaking:**`boot::[re_,un_]install_protocol_interface()` no longer consume a
`&Guid` parameter but instead follow our generic type-based API. For example:
`install_protocol_interface<DevicePath>(handle, interface)`.

# uefi - v0.36.1 (2025-11-05)

Expand Down
Loading