2. UEFI 规范核心概念:系统表、启动服务、运行时服务、协议与句柄

UEFI 规范定义了一套标准化的固件-操作系统接口,其核心由几个关键抽象构成:UEFI 系统表、启动服务、运行时服务、协议与句柄。理解这些概念是进行 UEFI 固件开发的基础。

2.1 UEFI 系统表 (System Table)

UEFI 系统表(EFI_SYSTEM_TABLE)是整个 UEFI 环境的入口点。它是一个全局结构体,包含了指向所有核心服务和配置信息的指针。在 UEFI 应用程序或驱动程序的入口点(ImageHandle 和 SystemTable 参数)中,系统表被传递给开发者。

系统表的主要组成部分包括:

  • 固件供应商信息:固件版本号和供应商名称。
  • 控制台句柄:标准输入、标准输出和标准错误输出设备的句柄。
  • 启动服务表:指向 EFI_BOOT_SERVICES 结构体的指针。
  • 运行时服务表:指向 EFI_RUNTIME_SERVICES 结构体的指针。
  • 配置表:指向系统配置表(如 ACPI、SMBIOS)的指针数组。
关键点:系统表是 UEFI 应用的“根对象”,所有其他服务和资源都通过它来访问。

2.2 启动服务 (Boot Services)

启动服务(EFI_BOOT_SERVICES)提供了一组在操作系统启动前可用的函数接口。这些服务在操作系统加载器(OS Loader)调用 ExitBootServices() 后不再可用。

启动服务的主要功能类别:

服务类别 说明 关键函数示例
事件与定时器 创建、等待和关闭事件,设置定时器。 CreateEvent, WaitForEvent, SetTimer
内存管理 分配和释放内存页或内存池。 AllocatePages, FreePages, AllocatePool
协议管理 安装、卸载、查找和打开协议接口。 InstallProtocolInterface, LocateProtocol, OpenProtocol
句柄管理 安装、卸载和定位句柄。 InstallMultipleProtocolInterfaces, LocateHandleBuffer
镜像管理 加载、启动和卸载 UEFI 镜像。 LoadImage, StartImage, UnloadImage
I/O 操作 对设备进行内存映射 I/O 或端口 I/O 操作。 IoRead, IoWrite, PciRead, PciWrite
开发提示:在编写 UEFI 驱动时,大部分初始化工作(如注册协议、分配内存)都在启动服务阶段完成。

2.3 运行时服务 (Runtime Services)

运行时服务(EFI_RUNTIME_SERVICES)在操作系统启动后仍然可用。它们提供与硬件平台状态相关的关键功能,例如时间管理、变量存储和重置系统。

运行时服务的主要功能:

  • 变量服务:GetVariable, SetVariable, GetNextVariableName。用于在 NVRAM 中存储和检索非易失性数据。
  • 时间服务:GetTime, SetTime, GetWakeupTime, SetWakeupTime。用于访问系统实时时钟。
  • 重置服务:ResetSystem。用于冷重启、热重启或关机。
  • 虚拟地址转换:SetVirtualAddressMap。用于在操作系统启用分页后,将运行时服务从物理地址映射到虚拟地址。
注意事项:运行时服务代码必须使用 EFI_RUNTIME_SERVICES 数据段,并且不能依赖启动服务中的内存分配或协议查找功能。

2.4 协议 (Protocol) 与句柄 (Handle) 机制

UEFI 的驱动模型基于 句柄 和 协议 的抽象。句柄代表一个实体(如设备、驱动或镜像),而协议是附加到句柄上的接口结构体,定义了该实体提供的功能。

2.4.1 句柄 (Handle)

句柄是一个不透明的指针(EFI_HANDLE),它唯一标识一个 UEFI 对象。句柄本身不包含数据,它只是一个标识符,用于关联一组协议。

2.4.2 协议 (Protocol)

协议是一个 GUID 标识的接口结构体,包含一组函数指针和数据字段。例如,EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 定义了 OutputString() 函数用于在屏幕上打印文本。

协议与句柄的关系:

  • 一个句柄可以安装多个协议。
  • 一个协议可以被多个句柄安装。
  • 通过 LocateProtocol() 或 OpenProtocol() 可以获取指定句柄上的协议接口。
核心思想:UEFI 驱动通过“生产”和“消费”协议来工作。一个驱动安装协议到句柄上,另一个驱动通过打开该句柄上的协议来使用其功能。
UEFI 核心概念关系图 EFI_SYSTEM_TABLE Boot Services Runtime Services Handle (句柄) Protocol (协议) 系统表是访问所有服务和资源的入口

2.5 协议与句柄的交互流程

在 UEFI 驱动模型中,设备驱动通常遵循以下流程来使用协议和句柄:

  1. 绑定:驱动通过 Supported() 检查句柄是否支持其设备,然后通过 Start() 初始化设备。
  2. 安装协议:驱动在初始化时,通过 InstallMultipleProtocolInterfaces() 将一个或多个协议接口安装到设备句柄上。
  3. 打开协议:其他驱动或应用通过 OpenProtocol() 或 LocateProtocol() 获取已安装的协议接口。
  4. 使用协议:通过协议结构体中的函数指针调用设备功能。
  5. 关闭协议:使用完毕后,通过 CloseProtocol() 释放协议接口。
开发实践:使用 LocateHandleBuffer() 可以枚举系统中所有安装了特定协议的句柄,这是实现设备枚举的常用方法。
协议与句柄交互流程 1. 绑定句柄 2. 安装协议 3. 打开协议 4. 使用协议 关键数据结构 句柄 (EFI_HANDLE) 标识设备;协议 (Protocol) 提供接口 通过 GUID 唯一标识协议类型 // 打开协议示例 Status = gBS->OpenProtocol ( Handle, &gEfiSimpleTextOutProtocolGuid, (VOID**)&ConOut, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);

2.6 核心概念对比

概念 本质 生命周期 访问方式 典型用途
系统表 全局结构体指针 整个 UEFI 运行期 入口参数 获取所有服务入口
启动服务 函数指针表 启动阶段(ExitBootServices 前) gBS->Function() 内存分配、协议管理
运行时服务 函数指针表 整个系统运行期 gRT->Function() 变量存储、时间访问
句柄 不透明标识符 由安装者管理 LocateHandle 标识设备或驱动
协议 GUID 标识的结构体 由安装者管理 OpenProtocol 提供设备功能接口
常见误区:不要混淆 gBS 和 gRT 的使用场景。在 ExitBootServices() 之后调用 gBS 的任何函数都会导致系统崩溃。