IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> UEFI源码学习3.3 - DXE Event实现 -> 正文阅读

[移动开发]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);
  }
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-09 12:51:06  更:2022-05-09 12:51:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 10:16:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码