3. UEFI启动流程模拟

任务目标

本案例要求使用模拟器(QEMU + OVMF)运行UEFI固件,观察并记录从SEC阶段到DXE阶段的启动流程关键日志,理解各阶段边界与典型输出特征。

环境准备

  • Linux主机(或WSL2),已安装 qemu-system-x86_64 与 ovmf 固件包(Debian/Ubuntu:apt install qemu-system-x86 ovmf)
  • OVMF固件路径通常为 /usr/share/OVMF/OVMF_CODE.fd 与 /usr/share/OVMF/OVMF_VARS.fd
  • 需要一份可启动的UEFI Shell镜像(Shell.efi),可从TianoCore发行包获取,或使用 UEFI Shell ISO

实现与运行

使用QEMU加载OVMF固件,开启调试串口输出,将固件日志重定向到文件。SEC与PEI阶段的早期日志通过调试端口(0x402)输出,DXE阶段后日志经串口(0x3F8)输出。

创建启动脚本 run_uefi.sh:

#!/bin/bash
qemu-system-x86_64 \
  -machine q35,accel=tcg \
  -m 512M \
  -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.fd \
  -drive if=pflash,format=raw,file=/usr/share/OVMF/OVMF_VARS.fd \
  -serial file:uefi_boot.log \
  -debugcon file:uefi_debug.log \
  -global isa-debugcon.iobase=0x402 \
  -cdrom UEFI_Shell.iso \
  -boot d \
  -no-reboot

执行 chmod +x run_uefi.sh && ./run_uefi.sh,等待约30秒后强制关闭QEMU(Ctrl+A X)。

关键日志观察点

查看 uefi_debug.log(SEC/PEI早期)与 uefi_boot.log(DXE及以后):

# SEC阶段特征:CPU初始化、临时RAM建立
grep -n "SecCore\|TempRam\|ResetVector" uefi_debug.log | head -20

# PEI阶段特征:内存发现、PEIM调度
grep -n "PeiCore\|MemoryDiscovered\|PpiList" uefi_debug.log | head -30

# DXE阶段特征:DXE核心启动、驱动加载
grep -n "DxeCore\|DriverBinding\|Loading driver" uefi_boot.log | head -40

预期输出示例

以下为代码逻辑可唯一推出的典型日志片段(非实测数据):

# uefi_debug.log 中可见
SecCoreStartupWithStack: Entry point at 0xFFF01000
TempRamInit: Initializing temporary RAM...
PeiCore: Entry point at 0xFFF1A000
MemoryDiscovered: PPI published, system memory at 0x80000000

# uefi_boot.log 中可见
DxeCore: Entry point, DXE phase started
Loading driver: PcRtc (Real Time Clock)
Loading driver: Fat (File System)

验证方法

  1. 确认 uefi_debug.log 非空,且包含 SecCoreStartupWithStack 与 PeiCore 字样,说明SEC→PEI流程已执行
  2. 确认 uefi_boot.log 包含 DxeCore 字样,说明已进入DXE阶段
  3. 两个日志文件时间戳连续,无长时间中断,说明阶段切换正常
  4. 若日志中同时出现 MemoryDiscovered(PEI)与 DxeCore,则完整观察到SEC→PEI→DXE的启动链路

常见问题

  • 日志为空:确认 -debugcon 与 -serial 参数正确,且OVMF固件包含调试输出支持(发行版OVMF默认开启)
  • 无法引导Shell:确认ISO包含 EFI/BOOT/BOOTX64.EFI,或改用 -drive file=shell.img,format=raw 直接加载FAT格式镜像
  • 阶段日志混杂:SEC/PEI早期输出走debugcon,DXE后走serial,若需统一可加 -serial mon:stdio 观察实时输出