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 驱动从加载到卸载经历以下步骤:

  1. 加载镜像:UEFI 启动管理器或 DXE 调度器将驱动 PE/COFF 镜像加载到内存。
  2. 调用入口点:系统调用驱动的 _ModuleEntryPoint 函数(即驱动入口点)。
  3. 安装驱动绑定协议:入口点函数安装 EFI_DRIVER_BINDING_PROTOCOL 实例到驱动句柄上。
  4. 设备枚举与匹配:系统遍历所有未匹配的设备句柄,调用驱动的 Supported() 函数检查是否支持该设备。
  5. 启动驱动:如果 Supported() 返回成功,系统调用 Start() 函数,驱动开始控制设备并安装 I/O 协议。
  6. 停止与卸载:系统调用 Stop() 停止设备控制,再调用卸载函数释放资源。
UEFI 驱动模型工作流程 加载驱动镜像 调用入口点 安装驱动绑定协议 设备枚举与匹配 启动驱动 (Start) 入口点通常只做初始化 不直接操作硬件 Supported() 检查设备 是否被当前驱动支持

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>
UEFI 驱动核心数据结构关系 驱动句柄 Driver Binding Protocol Loaded Image Protocol Component Name Protocol Device Path Protocol PCI I/O Protocol

4.5 驱动模型的设计原则

关键设计原则:
  • 协议驱动:驱动通过协议接口与设备通信,不直接访问硬件寄存器。
  • 句柄中心:所有设备、驱动、协议都通过句柄(EFI_HANDLE)标识和管理。
  • 延迟绑定:驱动在入口点不绑定设备,而是等待系统枚举后通过 Supported() 匹配。
  • 可卸载性:所有驱动必须支持 Stop() 和卸载函数,确保系统可以动态移除驱动。
  • 版本兼容:通过 Version 字段实现驱动优先级管理,高版本驱动优先绑定设备。