1. UEFI基础概念:UEFI与BIOS的区别、UEFI启动流程概览、UEFI固件架构

大家好,我是老周。做固件这行十几年了,今天咱们聊聊UEFI。说实话,刚入行那会儿我也觉得BIOS挺好的,干嘛要换?直到第一次被UEFI的启动速度惊到,才明白时代真的变了。

1.1 UEFI与BIOS的区别

先说说最核心的问题:UEFI和BIOS到底差在哪?

BIOS(Basic Input/Output System)是上世纪80年代的东西了。它跑在16位实模式下,只能访问1MB内存。你想想看,现在随便一个UEFI固件都几十MB,BIOS那点空间根本不够用。

UEFI(Unified Extensible Firmware Interface)是Intel在2005年左右推出来的。我参与的第一个UEFI项目是2010年,当时客户要求支持2TB以上的硬盘。BIOS的MBR分区表最大只支持2TB,而UEFI的GPT分区表可以支持到9.4ZB——这差距不是一星半点。

对比项 传统BIOS UEFI
运行模式 16位实模式 32/64位保护模式
寻址空间 1MB 理论无限制
磁盘分区 MBR(最大2TB) GPT(最大9.4ZB)
启动速度 慢(逐扇区扫描) 快(直接加载EFI文件)
驱动模型 无标准驱动接口 EFI Driver Model
网络支持 基本没有 原生UEFI网络栈
安全性 无 Secure Boot

关键点:UEFI本质上是一个微型操作系统。它有自己的驱动模型、协议栈、甚至图形界面。BIOS只是个固件,UEFI是个平台。

1.2 UEFI启动流程概览

UEFI的启动流程,说白了就是七个阶段。我习惯用SEC、PEI、DXE、BDS、TSL、RT、AL这七个缩写来记。嗯,这里要注意,不是所有平台都会走完所有阶段,但主流平台基本都这样。

UEFI 启动流程七阶段 SEC 安全验证 PEI 内存初始化 DXE 驱动调度 BDS 启动设备选择 TSL 操作系统加载 RT 运行时服务 AL 操作系统运行 各阶段说明: • SEC(Security Phase):CPU初始化和安全验证,这是第一个执行的代码 • PEI(Pre-EFI Initialization):初始化内存控制器,建立内存映射 • DXE(Driver Execution Environment):加载并执行UEFI驱动 • BDS(Boot Device Selection):选择启动设备,显示启动菜单 • TSL(Transient System Load):加载操作系统引导程序 • RT(Runtime):UEFI运行时服务,操作系统启动后仍可用 • AL(After Life):系统关机或休眠前的清理阶段

我的经验:排查启动故障时,先看卡在哪个阶段。如果连PEI都没过,多半是内存问题。如果卡在BDS,检查启动设备配置。我曾经遇到一个案例,客户说机器开机黑屏,结果发现是PEI阶段内存时序配置错了。

1.3 UEFI固件架构

UEFI固件架构,我习惯把它分成三层来看:

  • 底层:硬件抽象层(HAL),直接跟CPU、芯片组打交道
  • 中间层:EFI协议栈,包括各种驱动和服务
  • 上层:UEFI应用,比如Shell、Boot Manager、Setup界面

说白了,UEFI固件就是一个微内核操作系统。它有自己的内存管理、任务调度、协议通信机制。我刚开始学UEFI时,最不习惯的就是它的"Protocol"概念——其实就是面向对象里的接口,只不过用C语言实现了。

1.3.1 核心组件

组件 功能 常见问题
SEC Core 安全验证、CPU初始化 Reset Vector配置错误
PEI Core 内存初始化、PPI调度 内存时序参数错误
DXE Foundation 驱动加载、协议注册 驱动依赖顺序问题
Boot Manager 启动项管理、设备选择 BootOrder变量损坏
Runtime Services 操作系统运行时接口 GetVariable返回错误

避坑指南:我曾经在DXE阶段踩过一个坑——驱动加载顺序错了。两个驱动都依赖同一个Protocol,但A驱动先注册了Protocol,B驱动才加载。结果A驱动在注册Protocol时还没初始化完成,B驱动拿到的是个空指针。排查了两天才找到原因。所以,驱动依赖关系一定要理清楚。

1.3.2 UEFI变量存储

UEFI变量是固件和操作系统之间的桥梁。我经常用dmpstore命令来查看变量。举个例子:

Shell> dmpstore -all
Variable: BootOrder
  GUID: 8BE4DF61-93CA-11D2-AA0D-00E098032B8C
  Attributes: BS|RT|NV
  Data: 0000 0001 0002

Variable: Boot0000
  GUID: 8BE4DF61-93CA-11D2-AA0D-00E098032B8C
  Attributes: BS|RT|NV
  Data: "Windows Boot Manager"

看到Attributes里的NV了吗?这表示变量是非易失的,断电后还在。如果BootOrder变量损坏了,启动就会出问题。我遇到过一台机器,每次重启都进Shell,查了半天发现是BootOrder被写成了乱码。

1.3.3 UEFI驱动模型

UEFI驱动模型跟Windows的WDM有点像。驱动通过EFI_DRIVER_BINDING_PROTOCOL来绑定设备。我简单写个驱动入口的例子:

EFI_STATUS
EFIAPI
MyDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  // 注册驱动绑定协议
  Status = gBS->InstallMultipleProtocolInterfaces (
             &ImageHandle,
             &gEfiDriverBindingProtocolGuid,
             &gMyDriverBinding,
             NULL
             );
  return Status;
}

这段代码看着简单,但实际项目中坑不少。比如gBS是Boot Services表,只能在DXE和BDS阶段用。到了RT阶段,Boot Services就关闭了,再调用就会崩溃。

核心要点:UEFI固件架构的精髓在于"协议"和"句柄"这两个概念。协议就是接口,句柄就是对象。理解了这两个,UEFI开发就入门了。