Skip to content

Add freestanding set_timer and wait_for_event #1298

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 3 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
9 changes: 9 additions & 0 deletions uefi-test-runner/src/boot/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub fn test(st: &SystemTable<Boot>) {
test_timer(bt);
info!("Testing events...");
test_check_event_freestanding();
test_timer_freestanding();
test_event_callback(bt);
test_callback_with_ctx(bt);
info!("Testing watchdog...");
Expand Down Expand Up @@ -48,6 +49,14 @@ fn test_check_event_freestanding() {
assert!(!is_signaled);
}

fn test_timer_freestanding() {
let timer_event =
unsafe { boot::create_event(EventType::TIMER, Tpl::CALLBACK, None, None) }.unwrap();
let mut events = unsafe { [timer_event.unsafe_clone()] };
boot::set_timer(&timer_event, TimerTrigger::Relative(5_0 /*00 ns */)).unwrap();
assert_eq!(boot::wait_for_event(&mut events).unwrap(), 0);
}

fn test_timer(bt: &BootServices) {
let timer_event = unsafe { bt.create_event(EventType::TIMER, Tpl::APPLICATION, None, None) }
.expect("Failed to create TIMER event");
Expand Down
74 changes: 73 additions & 1 deletion uefi/src/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use {

pub use uefi::table::boot::{
AllocateType, EventNotifyFn, LoadImageSource, OpenProtocolAttributes, OpenProtocolParams,
SearchType,
SearchType, TimerTrigger,
};
pub use uefi_raw::table::boot::{EventType, MemoryType, Tpl};

Expand Down Expand Up @@ -233,6 +233,78 @@ pub fn check_event(event: Event) -> Result<bool> {
}
}

/// Sets the trigger for an event of type [`TIMER`].
///
/// # Errors
///
/// * [`Status::INVALID_PARAMETER`]: `event` is not valid.
///
/// [`TIMER`]: EventType::TIMER
pub fn set_timer(event: &Event, trigger_time: TimerTrigger) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };

let (ty, time) = match trigger_time {
TimerTrigger::Cancel => (0, 0),
TimerTrigger::Periodic(hundreds_ns) => (1, hundreds_ns),
TimerTrigger::Relative(hundreds_ns) => (2, hundreds_ns),
};
unsafe { (bt.set_timer)(event.as_ptr(), ty, time) }.to_result()
}

/// Stops execution until an event is signaled.
///
/// This function must be called at priority level [`Tpl::APPLICATION`].
///
/// The input [`Event`] slice is repeatedly iterated from first to last until
/// an event is signaled or an error is detected. The following checks are
/// performed on each event:
///
/// * If an event is of type [`NOTIFY_SIGNAL`], then a
/// [`Status::INVALID_PARAMETER`] error is returned with the index of the
/// event that caused the failure.
/// * If an event is in the signaled state, the signaled state is cleared
/// and the index of the event that was signaled is returned.
/// * If an event is not in the signaled state but does have a notification
/// function, the notification function is queued at the event's
/// notification task priority level. If the execution of the event's
/// notification function causes the event to be signaled, then the
/// signaled state is cleared and the index of the event that was signaled
/// is returned.
///
/// To wait for a specified time, a timer event must be included in `events`.
///
/// To check if an event is signaled without waiting, an already signaled
/// event can be used as the last event in the slice being checked, or the
/// [`check_event`] interface may be used.
///
/// # Errors
///
/// * [`Status::INVALID_PARAMETER`]: `events` is empty, or one of the events of
/// of type [`NOTIFY_SIGNAL`].
/// * [`Status::UNSUPPORTED`]: the current TPL is not [`Tpl::APPLICATION`].
///
/// [`NOTIFY_SIGNAL`]: EventType::NOTIFY_SIGNAL
pub fn wait_for_event(events: &mut [Event]) -> Result<usize, Option<usize>> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };

let number_of_events = events.len();
let events: *mut uefi_raw::Event = events.as_mut_ptr().cast();

let mut index = 0;
unsafe { (bt.wait_for_event)(number_of_events, events, &mut index) }.to_result_with(
|| index,
|s| {
if s == Status::INVALID_PARAMETER {
Some(index)
} else {
None
}
},
)
}

/// Connect one or more drivers to a controller.
///
/// Usually one disconnects and then reconnects certain drivers
Expand Down