【UEFI系列】Event
文章目录
- 前言
- 一、什么是Event,Event相关函数
- 二、Event的功能
- Event的种类
- CreateEvent
- CreateEventEx
- SignalEvent
- CloseEvent
- RegisterProtocolNotify
- WaitForEvent
- CheckEvent
- 三、Timer Event
- 怎么create Timer Event
前言
不同于ARM平台不支持SMI,x86和ARM平台的bios都会支持EVENT。
一、什么是Event,Event相关函数
Event,是UEFI中一个重要的东西,UEFI不再支持interrupt中断,所有的异步操作都要通过Event来完成。
比如说我们设定了一个功能,但是我们不想立即跑,就需要设置触发条件,它触发了才会跑。
比如,一个protocol install好之后功能会跑/exitbootservice了功能会跑/每个10s功能会跑。
Boot Services为开发者提供3类函数:事件相关函数、定时器相关函数、TPL(Task priority Level任务优先级)相关函数,用于用于操作事件、定时器、TPL。
Event, Timer, and Task Priority Functions
函数名 | 作用 |
---|---|
CreateEvent | 生成一个事件对象 |
CreateEventEx | 生成一个事件对象并将该事件加入到一个组内 //一执行就全部执行,比如挂在ReadyToBoot事件组里的 |
CloseEvent | 关闭事件对象 //关了就不能再被触发了,和create是对应的,而不是signal,相当于被删了 |
SignalEvent | 触发事件对象 |
WaitForEvent | 等待事件数组中的任一事件触发 |
CheckEvent | 检查事件状态 //确认是已经被执行还是等待被执行 |
SetTimer | 设置定时器属性 //比如说是周期性触发还是一次性触发,如果是周期性,周期是多少 |
RaiseTPL | 提升任务优先级 //比如说很多event都挂在同一个事件里,设置TPL就能设置执行的优先顺序 |
RestoreTPL | 恢复任务优先级 |
二、Event的功能
Event的种类
Boot Services中事件相关函数有6个,CreateEvent/CreateEventEx、SignalEvent及CloseEvent、WaitForEvent和CheckEvent。
关于Event的种类,Event.c里是这么写的:
/// Enumerate the valid types
UINT32 mEventTable[] = {/// 0x80000200 Timer event with a notification function that is/// queue when the event is signaled with SignalEvent()EVT_TIMER | EVT_NOTIFY_SIGNAL,/// 0x80000000 Timer event without a notification function. It can be/// signaled with SignalEvent() and checked with CheckEvent() or WaitForEvent().EVT_TIMER,/// 0x00000100 Generic event with a notification function that/// can be waited on with CheckEvent() or WaitForEvent()EVT_NOTIFY_WAIT,/// 0x00000200 Generic event with a notification function that/// is queue when the event is signaled with SignalEvent()EVT_NOTIFY_SIGNAL,/// 0x00000201 ExitBootServicesEvent.EVT_SIGNAL_EXIT_BOOT_SERVICES,/// 0x60000202 SetVirtualAddressMapEvent.EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,/// 0x00000000 Generic event without a notification function./// It can be signaled with SignalEvent() and checked with CheckEvent()/// or WaitForEvent().0x00000000,/// 0x80000100 Timer event with a notification function that can be/// waited on with CheckEvent() or WaitForEvent()EVT_TIMER | EVT_NOTIFY_WAIT,
};
宏定义 | 值 | 描述 |
---|---|---|
EVT_TIMER | 0x80000000 | 定时器事件 |
EVT_RUNTIME | 0x40000000 | 在运行时(OS 启动后)仍然有效的事件 |
EVT_NOTIFY_WAIT | 0x00000100 | 当 WaitForEvent() 被调用时触发通知函数 |
EVT_NOTIFY_SIGNAL | 0x00000200 | 当 SignalEvent() 被调用时触发通知函数 |
EVT_SIGNAL_EXIT_BOOT_SERVICES | 0x00000201 | 在 ExitBootServices 被调用时触发 |
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE | 0x60000202 | 在 SetVirtualAddressMap 被调用时触发 |
不同bios厂商还会定义不同的Event Types,比如:
宏定义 | 值 | 描述 |
---|---|---|
EVT_EFI_SIGNAL_READY_TO_BOOT | 0x00000203 | 在 ReadyToBoot 阶段触发 |
EVT_SIGNAL_LEGACY_BOOT | 0x00000204 | 在 Legacy Boot 被触发时调用 |
注意:Ready To Boot 事件发生在 UEFI Boot Manager 准备启动操作系统之前,但在调用 ExitBootServices() 之前。
CreateEvent
实际上CreateEvent的结构体原型:
typedef
EFI_STATUS
(EFIAPI *EFI_CREATE_EVENT)(IN UINT32 Type, //事件类型,就是我们之前介绍的几种typeIN EFI_TPL NotifyTpl, //事件Notify函数的优先级,是0~31的整数IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL, //事件NotifyFunction函数IN VOID *NotifyContext OPTIONAL, //传给事件NotifyFunction函数的参数OUT EFI_EVENT *Event //生成的事件);
typedef
VOID
(EFIAPI *EFI_EVENT_NOTIFY)(IN EFI_EVENT Event,IN VOID *Context);
优先级高的event可以中断优先级低的。关于优先级EDKII这样定义,可以看到NOTIFY的优先级相对比较高:
// Task priority level
#define TPL_APPLICATION 4
#define TPL_CALLBACK 8
#define TPL_NOTIFY 16
#define TPL_HIGH_LEVEL 31
- UEFI EDKII BIOS代码里的EVT_SIGNAL_EXIT_BOOT_SERVICES类型应用:
(exit boot services时出发的event,要执行的函数就是自己定义的ExitBootServicesEventA)
VOID EFIAPI ExitBootServicesEventA ( //其实就是EFI_EVENT_NOTIFY的结构IN EFI_EVENT Event,IN VOID *Context)
{DEBUG ((DEBUG_ERROR,"ExitBootServicesEventA\n"));
}//执行函数里的部分,前后代码懒得写了Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, //事件类型,exit boot services的时候触发TPL_NOTIFY, //优先级为16ExitBootServicesEventA, //执行的函数NULL,&EfiExitBootServicesEvent //event名称);
- UEFI EDKII BIOS代码里的EVT_NOTIFY_SIGNAL类型应用:
(满足条件触发的event,要执行的函数就是自己定义的MyCallback)
RegisterProtocolNotify 是专门用于 UEFI 中的Event机制的一部分。它的主要作用是注册一个事件,当某个指定的 Protocol 被安装或重新安装时触发该事件,从而实现异步通知功能。
VOID EFIAPI MyCallback ( //其实就是EFI_EVENT_NOTIFY的结构IN EFI_EVENT Event,IN VOID *Context)
{DEBUG ((DEBUG_ERROR,"MyCallback\n"));
}//执行函数里的部分,前后代码懒得写了
Status = gBS->CreateEvent(EVT_NOTIFY_SIGNAL, //事件类型TPL_CALLBACK, //优先级为16MyCallback, //执行的函数NULL, &MyEvent); //event名称if(!EFI_ERROR(Status))
{Status = gBS->RegisterProtocolNotify( //只要有地方Install或者Reinstall对应的guid的protocol就会触发&xxxProtocolGuid, //protocol的guidMyEvent, //event名称&CallBack ); //执行的函数
}
CreateEventEx
typedef
EFI_STATUS
(EFIAPI *EFI_CREATE_EVENT_EX)(IN UINT32 Type,IN EFI_TPL NotifyTpl,IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL,IN CONST VOID *NotifyContext OPTIONAL,IN CONST EFI_GUID *EventGroup OPTIONAL, //事件组,特定的guid。比起EFI_CREATE_EVENT多的参数OUT EFI_EVENT *Event);
当挂在这个事件组里的Event被触发后,其他地方此Event挂的函数都会被触发,根据TPL优先级执行。
比如ReadyToBoot这种,就是创建一个event在event组里,然后再触发这个event,挂在这个guid上的event组里的其他event全部触发。
有四个UEFI规定的事件组:
宏名称(前略) | 描述 |
---|---|
_EXIT_BOOT_SERVICES | 当系统调用 ExitBootServices() 时触发 |
_VIRTUAL_ADDRESS_CHANGE | 当系统调用 SetVirtualAddressMap() 时触发 |
_READY_TO_BOOT | 当系统准备启动 OS 时触发 |
_MEMORY_MAP_CHANGE | 当内存映射发生变化时触发 |
SignalEvent
立即触发Event,像如果出发的是事件组里的一个Event,其他此Event挂的函数也随后一起触发了。
Status = gBS->SignalEvent (XXXEvent);
CloseEvent
立即关闭Event,相当于删除,Event再也不会被触发。将Event从内核各个队列中清除,释放掉相关内存(Ex同理)。
Status = gBS->CloseEvent (XXXEvent);
RegisterProtocolNotify
看我在CreateEvent里的2.的那段应用。
用这个函数设置Install protocol的guid,等到时候install 这个guid的protocol时它就会被触发。
WaitForEvent
用于等待事件的发生,函数原型:
typedef
EFI_STATUS
(EFIAPI *EFI_WAIT_FOR_EVENT)(IN UINTN NumberOfEvents, //event数组内的event个数IN EFI_EVENT *Event, //event数组OUT UINTN *Index //返回处于触发态的event在数组内的下标);
WaitForEvent是阻塞操作,直到Event数组内任一事件被触发,或任一事件导致错误出现,WaitForEvent才返回。WaitForEvent从前到后依次检查Event数组内的事件,发现有被触发的事件或遇到错误则返回,如果所有事件都没有被触发,则从头开始重新检查。
当检查到某个事件处于触发态时,*Index赋值为该事件在Event数组中的下标,返回前该事件将重置为
非触发态。
当检查到某个事件是EVT_NOTIFY_SIGNAL类型时,*Index赋值为该事件在Event数组中的下标,并返回
EFI_INVALID_PARAMETER。
举例:
Void myEventNotify(EFI_EVENT Event,VOID *Context)
{static UINTN times=0; //must define static typePrint(L"myEventNotify Wait %din",times);times=times+1;if(times>5){Print(L"SignalEvent start\n");gBS->SignalEvent(Event);Print(L"SignalEvent end\n");}
}EFI_STATUS EventDemoEntryPoint(IN EFI_HANDLEImageHandle,IN EFL_SYSTEM_TABLE*SystemTable
)
{EFL_ STATUS Status;UINTN index=0,EFI_EVENT myEvent;Status = gBS->CreateEvent (EVT_NOTIFY_WAIT,TPL_NOTIFY,myEventNotify.(void*)NULL,&myEvent );Status = gBS->WaitForEvent(1,&myEvent,&index); //会一直停下来去检查myEvent从而触发myEventNotifyreturn Status;
}
Result:在WaitForEvent的循环中,每检查一次myEvent的状态,myEventNotify就执行一次。检查6次后,myEvent被触发,从而WaitForEvent结束等待。
CheckEvent
CheckEvent用于检测事件的状态。与WaitForEvent不同的是,CheckEvent调用后立刻返回。函数原型:
typedef
EFI_STATUS
(EFIAPI *EFI_CHECK_EVENT)(IN EFI_EVENT Event);
根据事件的属性和状态。返回值有如下4种情况:
(1)如果事件是EVT_NOTIFY_SIGNAL类型,则返回EFI_INVALID_PARAMETER。
(2)如果事件处于触发态,则返回EFIL_SUCCESS,并且在返回前,事件重置为非触发态。
(3)如果事件处于非触发态并且事件无Notification函数,则返回EFI_NOT_READY.
(4)如果事件处于非触发态并且事件有Notification函数(此事件只可能是EVT_NOTIFY_WAIT类型),则执行Notification函数。然后,检查事件状态标识,若事件处于触发态,则返回EFI_SUCCESS,否则返回NOT_READY。
三、Timer Event
定时器是一个特殊的事件,生成定时器事件后,可以通过SetTimer服务设
置定时器属性。函数原型:
typedef
EFI_STATUS
(EFIAPI *EFI_SET_TIMER)(IN EFI_EVENT Event, //Timer事件IN EFI_TIMER_DELAY Type, //定时器类别IN UINT64 TriggerTime //定时器触发时间,100ns为一个衡量单位);
/// Timer delay types
typedef enum {/// 用于取消定时器触发事件。设置后定时器不再触发TimerCancel,/// 重复性定时器。每TriggerTime*100ns,定时器触发一次TimerPeriodic,/// 一次性定时器。TriggerTime*100ns时触发TimerRelative
} EFI_TIMER_DELAY;
如果Type为TimerPeriodic并且TriggerTime是0,则定时器每个时钟点都会触发一次。
如果Type为TimerRelative并且TriggerTime是0,则定时器在下个时钟点触发(相当于立即出发)。
怎么create Timer Event
生成定时器事件一般分为两步:
第一步,通过CreateEvent生成一个EVT_TIMER事件。
第二步,通过SetTimer设置这个定时器事件的属性。
EFI_STATUS
TimerCreateTimer
( EFI_EVENT *Event,EFI_EVENT_NOTIFY Callback,VOID *Context,EFI_TIMER_DELAY Delay,UINT64 Trigger,EFI_TPL CallBackTPL )
{EFI_STATUS Status;UINT32 EventType = EFI_EVENT_TIMER;if (Callback != NULL )EventType |= EFI_EVENT_NOTIFY_SIGNAL;Status = gBS->CreateEvent(EventType,CallBackTPL,Callback,Context,Event); //第一步if(EFI_ERROR( Status ))return Status;Status = gBS->SetTimer( *Event, Delay, Trigger ); //第二步if (EFI ERROR( Status ))TimerStopTimer( Event );return Status;
}
更详细的例子:
void
myEventNotify(EFI_EVENT Event,VOID *Context)
{static UINTN times=0; //must define static typePrint(L"myEventNotify Wait %d\n",times);times=times+1;
}EFI_STATUS SetTimerDemoEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable
{EFI_STATUS Status;UINTN i=O;EFI_EVENT myEvent;Print(L"Entry to SetTimerDemoEntryPoint\n");Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL,TPL_CALLBACK,(EFL_EVENT_NOTIFY)myEventNotify,(void*)NULL,&myEvent);Status = gBS->SetTimer(myEvent, TimerPeriodic,10*1000*1000); //周期性for (i = 0; i < 5; i++) {Print(L"Waiting...\n");gBS->Stall(1000 * 1000); // 1秒,等待定时器触发}//终止信号gBS->SignalEvent(myEvent);//关闭eventgBS->CloseEvent(myEvent);Print(L"End to SetTimerDemoEntryPointin");return Status;
}
结果:
Entry to SetTimerDemoEntryPoint
Waiting...
myEventNotify Wait 0
Waiting...
myEventNotify Wait 1
Waiting...
myEventNotify Wait 2
Waiting...
myEventNotify Wait 3
Waiting...
myEventNotify Wait 4
myEventNotify Wait 5
End to SetTimerDemoEntryPoint