UEFI设备驱动与固件开发工程 - 第4节
4.1 UEFI 驱动模型概述
UEFI 驱动模型是 UEFI 规范定义的一套标准驱动架构,旨在解决传统 BIOS 驱动中设备枚举混乱、资源冲突、无法动态加载/卸载等问题。该模型以驱动绑定协议(Driver Binding Protocol)为核心,将驱动的生命周期划分为驱动入口点(Entry Point)、驱动绑定和驱动卸载三个阶段。
核心思想:UEFI 驱动不直接操作硬件,而是通过 I/O 抽象 和 句柄数据库 与系统固件交互。驱动本身是一个可加载的 PE/COFF 镜像,由 UEFI 启动管理器或 DXE 调度器加载到内存中执行。
4.1.1 驱动模型的三层架构
UEFI 驱动模型从上到下分为三层:
| 层次 | 职责 | 典型协议/组件 |
|---|---|---|
| 驱动层 | 实现设备控制逻辑,提供标准化的 I/O 接口 | Driver Binding Protocol, Component Name Protocol |
| 设备层 | 管理设备句柄、设备路径和 I/O 抽象 | Device Path Protocol, I/O Abstraction Protocols |
| 总线/桥接层 | 负责设备枚举、资源分配和总线拓扑管理 | PCI Bus Driver, USB Bus Driver, ACPI 枚举 |
理解要点:UEFI 驱动模型中的“驱动”是一个 协议生产者。驱动加载后,会在句柄数据库上安装一个或多个协议实例,供上层应用或其他驱动使用。
4.1.2 驱动模型的工作流程
一个典型的 UEFI 驱动从加载到卸载经历以下步骤:
- 加载镜像:UEFI 启动管理器或 DXE 调度器将驱动 PE/COFF 镜像加载到内存。
- 调用入口点:系统调用驱动的
_ModuleEntryPoint函数(即驱动入口点)。 - 安装驱动绑定协议:入口点函数安装
EFI_DRIVER_BINDING_PROTOCOL实例到驱动句柄上。 - 设备枚举与匹配:系统遍历所有未匹配的设备句柄,调用驱动的
Supported()函数检查是否支持该设备。 - 启动驱动:如果
Supported()返回成功,系统调用Start()函数,驱动开始控制设备并安装 I/O 协议。 - 停止与卸载:系统调用
Stop()停止设备控制,再调用卸载函数释放资源。
4.2 驱动绑定协议(Driver Binding Protocol)
EFI_DRIVER_BINDING_PROTOCOL 是 UEFI 驱动模型中最核心的协议,它定义了驱动与设备之间的绑定关系。该协议包含三个函数指针和一个版本号字段:
| 字段/函数 | 类型 | 说明 |
|---|---|---|
Supported() |
函数指针 | 检查驱动是否支持指定的设备句柄。不修改硬件状态。 |
Start() |
函数指针 | 驱动开始控制设备,安装 I/O 协议,分配资源。 |
Stop() |
函数指针 | 驱动停止控制设备,卸载 I/O 协议,释放资源。 |
Version |
UINT32 | 驱动版本号,用于驱动优先级比较。 |
ImageHandle |
EFI_HANDLE | 驱动镜像的句柄,由入口点传入。 |
DriverBindingHandle |
EFI_HANDLE | 驱动绑定协议安装所在的句柄。 |
4.2.1 Supported() 函数
Supported() 是驱动绑定协议的“门卫”函数。系统在枚举设备时,会为每个未绑定的设备句柄调用所有已安装驱动的 Supported() 函数,直到找到匹配的驱动。
EFI_STATUS
EFIAPI
AbcDriverSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
// 1. 检查设备是否已经绑定了其他驱动
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiAbcIoProtocolGuid,
NULL,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (!EFI_ERROR (Status)) {
return EFI_ALREADY_STARTED;
}
// 2. 检查设备是否支持本驱动所需的底层协议
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
NULL,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
// 3. 关闭协议(Supported 不修改状态)
gBS->CloseProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return EFI_SUCCESS;
}
重要规则:
Supported() 函数 不能修改硬件状态,也不能分配永久性资源。它只做“探测”工作,返回 EFI_SUCCESS 表示支持该设备,返回其他错误码表示不支持。
4.2.2 Start() 函数
Start() 是驱动真正开始工作的入口。当系统确定某个驱动支持某设备后,会调用该驱动的 Start() 函数来建立绑定关系。
EFI_STATUS
EFIAPI
AbcDriverStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
ABC_DEVICE_PRIVATE *Device;
// 1. 分配私有数据结构
Device = AllocateZeroPool (sizeof (ABC_DEVICE_PRIVATE));
if (Device == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// 2. 打开底层协议(PCI I/O 等)
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
(VOID **)&Device->PciIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
goto FREE_DEVICE;
}
// 3. 初始化硬件(配置 BAR、中断等)
Status = AbcInitHardware (Device);
if (EFI_ERROR (Status)) {
goto CLOSE_PCI_IO;
}
// 4. 安装 I/O 抽象协议
Status = gBS->InstallMultipleProtocolInterfaces (
&ControllerHandle,
&gEfiAbcIoProtocolGuid,
&Device->AbcIo,
NULL
);
if (EFI_ERROR (Status)) {
goto CLOSE_PCI_IO;
}
return EFI_SUCCESS;
CLOSE_PCI_IO:
gBS->CloseProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
FREE_DEVICE:
FreePool (Device);
return Status;
}
4.2.3 Stop() 函数
Stop() 是 Start() 的逆操作,负责释放所有资源并卸载协议。
EFI_STATUS
EFIAPI
AbcDriverStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
)
{
EFI_STATUS Status;
ABC_DEVICE_PRIVATE *Device;
// 1. 获取私有数据结构
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiAbcIoProtocolGuid,
(VOID **)&Device,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
// 2. 卸载 I/O 协议
Status = gBS->UninstallMultipleProtocolInterfaces (
ControllerHandle,
&gEfiAbcIoProtocolGuid,
&Device->AbcIo,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
// 3. 关闭底层协议
gBS->CloseProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
// 4. 释放私有数据结构
FreePool (Device);
return EFI_SUCCESS;
}
4.3 驱动入口点与卸载函数
4.3.1 驱动入口点(Entry Point)
每个 UEFI 驱动都有一个入口点函数,通常命名为 _ModuleEntryPoint 或自定义名称。入口点的主要职责是:
- 安装驱动绑定协议:将
EFI_DRIVER_BINDING_PROTOCOL实例安装到驱动句柄上。 - 注册卸载函数:通过
EFI_BOOT_SERVICES.RegisterProtocolNotify()或直接安装EFI_DRIVER_BINDING_PROTOCOL时附带卸载逻辑。 - 可选初始化:如全局变量初始化、调试日志配置等。
EFI_STATUS
EFIAPI
AbcDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
// 1. 保存系统表指针(全局变量)
gST = SystemTable;
gBS = SystemTable->BootServices;
// 2. 初始化驱动绑定协议实例
gAbcDriverBinding.Supported = AbcDriverSupported;
gAbcDriverBinding.Start = AbcDriverStart;
gAbcDriverBinding.Stop = AbcDriverStop;
gAbcDriverBinding.Version = ABC_DRIVER_VERSION;
gAbcDriverBinding.ImageHandle = ImageHandle;
gAbcDriverBinding.DriverBindingHandle = ImageHandle;
// 3. 安装驱动绑定协议到驱动句柄
Status = gBS->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gEfiDriverBindingProtocolGuid,
&gAbcDriverBinding,
NULL
);
return Status;
}
4.3.2 驱动卸载函数(Unload Function)
UEFI 驱动支持在运行时卸载。卸载函数通常通过 EFI_LOADED_IMAGE_PROTOCOL 的 Unload 字段注册。当系统决定卸载驱动时,会调用该函数。
EFI_STATUS
EFIAPI
AbcDriverUnload (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
// 1. 获取驱动绑定协议
Status = gBS->OpenProtocol (
ImageHandle,
&gEfiDriverBindingProtocolGuid,
(VOID **)&DriverBinding,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
// 2. 停止所有由本驱动控制的设备
Status = gBS->DisconnectController (
DriverBinding->DriverBindingHandle,
NULL,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
// 3. 卸载驱动绑定协议
Status = gBS->UninstallProtocolInterface (
ImageHandle,
&gEfiDriverBindingProtocolGuid,
DriverBinding
);
return Status;
}
最佳实践:在入口点中,建议通过
gBS->HandleProtocol() 获取 EFI_LOADED_IMAGE_PROTOCOL,然后设置其 Unload 字段指向卸载函数。这样系统在卸载驱动时会自动调用该函数。
4.4 驱动模型的核心数据结构
UEFI 驱动模型围绕句柄数据库和协议接口展开。以下表格总结了驱动开发中常用的核心数据结构:
| 数据结构 | 用途 | 所在头文件 |
|---|---|---|
EFI_DRIVER_BINDING_PROTOCOL |
驱动与设备的绑定接口 | <Protocol/DriverBinding.h> |
EFI_LOADED_IMAGE_PROTOCOL |
描述已加载的驱动镜像信息 | <Protocol/LoadedImage.h> |
EFI_COMPONENT_NAME_PROTOCOL |
提供驱动和设备的可读名称 | <Protocol/ComponentName.h> |
EFI_DEVICE_PATH_PROTOCOL |
描述设备的物理或逻辑路径 | <Protocol/DevicePath.h> |
EFI_PCI_IO_PROTOCOL |
PCI 设备的 I/O 访问接口 | <Protocol/PciIo.h> |
4.5 驱动模型的设计原则
关键设计原则:
- 协议驱动:驱动通过协议接口与设备通信,不直接访问硬件寄存器。
- 句柄中心:所有设备、驱动、协议都通过句柄(EFI_HANDLE)标识和管理。
- 延迟绑定:驱动在入口点不绑定设备,而是等待系统枚举后通过
Supported()匹配。 - 可卸载性:所有驱动必须支持
Stop()和卸载函数,确保系统可以动态移除驱动。 - 版本兼容:通过
Version字段实现驱动优先级管理,高版本驱动优先绑定设备。