UEFI BIOS深度解析:现代固件架构的革命性突破
本文深入探讨UEFI(统一可扩展固件接口)的架构设计、核心概念和相比Legacy BIOS的革命性优势
目录
UEFI概述
什么是UEFI?
UEFI(Unified Extensible Firmware Interface,统一可扩展固件接口)定义了连接操作系统和硬件体系之间的标准接口。它是由UEFI论坛(UEFI Forum)制定的开放标准,旨在替代传统的Legacy BIOS。
UEFI的发展历程
| 时间 | 事件 | 意义 |
|---|---|---|
| 1998年 | Intel提出EFI 1.0 | 最初的EFI规范 |
| 2005年 | UEFI 2.0发布 | 统一标准,更名为UEFI |
| 2006年 | UEFI 2.1发布 | 增加安全启动支持 |
| 2011年 | Windows 8要求UEFI | 推动UEFI普及 |
| 2017年 | UEFI 2.7发布 | 支持更多新特性 |
UEFI的设计目标
- 跨平台支持:支持x86、x64、ARM、MIPS等多种架构
- 模块化设计:驱动和应用程序可以动态加载
- 标准化接口:统一的API和协议规范
- 安全性:内置安全启动和可信链机制
- 易用性:支持图形界面和鼠标操作
UEFI核心概念
在UEFI构建的软件世界中,有一些基本概念必须熟悉,它们是理解UEFI的基础。
1. 启动管理器(Boot Manager)
定义: UEFI的应用程序和驱动可以在任何UEFI规范支持的设备和文件系统中加载,而决定加载顺序的就是启动管理器。
功能:
- 通过全局的NVRAM变量来管理加载顺序
- 可以配置启动选项
- 增加启动选项或从启动列表中删除无效的选项
- 支持多操作系统启动
NVRAM变量示例:
bash
# 查看启动项
efibootmgr -v
# 输出示例:
# BootCurrent: 0001
# BootOrder: 0001,0002,0003
# Boot0001* Windows Boot Manager
# Boot0002 Linux Boot Manager
# Boot0003 UEFI Shell
2. UEFI Images(UEFI镜像)
定义: UEFI Images是UEFI规范定义的包含可执行代码的二进制程序文件。
类型:
- UEFI应用程序 :
.efi文件,独立运行的程序 - UEFI驱动 :
.efi文件,为硬件提供驱动支持
文件结构:
UEFI镜像采用PE32(Portable Executable 32)文件结构,这是Windows可执行文件的标准格式:
PE32文件结构:
├── DOS Header (MZ)
├── PE Header
├── Section Headers
├── .text (代码段)
├── .data (数据段)
└── .reloc (重定位段)
优势:
- 支持跨平台(x86、ARM等)
- 标准化的加载机制
- 支持代码签名验证
3. UEFI Services(UEFI服务)
定义: UEFI Service是平台调用接口的集合,允许UEFI程序和操作系统调用。
分类:
- 启动服务(Boot Services,BS):在操作系统加载前可用
- 运行时服务(Runtime Services,RS):操作系统运行期间也可用
服务提供者:
- UEFI应用程序
- UEFI驱动
- UEFI OS Loader(操作系统加载器)
4. UEFI Protocol(UEFI协议)
定义: UEFI Protocol本质上是一种数据结构,它包含三个部分:
- 全局唯一标识符(GUID):128位唯一标识符
- 接口数据结构:定义协议的数据结构
- 服务:协议提供的函数接口
GUID格式:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
例如:EFI_BLOCK_IO_PROTOCOL_GUID
{964e5b22-6459-11d2-8e39-00a0c969723b}
Protocol示例:
c
// EFI_BLOCK_IO_PROTOCOL结构
typedef struct {
UINT64 Revision;
EFI_BLOCK_IO_MEDIA *Media;
EFI_BLOCK_RESET Reset;
EFI_BLOCK_READ ReadBlocks;
EFI_BLOCK_WRITE WriteBlocks;
EFI_BLOCK_FLUSH FlushBlocks;
} EFI_BLOCK_IO_PROTOCOL;
5. UEFI系统表(UEFI System Table)
定义: 所有UEFI镜像都会接收到一个指向UEFI系统表的指针,通过此系统表可以访问固件提供的UEFI Protocol。
系统表结构:
c
typedef struct {
EFI_TABLE_HEADER Hdr;
CHAR16 *FirmwareVendor;
UINT32 FirmwareRevision;
EFI_HANDLE ConsoleInHandle;
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
EFI_HANDLE ConsoleOutHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
EFI_HANDLE StandardErrorHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;
EFI_RUNTIME_SERVICES *RuntimeServices;
EFI_BOOT_SERVICES *BootServices;
UINTN NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigurationTable;
} EFI_SYSTEM_TABLE;
UEFI软件架构
架构层次图
┌─────────────────────────────────────────┐
│ 操作系统 (OS) │
├─────────────────────────────────────────┤
│ UEFI OS Loader │
│ (操作系统加载器) │
├─────────────────────────────────────────┤
│ UEFI Applications │
│ (UEFI应用程序) │
├─────────────────────────────────────────┤
│ UEFI Drivers │
│ (UEFI驱动) │
├─────────────────────────────────────────┤
│ Boot Services (BS) │
│ (启动服务) │
├─────────────────────────────────────────┤
│ Runtime Services (RS) │
│ (运行时服务) │
├─────────────────────────────────────────┤
│ UEFI Protocols │
│ (UEFI协议) │
├─────────────────────────────────────────┤
│ UEFI System Table │
│ (UEFI系统表) │
├─────────────────────────────────────────┤
│ Platform Hardware │
│ (平台硬件) │
└─────────────────────────────────────────┘
架构特点
图1-2 UEFI软件架构
UEFI为操作系统启动和启动前的运行状态提供了一个标准的环境,规范中详细规定了系统的控制权如何从启动前环境传递到操作系统。它所规定的与平台信息相关的启动服务(BS)和运行时服务(RT),都是与平台架构相分离的,即独立于平台上的抽象接口。这就是为什么发展到现在,UEFI可以支持X86、ARM、MIPS等各种CPU架构的原因。
服务生命周期
对Option ROM开发者来说:
┌─────────────────────────────────────────┐
│ Option ROM执行前 │
│ ✓ Boot Services可用 │
│ ✓ Runtime Services可用 │
├─────────────────────────────────────────┤
│ OS Loader加载后 │
│ ✓ Boot Services可用 │
│ ✓ Runtime Services可用 │
├─────────────────────────────────────────┤
│ ExitBootServices()调用后 │
│ ✗ Boot Services销毁 │
│ ✓ Runtime Services保留 │
└─────────────────────────────────────────┘
关键点:
- Option ROM运行于操作系统加载器执行前,启动服务和运行时服务都可以使用
- 从操作系统加载器被加载,到操作系统加载器执行
ExitBootServices()前的这段时间,是从UEFI环境向操作系统过渡的阶段,启动服务和运行时服务也同样可以使用 - 在操作系统加载器执行
ExitBootServices()之后,启动服务就完成了使命,其占用的资源会被回收,计算机系统进入UEFI Runtime阶段 - 此后,就只能使用运行时服务了,启动服务已经从系统中销毁
UEFI服务详解
启动服务(Boot Services)
启动服务提供的服务包括如下几项:
1. Event(事件)服务
功能: 允许程序进行异步操作,这是UEFI得以执行并发操作的基础。
事件类型:
- 定时器事件:基于时间的触发
- 等待事件:等待某个条件满足
- 通知函数:事件触发时调用的回调函数
示例代码:
c
EFI_EVENT TimerEvent;
// 创建定时器事件
gBS->CreateEvent(
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&TimerEvent
);
// 设置定时器(10秒)
gBS->SetTimer(TimerEvent, TimerRelative, 10000000);
// 等待事件
gBS->WaitForEvent(1, &TimerEvent, NULL);
2. Timer(定时器)服务
功能: 配合Event提供定时器功能。
定时器类型:
- TimerCancel:取消定时器
- TimerPeriodic:周期性定时器
- TimerRelative:相对时间定时器
- TimerAbsolute:绝对时间定时器
3. 内存管理
功能: 提供内存的分配和释放服务,管理系统的内存映射。
内存类型:
| 类型 | 说明 |
|---|---|
| EfiLoaderCode | 加载器代码 |
| EfiLoaderData | 加载器数据 |
| EfiBootServicesCode | 启动服务代码 |
| EfiBootServicesData | 启动服务数据 |
| EfiRuntimeServicesCode | 运行时服务代码 |
| EfiRuntimeServicesData | 运行时服务数据 |
| EfiConventionalMemory | 常规内存 |
| EfiUnusableMemory | 不可用内存 |
| EfiACPIReclaimMemory | ACPI回收内存 |
| EfiACPIMemoryNVS | ACPI非易失性内存 |
| EfiMemoryMappedIO | 内存映射IO |
| EfiMemoryMappedIOPortSpace | 内存映射IO端口空间 |
| EfiPalCode | PAL代码 |
内存分配示例:
c
EFI_PHYSICAL_ADDRESS Memory;
UINTN Pages = 10; // 分配10页(4KB/页)
// 分配内存
EFI_STATUS Status = gBS->AllocatePages(
AllocateAnyPages,
EfiBootServicesData,
Pages,
&Memory
);
// 使用内存...
// 释放内存
gBS->FreePages(Memory, Pages);
4. Protocol服务
功能: 提供安装和卸载Protocol的服务,以及查找、打开和关闭Protocol的服务。
主要函数:
InstallProtocolInterface():安装协议接口UninstallProtocolInterface():卸载协议接口LocateProtocol():定位协议OpenProtocol():打开协议CloseProtocol():关闭协议
示例:
c
EFI_BLOCK_IO_PROTOCOL *BlockIo;
// 定位Block IO协议
EFI_STATUS Status = gBS->LocateProtocol(
&gEfiBlockIoProtocolGuid,
NULL,
(VOID **)&BlockIo
);
if (EFI_ERROR(Status)) {
// 处理错误
}
5. Image服务
功能: 提供加载、卸载、启动和退出UEFI应用程序和驱动的服务。
主要函数:
LoadImage():加载镜像StartImage():启动镜像Exit():退出镜像UnloadImage():卸载镜像
6. 其他服务
包括设置看门狗定时器、延时、计算CRC校验值等,随着UEFI标准的发展,这类功能也在不断增加。
运行时服务(Runtime Services)
运行时服务提供的服务包括如下几项:
1. 系统变量服务
功能: 读取或者设置系统变量,比如设定启动项顺序。
变量命名空间:
EFI_GLOBAL_VARIABLE:全局变量EFI_IMAGE_SECURITY_DATABASE_GUID:安全数据库- 厂商自定义GUID:厂商特定变量
常用工具:
bash
# Linux下的efibootmgr工具
efibootmgr -v # 查看启动项
efibootmgr -o 0001,0002,0003 # 设置启动顺序
efibootmgr -c -L "Linux" -l /vmlinuz # 创建启动项
2. 时间服务
功能: 提供读取和设定系统时间的功能,也提供了读取和设定定时唤醒系统的功能。
时间结构:
c
typedef struct {
UINT16 Year; // 1900 - 9999
UINT8 Month; // 1 - 12
UINT8 Day; // 1 - 31
UINT8 Hour; // 0 - 23
UINT8 Minute; // 0 - 59
UINT8 Second; // 0 - 59
UINT8 Pad1;
UINT32 Nanosecond; // 0 - 999999999
INT16 TimeZone; // -1440 to 1440 or 2047
UINT8 Daylight;
UINT8 Pad2;
} EFI_TIME;
3. 内存虚拟地址服务
功能: 提供将内存的物理地址转换为虚拟地址的服务。
4. 其他服务
包括重启系统、更新BIOS(Capsules服务)、交换操作系统与BIOS的数据等各种服务。
UEFI vs Legacy BIOS对比
全面对比表
| 特性 | Legacy BIOS | UEFI BIOS |
|---|---|---|
| 运行模式 | 实模式(16位) | 保护模式(32/64位) |
| 可寻址内存 | 1MB | 4GB+(取决于架构) |
| 开发语言 | 主要使用汇编 | C/C++/Python |
| 代码大小 | 受ROM限制(通常<8MB) | 可存储在硬盘分区 |
| 启动速度 | 较慢 | 较快(并行初始化) |
| 图形界面 | 文本模式 | 支持高分辨率GUI |
| 鼠标支持 | 不支持 | 支持 |
| 安全启动 | 不支持 | 支持(Secure Boot) |
| GPT分区 | 不支持 | 原生支持 |
| 大硬盘支持 | 限制2TB | 支持超大容量 |
| 网络启动 | 有限支持 | 完整支持 |
| 驱动加载 | 静态链接 | 动态加载 |
| 模块化 | 差 | 优秀 |
| 跨平台 | x86/x64 | x86/x64/ARM/MIPS等 |
| 代码签名 | 不支持 | 支持 |
| 远程管理 | 不支持 | 支持 |
启动时间对比
Legacy BIOS启动流程:
上电 → POST → 硬件初始化 → 加载OS
时间:通常 30-60秒
UEFI启动流程:
上电 → SEC → PEI → DXE → BDS → TSL → OS
时间:通常 10-20秒(并行初始化)
内存使用对比
Legacy BIOS内存布局:
0x00000 - 0x9FFFF: 可用内存(640KB)
0xA0000 - 0xBFFFF: 视频内存
0xC0000 - 0xEFFFF: ROM扩展区
0xF0000 - 0xFFFFF: 系统BIOS
UEFI内存布局:
0x00000000 - 0x0009FFFF: 传统内存(可选)
0x000A0000 - 0x000FFFFF: 传统视频内存
0x00100000 - 0xFFFFFFFF: UEFI可用内存(4GB)
(64位系统可访问更大地址空间)
UEFI的优势
UEFI在发展过程中,打败其他新的BIOS架构,尤其是取代Legacy BIOS,得益于以下几大优势。
1. 开发效率高
技术栈对比:
| 方面 | Legacy BIOS | UEFI |
|---|---|---|
| 主要语言 | 汇编 | C/C++ |
| 辅助语言 | 无 | Python(工具链) |
| 代码复用 | 困难 | 容易 |
| 库支持 | 无 | 丰富(libjpeg等) |
| 调试工具 | 有限 | 完善 |
优势:
- 相比于Legacy BIOS采用汇编语言开发,UEFI采用了C语言、Python语言加少量汇编语言的混合模式编写,其中以C语言为主
- 实际上,UEFI的应用程序和驱动可以使用C++编写
- 它屏蔽了大量的底层硬件细节,为程序员提供了非常统一的调用接口
- 能很方便地在多种CPU架构间编写代码
- 很多代码可以重复使用,也可以直接使用大量的C语言库,比如libjpeg等,极大地提高了编程效率
代码示例对比:
Legacy BIOS(汇编):
assembly
; 显示字符 'A'
MOV AH, 0Eh ; 功能号
MOV AL, 'A' ; 字符
MOV BH, 0 ; 页号
INT 10h ; 调用BIOS中断
UEFI(C语言):
c
// 显示字符串
EFI_STATUS Status;
Status = gST->ConOut->OutputString(
gST->ConOut,
L"Hello, UEFI!\n"
);
2. 突破了ROM的容量限制
存储方式对比:
| 特性 | Legacy BIOS | UEFI |
|---|---|---|
| 存储位置 | Flash ROM | Flash ROM + 硬盘分区 |
| 容量限制 | 通常<8MB | 几乎无限制 |
| 文件系统 | 不支持 | 支持(FAT32等) |
| 可执行程序 | 受限 | 丰富 |
优势:
- UEFI可以解析文件系统,可以将硬盘的某个区域变为自己的存储空间
- 这样不但保证了充足的容量,而且可以直接执行一些常用的程序
- 比如操作系统的多引导、系统备份和恢复等
- 由于不受限于容量,UEFI可以吸取现代操作系统的设计理念,将功能分层化,不断添加各类新的功能,以满足未来计算机的发展
EFI系统分区(ESP):
GPT分区表:
├── EFI System Partition (ESP)
│ ├── \EFI\Boot\bootx64.efi
│ ├── \EFI\Microsoft\Boot\bootmgfw.efi
│ └── \EFI\Linux\grubx64.efi
├── Windows分区
├── Linux分区
└── 数据分区
3. 可扩展性好
模块化设计:
- UEFI采用模块化的设计,驱动可以动态加载
- 可以非常方便地添加对新硬件的支持
- UEFI的每个驱动,均有唯一的GUID标识
- 使得UEFI系统的升级变得安全而简单
应用场景:
- 各种备份和诊断功能,都可以在UEFI下实现
- 配合UEFI提供的网络功能,可以远程对主板进行故障诊断
- 或者进行BIOS更新
驱动加载示例:
c
// 动态加载驱动
EFI_HANDLE ImageHandle;
EFI_STATUS Status;
Status = gBS->LoadImage(
FALSE,
gImageHandle,
DevicePath,
NULL,
0,
&ImageHandle
);
if (!EFI_ERROR(Status)) {
gBS->StartImage(ImageHandle, NULL, NULL);
}
4. 系统性能高
运行模式对比:
| 特性 | Legacy BIOS | UEFI |
|---|---|---|
| CPU模式 | 16位实模式 | 32/64位保护模式 |
| 内存访问 | 受限(1MB) | 完整访问 |
| 并行处理 | 不支持 | 支持(异步操作) |
| CPU利用率 | 低 | 高 |
优势:
- Legacy BIOS运行于16位实模式,而UEFI是直接运行于保护模式下的,可以充分利用CPU和内存
- UEFI提供了异步操作机制,这类似于操作系统下的多进程模式
- 可充分提高CPU的利用率,减少等待时间
性能提升:
启动时间对比:
Legacy BIOS: 30-60秒
UEFI: 10-20秒(提升50-70%)
硬件初始化:
Legacy BIOS: 串行初始化
UEFI: 并行初始化(多核CPU优势)
5. 安全性高
安全机制对比:
| 安全特性 | Legacy BIOS | UEFI |
|---|---|---|
| 代码签名 | 不支持 | 支持 |
| 安全启动 | 不支持 | 支持(Secure Boot) |
| 可信链 | 无 | 完整可信链 |
| 恶意软件防护 | 弱 | 强 |
Secure Boot流程:
1. 主板出厂时内置可信公钥(CA证书)
↓
2. UEFI固件验证启动加载器签名
↓
3. 启动加载器验证内核签名
↓
4. 内核验证驱动签名
↓
5. 形成完整的可信链
优势:
- UEFI建立了完整的可信链机制
- 主板出厂时,可以内置一些可靠的公钥
- 这些公钥一般由比较权威的CA机构发布,比如VeriSign等
- 当系统的安全启动功能打开后,UEFI在执行应用程序和驱动前,会检测应用程序和驱动的证书
- 只有证书被内置公钥认证通过,应用程序和驱动才能被执行
- UEFI的安全机制提高了操作系统启动过程的安全性,能有效阻止恶意软件
6. 易用性好
用户界面对比:
| 特性 | Legacy BIOS | UEFI |
|---|---|---|
| 显示模式 | 文本模式(80x25) | 高分辨率图形 |
| 颜色支持 | 16色 | 真彩色 |
| 鼠标支持 | 不支持 | 支持 |
| 界面类型 | 命令行 | GUI图形界面 |
| 多语言 | 有限 | 完整支持 |
优势:
- 从UEFI的设计可以看出,它实际上是一个简化了的操作系统
- 它支持高分辨率的彩色显示,可以运行GUI(图形用户界面)
- 支持鼠标的使用
- 这使得开发人员可以构建非常友好的用户交互界面
- 用户能更容易、更方便地调节各种参数
UEFI Setup界面特点:
- 图形化菜单
- 鼠标操作
- 实时预览
- 多语言支持
- 搜索功能
- 帮助提示
总结
UEFI作为现代计算机固件的标准,相比Legacy BIOS在多个方面实现了革命性的突破:
- 开发效率:从汇编到C/C++,大幅提升开发效率
- 存储容量:突破ROM限制,支持文件系统
- 扩展性:模块化设计,动态加载驱动
- 性能:保护模式运行,并行初始化
- 安全性:完整的可信链和安全启动机制
- 易用性:图形界面,鼠标操作
这些优势使得UEFI成为现代计算机固件的标准选择,为未来的计算机发展奠定了坚实的基础。在下一篇文章中,我们将深入探讨UEFI的启动过程和开发环境搭建。
参考资料
- UEFI Specification 2.7+
- 《UEFI编程实践》- 罗冰
- Intel Platform Initialization Specification
- TianoCore EDK II Documentation
本文基于《UEFI编程实践》读书笔记整理,旨在帮助读者深入理解UEFI的架构设计和优势。
UEFI BIOS深度解析:现代固件架构的革命性突破
本文深入探讨UEFI(统一可扩展固件接口)的架构设计、核心概念和相比Legacy BIOS的革命性优势
目录
UEFI概述
什么是UEFI?
UEFI(Unified Extensible Firmware Interface,统一可扩展固件接口)定义了连接操作系统和硬件体系之间的标准接口。它是由UEFI论坛(UEFI Forum)制定的开放标准,旨在替代传统的Legacy BIOS。
UEFI的发展历程
| 时间 | 事件 | 意义 |
|---|---|---|
| 1998年 | Intel提出EFI 1.0 | 最初的EFI规范 |
| 2005年 | UEFI 2.0发布 | 统一标准,更名为UEFI |
| 2006年 | UEFI 2.1发布 | 增加安全启动支持 |
| 2011年 | Windows 8要求UEFI | 推动UEFI普及 |
| 2017年 | UEFI 2.7发布 | 支持更多新特性 |
UEFI的设计目标
- 跨平台支持:支持x86、x64、ARM、MIPS等多种架构
- 模块化设计:驱动和应用程序可以动态加载
- 标准化接口:统一的API和协议规范
- 安全性:内置安全启动和可信链机制
- 易用性:支持图形界面和鼠标操作
UEFI核心概念
在UEFI构建的软件世界中,有一些基本概念必须熟悉,它们是理解UEFI的基础。
1. 启动管理器(Boot Manager)
定义: UEFI的应用程序和驱动可以在任何UEFI规范支持的设备和文件系统中加载,而决定加载顺序的就是启动管理器。
功能:
- 通过全局的NVRAM变量来管理加载顺序
- 可以配置启动选项
- 增加启动选项或从启动列表中删除无效的选项
- 支持多操作系统启动
NVRAM变量示例:
bash
# 查看启动项
efibootmgr -v
# 输出示例:
# BootCurrent: 0001
# BootOrder: 0001,0002,0003
# Boot0001* Windows Boot Manager
# Boot0002 Linux Boot Manager
# Boot0003 UEFI Shell
2. UEFI Images(UEFI镜像)
定义: UEFI Images是UEFI规范定义的包含可执行代码的二进制程序文件。
类型:
- UEFI应用程序 :
.efi文件,独立运行的程序 - UEFI驱动 :
.efi文件,为硬件提供驱动支持
文件结构:
UEFI镜像采用PE32(Portable Executable 32)文件结构,这是Windows可执行文件的标准格式:
PE32文件结构:
├── DOS Header (MZ)
├── PE Header
├── Section Headers
├── .text (代码段)
├── .data (数据段)
└── .reloc (重定位段)
优势:
- 支持跨平台(x86、ARM等)
- 标准化的加载机制
- 支持代码签名验证
3. UEFI Services(UEFI服务)
定义: UEFI Service是平台调用接口的集合,允许UEFI程序和操作系统调用。
分类:
- 启动服务(Boot Services,BS):在操作系统加载前可用
- 运行时服务(Runtime Services,RS):操作系统运行期间也可用
服务提供者:
- UEFI应用程序
- UEFI驱动
- UEFI OS Loader(操作系统加载器)
4. UEFI Protocol(UEFI协议)
定义: UEFI Protocol本质上是一种数据结构,它包含三个部分:
- 全局唯一标识符(GUID):128位唯一标识符
- 接口数据结构:定义协议的数据结构
- 服务:协议提供的函数接口
GUID格式:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
例如:EFI_BLOCK_IO_PROTOCOL_GUID
{964e5b22-6459-11d2-8e39-00a0c969723b}
Protocol示例:
c
// EFI_BLOCK_IO_PROTOCOL结构
typedef struct {
UINT64 Revision;
EFI_BLOCK_IO_MEDIA *Media;
EFI_BLOCK_RESET Reset;
EFI_BLOCK_READ ReadBlocks;
EFI_BLOCK_WRITE WriteBlocks;
EFI_BLOCK_FLUSH FlushBlocks;
} EFI_BLOCK_IO_PROTOCOL;
5. UEFI系统表(UEFI System Table)
定义: 所有UEFI镜像都会接收到一个指向UEFI系统表的指针,通过此系统表可以访问固件提供的UEFI Protocol。
系统表结构:
c
typedef struct {
EFI_TABLE_HEADER Hdr;
CHAR16 *FirmwareVendor;
UINT32 FirmwareRevision;
EFI_HANDLE ConsoleInHandle;
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
EFI_HANDLE ConsoleOutHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
EFI_HANDLE StandardErrorHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;
EFI_RUNTIME_SERVICES *RuntimeServices;
EFI_BOOT_SERVICES *BootServices;
UINTN NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigurationTable;
} EFI_SYSTEM_TABLE;
UEFI软件架构
架构层次图
┌─────────────────────────────────────────┐
│ 操作系统 (OS) │
├─────────────────────────────────────────┤
│ UEFI OS Loader │
│ (操作系统加载器) │
├─────────────────────────────────────────┤
│ UEFI Applications │
│ (UEFI应用程序) │
├─────────────────────────────────────────┤
│ UEFI Drivers │
│ (UEFI驱动) │
├─────────────────────────────────────────┤
│ Boot Services (BS) │
│ (启动服务) │
├─────────────────────────────────────────┤
│ Runtime Services (RS) │
│ (运行时服务) │
├─────────────────────────────────────────┤
│ UEFI Protocols │
│ (UEFI协议) │
├─────────────────────────────────────────┤
│ UEFI System Table │
│ (UEFI系统表) │
├─────────────────────────────────────────┤
│ Platform Hardware │
│ (平台硬件) │
└─────────────────────────────────────────┘
架构特点
图1-2 UEFI软件架构
UEFI为操作系统启动和启动前的运行状态提供了一个标准的环境,规范中详细规定了系统的控制权如何从启动前环境传递到操作系统。它所规定的与平台信息相关的启动服务(BS)和运行时服务(RT),都是与平台架构相分离的,即独立于平台上的抽象接口。这就是为什么发展到现在,UEFI可以支持X86、ARM、MIPS等各种CPU架构的原因。
服务生命周期
对Option ROM开发者来说:
┌─────────────────────────────────────────┐
│ Option ROM执行前 │
│ ✓ Boot Services可用 │
│ ✓ Runtime Services可用 │
├─────────────────────────────────────────┤
│ OS Loader加载后 │
│ ✓ Boot Services可用 │
│ ✓ Runtime Services可用 │
├─────────────────────────────────────────┤
│ ExitBootServices()调用后 │
│ ✗ Boot Services销毁 │
│ ✓ Runtime Services保留 │
└─────────────────────────────────────────┘
关键点:
- Option ROM运行于操作系统加载器执行前,启动服务和运行时服务都可以使用
- 从操作系统加载器被加载,到操作系统加载器执行
ExitBootServices()前的这段时间,是从UEFI环境向操作系统过渡的阶段,启动服务和运行时服务也同样可以使用 - 在操作系统加载器执行
ExitBootServices()之后,启动服务就完成了使命,其占用的资源会被回收,计算机系统进入UEFI Runtime阶段 - 此后,就只能使用运行时服务了,启动服务已经从系统中销毁
UEFI服务详解
启动服务(Boot Services)
启动服务提供的服务包括如下几项:
1. Event(事件)服务
功能: 允许程序进行异步操作,这是UEFI得以执行并发操作的基础。
事件类型:
- 定时器事件:基于时间的触发
- 等待事件:等待某个条件满足
- 通知函数:事件触发时调用的回调函数
示例代码:
c
EFI_EVENT TimerEvent;
// 创建定时器事件
gBS->CreateEvent(
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&TimerEvent
);
// 设置定时器(10秒)
gBS->SetTimer(TimerEvent, TimerRelative, 10000000);
// 等待事件
gBS->WaitForEvent(1, &TimerEvent, NULL);
2. Timer(定时器)服务
功能: 配合Event提供定时器功能。
定时器类型:
- TimerCancel:取消定时器
- TimerPeriodic:周期性定时器
- TimerRelative:相对时间定时器
- TimerAbsolute:绝对时间定时器
3. 内存管理
功能: 提供内存的分配和释放服务,管理系统的内存映射。
内存类型:
| 类型 | 说明 |
|---|---|
| EfiLoaderCode | 加载器代码 |
| EfiLoaderData | 加载器数据 |
| EfiBootServicesCode | 启动服务代码 |
| EfiBootServicesData | 启动服务数据 |
| EfiRuntimeServicesCode | 运行时服务代码 |
| EfiRuntimeServicesData | 运行时服务数据 |
| EfiConventionalMemory | 常规内存 |
| EfiUnusableMemory | 不可用内存 |
| EfiACPIReclaimMemory | ACPI回收内存 |
| EfiACPIMemoryNVS | ACPI非易失性内存 |
| EfiMemoryMappedIO | 内存映射IO |
| EfiMemoryMappedIOPortSpace | 内存映射IO端口空间 |
| EfiPalCode | PAL代码 |
内存分配示例:
c
EFI_PHYSICAL_ADDRESS Memory;
UINTN Pages = 10; // 分配10页(4KB/页)
// 分配内存
EFI_STATUS Status = gBS->AllocatePages(
AllocateAnyPages,
EfiBootServicesData,
Pages,
&Memory
);
// 使用内存...
// 释放内存
gBS->FreePages(Memory, Pages);
4. Protocol服务
功能: 提供安装和卸载Protocol的服务,以及查找、打开和关闭Protocol的服务。
主要函数:
InstallProtocolInterface():安装协议接口UninstallProtocolInterface():卸载协议接口LocateProtocol():定位协议OpenProtocol():打开协议CloseProtocol():关闭协议
示例:
c
EFI_BLOCK_IO_PROTOCOL *BlockIo;
// 定位Block IO协议
EFI_STATUS Status = gBS->LocateProtocol(
&gEfiBlockIoProtocolGuid,
NULL,
(VOID **)&BlockIo
);
if (EFI_ERROR(Status)) {
// 处理错误
}
5. Image服务
功能: 提供加载、卸载、启动和退出UEFI应用程序和驱动的服务。
主要函数:
LoadImage():加载镜像StartImage():启动镜像Exit():退出镜像UnloadImage():卸载镜像
6. 其他服务
包括设置看门狗定时器、延时、计算CRC校验值等,随着UEFI标准的发展,这类功能也在不断增加。
运行时服务(Runtime Services)
运行时服务提供的服务包括如下几项:
1. 系统变量服务
功能: 读取或者设置系统变量,比如设定启动项顺序。
变量命名空间:
EFI_GLOBAL_VARIABLE:全局变量EFI_IMAGE_SECURITY_DATABASE_GUID:安全数据库- 厂商自定义GUID:厂商特定变量
常用工具:
bash
# Linux下的efibootmgr工具
efibootmgr -v # 查看启动项
efibootmgr -o 0001,0002,0003 # 设置启动顺序
efibootmgr -c -L "Linux" -l /vmlinuz # 创建启动项
2. 时间服务
功能: 提供读取和设定系统时间的功能,也提供了读取和设定定时唤醒系统的功能。
时间结构:
c
typedef struct {
UINT16 Year; // 1900 - 9999
UINT8 Month; // 1 - 12
UINT8 Day; // 1 - 31
UINT8 Hour; // 0 - 23
UINT8 Minute; // 0 - 59
UINT8 Second; // 0 - 59
UINT8 Pad1;
UINT32 Nanosecond; // 0 - 999999999
INT16 TimeZone; // -1440 to 1440 or 2047
UINT8 Daylight;
UINT8 Pad2;
} EFI_TIME;
3. 内存虚拟地址服务
功能: 提供将内存的物理地址转换为虚拟地址的服务。
4. 其他服务
包括重启系统、更新BIOS(Capsules服务)、交换操作系统与BIOS的数据等各种服务。
UEFI vs Legacy BIOS对比
全面对比表
| 特性 | Legacy BIOS | UEFI BIOS |
|---|---|---|
| 运行模式 | 实模式(16位) | 保护模式(32/64位) |
| 可寻址内存 | 1MB | 4GB+(取决于架构) |
| 开发语言 | 主要使用汇编 | C/C++/Python |
| 代码大小 | 受ROM限制(通常<8MB) | 可存储在硬盘分区 |
| 启动速度 | 较慢 | 较快(并行初始化) |
| 图形界面 | 文本模式 | 支持高分辨率GUI |
| 鼠标支持 | 不支持 | 支持 |
| 安全启动 | 不支持 | 支持(Secure Boot) |
| GPT分区 | 不支持 | 原生支持 |
| 大硬盘支持 | 限制2TB | 支持超大容量 |
| 网络启动 | 有限支持 | 完整支持 |
| 驱动加载 | 静态链接 | 动态加载 |
| 模块化 | 差 | 优秀 |
| 跨平台 | x86/x64 | x86/x64/ARM/MIPS等 |
| 代码签名 | 不支持 | 支持 |
| 远程管理 | 不支持 | 支持 |
启动时间对比
Legacy BIOS启动流程:
上电 → POST → 硬件初始化 → 加载OS
时间:通常 30-60秒
UEFI启动流程:
上电 → SEC → PEI → DXE → BDS → TSL → OS
时间:通常 10-20秒(并行初始化)
内存使用对比
Legacy BIOS内存布局:
0x00000 - 0x9FFFF: 可用内存(640KB)
0xA0000 - 0xBFFFF: 视频内存
0xC0000 - 0xEFFFF: ROM扩展区
0xF0000 - 0xFFFFF: 系统BIOS
UEFI内存布局:
0x00000000 - 0x0009FFFF: 传统内存(可选)
0x000A0000 - 0x000FFFFF: 传统视频内存
0x00100000 - 0xFFFFFFFF: UEFI可用内存(4GB)
(64位系统可访问更大地址空间)
UEFI的优势
UEFI在发展过程中,打败其他新的BIOS架构,尤其是取代Legacy BIOS,得益于以下几大优势。
1. 开发效率高
技术栈对比:
| 方面 | Legacy BIOS | UEFI |
|---|---|---|
| 主要语言 | 汇编 | C/C++ |
| 辅助语言 | 无 | Python(工具链) |
| 代码复用 | 困难 | 容易 |
| 库支持 | 无 | 丰富(libjpeg等) |
| 调试工具 | 有限 | 完善 |
优势:
- 相比于Legacy BIOS采用汇编语言开发,UEFI采用了C语言、Python语言加少量汇编语言的混合模式编写,其中以C语言为主
- 实际上,UEFI的应用程序和驱动可以使用C++编写
- 它屏蔽了大量的底层硬件细节,为程序员提供了非常统一的调用接口
- 能很方便地在多种CPU架构间编写代码
- 很多代码可以重复使用,也可以直接使用大量的C语言库,比如libjpeg等,极大地提高了编程效率
代码示例对比:
Legacy BIOS(汇编):
assembly
; 显示字符 'A'
MOV AH, 0Eh ; 功能号
MOV AL, 'A' ; 字符
MOV BH, 0 ; 页号
INT 10h ; 调用BIOS中断
UEFI(C语言):
c
// 显示字符串
EFI_STATUS Status;
Status = gST->ConOut->OutputString(
gST->ConOut,
L"Hello, UEFI!\n"
);
2. 突破了ROM的容量限制
存储方式对比:
| 特性 | Legacy BIOS | UEFI |
|---|---|---|
| 存储位置 | Flash ROM | Flash ROM + 硬盘分区 |
| 容量限制 | 通常<8MB | 几乎无限制 |
| 文件系统 | 不支持 | 支持(FAT32等) |
| 可执行程序 | 受限 | 丰富 |
优势:
- UEFI可以解析文件系统,可以将硬盘的某个区域变为自己的存储空间
- 这样不但保证了充足的容量,而且可以直接执行一些常用的程序
- 比如操作系统的多引导、系统备份和恢复等
- 由于不受限于容量,UEFI可以吸取现代操作系统的设计理念,将功能分层化,不断添加各类新的功能,以满足未来计算机的发展
EFI系统分区(ESP):
GPT分区表:
├── EFI System Partition (ESP)
│ ├── \EFI\Boot\bootx64.efi
│ ├── \EFI\Microsoft\Boot\bootmgfw.efi
│ └── \EFI\Linux\grubx64.efi
├── Windows分区
├── Linux分区
└── 数据分区
3. 可扩展性好
模块化设计:
- UEFI采用模块化的设计,驱动可以动态加载
- 可以非常方便地添加对新硬件的支持
- UEFI的每个驱动,均有唯一的GUID标识
- 使得UEFI系统的升级变得安全而简单
应用场景:
- 各种备份和诊断功能,都可以在UEFI下实现
- 配合UEFI提供的网络功能,可以远程对主板进行故障诊断
- 或者进行BIOS更新
驱动加载示例:
c
// 动态加载驱动
EFI_HANDLE ImageHandle;
EFI_STATUS Status;
Status = gBS->LoadImage(
FALSE,
gImageHandle,
DevicePath,
NULL,
0,
&ImageHandle
);
if (!EFI_ERROR(Status)) {
gBS->StartImage(ImageHandle, NULL, NULL);
}
4. 系统性能高
运行模式对比:
| 特性 | Legacy BIOS | UEFI |
|---|---|---|
| CPU模式 | 16位实模式 | 32/64位保护模式 |
| 内存访问 | 受限(1MB) | 完整访问 |
| 并行处理 | 不支持 | 支持(异步操作) |
| CPU利用率 | 低 | 高 |
优势:
- Legacy BIOS运行于16位实模式,而UEFI是直接运行于保护模式下的,可以充分利用CPU和内存
- UEFI提供了异步操作机制,这类似于操作系统下的多进程模式
- 可充分提高CPU的利用率,减少等待时间
性能提升:
启动时间对比:
Legacy BIOS: 30-60秒
UEFI: 10-20秒(提升50-70%)
硬件初始化:
Legacy BIOS: 串行初始化
UEFI: 并行初始化(多核CPU优势)
5. 安全性高
安全机制对比:
| 安全特性 | Legacy BIOS | UEFI |
|---|---|---|
| 代码签名 | 不支持 | 支持 |
| 安全启动 | 不支持 | 支持(Secure Boot) |
| 可信链 | 无 | 完整可信链 |
| 恶意软件防护 | 弱 | 强 |
Secure Boot流程:
1. 主板出厂时内置可信公钥(CA证书)
↓
2. UEFI固件验证启动加载器签名
↓
3. 启动加载器验证内核签名
↓
4. 内核验证驱动签名
↓
5. 形成完整的可信链
优势:
- UEFI建立了完整的可信链机制
- 主板出厂时,可以内置一些可靠的公钥
- 这些公钥一般由比较权威的CA机构发布,比如VeriSign等
- 当系统的安全启动功能打开后,UEFI在执行应用程序和驱动前,会检测应用程序和驱动的证书
- 只有证书被内置公钥认证通过,应用程序和驱动才能被执行
- UEFI的安全机制提高了操作系统启动过程的安全性,能有效阻止恶意软件
6. 易用性好
用户界面对比:
| 特性 | Legacy BIOS | UEFI |
|---|---|---|
| 显示模式 | 文本模式(80x25) | 高分辨率图形 |
| 颜色支持 | 16色 | 真彩色 |
| 鼠标支持 | 不支持 | 支持 |
| 界面类型 | 命令行 | GUI图形界面 |
| 多语言 | 有限 | 完整支持 |
优势:
- 从UEFI的设计可以看出,它实际上是一个简化了的操作系统
- 它支持高分辨率的彩色显示,可以运行GUI(图形用户界面)
- 支持鼠标的使用
- 这使得开发人员可以构建非常友好的用户交互界面
- 用户能更容易、更方便地调节各种参数
UEFI Setup界面特点:
- 图形化菜单
- 鼠标操作
- 实时预览
- 多语言支持
- 搜索功能
- 帮助提示
总结
UEFI作为现代计算机固件的标准,相比Legacy BIOS在多个方面实现了革命性的突破:
- 开发效率:从汇编到C/C++,大幅提升开发效率
- 存储容量:突破ROM限制,支持文件系统
- 扩展性:模块化设计,动态加载驱动
- 性能:保护模式运行,并行初始化
- 安全性:完整的可信链和安全启动机制
- 易用性:图形界面,鼠标操作
这些优势使得UEFI成为现代计算机固件的标准选择,为未来的计算机发展奠定了坚实的基础。在下一篇文章中,我们将深入探讨UEFI的启动过程和开发环境搭建。
参考资料
- UEFI Specification 2.7+
- 《UEFI编程实践》- 罗冰
- Intel Platform Initialization Specification
- TianoCore EDK II Documentation
本文基于《UEFI编程实践》读书笔记整理,旨在帮助读者深入理解UEFI的架构设计和优势。