UEFI SPEC目前不支持多线程,是单线程环境,但是它必须提供一种异步机制来支持有限多任务。UEFI不再为开发者提供中断支持,也就是说我们无法利用UEFI提供的接口设置中断服务程序,那么如果来实现异步操作呢?那就用到了本章所介绍的事件(Event)功能,UEFI提供了对事件Event的支持,Event的内部实现当然也是依赖与中断的。事件的支持是由BootServices启动服务表提供的。
事件接口
主要包含如下接口:
EFI_CREATE_EVENT CreateEvent;
EFI_CREATE_EVENT_EX CreateEventEx;
EFI_SET_TIMER SetTimer;
EFI_WAIT_FOR_EVENT WaitForEvent;
EFI_SIGNAL_EVENT SignalEvent;
EFI_CLOSE_EVENT CloseEvent;
EFI_CHECK_EVENT CheckEvent;
既然是异步处理,那么就会有一个优先级的问题,BootServices同样提供了任务优先级处理函数:
EFI_RAISE_TPL RaiseTPL;
EFI_RESTORE_TPL RestoreTPL;
一个事件生产者会使用到的接口是CreateEvent/CreateEventEx/SignalEvent/CloseEvent,事件使用者会使用的接口为WaitForEvent/CheckEvent。
typedef
EFI_STATUS
(EFIAPI *EFI_CREATE_EVENT)(
IN UINT32 Type,
IN EFI_TPL NotifyTpl,
IN EFI_EVENT_NOTIFY NotifyFunction,
IN VOID *NotifyContext,
OUT EFI_EVENT *Event
);
Type
事件类型:EVT_TIMER //定时器事件,没有notify函数 EVT_NOTIFY_WAIT //普通事件,有notify函数,当WaitForEvent()和CheckEvent()时,此notify函数被放到待执行队列 EVT_NOTIFY_SIGNAL//普通事件,有notify函数,当SignalEvent()触发时,此notify函数被放到待执行队列,通常配合定时器事件使用 EVT_SIGNAL_EXIT_BOOT_SERVICES //特殊事件 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE //特殊事件 EVT_RUNTIME_CONTEXT //特殊事件 EVT_RUNTIME //特殊事件 EVT_TIMER | EVT_NOTIFY_WAIT //带有notify函数的定时器事件,触发类型和EVT_NOTIFY_WAIT EVT_TIMER | EVT_NOTIFY_SIGNAL //带有notify函数的定时器事件,触发类型和EVT_NOTIFY_SIGNAL一样
由此函数原型,可知我们在创建事件时需要传入一个NotifyFunction,这个就是event回调函数,当事件发生时要执行的操作。
NotifyTpl
事件优先级别:// // Task priority level // #define TPL_APPLICATION 4 #define TPL_CALLBACK 8 #define TPL_NOTIFY 16 #define TPL_HIGH_LEVEL 31
其数值越大,优先级别越高。上面定义的运行级别是针对UEFI中的所有任务而言,而并非仅仅针对事件,对于大部分事件,其NotifyTpl一般都可以设置为TPL_NOTIFY级别。
NotifyFunction
Event回调函数,根据Type类型的不同,可以选择传入此函数,定时器类型是不需要此参数的,所以要设置为NULL。NotifyContext
回调函数的参数。Event
返回创建成功的Event句柄。
定时器事件
对于定时器事件的创建,我们需要执行如下步骤:
1.创建EVT_TIMER类型的事件
2.通过SetTimer设置定时器属性
实例:
EFI_STATUS MyTimer()
{
EFI_STATUS Status;
EFI_STATUS myEvent;
UINTN index = 0;
//step 1
Status = gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, (EFI_EVENT_NOTIFY)NULL, (void *)NULL, &myEvent);
//step 2
Status = gBS->SetTimer(myEvent, TimerPeriodic, 10 * 1000 * 1000);
while(1) {
Status = gBS->WaitForEvent(1, &myEvent, &index);
//timeout
}
gBS->CloseEvent(myEvent);
return EFI_SUCCESS;
}
SetTimer的第二个参数,可以选择定时器的类型:
TimerCancel //用于取消定时器触发,设置后定时器不再触发
TimerPeriodic //周期定时器 (单位100ns)
TimerRelative //一次性定时器(单位100ns)
时钟中断
UEFI内核实现采用的是一个单线程机制,不支持多线程操作,同时不对外提供中断支持,然而实际上异步操作的基础还是离不开中断。事件的实现就是利用了时钟中断,在时钟中断处理函数中,UEFI内核会检查系统中的定时器事件并处理到期的定时器时间,并在合适的时机调度事件的Notify函数,因此事件的实现基础就是时钟中断。