Skip to content

Commit 8f74777

Browse files
Merge pull request #550 from timrobertsdev/protocol-handler-services
Implement additional `BootServices` functions
2 parents 1068d2e + 4d6aabe commit 8f74777

File tree

4 files changed

+225
-16
lines changed

4 files changed

+225
-16
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
- Added structs to represent each type of device path node. All node
1717
types specified in the UEFI 2.10 Specification are now supported.
1818
- Added `DevicePathBuilder` for building new device paths.
19+
- Added `BootServices::install_protocol_interface`,
20+
`BootServices::uninstall_protocol_interface`, and
21+
`BootServices::reinstall_protocol_interface`.
22+
- Added `BootServices::register_protocol_notify`.
23+
- Added `SearchType::ByRegisterNotify`and `ProtocolSearchKey`.
1924

2025
### Changed
2126

src/table/boot.rs

+142-13
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,35 @@ pub struct BootServices {
128128
check_event: unsafe extern "efiapi" fn(event: Event) -> Status,
129129

130130
// Protocol handlers
131-
install_protocol_interface: usize,
132-
reinstall_protocol_interface: usize,
133-
uninstall_protocol_interface: usize,
131+
install_protocol_interface: unsafe extern "efiapi" fn(
132+
handle: &mut Option<Handle>,
133+
guid: &Guid,
134+
interface_type: InterfaceType,
135+
interface: *mut c_void,
136+
) -> Status,
137+
reinstall_protocol_interface: unsafe extern "efiapi" fn(
138+
handle: Handle,
139+
protocol: &Guid,
140+
old_interface: *mut c_void,
141+
new_interface: *mut c_void,
142+
) -> Status,
143+
uninstall_protocol_interface: unsafe extern "efiapi" fn(
144+
handle: Handle,
145+
protocol: &Guid,
146+
interface: *mut c_void,
147+
) -> Status,
134148
handle_protocol:
135149
extern "efiapi" fn(handle: Handle, proto: &Guid, out_proto: &mut *mut c_void) -> Status,
136150
_reserved: usize,
137-
register_protocol_notify: usize,
151+
register_protocol_notify: extern "efiapi" fn(
152+
protocol: &Guid,
153+
event: Event,
154+
registration: *mut ProtocolSearchKey,
155+
) -> Status,
138156
locate_handle: unsafe extern "efiapi" fn(
139157
search_ty: i32,
140-
proto: *const Guid,
141-
key: *mut c_void,
158+
proto: Option<&Guid>,
159+
key: Option<ProtocolSearchKey>,
142160
buf_sz: &mut usize,
143161
buf: *mut MaybeUninit<Handle>,
144162
) -> Status,
@@ -221,8 +239,8 @@ pub struct BootServices {
221239
) -> Status,
222240
locate_handle_buffer: unsafe extern "efiapi" fn(
223241
search_ty: i32,
224-
proto: *const Guid,
225-
key: *const c_void,
242+
proto: Option<&Guid>,
243+
key: Option<ProtocolSearchKey>,
226244
no_handles: &mut usize,
227245
buf: &mut *mut Handle,
228246
) -> Status,
@@ -618,6 +636,72 @@ impl BootServices {
618636
}
619637
}
620638

639+
/// Installs a protocol interface on a device handle. If the inner `Option` in `handle` is `None`,
640+
/// one will be created and added to the list of handles in the system and then returned.
641+
///
642+
/// When a protocol interface is installed, firmware will call all functions that have registered
643+
/// to wait for that interface to be installed.
644+
///
645+
/// # Safety
646+
///
647+
/// The caller is responsible for ensuring that they pass a valid `Guid` for `protocol`.
648+
pub unsafe fn install_protocol_interface(
649+
&self,
650+
mut handle: Option<Handle>,
651+
protocol: &Guid,
652+
interface: *mut c_void,
653+
) -> Result<Handle> {
654+
((self.install_protocol_interface)(
655+
&mut handle,
656+
protocol,
657+
InterfaceType::NATIVE_INTERFACE,
658+
interface,
659+
))
660+
// this `unwrapped_unchecked` is safe, `handle` is guaranteed to be Some() if this call is
661+
// successful
662+
.into_with_val(|| handle.unwrap_unchecked())
663+
}
664+
665+
/// Reinstalls a protocol interface on a device handle. `old_interface` is replaced with `new_interface`.
666+
/// These interfaces may be the same, in which case the registered protocol notifies occur for the handle
667+
/// without replacing the interface.
668+
///
669+
/// As with `install_protocol_interface`, any process that has registered to wait for the installation of
670+
/// the interface is notified.
671+
///
672+
/// # Safety
673+
///
674+
/// The caller is responsible for ensuring that there are no references to the `old_interface` that is being
675+
/// removed.
676+
pub unsafe fn reinstall_protocol_interface(
677+
&self,
678+
handle: Handle,
679+
protocol: &Guid,
680+
old_interface: *mut c_void,
681+
new_interface: *mut c_void,
682+
) -> Result<()> {
683+
(self.reinstall_protocol_interface)(handle, protocol, old_interface, new_interface).into()
684+
}
685+
686+
/// Removes a protocol interface from a device handle.
687+
///
688+
/// # Safety
689+
///
690+
/// The caller is responsible for ensuring that there are no references to a protocol interface
691+
/// that has been removed. Some protocols may not be able to be removed as there is no information
692+
/// available regarding the references. This includes Console I/O, Block I/O, Disk I/o, and handles
693+
/// to device protocols.
694+
///
695+
/// The caller is responsible for ensuring that they pass a valid `Guid` for `protocol`.
696+
pub unsafe fn uninstall_protocol_interface(
697+
&self,
698+
handle: Handle,
699+
protocol: &Guid,
700+
interface: *mut c_void,
701+
) -> Result<()> {
702+
(self.uninstall_protocol_interface)(handle, protocol, interface).into()
703+
}
704+
621705
/// Query a handle for a certain protocol.
622706
///
623707
/// This function attempts to get the protocol implementation of a handle,
@@ -655,6 +739,31 @@ impl BootServices {
655739
})
656740
}
657741

742+
/// Registers `event` to be signalled whenever a protocol interface is registered for
743+
/// `protocol` by `install_protocol_interface()` or `reinstall_protocol_interface()`.
744+
///
745+
/// Once `event` has been signalled, `BootServices::locate_handle()` can be used to identify
746+
/// the newly (re)installed handles that support `protocol`. The returned `SearchKey` on success
747+
/// corresponds to the `search_key` parameter in `locate_handle()`.
748+
///
749+
/// Events can be unregistered from protocol interface notification by calling `close_event()`.
750+
pub fn register_protocol_notify(
751+
&self,
752+
protocol: &Guid,
753+
event: Event,
754+
) -> Result<(Event, SearchType)> {
755+
let mut key: MaybeUninit<ProtocolSearchKey> = MaybeUninit::uninit();
756+
// Safety: we clone `event` a couple times, but there will be only one left once we return.
757+
unsafe { (self.register_protocol_notify)(protocol, event.unsafe_clone(), key.as_mut_ptr()) }
758+
// Safety: as long as this call is successful, `key` will be valid.
759+
.into_with_val(|| unsafe {
760+
(
761+
event.unsafe_clone(),
762+
SearchType::ByRegisterNotify(key.assume_init()),
763+
)
764+
})
765+
}
766+
658767
/// Enumerates all handles installed on the system which match a certain query.
659768
///
660769
/// You should first call this function with `None` for the output buffer,
@@ -677,8 +786,9 @@ impl BootServices {
677786

678787
// Obtain the needed data from the parameters.
679788
let (ty, guid, key) = match search_ty {
680-
SearchType::AllHandles => (0, ptr::null(), ptr::null_mut()),
681-
SearchType::ByProtocol(guid) => (2, guid as *const _, ptr::null_mut()),
789+
SearchType::AllHandles => (0, None, None),
790+
SearchType::ByRegisterNotify(registration) => (1, None, Some(registration)),
791+
SearchType::ByProtocol(guid) => (2, Some(guid), None),
682792
};
683793

684794
let status = unsafe { (self.locate_handle)(ty, guid, key, &mut buffer_size, buffer) };
@@ -1095,8 +1205,9 @@ impl BootServices {
10951205

10961206
// Obtain the needed data from the parameters.
10971207
let (ty, guid, key) = match search_ty {
1098-
SearchType::AllHandles => (0, ptr::null(), ptr::null_mut()),
1099-
SearchType::ByProtocol(guid) => (2, guid as *const _, ptr::null_mut()),
1208+
SearchType::AllHandles => (0, None, None),
1209+
SearchType::ByRegisterNotify(registration) => (1, None, Some(registration)),
1210+
SearchType::ByProtocol(guid) => (2, Some(guid), None),
11001211
};
11011212

11021213
unsafe { (self.locate_handle_buffer)(ty, guid, key, &mut num_handles, &mut buffer) }
@@ -1731,7 +1842,9 @@ pub enum SearchType<'guid> {
17311842
/// If the protocol implements the `Protocol` interface,
17321843
/// you can use the `from_proto` function to construct a new `SearchType`.
17331844
ByProtocol(&'guid Guid),
1734-
// TODO: add ByRegisterNotify once the corresponding function is implemented.
1845+
/// Return all handles that implement a protocol when an interface for that protocol
1846+
/// is (re)installed.
1847+
ByRegisterNotify(ProtocolSearchKey),
17351848
}
17361849

17371850
impl<'guid> SearchType<'guid> {
@@ -1844,3 +1957,19 @@ impl<'a> HandleBuffer<'a> {
18441957
unsafe { slice::from_raw_parts(self.buffer, self.count) }
18451958
}
18461959
}
1960+
1961+
newtype_enum! {
1962+
/// Interface type of a protocol interface
1963+
///
1964+
/// Only has one variant when this was written (v2.10 of the UEFI spec)
1965+
pub enum InterfaceType: i32 => {
1966+
/// Native interface
1967+
NATIVE_INTERFACE = 0,
1968+
}
1969+
}
1970+
1971+
/// Opaque pointer returned by [`BootServices::register_protocol_notify`] to be used
1972+
/// with [`BootServices::locate_handle`] via [`SearchType::ByRegisterNotify`].
1973+
#[derive(Debug, Clone, Copy)]
1974+
#[repr(transparent)]
1975+
pub struct ProtocolSearchKey(NonNull<c_void>);

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

+77-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use core::ffi::c_void;
2-
use core::ptr::NonNull;
2+
use core::ptr::{self, NonNull};
33

4-
use uefi::table::boot::{BootServices, EventType, TimerTrigger, Tpl};
5-
use uefi::Event;
4+
use uefi::proto::Protocol;
5+
use uefi::table::boot::{BootServices, EventType, SearchType, TimerTrigger, Tpl};
6+
use uefi::{unsafe_guid, Event, Identify};
67

78
pub fn test(bt: &BootServices) {
89
info!("Testing timer...");
@@ -12,6 +13,11 @@ pub fn test(bt: &BootServices) {
1213
test_callback_with_ctx(bt);
1314
info!("Testing watchdog...");
1415
test_watchdog(bt);
16+
info!("Testing protocol handler services...");
17+
test_register_protocol_notify(bt);
18+
test_install_protocol_interface(bt);
19+
test_reinstall_protocol_interface(bt);
20+
test_uninstall_protocol_interface(bt);
1521
}
1622

1723
fn test_timer(bt: &BootServices) {
@@ -72,3 +78,71 @@ fn test_watchdog(bt: &BootServices) {
7278
bt.set_watchdog_timer(0, 0x10000, None)
7379
.expect("Could not set watchdog timer");
7480
}
81+
82+
/// Dummy protocol for tests
83+
#[unsafe_guid("1a972918-3f69-4b5d-8cb4-cece2309c7f5")]
84+
#[derive(Protocol)]
85+
struct TestProtocol {}
86+
87+
unsafe extern "efiapi" fn _test_notify(_event: Event, _context: Option<NonNull<c_void>>) {
88+
info!("Protocol was (re)installed and this function notified.")
89+
}
90+
91+
fn test_register_protocol_notify(bt: &BootServices) {
92+
let protocol = &TestProtocol::GUID;
93+
let event = unsafe {
94+
bt.create_event(
95+
EventType::NOTIFY_SIGNAL,
96+
Tpl::NOTIFY,
97+
Some(_test_notify),
98+
None,
99+
)
100+
.expect("Failed to create an event")
101+
};
102+
103+
bt.register_protocol_notify(protocol, event)
104+
.expect("Failed to register protocol notify fn");
105+
}
106+
107+
fn test_install_protocol_interface(bt: &BootServices) {
108+
info!("Installing TestProtocol");
109+
110+
let _ = unsafe {
111+
bt.install_protocol_interface(None, &TestProtocol::GUID, ptr::null_mut())
112+
.expect("Failed to install protocol interface")
113+
};
114+
115+
let _ = bt
116+
.locate_handle_buffer(SearchType::from_proto::<TestProtocol>())
117+
.expect("Failed to find protocol after it was installed");
118+
}
119+
120+
fn test_reinstall_protocol_interface(bt: &BootServices) {
121+
info!("Reinstalling TestProtocol");
122+
let handle = bt
123+
.locate_handle_buffer(SearchType::from_proto::<TestProtocol>())
124+
.expect("Failed to find protocol to uninstall")
125+
.handles()[0];
126+
127+
unsafe {
128+
let _ = bt.reinstall_protocol_interface(
129+
handle,
130+
&TestProtocol::GUID,
131+
ptr::null_mut(),
132+
ptr::null_mut(),
133+
);
134+
}
135+
}
136+
137+
fn test_uninstall_protocol_interface(bt: &BootServices) {
138+
info!("Uninstalling TestProtocol");
139+
let handle = bt
140+
.locate_handle_buffer(SearchType::from_proto::<TestProtocol>())
141+
.expect("Failed to find protocol to uninstall")
142+
.handles()[0];
143+
144+
unsafe {
145+
bt.uninstall_protocol_interface(handle, &TestProtocol::GUID, ptr::null_mut())
146+
.expect("Failed to uninstall protocol interface");
147+
}
148+
}

uefi-test-runner/src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![no_std]
22
#![no_main]
33
#![feature(abi_efiapi)]
4+
#![feature(negative_impls)]
45

56
#[macro_use]
67
extern crate log;

0 commit comments

Comments
 (0)