UEFI源码学习3.3 - DXE Event实现

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。
虽然字面意思很简单,但实际上这个简单的操作在这个函数里也饶了一圈。

  1. 它首先会调用CoreNotifySignalList 或者CoreNotifyEvent 把EVENT插入到gEventQueue[Type]链表中
  2. 然后在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);
  }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值