UEFI开发学习7 - Event相关服务

1.前言

UEFI提供了异步操作,Event是异步操作的基础,有了Event的支持,才可以在UEFI系统内执行并发操作,基于Event的异步操作,提高了CPU利用率,减少了总的等待时间。

2.Event、Timer和Task Priority服务概览

BootService为开发者提供了下面的函数用于操作事件、定时器及TPL(任务优先级)。事件相关函数、定时器相关函数及TPL相关函数。

  • CreateEvent:生成一个事件对象
  • CreateEventEx:生成一个事件对象并将该事件加入到一个组内
  • CloseEvent:关闭事件对象
  • SignalEvent:触发事件对象
  • WaitForEvent:等待事件数组中的任一事件触发
  • CheckEvent:检查事件状态
  • SetTimer:设置定时器的类型和定时器事件的触发时间
  • RaiseTPL:提升任务优先级
  • RestoreTPL:恢复任务优先级

2.1事件相关函数

在学习事件相关函数之前,先认识一下实际的Event结构体都包含哪些成员。
.\MdeModulePkg\Core\Dxe\Event\Event.h

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;
  UINT8                   ExFlag;
  ///
  /// A list of all runtime events
  ///
  EFI_RUNTIME_EVENT_ENTRY RuntimeData;
  TIMER_EVENT_INFO        Timer;
} IEVENT;
2.1.1 CreateEvent

CreateEvent用于生成一个事件。

函数原型:
typedef
EFI_STATUS
(EFIAPI *EFI_CREATE_EVENT) (
IN UINT32 Type,
IN EFI_TPL NotifyTpl,
IN EFI_EVENT_NOTIFY NotifyFunction, OPTIONAL
IN VOID *NotifyContext, OPTIONAL
OUT EFI_EVENT *Event
);

事件类型
UEFI定义了事件的基本类型,事件的类型可以是以下一种或几种基本类型的组合。例如,某个事件可以是EVT_TIMER,也可以是EVT_TIMER|EVT_NOTIFY_WAIT或者EVT_TIMER|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
#define EVT_RUNTIME_CONTEXT                0x20000000

EVT_TIMER:定时器事件。普通Timer事件,没有Notification函数。生成事件后需调用SetTimer服务设置时钟属性。

EVT_RUNTIME:这种事件是运行时内存分配的。如果一个事件是在函数EFI_BOOT_SERVICES.ExitBootServices()调用之后被触发的,事件的数据结构和notification函数需要从运行时内存分配(runtime memory)。更多信息,参看SetVirtualAddressMap()。

EVT_NOTIFY_WAIT:普通事件。这个事件有一个Notification函数,当这个事件通过CheckEvent()检查状态或通过WaitForEvent()等待时,这个Notification函数会被放到待执行队列gEventQueue[Event->NotifyTpl]中。

EVT_NOTIFY_SIGNAL:普通事件。这个事件有一个Notification函数,当这个事件通过SignalEvent()被触发时,这个Notification函数会被放到待执行队列gEventQueue[Event->NotifyTpl]中等待执行。

EVT_SIGNAL_EXIT_BOOT_SERVICES:此类事件是一种特殊的EVT_NOTIFY_SIGNAL,实际上它是EVT_NOTIFY_SIGNAL和0x00000001的组合。当ExitBootServices()执行时,事件被触发。EVT_SIGNAL_EXIT_BOOT_SERVICES不能和其他类型混合使用。它的Notification函数和子函数不能使用启动服务中的内存分配服务,因为这些服务会修改当前的memory map;在Notification函数执行前所有的定时器服务会被禁用,因而在Notification函数中也不能使用定时器服务,该事件类型在功能上等同于EFI_EVENT_GROUP_EXIT_BOOT_SERVICES事件组。

EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:当SetVirtualAdressMap()执行时,系统触发此类事件,也就是将它的Notification函数放入待执行队列。它是EVT_RUNTIME_CONTEXT、EVT_RUNTIME、EVT_NOTIFY_SIGNAL和0x00000002的组合。它不能和其他任何事件类型组合使用。

Notification函数

函数原型:
typedef
VOID
(EFIAPI *EFI_EVENT_NOTIFY) (
IN EFI_EVENT Event,
IN VOID *Context
); 

如果事件的类型是EVT_NOTIFY_WAIT,则EFI_EVENT_NOTIFY函数会在等待此事件的过程中调用;如果事件的类型是EVT_NOTIFY_SIGNAL,则EFI_EVENT_NOTIFY函数会在事件触发时调用。既没有EVT_NOTIFY_WAIT属性也没有EFI_EVENT_NOTIFY属性的事件,Notification参数将被忽略。

Event的Notification函数待执行的事件队列,这是一个FIFO队列。

///
/// gEventQueue - A list of event's to notify for each priority level
///
LIST_ENTRY      gEventQueue[TPL_HIGH_LEVEL + 1];
2.1.2 CreateEventEx

CreateEventEx服务用于生成事件并将事件加入事件组。

函数原型:
typedef
EFI_STATUS
(EFIAPI *EFI_CREATE_EVENT_EX) (
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
);

由CreateEventEx生成的事件会加入到EventGroup中。当EventGroup中的任一事件被触发后,组中的所有其他事件都会被触发,进而组内所有的Notification都将被加入到待执行队列利用这个特性可以控制某些函数在某个时间点执行,这个时间点就是被触发的事件执行的那刻,例如,我们在很多地方都创建了事件,加入到某个事件组中,然后,我们就在想要这个事件组中的所有函数执行的地方,创建一个事件加入到这个事件组中,用SignalEvent函数触发这个事件,这样事件组中的所有Notification函数都会执行了。如果这样的事件组多了,可以形成控制流,如BDS_CONTROL_FLOW)。同组内NotifyTpl(优先级)高的Notification函数会先被执行。

如果输入参数EventGroup为NULL,则CreateEventEx退化为CreateEvent。

Type不能是EVT_SIGNAL_EXIT_BOOT_SERVICES或EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,因为这两种类型有各自对应的Group。

UEFI_2.9预定义了7个Event组:
EFI_EVENT_GROUP_EXIT_BOOT_SERVICES
ExitBootServices()执行时触发该组内所有Event。组内Event属性与EVT_SIGNAL_EXIT_BOOT_SERVICES类型的Event相同。

EFI_EVENT_GROUP_BEFORE_EXIT_BOOT_SERVICES
ExitBootServices()执行时,在触发EFI_EVENT_GROUP_EXIT_BOOT_SERVICES事件组之前此事件组内的所有Event会被触发。该事件是在引导环境中使用固件接口的最后一次机会。此事件的Notification函数不能依赖于任何类型的延迟处理(处理timer回调中发生的事情超出了notification函数的时间范围),因为系统固件在调度此事件组的处理程序后会立即停用Timer服务。

EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE
SetVirtualAddressMap()执行时触发该组内所有Event。组内Event属性与EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE类型的Event相同。

EFI_EVENT_GROUP_MEMORY_MAP_CHANGE
Memory Map改变时触发该组内所有Event。在Notification函数中不能使用启动服务的内存分配函数。

EFI_EVENT_GROUP_READY_TO_BOOT
Boot Manager加载并执行一个启动项时触发该组内所有Event。该事件组提供了在将控制权传递给启动选项之前修改设备或系统配置的最后机会。

EFI_EVENT_GROUP_AFTER_READY_TO_BOOT
在Boot Manager加载并执行一个启动项触发EFI_EVENT_GROUP_READY_TO_BOOT事件组后,会触发此事件组。

EFI_EVENT_GROUP_RESET_SYSTEM
ResetSystem()执行时触发该组内所有Event,然后系统将会重启。该事件组只在ExitBootServices()执行之前被触发。该事件组提供了在将控制权传递给启动选项之前检查设备或系统配置的最后机会。

2.1.3 CloseEvent

事件使用完毕后,必须调用CloseEvent关闭这个事件。CloseEvent执行后,该事件从内核各个队列中清除,并释放内存。
通常的原则是由事件的所有者(即调用CreateEvent产生该事件的调用者)调用CloseEvent函数。调用该函数后,指定的事件将从内核中删除。

2.1.4 SignalEvent

SignalEvent用于将事件的状态设置为触发态。如果事件类型为EVT_NOTIFY_SIGNAL,则将其Notification函数添加到就绪队列执行。如果该事件属于一个组,则将该组内所有事件都设置为触发态,并将组内所有EVT_NOTIFY_SIGNAL事件的Notification函数添加到就绪队列准备执行。

2.1.5 WaitForEvent
函数原型:
typedef
EFI_STATUS
(EFIAPI *EFI_WAIT_FOR_EVENT) (
IN UINTN NumberOfEvents,
IN EFI_EVENT *Event,
OUT UINTN *Index
);

WaitForEvent用于等待事件的发生,WaitForEvent是阻塞操作,直到Event数组内任一事件被触发,或任一事件导致错误出现,WaitForEvent才返回。WaitForEvent从前到后依次检查Event数组内的事件,发现有被触发的事件或遇到错误则返回,如果所有事件都没有被触发,则从头开始重新检查。
当检查到某个事件处于触发态时,*Index赋值为该事件在Event数组中的下标,返回前该事件将重置为非触发态。
当检查到某个事件是EVT_NOTIFY_SIGNAL类型时,*Index赋值为该事件在Event数组中的下标,并返回EFI_INVALID_PARAMETER。
WaitForEvent必须运行在TPL_APPLICATION级别,否则将返回EFI _UNSUPPORTED。

2.1.6 CheckEvent

这个函数用于检查事件是否处于触发态。

函数原型:
typedef
EFI_STATUS
(EFIAPI *EFI_CHECK_EVENT) (
	IN EFI_EVENT Event
); 

根据事件的属性和状态,返回值有如下4种情况:
1)如果事件是EVT_NOTIFY_SIGNAL类型,则返回EFI_INVALID_PARAMETER。
2)如果事件处于触发态,则返回EFI_SUCCESS,并且在返回前,事件重置为非触发态。
3)如果事件触发非触发态并且事件无Notification函数,则返回EFI_NOT_READY。
4)如果事件处于非触发态并且事件有Notification函数(此事件只可能是EVT_NOTIFY_WAIT类型),则执行Notification函数。然后检查事件状态标志,若事件处于触发态,则返回EFI_SUCCESS,否则返回EFI_NOT_READY。

2.2定时器事件

定时器是一类特殊的事件,生成定时器事件后,可以通过SetTimer服务设置定时器属性。

函数原型:
typedef
EFI_STATUS
(EFIAPI *EFI_SET_TIMER) (
IN EFI_EVENT Event,			      //Timer事件
IN EFI_TIMER_DELAY Type,		  //定时器类别
IN UINT64 TriggerTime            //定时器过期时间,100ns为一个单位
);

定时器类别有三个:
TimerCancel:用于取消定时器触发事件。设置后定时器不在触发
TimerPeriodic:重复型定时器。每TriggerTime*100ns,定时器触发一次
TimerRelative:一次性定时器。TiggerTime*100ns时触发

如果Type为TimerPriodic并且TriggerTime是0,则定时器每个始终滴答触发一次。
如果Type为TimerRelative并且TriggerTime是0,则定时器在下个时钟滴答触发。
生成定时器事件一般分为两步:
第一步,通过CreateEvent生成一个EVT_TIMER事件,
第二步,通过SetTimer设置这个定时器事件的属性。

2.3任务优先级

任务优先级可以是0~31的一个整数,UEFI预定义了4个优先级:

#define TPL_APPLICATION       4
#define TPL_CALLBACK          8
#define TPL_NOTIFY            16
#define TPL_HIGH_LEVEL        31

优先级高的任务可以中断优先级低的任务,并且从高优先级返回低优先级前会完成所有高于低优先级的任务。

UEFI规定了某些函数要运行在某个级别或更低的级别,比如ExitBootServices()函数要运行在TPL_APPLICATION级别。

UEFI提供RaiseTPL(NewTpl)函数用于提升当前任务的任务优先级至NewTpl,该函数的返回值为原来的任务优先级。RestoreTPL()用于恢复(通常是降低)任务优先级至原来的优先级。RaiseTPL和RestoreTPL必须成对出现,执行了RaiseTPL后,必须尽快调用RestoreTPL将任务优先级恢复到原来的值。在任务优先级恢复到原优先级之前,所有高于原优先级的触发态事件的Notification函数都要执行完毕。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值