BIOS BDS 阶段详解
BIOS BDS 阶段摘要 BDS(Boot Device Selection)是UEFI固件启动流程中的关键阶段,负责设备初始化、引导策略执行和操作系统加载。主要功能包括: 控制台初始化 - 设置键盘、显示等输入输出设备 引导管理 - 执行Boot Manager,处理Boot Order顺序 设备枚举 - 发现所有可引导设备(本地/网络/USB) 安全验证 - 实施安全启动策略检查 驱动加载
BIOS BDS 阶段详解
目录
- BDS 阶段概述
- BDS 在 UEFI 启动流程中的位置
- DXE 到 BDS 的移交
- 控制台初始化
- 引导策略与 Boot Manager
- UEFI 引导选项
- 固件启动菜单(Firmware Boot Menu)
- 引导设备枚举与排序
- 顺序引导(Boot Order)处理流程
- 网络引导(PXE/HTTP Boot)
- 安全启动在 BDS 阶段
- UEFI 驱动程序加载与管理
- 固件配置界面(Setup)
- Boot Manager 内部数据结构
- Boot Manager 协议与编程接口
- 退出启动服务(ExitBootServices)
- BDS 与操作系统引导加载程序交接
- EDK2 BDS 实现分析
- 常见 BDS 问题与调试
- 参考资料
1. BDS 阶段概述
1.1 什么是 BDS
BDS(Boot Device Selection)是 UEFI 固件启动流程中的第三个主要阶段,紧随 DXE(Driver Execution Environment)之后。BDS 的职责是:
- 初始化控制台设备(键盘、鼠标、显示输出)
- 加载并执行平台特定的策略(固件菜单、启动策略)
- 遍历引导选项列表(Boot Order),尝试引导操作系统
- 管理 UEFI 驱动程序(可选驱动加载)
- 实现安全启动策略验证
UEFI 固件阶段:
SEC ──→ PEI ──→ DXE ──→ BDS ──→ TSL ──→ RT
│ │ │ │ │ │
安全 预初始 驱动执 引导设 短暂 运行时
阶段 化阶段 行环境 备选择 系统
│
├── DXE: 初始化所有驱动程序
│ 建立 UEFI 系统表
│ 提供所有 Boot Services
│
└── BDS: 控制台初始化
Boot Manager 执行
引导操作系统
1.2 BDS 的设计目标
| 目标 | 说明 |
|---|---|
| 用户交互 | 提供固件设置界面和启动菜单 |
| 自动引导 | 按照配置的引导顺序尝试每个设备 |
| 策略驱动 | 引导选择策略由 NVRAM 中的配置决定,而非硬编码 |
| 设备发现 | 发现所有可引导设备(本地磁盘、网络、USB) |
| 驱动程序加载 | 按需加载可选 UEFI 驱动程序 |
| 安全策略执行 | 在引导前验证加载项的签名 |
1.3 BDS 阶段的关键特征
- 完整内存可用:DRAM 已初始化完毕,可随意分配
- 完整 UEFI 系统表:
BootServices、RuntimeServices、System Table全部就绪 - UEFI 协议可用:文件系统、图形输出、块 IO 等协议
- 全彩色图形输出:UEFI GOP(Graphics Output Protocol)
- 用户输入:键盘、鼠标、触摸输入
- NVRAM 可读写:引导配置持久化存储
2. BDS 在 UEFI 启动流程中的位置
2.1 完整的 UEFI 启动阶段时序
安全(Security Phase)
│ [~0-50ms]
├── 处理 Reset Vector
├── 建立 Cache-as-RAM (CAR)
└── 验证 PEI Core 签名
│
▼
预初始化(Pre-EFI Initialization)
│ [~50-300ms]
├── 初始化系统内存 (DRAM)
├── 创建 HOB 列表
└── 移交控制权给 DXE
│
▼
驱动执行环境(Driver Execution Environment)
│ [~100-1000ms]
├── DXE Core 初始化
├── 分派所有 DXE 驱动程序
│ ├── 芯片组驱动
│ ├── PCI 总线枚举
│ ├── ACPI 表生成
│ ├── SMBIOS 表生成
│ ├── 控制台驱动 (Serial, GOP)
│ └── 存储驱动 (SATA, NVMe, USB)
├── 创建 UEFI 系统表
└── 安装 DXE 就绪通知 → 触发 BDS
│
▼
引导设备选择(Boot Device Selection) ← 本文重点
│ [~100ms - 30s+]
├── 控制台初始化 (ConIn, ConOut, StdErr)
├── 执行 Boot Manager
├── 显示固件菜单(如用户按键)
├── 枚举引导设备
├── 按 BootOrder 遍历引导选项
└── 启动引导加载程序
│
▼
短暂系统加载(Transient System Load)
│
├── 引导加载程序执行
├── ExitBootServices() 调用
└── OS 接管
│
▼
运行时(Runtime)
[时间轴: 上电 → OS 加载, 总计 1-30 秒]
2.2 DXE → BDS 移交机制
DXE 阶段完成后,通过 EFI_EVENT_GROUP_READY_TO_BOOT 事件触发 BDS 入口:
// DXE Core 在完成所有驱动分派后触发 BDS 的机制
VOID
DxeCoreComplete(
VOID
)
{
//
// 1. 所有 DXE 驱动程序已分派完毕
//
//
// 2. 触发 ReadyToBoot 事件组
//
// 任何注册了这个事件的回调都会被执行
// BDS 入口通常会监听此事件
//
gBS->SignalEvent(gEfiEventReadyToBoot);
//
// 3. 调用 BDS 入口 (由平台 BDS 库实现)
//
// BDS 从这个时候开始接管控制权
//
PlatformBds();
//
// BDS 不应该返回 (如果 BDS 返回了, 进入死循环)
//
CpuDeadLoop();
}
2.3 UEFI 系统表 —— BDS 的运行时环境
BDS 阶段运行时,以下 UEFI 基础设施已完全就绪:
UEFI 系统表 (gST): UEFI 启动服务 (gBS):
┌──────────────────────┐ ┌──────────────────────┐
│ Hdr │ │ Hdr │
│ FirmwareVendor │ │ RaiseTPL │
│ FirmwareRevision │ │ RestoreTPL │
│ ConsoleInHandle │◄── ConIn │ AllocatePages │
│ ConIn │◄── ────┘ │ FreePages │
│ ConsoleOutHandle │◄── ConOut │ AllocatePool │
│ ConOut │◄── ────┘ │ FreePool │
│ StandardErrorHandle │◄── StdErr │ CreateEvent │
│ StdErr │◄── ────┘ │ SetTimer │
│ RuntimeServices │ │ WaitForEvent │
│ BootServices │ │ SignalEvent │
│ NumberOfTableEntries │ │ CloseEvent │
│ ConfigurationTable[] │ │ InstallProtocolInterface │
└──────────────────────┘ │ ConnectController │
│ DisconnectController │
UEFI 运行时服务 (gRT): │ LoadImage │
┌──────────────────────┐ │ StartImage │
│ GetTime │ │ Exit │
│ SetTime │ │ ExitBootServices │
│ GetVariable │ │ ... │
│ SetVariable │ └──────────────────────┘
│ GetNextVariableName │
│ SetVirtualAddressMap │ NVRAM 变量:
│ ... │ BootOrder, Boot####,
└──────────────────────┘ DriverOrder, Driver####,
Timeout, PlatformLang,
ConIn, ConOut, StdErr...
3. DXE 到 BDS 的移交
3.1 BDS 入口函数
BDS 阶段的入口通常由 PlatformBds() 或 BdsEntry() 函数开始:
// EDK2 中的 BDS 入口 (MdeModulePkg/Universal/BdsDxe/BdsEntry.c)
EFI_STATUS
EFIAPI
BdsEntry(
IN EFI_BOOT_SERVICES *BootServices
)
{
EFI_STATUS Status;
DEBUG((EFI_D_INFO, "[BDS] BDS Entry Point\n"));
// ═══════════════════════════════════════════
// 阶段 1: BDS 核心初始化
// ═══════════════════════════════════════════
// 1.1 初始化 BDS 内部数据结构
InitializeBdsData();
// 1.2 初始化控制台
//
// ConIn = 输入控制台 (键盘等)
// ConOut = 输出控制台 (显示设备)
// StdErr = 标准错误 (串口/显示)
//
Status = InitializeConsole();
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "[BDS] Console init failed: %r\n", Status));
}
// ═══════════════════════════════════════════
// 阶段 2: 设置默认引导选项
// ═══════════════════════════════════════════
// 确保至少有一个引导选项存在
// 如果 NVRAM 为空,创建默认引导选项
Status = SetDefaultBootCfgIfNeeded();
// ═══════════════════════════════════════════
// 阶段 3: 处理按键和超时
// ═══════════════════════════════════════════
// 3.1 检查是否用户要求进入设置
if (CheckUserRequestedSetup()) {
EnterSetupMenu(); // 进入固件设置界面
}
// 3.2 检查是否用户要求进入启动菜单
if (CheckUserRequestedBootMenu()) {
EnterBootMenu(); // 显示引导设备菜单
}
// 3.3 等待超时或用户按键
//
// 显示 "Press F2 for Setup, F12 for Boot Menu..."
// 在 Timeout 秒内检查按键
//
WaitForBootTimeout();
// ═══════════════════════════════════════════
// 阶段 4: 执行引导
// ═══════════════════════════════════════════
// 4.1 调用 Boot Manager 引导
Status = BdsBootManager();
// 4.2 如果所有引导选项都失败
if (EFI_ERROR(Status)) {
// 显示 "No bootable device" 错误
DisplayNoBootableDevice();
}
// ═══════════════════════════════════════════
// 阶段 5: 等待用户干预
// ═══════════════════════════════════════════
// 如果引导失败,进入等待循环
// 用户可以选择进入设置或重试
while (TRUE) {
// 等待用户按键
// F2 → 进入设置
// Enter → 重试引导
// ...
}
}
3.2 BDS 核心初始化
// BDS 内部数据结构初始化
typedef struct {
//
// 控制台句柄
//
EFI_HANDLE ConsoleInHandle;
EFI_HANDLE ConsoleOutHandle;
EFI_HANDLE StandardErrorHandle;
//
// 控制台设备路径
//
EFI_DEVICE_PATH_PROTOCOL *ConInDevicePath;
EFI_DEVICE_PATH_PROTOCOL *ConOutDevicePath;
EFI_DEVICE_PATH_PROTOCOL *StdErrDevicePath;
//
// 引导选项列表
//
LIST_ENTRY BootOptionList; // 所有 Boot#### 选项
LIST_ENTRY DriverOptionList; // 所有 Driver#### 选项
//
// 当前引导状态
//
BOOLEAN BootAttempted;
BOOLEAN SetupEntered;
BOOLEAN BootMenuEntered;
//
// 时间配置
//
UINT16 BootTimeOut; // 超时秒数 (从 NVRAM 读取)
BOOLEAN BootMenuPresent;
} BDS_PRIVATE_DATA;
3.3 超时处理
// BDS 超时处理逻辑
#define DEFAULT_BOOT_TIMEOUT 5 // 默认 5 秒
VOID
WaitForBootTimeout(
VOID
)
{
EFI_STATUS Status;
UINT16 Timeout;
UINTN Index;
EFI_EVENT TimerEvent;
EFI_INPUT_KEY Key;
// 1. 从 NVRAM 读取 Timeout 变量
UINTN DataSize = sizeof(Timeout);
Status = gRT->GetVariable(
L"Timeout",
&gEfiGlobalVariableGuid,
NULL,
&DataSize,
&Timeout
);
if (EFI_ERROR(Status)) {
Timeout = DEFAULT_BOOT_TIMEOUT;
}
// 2. 如果 Timeout = 0, 立即引导 (无等待)
if (Timeout == 0) {
return;
}
// 3. 如果 Timeout = 0xFFFF, 无限等待用户
if (Timeout == 0xFFFF) {
WaitForeverForUserInput();
return;
}
// 4. 创建定时器事件
Status = gBS->CreateEvent(
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&TimerEvent
);
// 设置定时器
gBS->SetTimer(TimerEvent, TimerRelative, Timeout * ONE_SECOND);
// 显示倒计时信息
Print(L"Press F2 for Setup, F12 for Boot Menu in %d seconds...", Timeout);
// 5. 等待定时器或按键
while (TRUE) {
UINTN WaitCount = 1;
EFI_EVENT WaitList[] = { TimerEvent, gST->ConIn->WaitForKey };
Status = gBS->WaitForEvent(2, WaitList, &Index);
if (Index == 0) {
// 定时器超时 → 自动引导
break;
} else if (Index == 1) {
// 用户按键
gST->ConIn->ReadKeyStroke(gST->ConIn, &Key);
if (Key.ScanCode == SCAN_F2) {
EnterSetupMenu();
} else if (Key.ScanCode == SCAN_F12) {
EnterBootMenu();
} else {
// 任何其他按键 → 立即引导
break;
}
}
}
gBS->CloseEvent(TimerEvent);
}
4. 控制台初始化
4.1 控制台协议
UEFI 定义了三种标准控制台:
| 控制台 | NVRAM 变量 | 协议 | 用途 |
|---|---|---|---|
| ConIn (控制台输入) | ConIn |
EFI_SIMPLE_TEXT_INPUT_PROTOCOL |
键盘、鼠标输入 |
| ConOut (控制台输出) | ConOut |
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL |
文本屏幕输出 |
| StdErr (标准错误) | ErrOut |
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL |
错误信息输出 |
4.2 控制台初始化流程
// BDS 控制台初始化
EFI_STATUS
InitializeConsole(
VOID
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *ConInDevicePath;
EFI_DEVICE_PATH_PROTOCOL *ConOutDevicePath;
EFI_DEVICE_PATH_PROTOCOL *ErrOutDevicePath;
// ═══════════════════════════════════════════
// 1. 从 NVRAM 读取控制台设备路径配置
// ═══════════════════════════════════════════
//
// ConIn 变量存储了输入控制台的设备路径列表
// 例如:
// "UsbKeyboard(0x1234,0x5678)/UsbKeyboard(...)"
// 或 "Serial(9600,8,N,1)"
//
ConInDevicePath = GetConsoleDevicePath(L"ConIn");
ConOutDevicePath = GetConsoleDevicePath(L"ConOut");
ErrOutDevicePath = GetConsoleDevicePath(L"ErrOut");
// ═══════════════════════════════════════════
// 2. 连接控制台设备
// ═══════════════════════════════════════════
//
// 让 UEFI 驱动框架连接这些设备路径
// 这会导致对应的驱动程序绑定到设备上
//
// 连接输入控制台 (键盘等)
if (ConInDevicePath != NULL) {
Status = gBS->ConnectController(ConInDevicePath, NULL, NULL, TRUE);
if (EFI_ERROR(Status)) {
// 尝试使用默认控制台 (PS/2 键盘或串口)
Status = ConnectDefaultConsoleIn();
}
}
// 连接输出控制台 (GOP 显示设备)
if (ConOutDevicePath != NULL) {
Status = gBS->ConnectController(ConOutDevicePath, NULL, NULL, TRUE);
if (EFI_ERROR(Status)) {
Status = ConnectDefaultConsoleOut();
}
}
// 连接标准错误控制台
if (ErrOutDevicePath != NULL) {
gBS->ConnectController(ErrOutDevicePath, NULL, NULL, TRUE);
}
// ═══════════════════════════════════════════
// 3. 如果 NVRAM 没有配置, 自动检测控制台
// ═══════════════════════════════════════════
if (ConInDevicePath == NULL || ConOutDevicePath == NULL) {
Status = AutoDetectConsole();
}
// ═══════════════════════════════════════════
// 4. 更新 UEFI 系统表中的控制台指针
// ═══════════════════════════════════════════
//
// gST->ConIn, gST->ConOut, gST->StdErr
// 现在指向实际的控制台设备协议
//
DEBUG((
EFI_D_INFO,
"[BDS] Console initialized: ConIn=%p, ConOut=%p, StdErr=%p\n",
gST->ConIn, gST->ConOut, gST->StdErr
));
// ═══════════════════════════════════════════
// 5. 显示启动徽标 (Splash Screen / OEM Logo)
// ═══════════════════════════════════════════
ShowStartupLogo();
return EFI_SUCCESS;
}
4.3 图形输出初始化
现代 UEFI 固件使用 GOP(Graphics Output Protocol)进行图形显示:
// GOP 初始化与启动徽标显示
EFI_STATUS
InitializeGraphicsConsole(
VOID
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
// 1. 查找所有支持 GOP 的设备
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiGraphicsOutputProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR(Status) || HandleCount == 0) {
// 没有 GOP 设备, 使用文本模式 (UGA 或串口)
return EFI_UNSUPPORTED;
}
// 2. 使用第一个 GOP 设备
Status = gBS->HandleProtocol(
HandleBuffer[0],
&gEfiGraphicsOutputProtocolGuid,
(VOID **)&Gop
);
if (EFI_ERROR(Status)) {
return Status;
}
// 3. 查询当前图形模式信息
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
UINTN SizeOfInfo;
Status = Gop->QueryMode(Gop, Gop->Mode->Mode, &SizeOfInfo, &Info);
if (EFI_ERROR(Status)) {
// 查询失败, 使用当前模式
Info = Gop->Mode->Info;
}
DEBUG((
EFI_D_INFO,
"[BDS] GOP Mode: %dx%d, Format=%d\n",
Info->HorizontalResolution,
Info->VerticalResolution,
Info->PixelFormat
));
// 4. 设置文本输出模式对应到 GOP
// UEFI Simple Text Output 在 GOP 上使用固定字体
// 通常使用 80x25 或 100x31 字符模式
// 5. 显示启动画面 Logo
DisplayOemLogo(Gop, Info);
gBS->FreePool(HandleBuffer);
return EFI_SUCCESS;
}
// 显示 OEM 启动徽标 (BMP 或 JPEG)
EFI_STATUS
DisplayOemLogo(
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
IN EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo
)
{
VOID *LogoData;
UINTN LogoSize;
EFI_STATUS Status;
// 1. 从 FV 或 NVRAM 中读取徽标图像
Status = GetSectionFromFv(
&gOemLogoFileGuid,
EFI_SECTION_RAW,
0,
&LogoData,
&LogoSize
);
if (EFI_ERROR(Status)) {
return Status;
}
// 2. 解码并渲染徽标 (居中显示)
//
// 处理: BMP → 转换为 GOP 像素格式
// JPEG/PNG → 使用图像解码协议
//
Status = RenderImageToGop(
Gop,
LogoData, LogoSize,
(ModeInfo->HorizontalResolution - LogoWidth) / 2,
(ModeInfo->VerticalResolution - LogoHeight) / 2
);
FreePool(LogoData);
return Status;
}
5. 引导策略与 Boot Manager
5.1 Boot Manager 的角色
UEFI Boot Manager 是 BDS 阶段的核心组件,负责执行引导策略:
Boot Manager 逻辑:
BDS Entry
│
├── 1. 处理平台特定初始化
│ ├── 芯片组/平台后初始化
│ └── 连接平台设备
│
├── 2. 加载驱动程序选项 (Driver####)
│ ├── 从 NVRAM 读取 DriverOrder
│ ├── 按顺序加载每个 Driver#### 选项
│ └── 连接驱动对应的设备
│
├── 3. 处理固件设置入口
│ ├── 检查 "F2" 按键
│ ├── 检查 "Del" 按键
│ ├── 检查 Setup 标志变量
│ └── 如果触发 → 进入固件设置界面
│
├── 4. 执行引导 (Boot Manager)
│ ├── 从 NVRAM 读取 BootOrder
│ ├── 对每个 Boot####:
│ │ ├── 连接设备路径
│ │ ├── 验证安全启动签名
│ │ ├── 尝试加载 EFI 应用程序
│ │ └── 如果成功 → 启动
│ └── 如果全部失败 → 错误处理
│
└── 5. 引导失败恢复
└── 等待用户干预
5.2 UEFI Boot Manager 规范
根据 UEFI 规范,Boot Manager 的定义如下:
The UEFI Boot Manager is a firmware policy engine that is
active after the DXE phase is complete. The Boot Manager
determines which UEFI drivers and UEFI applications should
be loaded and executed. The Boot Manager’s behavior is
controlled by a set of NVRAM variables.— UEFI Specification, Chapter 3: Boot Manager
5.3 Boot Manager 执行循环
// Boot Manager 主循环 (简化版)
EFI_STATUS
EFIAPI
BdsBootManager(
VOID
)
{
EFI_STATUS Status;
UINT16 *BootOrder;
UINTN BootOrderSize;
UINTN BootOrderCount;
BOOLEAN BootSucceeded;
BootSucceeded = FALSE;
// ═══════════════════════════════════════════
// 1. 从 NVRAM 读取 BootOrder 变量
// ═══════════════════════════════════════════
//
// BootOrder 是一个 UINT16 数组, 例如:
// { 0x0001, 0x0003, 0x0000, 0x0002 }
// 表示引导顺序为 Boot0001 → Boot0003 → Boot0000 → Boot0002
//
BootOrder = BdsReadBootOrder(&BootOrderSize);
BootOrderCount = BootOrderSize / sizeof(UINT16);
if (BootOrderCount == 0) {
DEBUG((EFI_D_ERROR, "[BDS] BootOrder is empty!\n"));
return EFI_NOT_FOUND;
}
// ═══════════════════════════════════════════
// 2. 按顺序遍历所有引导选项
// ═══════════════════════════════════════════
for (UINTN Index = 0; Index < BootOrderCount; Index++) {
UINT16 BootCurrent = BootOrder[Index];
EFI_HANDLE ImageHandle;
BDS_BOOT_OPTION *BootOption;
DEBUG((EFI_D_INFO, "[BDS] Trying Boot%04x...\n", BootCurrent));
// 2.1 读取 Boot#### 变量
Status = BdsReadBootOption(BootCurrent, &BootOption);
if (EFI_ERROR(Status)) {
continue;
}
// 2.2 连接设备路径
//
// Boot#### 包含了设备路径, 例如:
// "PciRoot(0)/Pci(0x1F,0x2)/Sata(0x0,0x0)/HD(1,GPT,...)/\EFI\BOOT\BOOTX64.EFI"
//
// 需要连接设备路径上的所有控制器
//
Status = gBS->ConnectController(BootOption->DeviceHandle, NULL, NULL, TRUE);
if (EFI_ERROR(Status)) {
// 尝试枚举所有设备
BdsConnectAll();
}
// 2.3 安全启动验证
//
// 如果安全启动已启用, 验证 BootOption 的签名
//
if (IsSecureBootEnabled()) {
Status = SecureBootVerify(BootOption);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "[BDS] Secure Boot rejects Boot%04x\n", BootCurrent));
continue;
}
}
// 2.4 加载并启动 EFI 映像
//
// LoadImage: 从设备读取 EFI 映像到内存
// StartImage: 执行 EFI 映像
//
Status = gBS->LoadImage(
FALSE, // BootPolicy = FALSE (使用设备路径)
gImageHandle, // 调用者映像句柄
BootOption->DevicePath, // EFI 映像路径
NULL, // 源缓冲区 (NULL = 从设备读)
0, // 源大小 (0 = 自动)
&ImageHandle // [输出] 加载的映像句柄
);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_WARN, "[BDS] LoadImage failed: %r\n", Status));
continue;
}
// 2.5 启动映像
Status = gBS->StartImage(
ImageHandle,
&BootOption->ExitDataSize,
&BootOption->ExitData
);
if (Status == EFI_SUCCESS) {
BootSucceeded = TRUE;
break;
}
// 如果映像返回错误, 尝试下一个
DEBUG((EFI_D_WARN, "[BDS] StartImage returned: %r\n", Status));
}
if (!BootSucceeded) {
DEBUG((EFI_D_ERROR, "[BDS] No bootable device found!\n"));
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
6. UEFI 引导选项
6.1 引导选项结构
UEFI 引导选项存储在 NVRAM 变量中,变量名为 Boot####(十六进制数字):
// Boot#### 变量中存储的数据结构
//
// 变量名: "Boot0001", "Boot0002", ..., "Boot####"
// GUID: EFI_GLOBAL_VARIABLE (8BE4DF61-93CA-11d2-AA0D-00E098032B8C)
typedef struct {
//
// 引导选项属性标志
//
UINT32 Attributes;
// Bits:
// 0x01 = LOAD_OPTION_ACTIVE (选项已激活)
// 0x02 = LOAD_OPTION_FORCE_RECONNECT (强制重新连接设备)
// 0x04 = LOAD_OPTION_HIDDEN (在启动菜单中隐藏)
// 0x08 = LOAD_OPTION_CATEGORY (类别: 0=引导, 1=应用程序)
//
// 设备路径列表长度
//
UINT16 FilePathListLength;
//
// 描述 (以 NUL 结尾的 UCS-2 字符串)
//
// CHAR16 Description[];
//
// 设备路径列表
//
// EFI_DEVICE_PATH_PROTOCOL FilePathList[];
//
// 可选数据
//
// UINT8 OptionalData[];
//
} EFI_LOAD_OPTION;
6.2 从 NVRAM 读取引导选项
// 读取 Boot#### 引导选项
EFI_STATUS
BdsReadBootOption(
IN UINT16 BootCurrent, // 如 0x0001
OUT BDS_BOOT_OPTION **BootOption
)
{
EFI_STATUS Status;
CHAR16 VariableName[20];
UINT8 *VariableData;
UINTN DataSize;
EFI_LOAD_OPTION *LoadOption;
BDS_BOOT_OPTION *Option;
// 1. 构造变量名: "Boot0001"
UnicodeSPrint(VariableName, sizeof(VariableName), L"Boot%04x", BootCurrent);
// 2. 读取 NVRAM 变量
DataSize = 0;
Status = gRT->GetVariable(
VariableName,
&gEfiGlobalVariableGuid,
NULL,
&DataSize,
NULL
);
if (Status != EFI_BUFFER_TOO_SMALL) {
return EFI_NOT_FOUND;
}
VariableData = (UINT8 *)AllocatePool(DataSize);
Status = gRT->GetVariable(
VariableName,
&gEfiGlobalVariableGuid,
NULL,
&DataSize,
VariableData
);
if (EFI_ERROR(Status)) {
FreePool(VariableData);
return Status;
}
// 3. 解析 EFI_LOAD_OPTION
LoadOption = (EFI_LOAD_OPTION *)VariableData;
Option = (BDS_BOOT_OPTION *)AllocateZeroPool(sizeof(BDS_BOOT_OPTION));
Option->OptionNumber = BootCurrent;
Option->Attributes = LoadOption->Attributes;
Option->Description = AllocateCopyPool(
StrSize((CHAR16 *)(LoadOption + 1)),
(CHAR16 *)(LoadOption + 1)
);
// 设备路径在描述字符串之后
Option->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)
((UINT8 *)(LoadOption + 1) + StrSize(Option->Description));
// 可选数据在设备路径之后
Option->OptionalData = NULL;
UINTN FilePathLen = LoadOption->FilePathListLength;
UINTN TotalLen = sizeof(EFI_LOAD_OPTION) + StrSize(Option->Description)
+ FilePathLen;
if (DataSize > TotalLen) {
Option->OptionalData = VariableData + TotalLen;
Option->OptionalDataSize = DataSize - TotalLen;
}
// 4. 检查活动状态
Option->IsActive = (LoadOption->Attributes & LOAD_OPTION_ACTIVE) != 0;
*BootOption = Option;
return EFI_SUCCESS;
}
6.3 引导选项示例
引导选项在 NVRAM 中的典型布局:
BootOrder = { 0001, 0003, 0000, 0002 }
▲
└── 引导顺序: Boot0001 → Boot0003 → Boot0000 → Boot0002
─────────────────────────────────────────────────────────────────────
Variable: Boot0001
Attributes: 0x01 (ACTIVE)
Description: "Windows Boot Manager"
DevicePath: HD(1,GPT,1234-5678,...)/\EFI\Microsoft\Boot\bootmgfw.efi
OptionalData: (无)
Variable: Boot0003
Attributes: 0x01 (ACTIVE)
Description: "ubuntu"
DevicePath: HD(2,GPT,ABCD-EF01,...)/\EFI\ubuntu\shimx64.efi
OptionalData: (无)
Variable: Boot0000
Attributes: 0x01 (ACTIVE)
Description: "UEFI: SanDisk Extreme Pro"
DevicePath: USB(0x0781,0x5583)/HD(1,MBR,...)/\EFI\BOOT\BOOTX64.EFI
Variable: Boot0002
Attributes: 0x01 (ACTIVE)
Description: "UEFI PXE IPv4 Intel(R) Ethernet"
DevicePath: MAC(001122334455,0x1)/IPv4(0.0.0.0)
6.4 默认引导选项
系统首次启动或 NVRAM 被清空时,固件会自动创建默认引导选项:
// 创建默认引导选项
EFI_STATUS
SetDefaultBootCfgIfNeeded(
VOID
)
{
EFI_STATUS Status;
UINT16 *BootOrder;
UINTN DataSize;
// 1. 检查 BootOrder 是否已存在
DataSize = 0;
Status = gRT->GetVariable(
L"BootOrder",
&gEfiGlobalVariableGuid,
NULL,
&DataSize,
NULL
);
if (Status != EFI_BUFFER_TOO_SMALL) {
// BootOrder 不存在 → 创建默认配置
// 2. 按照标准 UEFI 规范创建默认引导选项
//
// 顺序:
// 1. 可移动媒体引导 (Removable Media)
// 2. UEFI 壳 (UEFI Shell)
// 3. 网络引导 (PXE / HTTP)
//
// 具体实现: 枚举所有支持 Simple File System 的
// 设备和网络设备, 创建对应的 Boot####
//
Status = EnumerateAndCreateDefaultBootOptions();
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "[BDS] Failed to create default boot options\n"));
}
}
return EFI_SUCCESS;
}
7. 固件启动菜单(Firmware Boot Menu)
7.1 启动菜单调用方式
用户进入固件菜单的常见方式:
1. 按键进入
开机时按特定键:
F2 → 固件设置 (Setup)
Del → 固件设置 (Setup)
F12 → 引导设备菜单 (Boot Menu)
F10 → 引导设备菜单 (某些平台)
Esc → 引导菜单 (某些平台)
F9 → 系统诊断
2. 从 OS 重启进入
Windows: "高级启动" → "UEFI 固件设置"
Linux: systemctl reboot --firmware-setup
macOS: Option 键开机
3. 启动失败自动进入
多次引导失败 → 自动显示启动菜单
7.2 引导设备菜单实现
// 引导设备菜单 (Boot Menu)
EFI_STATUS
EnterBootMenu(
VOID
)
{
EFI_STATUS Status;
UINTN SelectedIndex;
BDS_BOOT_OPTION **MenuOptions;
UINTN OptionCount;
DEBUG((EFI_D_INFO, "[BDS] Entering Boot Menu\n"));
// 1. 清除屏幕
gST->ConOut->ClearScreen(gST->ConOut);
// 2. 收集所有可引导的选项
Status = CollectBootMenuOptions(&MenuOptions, &OptionCount);
if (EFI_ERROR(Status)) {
return Status;
}
// 3. 显示菜单界面
while (TRUE) {
// 3.1 显示标题
Print(L"\n Please select boot device:\n\n");
// 3.2 列出所有选项
for (UINTN i = 0; i < OptionCount; i++) {
Print(L" %d. %s\n", i + 1, MenuOptions[i]->Description);
}
Print(L" \n");
Print(L" Q. Quit and continue boot\n");
// 3.3 等待用户选择
EFI_INPUT_KEY Key;
Status = gST->ConIn->ReadKeyStroke(gST->ConIn, &Key);
if (Key.UnicodeChar >= '1' && Key.UnicodeChar <= '9') {
SelectedIndex = Key.UnicodeChar - '1';
if (SelectedIndex < OptionCount) {
// 启动选中的引导选项
Status = BootFromOption(MenuOptions[SelectedIndex]);
break;
}
} else if (Key.UnicodeChar == 'q' || Key.UnicodeChar == 'Q') {
// 退出菜单, 继续正常引导
break;
}
}
// 4. 释放资源
for (UINTN i = 0; i < OptionCount; i++) {
FreePool(MenuOptions[i]);
}
FreePool(MenuOptions);
return EFI_SUCCESS;
}
// 收集所有可引导选项
EFI_STATUS
CollectBootMenuOptions(
OUT BDS_BOOT_OPTION ***Options,
OUT UINTN *Count
)
{
UINT16 *BootOrder;
UINTN BootOrderSize;
UINTN BootOrderCount;
BDS_BOOT_OPTION **BootOptions;
UINTN ValidCount = 0;
// 读取 BootOrder
BootOrder = BdsReadBootOrder(&BootOrderSize);
BootOrderCount = BootOrderSize / sizeof(UINT16);
BootOptions = (BDS_BOOT_OPTION **)
AllocateZeroPool(BootOrderCount * sizeof(BDS_BOOT_OPTION *));
for (UINTN i = 0; i < BootOrderCount; i++) {
BDS_BOOT_OPTION *Option;
Status = BdsReadBootOption(BootOrder[i], &Option);
if (EFI_ERROR(Status)) continue;
// 只包含激活的、非隐藏的引导选项
if (Option->IsActive &&
!(Option->Attributes & LOAD_OPTION_HIDDEN)) {
BootOptions[ValidCount++] = Option;
}
}
// 最后添加"UEFI Shell"选项 (如果存在)
AddShellBootOption(&BootOptions, &ValidCount);
*Options = BootOptions;
*Count = ValidCount;
return EFI_SUCCESS;
}
7.3 固件设置界面(Setup Menu)
// 进入固件设置界面
VOID
EnterSetupMenu(
VOID
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
DEBUG((EFI_D_INFO, "[BDS] Entering Setup Menu\n"));
// 1. 查找所有固件设置表单 (HII 数据库)
//
// UEFI 使用 HII (Human Interface Infrastructure)
// 来定义和管理固件配置界面
//
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiHiiDatabaseProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
// 2. 查找 Setup 浏览器协议
EFI_FORM_BROWSER2_PROTOCOL *FormBrowser;
Status = gBS->LocateProtocol(
&gEfiFormBrowser2ProtocolGuid,
NULL,
(VOID **)&FormBrowser
);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "[BDS] FormBrowser not found!\n"));
return;
}
// 3. 设置标志: 正在设置中
// 这样后续引导不会干扰
SetSetupMode(TRUE);
// 4. 调用 HII 浏览器
//
// 这会在图形/文本模式下显示完整的固件配置界面
// 包括:
// - Main (系统信息, 日期时间)
// - Advanced (高级设置)
// - Security (安全设置, 密码)
// - Boot (引导配置)
// - Save & Exit (保存退出)
//
FormBrowser->SendForm(
FormBrowser,
HandleBuffer, // HII 句柄列表
HandleCount, // 句柄数量
NULL, // 默认 FormSet GUID (NULL = 所有)
0, // 默认 Form ID
NULL, // 默认 Question ID
NULL // 回调函数
);
// 5. 退出设置, 继续 BDS 流程
SetSetupMode(FALSE);
DEBUG((EFI_D_INFO, "[BDS] Exiting Setup Menu\n"));
FreePool(HandleBuffer);
}
8. 引导设备枚举与排序
8.1 设备枚举
BDS 阶段需要发现所有可能的引导设备:
// 枚举所有可引导设备
EFI_STATUS
EnumerateBootableDevices(
VOID
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
//
// ═══════════════════════════════════════════
// 1. 连接所有设备到驱动框架
//
// 调用 gBS->ConnectController 遍历所有未连接的
// 设备句柄, 让驱动绑定到设备上
//
// 这会导致:
// - PCI 枚举已完成
// - USB 控制器初始化并枚举 USB 设备
// - NVMe/SATA 控制器初始化
// - 网络适配器初始化
// ═══════════════════════════════════════════
//
BdsConnectAll();
//
// ═══════════════════════════════════════════
// 2. 寻找支持 Simple File System Protocol 的设备
//
// 这是 UEFI 常用的引导接口:
// 设备支持文件系统 → 可以存放 .efi 文件
// ═══════════════════════════════════════════
//
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (!EFI_ERROR(Status)) {
for (UINTN i = 0; i < HandleCount; i++) {
// 检查该设备是否有 EFI/BOOT/BOOTX64.EFI
if (HasEfiBootFile(HandleBuffer[i])) {
RegisterBootDevice(HandleBuffer[i],
"UEFI OS", LoadOptionTypeBoot);
}
}
FreePool(HandleBuffer);
}
//
// ═══════════════════════════════════════════
// 3. 寻找支持 Load File Protocol 的设备
//
// 这通常是网络引导 (PXE) 或远程引导
// ═══════════════════════════════════════════
//
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiLoadFileProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (!EFI_ERROR(Status)) {
for (UINTN i = 0; i < HandleCount; i++) {
RegisterBootDevice(HandleBuffer[i],
"UEFI PXE Network", LoadOptionTypeBoot);
}
FreePool(HandleBuffer);
}
//
// ═══════════════════════════════════════════
// 4. 寻找 UEFI Shell 设备
// ═══════════════════════════════════════════
//
if (IsShellSupported()) {
RegisterBootDevice(NULL, "UEFI Shell", LoadOptionTypeBoot);
}
return EFI_SUCCESS;
}
8.2 引导选项排序
// 按 BootOrder 对引导选项排序
INTN
EFIAPI
CompareBootOptions(
IN CONST VOID *Option1,
IN CONST VOID *Option2
)
{
BDS_BOOT_OPTION *BdsOption1 = *(BDS_BOOT_OPTION **)Option1;
BDS_BOOT_OPTION *BdsOption2 = *(BDS_BOOT_OPTION **)Option2;
UINT16 *BootOrder;
UINTN BootOrderSize;
UINTN Index1, Index2;
UINTN i;
// 读取 BootOrder 数组
BootOrder = BdsReadBootOrder(&BootOrderSize);
// 查找每个选项在 BootOrder 中的位置
Index1 = BootOrderSize / sizeof(UINT16); // 默认: 排到最后
Index2 = BootOrderSize / sizeof(UINT16);
for (i = 0; i < BootOrderSize / sizeof(UINT16); i++) {
if (BootOrder[i] == BdsOption1->OptionNumber) {
Index1 = i;
}
if (BootOrder[i] == BdsOption2->OptionNumber) {
Index2 = i;
}
}
FreePool(BootOrder);
if (Index1 < Index2) return -1;
if (Index1 > Index2) return 1;
return 0;
}
9. 顺序引导(Boot Order)处理流程
9.1 完整引导循环
// 完整引导循环
EFI_STATUS
BdsBootManager(
VOID
)
{
EFI_STATUS Status;
BOOT_LOOP LoopControl;
EFI_BOOT_MODE BootMode;
LoopControl = BootLoopContinue;
while (LoopControl == BootLoopContinue) {
BOOLEAN BootAttempted = FALSE;
UINT16 *BootOrder;
UINTN BootOrderCount;
UINTN Index = 0;
// ═══════════════════════════════════════════
// 重新读取 BootOrder (可能被设置程序修改)
// ═══════════════════════════════════════════
BootOrder = BdsReadBootOrder(&BootOrderCount);
while (Index < BootOrderCount) {
UINT16 BootCurrent;
BDS_BOOT_OPTION *BootOption;
EFI_HANDLE ImageHandle;
CHAR16 LoadOptionName[20];
BootCurrent = BootOrder[Index++];
// 读取引导选项
Status = BdsReadBootOption(BootCurrent, &BootOption);
if (EFI_ERROR(Status)) continue;
// 检查选项是否已激活
if (!BootOption->IsActive) {
FreePool(BootOption);
continue;
}
// 跳过隐藏的选项 (除非是从 OS 发起的 OS 恢复)
if (BootOption->Attributes & LOAD_OPTION_HIDDEN) continue;
// ═══════════════════════════════════════════
// 连接引导设备
// ═══════════════════════════════════════════
//
// 连接设备路径上的所有节点
// 例如: PciRoot → PciBridge → SATA → HD
// 确保设备可用
//
Status = gBS->ConnectController(
BootOption->DeviceHandle,
NULL,
NULL,
TRUE
);
// ═══════════════════════════════════════════
// 安全启动验证
// ═══════════════════════════════════════════
if (IsSecureBootEnabled()) {
Status = VerifyBootOptionSignature(BootOption);
if (EFI_ERROR(Status)) {
DEBUG((
EFI_D_ERROR,
"[BDS] Secure Boot blocked Boot%04x\n",
BootCurrent
));
FreePool(BootOption);
continue;
}
}
// ═══════════════════════════════════════════
// 加载映像
// ═══════════════════════════════════════════
Status = gBS->LoadImage(
FALSE, // BootPolicy
gImageHandle,
BootOption->DevicePath,
NULL,
0,
&ImageHandle
);
if (EFI_ERROR(Status)) {
// 加载失败, 尝试下一个
DEBUG((
EFI_D_WARN,
"[BDS] Boot%04x LoadImage: %r\n",
BootCurrent, Status
));
FreePool(BootOption);
continue;
}
// ═══════════════════════════════════════════
// 启动映像
// ═══════════════════════════════════════════
BootAttempted = TRUE;
//
// 设置 BootCurrent 变量
// 引导加载程序可以读取此变量知道自己是哪个选项启动的
//
UnicodeSPrint(LoadOptionName, sizeof(LoadOptionName),
L"Boot%04x", BootCurrent);
gRT->SetVariable(
L"BootCurrent",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
sizeof(UINT16),
&BootCurrent
);
//
// 调用 StartImage 执行引导加载程序
//
Status = gBS->StartImage(
ImageHandle,
&BootOption->ExitDataSize,
&BootOption->ExitData
);
if (Status == EFI_SUCCESS) {
// 引导加载程序成功返回意味着 ExitBootServices 已调用
// BDS 不应该到这里
LoopControl = BootLoopExit;
break;
}
//
// 引导加载程序返回了错误
// 可能是 "BootNext" 一次性引导, 需要清理
//
if (BootCurrent == GetBootNext()) {
// 清除 BootNext 变量
gRT->SetVariable(
L"BootNext",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
0,
NULL
);
}
FreePool(BootOption);
}
FreePool(BootOrder);
//
// 如果所有引导选项都失败
//
if (!BootAttempted) {
LoopControl = BootLoopContinue;
// 显示错误并等待用户输入
DisplayBootError();
// 如果有按键进入 Setup, 重置 LoopControl = BootLoopContinue
// 如果用户重启, 退出
}
}
return EFI_SUCCESS;
}
9.2 BootNext — 一次性引导
UEFI 支持 BootNext 变量,用于实现一次性引导:
// BootNext 处理
EFI_STATUS
HandleBootNext(
VOID
)
{
EFI_STATUS Status;
UINT16 BootNext;
UINTN DataSize;
// 1. 读取 BootNext 变量
DataSize = sizeof(BootNext);
Status = gRT->GetVariable(
L"BootNext",
&gEfiGlobalVariableGuid,
NULL,
&DataSize,
&BootNext
);
if (EFI_ERROR(Status)) {
return EFI_NOT_FOUND; // 没有一次性引导请求
}
DEBUG((EFI_D_INFO, "[BDS] BootNext = Boot%04x\n", BootNext));
// 2. 清除 BootNext (防止下次启动再次执行)
gRT->SetVariable(
L"BootNext",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
0,
NULL
);
// 3. 将 BootNext 插入到 BootOrder 的第一个位置
//
// 但不修改 NVRAM 中的 BootOrder
// 只在当前启动中有效
//
Status = InsertBootNextToFront(BootNext);
return Status;
}
9.3 引导选项的 Boot Manager 菜单显示
典型的 Boot Order 在固件设置中的显示:
Boot Configuration
┌─────────────────────────────────────────────┐
│ Boot Option Priorities │
│ │
│ Boot Option #1: [Windows Boot Manager] │
│ Boot Option #2: [ubuntu] │
│ Boot Option #3: [UEFI: SanDisk SSD] │
│ Boot Option #4: [UEFI PXE IPv4] │
│ Boot Option #5: [UEFI Shell] │
│ │
│ [ Add New Boot Option ] │
│ [ Delete Boot Option ] │
│ │
│ Boot Option #1 → Boot0001 → Windows │
│ Boot Option #2 → Boot0003 → Ubuntu │
│ Boot Option #3 → Boot0000 → SanDisk │
│ Boot Option #4 → Boot0002 → PXE │
│ Boot Option #5 → Boot0004 → Shell │
│ │
│ Boot Order: 1 3 0 2 4 │
└─────────────────────────────────────────────┘
10. 网络引导(PXE/HTTP Boot)
10.1 PXE 引导过程
UEFI 支持通过网络引导操作系统:
UEFI PXE 引导流程:
1. DHCP/DISCOVER (广播)
│ Client: "我是 XX:XX:XX:XX:XX:XX, 需要 IP 地址和引导文件"
│
├── DHCP Server 回应:
│ + IP 地址
│ + 下一跳服务器 (next-server)
│ + 引导文件名 (bootfile)
│
2. TFTP/HTTP 下载引导文件
│ Client → Server: GET bootfile.efi
│ Server → Client: 传输文件内容
│
3. UEFI LoadImage + StartImage
│ gBS->LoadImage(BootPolicy=TRUE, DevicePath=)
│ gBS->StartImage(ImageHandle)
│
4. GRUB / iPXE / Windows Boot Manager 执行
10.2 UEFI HTTP Boot
现代 UEFI 支持 HTTP Boot(取代传统 TFTP):
// HTTP Boot 流程
EFI_STATUS
HttpBootStart(
VOID
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
EFI_HTTP_BOOT_PROTOCOL *HttpBoot;
// 1. 查找 HTTP Boot 协议实例
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiHttpBootProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "[BDS] HTTP Boot not available\n"));
return Status;
}
// 2. 对每个网络设备尝试 HTTP Boot
for (UINTN i = 0; i < HandleCount; i++) {
Status = gBS->HandleProtocol(
HandleBuffer[i],
&gEfiHttpBootProtocolGuid,
(VOID **)&HttpBoot
);
if (EFI_ERROR(Status)) continue;
// 3. 启动 HTTP Boot
//
// HTTP Boot Protocol 内部处理:
// - DHCP 获取网络配置
// - HTTP 下载引导文件
// - 调用 LoadImage/StartImage
//
Status = HttpBoot->Start(HttpBoot);
if (Status == EFI_SUCCESS) {
break;
}
}
FreePool(HandleBuffer);
return Status;
}
10.3 UEFI 网络引导协议栈
UEFI 网络引导协议栈:
┌──────────────────────────────────────┐
│ 网络引导应用 │
│ (iPXE, GRUB, 或其他 HTTP Boot 应用) │
├──────────────────────────────────────┤
│ UEFI 网络应用层协议 │
│ ┌─────────────────────────────────┐ │
│ │ HTTP Boot Protocol │ │ ← HTTP 引导
│ │ Load File Protocol │ │ ← PXE/TFTP 引导
│ └─────────────────────────────────┘ │
├──────────────────────────────────────┤
│ UEFI 网络传输层协议 │
│ ┌───────────┐ ┌───────────────────┐ │
│ │ TCP │ │ UDP │ │
│ └───────────┘ └───────────────────┘ │
├──────────────────────────────────────┤
│ UEFI 网络层协议 │
│ ┌─────────────────────────────────┐ │
│ │ IPv4 / IPv6 │ │
│ └─────────────────────────────────┘ │
├──────────────────────────────────────┤
│ UEFI 网络底层协议 │
│ ┌──────────┐ ┌───────────────────┐ │
│ │ ARP │ │ DHCP │ │
│ ├──────────┤ ├───────────────────┤ │
│ │ ICMP │ │ DNS │ │
│ └──────────┘ └───────────────────┘ │
├──────────────────────────────────────┤
│ UEFI 网络数据链路层 │
│ ┌─────────────────────────────────┐ │
│ │ SNP (Simple Network Protocol) │ │
│ │ MNP (Managed Network Protocol) │ │
│ └─────────────────────────────────┘ │
├──────────────────────────────────────┤
│ 网络适配器硬件 │
│ (NIC: Intel I219, Realtek, ...) │
└──────────────────────────────────────┘
11. 安全启动在 BDS 阶段
11.1 安全启动流程
// BDS 阶段的安全启动验证
EFI_STATUS
SecureBootVerify(
IN BDS_BOOT_OPTION *BootOption
)
{
EFI_STATUS Status;
BOOLEAN SecureBootEnabled;
UINT8 *ImageBuffer;
UINTN ImageSize;
EFI_HANDLE ImageHandle;
// 1. 检查安全启动是否开启
SecureBootEnabled = IsSecureBootEnabled();
if (!SecureBootEnabled) {
return EFI_SUCCESS; // 安全启动已禁用, 跳过验证
}
DEBUG((EFI_D_INFO, "[BDS] SecureBoot: verifying Boot%04x\n",
BootOption->OptionNumber));
// 2. 从设备路径加载映像到内存 (但不执行)
Status = gBS->LoadImage(
FALSE,
gImageHandle,
BootOption->DevicePath,
NULL,
0,
&ImageHandle
);
if (EFI_ERROR(Status)) {
return Status;
}
// 3. 获取映像的数据库指针
Status = gBS->OpenProtocol(
ImageHandle,
&gEfiLoadedImageProtocolGuid,
(VOID **)&LoadedImage,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
// 4. 执行映像签名验证
//
// 验证流程:
// a. 提取映像的签名 (嵌入在 PE/COFF 头部)
// b. 检查 db (允许签名数据库)
// └── 如果签名在 db 中 → 通过
// c. 检查 dbx (禁止签名数据库)
// └── 如果签名在 dbx 中 → 拒绝
// d. 如果既不在 db 也不在 dbx:
// ├── 如果 SetupMode == 1 (设置模式) → 通过
// └── 如果 SecureBoot == 1 (用户模式) → 拒绝
//
Status = VerifyImageSignature(
LoadedImage->ImageBase,
LoadedImage->ImageSize
);
if (EFI_ERROR(Status)) {
//
// 安全启动验证失败
// 可以选择记录审计日志并拒绝启动
//
DEBUG((
EFI_D_ERROR,
"[BDS] Secure Boot: Image rejected! Status=%r\n",
Status
));
return Status;
}
return EFI_SUCCESS;
}
11.2 安全启动密钥数据库
// UEFI 安全启动密钥结构
//
// 安全启动使用以下 NVRAM 变量:
//
// PK = Platform Key (平台密钥, 1 个)
// KEK = Key Exchange Key (密钥交换密钥, 多个)
// db = Allowed Signatures Database (允许签名数据库)
// dbx = Forbidden Signatures Database (禁止签名数据库)
// dbt = Allowed Timestamp Database (时间戳数据库, 可选)
// dbr = Allowed Recovery Database (恢复数据库, 可选)
//
//
// 签名列表结构 (db, dbx, dbt, dbr 使用)
//
typedef struct {
EFI_GUID SignatureType; // 签名类型
UINT32 SignatureListSize; // 本结构总大小
UINT32 SignatureHeaderSize; // 签名头部大小 (通常为 0)
UINT32 SignatureSize; // 每个签名条目的大小
// 签名头部 (SignatureHeaderSize 字节)
// 签名条目列表 (不定长)
} EFI_SIGNATURE_LIST;
typedef struct {
EFI_GUID Owner; // 所有者 GUID
// 签名数据 (如 RSA-2048 的 256 字节哈希):
// UINT8 SignatureData[];
} EFI_SIGNATURE_DATA;
//
// 签名类型 GUID:
// EFI_CERT_SHA256_GUID → SHA-256 哈希
// EFI_CERT_RSA2048_GUID → RSA-2048 公钥
// EFI_CERT_RSA2048_SHA256_GUID → RSA-2048 + SHA-256 签名
// EFI_CERT_X509_GUID → X.509 证书
// EFI_CERT_X509_SHA256_GUID → X.509 证书的 SHA-256 哈希
// EFI_CERT_X509_SHA384_GUID → X.509 证书的 SHA-384 哈希
// EFI_CERT_X509_SHA512_GUID → X.509 证书的 SHA-512 哈希
//
11.3 安全启动验证流程
Secure Boot 验证流程图:
用户加载映像 LoadImage()
│
├── SecureBoot 是否开启?
│ ├── 否 → 直接通过 (EFI_SUCCESS)
│ └── 是 → 继续验证
│
├── 映像是否在 dbx (禁止列表) 中?
│ ├── 是 → 拒绝加载 (EFI_SECURITY_VIOLATION)
│ └── 否 → 继续验证
│
├── 映像是否在 db (允许列表) 中?
│ ├── 是 → 允许加载 (EFI_SUCCESS)
│ └── 否 → 继续验证
│
├── SetupMode 是否为 1 (设置模式)?
│ ├── 是 → 允许加载
│ └── 否 → 继续验证
│
├── 映像是否签名?
│ ├── 未签名 → 拒绝 (在用户模式下)
│ └── 已签名 → 验证签名链
│
├── 签名是否可链到 KEK 和 PK?
│ ├── 否 → 拒绝
│ └── 是 → 允许加载
│
└── 最终决定:
EFI_SUCCESS = 允许引导
EFI_SECURITY_VIOLATION = 阻止引导
11.4 AUdIT MODE
安全启动的几种模式:
┌──────────────┬────────────────┬─────────────────┐
│ 模式 │ SetupMode │ SecureBoot │
├──────────────┼────────────────┼─────────────────┤
│ 设置模式 │ 1 │ 0 │
│ (Setup Mode) │ PK 未设置 │ 可以安装任意映像 │
│ │ │ 可更改密钥 │
├──────────────┼────────────────┼─────────────────┤
│ 用户模式 │ 0 │ 1 │
│ (User Mode) │ PK 已安装 │ 只运行签名的映像 │
│ │ │ 需要 PK 才能改密钥 │
├──────────────┼────────────────┼─────────────────┤
│ 审计模式 │ 0 │ 0 │
│ (Audit Mode) │ PK 已安装 │ 记录日志但不阻止 │
│ │ │ (用于调试和迁移) │
├──────────────┼────────────────┼─────────────────┤
│ 部署模式 │ 0 │ 1 │
│ (Deployed │ PK 已安装 │ 最严格的验证 │
│ Mode) │ │ 不能禁用安全启动 │
└──────────────┴────────────────┴─────────────────┘
12. UEFI 驱动程序加载与管理
12.1 驱动程序选项
BDS 阶段可以加载可选的 UEFI 驱动程序:
Driver#### 选项 (与 Boot#### 类似):
DriverOrder = { 0000, 0002, 0001 }
│
├── Driver0000 → "\EFI\DRIVERS\NVMe.efi"
├── Driver0002 → "\EFI\DRIVERS\USBXHCI.efi"
└── Driver0001 → "\EFI\DRIVERS\AHCI.efi"
在 Boot Manager 执行之前,
BDS 先加载所有 Driver#### 选项中的驱动
12.2 驱动程序加载
// BDS 驱动程序加载流程
EFI_STATUS
BdsLoadDrivers(
VOID
)
{
EFI_STATUS Status;
UINT16 *DriverOrder;
UINTN DriverOrderCount;
UINTN Index;
// 1. 读取 DriverOrder
DriverOrder = BdsReadDriverOrder(&DriverOrderCount);
// 2. 按顺序加载每个驱动
for (Index = 0; Index < DriverOrderCount; Index++) {
UINT16 DriverCurrent = DriverOrder[Index];
BDS_DRIVER_OPTION *DriverOption;
Status = BdsReadDriverOption(DriverCurrent, &DriverOption);
if (EFI_ERROR(Status)) continue;
if (!DriverOption->IsActive) {
FreePool(DriverOption);
continue;
}
DEBUG((
EFI_D_INFO,
"[BDS] Loading Driver%04x: %s\n",
DriverCurrent, DriverOption->Description
));
// 连接驱动所属设备
gBS->ConnectController(DriverOption->DeviceHandle, NULL, NULL, TRUE);
// 加载并启动驱动
EFI_HANDLE ImageHandle;
Status = gBS->LoadImage(
FALSE,
gImageHandle,
DriverOption->DevicePath,
NULL,
0,
&ImageHandle
);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_WARN, "[BDS] Driver%04x LoadImage: %r\n",
DriverCurrent, Status));
continue;
}
// 启动驱动
Status = gBS->StartImage(ImageHandle, NULL, NULL);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_WARN, "[BDS] Driver%04x StartImage: %r\n",
DriverCurrent, Status));
}
FreePool(DriverOption);
}
// 3. 连接所有驱动对应的设备
BdsConnectAll();
return EFI_SUCCESS;
}
12.3 UEFI 驱动模型
UEFI 驱动模型在 BDS 阶段的作用:
┌──────────────────────────────────────────────┐
│ UEFI 驱动模型 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 总线驱动 │───▶│ 设备驱动 │───▶│ 文件系统 │ │
│ │ (PCI Bus)│ │ (SATA) │ │ (NTFS) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 枚举设备 │ │ 绑定到 │ │ 提供 │ │
│ │ 创建句柄 │ │ 设备句柄 │ │ GOP/FS │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ BDS 通过 ConnectController 触发驱动绑定: │
│ gBS->ConnectController(Handle, ...) │
│ → 寻找匹配的 Driver Binding Protocol │
│ → 调用 DriverBinding->Supported() │
│ → 调用 DriverBinding->Start() │
│ → 驱动初始化完成 │
└──────────────────────────────────────────────┘
13. 固件配置界面(Setup)
13.1 HII 架构
UEFI 使用 HII(Human Interface Infrastructure)构建固件配置界面:
HII 配置界面架构:
┌──────────────────────────────────────────────┐
│ 固件设置界面 (Setup Browser) │
│ ┌────────────────────────────────────────┐ │
│ │ Main │ Advanced │ Security │ Boot │Exit│ │
│ ├────────────────────────────────────────┤ │
│ │ [System Information] │ │
│ │ BIOS Version: X.X.X │ │
│ │ Processor: Intel Core i7-xxxx │ │
│ │ Memory: 32 GB DDR4 │ │
│ │ [Boot Configuration] │ │
│ │ Boot Mode: [UEFI] │ │
│ │ Fast Boot: [Enabled] │ │
│ └────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Form Browser (EFI_FORM_BROWSER2_PROTOCOL) │
│ - 渲染表单 (Form) │
│ - 处理用户输入 │
│ - 调用配置回调 │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ HII 数据库 (EFI_HII_DATABASE_PROTOCOL) │
│ - 存储表单定义 (IFR 字节码) │
│ - 存储字符串 (多语言支持) │
│ - 存储图像 (Logo, 图标) │
│ - 存储字体 │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ IFR (Internal Forms Representation) │
│ ┌────────────────────────────────────────┐ │
│ │ opcode: FORM_SET │ │
│ │ opcode: FORM │ │
│ │ opcode: TEXT (处理器) │ │
│ │ opcode: ONE_OF (引导模式) │ │
│ │ opcode: CHECKBOX (快速启动) │ │
│ │ opcode: NUMERIC (超时) │ │
│ │ opcode: STRING (设备路径) │ │
│ └────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
13.2 IFR 示例
// IFR (Internal Forms Representation) 字节码示例
// 使用 VFR (Visual Forms Representation) 语言定义表单
// VFR 被编译为 IFR 字节码
// 一个典型的固件设置页面定义
formset
guid = FORMSET_GUID,
title = STRING_TOKEN(STR_MAIN_TITLE),
help = STRING_TOKEN(STR_MAIN_HELP),
// ── 系统信息页面 ──
form formid = 1,
title = STRING_TOKEN(STR_SYSTEM_INFO);
text
text = STRING_TOKEN(STR_BIOS_VERSION),
text = STRING_TOKEN(STR_BIOS_VERSION_VALUE);
endtext;
text
text = STRING_TOKEN(STR_PROCESSOR),
text = STRING_TOKEN(STR_PROCESSOR_VALUE);
endtext;
text
text = STRING_TOKEN(STR_SYSTEM_MEMORY),
text = STRING_TOKEN(STR_MEMORY_VALUE);
endtext;
endform;
// ── 引导配置页面 ──
form formid = 2,
title = STRING_TOKEN(STR_BOOT_CONFIG);
// 引导模式选择
oneof varid = BootMode,
prompt = STRING_TOKEN(STR_BOOT_MODE),
help = STRING_TOKEN(STR_BOOT_MODE_HELP),
option text = STRING_TOKEN(STR_UEFI), value = 0, flags = DEFAULT;
option text = STRING_TOKEN(STR_LEGACY), value = 1;
endoneof;
// 快速启动开关
checkbox varid = FastBoot,
prompt = STRING_TOKEN(STR_FAST_BOOT),
help = STRING_TOKEN(STR_FAST_BOOT_HELP),
flags = CHECKBOX_DEFAULT;
endcheckbox;
// 超时设置
numeric varid = BootTimeout,
prompt = STRING_TOKEN(STR_BOOT_TIMEOUT),
help = STRING_TOKEN(STR_BOOT_TIMEOUT_HELP),
minimum = 0,
maximum = 30,
step = 1,
default = 5;
endnumeric;
endform;
endformset;
14. Boot Manager 内部数据结构
14.1 内部选项结构
// BDS 内部使用的完整引导选项结构
typedef struct {
//
// 选项编号 (如 0x0001 对应 Boot0001)
//
UINT16 OptionNumber;
//
// 选项类型
//
EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType; // Boot 或 Driver
//
// 选项属性
//
UINT32 Attributes;
#define LOAD_OPTION_ACTIVE 0x00000001
#define LOAD_OPTION_FORCE_RECONNECT 0x00000002
#define LOAD_OPTION_HIDDEN 0x00000008
#define LOAD_OPTION_CATEGORY 0x00001F00
#define LOAD_OPTION_CATEGORY_BOOT 0x00000000
#define LOAD_OPTION_CATEGORY_APP 0x00000100
//
// 描述字符串 (Unicode)
//
CHAR16 *Description;
//
// 设备路径 (指向 .efi 文件或设备)
//
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
//
// 可选的额外数据
//
UINT8 *OptionalData;
UINT16 OptionalDataSize;
//
// 设备句柄 (连接后的设备)
//
EFI_HANDLE DeviceHandle;
//
// 文件路径 (从设备路径中提取的文件部分)
//
EFI_DEVICE_PATH_PROTOCOL *FilePath;
//
// 状态
//
BOOLEAN IsActive; // 选项是否激活
BOOLEAN IsBootable; // 设备是否可引导
//
// 退出数据 (引导加载程序返回的数据)
//
UINTN ExitDataSize;
VOID *ExitData;
//
// 加载的映像句柄 (可选, 调试用)
//
EFI_HANDLE ImageHandle;
} BDS_BOOT_OPTION;
14.2 Boot Manager 全局数据
// Boot Manager 全局数据
typedef struct {
//
// 所有引导选项的链表
//
LIST_ENTRY BootOptionList;
UINTN BootOptionCount;
//
// 所有驱动选项的链表
//
LIST_ENTRY DriverOptionList;
UINTN DriverOptionCount;
//
// 控制台信息
//
EFI_DEVICE_PATH_PROTOCOL *ConInDevicePath;
EFI_DEVICE_PATH_PROTOCOL *ConOutDevicePath;
EFI_DEVICE_PATH_PROTOCOL *StdErrDevicePath;
//
// 当前引导状态
//
UINT16 BootCurrent; // 当前引导的 Boot####
UINT16 BootNext; // 一次性引导选项
//
// 超时设置
//
UINT16 Timeout; // 引导倒计时秒数
BOOLEAN TimeoutElapsed; // 超时是否已到
//
// 安全启动状态
//
BOOLEAN SecureBootEnabled;
UINT8 SetupMode; // 0=User, 1=Setup
//
// 重启/关闭标记
//
BOOLEAN ShutdownRequested;
BOOLEAN RebootRequested;
//
// 平台特定标志
//
BOOLEAN SetupMenuRequested;
BOOLEAN BootMenuRequested;
//
// 调试
//
UINTN BootAttemptCount; // 已尝试的引导次数
} BDS_GLOBAL_DATA;
15. Boot Manager 协议与编程接口
15.1 EFI_BOOT_MANAGER_PROTOCOL
// Boot Manager Protocol (由 UEFI 规范定义)
typedef struct _EFI_BOOT_MANAGER_PROTOCOL {
//
// 加载引导选项
// 从 NVRAM 读取并解析一个 Boot#### 选项
//
EFI_BOOT_MANAGER_LOAD_OPTION LoadOption;
//
// 释放引导选项占用的资源
//
EFI_BOOT_MANAGER_FREE_OPTION FreeOption;
//
// 启动引导选项
// 执行 LoadImage + StartImage
//
EFI_BOOT_MANAGER_START_OPTION StartOption;
//
// 写入引导选项到 NVRAM
//
EFI_BOOT_MANAGER_WRITE_OPTION WriteOption;
//
// 枚举所有引导选项
//
EFI_BOOT_MANAGER_ENUMERATE_BOOT_OPTIONS EnumerateBootOptions;
//
// 获取引导顺序
//
EFI_BOOT_MANAGER_GET_BOOT_ORDER GetBootOrder;
//
// 设置引导顺序
//
EFI_BOOT_MANAGER_SET_BOOT_ORDER SetBootOrder;
} EFI_BOOT_MANAGER_PROTOCOL;
15.2 编程接口示例
// 通过编程接口添加引导选项
EFI_STATUS
AddNewBootOption(
IN CHAR16 *Description,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN UINT32 Attributes
)
{
EFI_STATUS Status;
UINT16 BootIndex;
UINT16 *BootOrder;
UINTN BootOrderSize;
UINTN NewOrderSize;
UINT16 *NewBootOrder;
// 1. 查找下一个可用的 Boot#### 编号
BootIndex = FindNextFreeBootOption();
// 2. 构造 EFI_LOAD_OPTION 数据
//
// 结构:
// UINT32 Attributes
// UINT16 FilePathListLength
// CHAR16[] Description (以 NUL 结尾)
// EFI_DEVICE_PATH_PROTOCOL[] FilePathList
// UINT8[] OptionalData (可选)
//
UINTN DescSize = StrSize(Description);
UINTN FilePathSize = GetDevicePathSize(DevicePath);
UINTN TotalSize = sizeof(EFI_LOAD_OPTION) + DescSize + FilePathSize;
UINT8 *OptionData = AllocatePool(TotalSize);
EFI_LOAD_OPTION *LoadOption = (EFI_LOAD_OPTION *)OptionData;
LoadOption->Attributes = Attributes;
LoadOption->FilePathListLength = (UINT16)FilePathSize;
// 复制描述字符串
CopyMem(OptionData + sizeof(EFI_LOAD_OPTION),
Description, DescSize);
// 复制设备路径
CopyMem(OptionData + sizeof(EFI_LOAD_OPTION) + DescSize,
DevicePath, FilePathSize);
// 3. 写入 NVRAM: Boot####
CHAR16 VariableName[20];
UnicodeSPrint(VariableName, sizeof(VariableName),
L"Boot%04x", BootIndex);
Status = gRT->SetVariable(
VariableName,
&gEfiGlobalVariableGuid,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
TotalSize,
OptionData
);
FreePool(OptionData);
// 4. 更新 BootOrder
BootOrder = BdsReadBootOrder(&BootOrderSize);
NewOrderSize = BootOrderSize + sizeof(UINT16);
NewBootOrder = AllocatePool(NewOrderSize);
// 将新选项放在第一个位置
NewBootOrder[0] = BootIndex;
if (BootOrder != NULL) {
CopyMem(&NewBootOrder[1], BootOrder, BootOrderSize);
FreePool(BootOrder);
}
Status = gRT->SetVariable(
L"BootOrder",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
NewOrderSize,
NewBootOrder
);
FreePool(NewBootOrder);
return Status;
}
16. 退出启动服务(ExitBootServices)
16.1 ExitBootServices 的含义
当引导加载程序准备接管系统时,调用 ExitBootServices():
ExitBootServices 的作用:
┌─────────────────────────────────────────────┐
│ ExitBootServices() 调用 │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ 调用前 (BDS/TSL): │ │
│ │ - Boot Services 可用 │ │
│ │ - 固件可控制所有硬件 │ │
│ │ - UEFI 驱动和协议仍然可用 │ │
│ │ - 固件中断处理程序就绪 │ │
│ │ - 固件管理页表和 GDT │ │
│ └───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────┐ │
│ │ ExitBootServices() 执行操作: │ │
│ │ 1. 终止所有 UEFI 事件 │ │
│ │ 2. 释放所有 Boot Services 内存 │ │
│ │ 3. 关闭所有 Boot Services 协议 │ │
│ │ 4. 将控制台交由 OS 管理 │ │
│ │ 5. 禁用 UEFI 中断处理 │ │
│ └───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────┐ │
│ │ 调用后 (RT): │ │
│ │ - 只有 Runtime Services 可用 │ │
│ │ - 操作系统完全控制硬件 │ │
│ │ - UEFI 驱动停止工作 │ │
│ │ - 只有 gRT 中的功能可用 │ │
│ │ - SetVirtualAddressMap 可被调用 │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
16.2 ExitBootServices 调用过程
// 引导加载程序调用 ExitBootServices 的过程
//
// 这段代码通常由引导加载程序 (如 GRUB, Windows Boot Manager) 执行
// 不是由 BDS 执行
EFI_STATUS
CallExitBootServices(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINTN MapKey;
UINTN MemoryMapSize;
EFI_MEMORY_DESCRIPTOR *MemoryMap;
UINTN MapKey2;
UINTN DescriptorSize;
UINT32 DescriptorVersion;
// 1. 获取当前内存映射的 MapKey
MemoryMapSize = 0;
MemoryMap = NULL;
Status = gBS->GetMemoryMap(
&MemoryMapSize,
MemoryMap,
&MapKey,
&DescriptorSize,
&DescriptorVersion
);
// 2. 分配内存映射缓冲区
MemoryMap = AllocatePool(MemoryMapSize);
Status = gBS->GetMemoryMap(
&MemoryMapSize,
MemoryMap,
&MapKey,
&DescriptorSize,
&DescriptorVersion
);
// 3. 调用 ExitBootServices
//
// 这个函数:
// - 接收 ImageHandle (调用者的映像句柄)
// - 接收 MapKey (内存映射的密钥, 用于检测竞态条件)
//
// 如果另一个事件在 GetMemoryMap 和 ExitBootServices
// 之间修改了内存映射, ExitBootServices 会返回错误
// 此时需要重新获取 MapKey 并再次调用
//
Status = gBS->ExitBootServices(ImageHandle, MapKey);
if (Status == EFI_INVALID_PARAMETER) {
//
// MapKey 不匹配, 重新获取并重试
//
MemoryMapSize = 0;
Status = gBS->GetMemoryMap(
&MemoryMapSize,
NULL,
&MapKey,
&DescriptorSize,
&DescriptorVersion
);
MemoryMap = AllocatePool(MemoryMapSize);
Status = gBS->GetMemoryMap(
&MemoryMapSize,
MemoryMap,
&MapKey,
&DescriptorSize,
&DescriptorVersion
);
Status = gBS->ExitBootServices(ImageHandle, MapKey);
}
if (EFI_ERROR(Status)) {
// ExitBootServices 失败 (!)
// 系统仍然在固件控制下
return Status;
}
//
// 从此处开始:
// - gBS (Boot Services) 不可用
// - gRT (Runtime Services) 仍然可用
// - 操作系统已经控制了所有硬件
//
// 4. 可选: 调用 SetVirtualAddressMap
// 将 Runtime Services 转换为虚拟地址模式
// (依赖于操作系统策略)
return EFI_SUCCESS;
}
16.3 ExitBootServices 之后的系统状态
ExitBootServices 之后可用的 UEFI 运行时服务:
gRT->GetTime() // 读取 RTC 时间
gRT->SetTime() // 设置 RTC 时间
gRT->GetWakeupTime() // 读取唤醒时间
gRT->SetWakeupTime() // 设置唤醒时间
gRT->GetVariable() // 读取 NVRAM 变量
gRT->SetVariable() // 写入 NVRAM 变量
gRT->GetNextVariableName() // 枚举 NVRAM 变量
gRT->GetNextHighMonotonicCount() // 单调计数器
gRT->SetVirtualAddressMap() // 转换地址空间 (可选)
gRT->ConvertPointer() // 指针转换
gRT->GetCapabilities() // 重置能力查询
gRT->ResetSystem() // 系统重启 (Cold/Warm/Shutdown)
不可用的启动服务:
gBS->LoadImage() // ✗
gBS->StartImage() // ✗
gBS->AllocatePages() // ✗
gBS->OpenProtocol() // ✗
gBS->ConnectController() // ✗
gBS->CreateEvent() // ✗
... // ✗ 所有 Boot Services
17. BDS 与操作系统引导加载程序交接
17.1 引导加载程序执行环境
BDS 调用 gBS->StartImage() 后,引导加载程序获得控制权:
BDS 传递给引导加载程序的执行环境:
┌──────────────────────────────────────────────┐
│ 引导加载程序启动时的状态 │
│ │
│ 内存: │
│ - 所有物理内存已映射 │
│ - UEFI 保留了一些内存区域 │
│ - 没有虚拟地址映射 (OS 需要自己建立页表) │
│ │
│ 处理器: │
│ - 64 位长模式 (x64) │
│ - 中断关闭 (CLI) │
│ - 无浮点上下文 │
│ - TLB/缓存不确定状态 │
│ │
│ UEFI 协议: │
│ - 全套 Boot Services 可用 │
│ - Runtime Services 可用 │
│ - 所有 UEFI 协议可用 │
│ - 控制台就绪 │
│ │
│ 参数: │
│ - ImageHandle → 引导加载程序自身的句柄 │
│ - SystemTable → UEFI 系统表指针 │
│ (包含 BootServices, │
│ RuntimeServices, 配置表) │
│ - LoadedImage → 映像加载信息 │
│ (设备路径, 加载地址, 大小) │
│ │
└──────────────────────────────────────────────┘
17.2 引导加载程序接口
// UEFI 引导加载程序的标准入口点
//
// 每个 UEFI 应用程序 (包括引导加载程序) 的入口点:
//
typedef
EFI_STATUS
(EFIAPI *EFI_IMAGE_ENTRY_POINT)(
IN EFI_HANDLE ImageHandle, // 映像实例句柄
IN EFI_SYSTEM_TABLE *SystemTable // UEFI 系统表
);
//
// 引导加载程序入口函数 (如 GRUB, bootmgfw.efi, systemd-boot)
//
EFI_STATUS
EFIAPI
BootLoaderEntry(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// 引导加载程序执行流程:
//
// 1. 保存系统表指针 (全局变量)
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
// 2. 初始化引导加载程序自身
// └── 内存分配
// └── 字符输出设置
// └── 加载配置文件
// 3. 显示引导菜单 (如 GRUB 菜单)
// └── 列出可用的操作系统内核
// └── 等待用户选择
// 4. 加载操作系统内核
// └── 读取内核文件到内存
// └── 收集硬件信息 (内存映射, ACPI 表, SMBIOS)
// └── 设置内核启动参数
// 5. 调用 ExitBootServices()
// └── 终止 UEFI 启动服务
// └── 固件交出控制权
// 6. 跳转到内核入口
// └── 设置页表 (如果 OS 需要)
// └── 设置内核栈
// └── jmp kernel_entry
return EFI_SUCCESS;
}
17.3 典型引导加载程序的 BDS 交互
GRUB (Linux) 与 BDS 的交互:
BDS → LoadImage(HD0, \EFI\GRUB\grubx64.efi)
→ StartImage(ImageHandle)
│
▼
grubx64.efi 入口:
├── 初始化 GRUB 控制台
├── 加载 GRUB 模块 (通过 UEFI 文件系统协议)
├── 读取 grub.cfg 配置文件
├── 显示 GRUB 菜单
├── 用户选择 "Boot Linux"
│
├── 加载 vmlinuz-linux (通过 UEFI 文件系统)
├── 加载 initramfs (通过 UEFI 文件系统)
├── 收集 UEFI 配置表 (ACPI, SMBIOS)
├── 收集内存映射 (GetMemoryMap)
│
├── ExitBootServices()
│ └── UEFI 固件不再控制硬件
│
└── 跳转到 Linux 内核入口 (startup_64)
───────────────────────────────────────────
Windows Boot Manager 与 BDS 的交互:
BDS → LoadImage(HD0, \EFI\Microsoft\Boot\bootmgfw.efi)
→ StartImage(ImageHandle)
│
▼
bootmgfw.efi 入口:
├── 初始化 Windows 引导环境
├── 读取 BCD (Boot Configuration Data)
├── 显示 Windows 引导菜单 (如多系统)
│
├── 加载 winload.efi
├── 加载 HAL (硬件抽象层)
├── 收集 UEFI 配置表
│
├── ExitBootServices()
│
└── 跳转到 Windows 内核入口
18. EDK2 BDS 实现分析
18.1 EDK2 BDS 文件结构
edk2/MdeModulePkg/Universal/BdsDxe/
│
├── BdsEntry.c ← BDS 入口 (BdsEntry)
├── BdsEntry.h
├── BdsDxe.inf ← 模块描述文件
│
├── BdsMisc.c ← BDS 辅助函数
│ ├── BdsReadBootOrder()
│ ├── BdsReadBootOption()
│ ├── BdsSetBootOrder()
│ └── BdsConnectAll()
│
├── Language.c ← 语言选择
├── Device.c ← 设备连接管理
├── Device.h
│
├── HwErrRecSupport.c ← 硬件错误记录支持
│
└── BootManager.c ← Boot Manager 核心
├── BootManagerMenu ()
└── ...
edk2/MdeModulePkg/Library/
├── UefiBootManagerLib/ ← Boot Manager 库
│ ├── BmLoadOption.c ← 引导选项加载
│ ├── BmBoot.c ← 引导执行
│ ├── BmConnect.c ← 设备连接
│ ├── BmConsole.c ← 控制台初始化
│ ├── BmDriver.c ← 驱动加载
│ ├── BmMisc.c ← 杂项
│ └── BmVariable.c ← NVRAM 变量操作
│
└── PlatformBootManagerLib/ ← 平台 BDS 策略 (各平台不同)
├── PlatformBootManager.c
└── ...
18.2 PlatformBootManagerLib
平台 BDS 策略是 BDS 阶段与平台相关的部分,由 PlatformBootManagerLib 定义:
// 平台 BDS 策略 (每个平台有自己实现)
// edk2-platforms/Platform/<Vendor>/<Platform>/Library/PlatformBootManagerLib/
/**
平台 BDS 初始化
这个函数由 BDS Entry 调用,完成平台特定的 BDS 初始化。
不同平台可以有不同的实现:
- Intel 参考平台: 初始化串口、显示启动Logo
- 服务器平台: 初始化 BMC、显示 POST 卡
- 嵌入式平台: 可能没有显示设备
**/
VOID
EFIAPI
PlatformBootManagerBeforeConsole(
VOID
)
{
//
// 1. 平台特定早期初始化 (在控制台初始化之前)
//
// 初始化调试输出 (串口)
SerialPortInitialize();
DEBUG((EFI_D_INFO, "[PlatformBDS] Platform BDS start\n"));
//
// 2. 连接必要的设备
//
// 某些设备需要在控制台初始化之前连接
//
gBS->ConnectController(PciRootBridgeHandle, NULL, NULL, FALSE);
//
// 3. 注册平台特定的控制台设备
//
// Platform specific console path:
// - Serial console for servers
// - GOP for client platforms
//
PlatformRegisterDefaultConIn();
PlatformRegisterDefaultConOut();
//
// 4. 安装平台特定 PPI/Protocol (如果有)
//
// ...
}
VOID
EFIAPI
PlatformBootManagerAfterConsole(
VOID
)
{
//
// 1. 控制台已初始化
//
// 显示 OEM 启动徽标
ShowPlatformLogo();
//
// 2. 注册平台特定引导选项
//
// 添加 UEFI Shell 引导选项
PlatformRegisterBootOptionShell();
// 添加操作系统引导选项 (如果未配置)
PlatformRegisterDefaultBootOption();
//
// 3. 注册 "F2/F12" 热键
//
// F2 → Setup
// Delete → Setup
// F12 → Boot Menu
// F10 → BIOS Flash Update (某些平台)
//
EfiBootManagerRegisterBootOptionHotkey(
&mSetupOption, // Setup 选项
L"F2", // 按键
FALSE, // Shift
FALSE, // Ctrl
FALSE // Alt
);
EfiBootManagerRegisterBootOptionHotkey(
&mBootMenuOption, // Boot Menu 选项
L"F12", // 按键
FALSE, FALSE, FALSE
);
}
/**
无法引导时调用
**/
EFI_STATUS
EFIAPI
PlatformBootManagerUnableToBoot(
VOID
)
{
//
// 所有引导选项都失败时
//
// 显示用户的错误信息
//
Print(L"\n No bootable device found.\n");
Print(L" Press any key to enter Setup...\n");
// 等待按键进入 Setup
WaitForSingleEvent(gST->ConIn->WaitForKey, 0);
// 进入固件设置
EnterSetupMenu();
// 从设置返回后, 重启
gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
return EFI_SUCCESS;
}
18.3 Boot Manager 库关键函数
// EDK2 Boot Manager Library 关键函数
// edk2/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
/**
加载并启动一个引导选项
@param[in] BootOption 引导选项指针
@param[out] ExitDataSize 退出数据大小
@param[out] ExitData 退出数据
@retval EFI_SUCCESS 引导选项正常启动并返回
@retval others 引导选项启动失败
**/
EFI_STATUS
EFIAPI
EfiBootManagerBoot(
IN BDS_BOOT_OPTION *BootOption,
OUT UINTN *ExitDataSize OPTIONAL,
OUT VOID **ExitData OPTIONAL
)
{
EFI_STATUS Status;
EFI_HANDLE ImageHandle;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
EFI_DEVICE_PATH_PROTOCOL *FilePath;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
CHAR16 BootOptionName[20];
//
// 1. 如果 BootOption 设置了 LOAD_OPTION_FORCE_RECONNECT,
// 强制重新连接设备
//
if (BootOption->Attributes & LOAD_OPTION_FORCE_RECONNECT) {
DEBUG((EFI_D_INFO, "[Bm] Force reconnect for Boot%04x\n",
BootOption->OptionNumber));
gBS->DisconnectController(BootOption->DeviceHandle, NULL, NULL);
gBS->ConnectController(BootOption->DeviceHandle, NULL, NULL, TRUE);
}
//
// 2. 将设备路径分解为设备部分和文件部分
//
// 例: "PciRoot(0)/Pci(0x1F,2)/Sata(0,0)/HD(1,GPT,...)/\EFI\BOOT\BOOTX64.EFI"
// 设备部分: "PciRoot(0)/Pci(0x1F,2)/Sata(0,0)/HD(1,GPT,...)"
// 文件部分: "\EFI\BOOT\BOOTX64.EFI"
//
EfiBootManagerExpandLoadOption(BootOption);
DevicePath = BootOption->DevicePath;
FilePath = BootOption->FilePath;
//
// 3. 连接设备路径上的所有节点
//
EfiBootManagerConnectDevicePath(DevicePath, &BootOption->DeviceHandle);
//
// 4. 检查安全启动
//
if (IsSecureBootEnabled()) {
Status = EfiBootManagerVerifyBootOption(BootOption);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "[Bm] Secure boot rejected\n"));
return Status;
}
}
//
// 5. 设置 BootCurrent 变量
//
UnicodeSPrint(BootOptionName, sizeof(BootOptionName),
L"Boot%04x", BootOption->OptionNumber);
gRT->SetVariable(
L"BootCurrent",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
sizeof(UINT16),
&BootOption->OptionNumber
);
//
// 6. 设置 LoadOptions
//
// 将引导选项的可选数据传递给引导加载程序
//
if (BootOption->OptionalData != NULL && BootOption->OptionalDataSize > 0) {
// LoadedImage->LoadOptions 和 LoadOptionsSize
// 引导加载程序可以读取这些值
}
//
// 7. 加载映像 (LoadImage)
//
Status = gBS->LoadImage(
FALSE, // BootPolicy = FALSE
gImageHandle, // 调用者
BootOption->DevicePath,
NULL,
0,
&ImageHandle
);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "[Bm] LoadImage error: %r\n", Status));
return Status;
}
//
// 8. 设置引导描述
//
Status = gBS->HandleProtocol(
ImageHandle,
&gEfiLoadedImageProtocolGuid,
(VOID **)&LoadedImage
);
if (!EFI_ERROR(Status)) {
LoadedImage->LoadOptionsSize = BootOption->OptionalDataSize;
LoadedImage->LoadOptions = BootOption->OptionalData;
}
//
// 9. 启动映像 (StartImage)
//
// 这个调用通常不返回——引导加载程序会
// 调用 ExitBootServices 并接管系统
//
Status = gBS->StartImage(
ImageHandle,
ExitDataSize,
ExitData
);
//
// 10. 如果引导加载程序返回...
//
// 这可能是因为:
// - BootNext 一次性引导完成
// - 引导加载程序出错
// - 用户选择了其他引导选项
//
// 清理 BootNext
BootNext = 0;
gRT->GetVariable(L"BootNext", &gEfiGlobalVariableGuid,
NULL, &DataSize, &BootNext);
if (BootOption->OptionNumber == BootNext) {
gRT->SetVariable(L"BootNext", &gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
0, NULL);
}
return Status;
}
19. 常见 BDS 问题与调试
19.1 常见问题
| 问题 | 可能原因 | 排查方法 |
|---|---|---|
| 无法引导到操作系统 | BootOrder 为空或错误 | 检查 NVRAM 中 BootOrder 和 Boot#### 变量 |
| “No bootable device” | 设备路径无效或设备未连接 | 检查设备枚举和驱动绑定 |
| 安全启动阻止引导 | 引导加载程序未签名或签名过期 | 检查 db/dbx, 尝试禁用安全启动 |
| 引导顺序无效 | BootOrder 指向不存在的 Boot#### | 重新设置引导顺序 |
| 控制台无输出 | GOP 驱动未加载或显示设备故障 | 检查 ConOut 设备路径 |
| 键盘无响应 | ConIn 设备路径无效 | 检查 ConIn 设备路径 |
| 固件设置无法进入 | 按键映射错误或热键冲突 | 检查 PlatformBootManager 热键注册 |
| PXE 引导超时 | DHCP 服务器无响应或网络未连接 | 检查网络连接和 DHCP 配置 |
| 退出设置后重启 | NVRAM 数据损坏或需清空 CMOS | 重置 NVRAM 或清除 CMOS |
| 引导加载程序崩溃 | ExitBootServices 内存映射不一致 | 检查 GetMemoryMap 和 MapKey |
19.2 BDS 调试方法
// BDS 调试技巧
// 1. 启用详细的 BDS 日志
// 在固件构建中设置:
// DEBUG_PROPERTY_DEBUG_LEVEL = 0x80000000 | DEBUG_INFO
//
// 典型的 BDS DEBUG 输出:
//
// [BDS] BDS Entry Point
// [BDS] Console init: ConOut GOP, ConIn USB KBD
// [BDS] BootOrder = { 0001, 0003, 0000 }
// [BDS] Trying Boot0001: "Windows Boot Manager"
// [BDS] DevicePath: HD(1,GPT,...)/\EFI\Microsoft\Boot\bootmgfw.efi
// [BDS] Connecting device path...
// [BDS] LoadImage: Success
// [BDS] StartImage: bootmgfw.efi
// [BDS] (bootmgfw.efi running...)
// [BDS] StartImage returned: EFI_SUCCESS
// [BDS] BootNext detected, cleaning up
// [BDS] Trying Boot0003: "ubuntu"
// 2. 使用 UEFI Shell 手动调试
//
// Shell> bcfg boot dump // 列出所有引导选项
// Shell> bcfg boot add 0 fs0:\EFI\BOOT\BOOTX64.EFI "My Boot"
// Shell> bcfg boot rm 3 // 删除引导选项 3
// Shell> reset -s // 进入固件设置
//
// Shell> fs0:
// FS0:\> \EFI\BOOT\BOOTX64.EFI // 手动加载引导加载程序
// 3. 修改 NVRAM 变量
//
// Shell> dmpstore BootOrder // 查看 BootOrder
// Shell> setvar BootOrder -guid 8BE4DF61-93CA-11d2-AA0D-00E098032B8C
// -bs -rt -nv =010003000200 // 设置 BootOrder = {1, 3, 0, 2}
//
// Shell> setvar Boot0001 -guid ... -bs -rt -nv =... // 设置 Boot0001
// 4. NVRAM 变量操作代码
EFI_STATUS
DumpBootVariables(
VOID
)
{
CHAR16 VariableName[256];
UINTN VariableNameSize;
EFI_GUID VendorGuid;
EFI_STATUS Status;
VariableNameSize = sizeof(VariableName);
Status = gRT->GetNextVariableName(
&VariableNameSize,
VariableName,
&VendorGuid
);
while (!EFI_ERROR(Status)) {
// 只显示 UEFI 全局变量
if (CompareGuid(&VendorGuid, &gEfiGlobalVariableGuid)) {
// 检查是否 Boot#### 变量
if (StrnCmp(VariableName, L"Boot", 4) == 0 ||
StrnCmp(VariableName, L"Driver", 6) == 0 ||
StrCmp(VariableName, L"BootOrder") == 0 ||
StrCmp(VariableName, L"BootNext") == 0 ||
StrCmp(VariableName, L"Timeout") == 0) {
DEBUG((EFI_D_INFO, " %s\n", VariableName));
}
}
VariableNameSize = sizeof(VariableName);
Status = gRT->GetNextVariableName(
&VariableNameSize,
VariableName,
&VendorGuid
);
}
return EFI_SUCCESS;
}
19.3 基于 POST 码的 BDS 调试
// BDS 阶段 POST 码分配
#define POST_BDS_START 0x40 // BDS 阶段开始
#define POST_BDS_CONSOLE_INIT 0x41 // 控制台初始化
#define POST_BDS_CONSOLE_DONE 0x42 // 控制台就绪
#define POST_BDS_DRIVERS_LOAD 0x43 // 驱动程序加载
#define POST_BDS_DRIVERS_DONE 0x44 // 驱动加载完成
#define POST_BDS_SETUP_ENTER 0x45 // 进入固件设置
#define POST_BDS_SETUP_EXIT 0x46 // 退出固件设置
#define POST_BDS_BOOT_MENU 0x47 // 引导菜单显示
#define POST_BDS_BOOT_START 0x48 // 开始尝试引导
#define POST_BDS_BOOT_ATTEMPT 0x49 // 尝试引导选项 N
#define POST_BDS_BOOT_SUCCESS 0x4A // 引导成功
#define POST_BDS_BOOT_FAIL 0x4B // 引导失败
#define POST_BDS_NO_BOOTABLE 0x4C // 无可用引导设备
#define POST_BDS_EXIT 0x4F // BDS 阶段结束
// 引导选项尝试时的细分 POST 码
#define POST_BDS_LOAD_IMAGE 0x50 // LoadImage 开始
#define POST_BDS_SECURE_VERIFY 0x51 // 安全启动验证
#define POST_BDS_START_IMAGE 0x52 // StartImage 开始
20. 参考资料
规范
- UEFI Specification 2.x — Chapter 3: Boot Manager
- Section 3.1: Boot Manager Overview
- Section 3.2: Boot Options
- Section 3.3: Boot Order
- Section 3.4: Boot Manager Programming
- UEFI Specification 2.x — Chapter 4: Protocols (Boot Manager Protocol)
- UEFI Specification 2.x — Chapter 7: Secure Boot
- UEFI Platform Initialization (PI) Specification 1.7+ — Volume 5: Standards
EDK2 源码
edk2/MdeModulePkg/Universal/BdsDxe/— BDS DXE 驱动edk2/MdeModulePkg/Library/UefiBootManagerLib/— Boot Manager 核心库BmBoot.c— 引导执行BmLoadOption.c— 引导选项管理BmConnect.c— 设备连接BmConsole.c— 控制台初始化
edk2/MdeModulePkg/Library/PlatformBootManagerLibNull/— 默认平台 BDS(空实现)edk2/SecurityPkg/Library/SecureBootLib/— 安全启动库
平台 BDS 参考实现
edk2-platforms/Platform/Intel/MinPlatformPkg/— Intel MinPlatformBds/— Intel 参考 BDS
edk2-platforms/Platform/Intel/Vlv2TbltDevicePkg/— Valley Island 平台edk2-platforms/Silicon/Intel/CoffeelakeSiliconPkg/— Coffee Lake 芯片组
调试和工具
- UEFI Shell — Shell 命令:
bcfg,dmpstore,setvar,reset - UEFI 调试器 — 基于 ITP/JTAG 的 UEFI 调试
- OVMF (QEMU) — 使用 QEMU 模拟 UEFI,方便 BDS 调试
本文全面解析了 UEFI BDS 阶段的架构设计、实现机制和关键流程。BDS 是用户最直观感受到的 UEFI 阶段——它管理着固件设置界面、引导菜单、引导设备选择、安全启动策略等直接影响用户体验的核心功能。从 DXE 就绪后的控制台初始化,到 Boot Manager 遍历引导选项执行引导加载程序,再到最终 ExitBootServices() 移交系统给 OS,BDS 阶段完成了一次优雅的控制权转交。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)