QEMU启动OVMF shell超时分析
实验环境
| 类型 | 描述 |
|---|---|
| OVMF固件版本 | edk2 20211219 |
| QEMU版本 | 20260729 |
| QEMU运行脚本内容 | qemu-system-x86_64.exe -bios "OVMF.fd" -M "pc" -m 256 -cpu "qemu64" -boot order=dc -serial stdio -hda fat:rw:FAT |
运行现象
可以正常boot OVMF固件,但在QEMU使用的屏幕上打印">>Start PXE over IPv4"之后,大概会等待1min,才会进入UEFI shell界面。
现象对应的代码路径
BDS 尝试启动 PXE 启动项时,走的是 EfiPxeLoadFile()(PXE 驱动提供的 EFI_LOAD_FILE_PROTOCOL),执行顺序是:
-
介质检测 :
NetLibDetectMediaWaitTimeout(..., PXEBC\_CHECK\_MEDIA\_WAITING\_TIME, ...),最多等 20 秒 网线/链路就绪。 -
PxeBc->Start() :在
PxeBcImpl.c:129 打印>>Start PXE over IPv4,这里只做初始化,不等待。 -
PxeBcLoadBootFile → PxeBcDiscoverBootFile :真正耗时的是这一步里的
PxeBc->Dhcp(),即 DHCP 的 D.O.R.A. 过程。
Root Cause分析:DHCP D.O.R.A. 超时
进入 PxeBcDhcp4Dora() 后,配置了 DHCP 重试参数:
c
Config.DiscoverTryCount = PXEBC_DHCP_RETRIES; // = 4
Config.DiscoverTimeout = mPxeDhcpTimeout; // = { 4, 8, 16, 32 } 秒
- 超时表定义在
PxeBcDhcp4.c:28:UINT32 mPxeDhcpTimeout[4] = { 4, 8, 16, 32 }; - 重试次数定义在
PxeBcImpl.h:69:#define PXEBC_DHCP_RETRIES 4
关键点 :QEMU 默认的 user-mode(SLIRP)网络不会提供 DHCP/PXE 服务 ,所以客户端发出 DHCP Discover 广播后永远等不到 Offer。于是按 4s → 8s → 16s → 32s 依次重发 4 次,累计等待 4+8+16+32 = 60 秒 后返回 EFI_TIMEOUT,PXE 启动失败,BDS 才转向下一个启动项。
也就是说,打印 >>Start PXE over IPv4 之后的"长时间停住",本质是在干等一个不存在的 DHCP 服务器,约 60 秒。如果虚拟机里配了多个网卡,每个网卡都会再重来一遍 60 秒。
验证方法
-
在 QEMU 日志里打时间戳:从打印
>>Start PXE over IPv4 到下一个启动项/报错,间隔应该接近 60 秒(若还叠加了介质检测,则是 20s + 60s)。 -
观察是否打印
Station IP address is ...(DHCP 成功才打印,PxeBcDhcp4.c:1741)------没有打印说明 DHCP 一直超时。
解决办法
-
根本避免 PXE 尝试(推荐,最省事):
- 用
-boot order=c(磁盘优先),让 PXE 不在启动顺序前段; - 或在 QEMU 命令行不给网卡指定
bootindex,配合-boot strict=on,使网卡不进入 BootOrder; - 如果根本不需要网络启动,直接用
-net none/ 不添加-netdev。
- 用
-
确实需要网络启动:在宿主机配置 DHCP/PXE 服务器(如 dnsmasq),QEMU 的 SLIRP 不提供 PXE 服务,必须外接。
-
只想缩短卡顿时长 (改代码重编):把
mPxeDhcpTimeout 或PXEBC\_DHCP\_RETRIES 调小(例如改成{2, 4, 8} 或 retries=2),可显著减少 60s 等待,但这属于修改 UEFI 网络栈行为。
一句话:慢的原因是 UEFI 的 PXE 驱动在没有 DHCP 服务器的环境下按 4/8/16/32 秒做了 4 次 Discover 重试,累计约 60 秒 ,与 GetFrontPageTimeoutFromQemu/PcdPlatformBootTimeOut 无关。
最终选择方案
在qemu运行脚本中添加-net none,不再出现超时等待。
完整的QEMU运行脚本内容如下:
qemu-system-x86_64.exe -bios "OVMF.fd" -M "pc" -m 256 -cpu "qemu64" -boot order=dc -net none -serial stdio -hda fat:rw:FAT