EFI基本概念之Event

1 基本概念及函數

    最近在幫公司弄一個ALPHA架構的EFI BIOS,在調DXE_CORE的時候,發現必須要裝幾個CPU架構相關的協議。如果這幾個協議不去安裝,代碼就不會執行BDS階段。代碼偵測協議有沒有安裝,是通過CoreRegisterProtocolNotify()函數實現的,這個函數在未安裝協議之前,便先建立PROTOCOL_ENTRY變量,然後將其鏈接到mProtocolDatabase鏈表中,並將已經有的Event鏈接到自己的Notify鏈表中。等到代碼真正安裝Protocol實例後,這個Event便會被調用,代碼有Event NotifyFunction中便知道某個CPU架構類協議有沒有建立起來。開始時代碼中沒加裝載這幾個協議的驅動,所以代碼跑不到BDS階段,只是打印出架構協議未安裝的調試信息。這個設計很巧妙,也展現了EFIEvent強大的一角。

    新聞聯播上總是說,北京時間XX點,發生了XX事件。這句話有兩個重點,時間和事情。EFI的Event同樣也有這兩個特性:一,Event是個可發生(執行)的函數;二,Event需要在某個時間點發生。在EFI裏,實現第一個特性是用CreateEvent()或CreateEventEx(),實現第二個特性需要使用SignalEvent()。Event除了這幾個函數外,也包括關閉事件函數CloseEvent(),等待事件函數WaitForEvent(),檢查事件函數CheckEvent()。下面分別介紹一下他們的基本用法。

1.1 創建事件

EFI_STATUS

EFIAPI

CoreCreateEvent (

  IN UINT32                   Type,

  IN EFI_TPL                  NotifyTpl,

  INEFI_EVENT_NOTIFY         NotifyFunction,OPTIONAL

  IN VOID                     *NotifyContext, OPTIONAL

  OUTEFI_EVENT               *Event

  )

這個函數創建了一個Type類別的,NotifyTpl級別的,擁有NotifyFunction函數的Event事件。NotifyFunction是可選的,如果NotifyTpl擁有EVT_NOTIFY_SIGNAL或EVT_NOTIFY_WAIT級別,則其作爲有效參數被使用。

 

EFI_STATUS

EFIAPI

CoreCreateEventEx (

  IN UINT32                   Type,

  IN EFI_TPL                  NotifyTpl,

  INEFI_EVENT_NOTIFY         NotifyFunction,OPTIONAL

  IN CONSTVOID               *NotifyContext,OPTIONAL

  IN CONSTEFI_GUID           *EventGroup,    OPTIONAL

  OUTEFI_EVENT               *Event

  )

    此函數比CreateEvent()多了一個EventGroup函數,與CreateEvent創造的事件不同的是,BootServcies函數無法單獨觸發。

 

1.2 銷燬事件

EFI_STATUS

EFIAPI

CoreCloseEvent (

  IN EFI_EVENT    UserEvent

  )

    此函數將UserEvent銷燬,從鏈表中摘除,並釋放佔有的內存空間。

 

1.3 觸發事件

EFI_STATUS

EFIAPI

CoreSignalEvent (

  IN EFI_EVENT    UserEvent

  )

    觸發UserEvent事件。

 

1.4 等待事件

EFI_STATUS

EFIAPI

CoreWaitForEvent (

  IN UINTN        NumberOfEvents,

  IN EFI_EVENT    *UserEvents,

  OUT UINTN       *UserIndex

  )

    此函數停止當前的任務,等待NumberOfEvents個事件發生,若其中一個事件發生,則退出並反饋給用戶這個信息:第userIndex個事件發生了。

 

1.5 檢查事件

EFI_STATUS

EFIAPI

CoreCheckEvent (

  IN EFI_EVENT        UserEvent

  )

    此函數檢查UserEvent的現在狀態。

 

1.6 其它事件相關函數

1.6.1 CoreSetTimer()

    爲Timer事件設置類型和觸發時間,Timer那一章介紹過了,此處略過。

1.6.2 CoreRaiseTpl ()

    爲當前任務升權。

1.6.3 CoreRestoreTpl ()

    恢復任務權限級別,所有Event級別比新的任務權限級別高的,都會得到執行。

 

 

2 Event相關結構

2.1 Event真身

typedef struct {

  UINTN                   Signature;

  UINT32                  Type;

  UINT32                  SignalCount;

  LIST_ENTRY              SignalLink;

  EFI_TPL                 NotifyTpl;

 EFI_EVENT_NOTIFY       NotifyFunction;

  VOID                    *NotifyContext;

  EFI_GUID                EventGroup;

  LIST_ENTRY              NotifyLink;

  BOOLEAN                 ExFlag;

 EFI_RUNTIME_EVENT_ENTRY RuntimeData;

 TIMER_EVENT_INFO        Timer;

} IEVENT;

    CreateEvent()函數最後一個參數是輸出參數EVENT,它的真身就是IEVENT結構體。其它輸入參數,都被賦於了這個結構體的成員。

 

2.2 gEfiCurrentTpl

    當前任務級別。可以將其看爲Legacy BIOS中的當前中斷優先級。

 

2.3 gEventQueue[]

    一個專門鏈接處於觸發態的事件的雙向鏈表,其有32個元素,當前只用了4個。分別是: TPL_APPLICATION,TPL_CALLBACK,TPL_NOTIFY,TPL_HIGH_LEVEL

    Event事件被SignalEvent()後,便會將其塞入到gEventQueue[Event-> NotifyTpl]鏈表裏。當CoreRestoreTpl()函數執行時,如果Event-> NotifyTpl大於恢復後的任務權限級別,gEventQueue [Event-> NotifyTpl]所鏈接的所有Event都會被執行,執行完後,Event從gEventQueue [Event-> NotifyTpl]中移除。

 

2.4 gEventPending

    該全局變量爲一指示型變量。其每一位指示gEventQueue[]的每一個元素是否鏈接有相應的Event,若有則爲1,沒有值爲0。因其類型爲UINTN,但是gEvent[]的元素有32個。所以如果某一類型CPU的數據寬度小於32位,我們要強制將其類型設爲大於或等於32位的。

 

2.5 gEventSignalQueue

    這是一個雙向鏈表。當我們創造一個EVENT時,要將EVENT鏈入該鏈表中。當CloseEvent()時,從該鏈表中移除。

 

2.6 mEventTable[]

    有效的Event類型。EVENT類型不在此數組元素內,創建EVENT時不會成功。



(完,附上一張EVENT相關結構圖,是2014年,小明剛學BIOS時,畫的一張圖。RuntimeData的直接忽略吧,畢竟代碼還未支持,而且也沒有支持的必要)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章