use core::{
ffi::c_void,
sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
};
use r_efi::efi;
use patina::pi::protocols::timer;
use patina_internal_cpu::{cpu::EfiCpu, interrupts};
use crate::{
event_db::{SpinLockedEventDb, TimerDelay},
gcd,
protocols::PROTOCOL_DB,
systemtables::EfiSystemTable,
};
pub static EVENT_DB: SpinLockedEventDb = SpinLockedEventDb::new();
static CURRENT_TPL: AtomicUsize = AtomicUsize::new(efi::TPL_APPLICATION);
static SYSTEM_TIME: AtomicU64 = AtomicU64::new(0);
extern "efiapi" fn create_event(
event_type: u32,
notify_tpl: efi::Tpl,
notify_function: Option<efi::EventNotify>,
notify_context: *mut c_void,
event: *mut efi::Event,
) -> efi::Status {
if event.is_null() {
return efi::Status::INVALID_PARAMETER;
}
let notify_context = if !notify_context.is_null() { Some(notify_context) } else { None };
let (event_type, event_group) = match event_type {
efi::EVT_SIGNAL_EXIT_BOOT_SERVICES => (efi::EVT_NOTIFY_SIGNAL, Some(efi::EVENT_GROUP_EXIT_BOOT_SERVICES)),
efi::EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE => {
(efi::EVT_NOTIFY_SIGNAL, Some(efi::EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE))
}
other => (other, None),
};
match EVENT_DB.create_event(event_type, notify_tpl, notify_function, notify_context, event_group) {
Ok(new_event) => {
unsafe { event.write_unaligned(new_event) };
efi::Status::SUCCESS
}
Err(err) => err.into(),
}
}
extern "efiapi" fn create_event_ex(
event_type: u32,
notify_tpl: efi::Tpl,
notify_function: Option<efi::EventNotify>,
notify_context: *const c_void,
event_group: *const efi::Guid,
event: *mut efi::Event,
) -> efi::Status {
if event.is_null() {
return efi::Status::INVALID_PARAMETER;
}
let notify_context = if !notify_context.is_null() { Some(notify_context as *mut c_void) } else { None };
match event_type {
efi::EVT_SIGNAL_EXIT_BOOT_SERVICES | efi::EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE => {
return efi::Status::INVALID_PARAMETER;
}
_ => (),
}
let event_group = if !event_group.is_null() { Some(unsafe { event_group.read_unaligned() }) } else { None };
match EVENT_DB.create_event(event_type, notify_tpl, notify_function, notify_context, event_group) {
Ok(new_event) => {
unsafe { event.write_unaligned(new_event) };
efi::Status::SUCCESS
}
Err(err) => err.into(),
}
}
pub extern "efiapi" fn close_event(event: efi::Event) -> efi::Status {
match EVENT_DB.close_event(event) {
Ok(()) => efi::Status::SUCCESS,
Err(err) => err.into(),
}
}
pub extern "efiapi" fn signal_event(event: efi::Event) -> efi::Status {
match EVENT_DB.signal_event(event) {
Ok(()) => efi::Status::SUCCESS,
Err(err) => err.into(),
}
}
extern "efiapi" fn wait_for_event(
number_of_events: usize,
event_array: *mut efi::Event,
out_index: *mut usize,
) -> efi::Status {
if number_of_events == 0 || event_array.is_null() {
return efi::Status::INVALID_PARAMETER;
}
if CURRENT_TPL.load(Ordering::SeqCst) != efi::TPL_APPLICATION {
return efi::Status::UNSUPPORTED;
}
loop {
let mut event_ptr = event_array;
for index in 0..number_of_events {
let event = unsafe { event_ptr.read_unaligned() };
match check_event(event) {
efi::Status::NOT_READY => (),
status => {
if !out_index.is_null() {
unsafe {
out_index.write_unaligned(index);
};
}
return status;
}
}
event_ptr = unsafe { event_ptr.add(1) };
}
EfiCpu::sleep();
}
}
pub extern "efiapi" fn check_event(event: efi::Event) -> efi::Status {
let event_type = match EVENT_DB.get_event_type(event) {
Ok(event_type) => event_type,
Err(err) => return err.into(),
};
if event_type.is_notify_signal() {
return efi::Status::INVALID_PARAMETER;
}
match EVENT_DB.read_and_clear_signaled(event) {
Ok(signaled) => {
if signaled {
return efi::Status::SUCCESS;
}
}
Err(err) => return err.into(),
}
match EVENT_DB.queue_event_notify(event) {
Ok(()) => (),
Err(err) => return err.into(),
}
let old_tpl = raise_tpl(efi::TPL_HIGH_LEVEL);
restore_tpl(old_tpl);
match EVENT_DB.read_and_clear_signaled(event) {
Ok(signaled) => {
if signaled {
return efi::Status::SUCCESS;
}
}
Err(err) => return err.into(),
}
efi::Status::NOT_READY
}
pub extern "efiapi" fn set_timer(event: efi::Event, timer_type: efi::TimerDelay, trigger_time: u64) -> efi::Status {
let timer_type = match TimerDelay::try_from(timer_type) {
Err(err) => return err,
Ok(timer_type) => timer_type,
};
let (trigger_time, period) = match timer_type {
TimerDelay::Cancel => (None, None),
TimerDelay::Relative => (Some(SYSTEM_TIME.load(Ordering::SeqCst) + trigger_time), None),
TimerDelay::Periodic => (Some(SYSTEM_TIME.load(Ordering::SeqCst) + trigger_time), Some(trigger_time)),
};
match EVENT_DB.set_timer(event, timer_type, trigger_time, period) {
Ok(()) => efi::Status::SUCCESS,
Err(err) => err.into(),
}
}
pub extern "efiapi" fn raise_tpl(new_tpl: efi::Tpl) -> efi::Tpl {
if new_tpl > efi::TPL_HIGH_LEVEL {
panic!("Invalid attempt to raise TPL above TPL_HIGH_LEVEL: {new_tpl:#x?}");
}
let prev_tpl = CURRENT_TPL.fetch_max(new_tpl, Ordering::SeqCst);
if new_tpl < prev_tpl {
panic!("Invalid attempt to raise TPL to lower value. New TPL: {new_tpl:#x?}, Prev TPL: {prev_tpl:#x?}");
}
if (new_tpl == efi::TPL_HIGH_LEVEL) && (prev_tpl < efi::TPL_HIGH_LEVEL) {
interrupts::disable_interrupts();
}
prev_tpl
}
pub extern "efiapi" fn restore_tpl(new_tpl: efi::Tpl) {
let prev_tpl = CURRENT_TPL.fetch_min(new_tpl, Ordering::SeqCst);
if new_tpl > prev_tpl {
panic!("Invalid attempt to restore TPL to higher value. New TPL: {new_tpl:#x?}, Prev TPL: {prev_tpl:#x?}");
}
if new_tpl < prev_tpl {
loop {
static EVENT_NOTIFIES_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
let event =
match EVENT_NOTIFIES_IN_PROGRESS.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {
Ok(_) => {
let result = EVENT_DB.consume_next_event_notify(new_tpl);
EVENT_NOTIFIES_IN_PROGRESS.store(false, Ordering::Release);
result
}
_ => break,
};
let Some(event) = event else {
break;
};
if event.notify_tpl < efi::TPL_HIGH_LEVEL {
interrupts::enable_interrupts();
} else {
interrupts::disable_interrupts();
}
CURRENT_TPL.store(event.notify_tpl, Ordering::SeqCst);
let notify_context = event.notify_context.unwrap_or(core::ptr::null_mut());
if EVENT_DB.get_event_type(event.event).unwrap().is_notify_signal() {
let _ = EVENT_DB.clear_signal(event.event);
}
if let Some(notify_function) = event.notify_function {
(notify_function)(event.event, notify_context);
}
}
}
CURRENT_TPL.store(new_tpl, Ordering::SeqCst);
if new_tpl < efi::TPL_HIGH_LEVEL {
interrupts::enable_interrupts();
}
}
extern "efiapi" fn timer_tick(time: u64) {
let old_tpl = raise_tpl(efi::TPL_HIGH_LEVEL);
SYSTEM_TIME.fetch_add(time, Ordering::SeqCst);
let current_time = SYSTEM_TIME.load(Ordering::SeqCst);
EVENT_DB.timer_tick(current_time);
restore_tpl(old_tpl); }
extern "efiapi" fn timer_available_callback(event: efi::Event, _context: *mut c_void) {
match PROTOCOL_DB.locate_protocol(timer::PROTOCOL_GUID) {
Ok(timer_arch_ptr) => {
let timer_arch_ptr = timer_arch_ptr as *mut timer::Protocol;
let timer_arch = unsafe { &*(timer_arch_ptr) };
(timer_arch.register_handler)(timer_arch_ptr, timer_tick);
if let Err(status_err) = EVENT_DB.close_event(event) {
log::warn!("Could not close event for timer_available_callback due to error {status_err:?}");
}
}
Err(err) => panic!("Unable to locate timer arch: {err:?}"),
}
}
pub fn gcd_map_change(map_change_type: gcd::MapChangeType) {
match map_change_type {
gcd::MapChangeType::AddMemorySpace
| gcd::MapChangeType::AllocateMemorySpace
| gcd::MapChangeType::FreeMemorySpace
| gcd::MapChangeType::RemoveMemorySpace
| gcd::MapChangeType::SetMemoryCapabilities => EVENT_DB.signal_group(efi::EVENT_GROUP_MEMORY_MAP_CHANGE),
gcd::MapChangeType::SetMemoryAttributes => (),
}
}
pub fn init_events_support(st: &mut EfiSystemTable) {
let mut bs = st.boot_services().get();
bs.create_event = create_event;
bs.create_event_ex = create_event_ex;
bs.close_event = close_event;
bs.signal_event = signal_event;
bs.wait_for_event = wait_for_event;
bs.check_event = check_event;
bs.set_timer = set_timer;
bs.raise_tpl = raise_tpl;
bs.restore_tpl = restore_tpl;
st.boot_services().set(bs);
let event = EVENT_DB
.create_event(efi::EVT_NOTIFY_SIGNAL, efi::TPL_CALLBACK, Some(timer_available_callback), None, None)
.expect("Failed to create timer available callback.");
PROTOCOL_DB
.register_protocol_notify(timer::PROTOCOL_GUID, event)
.expect("Failed to register protocol notify on timer arch callback.");
}
#[cfg(test)]
#[coverage(off)]
mod tests {
use super::*;
use crate::test_support;
use std::{ptr, sync::atomic::Ordering};
fn with_locked_state<F: Fn() + std::panic::RefUnwindSafe>(f: F) {
test_support::with_global_lock(|| {
test_support::init_test_logger();
unsafe {
crate::test_support::init_test_gcd(None);
crate::test_support::reset_allocators();
crate::test_support::init_test_protocol_db();
}
let _guard = test_support::StateGuard::new(|| {
unsafe {
crate::GCD.reset();
crate::PROTOCOL_DB.reset();
crate::allocator::reset_allocators();
}
});
f();
})
.unwrap();
}
extern "efiapi" fn test_notify(_event: efi::Event, _context: *mut c_void) {}
static NOTIFY_CALLED: AtomicBool = AtomicBool::new(false);
extern "efiapi" fn tracking_notify(_event: efi::Event, _context: *mut c_void) {
NOTIFY_CALLED.store(true, Ordering::SeqCst);
}
#[test]
fn test_create_event_null_event_pointer() {
with_locked_state(|| {
let result = create_event(0, efi::TPL_APPLICATION, None, ptr::null_mut(), ptr::null_mut());
assert_eq!(result, efi::Status::INVALID_PARAMETER);
});
}
#[test]
fn test_create_event_success() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let result = create_event(0, efi::TPL_APPLICATION, None, ptr::null_mut(), &mut event);
assert_eq!(result, efi::Status::SUCCESS);
});
}
#[test]
fn test_create_event_with_notify_context() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let context = Box::into_raw(Box::new(42)) as *mut c_void;
let result = create_event(0, efi::TPL_APPLICATION, None, context, &mut event);
assert_eq!(result, efi::Status::SUCCESS);
});
}
#[test]
fn test_create_event_with_notify_function() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let notify_fn: Option<efi::EventNotify> = Some(test_notify);
let result = create_event(efi::EVT_NOTIFY_WAIT, efi::TPL_CALLBACK, notify_fn, ptr::null_mut(), &mut event);
assert_eq!(result, efi::Status::SUCCESS);
});
}
#[test]
fn test_create_event_virtual_address_change() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let notify_fn: Option<efi::EventNotify> = Some(test_notify);
let result = create_event(
efi::EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
efi::TPL_CALLBACK,
notify_fn,
ptr::null_mut(),
&mut event,
);
assert_eq!(result, efi::Status::SUCCESS);
});
}
#[test]
fn test_create_event_exit_boot_services() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let notify_fn: Option<efi::EventNotify> = Some(test_notify);
let result = create_event(
efi::EVT_SIGNAL_EXIT_BOOT_SERVICES,
efi::TPL_CALLBACK,
notify_fn,
ptr::null_mut(),
&mut event,
);
assert_eq!(result, efi::Status::SUCCESS);
});
}
#[test]
fn test_create_event_ex_null_event() {
with_locked_state(|| {
let result = create_event_ex(0, efi::TPL_APPLICATION, None, ptr::null(), ptr::null(), ptr::null_mut());
assert_eq!(result, efi::Status::INVALID_PARAMETER);
});
}
#[test]
fn test_create_event_ex_with_event_group() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let event_guid: efi::Guid =
efi::Guid::from_fields(0x87a2e5d9, 0xc34f, 0x4b21, 0x8e, 0x57, &[0x1a, 0xf9, 0x3c, 0x82, 0xd7, 0x6b]);
let notify_fn: Option<efi::EventNotify> = Some(test_notify);
let result = create_event_ex(
efi::EVT_NOTIFY_SIGNAL,
efi::TPL_CALLBACK,
notify_fn,
ptr::null(),
&event_guid,
&mut event,
);
assert_eq!(result, efi::Status::SUCCESS);
});
}
#[test]
fn test_create_event_ex_exit_boot_services() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let result = create_event_ex(
efi::EVT_SIGNAL_EXIT_BOOT_SERVICES,
efi::TPL_CALLBACK,
Some(test_notify),
ptr::null(),
ptr::null(),
&mut event,
);
assert_eq!(result, efi::Status::INVALID_PARAMETER);
});
}
#[test]
fn test_create_event_ex_virtual_address_change() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let result = create_event_ex(
efi::EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
efi::TPL_CALLBACK,
Some(test_notify),
ptr::null(),
ptr::null(),
&mut event,
);
assert_eq!(result, efi::Status::INVALID_PARAMETER);
});
}
#[test]
fn test_close_event() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let notify_fn: Option<efi::EventNotify> = Some(test_notify);
let _ = create_event(
efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
efi::TPL_NOTIFY,
notify_fn,
ptr::null_mut(),
&mut event,
);
let result = EVENT_DB.close_event(event);
assert!(result.is_ok());
assert!(!EVENT_DB.is_valid(event));
});
}
#[test]
fn test_signal_event() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let notify_fn: Option<efi::EventNotify> = Some(test_notify);
let _ = create_event(
efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
efi::TPL_NOTIFY,
notify_fn,
ptr::null_mut(),
&mut event,
);
let result = signal_event(event);
assert_eq!(result, efi::Status::SUCCESS);
assert!(EVENT_DB.read_and_clear_signaled(event).is_ok());
});
}
#[test]
fn test_wait_for_event_signaled() {
with_locked_state(|| {
CURRENT_TPL.store(efi::TPL_APPLICATION, Ordering::SeqCst);
let mut event: efi::Event = ptr::null_mut();
create_event(efi::EVT_NOTIFY_WAIT, efi::TPL_NOTIFY, Some(test_notify), ptr::null_mut(), &mut event);
signal_event(event);
let events: [efi::Event; 1] = [event];
let mut index: usize = 0;
let mut test_wait = || {
let status = wait_for_event(1, events.as_ptr() as *mut efi::Event, &mut index as *mut usize);
assert_eq!(status, efi::Status::SUCCESS);
assert_eq!(index, 0);
};
test_wait();
let _ = close_event(event);
});
}
#[test]
fn test_timer_delay_relative_basic() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let notify_fn: Option<efi::EventNotify> = Some(test_notify);
let result = create_event(
efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
efi::TPL_NOTIFY,
notify_fn,
ptr::null_mut(),
&mut event,
);
assert_eq!(result, efi::Status::SUCCESS);
let initial_time = 1000u64;
SYSTEM_TIME.store(initial_time, Ordering::SeqCst);
let wait_time = 500u64;
let result = set_timer(event, 1 , wait_time);
assert_eq!(result, efi::Status::SUCCESS);
})
}
#[test]
fn test_timer_delay_error_handling() {
with_locked_state(|| {
let invalid_event: efi::Event = ptr::null_mut();
let result = set_timer(invalid_event, 1 , 100);
assert_ne!(result, efi::Status::SUCCESS);
let mut event: efi::Event = ptr::null_mut();
let notify_fn: Option<efi::EventNotify> = Some(test_notify);
let result = create_event(
efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
efi::TPL_NOTIFY,
notify_fn,
ptr::null_mut(),
&mut event,
);
assert_eq!(result, efi::Status::SUCCESS);
let invalid_timer_type = 10; let result = set_timer(event, invalid_timer_type, 100);
assert_ne!(result, efi::Status::SUCCESS);
let _ = EVENT_DB.close_event(event);
});
}
#[test]
fn test_set_timer_cancel() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let notify_fn: Option<efi::EventNotify> = Some(test_notify);
let result = create_event(
efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
efi::TPL_NOTIFY,
notify_fn,
ptr::null_mut(),
&mut event,
);
assert_eq!(result, efi::Status::SUCCESS);
let result = set_timer(event, 1 , 500);
assert_eq!(result, efi::Status::SUCCESS);
let result = set_timer(event, 0 , 0);
assert_eq!(result, efi::Status::SUCCESS);
let _ = close_event(event);
});
}
#[test]
fn test_set_timer_periodic() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let notify_fn: Option<efi::EventNotify> = Some(test_notify);
let result = create_event(
efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
efi::TPL_NOTIFY,
notify_fn,
ptr::null_mut(),
&mut event,
);
assert_eq!(result, efi::Status::SUCCESS);
let result = set_timer(event, 2 , 100);
assert_eq!(result, efi::Status::SUCCESS);
let _ = close_event(event);
});
}
#[test]
fn test_event_notification() {
with_locked_state(|| {
CURRENT_TPL.store(efi::TPL_APPLICATION, Ordering::SeqCst);
NOTIFY_CALLED.store(false, Ordering::SeqCst);
let mut event: efi::Event = ptr::null_mut();
let result = create_event(
efi::EVT_NOTIFY_SIGNAL,
efi::TPL_CALLBACK,
Some(tracking_notify),
ptr::null_mut(),
&mut event,
);
assert_eq!(result, efi::Status::SUCCESS);
let result = signal_event(event);
assert_eq!(result, efi::Status::SUCCESS);
assert!(NOTIFY_CALLED.load(Ordering::SeqCst));
let _ = close_event(event);
});
}
#[test]
fn test_event_notification_with_tpl_change_fires_lower_events() {
with_locked_state(|| {
NOTIFY_CALLED.store(false, Ordering::SeqCst);
extern "efiapi" fn test_tpl_switching_notify(_event: efi::Event, _context: *mut c_void) {
let old_tpl = raise_tpl(efi::TPL_HIGH_LEVEL);
restore_tpl(efi::TPL_APPLICATION);
if old_tpl > efi::TPL_APPLICATION {
raise_tpl(old_tpl);
}
}
let mut event: efi::Event = ptr::null_mut();
let result = create_event(
efi::EVT_NOTIFY_SIGNAL,
efi::TPL_CALLBACK,
Some(tracking_notify),
ptr::null_mut(),
&mut event,
);
assert_eq!(result, efi::Status::SUCCESS);
let mut event2: efi::Event = ptr::null_mut();
let result = create_event(
efi::EVT_NOTIFY_SIGNAL,
efi::TPL_NOTIFY,
Some(test_tpl_switching_notify),
ptr::null_mut(),
&mut event2,
);
assert_eq!(result, efi::Status::SUCCESS);
let old_tpl = raise_tpl(efi::TPL_CALLBACK);
let result = signal_event(event);
assert_eq!(result, efi::Status::SUCCESS);
assert!(!NOTIFY_CALLED.load(Ordering::SeqCst));
let result = signal_event(event2);
assert_eq!(result, efi::Status::SUCCESS);
assert!(NOTIFY_CALLED.load(Ordering::SeqCst));
assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_CALLBACK);
let _ = close_event(event);
let _ = close_event(event2);
restore_tpl(old_tpl);
});
}
#[test]
fn test_wait_for_event_null_parameters() {
with_locked_state(|| {
let mut index: usize = 0;
let events: [efi::Event; 1] = [ptr::null_mut()];
let status = wait_for_event(1, ptr::null_mut(), &mut index as *mut usize);
assert_eq!(status, efi::Status::INVALID_PARAMETER);
let status = wait_for_event(0, events.as_ptr() as *mut efi::Event, &mut index as *mut usize);
assert_eq!(status, efi::Status::INVALID_PARAMETER);
});
}
#[test]
fn test_wait_for_event_wrong_tpl() {
with_locked_state(|| {
let mut index: usize = 0;
let events: [efi::Event; 1] = [ptr::null_mut()];
CURRENT_TPL.store(efi::TPL_NOTIFY, Ordering::SeqCst);
let status = wait_for_event(1, events.as_ptr() as *mut efi::Event, &mut index as *mut usize);
assert_eq!(status, efi::Status::UNSUPPORTED);
CURRENT_TPL.store(efi::TPL_APPLICATION, Ordering::SeqCst);
});
}
#[test]
fn test_check_event_with_invalid_event() {
with_locked_state(|| {
let invalid_event: efi::Event = ptr::null_mut();
let result = check_event(invalid_event);
assert_ne!(result, efi::Status::SUCCESS);
});
}
#[test]
fn test_check_event_notify_signal_type() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let result =
create_event(efi::EVT_NOTIFY_SIGNAL, efi::TPL_NOTIFY, Some(test_notify), ptr::null_mut(), &mut event);
assert_eq!(result, efi::Status::SUCCESS);
let result = check_event(event);
assert_eq!(result, efi::Status::INVALID_PARAMETER);
let _ = close_event(event);
});
}
#[test]
fn test_check_event_signaled_event() {
with_locked_state(|| {
let mut event: efi::Event = ptr::null_mut();
let result =
create_event(efi::EVT_NOTIFY_WAIT, efi::TPL_NOTIFY, Some(test_notify), ptr::null_mut(), &mut event);
assert_eq!(result, efi::Status::SUCCESS);
let result = signal_event(event);
assert_eq!(result, efi::Status::SUCCESS);
let result = check_event(event);
assert_eq!(result, efi::Status::SUCCESS);
let result = check_event(event);
assert_eq!(result, efi::Status::NOT_READY);
let _ = close_event(event);
});
}
#[test]
fn test_raise_tpl_sequence() {
with_locked_state(|| {
let original_tpl = CURRENT_TPL.load(Ordering::SeqCst);
CURRENT_TPL.store(efi::TPL_APPLICATION, Ordering::SeqCst);
let prev_tpl = raise_tpl(efi::TPL_CALLBACK);
assert_eq!(prev_tpl, efi::TPL_APPLICATION);
assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_CALLBACK);
let prev_tpl = raise_tpl(efi::TPL_NOTIFY);
assert_eq!(prev_tpl, efi::TPL_CALLBACK);
assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_NOTIFY);
let prev_tpl = raise_tpl(efi::TPL_HIGH_LEVEL);
assert_eq!(prev_tpl, efi::TPL_NOTIFY);
assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_HIGH_LEVEL);
CURRENT_TPL.store(original_tpl, Ordering::SeqCst);
interrupts::enable_interrupts();
});
}
#[test]
fn test_raise_tpl_too_high() {
with_locked_state(|| {
let too_high_tpl = efi::TPL_HIGH_LEVEL + 1;
let would_panic = too_high_tpl > efi::TPL_HIGH_LEVEL;
assert!(would_panic, "TPL values greater than HIGH_LEVEL should not be allowed");
let original_tpl = CURRENT_TPL.load(Ordering::SeqCst);
CURRENT_TPL.store(efi::TPL_APPLICATION, Ordering::SeqCst);
let prev_tpl = raise_tpl(efi::TPL_HIGH_LEVEL);
assert_eq!(prev_tpl, efi::TPL_APPLICATION);
assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_HIGH_LEVEL);
CURRENT_TPL.store(original_tpl, Ordering::SeqCst);
});
}
#[test]
fn test_raise_tpl_to_lower() {
with_locked_state(|| {
let original_tpl = CURRENT_TPL.load(Ordering::SeqCst);
let current_tpl = efi::TPL_NOTIFY;
let lower_tpl = efi::TPL_CALLBACK;
CURRENT_TPL.store(current_tpl, Ordering::SeqCst);
let would_panic = lower_tpl < current_tpl;
assert!(would_panic, "Attempting to raise TPL to a lower value should cause a panic");
let prev_tpl = raise_tpl(current_tpl); assert_eq!(prev_tpl, current_tpl);
let higher_tpl = efi::TPL_HIGH_LEVEL; let prev_tpl = raise_tpl(higher_tpl);
assert_eq!(prev_tpl, current_tpl);
assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), higher_tpl);
CURRENT_TPL.store(original_tpl, Ordering::SeqCst);
});
}
#[test]
fn test_restore_tpl_sequence() {
with_locked_state(|| {
let original_tpl = CURRENT_TPL.load(Ordering::SeqCst);
CURRENT_TPL.store(efi::TPL_HIGH_LEVEL, Ordering::SeqCst);
interrupts::disable_interrupts();
restore_tpl(efi::TPL_NOTIFY);
assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_NOTIFY);
restore_tpl(efi::TPL_CALLBACK);
assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_CALLBACK);
restore_tpl(efi::TPL_APPLICATION);
assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_APPLICATION);
CURRENT_TPL.store(original_tpl, Ordering::SeqCst);
});
}
#[test]
fn test_restore_tpl_to_higher() {
with_locked_state(|| {
let original_tpl = CURRENT_TPL.load(Ordering::SeqCst);
let current_tpl = efi::TPL_NOTIFY;
let higher_tpl = efi::TPL_HIGH_LEVEL;
CURRENT_TPL.store(current_tpl, Ordering::SeqCst);
let would_panic = higher_tpl > current_tpl;
assert!(would_panic, "Attempting to restore TPL to a higher value should cause a panic");
restore_tpl(current_tpl); assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), current_tpl);
let lower_tpl = efi::TPL_CALLBACK; restore_tpl(lower_tpl);
assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), lower_tpl);
CURRENT_TPL.store(original_tpl, Ordering::SeqCst);
});
}
#[test]
fn test_gcd_map_change() {
with_locked_state(|| {
gcd_map_change(gcd::MapChangeType::AddMemorySpace);
gcd_map_change(gcd::MapChangeType::AllocateMemorySpace);
gcd_map_change(gcd::MapChangeType::FreeMemorySpace);
gcd_map_change(gcd::MapChangeType::RemoveMemorySpace);
gcd_map_change(gcd::MapChangeType::SetMemoryAttributes);
gcd_map_change(gcd::MapChangeType::SetMemoryCapabilities);
});
}
#[test]
fn test_timer_tick() {
with_locked_state(|| {
let original_time = SYSTEM_TIME.load(Ordering::SeqCst);
let test_time = 1000;
timer_tick(test_time);
assert_eq!(SYSTEM_TIME.load(Ordering::SeqCst), original_time + test_time);
SYSTEM_TIME.store(original_time, Ordering::SeqCst);
});
}
#[test]
fn test_init_events_support() {
with_locked_state(|| {
let mut st = EfiSystemTable::allocate_new_table();
init_events_support(&mut st);
let boot_services = st.boot_services().get();
assert_eq!(boot_services.create_event as usize, create_event as *const () as usize);
assert_eq!(boot_services.create_event_ex as usize, create_event_ex as *const () as usize);
assert_eq!(boot_services.close_event as usize, close_event as *const () as usize);
assert_eq!(boot_services.signal_event as usize, signal_event as *const () as usize);
assert_eq!(boot_services.wait_for_event as usize, wait_for_event as *const () as usize);
assert_eq!(boot_services.check_event as usize, check_event as *const () as usize);
assert_eq!(boot_services.set_timer as usize, set_timer as *const () as usize);
assert_eq!(boot_services.raise_tpl as usize, raise_tpl as *const () as usize);
assert_eq!(boot_services.restore_tpl as usize, restore_tpl as *const () as usize);
});
}
}