UEFI源码解析之EVENT

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

 

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值