一、 UEFI Driver Binding 模型
1、简要描述
UEFI Driver Binding 是 UEFI Driver 管理设备的标准模型。
UEFI Driver 加载后,通过安装EFI_DRIVER_BINDING_PROTOCOL到Driver Image Handle上,向系统声明自己可以识别、启动和停止某类设备Controller。
2、核心函数
EFI_DRIVER_BINDING_PROTOCOL包含三个核心函数:Support、Start和Stop。
typedefstruct_EFI_DRIVER_BINDING_PROTOCOL{EFI_DRIVER_BINDING_SUPPORTED Supported;//判断该Driver是否支持某个 Controller。EFI_DRIVER_BINDING_START Start;//启动 Controller,打开 Protocol,安装 Protocol。EFI_DRIVER_BINDING_STOP Stop;//停止 Controller,关闭 Protocol,卸载 Protocol。UINT32 Version;//驱动优先级,版本高的优先尝试EFI_HANDLE ImageHandle;//当前Driver镜像的HandleEFI_HANDLE DriverBindingHandle;//安装Driver Binding Protocol的Handle}EFI_DRIVER_BINDING_PROTOCOL;3、运作流程
DXE Dispatcher 加载 Driver Image | v DriverEntryPoint 执行 //UEFI DXE Driver被加载后最先执行的入口函数 | v 安装 EFI_DRIVER_BINDING_PROTOCOL | v 系统调用 ConnectController() //触发 Driver Binding 流程的 Boot Services 接口 | v 遍历可用 Driver Binding Protocol | v 调用 Supported() | v 如果支持,调用 Start() | v Driver 打开底层 Protocol,初始化设备 | v Driver 安装新的 Protocol | v 需要断开时调用 DisconnectController() | v 触发 Stop()4、ControllerHandle、DriverBindingHandle 和 ChildHandle
| Handle | 说明 |
|---|---|
| DriverBindingHandle | 驱动自身的Handle |
| ControllerHandle | 被驱动管理的设备对象 |
| ChildHandle | Bus Driver枚举出的子设备对象 |
例如:
USB Bus Driver | |DriverBindingHandle v USB Controller | |ControllerHandle v USB Device 1: U盘 --> ChildHandle USB Device 2: 键盘 --> ChildHandle USB Device 3: 鼠标 --> ChildHandle5、Driver 类型
| 类型 | 作用 | 是否创建ChildHandle |
|---|---|---|
| Device Driver | 直接驱动某个设备 | 通常不会创建 |
| Bus Driver | 枚举总线上的子设备 | 通常会创建 |
| Hybrid Driver | 同时具备设备驱动和总线驱动能力 | 执行总线时会创建 |
6、OpenProtocol
在Driver Binding模型中,OpenProtocol()起到获取接口、记录驱动和设备之间的管理关系的作用。
三个重要的Attributes参数:
EFI_OPEN_PROTOCOL_BY_DRIVER //获取接口,不表示接管设备。 EFI_OPEN_PROTOCOL_GET_PROTOCOL //表示这个 Driver 正式接管这个 Controller。 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER //Bus Driver 创建 Child Handle 时使用。- ChildHandle创建流程:
Bus Driver 在 Start() 里枚举到子设备 创建 ChildHandle 安装 Device Path Protocol 安装描述这个子设备的 Protocol 用 OpenProtocol BY_CHILD_CONTROLLER 建立父子关系7、常见案例——USB U盘出现fs0:
(1)DXE Dispatcher 加载 USB Bus Driver (2)USB Bus Driver 安装 Driver Binding Protocol (3)ConnectController() 尝试连接 USB Controller (4)调用 USB Bus Driver 的 Supported() (5)Supported() 判断 Controller 支持 USB (6)调用 Start() (7)Start() 初始化 USB Controller (8)枚举 USB 设备 (9)为 U 盘创建 ChildHandle (10)安装相关 USB Protocol (11)后续 Mass Storage Driver 连接这个 ChildHandle (12)安装 Block IO Protocol (13)文件系统 Driver 发现 Block IO Protocol (14)安装 Simple File System Protocol (15)Shell 看到 fs0:8、常见源码位置
MdePkg/Include/Protocol/DriverBinding.h MdeModulePkg/Bus/Pci/PciBusDxe/ MdeModulePkg/Bus/Usb/UsbBusDxe/ MdeModulePkg/Universal/Disk/9、Q&A
(1)Driver Binding 是什么?
- UEFI Driver Binding 是 UEFI 驱动管理设备的标准模型,用来判断、启动和停止某个设备 Controller。
(2)Supported / Start / Stop 分别做什么?
Supported()判断驱动是否支持这个设备。Start()真正启动设备,打开底层 Protocol,初始化设备,并安装新的 Protocol。Stop()停止设备,关闭 Protocol,卸载 Protocol,释放资源。
(3)Device Driver 和 Bus Driver 有什么区别?
Device Driver直接驱动某个具体设备。Bus Driver管理一条总线,并枚举出子设备,也就是ChildHandle。
(4)OpenProtocol BY_DRIVER是什么意思?
- 表示这个 Driver 要以驱动身份打开某个 Protocol,并正式接管这个 Controller。
二、Event 与 Timer 机制
1、简要描述
Event 是 UEFI 中的事件通知机制,Timer 是一种可以按时间触发的 Event。
它们用于异步通知、定时任务、等待用户输入、阶段退出回调等场景。
2、Event
(1)Event 常见用途
在DXE阶段常用:
等待某个事件发生,如键盘输入或某个 Protocol 被安装。
定时触发回调。
在某个阶段发生时收到通知,如
ExitBootServices时通知驱动清理资源。VirtualAddressChange 时通知 Runtime Driver 转换地址。
Protocol 安装后触发 notify。
(2)Event接口
| 类型 | 详细 | 作用 |
|---|---|---|
| 创建 Event | CreateEvent()&CreateEventEx() | 创建一个 Event,等待事件、通知事件或 Timer 事件 |
| 触发 Event | SignalEvent() | 主动触发一个 Event,让它变成“已发生”状态 |
| 等待 Event | WaitForEvent() | 等待一个或多个 Event 发生,常用于等待键盘输入等 |
| 检查 Event | CheckEvent() | 检查某个 Event 是否已经发生,不阻塞等待 |
| 关闭 Event | CloseEvent() | 关闭 Event,释放相关资源 |
| 设置 Timer | SetTimer() | 给 Event 设置定时触发方式 |
实例:
(1)自己创建、触发、等待、释放 Event。
gBS->CreateEvent(Type,NotifyTpl,NotifyFunction,NotifyContext,&Event);gBS->SignalEvent(Event);gBS->WaitForEvent(1,&Event,&Index);gBS->CloseEvent(Event);(2)等待键盘输入
gBS->WaitForEvent(1,&gST->ConIn->WaitForKey,&Index);(3)Event类型
EVT_NOTIFY_SIGNAL //Event 被 Signal 时,执行回调函数。 EVT_NOTIFY_WAIT //当有人等待或检查这个 Event 时,执行回调函数。 EVT_TIMER //表示这个 Event 可以被 Timer 触发,和 SetTimer() 配合使用。(4)NotifyFunction
Event 发生后,系统调用 NotifyFunction,Context 用来告诉回调函数该处理什么对象。
VOID EFIAPINotifyFunction(IN EFI_EVENT Event,IN VOID*Context//创建 Event 时传进去的上下文数据。);3、Timer
Timer 不是单独一套机制,而是挂在 Event 上的定时触发能力。
(1)TPL
TPL 是 Task Priority Level,表示事件回调执行时的任务优先级。
常见 TPL:
| TPL | 说明 |
|---|---|
TPL_APPLICATION | 普通应用级别 |
TPL_CALLBACK | 普通回调常用 |
TPL_NOTIFY | 较高优先级通知回调 |
TPL_HIGH_LEVEL | 最高级别,通常不应随意使用 |
(2)Timer类型
| 类型 | 作用 |
|---|---|
TimerCancel | 取消定时器 |
TimerPeriodic | 周期性触发 |
TimerRelative | 延迟一次触发 |
- 注意:UEFI Timer 的时间单位是:100ns,
gBS->SetTimer(Event, TimerPeriodic, 10000000);的意思是大约每 1 秒触发一次。
(3)通用流程
CreateEvent 创建带 EVT_TIMER 的 Event | SetTimer 设置定时方式 | 时间到 | Event 被触发 | 执行 NotifyFunction | CloseEvent 关闭 Event4、源码位置
MdeModulePkg/Core/Dxe/Event/ MdePkg/Include/Uefi/UefiSpec.h MdePkg/Include/Guid/EventGroup.h三、Variable Services 与 NVRAM 存储
1、简要描述
UEFI Variable 是BIOS/UEFI 提供的一套键值存储机制。
数据通常保存在NVRAM中,掉电后依然存在。
Variable Services 属于 Runtime Services,所以部分变量在 OS runtime 阶段仍可访问。
2、Variable 的组成
| 要素 | 含义 |
|---|---|
| VariableName | 变量名,Unicode 字符串 |
| VendorGUID | 变量命名空间,避免不同厂商变量重名 |
| Data | 变量数据 |
| Attributes | 变量属性,决定变量能在哪些阶段访问、是否掉电保存等 |
3、Variable Services常用接口
主要都在Runtime Services中:
gRT->GetVariable()//读取变量gRT->SetVariable()//创建、修改或删除变量gRT->GetNextVariableName()//枚举变量gRT->QueryVariableInfo()//查询变量存储空间信息4、NVRAM
- 非易失性存储区域,Non-Volatile RAM,保存于SPI Flash的某个变量存储区域中。
- 典型实现中,Variable Runtime DXE 负责变量服务逻辑,Fault Tolerant Write 负责可靠写 Flash,Variable Store 负责组织变量数据结构。厂商 BIOS 可能会在此基础上加入权限、锁定、安全启动、恢复机制等。
5、Variable Attributes
| 属性 | 说明 |
|---|---|
EFI_VARIABLE_NON_VOLATILE | 掉电保留 |
EFI_VARIABLE_BOOTSERVICE_ACCESS | Boot Services 阶段可访问 |
EFI_VARIABLE_RUNTIME_ACCESS | OS Runtime 阶段可访问 |
EFI_VARIABLE_HARDWARE_ERROR_RECORD | 硬件错误记录 |
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | 基于时间的认证写入 |
EFI_VARIABLE_APPEND_WRITE | 追加写入 |
6、GetVariable和SetVariable
GetVariable读取流程:
DataSize = 0,Data = NULL | v 第一次调用 GetVariable() | v 返回 EFI_BUFFER_TOO_SMALL | v UEFI 把真正需要的大小写回 DataSize | v AllocatePool(DataSize) 分配缓冲区 | v 第二次调用 GetVariable() | v 变量数据被读取到 Data 中 | v 使用变量数据 | v FreePool(Data) 释放缓冲区SetVariable会修改 NVRAM,以下变量不能随意修改:
BootOrder Boot#### Setup PK / KEK / db / dbx Secure Boot 相关变量7、GetNextVariableName
GetNextVariableName用于枚举所有变量。
gRT->GetNextVariableName()|VariableName+VendorGuid//返回值|gRT->GetVariable()//读取变量的数据和属性8、源码位置
MdeModulePkg/Universal/Variable/RuntimeDxe/ MdeModulePkg/Universal/FaultTolerantWriteDxe/ MdePkg/Include/Uefi/UefiSpec.h MdePkg/Include/Guid/GlobalVariable.h四、 Boot Option 与启动项管理
1、简要描述
Boot Option 是 UEFI 用 Variable 保存和管理启动项的机制。
2、关键变量
| 变量 | 作用 |
|---|---|
BootOrder | 启动项顺序,UINT16数组, |
Boot#### | 具体启动项,####是四位十六进制编号 |
BootCurrent | 本次启动所使用的启动项 |
BootNext | 下一次启动优先生效的启动项,只生效一次 |
3、Boot####
Boot####的数据通常是EFI_LOAD_OPTION格式:
UINT32 Attributes //启动项属性,是否启用 UINT16 FilePathListLength //Device Path 长度 CHAR16 Description[] //启动项显示名称 FilePathList[] //启动设备/启动文件路径 UINT8 OptionalData[] //可选参数4、BDS 阶段如何使用 Boot Option
BDS 阶段下的流程:
读取 BootNext | 如果 BootNext 存在,优先尝试它,并清除 BootNext | 否则读取 BootOrder | 按照顺序读取 Boot#### | 检查启动项是否启用 | 解析 Device Path //EFI_DEVICE_PATH_PROTOCOL | 加载对应 EFI Image | StartImage 启动 | 如果失败,尝试下一个启动项5、启动项解析步骤
读取 BootOrder 遍历每个 UINT16 编号 拼接 Boot0001 变量名 读取 Boot#### 解析 Attributes 解析 FilePathListLength 解析 Description 解析 Device Path 输出启动项信息6、推荐源码位置
MdePkg/Include/Uefi/UefiSpec.h MdePkg/Include/Guid/GlobalVariable.h MdePkg/Include/Protocol/DevicePath.h MdePkg/Library/UefiDevicePathLib/ MdeModulePkg/Universal/BdsDxe/ MdeModulePkg/Library/UefiBootManagerLib/7、Q&A
(1)Boot Option 是什么?
Boot Option 是 UEFI 用来描述和管理启动项的机制,通常保存在 NVRAM Variable 中。
(2)BootOrder 是什么?
BootOrder 是启动顺序列表,里面保存的是一组 Boot#### 编号。
(3)Boot#### 是什么?
Boot#### 是具体启动项,比如 Windows Boot Manager、USB 启动项、PXE 启动项等。
(4)BootNext 是什么?
BootNext 是下一次启动临时使用的启动项,只生效一次。
(5)Boot#### 里面保存什么?
保存 EFI_LOAD_OPTION,包括启动项属性、显示名称、Device Path 和可选参数。
(6)Boot Option 和 Variable Services 有什么关系?
Boot Option 本质上就是一组 UEFI Variables。
五、Boot Services 与 Runtime Services 区别与应用
1、简要内容
- Boot Services:启动阶段用的服务,ExitBootServices 后失效。
- Runtime Services:运行时服务,进入 OS 后仍可用。
2、Boot Services
Boot Services 是 UEFI 在启动阶段提供的大量服务。
作用:
内存分配 Protocol 管理 Handle 管理 Event / Timer 加载和启动 Image Device Path 操作 驱动连接 等待输入常见接口:
gBS->AllocatePool() gBS->FreePool() gBS->LocateProtocol() gBS->OpenProtocol() gBS->InstallProtocolInterface() gBS->CreateEvent() gBS->SetTimer() gBS->LoadImage() gBS->StartImage() gBS->ConnectController() gBS->ExitBootServices() //Boot Services 只在 ExitBootServices 之前有效,OS Loader 调用 ExitBootServices() 后,系统控制权交给 OS,Boot Services 就不能再使用了。3、Runtime Services
Runtime Services 是 UEFI 在 OS 阶段仍然可能保留的服务。
作用:
时间服务 变量服务 系统复位 Capsule Update 虚拟地址转换常见接口:
gRT->GetTime() gRT->SetTime() gRT->GetVariable() gRT->SetVariable() gRT->GetNextVariableName() gRT->ResetSystem() gRT->UpdateCapsule() gRT->SetVirtualAddressMap() gRT->ConvertPointer() ## Runtime Services 在 ExitBootServices 后仍可能可用。4、生命周期区别
| 对比项 | Boot Services | Runtime Services |
|---|---|---|
| 有效期 | ExitBootServices()前 | ExitBootServices()后仍可用 |
| 主要使用者 | UEFI Driver、UEFI App、Bootloader | OS runtime |
| 典型功能 | Protocol、Event、Memory、Image | Variable、Time、Reset |
| 内存要求 | Boot Service Code/Data | Runtime Code/Data 必须保留 |
使用场景:
| 场景 | 服务 |
|---|---|
| 枚举 Protocol | Boot Services |
| 安装 Protocol | Boot Services |
| 分配 Boot 阶段内存 | Boot Services |
| 加载 EFI 文件 | Boot Services |
| 启动 EFI Image | Boot Services |
| Event / Timer | Boot Services |
| 读写 UEFI Variable | Runtime Services |
| 获取系统时间 | Runtime Services |
| 重启/关机 | Runtime Services |
| Capsule Update | Runtime Services |
5、源码位置
MdePkg/Include/Uefi/UefiSpec.h MdeModulePkg/Core/Dxe/ MdeModulePkg/Core/RuntimeDxe/ MdeModulePkg/Universal/Variable/RuntimeDxe/6、Q&A
(1)Boot Services和Runtime Services的区别是什么?
ExitBootServices()后 Boot Services 失效,Runtime Services 仍可用。
(2)Protocol 相关接口属于哪类?
属于 Boot Services。
(3)Variable Services 属于哪类?
属于 Runtime Services.
(4)LoadImage / StartImage 属于哪类?
属于 Boot Services。