当前位置: 首页 > news >正文

【UEFI系列】Event

文章目录

  • 前言
  • 一、什么是Event,Event相关函数
  • 二、Event的功能
    • Event的种类
    • CreateEvent
    • CreateEventEx
    • SignalEvent
    • CloseEvent
    • RegisterProtocolNotify
    • WaitForEvent
    • CheckEvent
  • 三、Timer Event
    • 怎么create Timer Event


前言

不同于ARM平台不支持SMI,x86和ARM平台的bios都会支持EVENT。


一、什么是Event,Event相关函数

Event,是UEFI中一个重要的东西,UEFI不再支持interrupt中断,所有的异步操作都要通过Event来完成。
比如说我们设定了一个功能,但是我们不想立即跑,就需要设置触发条件,它触发了才会跑。
比如,一个protocol install好之后功能会跑/exitbootservice了功能会跑/每个10s功能会跑。

Boot Services为开发者提供3类函数:事件相关函数、定时器相关函数、TPL(Task priority Level任务优先级)相关函数,用于用于操作事件、定时器、TPL。

Event, Timer, and Task Priority Functions

函数名作用
CreateEvent生成一个事件对象
CreateEventEx生成一个事件对象并将该事件加入到一个组内 //一执行就全部执行,比如挂在ReadyToBoot事件组里的
CloseEvent关闭事件对象 //关了就不能再被触发了,和create是对应的,而不是signal,相当于被删了
SignalEvent触发事件对象
WaitForEvent等待事件数组中的任一事件触发
CheckEvent检查事件状态 //确认是已经被执行还是等待被执行
SetTimer设置定时器属性 //比如说是周期性触发还是一次性触发,如果是周期性,周期是多少
RaiseTPL提升任务优先级 //比如说很多event都挂在同一个事件里,设置TPL就能设置执行的优先顺序
RestoreTPL恢复任务优先级

二、Event的功能

Event的种类

Boot Services中事件相关函数有6个,CreateEvent/CreateEventEx、SignalEvent及CloseEvent、WaitForEvent和CheckEvent。

关于Event的种类,Event.c里是这么写的:

/// Enumerate the valid types
UINT32  mEventTable[] = {/// 0x80000200       Timer event with a notification function that is/// queue when the event is signaled with SignalEvent()EVT_TIMER | EVT_NOTIFY_SIGNAL,/// 0x80000000       Timer event without a notification function. It can be/// signaled with SignalEvent() and checked with CheckEvent() or WaitForEvent().EVT_TIMER,/// 0x00000100       Generic event with a notification function that/// can be waited on with CheckEvent() or WaitForEvent()EVT_NOTIFY_WAIT,/// 0x00000200       Generic event with a notification function that/// is queue when the event is signaled with SignalEvent()EVT_NOTIFY_SIGNAL,/// 0x00000201       ExitBootServicesEvent.EVT_SIGNAL_EXIT_BOOT_SERVICES,/// 0x60000202       SetVirtualAddressMapEvent.EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,/// 0x00000000       Generic event without a notification function./// It can be signaled with SignalEvent() and checked with CheckEvent()/// or WaitForEvent().0x00000000,/// 0x80000100       Timer event with a notification function that can be/// waited on with CheckEvent() or WaitForEvent()EVT_TIMER | EVT_NOTIFY_WAIT,
};
宏定义描述
EVT_TIMER0x80000000定时器事件
EVT_RUNTIME0x40000000在运行时(OS 启动后)仍然有效的事件
EVT_NOTIFY_WAIT0x00000100当 WaitForEvent() 被调用时触发通知函数
EVT_NOTIFY_SIGNAL0x00000200当 SignalEvent() 被调用时触发通知函数
EVT_SIGNAL_EXIT_BOOT_SERVICES0x00000201ExitBootServices 被调用时触发
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE0x60000202在 SetVirtualAddressMap 被调用时触发

不同bios厂商还会定义不同的Event Types,比如:

宏定义描述
EVT_EFI_SIGNAL_READY_TO_BOOT0x00000203ReadyToBoot 阶段触发
EVT_SIGNAL_LEGACY_BOOT0x00000204在 Legacy Boot 被触发时调用

注意:Ready To Boot 事件发生在 UEFI Boot Manager 准备启动操作系统之前,但在调用 ExitBootServices() 之前。

CreateEvent

实际上CreateEvent的结构体原型:

typedef
EFI_STATUS
(EFIAPI *EFI_CREATE_EVENT)(IN  UINT32                       Type, //事件类型,就是我们之前介绍的几种typeIN  EFI_TPL                      NotifyTpl, //事件Notify函数的优先级,是0~31的整数IN  EFI_EVENT_NOTIFY             NotifyFunction OPTIONAL, //事件NotifyFunction函数IN  VOID                         *NotifyContext OPTIONAL, //传给事件NotifyFunction函数的参数OUT EFI_EVENT                    *Event //生成的事件);
typedef
VOID
(EFIAPI *EFI_EVENT_NOTIFY)(IN  EFI_EVENT                Event,IN  VOID                     *Context);

优先级高的event可以中断优先级低的。关于优先级EDKII这样定义,可以看到NOTIFY的优先级相对比较高:

// Task priority level
#define TPL_APPLICATION  4
#define TPL_CALLBACK     8
#define TPL_NOTIFY       16
#define TPL_HIGH_LEVEL   31
  1. UEFI EDKII BIOS代码里的EVT_SIGNAL_EXIT_BOOT_SERVICES类型应用:
    (exit boot services时出发的event,要执行的函数就是自己定义的ExitBootServicesEventA)
VOID EFIAPI ExitBootServicesEventA ( //其实就是EFI_EVENT_NOTIFY的结构IN EFI_EVENT  Event,IN VOID       *Context)
{DEBUG ((DEBUG_ERROR,"ExitBootServicesEventA\n"));
}//执行函数里的部分,前后代码懒得写了Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, //事件类型,exit boot services的时候触发TPL_NOTIFY, //优先级为16ExitBootServicesEventA, //执行的函数NULL,&EfiExitBootServicesEvent //event名称);
  1. UEFI EDKII BIOS代码里的EVT_NOTIFY_SIGNAL类型应用:
    (满足条件触发的event,要执行的函数就是自己定义的MyCallback)

RegisterProtocolNotify 是专门用于 UEFI 中的Event机制的一部分。它的主要作用是注册一个事件,当某个指定的 Protocol 被安装或重新安装时触发该事件,从而实现异步通知功能。

VOID EFIAPI MyCallback ( //其实就是EFI_EVENT_NOTIFY的结构IN EFI_EVENT  Event,IN VOID       *Context)
{DEBUG ((DEBUG_ERROR,"MyCallback\n"));
}//执行函数里的部分,前后代码懒得写了
Status = gBS->CreateEvent(EVT_NOTIFY_SIGNAL, //事件类型TPL_CALLBACK, //优先级为16MyCallback, //执行的函数NULL, &MyEvent); //event名称if(!EFI_ERROR(Status))
{Status = gBS->RegisterProtocolNotify( //只要有地方Install或者Reinstall对应的guid的protocol就会触发&xxxProtocolGuid, //protocol的guidMyEvent, //event名称&CallBack ); //执行的函数
}

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, //事件组,特定的guid。比起EFI_CREATE_EVENT多的参数OUT      EFI_EVENT              *Event);

当挂在这个事件组里的Event被触发后,其他地方此Event挂的函数都会被触发,根据TPL优先级执行。
比如ReadyToBoot这种,就是创建一个event在event组里,然后再触发这个event,挂在这个guid上的event组里的其他event全部触发。

有四个UEFI规定的事件组:

宏名称(前略)描述
_EXIT_BOOT_SERVICES当系统调用 ExitBootServices() 时触发
_VIRTUAL_ADDRESS_CHANGE当系统调用 SetVirtualAddressMap() 时触发
_READY_TO_BOOT当系统准备启动 OS 时触发
_MEMORY_MAP_CHANGE当内存映射发生变化时触发

SignalEvent

立即触发Event,像如果出发的是事件组里的一个Event,其他此Event挂的函数也随后一起触发了。

Status = gBS->SignalEvent (XXXEvent);

CloseEvent

立即关闭Event,相当于删除,Event再也不会被触发。将Event从内核各个队列中清除,释放掉相关内存(Ex同理)。

Status = gBS->CloseEvent (XXXEvent);

RegisterProtocolNotify

看我在CreateEvent里的2.的那段应用。
用这个函数设置Install protocol的guid,等到时候install 这个guid的protocol时它就会被触发。

WaitForEvent

用于等待事件的发生,函数原型:

typedef
EFI_STATUS
(EFIAPI *EFI_WAIT_FOR_EVENT)(IN  UINTN                    NumberOfEvents, //event数组内的event个数IN  EFI_EVENT                *Event, //event数组OUT UINTN                    *Index //返回处于触发态的event在数组内的下标);

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

举例:

Void myEventNotify(EFI_EVENT Event,VOID *Context)
{static UINTN times=0; //must define static typePrint(L"myEventNotify Wait %din",times);times=times+1;if(times>5){Print(L"SignalEvent start\n");gBS->SignalEvent(Event);Print(L"SignalEvent end\n");}
}EFI_STATUS EventDemoEntryPoint(IN EFI_HANDLEImageHandle,IN EFL_SYSTEM_TABLE*SystemTable
)
{EFL_ STATUS Status;UINTN index=0,EFI_EVENT myEvent;Status = gBS->CreateEvent (EVT_NOTIFY_WAIT,TPL_NOTIFY,myEventNotify.(void*)NULL,&myEvent );Status = gBS->WaitForEvent(1,&myEvent,&index); //会一直停下来去检查myEvent从而触发myEventNotifyreturn Status;
}

Result:在WaitForEvent的循环中,每检查一次myEvent的状态,myEventNotify就执行一次。检查6次后,myEvent被触发,从而WaitForEvent结束等待。
在这里插入图片描述

CheckEvent

CheckEvent用于检测事件的状态。与WaitForEvent不同的是,CheckEvent调用后立刻返回。函数原型:

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

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

三、Timer Event

定时器是一个特殊的事件,生成定时器事件后,可以通过SetTimer服务设
置定时器属性。函数原型:

typedef
EFI_STATUS
(EFIAPI *EFI_SET_TIMER)(IN  EFI_EVENT                Event, //Timer事件IN  EFI_TIMER_DELAY          Type, //定时器类别IN  UINT64                   TriggerTime //定时器触发时间,100ns为一个衡量单位);
/// Timer delay types
typedef enum {/// 用于取消定时器触发事件。设置后定时器不再触发TimerCancel,/// 重复性定时器。每TriggerTime*100ns,定时器触发一次TimerPeriodic,/// 一次性定时器。TriggerTime*100ns时触发TimerRelative
} EFI_TIMER_DELAY;

如果Type为TimerPeriodic并且TriggerTime是0,则定时器每个时钟点都会触发一次。
如果Type为TimerRelative并且TriggerTime是0,则定时器在下个时钟点触发(相当于立即出发)。

怎么create Timer Event

生成定时器事件一般分为两步:
第一步,通过CreateEvent生成一个EVT_TIMER事件。
第二步,通过SetTimer设置这个定时器事件的属性。

EFI_STATUS
TimerCreateTimer
( 	EFI_EVENT *Event,EFI_EVENT_NOTIFY Callback,VOID *Context,EFI_TIMER_DELAY Delay,UINT64 Trigger,EFI_TPL CallBackTPL )
{EFI_STATUS Status;UINT32 EventType = EFI_EVENT_TIMER;if (Callback != NULL )EventType |= EFI_EVENT_NOTIFY_SIGNAL;Status = gBS->CreateEvent(EventType,CallBackTPL,Callback,Context,Event); //第一步if(EFI_ERROR( Status ))return Status;Status = gBS->SetTimer( *Event, Delay, Trigger ); //第二步if (EFI ERROR( Status ))TimerStopTimer( Event );return Status;
}

更详细的例子:

void
myEventNotify(EFI_EVENT Event,VOID *Context)
{static UINTN times=0; //must define static typePrint(L"myEventNotify Wait %d\n",times);times=times+1;
}EFI_STATUS SetTimerDemoEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable
{EFI_STATUS Status;UINTN i=O;EFI_EVENT myEvent;Print(L"Entry to SetTimerDemoEntryPoint\n");Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL,TPL_CALLBACK,(EFL_EVENT_NOTIFY)myEventNotify,(void*)NULL,&myEvent);Status = gBS->SetTimer(myEvent, TimerPeriodic,10*1000*1000); //周期性for (i = 0; i < 5; i++) {Print(L"Waiting...\n");gBS->Stall(1000 * 1000); // 1秒,等待定时器触发}//终止信号gBS->SignalEvent(myEvent);//关闭eventgBS->CloseEvent(myEvent);Print(L"End to SetTimerDemoEntryPointin");return Status;
}

结果:

Entry to SetTimerDemoEntryPoint
Waiting...
myEventNotify Wait 0
Waiting...
myEventNotify Wait 1
Waiting...
myEventNotify Wait 2
Waiting...
myEventNotify Wait 3
Waiting...
myEventNotify Wait 4
myEventNotify Wait 5
End to SetTimerDemoEntryPoint
http://www.lryc.cn/news/606534.html

相关文章:

  • 超简单的跳动爱心
  • 【SpringMVC】拦截器,实现小型登录验证
  • 逻辑回归——银行贷款案例分析
  • Lomsat gelral 树上启发式合并
  • Coze是什么?能做什么?
  • Dify插件安装失败,一直处于安装状态?
  • 【智能体agent】入门之--1.初体验
  • OpenCV学习day2
  • RabbitMQ的特点和消息可靠性保障
  • 【neo4j】跨版本升级数据库
  • 《Java 程序设计》第 14 章 - JavaFX 基础
  • MySQL 8.0 OCP 1Z0-908 题目解析(42)
  • 企业级部署 (基于tomcat与nginx)
  • Linux和shell
  • 【运维基础】Linux 文件系统基本管理
  • Side band ECC、Inline ECC、On-die ECC、Link ECC
  • chrome.storage 和 localStorage
  • Android 基础入门学习目录(持续更新)
  • kettle插件-kettle http client plus插件,轻松解决https接口无法调用文件流下载问题
  • 面试笔记【16:9区域问题】
  • SQL注入SQLi-LABS 靶场less25a-28a详细通关攻略
  • ESP32 外设驱动开发指南 (ESP-IDF框架)——GPIO篇:基础配置、外部中断与PWM(LEDC模块)应用
  • 机械学习--逻辑回归
  • 第1章:基础篇——第1节:基础操作与认识界面
  • Vercel 全面介绍与网站部署指南
  • 老旧远程控制管理模块(物联网设备)渗透实战:SNMP泄露+内核提权攻击链深度解析
  • Gold 序列
  • 7月31号打卡
  • nvm安装nodejs后提示No installations recognized
  • 爱普生002墨水与004墨水基本参数及支持机型