edk2/MdeModulePkg/Core/Dxe/Event/Event.c
edk2/MdeModulePkg/Core/Dxe/Event/Tpl.c
1. CoreCreateEvent / CoreCreateEventEx
CoreCreateEvent/CoreCreateEventEx用于创建一个Event,CoreCreateEvent实际上是一个wrapper函数,它内部调用了CoreCreateEventEx,只是EventGroup参数为NULL。
CoreCreateEvent (
IN UINT32 Type, --> Event类型
IN EFI_TPL NotifyTpl, --> 通知优先级
IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL, --> Event的回调函数
IN VOID *NotifyContext OPTIONAL, --> 通知上下文
OUT EFI_EVENT *Event --> Event指针
)
{
return CoreCreateEventEx (Type, NotifyTpl, NotifyFunction, NotifyContext, NULL, Event);
}
CoreCreateEventEx这个函数也是一个wrapper函数,仅仅在外层检查了参数合法性后调用CoreCreateEventInternal。
CoreCreateEventEx (
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
)
{
if ((Type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) != 0) {
if ((NotifyTpl != TPL_APPLICATION) &&
(NotifyTpl != TPL_CALLBACK) &&
(NotifyTpl != TPL_NOTIFY))
{
return EFI_INVALID_PARAMETER;
}
}
return CoreCreateEventInternal (Type, NotifyTpl, NotifyFunction, NotifyContext, EventGroup, Event);
}
CoreCreateEventInternal里面简单来说就为Event创建一个IEVENT对象,并根据参数将IEVENT的成员一个一个填好。如果EVENT的类型是EVT_NOTIFY_SIGNAL, 则要将该EVENT插入到gEventSignalQueue。
CoreCreateEventInternal (
...
)
{
EFI_STATUS Status;
IEVENT *IEvent;
INTN Index;
...
if (EventGroup != NULL) {
//如果带有EventGroup参数,Event类型必须不能为EVT_SIGNAL_EXIT_BOOT_SERVICES和EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
if ((Type == EVT_SIGNAL_EXIT_BOOT_SERVICES) || (Type == EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)) {
return EFI_INVALID_PARAMETER;
}
//根据GUID设置Event类型
if (CompareGuid (EventGroup, &gEfiEventExitBootServicesGuid)) {
Type = EVT_SIGNAL_EXIT_BOOT_SERVICES;
} else if (CompareGuid (EventGroup, &gEfiEventVirtualAddressChangeGuid)) {
Type = EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE;
}
} else {
//如果没有EvenetGroup参数,Event类型是EVT_SIGNAL_EXIT_BOOT_SERVICES和EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE的话,设置相应EventGroup类型
if (Type == EVT_SIGNAL_EXIT_BOOT_SERVICES) {
EventGroup = &gEfiEventExitBootServicesGuid;
} else if (Type == EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) {
EventGroup = &gEfiEventVirtualAddressChangeGuid;
}
}
if ((Type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) != 0) {
//如果是EVT_NOTIFY_WAIT 或者EVT_NOTIFY_SIGNAL的Event,
//NotifyFunction必须要有,NotifyTpl 优先级必须在TPL_APPLICATION和TPL_HIGH_LEVEL之间
if ((NotifyFunction == NULL) ||
(NotifyTpl <= TPL_APPLICATION) ||
(NotifyTpl >= TPL_HIGH_LEVEL))
{
return EFI_INVALID_PARAMETER;
}
} else {
//其他的类型的Event则不需要通知相关的参数
NotifyTpl = 0;
NotifyFunction = NULL;
NotifyContext = NULL;
}
//为Event分配一个IEVENT对象。
if ((Type & EVT_RUNTIME) != 0) {
IEvent = AllocateRuntimeZeroPool (sizeof (IEVENT));
} else {
IEvent = AllocateZeroPool (sizeof (IEVENT));
}
//根据参数配置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;
}
//将分配的IEVENT指针返回给调用者。
*Event = IEvent;
if ((Type & EVT_RUNTIME) != 0) {
//如果是EVT_RUNTIME类型的EVENT,则需要填入RuntimeData相关的成员
IEvent->RuntimeData.Type = Type;
IEvent->RuntimeData.NotifyTpl = NotifyTpl;
IEvent->RuntimeData.NotifyFunction = NotifyFunction;
IEvent->RuntimeData.NotifyContext = (VOID *)NotifyContext;
IEvent->RuntimeData.Event = (EFI_EVENT *)IEvent;
//把Event插入gRuntime->EventHea链表
InsertTailList (&gRuntime->EventHead, &IEvent->RuntimeData.Link);
}
CoreAcquireEventLock ();
if ((Type & EVT_NOTIFY_SIGNAL) != 0x00000000) {
//如果是EVT_NOTIFY_SIGNAL类型的EVENT,将EVENT插入gEventSignalQueue链表。
InsertHeadList (&gEventSignalQueue, &IEvent->SignalLink);
}
...
}
2. CoreSignalEvent
CoreSignalEvent会向EVENT发送一个信号,然后在这个函数中回去调用在CreateEvent中注册的回调函数NotifyFunction。
虽然字面意思很简单,但实际上这个简单的操作在这个函数里也饶了一圈。
- 它首先会调用CoreNotifySignalList 或者CoreNotifyEvent 把EVENT插入到gEventQueue[Type]链表中
- 然后在CoreReleaseEventLock 的调用中从gEventQueue[Type]拿到pending的EVNET,然后去调用EVNET对应的NotifyFunction
EFI_STATUS
EFIAPI
CoreSignalEvent (
IN EFI_EVENT UserEvent
)
{
IEVENT *Event;
Event = UserEvent;
if (Event == NULL) {
return EFI_INVALID_PARAMETER;
}
if (Event->Signature != EVENT_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
CoreAcquireEventLock ();
//
// If the event is not already signalled, do so
//
if (Event->SignalCount == 0x00000000) {
Event->SignalCount++;
//
// If signalling type is a notify function, queue it
//
if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
if ((Event->ExFlag & EVT_EXFLAG_EVENT_GROUP) != 0) {
//
// The CreateEventEx() style requires all members of the Event Group
// to be signaled.
//
CoreReleaseEventLock ();
CoreNotifySignalList (&Event->EventGroup);
CoreAcquireEventLock ();
} else {
CoreNotifyEvent (Event);
}
}
}
CoreReleaseEventLock ();
return EFI_SUCCESS;
}
VOID
CoreNotifyEvent (
IN IEVENT *Event
)
{
...
//如果Event已经插入了在gEventQueue[Event->NotifyTpl]中,那么就把它从链表中删除
if (Event->NotifyLink.ForwardLink != NULL) {
RemoveEntryList (&Event->NotifyLink);
Event->NotifyLink.ForwardLink = NULL;
}
//把这个EVENT插入相应优先级的gEventQueue。
InsertTailList (&gEventQueue[Event->NotifyTpl], &Event->NotifyLink);
//将gEventPending 设置上,这在后面的函数中需要使用
gEventPending |= (UINTN)(1 << Event->NotifyTpl);
}
CoreReleaseEventLock这个函数在释放gEventQueueLock的同时还会去查看gEventQueue中有没有pending的EVENT,如果有,就调用它们的注册的回调函数。CoreReleaseEventLock的函数调用栈如下,最终会调用到CoreDispatchEventNotifies这个函数中把Event的回调函数调用以下
CoreReleaseEventLock
-->CoreReleaseLock
-->CoreRestoreTpl
-->CoreDispatchEventNotifies
这个函数用于恢复当前任务的Event通知优先级,同时如果有pending的EVENT存在,则调用CoreDispatchEventNotifies处理EVENT。
CoreRestoreTpl (
IN EFI_TPL NewTpl
)
{
EFI_TPL OldTpl;
EFI_TPL PendingTpl;
OldTpl = gEfiCurrentTpl;
//如果有pengding的event存在,调用CoreDispatchEventNotifies
while (gEventPending != 0) {
...
CoreDispatchEventNotifies (gEfiCurrentTpl);
}
//当当前任务恢复通知有优先级到NewIpl
gEfiCurrentTpl = NewTpl;
...
}
CoreDispatchEventNotifies 这里就是最终从gEventQueue拿出IEVENT,然后调用回调函数Event->NotifyFunction (Event, Event->NotifyContext);
VOID
CoreDispatchEventNotifies (
IN EFI_TPL Priority
)
{
IEVENT *Event;
LIST_ENTRY *Head;
CoreAcquireEventLock ();
//拿到对应通知优先级的gEventQueue
Head = &gEventQueue[Priority];
//遍历gEventQueueP链表
while (!IsListEmpty (Head)) {
//拿到IEVENT对象
Event = CR (Head->ForwardLink, IEVENT, NotifyLink, EVENT_SIGNATURE);
//把它从gEventQueue中删掉
RemoveEntryList (&Event->NotifyLink);
Event->NotifyLink.ForwardLink = NULL;
//把SignalCount清0.
if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
Event->SignalCount = 0;
}
//这里有递归调用解锁
CoreReleaseEventLock ();
//**Signal调用回调函数NotifyFunction**
Event->NotifyFunction (Event, Event->NotifyContext);
CoreAcquireEventLock ();
}
gEventPending &= ~(UINTN)(1 << Priority);
//这里有递归调用解锁
CoreReleaseEventLock ();
}
3.CoreWaitForEvent / CoreCheckEvent
CoreWaitForEvent 里面调用了CoreCheckEvent。所以先看CoreCheckEvent。
在CoreCheckEvent中,主要根据Event状态做两种处理。
- Event->SignalCount = 0的情况下调用CoreNotifyEvent 通知Event,并报告给调用者这个Event还没有Ready。
- Event->SignalCount != 0的情况下,就把Event->SignalCount 清0,并报告给调用者这个Event已经被Singal过了。
CoreCheckEvent (
IN EFI_EVENT UserEvent
)
{
IEVENT *Event;
EFI_STATUS Status;
Event = UserEvent;
...
Status = EFI_NOT_READY;
if ((Event->SignalCount == 0) && ((Event->Type & EVT_NOTIFY_WAIT) != 0)) {
//调用CoreNotifyEvent 通知Event
CoreAcquireEventLock ();
if (Event->SignalCount == 0) {
CoreNotifyEvent (Event);
}
CoreReleaseEventLock ();
}
if (Event->SignalCount != 0) {
//把Event->SignalCount 清0,并报告给调用者这个Event已经被Singal过了
CoreAcquireEventLock ();
if (Event->SignalCount != 0) {
Event->SignalCount = 0;
Status = EFI_SUCCESS;
}
CoreReleaseEventLock ();
}
return Status;
}
CoreWaitForEvent 这个函数调用了CoreCheckEvent, 根据CoreCheckEvent的返回值做一些操作。
- 如果CoreCheckEvent返回了EFI_NOT_READY,从上面的分析可以得到就是返回了EFI_SUCCESS,那么就会返回给调用者某个Event已经OK了,可以去处理这个Event了。
- 如果CoreCheckEvent返回了EFI_NOT_READY,那么就会调用CoreSignalEvent (gIdleLoopEvent);发送IDLE的Event,让CPU进入IDLE模式。gIdleLoopEvent这个Event实现在CpuDXE中,在CpuDXE中,IDLE模式的操作其实就是让CPU进入WFI,等待中断到来并等待其他驱动Singal需要的Event,这样就达到了阻塞Wait的效果。
CoreWaitForEvent (
IN UINTN NumberOfEvents,
IN EFI_EVENT *UserEvents,
OUT UINTN *UserIndex
)
{
EFI_STATUS Status;
UINTN Index;
//只能在TPL_APPLICATION的优先级下才能调用CoreWaitForEvent。
if (gEfiCurrentTpl != TPL_APPLICATION) {
return EFI_UNSUPPORTED;
}
for ( ; ;) {
//查询所有等待的中断,这里参数是一个数组,所以要遍历这个Evnet数组
for (Index = 0; Index < NumberOfEvents; Index++) {
//检查这个Event的状态
Status = CoreCheckEvent (UserEvents[Index]);
//如果是READY,那么就返回。
if (Status != EFI_NOT_READY) {
if (UserIndex != NULL) {
*UserIndex = Index;
}
return Status;
}
}
//如果是EFI_NOT_READY,那么就阻塞等待。
CoreSignalEvent (gIdleLoopEvent);
}
}