美文网首页
第六章 事件

第六章 事件

作者: Maxwell_Li | 来源:发表于2018-09-05 11:21 被阅读0次

    作者:Maxwell Li
    日期:2017/12/09
    未经作者允许,禁止转载本文任何内容。如需转载请留言。


    [TOC]

    UEFI 事件服务

    函数名 作用
    CreateEvent 生成一个事件对象
    CreateEventEX 生成一个事件对象并加入到一个组内
    CloseEvent 关闭事件对象
    SignalEvent 触发事件对象
    WaitForEvent 等待事件数组中的任意事件触发
    CheckEvent 检查事件状态
    SetTimer 设置定时器属性
    RaiseTPL 提升任务优先级
    RestoreTPL 恢复任务优先级

    6.1 事件函数

    6.1.1 WaitForEvent 服务

    WaitForEvent 服务函数原型:

    /**等待 Event 数组内任一事件被触发
      @retval EFI_SUCCESS            下标为 *index 的事件被触发
      @retval EFI_UNSUPPORTED        当前的 TPL 不是 TPL_APPLICATION
      @retval EFI_INVALID_PARAMETER  下标为 *index 的时间类型为 EVT_NOTIFY_SIGNAL
    **/
    typedef EFI_STATUS(EFIAPI *EFI_WAIT_FOR_EVENT)(
    IN UINTN NumberOfEvents,  // Event 数组内 Event 个数
    IN EFI_EVENT *Event,      // Event 数组
    OUT UINTN *Index          // 返回处于触发状态的事件在数组内的下标
    );
    

    WaitForEvent 没有超时属性,如果需要只等待一定的时间,需要在事件组加入定时器事件。

    6.1.2 CreateEvent 服务

    CreateEvent 服务函数原型:

    typedef EFI_STATUS(EFIAPI *EFICREATE_EVENT)(
      IN UINT32 Type,                               // 事件类型
      IN EFI_TPL NotifyTpl,                         // 事件 Notification 函数的优先级
      IN EFI_EVENT_NOTIFY NotifyFunction, OPTIONAL  // 事件 Notification 函数
      IN VOID *NotifyContext, OPTIONAL              // 传给事件 Notification 函数的参数
      OUT EFI_EVENT *Event                          // 生成的事件
      );
    

    事件的类型

    CreateEvent 第一个参数为 Type。

    常用事件类型

    事件类型 事件特征
    EVT_TIMER 定时器事件。普通 Timer 事件,没有 Notification 函数。生成事件后需设置时钟属性。事件可以:
    1. 通过 SetTimer() 设置等待事件
    2. 到期后通过 SignalEvent() 触发
    3. 通过 WaitForEvent() 等待事件被触发
    4. 通过 CheckEvent() 检查状态
    EVT_NOTIFY_WAIT 普通事件。该事件有一个 Notification 函数,当该事件通过 CheckEvent() 检查状态或通过 WaitForEvent() 等待时,Notification 函数会被放到待执行队列 gEventQueue[Event->NotifyTpl] 中。
    EVT_NOTIFY_SIGNAL 普通事件。该事件有一个 Notification 函数,当该事件通过 SignalEvent() 被触发时,Notification 函数会被放到待执行队列 gEventQueue[Event->NotifyTpl] 中。
    0x00000000 普通事件。此类事件没有 Notification 函数。事件可以:
    1. 通过SignalEvent() 被触发
    2. 通过 WaitForEvent() 等待事件被触发
    3. 通过 CheckEvent() 检查状态
    EVT_TIMER |
    EVT_NOTIFY_WAIT
    带 Notification 函数的定时器时间。同时具有 EVT_TIMER 和 EVT_NOTIFY_WAIT 的特性。
    EVT_TIMER |
    EVT_NOTIFY_SIGNAL
    带 Notification 函数的定时器时间。同时具有 EVT_TIMER 和 EVT_NOTIFY_SIGNAL 的特性。

    两种特殊的事件:

    • EVT_SIGNAL_EXIT_BOOT_SERVICES:是 EVT_NOTIFY_SIGNAL 和 0x00000001 的组合。当 ExitBootServices() 执行时,事件被触发。该事件不能和其他类型混合使用。它的 Notification 函数和子函数不能使用启动服务中的内存分配服务,也不能使用定时器服务。
    • EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:是 EVT_RUNTIME_CONTEXT、EVT_RUNTIME、EVT_NOTIFY_SIGNAL、0x00000002 的组合。不能和这四类以外的类型组合使用,当 SetVirtualAddressMap() 被调用时触发。

    优先级

    CreateEvent 第二个参数为 NotifyTPL,是 0-31 的一个整数。优先级别搞得任务可以中断级别低的任务。

    UEFI 预定任务优先级

    任务优先级 用法
    TPL_APPLICATION(4) 应用程序(包括 Boot Manager 和 OS Loader)运行在这个级别。
    当程序运行在这个级别时,任务队列中没有任何处于就绪状态的事件 Notification 函数。
    TPL_CALLBACK(8) 比较耗时的操作通常在该优先级执行。
    例如:文件系统、磁盘操作等。
    TPL_NOTIFY(16) 运行在该级别的程序不允许阻塞,必须尽快执行完毕且返回。
    如果需要更多操作,则使用 Event 由内核重新调度。
    通常底层的 IO 操作允许在这个级别。
    例如:UEFI 内核中读取键盘状态的代码。
    大部分 Event 的 Notification 函数允许在这个级别。
    TPL_HIGH_LEVEL(31) 在该级别,中断被禁止。
    UEFI 内核全局变量的修改需要允许在该级别。

    Notification 函数 NotifyFunction

    CreateEvent 第三个参数为 NotifiFunction,是 EFI_EVENT_NOTIFY 类型的函数指针。

    • 事件类型是 EVT_NOTIFY_WAIT,EFI_EVENT_NOTIFY 函数会在等待此事件的过程中调用。
    • 事件类型是 EVT_NOTIFY_SIGNAL,EFI_EVENT_NOTIFY 函数会在事件触发时调用。
    • 若不是以上类型,Notification 参数将被忽略。

    CreateEvent 第四个参数为 NotifyContext,将在 Notification 函数被调用时作为第二个参数传递给该函数,用于指向这个 Notification 函数的上下文。

    6.1.3 CreateEventEx 服务

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

    CreateEventEx 服务函数原型:

    typedef EFI_STATUS(EFIAPI *EFICREATE_EVENT_EX)(
      IN UINT32 Type,                               // 事件类型
      IN EFI_TPL NotifyTpl,                         // 事件 Notification 函数的优先级
      IN EFI_EVENT_NOTIFY NotifyFunction, OPTIONAL  // 事件 Notification 函数
      IN VOID *NotifyContext, OPTIONAL              // 传给事件 Notification 函数的参数
      IN CONST EFI_GUID *EventGroup OPTIONAL,       // 事件组
      OUT EFI_EVENT *Event                          // 生成的事件
      );
    

    由 CreateEventEx 生成的事件会加入到 EventGroup 中。当 EventGroup 中的任一事件被触发后,组内所有其他事件都会被触发,进而组内所有 Notification 函数都将加入到待执行队列。如果 EventGroup 为 NULL,则 CreateEventEx 与 CreateEvent 相同。

    UEFI 预定义的四个 Event 组:

    1. EFI_EVENT_GROUP_EXIT_BOOT_SERVICES
    • GUID:gEfiEventExitBootServicesGuid
    • ExitBootServices() 执行时触发该组所有 Event,组内 Event 属性同于 EVT_SIGNAL_EXIT_BOOT_SERVICES。
    1. EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE
    • GUID:fEfiEventVirtualAddressChangeGuid
    • SetVirtualAddressMap() 执行时触发该组所有 Event,组内 Event 属性同于 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE。
    1. EFI_EVENT_GROUP_MEMORY_MAP_CHANGE
    • GUID:gEfiEventMemoryMapChangeGuid
    • Memory Map 改变时触发该组所有 Event,在 Notification 函数中不能使用启动服务的内存分配函数。
    1. EFI_EVENT_GROUP_READY_TO_BOOT
    • GUID:gEfiEventReadyToBoot
    • Boot Manager 加载并且执行一个启动项时触发该组所有 Event。

    6.1.4 CheckEvent 服务

    CheckEvent 服务用于检查事件的状态,返回值有以下四种情况:

    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。

    6.1.5 SignalEvent 服务

    SignalEvent 服务用于将事件设置为触发状态。

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

    6.1.6 CloseEvent 服务

    事件使用完毕后必须调用 CloseEvent 关闭该事件。调用该函数后,指定事件从内核中删除。

    6.2 定时器事件

    SetTimer 服务函数原型:

    /**设置定时器属性
      @retval EFI_SUCESS             属性设置成功
      @retval EFI_INVALID_PARAMETER  参数 Event 不是 EVT_TIMER 或参数非法
    **/
    typedef EFI_STATUS(EFIAPI *EFI_SET_TIMER)(
      IN EFI_EVENT Event,       // Timer 事件
      IN EFI_TIMER_DELAY Type,  // 定时器类别
      IN UINT64 TriggerTime     // 定时器过期时间,100ns 为一个单位
      );
    

    定时器类别

    Type 作用
    TimerCancel 用于取消定时器触发事件,设置后定时器不再触发
    TimerPeriodic 重复型定时器。每 TriggerTime*100ns 定时器触发一次
    TimerRelative 一次性定时器。TriggerTime*100ns 定时器触发
    • Type 为 TimerPeriodic 且 TriggerTime 为0:定时器每个时钟滴答触发一次。
    • Type 为 TimerRelative 且 TriggerTime 为0:定时器在下个时钟滴答触发。

    6.3 任务优先级

    6.3.1 提升和恢复任务优先级

    • RaiseTPL(NewTpl) 用于提升当前任务的优先级至 NewTpl,该函数的返回值为原来的任务优先级。
    • RestoreTPL 用于恢复至原来的任务优先级。

    RaiseTPL 和 RestoreTPL 必须成对出现。当任务优先级提升至 TPL_HEIGH_LEVEL 时,将关闭中断。当任务优先级恢复到原来值时,中断被重新打开。

    UEFI 是单 CPU 单线程系统,数据竞争来自中断处理函数。通过 CoreAcquireLock 和 CoreReleaseLock 实现 UEFI 锁。

    6.3.2 UEFI 中的时钟中断

    时钟处理函数 CoreTimerTick

    时钟处理函数 CoreTimerTick 在时钟中断中调用,是时钟中断处理函数的主体。进入该函数时需要加锁,离开时需要解锁。主要功能:维持系统时间、检查定时器事件列表中到期事件。

    CoreTimerTick 函数实现:

    VOID EFIAPI CoreTimerTick( IN UINT64 Duration )
    {
      IEVENT *Event;
      CoreAcquireLock (&mEfiSystemTimeLock);
      // 任务一:更新系统时间
      mEfiSystemTime += Duration;
      // 任务二:检查系统中的定时器时间是否到期
      if (!IsListEmpty (&mEfiTimerList)) {
        Event = CR (mEfiTimerList.ForwardLink, IEVENT, Timer.Link, EVENT_SIGNATURE);
        // 如果到期,则触发 mEfiCheckTimerEvent 事件
        // 该事件会检查并触发所有到期的定时器事件
        if (Event->Timer.TriggerTime <= mEfiSystemTime) {
          CoreSignalEvent (mEfiCheckTimerEvent);
        }
      }
      CoreReleaseLock (&mEfiSystemTimeLock);  // 释放锁并开中断
    }
    

    设置时钟处理函数及安装时钟中断

    函数 CoreTimerTick 如何注册到时钟中断处理函数中:

    (1)安装时钟中断处理函数的时机
    (2)向 gTimer 注册 CoreTimerTick 函数
    (3)向 CPU 注册中断处理函数 TimerInterruptHandler
    (4)在 CPU 时钟中断向量中调用时钟中断处理函数

    该部分待补充。

    6.3.3 UEFI 事件 Notification 函数的派发

    事件 Notification 函数的派发是在 gBS->RestoreTpl 服务中完成的。gBS->RestoreTpl 实际指向 CoreRestoreTpl 函数,实现如下:

    VOID EFIAPI CoreRestoreTpl ( IN EFI_TPL NewTpl )
    {
      EFI_TPL OldTpl;
      OldTpl = gEfiCurrentTpl;
      ASSERT (NewTpl <= OldTpl);
      ASSERT (VALID_TPL (NewTpl));
      if (OldTpl >= TPL_HIGH_LEVEL  &&  NewTpl < TPL_HIGH_LEVEL) {
        gEfiCurrentTpl = TPL_HIGH_LEVEL;
      }
      // 事件列表中高优先级的任务就绪
      while (((-2 << NewTpl) & gEventPending) != 0) {
        // gEfiCurrentTpl 赋值为 gEventPending 中的为 1 的最高位
        gEfiCurrentTpl = (UINTN) HighBitSet64 (gEventPending);
        if (gEfiCurrentTpl < TPL_HIGH_LEVEL) {
          CoreSetInterruptState (TRUE);
        }
        // 执行 gEventQueue[gEfiCurrentTpl] 队列中所有 Event 的 Notification 函数
        CoreDispatchEventNotifies (gEfiCurrentTpl);
      }
      // 设置新的任务优先级
      gEfiCurrentTpl = NewTpl;
      // 如果优先级低于 TPL_HIGH_LEVEL,打开中断。
      if (gEfiCurrentTpl < TPL_HIGH_LEVEL) {
        CoreSetInterruptState (TRUE);
      }
    
    • ((-2 << NewTpl) & gEventPending) != 0 表示事件列表中有高优先级的任务待执行。
    • 时钟中断每 10ms(DEFAULT_TIMER_TICK_DURATION)中断一次。进入时钟中断时,会调用 RaiseTpl,任务优先级提升至 TPL_HEIGH_LEVEL,中断调用返回时,会调用 RestoreTpl 恢复优先级。因此中断调用返回后,所有高于原优先级的 Notification 都已被执行。

    相关文章

      网友评论

          本文标题:第六章 事件

          本文链接:https://www.haomeiwen.com/subject/cjkrwftx.html