UEFI架构解读系列之EVENT

在UEFI 开机过程中,我们会有这样的需求,某个特定的条件满足的时候,去运行一段代码,在legacy BIOS 里面,我们是用中断去做的,在UEFI 里面,我们是用EVENT 去实现的。



先用一个uefi shell 下的app , 来示范 CreateEvent ,  WaitForEvent, 和用法,让各位对event 有个感性认识,该程序不断在屏幕上打印一串字符,直到按下某个键。

 

#include <Uefi.h>
#include <Library/PcdLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>


/**
  The user Entry Point for Application. The user code starts with this function
  as the real entry point for the application.

  @param[in] ImageHandle    The firmware allocated handle for the EFI image.  
  @param[in] SystemTable    A pointer to the EFI System Table.
  
  @retval EFI_SUCCESS       The entry point is executed successfully.
  @retval other             Some error occurs when executing this entry point.

**/
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{

    EFI_STATUS         Status;
    EFI_EVENT          TimerEvent;
    EFI_EVENT          WaitList[2];
    EFI_INPUT_KEY      Key;
    UINTN              Index;

    do{
      Print(L"guangzhi jia...");

      Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);

      //    
      // Set a timer event of 1 second expiration    
      //  
      Status = gBS->SetTimer (TimerEvent, TimerRelative, 20000000);    

      //
      // Wait for the keystroke event or the timer    
      //  
      WaitList[0] = gST->ConIn->WaitForKey;  
      WaitList[1] = TimerEvent;  

      Status = gBS->WaitForEvent (2, WaitList, &Index);  
      //    
      // Check for the timer expiration    
      //  
      if (!EFI_ERROR (Status) && Index == 1) {    
        Status = EFI_TIMEOUT;     
      }  
      gBS->CloseEvent (TimerEvent); 
      gST->ConIn->ReadKeyStroke(gST->ConIn, &Key);   
      }while(Status == EFI_TIMEOUT||Key.ScanCode!=SCAN_ESC);    

    Print(L"\nDone!\n");

    return EFI_SUCCESS;


}


程序效果如下:



1. EVENT 数据结构:

typedef struct {
  UINTN                   Signature;
  UINT32                  Type;
  UINT32                  SignalCount;
  ///
  /// Entry if the event is registered to be signalled
  ///
  LIST_ENTRY              SignalLink;
  ///
  /// Notification information for this event
  ///
  EFI_TPL                 NotifyTpl;
  EFI_EVENT_NOTIFY        NotifyFunction;
  VOID                    *NotifyContext;
  EFI_GUID                EventGroup;
  LIST_ENTRY              NotifyLink;
  BOOLEAN                 ExFlag;
  ///
  /// A list of all runtime events
  ///
  EFI_RUNTIME_EVENT_ENTRY RuntimeData;
  TIMER_EVENT_INFO        Timer;
} IEVENT;

这个就像面向对象语言中的类的概念,每一个能够被EFI 管理的object都要去实现它,即用相应的值去填充具体每一个成员,下面对每个成员进行解释:

type 即为事件类型,在UEFI Spec 6.1 (EFI_BOOT_SERVICES.CreateEvent) 里面,有明确定义:

</pre><pre code_snippet_id="1628954" snippet_file_name="blog_20160330_4_5999732" name="code" class="cpp">//
// These types can be ORed together as needed - for example,
// EVT_TIMER might be Ored with EVT_NOTIFY_WAIT or
// EVT_NOTIFY_SIGNAL.
//
#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

SignalCount 看起来,只有两个值, 0 和1. 表示这个event 是否处于触发状态。Entry if the event is registered to be signalled。 
如果把将要触发的事情放到一个链表中,这个就一个node(的入口)。
NotifyTpl:指明NotifyFunction 会放入到哪个级别的回调函数队列。
</pre><p>NotifyFunction: 回调函数</p><p>Timer: 表示定时器 事件的相关信息</p><h3>event 分发机制</h3><div>和我们现实生活中的事情一样,事情都有个轻重缓急,UEFI 将不同级别的事情,放在不同的链表里面,所有链表的头节点组成了数组gEventQueue.</div><div></div><h3>关键函数分析:</h3><div><pre name="code" class="cpp">

136-138: 对每一个级别的event 列表进行初始化。
140:初始化时钟


©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页