Skip to content

Commit a654c08

Browse files
Merge pull request #293 from timrobertsdev/impl_event_fns
Implement missing `Event`-related functions
2 parents a4f4fd2 + 14b8318 commit a654c08

File tree

6 files changed

+162
-41
lines changed

6 files changed

+162
-41
lines changed

src/data_types/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,21 @@ impl Handle {
1616
}
1717

1818
/// Handle to an event structure
19-
#[derive(Clone, Copy)]
2019
#[repr(transparent)]
2120
pub struct Event(*mut c_void);
2221

22+
impl Event {
23+
/// Clone this `Event`
24+
///
25+
/// # Safety
26+
/// When an event is closed by calling `BootServices::close_event`, that event and ALL references
27+
/// to it are invalidated and the underlying memory is freed by firmware. The caller must ensure
28+
/// that any clones of a closed `Event` are never used again.
29+
pub unsafe fn unsafe_clone(&self) -> Self {
30+
Self(self.0)
31+
}
32+
}
33+
2334
/// Trait for querying the alignment of a struct
2435
///
2536
/// Needed for dynamic-sized types because `mem::align_of` has a `Sized` bound (due to `dyn Trait`)

src/proto/console/pointer/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ impl<'boot> Pointer<'boot> {
4747

4848
/// Event to be used with `BootServices::wait_for_event()` in order to wait
4949
/// for input from the pointer device
50-
pub fn wait_for_input_event(&self) -> Event {
51-
self.wait_for_input
50+
pub fn wait_for_input_event(&self) -> &Event {
51+
&self.wait_for_input
5252
}
5353

5454
/// Returns a reference to the pointer device information.

src/proto/console/text/input.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ impl Input {
4444

4545
/// Event to be used with `BootServices::wait_for_event()` in order to wait
4646
/// for a key to be available
47-
pub fn wait_for_key_event(&self) -> Event {
48-
self.wait_for_key
47+
pub fn wait_for_key_event(&self) -> &Event {
48+
&self.wait_for_key
4949
}
5050
}
5151

src/table/boot.rs

Lines changed: 98 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use core::cell::UnsafeCell;
1313
use core::ffi::c_void;
1414
use core::fmt::{Debug, Formatter};
1515
use core::mem::{self, MaybeUninit};
16+
use core::ptr::NonNull;
1617
use core::{ptr, slice};
1718

1819
/// Contains pointers to all of the boot services.
@@ -48,17 +49,17 @@ pub struct BootServices {
4849
ty: EventType,
4950
notify_tpl: Tpl,
5051
notify_func: Option<EventNotifyFn>,
51-
notify_ctx: *mut c_void,
52-
event: *mut Event,
52+
notify_ctx: Option<NonNull<c_void>>,
53+
out_event: *mut Event,
5354
) -> Status,
5455
set_timer: unsafe extern "efiapi" fn(event: Event, ty: u32, trigger_time: u64) -> Status,
5556
wait_for_event: unsafe extern "efiapi" fn(
5657
number_of_events: usize,
5758
events: *mut Event,
5859
out_index: *mut usize,
5960
) -> Status,
60-
signal_event: usize,
61-
close_event: usize,
61+
signal_event: extern "efiapi" fn(event: Event) -> Status,
62+
close_event: unsafe extern "efiapi" fn(event: Event) -> Status,
6263
check_event: unsafe extern "efiapi" fn(event: Event) -> Status,
6364

6465
// Protocol handlers
@@ -149,7 +150,14 @@ pub struct BootServices {
149150
set_mem: unsafe extern "efiapi" fn(buffer: *mut u8, len: usize, value: u8),
150151

151152
// New event functions (UEFI 2.0 or newer)
152-
create_event_ex: usize,
153+
create_event_ex: unsafe extern "efiapi" fn(
154+
ty: EventType,
155+
notify_tpl: Tpl,
156+
notify_fn: Option<EventNotifyFn>,
157+
notify_ctx: Option<NonNull<c_void>>,
158+
event_group: Option<NonNull<Guid>>,
159+
out_event: *mut Event,
160+
) -> Status,
153161
}
154162

155163
impl BootServices {
@@ -307,44 +315,81 @@ impl BootServices {
307315
&self,
308316
event_ty: EventType,
309317
notify_tpl: Tpl,
310-
notify_fn: Option<fn(Event)>,
318+
notify_fn: Option<EventNotifyFn>,
319+
notify_ctx: Option<NonNull<c_void>>,
311320
) -> Result<Event> {
312321
// Prepare storage for the output Event
313322
let mut event = MaybeUninit::<Event>::uninit();
314323

315-
// Use a trampoline to handle the impedance mismatch between Rust & C
316-
unsafe extern "efiapi" fn notify_trampoline(e: Event, ctx: *mut c_void) {
317-
let notify_fn: fn(Event) = mem::transmute(ctx);
318-
notify_fn(e); // SAFETY: Aborting panics are assumed here
319-
}
320-
let (notify_func, notify_ctx) = notify_fn
321-
.map(|notify_fn| {
322-
(
323-
Some(notify_trampoline as EventNotifyFn),
324-
notify_fn as fn(Event) as *mut c_void,
325-
)
326-
})
327-
.unwrap_or((None, ptr::null_mut()));
328-
329324
// Now we're ready to call UEFI
330325
(self.create_event)(
331326
event_ty,
332327
notify_tpl,
333-
notify_func,
328+
notify_fn,
329+
notify_ctx,
330+
event.as_mut_ptr(),
331+
)
332+
.into_with_val(|| event.assume_init())
333+
}
334+
335+
/// Creates a new `Event` of type `event_type`. The event's notification function, context,
336+
/// and task priority are specified by `notify_fn`, `notify_ctx`, and `notify_tpl`, respectively.
337+
/// The `Event` will be added to the group of `Event`s identified by `event_group`.
338+
///
339+
/// If no group is specified by `event_group`, this function behaves as if the same parameters
340+
/// had been passed to `create_event()`.
341+
///
342+
/// Event groups are collections of events identified by a shared `Guid` where, when one member
343+
/// event is signaled, all other events are signaled and their individual notification actions
344+
/// are taken. All events are guaranteed to be signaled before the first notification action is
345+
/// taken. All notification functions will be executed in the order specified by their `Tpl`.
346+
///
347+
/// A single event can only be part of a single event group. An event may be removed from an
348+
/// event group by using `close_event()`.
349+
///
350+
/// The `EventType` of an event uses the same values as `create_event()`, except that
351+
/// `EventType::SIGNAL_EXIT_BOOT_SERVICES` and `EventType::SIGNAL_VIRTUAL_ADDRESS_CHANGE`
352+
/// are not valid.
353+
///
354+
/// If `event_type` has `EventType::NOTIFY_SIGNAL` or `EventType::NOTIFY_WAIT`, then `notify_fn`
355+
/// mus be `Some` and `notify_tpl` must be a valid task priority level, otherwise these parameters
356+
/// are ignored.
357+
///
358+
/// More than one event of type `EventType::TIMER` may be part of a single event group. However,
359+
/// there is no mechanism for determining which of the timers was signaled.
360+
///
361+
/// # Safety
362+
///
363+
/// The caller must ensure they are passing a valid `Guid` as `event_group`, if applicable.
364+
pub unsafe fn create_event_ex(
365+
&self,
366+
event_type: EventType,
367+
notify_tpl: Tpl,
368+
notify_fn: Option<EventNotifyFn>,
369+
notify_ctx: Option<NonNull<c_void>>,
370+
event_group: Option<NonNull<Guid>>,
371+
) -> Result<Event> {
372+
let mut event = MaybeUninit::<Event>::uninit();
373+
374+
(self.create_event_ex)(
375+
event_type,
376+
notify_tpl,
377+
notify_fn,
334378
notify_ctx,
379+
event_group,
335380
event.as_mut_ptr(),
336381
)
337382
.into_with_val(|| event.assume_init())
338383
}
339384

340385
/// Sets the trigger for `EventType::TIMER` event.
341-
pub fn set_timer(&self, event: Event, trigger_time: TimerTrigger) -> Result {
386+
pub fn set_timer(&self, event: &Event, trigger_time: TimerTrigger) -> Result {
342387
let (ty, time) = match trigger_time {
343388
TimerTrigger::Cancel => (0, 0),
344389
TimerTrigger::Periodic(hundreds_ns) => (1, hundreds_ns),
345390
TimerTrigger::Relative(hundreds_ns) => (2, hundreds_ns),
346391
};
347-
unsafe { (self.set_timer)(event, ty, time) }.into()
392+
unsafe { (self.set_timer)(event.unsafe_clone(), ty, time) }.into()
348393
}
349394

350395
/// Stops execution until an event is signaled.
@@ -389,6 +434,35 @@ impl BootServices {
389434
)
390435
}
391436

437+
/// Place 'event' in the signaled stated. If 'event' is already in the signaled state,
438+
/// then nothing further occurs and `Status::SUCCESS` is returned. If `event` is of type
439+
/// `EventType::NOTIFY_SIGNAL`, then the event's notification function is scheduled to
440+
/// be invoked at the event's notification task priority level.
441+
///
442+
/// This function may be invoked from any task priority level.
443+
///
444+
/// If `event` is part of an event group, then all of the events in the event group are
445+
/// also signaled and their notification functions are scheduled.
446+
///
447+
/// When signaling an event group, it is possible to create an event in the group, signal
448+
/// it, and then close the event to remove it from the group.
449+
pub fn signal_event(&self, event: &Event) -> Result {
450+
// Safety: cloning this event should be safe, as we're directly passing it to firmware
451+
// and not keeping the clone around.
452+
unsafe { (self.signal_event)(event.unsafe_clone()).into() }
453+
}
454+
455+
/// Removes `event` from any event group to which it belongs and closes it. If `event` was
456+
/// registered with `register_protocol_notify()`, then the corresponding registration will
457+
/// be removed. It is safe to call this function within the corresponding notify function.
458+
///
459+
///
460+
/// Note: The UEFI Specification v2.9 states that this may only return `EFI_SUCCESS`, but,
461+
/// at least for application based on EDK2 (such as OVMF), it may also return `EFI_INVALID_PARAMETER`.
462+
pub fn close_event(&self, event: Event) -> Result {
463+
unsafe { (self.close_event)(event).into() }
464+
}
465+
392466
/// Checks to see if an event is signaled, without blocking execution to wait for it.
393467
///
394468
/// The returned value will be `true` if the event is in the signaled state,
@@ -1116,7 +1190,7 @@ bitflags! {
11161190
}
11171191

11181192
/// Raw event notification function
1119-
type EventNotifyFn = unsafe extern "efiapi" fn(event: Event, context: *mut c_void);
1193+
type EventNotifyFn = unsafe extern "efiapi" fn(event: Event, context: Option<NonNull<c_void>>);
11201194

11211195
/// Timer events manipulation
11221196
pub enum TimerTrigger {

uefi-services/src/lib.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
#![feature(asm)]
1515
#![feature(lang_items)]
1616
#![feature(panic_info_message)]
17+
#![feature(abi_efiapi)]
1718

1819
#[macro_use]
1920
extern crate log;
2021
// Core types.
2122
extern crate uefi;
2223

24+
use core::ffi::c_void;
2325
use core::ptr::NonNull;
2426

2527
use cfg_if::cfg_if;
@@ -81,6 +83,7 @@ pub fn init(st: &mut SystemTable<Boot>) -> Result {
8183
EventType::SIGNAL_EXIT_BOOT_SERVICES,
8284
Tpl::NOTIFY,
8385
Some(exit_boot_services),
86+
None,
8487
)
8588
.map_inner(|_| ())
8689
}
@@ -107,19 +110,19 @@ unsafe fn init_logger(st: &mut SystemTable<Boot>) {
107110
}
108111

109112
/// Notify the utility library that boot services are not safe to call anymore
110-
fn exit_boot_services(_e: Event) {
113+
/// As this is a callback, it must be `extern "efiapi"`.
114+
unsafe extern "efiapi" fn exit_boot_services(_e: Event, _ctx: Option<NonNull<c_void>>) {
111115
// DEBUG: The UEFI spec does not guarantee that this printout will work, as
112116
// the services used by logging might already have been shut down.
113117
// But it works on current OVMF, and can be used as a handy way to
114118
// check that the callback does get called.
115119
//
116120
// info!("Shutting down the UEFI utility library");
117-
unsafe {
118-
SYSTEM_TABLE = None;
119-
if let Some(ref mut logger) = LOGGER {
120-
logger.disable();
121-
}
121+
SYSTEM_TABLE = None;
122+
if let Some(ref mut logger) = LOGGER {
123+
logger.disable();
122124
}
125+
123126
uefi::alloc::exit_boot_services();
124127
}
125128

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

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use core::ffi::c_void;
2+
use core::ptr::NonNull;
3+
4+
use uefi::proto::console::text::Output;
15
use uefi::table::boot::{BootServices, EventType, TimerTrigger, Tpl};
26
use uefi::{prelude::*, Event};
37

@@ -6,26 +10,55 @@ pub fn test(bt: &BootServices) {
610
test_timer(bt);
711
info!("Testing events...");
812
test_event_callback(bt);
13+
test_callback_with_ctx(bt);
914
info!("Testing watchdog...");
1015
test_watchdog(bt);
1116
}
1217

1318
fn test_timer(bt: &BootServices) {
14-
let timer_event = unsafe { bt.create_event(EventType::TIMER, Tpl::APPLICATION, None) }
19+
let timer_event = unsafe { bt.create_event(EventType::TIMER, Tpl::APPLICATION, None, None) }
1520
.expect_success("Failed to create TIMER event");
16-
let mut events = [timer_event];
17-
bt.set_timer(timer_event, TimerTrigger::Relative(5_0 /*00 ns */))
21+
let mut events = unsafe { [timer_event.unsafe_clone()] };
22+
bt.set_timer(&timer_event, TimerTrigger::Relative(5_0 /*00 ns */))
1823
.expect_success("Failed to set timer");
1924
bt.wait_for_event(&mut events)
2025
.expect_success("Wait for event failed");
2126
}
2227

2328
fn test_event_callback(bt: &BootServices) {
24-
fn callback(_event: Event) {
29+
extern "efiapi" fn callback(_event: Event, _ctx: Option<NonNull<c_void>>) {
2530
info!("Inside the event callback");
2631
}
27-
let event = unsafe { bt.create_event(EventType::NOTIFY_WAIT, Tpl::CALLBACK, Some(callback)) }
28-
.expect_success("Failed to create custom event");
32+
33+
let event =
34+
unsafe { bt.create_event(EventType::NOTIFY_WAIT, Tpl::CALLBACK, Some(callback), None) }
35+
.expect_success("Failed to create custom event");
36+
bt.check_event(event)
37+
.expect_success("Failed to check event");
38+
}
39+
40+
fn test_callback_with_ctx(bt: &BootServices) {
41+
extern "efiapi" fn callback(_event: Event, ctx: Option<NonNull<c_void>>) {
42+
info!("Inside the event callback with context");
43+
unsafe {
44+
let ctx = &mut *(ctx.unwrap().as_ptr() as *mut Output);
45+
// Clear the screen as a quick test that we successfully passed context
46+
ctx.clear().expect_success("Failed to clear screen");
47+
}
48+
}
49+
50+
let ctx = unsafe { &mut *(bt.locate_protocol::<Output>().unwrap().unwrap().get()) };
51+
52+
let event = unsafe {
53+
bt.create_event(
54+
EventType::NOTIFY_WAIT,
55+
Tpl::CALLBACK,
56+
Some(callback),
57+
Some(NonNull::new_unchecked(ctx as *mut _ as *mut c_void)),
58+
)
59+
.expect_success("Failed to create event with context")
60+
};
61+
2962
bt.check_event(event)
3063
.expect_success("Failed to check event");
3164
}

0 commit comments

Comments
 (0)