从BIOS到UEFI与EDK2:开源固件生态与启动流程全解析
2026/9/7 11:57:07 网站建设 项目流程

UEFI 这个话题,这几年在圈子里越来越热。以前大家聊 BIOS,无非是进设置调个启动项、开个虚拟化、改个风扇策略,遇到问题顶多刷个 BIOS 碰碰运气。但最近一两年风向明显变了:一方面,新版主板和整机厂商在快速淘汰传统 BIOS 模式;另一方面,开源固件的力量越来越强,从笔记本到服务器,从 ARM 开发板到 RISC-V 平台,到处都能看到 UEFI 的身影。我自己在折腾旧平台装新系统、给服务器做固件维护的时候,也踩了不少 UEFI 相关的坑。所以这篇东西,我想把从传统 BIOS 到 UEFI,再到 EDK2 和开源固件生态这条线梳理清楚,把那些零散的知识点和实操经验串成一个完整的图鉴。

这篇文章会覆盖几块内容:UEFI 到底是什么、和 BIOS 相比解决了哪些核心痛点;UEFI 固件在开机过程中是怎么一步步把系统拉起来的;EDK2 作为 UEFI 的事实标准实现,它的工程结构、编译流程和关键组件是怎么回事;然后会聊聊当前开源固件生态里几个重量级项目,以及它们和 EDK2 的关系。最后我会分享一些实际排查 UEFI 启动问题的思路,比如开机不进系统、启动模式不对、固件不支持 NVMe 这类情况,该从哪里下手。

无论你是刚接触固件开发的新人、玩机多年的老鸟,还是做服务器运维的工程师,这篇文章应该都能给你提供一个相对完整的视角。至少看完之后,再遇到 UEFI 相关的概念和报错,不会两眼一抹黑。

1. 从 BIOS 到 UEFI:不只是换了个名字

1.1 BIOS 时代的局限在哪

先聊 BIOS。BIOS 全称 Basic Input Output System,诞生于 IBM PC 时代,基本设计思路就是一段固化在主板芯片里的程序,负责在开机时做硬件自检(POST)、初始化最基本的设备,然后把控制权交给硬盘上的引导程序。这个设计在几十年里撑住了整个 PC 兼容生态,但它的局限也随着硬件发展越来越明显。

首先是寻址能力的瓶颈。传统 BIOS 的 16 位实模式设计基于 BIOS 中断调用,MBR 分区表只能管理 2TB 以下的磁盘,超过 2TB 的容量根本没法用。当年我为了给一块 3TB 的仓库盘做系统盘,折腾了半天 GPT 转 MBR 的兼容方案,最后发现只要用 BIOS 引导,系统就只能在非 UEFI 模式下以 MBR 方式跑,白白浪费了容量。

其次是启动速度。BIOS 的POST流程是串行的,每个设备初始化都得等着,整个过程动辄十几秒甚至更久。现在主流 PC 开机进系统只要几秒,这种差距来自 UEFI 支持并行初始化、按需加载驱动,把能用到的模块先跑起来,而不是把所有设备都轮一遍。

再一个就是扩展性。BIOS 的 Option ROM 机制非常古老,想在一台老主板上用 NVMe 固态硬盘启动,得去找魔改 BIOS,把 NVMe 模块硬塞进去。这种社区改版 BIOS 风险很大,刷坏了就得编程器伺候。而且 BIOS 的界面和交互方式太原始,连个图形化界面都很难做流畅。

1.2 UEFI 的核心设计思想

UEFI,全称 Unified Extensible Firmware Interface,统一可扩展固件接口。它的出现并不是为了简单替代 BIOS,而是重新定义了操作系统和固件之间的接口规范。如果你把固件理解成“硬件和系统之间的一层翻译”,那 BIOS 是只能做简单口译的初级翻译,UEFI 则是可以做同声传译、还能灵活扩展功能的专业团队。

UEFI 的几个核心设计:

一是模块化。驱动、协议、应用都对应独立模块,按需加载。和 BIOS 那种“一股脑全部塞进去”的方式完全不同。UEFI 里的 Driver 可以被加载和卸载,协议接口可以动态查找和绑定。

二是基于 C 语言而非汇编。传统 BIOS 开发高度依赖 16 位汇编,门槛极高,生态也被锁死在极少数芯片厂商和 BIOS 厂商手里。UEFI 的规范完全基于 C 语言,整个固件开发的门槛一下子降了下来,开源社区也能真正参与进来。

三是安全启动机制。Secure Boot 从固件层面校验引导程序的签名,防止恶意软件在系统加载前注入。这在 BIOS 时代是不存在的,也和系统里的杀毒软件完全不在一个层面。

四是硬件抽象和协议化。UEFI 定义了一整套 Protocol 接口,固件里的驱动、服务、系统应用都通过这些协议互相通信。比如你想在 UEFI 环境下操作文件系统,可以用 SIMPLE_FILE_SYSTEM_PROTOCOL,不必关心底层磁盘是 SATA 还是 NVMe。

可以说 UEFI 不是“更好的 BIOS”,而是一套全新的固件设计范式。传统 BIOS 那一套中断调用和实模式机制,在 UEFI 里被彻底替换成了基于驱动的协议栈。

2. UEFI 固件怎么工作:一次完整开机过程拆解

2.1 开机流程的各个阶段

UEFI 的启动过程,通常被分为 SEC、PEI、DXE、BDS、TSL、RT 这几个阶段。对普通用户和大部分开发工程师来说,TSL(Transient System Load,即临时系统加载)之后的阶段已经进入 OS Loader 的工作范围,真正需要关注的是前几个阶段。

先看 SEC(Security Phase,安全验证阶段)。这是 CPU 复位后执行的第一段代码,主要任务是建立临时的执行环境,完成 CPU 的初始状态设置,同时负责接收启动时产生的 Boot Mode。这个阶段非常短,但它的作用很关键,所有后续阶段的可信基础都是在 SEC 阶段建立的。

然后是 PEI(Pre-EFI Initialization,EFI 预初始化阶段)。PEI 的任务是对内存、CPU、芯片组做最基本的初始化,尤其是要找出内存的大小和位置。原因是后续的 DXE 阶段需要一个可用的内存环境来运行 C 语言代码。PEI 阶段会把一些关键信息,比如内存映射、固件卷位置,通过 HOB(Hand-Off Block)结构传给后面的阶段。

PEI 之后是 DXE(Driver Execution Environment,驱动执行环境)阶段。DXE 开始全面初始化系统中的各种设备,加载各种 UEFI Driver,建立起完整的协议栈。这个阶段会做 PCI 枚举、ACPI 表生成、SMBus 初始化等大量工作。真正到了 DXE 阶段,固件代码已经是完全跑在高位内存里的 64 位代码了,可以调用丰富的协议接口。

接下来是 BDS(Boot Device Selection,启动设备选择)阶段。BDS 负责按照启动顺序加载启动项,也就是我们常说的 Boot Option。它从 NVRAM 里读取启动顺序,逐个尝试加载引导程序。如果某个启动项失败,BDS 会继续尝试下一个。这也是为什么 U 盘没插好或者启动顺序不对,系统会直接跳过某个引导程序的原因。

TSL 之后就是 OS 启动。OS Loader(比如 GRUB2 或 Windows Boot Manager)被加载后,固件会通过 EXIT_BOOT_SERVICES 退出启动服务,把控制权完全交给操作系统。从这里开始,固件的主要作用就变成提供运行时服务了。

2.2 实模式与保护模式的切换过程

传统 BIOS 时代,整个 POST 过程和大部分运行时都是跑在 16 位实模式下的。实模式寻址空间只有 1MB,只能访问低于 1MB 的内存,所以 BIOS 代码被各种限制压得很惨。UEFI 把启动早期通过 PEI 阶段,从实模式一步步切换到保护模式和长模式,整个流程用 C 代码就可以控制,不需要像 BIOS 时代那样手工切换一大堆段寄存器。

具体到代码层面,PEI 阶段可能还在低端内存里执行。等内存初始化完成后,DXE 阶段就会把代码搬迁到高内存地址运行,同时进入 64 位模式。这个切换过程主要由 CPU 架构相关的代码完成,不同 CPU 厂商的实现细节并不一样。对开发者来说,理解这个阶段的意义在于:你写的 UEFI 驱动,可能在启动的关键路径上,也可能只在某个特定协议调用时加载。调试起来,早期 PEI 阶段往往没有串口,没有显示输出,只能靠调试器或者固件调试卡。

2.3 UEFI 启动模式下 GPT 和 MBR 的差异

UEFI 规范要求使用 GPT 分区表来配合 EFI System Partition(ESP)。ESP 是一个 FAT 格式的独立分区,里面存放引导程序,比如 Windows 的 bootmgfw.efi,或者 GRUB 的 grubx64.efi。

很多人在做 U 盘启动盘的时候会纠结格式化选 FAT32 还是 NTFS。答案是:UEFI 固件原生只认识 FAT 文件系统。绝大多数主板固件没有内置 NTFS 驱动,所以 UEFI 引导的 U 盘必须是 FAT32 格式。如果你想用 NTFS 的 U 盘做启动盘,得依赖固件里的第三方 NTFS 驱动,主板上不一定有。这里有一个比较微妙的地方:部分 UEFI 固件确实可以通过 Boot Option 列出 NTFS 设备,但那通常是厂商额外加了解析模块,不是 UEFI 规范默认提供的功能。

GPT 和 MBR 的另一个关键区别是冗余和保护。GPT 在磁盘头部和尾部各保存一份分区表,损坏一份还能靠另一份恢复;MBR 只有固定位置的一份,坏了就全盘不认。参考 UEFI 规范,GPT 是 UEFI 启动的必需分区表格式,这也是为什么很多老主板刷了魔改 BIOS 之后能支持 NVMe 启动,但磁盘分区表依然必须是 GPT。我自己遇到过一个情况:系统安装在 GPT 分区上,但因为启动方式还是 Legacy,启动时系统直接报错 “Error: Can't find GRUB”,这就是引导模式不匹配的典型问题。

3. EDK2 深入拆解:UEFI 固件的实际载体

3.1 什么是 EDK2,它和 UEFI 是什么关系

UEFI 是一个规范,PDF 文档定义了各种协议、接口和行为,但它本身不是可执行的代码。EDK2 就是 Intel 主导、由 TianoCore 社区维护的 UEFI 规范参考实现。可以这样理解:UEFI 是菜谱,EDK2 是按照菜谱做出来的标准菜,而各家主板厂商在 EDK2 基础上加入自己的定制模块,就成了我们熟知的品牌 BIOS。

EDK2 全称 EFI Development Kit II。目前绝大多数 UEFI 固件,不管是 Intel、AMD 平台还是 ARM 平台,底层基本都是 EDK2 或者波及其衍生代码。它不只是一个单独的仓库,而是一套庞大的代码库,包含固件核心、驱动框架、库函数、构建工具链等大量组件。

EDK2 的历史比较复杂,它分出了几个大仓库:edk2 主仓库存放核心代码,edk2-platforms 存放各平台相关代码,edk2-non-osi 存放一些只有二进制形式的驱动。社区习惯上把这一整套都叫做 EDK2,但实际开发中需要搞清楚你拉的是哪个仓库的代码。

3.2 EDK2 的核心模块:DXE 驱动、SMM 和 Protocol

EDK2 里的“模块”这个概念,对应 UEFI 规范里的 Image,比如一个 DXE driver、一个 UEFI application,或者一个 PEIM。模块有自己的入口函数,代码里通常是DxeMainSecurityStubDxe这样命名。

DXE 驱动在 EDK2 里有几个主要类型:一种是纯 DXE driver,只在 DXE 阶段被加载初始化,跑完就常驻或卸载;一种是 Runtime driver,会在 ExitBootServices 之后继续存在,为操作系统提供 Runtime Services,比如更新系统时间、设置启动项这类操作;还有一种细分是 SMM driver,运行在系统管理模式(System Management Mode, SMM)里,可以访问普通驱动访问不了的高特权资源。SMM 驱动经常被用于固件安全功能和硬件状态保护,但因为 SMM 特权极高,也是安全研究的热门目标。对普通固件研发工程师来说,SMM 驱动的内存安全和权限校验是重中之重。

Protocol 是 EDK2 里最重要的接口概念。一个 Protocol 本质上是一个 GUID 加一个结构体指针,里面放着函数指针和数据。驱动 A 安装一个 Protocol,驱动 B 通过gBS->LocateProtocol找到这个 Protocol,然后调用其中的函数。用设计模式的话说,这就是一种服务定位器和服务注册机制。EDK2 里几乎所有的功能都是通过 Protocol 暴露出去的,比如磁盘 I/O 相关的BLOCK_IO_PROTOCOLDISK_IO_PROTOCOL,显示输出相关的GOP_PROTOCOL,以及文件系统相关的SIMPLE_FILE_SYSTEM_PROTOCOL

3.3 EDK2 的构建系统和编译流程

在 EDK2 里写一个模块,并不是直接拿 GCC 编译.c文件就行,而是要经过一套特殊的构建流程。EDK2 使用的构建工具叫build,它读取平台的 DSC(Platform Description)文件、模块的 INF 文件和 DEC 文件,生成 AutoGen 头文件,然后调用真正的 C 编译器编译,最后链接出 .efi 文件。

DSC 文件是一个平台的“配方”,里面定义了包含哪些模块、启用哪些 PCD(Platform Configuration Database,平台配置数据库)、使用哪些库实例。INF 文件描述了一个模块的源文件列表、依赖的库、使用的 GUID 和协议。DEC 文件则是一个包(Package)的声明文件,定义了包内模块共享的头文件路径、GUID 值和 PCD 定义。

我第一次编译 EDK2 的时候,被这一套文件体系绕晕了很久。后来才意识到,这套设计与传统 Linux 内核构建有本质不同。EDK2 几乎不用 Makefile 来管理依赖,而是由build工具解析各类描述文件,生成完整的构建流程。以 OvmfPkg 为例,这个包模拟了一个 UEFI 固件运行环境,我们可以直接在 Linux 上编译 OvmfPkgX64,产出一个OVMF.fd固件镜像,然后在 QEMU 里加载运行。

实际编译 OvmfPkg 的命令大概是这样的:

git clone https://github.com/tianocore/edk2.git cd edk2 git submodule update --init --recursive make -C BaseTools source edksetup.sh build -a X64 -p OvmfPkg/OvmfPkgX64.dsc -t GCC5 -b DEBUG

编译产物通常在Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd。这个 .fd 文件就是一个完整的 UEFI 固件镜像,可以直接传给 QEMU 使用。很多做固件模拟、Fuzzing 和漏洞研究的开发者,日常就是围绕 OVMF 在跑。

3.4 UEFI Shell 和常用固件调试命令

真要深入固件开发,UEFI Shell 是绕不开的工具。它相当于 UEFI 环境下的一个命令行解释器,可以在没有操作系统的情况下直接访问硬件、操作文件、运行 .efi 应用。UEFI Shell 里常见的命令有:

  • map:查看设备映射,比如FS0BLK0这些设备名。
  • lscd:文件浏览和切换目录。
  • edit:类似 DOS 下的 Edit 编辑器,可以直接查看文本文件。
  • memmap:显示当前内存映射。
  • dmpstore:查看和修改 NVRAM 变量。
  • bcfg:手动管理启动项,这在调试引导问题时非常有用。

比如你在固件环境里想查看所有启动项:

Shell> bcfg boot dump

如果你在调试一个自定义的 .efi 应用,也可以用 Shell 直接运行:

Shell> fs0: FS0:\> MyTest.efi

UEFI Shell 自己也是一个 .efi 应用,需要放在 FAT 格式的 U 盘上,然后在固件里手动加载运行。很多主板在固件设置里没有直接提供 Shell 入口,但如果固件里保留了这个模块,就可以通过启动项指定加载 Shell.efi。

4. 开源固件生态:不止 EDK2 这一个玩家

4.1 TianoCore 和上游社区的角色

EDK2 的正式开源社区是 TianoCore,由 Intel 在 2004 年发起,目标是推动 UEFI 固件的开源发展。TianoCore 不是一个简单的代码托管站,它还维护着 EDK2 的邮件列表、Bugzilla、补丁审阅流程和年度固件开发者大会。

TianoCore 社区的上游代码质量要求非常高。我自己提交过几个小补丁,光是 commit message 的格式规范、收件人列表和 Signed-off-by 就折腾了好几次。但正是这种严格的流程,保证了 EDK2 在几十年跨度、多架构平台上还能保持相当高的稳定性。

TianoCore 之外,比较重要的开源固件项目还有 coreboot、openhbm、u-boot 的 UEFI 实现等。它们和 EDK2 的关系不是替代,而是互补和竞争共存。coreboot 的强项是极速启动和深度硬件初始化,它把平台初始化做完之后,可以加载一个 UEFI Payload,而这个 Payload 往往就是用 EDK2 编译出来的。所以很多主流笔记本上的开源固件方案,实际上是 coreboot + EDK2 Payload 的组合体,既有 coreboot 的简洁,又有 UEFI 的兼容性。

4.2 coreboot:极简启动和高硬件覆盖

coreboot 的前身是 LinuxBIOS,目标是用尽可能少的代码完成硬件初始化,然后快速把控制权交给系统或下一个固件阶段。它对硬件支持的精确度极高,每个主板的代码都相当底层。coreboot 的启动速度能压到几百毫秒,这是传统 UEFI 固件很难做到的。

但 coreboot 也有明显的短板:它不支持所有硬件。每年官方支持的主板和平台列表是固定的,很多消费级主板根本没有 coreboot 移植。想给一块主流 Z 系列主板刷 coreboot,通常需要自己移植或依赖社区非官方补丁,这对普通用户来说门槛太高。

coreboot 里有个概念叫 payload,也就是 coreboot 引导的下一段程序。可选的 payload 有 SeaBIOS(提供传统 BIOS 兼容层)、Tianocore(提供 UEFI 环境)、GRUB2、Linux 内核等。实际项目中,如果要在一个 coreboot 主板上启动 Windows 或 macOS,就必须用 Tianocore payload 来提供 UEFI 兼容层。这也是 EDK2 在开源固件生态中地位如此稳固的原因之一。

4.3 OpenBMC:服务器管理固件的开源风向

OpenBMC 是面向服务器管理控制器的开源固件项目。BMC(Baseboard Management Controller)是一颗独立于 CPU 的芯片,负责远程管理、传感器监控、日志记录和电源控制。传统 BMC 固件是闭源的,漏洞频出,修复周期又长。OpenBMC 把 BMC 固件开源化后,整个服务器管理层面的透明度、可定制性和可审计性都大大提高。

OpenBMC 的软件栈和 EDK2 差别很大。它基于 Linux 内核和 Yocto 构建系统,上层用 phosphor-dbus-interfaces 定义管理接口,用 webui-vue 提供 Web 管理界面。很多国产服务器和 ODM 厂商已经在供货 OpenBMC 方案。虽然 OpenBMC 不是一个 UEFI 实现,但它在固件生态里的地位越来越重要,尤其是云厂商和超大规模数据中心。

4.4 U-Boot 与嵌入式平台的 UEFI 补全

嵌入式平台现在也越来越多地使用 UEFI。U-Boot 本身是嵌入式 Linux 引导界的霸主,它也为多种 SoC 平台实现了 UEFI 接口。也就是说,你在嵌入式板上既能通过 U-Boot 引导 Linux 内核,也能通过 U-Boot 的 UEFI 实现来引导一个标准 UEFI 应用,比如 GRUB 或者 Windows Boot Manager。

对嵌入式开发者来说,U-Boot 的 UEFI 实现是一层很方便的兼容层。比如一个基于 Rockchip 芯片的开发板,厂商默认的 U-Boot 可能不提供标准 UEFI 接口,但你可以自己编译 U-Boot,开启CONFIG_EFI_LOADER,然后在 U-Boot 环境里运行bootefi命令来启动 UEFI 应用。这让 ARMed 平台的系统部署方式逐渐向 x86 看齐,也让 BIOS 研发工程师转型嵌入式的难度降低了很多。

4.5 其他值得关注的开源固件方向

还有一个方向是 NERF(Non-Extensible Reduced Firmware),它是由谷歌提出的开源固件方案,试图让每个固件组件的逻辑更简单,减少不必要的启动阶段。NERF 更多用于 ChromeOS 设备,是一个相对实验性的项目。

另外,RISC-V 平台的固件生态也在快速崛起。RISC-V 上通常使用 OpenSBI(Open Supervisor Binary Interface)作为 M-mode 固件,提供 SBI 服务,然后上面跑 U-Boot 或者 EDK2。如果你想在 RISC-V 平台上体验 UEFI 启动流程,EDK2 也有对应的 RISC-V 架构支持,虽然成熟度还比不上 x86 和 ARM,但方向已经很清晰了。

5. 实操经验:UEFI 启动问题的排查黄金路线

5.1 开机进不了系统:先分清阶段

很多人遇到 UEFI 启动问题,第一反应就是重装系统或者换 U 盘。但我建议先按照启动阶段一步步定位,别盲目操作。

开机后注意观察屏幕表现:是直接黑屏,还是卡在品牌 Logo,还是已经出现了类似 “No bootable device” 的提示,还是已经进入系统选择界面但选不对?这个现象对应完全不同的故障阶段。

如果卡在品牌 Logo 或黑屏,问题大概率出在 PEI 或 DXE 阶段之前的固件初始化,比如内存不稳定、CPU 过压、外接设备短路等。这时可以尝试最小化硬件配置,只留 CPU、单根内存和核显,再开机。

如果提示 “No bootable device”,说明固件初始化完成了,但 BDS 阶段没有找到有效启动项。这时重点检查磁盘是否被识别、启动顺序是否正确、U 盘是不是 FAT32 格式、启动模式是 UEFI 还是 Legacy。

如果系统能进 GRUB 但进不了 Windows 或 Linux,那么说明固件没问题,问题在系统引导器或者系统层。这时候不用再折腾固件了,应该去修复引导器、检查磁盘分区表是否匹配启动模式。

5.2 UEFI 还是 Legacy:启动模式不匹配的排查思路

启动模式不匹配的典型场景是:磁盘分区表是 GPT,但固件设置里选了 Legacy;或者磁盘分区表是 MBR,但固件选了 UEFI。这两种情况都会导致系统无法引导。

排查方法很简单:进固件设置,确认启动模式。现在大多数主板把启动模式分为 “UEFI”、“Legacy” 和 “UEFI with CSM”。CSM(Compatibility Support Module)是为了兼容旧系统保留的 Legacy 支持模块。如果你不跑旧系统,建议直接关闭 CSM,强制 UEFI。

我自己遇到过一台老笔记本,默认开启 CSM,U 盘启动一直提示 “Error: BIOS/legacy boot of uefi-only media”,后来进设置关掉 CSM,U 盘 UEFI 启动就正常了。还有一个常用技巧:用diskpart或者 Linux 下的gdisk查看磁盘分区表类型,必要时用gdisk把 MBR 无损转换为 GPT。

5.3 固件不支持 NVMe 启动的补救方案

NVMe 启动问题主要集中在老平台上。很多 2017 年之前的主板芯片组没有原生 NVMe OpROM,导致插上 NVMe 固态后,固件里能看到盘,但启动项里没有它。社区常见解决方案有两个。

一是用 Clover 或 OpenCore 这样的引导器作为中间层,先从一个 U 盘或另一块小硬盘启动,再由引导器加载 NVMe 驱动、引导系统。这个方案不需要改固件,但要额外占用一个启动设备。

二是修改固件镜像,把 NVMe 模块刷到主板的 OptionROM 或 DXE 驱动里。这就是大家常说的“魔改 BIOS”的原理。很多社区论坛里流传的魔改 BIOS 就是在 EDK2 编译出的固件镜像里,加入了 NVMe 驱动 DXE 模块,然后重新封装刷写。风险点是:万一 BIOS 芯片容量不够、模块兼容性有问题,刷完可能直接变砖。所以魔改 BIOS 一定要确认型号完全匹配,最好有编程器和备份能力再动手。

5.4 常见 UEFI 固件维护小技巧

固件维护里,有几个高频需求:备份当前固件、重置 NVRAM、清除密码、设置启动项。

备份固件不一定要用编程器。如果能进 UEFI Shell,可以用dmpstore配合某些工具读 Flash 内容,但更可靠的是在固件设置界面里自带更新功能,先导出当前版本。真正的整片固件备份通常需要编程器读取 SPI Flash 芯片。编程器有很多型号,新手建议选 CH341A,便宜且资料多,但操作时要注意芯片型号,别把 1.8V 的芯片接到 3.3V 模式上,烧芯片的风险很大。

清密码也有几种方式:典型做法是拔 CMOS 电池并放电,原理是 NVRAM 变量存在 RTC 供电区域,断电后会被清空。有些高端本子或服务器,密码存在专门的 TPM 或 Flash 区域,单纯拔电池没用,需要固件层面操作。顺带一提,如果是服务器 BMC 密码忘了,可以查 OpenBMC 或者各厂商的默认恢复说明,很多是短路特定引脚或长按电源键。

设置启动顺序最稳妥的方式还是进固件界面,但如果你远程操作,UEFI Shell 下的bcfg命令会更方便。比如把FS0:上的EFI\BOOT\BOOTX64.EFI添加到第一启动顺序:

Shell> bcfg boot add 0 FS0:\EFI\BOOT\BOOTX64.EFI "MyBootEntry"

5.5 典型问题速查表

下面整理一些实际中常见的 UEFI 问题场景和大致解决方向,方便大家对照排查。

问题现象可能原因排查方向
开机提示 No bootable device启动顺序错误 / ESP 丢失检查启动项列表,确认 ESP 存在
U 盘启动失败,提示 UEFI-only media启动模式不匹配或 U 盘分区表不对改成 UEFI 模式,U 盘用 FAT32 + GPT
新盘装系统后无法引导固件启动模式与分区表不匹配确认 GPT/UEFI 或 MBR/Legacy 一致性
插 NVMe 后固件看不到老平台缺少 NVMe 驱动用 Clover 中间引导或魔改固件
开机卡 Logo硬件初始化失败最小化硬件排查,检查内存和显卡
BIOS 密码无法清除密码存在 Flash 或 TPM 区域尝试放电 NVRAM,必要时要求厂商解锁
开机进 UEFI Shell 但找不到硬盘Shell 未加载文件系统驱动map -r重新扫描设备,确认 ESP 格式为 FAT

6. 固件开发的入门路径和生态展望

6.1 从哪开始接触 EDK2

如果你想真正进入 UEFI 固件开发,我建议从三个方向切入:先能用、再能看、最后能改。

能用指的是会用 OVMF 在虚拟机里跑一套 UEFI 固件,会用 UEFI Shell。这一步在你电脑上不需要真实主板配合,直接用 QEMU 和 OVMF 就能完成。我以前还专门写过一个脚本,一键编译 OVMF 并启动 QEMU,目的是快速验证某个 .efi 应用在不同固件版本下的行为。

能看指的是能读懂 EDK2 的代码结构。不建议一上来就从MdeModulePkg的几千个文件开始啃,而是从一个具体问题切入,比如“固件是怎么检测到 U 盘的”,沿着UsbBusDxePartitionDxeFatPkg这几条线索往下看,理解每个模块的职责边界。

能改指的是能自己写一个简单的 UEFI Application 或 DXE Driver。比如写一个 .efi 程序,在 UEFI Shell 里打印当前系统时间或者内存信息。这个过程会让你体会到 UEFI 应用开发和普通 Linux 应用开发的差异:没有 libc、没有系统调用,一切操作都靠 Protocol 和 Boot Services 完成。

一个很典型的例子是 UEFI 下获取 SMBIOS 信息。代码里需要先定位EFI_SMBIOS_PROTOCOL,再通过它遍历表项,最后格式化输出。这个逻辑如果用传统 C 写会非常笨重,但在 UEFI 里反而是很自然的做法。

6.2 UEFI 和开源固件的未来

从行业趋势看,UEFI 的地位会越来越稳固,但它的实现方式正在从闭源黑盒走向开源协作。TianoCore 社区、coreboot 社区、OpenBMC 社区,再加上 Google 推动的 NERF,都在挤压传统闭源固件厂商的空间。尤其是大规模云厂商,一旦发现闭源固件的定制和修复成本过高,就会更愿意采买或投资开源固件方案。

另一个值得关注的方向是固件安全。此前曝出的多个漏洞都集中在 SMM 和 NVRAM 相关组件上,开源固件至少让研究者能够看到代码,提前发现和修复问题。这与闭源固件“出事才补丁”的模式有本质区别。

我自己近年的一个体会是:固件开发不再是小圈子里的冷门方向。随着 UEFI Secure Boot 成为标配、硬件级安全要求提高、以及 RISC-V 等新架构崛起,固件工程师的需求反而在增长。如果你有扎实的 C 语言功底、了解计算机体系结构,再从 EDK2 的 UEFI 机制入手,这条路是可以走通的。

6.3 一些学习资源建议

学习 UEFI 和 EDK2,资源不必贪多,但要选对主线。首先是 UEFI 规范的官方文档,虽然后面几十章看起来非常劝退,但不必全读,只看核心章节就能建立很好的框架。其次是 TianoCore 的官方 wiki 和邮件列表,很多疑难问题在邮件列表里都有深入讨论。社区里 edk2-devel 的讨论质量相当高,提交补丁之前去翻一翻能学到很多东西。

书籍方面,比较经典的《Beyond BIOS》是入门必读,虽然版本略老,但核心概念没变。还有一本叫《Harnessing the UEFI Shell》的小册子,对 UEFI Shell 命令讲得很清楚,适合做工具书。

视频资源方面,TianoCore 社区每年会发布固件开发者大会的演讲回放,覆盖从构建系统到安全研究的各种主题,强烈推荐。至于中文资料,目前还是偏少且碎片化严重,这也是我写下这篇文章的原因之一,希望更多人能在这个方向少踩坑、少走弯路。

7. 实际操作中的几点心得

最后聊几个我踩过坑之后的真实感受。

第一,遇到固件问题,先备份再折腾。不管是刷 BIOS 还是改 NVRAM,操作前一定要先备份当前固件。很多主板固件界面里没有备份功能,那就用编程器备份,或者查厂商是否提供官方固件备份工具。没有备份就贸然刷写,出了问题找救援的成本远高于备份的时间成本。

第二,不要把 CSM 当成万能开关。CSM 确实能提供 Legacy 兼容,但它也会降低安全性和启动速度,还可能掩盖 UEFI 启动路径上的问题。如果你确定系统支持 UEFI,直接关掉 CSM,能避免很多奇奇怪怪的引导错误。

第三,UEFI 调试日志是你的朋友。很多主板固件支持开启串口调试日志或者 LOG 输出,把日志开启后,启动失败的具体模块就能一目了然。哪怕没有串口,很多固件也能把 DEBUG 信息写入内存区域,通过 Windows 事件日志或 Linux 的 dmesg 查看,只是需要提前开启相关开关。

第四,社区资源非常宝贵,但要学会甄别。魔改 BIOS、第三方固件、非官方启动工具确实能解决一些特定问题,但来源不明的固件镜像可能含有恶意修改,甚至直接刷坏硬件。如果你不是特别清楚自己在做什么,优先用官方固件或知名开源项目构建的固件。

UEFI 和开源固件这个领域,确实是有门槛的,但只要思路清晰、工具上手,其实并不神秘。希望这篇图鉴能帮你把散落的知识点串起来,以后无论是排查启动故障、了解固件原理,还是想入门固件开发,都能有个清晰的落脚点。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询