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,
};
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 {
assert!(new_tpl <= efi::TPL_HIGH_LEVEL, "Invalid attempt to raise TPL above TPL_HIGH_LEVEL");
let prev_tpl = CURRENT_TPL.fetch_max(new_tpl, Ordering::SeqCst);
assert!(
new_tpl >= prev_tpl,
"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);
assert!(
new_tpl <= prev_tpl,
"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(bs: &mut efi::BootServices) {
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;
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();
}
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(|| {
extern "efiapi" fn dummy_raise_tpl(_new_tpl: efi::Tpl) -> efi::Tpl {
0
}
extern "efiapi" fn dummy_restore_tpl(_old_tpl: efi::Tpl) {}
extern "efiapi" fn dummy_allocate_pages(
_allocation_type: u32,
_memory_type: u32,
_pages: usize,
_memory: *mut u64,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_free_pages(_memory: u64, _pages: usize) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_get_memory_map(
_memory_map_size: *mut usize,
_memory_map: *mut efi::MemoryDescriptor,
_map_key: *mut usize,
_descriptor_size: *mut usize,
_descriptor_version: *mut u32,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_allocate_pool(
_pool_type: u32,
_size: usize,
_buffer: *mut *mut c_void,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_free_pool(_buffer: *mut c_void) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_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 {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_set_timer(_event: efi::Event, _type: u32, _trigger_time: u64) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_wait_for_event(
_number_of_events: usize,
_event: *mut efi::Event,
_index: *mut usize,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_signal_event(_event: efi::Event) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_close_event(_event: efi::Event) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_check_event(_event: efi::Event) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_install_protocol_interface(
_handle: *mut efi::Handle,
_protocol: *mut efi::Guid,
_interface_type: u32,
_interface: *mut c_void,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_reinstall_protocol_interface(
_handle: efi::Handle,
_protocol: *mut efi::Guid,
_old_interface: *mut c_void,
_new_interface: *mut c_void,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_uninstall_protocol_interface(
_handle: efi::Handle,
_protocol: *mut efi::Guid,
_interface: *mut c_void,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_handle_protocol(
_handle: efi::Handle,
_protocol: *mut efi::Guid,
_interface: *mut *mut c_void,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_register_protocol_notify(
_protocol: *mut efi::Guid,
_event: efi::Event,
_registration: *mut *mut c_void,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_locate_handle(
_search_type: u32,
_protocol: *mut efi::Guid,
_search_key: *mut c_void,
_buffer_size: *mut usize,
_buffer: *mut efi::Handle,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_locate_device_path(
_protocol: *mut efi::Guid,
_device_path: *mut *mut r_efi::protocols::device_path::Protocol,
_device: *mut efi::Handle,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_install_configuration_table(
_guid: *mut efi::Guid,
_table: *mut c_void,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_load_image(
_boot_policy: efi::Boolean,
_parent_image_handle: efi::Handle,
_device_path: *mut r_efi::protocols::device_path::Protocol,
_source_buffer: *mut c_void,
_source_size: usize,
_image_handle: *mut efi::Handle,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_start_image(
_image_handle: efi::Handle,
_exit_data_size: *mut usize,
_exit_data: *mut *mut u16,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_exit(
_image_handle: efi::Handle,
_exit_status: efi::Status,
_exit_data_size: usize,
_exit_data: *mut u16,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_unload_image(_image_handle: efi::Handle) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_exit_boot_services(_image_handle: efi::Handle, _map_key: usize) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_get_next_monotonic_count(_count: *mut u64) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_stall(_microseconds: usize) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_set_watchdog_timer(
_timeout: usize,
_watchdog_code: u64,
_data_size: usize,
_watchdog_data: *mut u16,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_connect_controller(
_controller_handle: efi::Handle,
_driver_image_handle: *mut efi::Handle,
_remaining_device_path: *mut r_efi::protocols::device_path::Protocol,
_recursive: efi::Boolean,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_disconnect_controller(
_controller_handle: efi::Handle,
_driver_image_handle: efi::Handle,
_child_handle: efi::Handle,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_open_protocol(
_handle: efi::Handle,
_protocol: *mut efi::Guid,
_interface: *mut *mut c_void,
_agent_handle: efi::Handle,
_controller_handle: efi::Handle,
_attributes: u32,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_close_protocol(
_handle: efi::Handle,
_protocol: *mut efi::Guid,
_agent_handle: efi::Handle,
_controller_handle: efi::Handle,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_open_protocol_information(
_handle: efi::Handle,
_protocol: *mut efi::Guid,
_entry_buffer: *mut *mut efi::OpenProtocolInformationEntry,
_entry_count: *mut usize,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_protocols_per_handle(
_handle: efi::Handle,
_protocol_buffer: *mut *mut *mut efi::Guid,
_protocol_buffer_count: *mut usize,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_locate_handle_buffer(
_search_type: u32,
_protocol: *mut efi::Guid,
_search_key: *mut c_void,
_no_handles: *mut usize,
_buffer: *mut *mut efi::Handle,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_locate_protocol(
_protocol: *mut efi::Guid,
_registration: *mut c_void,
_interface: *mut *mut c_void,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_install_multiple_protocol_interfaces(
_handle: *mut efi::Handle,
_args: *mut c_void,
_more_args: *mut c_void,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_uninstall_multiple_protocol_interfaces(
_handle: efi::Handle,
_args: *mut c_void,
_more_args: *mut c_void,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_calculate_crc32(
_data: *mut c_void,
_data_size: usize,
_crc32: *mut u32,
) -> efi::Status {
efi::Status::SUCCESS
}
extern "efiapi" fn dummy_copy_mem(_destination: *mut c_void, _source: *mut c_void, _length: usize) {}
extern "efiapi" fn dummy_set_mem(_buffer: *mut c_void, _size: usize, _value: u8) {}
extern "efiapi" fn dummy_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 {
efi::Status::SUCCESS
}
let mut boot_services = efi::BootServices {
hdr: efi::TableHeader { signature: 0, revision: 0, header_size: 0, crc32: 0, reserved: 0 },
raise_tpl: dummy_raise_tpl,
restore_tpl: dummy_restore_tpl,
allocate_pages: dummy_allocate_pages,
free_pages: dummy_free_pages,
get_memory_map: dummy_get_memory_map,
allocate_pool: dummy_allocate_pool,
free_pool: dummy_free_pool,
create_event: dummy_create_event,
set_timer: dummy_set_timer,
wait_for_event: dummy_wait_for_event,
signal_event: dummy_signal_event,
close_event: dummy_close_event,
check_event: dummy_check_event,
install_protocol_interface: dummy_install_protocol_interface,
reinstall_protocol_interface: dummy_reinstall_protocol_interface,
uninstall_protocol_interface: dummy_uninstall_protocol_interface,
handle_protocol: dummy_handle_protocol,
reserved: ptr::null_mut(),
register_protocol_notify: dummy_register_protocol_notify,
locate_handle: dummy_locate_handle,
locate_device_path: dummy_locate_device_path,
install_configuration_table: dummy_install_configuration_table,
load_image: dummy_load_image,
start_image: dummy_start_image,
exit: dummy_exit,
unload_image: dummy_unload_image,
exit_boot_services: dummy_exit_boot_services,
get_next_monotonic_count: dummy_get_next_monotonic_count,
stall: dummy_stall,
set_watchdog_timer: dummy_set_watchdog_timer,
connect_controller: dummy_connect_controller,
disconnect_controller: dummy_disconnect_controller,
open_protocol: dummy_open_protocol,
close_protocol: dummy_close_protocol,
open_protocol_information: dummy_open_protocol_information,
protocols_per_handle: dummy_protocols_per_handle,
locate_handle_buffer: dummy_locate_handle_buffer,
locate_protocol: dummy_locate_protocol,
install_multiple_protocol_interfaces: dummy_install_multiple_protocol_interfaces,
uninstall_multiple_protocol_interfaces: dummy_uninstall_multiple_protocol_interfaces,
calculate_crc32: dummy_calculate_crc32,
copy_mem: dummy_copy_mem,
set_mem: dummy_set_mem,
create_event_ex: dummy_create_event_ex,
};
init_events_support(&mut boot_services);
assert!(boot_services.create_event as usize != dummy_create_event as *const () as usize);
assert!(boot_services.create_event_ex as usize != dummy_create_event_ex as *const () as usize);
assert!(boot_services.close_event as usize != dummy_close_event as *const () as usize);
assert!(boot_services.signal_event as usize != dummy_signal_event as *const () as usize);
assert!(boot_services.wait_for_event as usize != dummy_wait_for_event as *const () as usize);
assert!(boot_services.check_event as usize != dummy_check_event as *const () as usize);
assert!(boot_services.set_timer as usize != dummy_set_timer as *const () as usize);
assert!(boot_services.raise_tpl as usize != dummy_raise_tpl as *const () as usize);
assert!(boot_services.restore_tpl as usize != dummy_restore_tpl as *const () as usize);
});
}
}