DXE-Event和Timer练习

一、了解

UEFI内部只使用时钟中断,摒弃了其他中断,所以异步操作通过Event实现。至于使用event替代中断的优点,个人认为在于,使用中断不方便代码在不同平台的移植。
 以下是相关API

CreateEventCreates a general-purpose event structure
CreateEventExCreates an event structure as part of an event group
CloseEventCloses and frees an event structure
SignalEventSignals an event
WaitForEventStops execution until an event is signaled
CheckEventChecks whether an event is in the signaled state
SetTimerSets an event to be signaled at a particular time
RaiseTPLRaises the task priority level
RestoreTPLRestores/lowers the task priority level

UEFI只保留了时钟中断,所有异步时间都是通过event完成。

摒弃复杂的中断操作,更有利于代码在不同平台的移植。

主要相关函数如下:

1. CreateEventEx

生成一个事件对象并将该事件加入到一个组内
由 CreateEventEx 生成的事件会加入到 EventGroup 中。当 EventGroup 中的任一事件被触发后,组内所有其他事件都会被触发,进而组内所有 Notification 函数都将加入到待执行队列。

/**
  Creates an event in a group.

  @param[in]   Type             The type of event to create and its mode and attributes.
  @param[in]   NotifyTpl        The task priority level of event notifications,if needed.
  @param[in]   NotifyFunction   The pointer to the event's notification function, if any.
  @param[in]   NotifyContext    The pointer to the notification function's context; corresponds to parameter
                                Context in the notification function.
  @param[in]   EventGroup       The pointer to the unique identifier of the group to which this event belongs.
                                If this is NULL, then the function behaves as if the parameters were passed
                                to CreateEvent.
  @param[out]  Event            The pointer to the newly created event if the call succeeds; undefined
                                otherwise.

  @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
  );

第一个参数:事件类型

  • 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 的特性。

第二个参数:优先级

  • 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 函数

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

第四个参数:

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

2. CreateEvent

生成一个事件对象
如果 EventGroup 为 NULL,则 CreateEventEx 与 CreateEvent 相同。CreateEvent实现如下:

EFI_STATUS
EFIAPI
CoreCreateEvent (
  IN UINT32                   Type,
  IN EFI_TPL                  NotifyTpl,
  IN EFI_EVENT_NOTIFY         NotifyFunction, OPTIONAL
  IN VOID                     *NotifyContext, OPTIONAL
  OUT EFI_EVENT               *Event
  )
{
  return CoreCreateEventEx (Type, NotifyTpl, NotifyFunction, NotifyContext, NULL, Event);
}

3. CloseEvent

关闭事件对象
调用该函数后,指定事件从内核中删除。

4. SignalEvent

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

5. 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. WaitForEvent

等待事件数组的任一事件触发 ,内部循环调用CheckEvent,根据时间状态进行相应操作

7. 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 UINT64TriggerTime     // 定时器过期时间,100ns 为一个单位   
                             );

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

8. RaiseTPL和RestoreTPL

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

RaiseTPL 和 RestoreTPL 必须成对出现。当任务优先级提升至TPL_HEIGH_LEVEL时,将关闭中断。当任务优先级恢复到原来值时,中断被重新打开。
UEFI 是单 CPU 单线程系统,数据竞争 RaiseTPL 和 RestoreTPL 必须成对出现。当任务优先级提升至 TPL_HEIGH_LEVEL时,将关闭中断。当任务优先级恢复到原来值时,中断被重新打开。 UEFI 是单 CPU 单线程系统,数据竞争来自中断处理函数。通过CoreAcquireLock 和 CoreReleaseLock 实现 UEFI 锁。来自中断处理函数。通过 CoreAcquireLock 和 CoreReleaseLock 实现 UEFI 锁。

二、编程练习

1.CreateEvent

实现每秒打印,按键Enter退出。

C文件

PATH:

edk2/EmulatorPkg/TestByMy/DxeTest/TestEvent/TestCreatEvent/TestEvent.c

code:

/** @file

event tarin

**/

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


/**
  Invoke a notification event

  @param[in]  Event                 Event whose notification function is being invoked.
  @param[in]  Context               The pointer to the notification function's context,
                                    which is implementation-dependent.

**/
VOID
EFIAPI
NotifyPrint (
  IN  EFI_EVENT                Event,
  IN  VOID                     *Context
  )
{
  Print (L"%s\n",(CHAR16 *)Context);
}


/**
  The user Entry Point for App

  @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
TestEventEntry (
  IN EFI_HANDLE               ImageHandle,
  IN EFI_SYSTEM_TABLE         *SystemTable
  )
{
  EFI_STATUS                  Status;
  EFI_EVENT                   MyEvent[2];
  UINTN                       Index;
  CHAR16                      *MyString = L"print from Notify!";
  EFI_INPUT_KEY               MyKey;

  Status = gBS->CreateEvent (
                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
                  TPL_CALLBACK,
                  NotifyPrint,
                  MyString,
                  &MyEvent[1]
                  );
  ASSERT_EFI_ERROR (Status);

  Status = gBS->SetTimer(
                  MyEvent[1],
                  TimerPeriodic,
                  EFI_TIMER_PERIOD_SECONDS(1)
                  );
  ASSERT_EFI_ERROR (Status);
  
  MyEvent[0] = gST->ConIn->WaitForKey;
 
  while (1) {
    //Status = gBS->CheckEvent (MyEvent[1]);
    Status = gBS->WaitForEvent (
                  2,
                  MyEvent,
                  &Index
                  );

    if (Index == 0) {
      Status = gST->ConIn->ReadKeyStroke (
                         gST->ConIn, 
                         &MyKey
                         );
      if (MyKey.UnicodeChar == 0x0D) {
        gBS->CloseEvent(MyEvent[1]);
        break;
      }
    }
  }
  
  return  Status;
}

inf文件

PATH:

edk2/EmulatorPkg/TestByMy/DxeTest/TestEvent/TestCreatEvent/TestEvent.inf

code:

## @file
#  Sample UEFI Application Reference EDKII Module.
#
#  
#  test createvent
#
#
#
##

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = TestEvent
  
  FILE_GUID                      = 6987936E-ED34-44db-AE97-1F25E4E21116
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = TestEventEntry



[Sources]
  TestEvent.c

[Packages]
  MdePkg/MdePkg.dec
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  UefiBootServicesTableLib
  DebugLib

dsc文件

PATH:

edk2/EmulatorPkg/EmulatorPkg.dsc

code:

 #  DXE Phase modules
 ##
 #
EmulatorPkg/TestByMy/DxeTest/TestEvent/TestCreatEvent/TestEvent.inf

2.CreateEventEx

实现两个event生成在一个group

C文件

PATH:

edk2/EmulatorPkg/TestByMy/DxeTest/TestEvent/TestCreatEventEx/TestCreatEventEx.c

code:

/** @file

event tarin

**/

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


EFI_GUID gEfiMyEventGuid = { 0x7CE88FA3, 0x5BD7, 0x4679, { 0x88, 0xA8, 0xA8, 0xD8, 0xDE, 0xE5, 0x0D, 0x2B }};


/**
  Invoke a notification event

  @param[in]  Event                 Event whose notification function is being invoked.
  @param[in]  Context               The pointer to the notification function's context,
                                    which is implementation-dependent.

**/
VOID
EFIAPI
NotifyPrint1 (
  IN  EFI_EVENT                Event,
  IN  VOID                     *Context
  )
{
  Print (L"%s\n",(CHAR16 *)Context);
}

/**
  Invoke a notification event

  @param[in]  Event                 Event whose notification function is being invoked.
  @param[in]  Context               The pointer to the notification function's context,
                                    which is implementation-dependent.

**/
VOID
EFIAPI
NotifyPrint2 (
  IN  EFI_EVENT                Event,
  IN  VOID                     *Context
  )
{
  Print (L"%s\n",(CHAR16 *)Context);
}

/**
  The user Entry Point for App

  @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
TestEventExEntry (
  IN EFI_HANDLE               ImageHandle,
  IN EFI_SYSTEM_TABLE         *SystemTable
  )
{
  EFI_STATUS                  Status;
  EFI_EVENT                   MyEvent[2];
  CHAR16                      *Test1String = L"event1";
  CHAR16                      *Test2String = L"event2";

 Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_CALLBACK,
                  NotifyPrint1,
                  Test2String,
                  &gEfiMyEventGuid,
                  &MyEvent[1]
                  );
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_CALLBACK,
                  NotifyPrint1,
                  Test1String,
                  &gEfiMyEventGuid,
                  &MyEvent[0]
                  );


  gBS->SignalEvent(MyEvent[0]);

  gBS->CloseEvent(MyEvent[0]);
  gBS->CloseEvent(MyEvent[1]);

  return  Status;
}

inf文件

PATH:

edk2/EmulatorPkg/TestByMy/DxeTest/TestEvent/TestCreatEventEx/TestCreatEventEx.inf

code:

## @file
#  Sample UEFI Application Reference EDKII Module.
#
#  
#  test createvent
#
#
#
##

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = TestEventEx
  
  FILE_GUID                      = 6987936E-ED34-44db-A597-1F25E4881116
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = TestEventExEntry



[Sources]
  TestCreatEventEx.c

[Packages]
  MdePkg/MdePkg.dec
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  UefiBootServicesTableLib
  DebugLib

dsc文件

PATH:

edk2/EmulatorPkg/EmulatorPkg.dsc

code:

#DXE Phase modules
##
#
EmulatorPkg/TestByMy/DxeTest/TestEvent/TestCreatEventEx/TestCreatEventEx.inf
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值