事件

事件

2023-07-25
bios
  • UEFI的所有异步操作需要通过事件来完成。

事件函数 #

WaitForEvent 等待事件发生 #

/**
  @retval EFI_SUCCESS           The event indicated by Index was signaled.
  @retval EFI_INVALID_PARAMETER 1. NumberOfEvents is 0; 2. The event indicated by Index is of type EVT_NOTIFY_SIGNAL.
  @retval EFI_UNSUPPORTED       The current TPL is not TPL_APPLICATION.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_WAIT_FOR_EVENT)(
  IN  UINTN                    NumberOfEvents,  //event数组的长度
  IN  EFI_EVENT                *Event,  //event数组
  OUT UINTN                    *Index   //返回触发事件的下标
  );
  • WaitForEvent是阻塞操作,直到Event数组内任一事件被触发或者事件导致错误出现时,WaitForEvent才返回。
  • 事件触发后返回index,并将事件重置为非触发状态。
  • EVT_NOTIFY_SIGNAL类型的事件似乎不能用WaitForEvent。

CreateEvent 创建事件 #

/**
  @retval EFI_SUCCESS           The event structure was created.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
  @retval EFI_OUT_OF_RESOURCES  The event could not be allocated.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_CREATE_EVENT)(
  IN  UINT32                       Type,    //事件类型
  IN  EFI_TPL                      NotifyTpl,   //Notification函数的优先级
  IN  EFI_EVENT_NOTIFY             NotifyFunction,  //Notification函数
  IN  VOID                         *NotifyContext,  //传给Notification函数的参数
  OUT EFI_EVENT                    *Event   //生成的事件
  );

事件类型 #

  • 事件类型可以是一种或多种基本类型的组合。常用的事件类型如下:
类型 特征
EVT_TIMER 定时器事件,没有Notification函数,生成事件后需要调用setTimer服务设置时钟属性。事件可以通过SetTimer设置等待事件、到期后通过SignalEvent触发、通过WaitForEvent等待事件触发、通过CheckEvent检查事件
EVT_NOTIFY_WAIT 有一个Notification函数,当调用CheckEvent或WaitForEvent时,Notifyication函数会被放到待执行队列
EVT_NOTIFY_SIGNAL 有一个Notification函数,当前事件通过SignalEvent被触发时,这个Notification函数会被放到待执行队列
0X00000000 没有Notification函数,事件可以通过signalevent触发、waitforevent等待事件被触发、checkevent检查状态
  • 还有两种特殊的事件
    • EVT_SIGNAL_EXIT_BOOT_SERVICES:当ExitBootServices被执行时,事件被触发。
    • EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:当SetVirtualAddressMap被调用时触发此类型的事件。

优先级 #

  • 有四个预定义的优先级
优先级 用法 函数
TPL_APPLICATION 优先级最低,当程序运行在此级别时,任务队列中没有任何处于就绪状态的Notification函数 下列安徽念书运行在此级别ExitBootServices()、WaitForEvent()等
TPL_CALLBACK 比较耗时的操作通常在这个优先级 Serial I/O Protocol、UnloadImage
TPL_NOTIFY 运行在这个级别的程序不允许阻塞,大部分Event的Notification函数允许在这个级别 Memory Allocation Services、HII Protocols
TPL_HIGH_LEVEL UEFI内核全局变量的修改允许在这个级别 SignalEvent、stall

Notification函数 #

typedef
VOID
(EFIAPI *EFI_EVENT_NOTIFY)(
  IN  EFI_EVENT                Event,   //拥有此函数的事件
  IN  VOID                     *Context //上下文指针,在CreateEvent设置
  );
  • 根据上面的事件类型可知,EVT_NOTIFY_WAIT的函数会在等待事件的过程中调用,而EVT_NOTIFY_SIGNAL的Notification函数会在SignalEvent调用。

CreateEventEx #

/**
  @retval EFI_SUCCESS           The event structure was created.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
  @retval EFI_OUT_OF_RESOURCES  The event could not be allocated.

**/
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用于生成事件并将事件加入事件组。当事件组中的任意事件被触发后,组中的所有事件都会被触发,进而组内所有的Notification函数都会被加入待执行队列,组内优先级最高的Notification函数会被先执行。
  • 存在四个预定义的Event组:
    • EFI_EVENT_GROUP_EXIT_BOOT_SERVICES:当执行ExitBootServices触发组内所有的事件。
    • EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE:当执行SetVirtualAddressMap触发组内所有的Event。
    • EFI_EVENT_GROUP_MEMORY_MAP_CHANGE:Memory Map改变时触发组内所有的Event。
    • EFI_EVENT_GROUP_READY_TO_BOOT:Boot Manager加载并且执行一个启动项时触发组内所有的Event。

CheckEvent 检查事件状态 #

/**
  @retval EFI_SUCCESS           事件是触发态
  @retval EFI_NOT_READY         事件是非触发态
  @retval EFI_INVALID_PARAMETER 事件类型是EVT_NOTIFY_SIGNAL

**/
typedef
EFI_STATUS
(EFIAPI *EFI_CHECK_EVENT)(
  IN EFI_EVENT                Event
  );

SignalEvent 触发事件 #

typedef
EFI_STATUS
(EFIAPI *EFI_SIGNAL_EVENT)(
  IN  EFI_EVENT                Event
  );
  • 将事件设置为触发态。如果该事件在一个组中,则将族中所有的事件设置为触发态。

CloseEvent 关闭事件 #

typedef
EFI_STATUS
(EFIAPI *EFI_CLOSE_EVENT)(
  IN EFI_EVENT                Event
  );

SetTimer #

  • EVT_TIMER是一类特殊的事件,可以通过SetTimer服务设置定时器属性。
/**
  @retval EFI_SUCCESS           The event has been set to be signaled at the requested time.
  @retval EFI_INVALID_PARAMETER Event or Type is not valid.

**/
typedef
EFI_STATUS
(EFIAPI *EFI_SET_TIMER)(
  IN  EFI_EVENT                Event,
  IN  EFI_TIMER_DELAY          Type,    //定时器类型
  IN  UINT64                   TriggerTime  //过期事件,100ns为一个单位
  );
  • 定时器类型如下:
类型
TimerCancel 取消定时器触发
TimerPeriodic 重复型定时器
TimerRelative 一次性定时器
  • 如果Type为TimerPeriodic并且TriggerTIme是0,则定时器每个时钟滴答触发一次。
  • 生成定时器事件包含两步:
    1. 通过CreateEvent生成一个EVT_TIMER事件
    2. 通过SetTimer设置这个定时器事件的属性。