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

大家好,我是你们的讲师。今天咱们来聊聊UEFI安全启动联调实战的第一章——基础概念。说实话,我见过太多工程师一上来就扎进代码里,结果连UEFI和BIOS到底差在哪都没搞明白。嗯,这章咱们就把地基打牢。

1.1 UEFI与BIOS:到底有什么区别?

先问大家一个问题:你电脑开机时,屏幕上闪过的那几行白字,是BIOS还是UEFI?

很多人觉得这俩是一回事。其实不然。我刚开始做固件时也这么认为,直到有一次调试一块新主板,死活进不去设置界面,才发现BIOS和UEFI完全是两套东西。

咱们直接上对比表,一目了然:

对比项 传统BIOS UEFI
运行模式 16位实模式 32位/64位保护模式
寻址能力 1MB限制 理论无限制
磁盘分区 MBR(主引导记录) GPT(GUID分区表)
启动速度 较慢(逐项检测) 较快(并行初始化)
用户界面 文本菜单,键盘操作 图形界面,支持鼠标
扩展性 差,依赖特定硬件 好,支持驱动模块
安全启动 不支持 原生支持

说白了,BIOS是上世纪80年代的老古董,UEFI是它的现代化替代品。我个人习惯把BIOS比作「老式电话交换机」,而UEFI就是「现代IP网络」——功能更强、更灵活、也更安全。

核心区别一句话: BIOS跑在16位实模式,只能访问1MB内存;UEFI跑在保护模式,能直接访问全部内存和CPU特性。

1.2 UEFI启动流程:从按下电源键到操作系统

你按下电源键的那一瞬间,UEFI到底干了什么?我拆解给你看。

整个流程大致分七个阶段,我习惯叫它「七步走」:

  1. SEC(安全验证)阶段:CPU复位,执行第一条指令。这里做最基础的硬件自检和安全验证。
  2. PEI(EFI前期初始化)阶段:初始化内存控制器,把内存时序调好。嗯,这一步出问题最常见——我遇到过内存颗粒虚焊导致PEI卡死的案例。
  3. DXE(驱动执行环境)阶段:加载各种驱动——磁盘、网卡、显卡等。这是最复杂的阶段,也是安全启动的关键战场。
  4. BDS(启动设备选择)阶段:扫描启动设备,按优先级选择从哪个盘启动。
  5. TSL(临时系统加载)阶段:加载操作系统引导器(比如Windows的bootmgfw.efi)。
  6. RT(运行时)阶段:把控制权交给操作系统,UEFI进入后台服务模式。
  7. AL(高级配置)阶段:操作系统接管后,UEFI提供运行时服务接口。

为了让你更直观地理解,我画了一张流程图:

UEFI启动流程七阶段 SEC PEI DXE BDS TSL RT AL 安全验证 内存初始化 驱动加载 选择启动设备 加载引导器 运行时服务 高级配置 关键节点说明 • SEC→PEI:CPU复位后第一条指令执行 • DXE→BDS:所有驱动加载完毕,准备启动 安全启动验证点 SEC阶段验证固件签名 → DXE阶段验证驱动签名 → TSL阶段验证引导器签名
避坑指南: 我曾经在DXE阶段踩过一个坑——某个第三方网卡驱动没有正确签名,导致安全启动直接卡死在BDS阶段。排查了整整两天才发现是驱动签名链断了。所以,驱动签名验证一定要做完整。

1.3 UEFI固件架构概述

UEFI固件不是一个大而全的「铁板一块」,它更像一个模块化的操作系统。你想想看,它要管理内存、加载驱动、提供界面、还要跟操作系统交互——这复杂度不亚于一个小型OS。

我习惯把UEFI固件架构分成三层:

  • 底层硬件抽象层:直接跟CPU、芯片组、内存控制器打交道。这部分代码通常由芯片厂商提供,我们一般不动它。
  • 中间服务层:包括启动服务(Boot Services)和运行时服务(Runtime Services)。启动服务在操作系统接管前可用,运行时服务则一直可用。
  • 上层应用层:包括UEFI Shell、设置界面、网络协议栈等。这部分是我们做定制化开发的主要战场。

来看一个典型的UEFI固件结构代码片段——这是我在项目中常用的一个模块初始化框架:


// UEFI驱动入口点示例
EFI_STATUS
EFIAPI
MyDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  // 1. 获取启动服务表
  // 这一步很关键,所有系统服务都通过这个表访问
  Status = gBS->LocateProtocol (
             &gEfiSimpleFileSystemProtocolGuid,
             NULL,
             (VOID**)&gFileSystem
             );
  if (EFI_ERROR (Status)) {
    // 我遇到过文件系统协议找不到的情况
    // 通常是DXE阶段驱动加载顺序问题
    return Status;
  }

  // 2. 注册协议接口
  Status = gBS->InstallMultipleProtocolInterfaces (
             &ImageHandle,
             &gEfiMyCustomProtocolGuid,
             &mMyCustomProtocol,
             NULL
             );
  ASSERT_EFI_ERROR (Status);

  // 3. 注册事件回调(比如定时器事件)
  Status = gBS->CreateEvent (
             EVT_TIMER | EVT_NOTIFY_SIGNAL,
             TPL_CALLBACK,
             MyTimerCallback,
             NULL,
             &mTimerEvent
             );
  if (!EFI_ERROR (Status)) {
    gBS->SetTimer (mTimerEvent, TimerPeriodic, 10000000); // 1秒周期
  }

  return EFI_SUCCESS;
}
注意: 上面代码中的 gBS 是全局的Boot Services指针。千万别在RT阶段(操作系统接管后)还调用Boot Services函数——那会导致系统崩溃。我见过有人把 gBS->AllocatePool 放在运行时服务里调用,结果蓝屏了。

说到UEFI固件架构,还有一个概念必须提——Protocol(协议)。这是UEFI里最核心的通信机制。说白了,就是「你提供接口,我调用接口」。每个驱动都注册一个或多个Protocol,其他驱动通过GUID找到它并调用。

举个例子:你想读写硬盘,不需要知道硬盘是SATA还是NVMe,只需要找到 gEfiBlockIoProtocolGuid 这个Protocol,调用它的 ReadBlocks() 函数就行。这就是UEFI的抽象能力。

一句话总结: UEFI固件 = 硬件抽象层 + 服务层 + 应用层,通过Protocol实现模块间解耦。安全启动就是在这些层之间插入签名验证的「关卡」。

好了,这一章的基础概念就讲到这里。记住:UEFI不是BIOS的升级版,它是完全不同的架构。下一章我们会深入安全启动的核心——签名验证机制。到时候我会带大家手撕一个实际的签名验证流程。

可可精品资料库