UEFI-EDK2是单核单线程的架构,除了时钟中断外,没有其他中断机制,Event在UEFI中实现了异步执行的能力;
Event相关的接口包括:BootService中定义
(EFI_CREATE_EVENT) CoreCreateEvent,
(EFI_SET_TIMER) CoreSetTimer,
(EFI_WAIT_FOR_EVENT) CoreWaitForEvent,
(EFI_SIGNAL_EVENT) CoreSignalEvent,
(EFI_CLOSE_EVENT) CoreCloseEvent,
(EFI_CHECK_EVENT) CoreCheckEvent,
(EFI_CREATE_EVENT_EX) CoreCreateEventEx;
同时有两个调整优先级的接口与event息息相关;
(EFI_RAISE_TPL) CoreRaiseTpl,
(EFI_RESTORE_TPL) CoreRestoreTpl,
event在EDK2源码中的表示。
EFI_EVENT只是一个VOID型指针typedef VOID* EFI_EVENT;其内部是的实现叫IEVENT;
typedef struct {
UINTN Signature;
UINT32 Type;
UINT32 SignalCount; //事件是否被触发的计数
LIST_ENTRY SignalLink; //链接event到gEventSignalQueue中;
EFI_TPL NotifyTpl;
EFI_EVENT_NOTIFY NotifyFunction;
VOID *NotifyContext;
EFI_GUID EventGroup;
LIST_ENTRY NotifyLink;
UINT8 ExFlag;
EFI_RUNTIME_EVENT_ENTRY RuntimeData;
TIMER_EVENT_INFO Timer;
} IEVENT;
以下通过详细描述event相关的接口来解析event中各元素的作用,以及在代码中的实现;
1. EVENT创建
CoreCreateEvent()/CoreCreateEventEx()最终的实现都在CoreCreateEventInternal ()中,CoreCreateEventEx()中多了EventGroup参数;
Type:事件类型;
NotifyTpl:事件任务优先级;
NotifyFunction:事件唤醒时执行的回调函数;
NotifyContext:回调函数参数;
EventGroup:能绑定多个事件的guid,唤醒时该guid下的事件都会唤醒,需要关注几个常用的EventGroup GUID;
Event:返回event指针;
//不同的事件类型
#define EVT_TIMER 0x80000000
#define EVT_RUNTIME 0x40000000
#define EVT_NOTIFY_WAIT 0x00000100
#define EVT_NOTIFY_SIGNAL 0x00000200
#define EVT_SIGNAL_EXIT_BOOT_SERVICES 0x00000201
#define EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 0x60000202
//数字越大优先级越高
#define TPL_APPLICATION 4
#define TPL_CALLBACK 8
#define TPL_NOTIFY 16
#define TPL_HIGH_LEVEL 31
回调函数定义:
所有有效的事件类型存储在全局变量mEventTable[]中:
INT32 mEventTable[] = {
EVT_TIMER | EVT_NOTIFY_SIGNAL,
EVT_TIMER,
EVT_NOTIFY_WAIT,
EVT_NOTIFY_SIGNAL,
EVT_SIGNAL_EXIT_BOOT_SERVICES, //ExitBootService时唤醒的事件
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
0x00000000,
EVT_TIMER | EVT_NOTIFY_WAIT,
};
EVT_NOTIFY_SIGNAL/EVT_NOTIFY_WAIT/EVT_TIMER: 有回调接口的事件类型;
event创建接口实现:
CoreCreateEventInternal (
IN UINT32 Type,
IN EFI_TPL NotifyTpl,
IN EFI_EVENT_NOTIFY NotifyFunction, OPTIONAL
IN CONST VOID *NotifyContext, OPTIONAL
IN CONST EFI_GUID *EventGroup, OPTIONAL
OUT EFI_EVENT *Event
)
{
//事件类型Type检查
for (Index = 0; Index < (sizeof (mEventTable) / sizeof (UINT32)); Index++) {
if (Type == mEventTable[Index]) {
Status = EFI_SUCCESS;
break;
}
}
if(EFI_ERROR (Status)) {
return EFI_INVALID_PARAMETER;
}
// 两个典型事件组的转换处理
if (EventGroup != NULL) {
//bootService完成后唤醒的event
if (CompareGuid (EventGroup, &gEfiEventExitBootServicesGuid)) {
Type = EVT_SIGNAL_EXIT_BOOT_SERVICES;
} else if (CompareGuid (EventGroup, &gEfiEventVirtualAddressChangeGuid)) {
Type = EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE;
}
} else {
if (Type == EVT_SIGNAL_EXIT_BOOT_SERVICES) {
EventGroup = &gEfiEventExitBootServicesGuid;
} else if (Type == EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) {
EventGroup = &gEfiEventVirtualAddressChangeGuid;
}
}
//需要在runtime阶段不释放的event
if ((Type & EVT_RUNTIME) != 0) {
IEvent = AllocateRuntimeZeroPool (sizeof (IEVENT));
} else {
IEvent = AllocateZeroPool (sizeof (IEVENT));
}
//事件内容填充
IEvent->Signature = EVENT_SIGNATURE;
IEvent->Type = Type;
IEvent->NotifyTpl = NotifyTpl;
IEvent->NotifyFunction = NotifyFunction;
IEvent->NotifyContext = (VOID *)NotifyContext;
if (EventGroup != NULL) {
CopyGuid (&IEvent->EventGroup, EventGroup);
IEvent->ExFlag |= EVT_EXFLAG_EVENT_GROUP; //设置group标记
}
*Event = IEvent; //返回值处理
if ((Type & EVT_RUNTIME) != 0) { //RUMTIME阶段仍有效的EVENT
IEvent->RuntimeData.Type = Type;
IEvent->RuntimeData.NotifyTpl = NotifyTpl;
IEvent->RuntimeData.NotifyFunction = NotifyFunction;
IEvent->RuntimeData.NotifyContext = (VOID *) NotifyContext;
IEvent->RuntimeData.Event = (EFI_EVENT *) IEvent;
//在runtime.c中RuntimeDriverSetVirtualAddressMap处理回调notifyfunction;
//gRT->SetVirtualAddressMap
InsertTailList (&gRuntime->EventHead, &IEvent->RuntimeData.Link);
}
CoreAcquireEventLock (); //通过RaiseTPL实现
//有回调接口的事件能被SignalEvent唤醒
if ((Type & EVT_NOTIFY_SIGNAL) != 0x00000000) {
InsertHeadList (&gEventSignalQueue, &IEvent->SignalLink);
}
CoreReleaseEventLock ();
}
链表gEventSignalQueue:当事件为Signal类型时加入该链表;
2. EVENT触发/唤醒
事件触发,只是将事件放到回调队列gEventQueue,并不是立刻执行回调,在TPL restore时触发回调的执行;
CoreSignalEvent (IN EFI_EVENT UserEvent)
{
CoreAcquireEventLock ();
if (Event->SignalCount == 0x00000000) { //表示事件没有被signal过
Event->SignalCount++;
//事件为signal,证明有回调,则把事件加入回调队列
if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
if ((Event->ExFlag & EVT_EXFLAG_EVENT_GROUP) != 0) {
CoreNotifySignalList (&Event->EventGroup); //事件组处理
} else {
CoreNotifyEvent (Event); //单个事件处理
}
}
}
CoreReleaseEventLock ();
}
CoreNotifySignalList (IN EFI_GUID *EventGroup)
{
Head = &gEventSignalQueue;
for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
Event = CR (Link, IEVENT, SignalLink, EVENT_SIGNATURE);
if (CompareGuid (&Event->EventGroup, EventGroup)) { //事件组比较确认
CoreNotifyEvent (Event); //遍历事件组中的事件挨个处理
}
}
}
CoreNotifyEvent (IN IEVENT *Event)
{
InsertTailList (&gEventQueue[Event->NotifyTpl], &Event->NotifyLink);
gEventPending |= (UINTN)(1 << Event->NotifyTpl);
}
LIST_ENTRY gEventQueue[TPL_HIGH_LEVEL + 1];
分优先级存储有回调接口的事件,CoreDispatchEventNotifies (tpl)从对应gEventQueue[tpl]中取出事件event,遍历执行回调函数;CoreRestoreTpl ()中调用CoreDispatchEventNotifies ;
CoreDispatchEventNotifies (IN EFI_TPL Priority)
{
Head = &gEventQueue[Priority];
while (!IsListEmpty (Head)) {
Event = CR (Head->ForwardLink, IEVENT, NotifyLink, EVENT_SIGNATURE);
RemoveEntryList (&Event->NotifyLink);
Event->NotifyLink.ForwardLink = NULL;
if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
Event->SignalCount = 0; //回调执行会清除触发状态,避免下次Signal时跳过入队;
}
CoreReleaseEventLock ();
Event->NotifyFunction (Event, Event->NotifyContext);
CoreAcquireEventLock ();
}
gEventPending &= ~(UINTN)(1 << Priority);
CoreReleaseEventLock ();
}
gEventPending :A bitmask of the EventQueues that are pending
最高bit不为0表示该tpl上有事件的回调接口需要处理,在CoreRestoreTpl 中实现为:
3. 等待事件发生/检查事件状态
暂停代码往下执行,等待NumberOfEvents各事件中的某个事件发生,并返回事件在数组UserEvent中的下标值UserIndex;
CoreWaitForEvent (
IN UINTN NumberOfEvents,
IN EFI_EVENT *UserEvents,
OUT UINTN *UserIndex
)
{
for(;;) {
for(Index = 0; Index < NumberOfEvents; Index++) {
Status = CoreCheckEvent (UserEvents[Index]); //检查事件是否发生
if (Status != EFI_NOT_READY) { //事件已发生
if (UserIndex != NULL) {
*UserIndex = Index; //返回事件下标
}
return Status;
}
}
CoreSignalEvent (gIdleLoopEvent); //唤醒空闲事件/组
}
}
检查事件状态:
CoreCheckEvent (IN EFI_EVENT UserEvent)
{
如果是wait类型事件,唤醒该事件
//如果是Signal事件且已给唤醒(SignalCount != 0),清楚唤醒标记,便于下次能唤醒入回调队列
}
4. 定时器事件
typedef enum { //定时器类型
TimerCancel, //取消定时器
TimerPeriodic, //周期性定时器
TimerRelative //当前开始计时定时器
} EFI_TIMER_DELAY;
设置定时器:先有EVT_TIMER事件,再设置定时器,谁先满足条件谁先执行
CoreSetTimer (
IN EFI_EVENT UserEvent, //定时器事件
IN EFI_TIMER_DELAY Type,
IN UINT64 TriggerTime // 颗粒度 100ns
)
{
CoreAcquireLock (&mEfiTimerLock);
if (Type != TimerCancel) {
if (Type == TimerPeriodic) { //周期性定时器
if (TriggerTime == 0) {
gTimer->GetTimerPeriod (gTimer, &TriggerTime);
}
Event->Timer.Period = TriggerTime;
}
Event->Timer.TriggerTime = CoreCurrentSystemTime () + TriggerTime;
CoreInsertEventTimer (Event); //插入定时器,由时钟中断触发
if (TriggerTime == 0) {
CoreSignalEvent (mEfiCheckTimerEvent); //计时满足时触发定时器事件
}
}
CoreReleaseLock (&mEfiTimerLock);
}
mEfiTimerList:定时器按到期时间排序插入该链表中;
mEfiCheckTimerEvent:所有定时器的触发入口,在初始化event服务的时候创建,用于触发定时器队列的遍历,可以理解为定时器事件分为两级,一个总的触发入口,定时队列里面成员分别有自己的回调处理;
CoreInitializeEventServices ()----> CoreInitializeTimer()
{
}
每次CoreSignalEvent (mEfiCheckTimerEvent)时触发回调接口CoreCheckTimers()
CoreCheckTimers()
{
CoreAcquireLock (&mEfiTimerLock);
SystemTime = CoreCurrentSystemTime ();
while (!IsListEmpty (&mEfiTimerList)) {
Event = CR (mEfiTimerList.ForwardLink, IEVENT, Timer.Link, EVENT_SIGNATURE);
if (Event->Timer.TriggerTime > SystemTime) { //时间没到不满足,退出
break;
}
//定时器队列中移除定时器
RemoveEntryList (&Event->Timer.Link);
Event->Timer.Link.ForwardLink = NULL;
CoreSignalEvent (Event); //触发定时器事件
if (Event->Timer.Period != 0) {
//周期性timer重新计算事件并加入定时器队列
Event->Timer.TriggerTime = Event->Timer.TriggerTime + Event->Timer.Period;
if (Event->Timer.TriggerTime <= SystemTime) {
Event->Timer.TriggerTime = SystemTime;
CoreSignalEvent (mEfiCheckTimerEvent);
}
//加入定时器队列
CoreInsertEventTimer (Event);
}
}
CoreReleaseLock (&mEfiTimerLock);
}
}
5. 时钟中断的注册和时钟滴答的处理/时钟中断处理
滴答处理函数:
CoreTimerTick (IN UINT64 Duration)
{
CoreAcquireLock (&mEfiSystemTimeLock);
mEfiSystemTime += Duration;
if (!IsListEmpty (&mEfiTimerList)) { //定时器队列
Event = CR (mEfiTimerList.ForwardLink, IEVENT, Timer.Link, EVENT_SIGNATURE);
if (Event->Timer.TriggerTime <= mEfiSystemTime) {
//时间满足则触发定时器队列扫描CoreCheckTimers
CoreSignalEvent (mEfiCheckTimerEvent);
}
}
CoreReleaseLock (&mEfiSystemTimeLock);
}
CoreTimerTick滴答处理函数的注册:参考gBds的初始化流程
GenericProtocolNotify()
{
}
CoreNotifyOnProtocolEntryTable (
EFI_CORE_PROTOCOL_NOTIFY_ENTRY *Entry
)
{
}
gEfiTimerArchProtocolGuid:特定平台定时器的实现和安装,依赖具体的平台实现,如Arm平台实现在edk2-stable-202011\ArmPkg\Drivers\TimerDxe\TimerDxe.c;
回归到注册CoreTimerTick:
gTimer->RegisterHandler (gTimer, CoreTimerTick)最终调用对应平台的TimerDriverRegisterHander()
时钟中断处理函数中调用注册的定时器滴答处理函数
TimerInterruptHandler (
IN HARDWARE_INTERRUPT_SOURCE Source,
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
}
时钟中断处理函数的注册:
TimerInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL, (VOID **)&gInterrupt);
Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerVirtIntrNum), TimerInterruptHandler);
Status = gInterrupt->RegisterInterruptSource (gInterrupt, TimerHypIntrNum, TimerInterruptHandler);
Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerSecIntrNum), TimerInterruptHandler);
Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerIntrNum), TimerInterruptHandler);
}
gHardwareInterruptProtocolGuid对应中断的注册接口guid:
edk2-stable-202011\ArmPkg\Drivers\ArmGic\ArmGicCommonDxe.c
edk2-stable-202011\ArmPkg\Drivers\ArmGic\GicV3\ArmGicV3Dxe.c
EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = {
RegisterInterruptSource,
GicV3EnableInterruptSource,
GicV3DisableInterruptSource,
GicV3GetInterruptSourceState,
GicV3EndOfInterrupt
};
...........之后单独开章节撸时钟中断的实现。
6. Event回调接口的派发/TPL的实现/Lock的实现
//提升任务优先级,当优先级为最高优先级HIGH_LEVEL时,必须关闭中断,防止被其他任务抢断;
EFI_TPL EFIAPI CoreRaiseTpl ( IN EFI_TPL NewTpl)
{
OldTpl = gEfiCurrentTpl;
// 最高优先级时关闭中断,该任务不会被打断;
if (NewTpl >= TPL_HIGH_LEVEL && OldTpl < TPL_HIGH_LEVEL) {
CoreSetInterruptState (FALSE);
}
gEfiCurrentTpl = NewTpl;
return OldTpl; //返回之前的优先级
}
还原/降低任务优先级
CoreRestoreTpl (IN EFI_TPL NewTpl)
{
OldTpl = gEfiCurrentTpl;
if (OldTpl >= TPL_HIGH_LEVEL && NewTpl < TPL_HIGH_LEVEL) {
gEfiCurrentTpl = TPL_HIGH_LEVEL;
}
//分发ggEventQueue[]中的事件,最终调用回调接口NotifyFunction
while (gEventPending != 0) {
PendingTpl = (UINTN) HighBitSet64 (gEventPending);
//Pending事件的优先级比当前优先级低则无需执行事件回调;
if (PendingTpl <= NewTpl) {
break;
}
//将优先级高于当前优先级的pengding事件处理掉,方式事件阻塞;
gEfiCurrentTpl = PendingTpl;
if (gEfiCurrentTpl < TPL_HIGH_LEVEL) { 优先级回退到低于HIGH_LEVEL,需要打开中断;
CoreSetInterruptState (TRUE);
}
CoreDispatchEventNotifies (gEfiCurrentTpl); //见Event Signal的描述
}
gEfiCurrentTpl = NewTpl;
//优先级回退到低于HIGH_LEVEL,需要打开中断;
if (gEfiCurrentTpl < TPL_HIGH_LEVEL) {
CoreSetInterruptState (TRUE);
}
}
定时器Event事件的派发在CoreStoreTpl中实现,在每个时钟滴答到来时,触发时钟中断处理函数CoreTimerTick (IN UINT64 Duration)
{
CoreAcquireLock (&mEfiSystemTimeLock);
mEfiSystemTime += Duration;
.......定时器处理.....
CoreReleaseLock (&mEfiSystemTimeLock);
}
时钟中断中先CoreAcquireLock 提拉优先级到最高mEfiSystemTimeLock :TPL_HIGH_LEVEL,然后CoreReleaseLock调用CoreRestoreTpl 降低优先级,此时所有高于原优先级的event/NotifyFunction被分发;其余有优先级变化的场景如CoreCheckEvent都是如此;
加锁机制的实现:
CoreAcquireLock (IN EFI_LOCK *Lock)
{
//提升当前任务的优先级,使得gEventPending中低于该优先级的事件不会处理
Lock->OwnerTpl = CoreRaiseTpl (Lock->Tpl);
Lock->Lock = EfiLockAcquired;
}
CoreReleaseLock (IN EFI_LOCK *Lock)
{
Tpl = Lock->OwnerTpl;
Lock->Lock = EfiLockReleased;
CoreRestoreTpl (Tpl);
}
常见锁:
// Protocol操作时使用的锁
EFI_LOCK gProtocolDatabaseLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
// Event操作时候的锁
EFI_LOCK gEventQueueLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL);
其他常见锁:
EFI_LOCK mDispatcherLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL);
EFI_LOCK mEfiSystemTimeLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL);
EFI_LOCK mEfiTimerLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL - 1);
EFI_LOCK mGcdMemorySpaceLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
EFI_LOCK mGcdIoSpaceLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
EFI_LOCK mMemoryProfileLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
STATIC EFI_LOCK mPoolMemoryLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
EFI_LOCK gMemoryLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
STATIC EFI_LOCK mMemoryAttributesTableLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
................................
Event附件和引用:
UEFI EVENT 全解 - hammerqiu - 博客园 (cnblogs.com)
EFI基本概念之Event_zhao_longwei的专栏-CSDN博客_efiapi