English

文章

keelOS: 远古系统?我们也不会放弃!

Windows XP SP2 和 Windows Server 2003 R2 已经在 keelOS 上装好能用,用的全是系统自带的驱动,不用往 guest 里装任何东西。为此 keelOS 给 bhyve 新写了 3 个设备(IDE、PCnet 网卡、UHCI),接上了 SeaBIOS 和 VGA/VBE 显示,并修正了原版 bhyve 在老系统面前才会暴露的一批错误。

为什么还要管老系统

老系统没有消失,它们跑在虚拟机里。小公司的财务软件、工控程序、只认某个版本 Windows 的专用工具,很多年前就从物理机搬进了 VMware,一直跑到今天。

Broadcom 改了 VMware 的许可方式之后,受影响最大的是小公司和家用实验室,正是 keelOS 的用户。要接住这批用户,他们手里的老虚拟机就得能跑。

在原版 bhyve 上装 Windows XP 走不通,原因有三层:

  • 固件:老系统从传统 BIOS 引导。bhyve 的固件是 UEFI,上游 EDK2 已经删掉了兼容模块(CSM)。
  • 设备:bhyve 的磁盘是 AHCI、NVMe、virtio,网卡是 virtio 和 e1000,USB 是 xHCI。Windows XP 一个驱动都不带。
  • 细节:老系统用的是 8259 中断控制器、PIT 时钟、实模式、VGA 寄存器。这些路径在 bhyve 里很少有人走,错误一直没被发现。

keelOS 的做法是对标 ESXi 和 QEMU/KVM:它们给老系统什么虚拟硬件,我们就给什么。这样迁移过来的虚拟机里驱动已经齐了,不用改 guest。

老系统要的是另一套虚拟硬件

从固件到中断控制器,老系统和新系统看到的是两台不同的机器。

新旧两套虚拟硬件:6 层对照
新旧两套虚拟硬件:6 层对照

左边是 bhyve 原有的、新系统在用的硬件。右边是老系统认得的硬件,上面五层是 keelOS 补上的,最下面一层 bhyve 本来就有,但有错。

固件:直接用 SeaBIOS

传统 BIOS 引导的 guest 直接用 SeaBIOS 当固件,和 QEMU/KVM 一样。bhyve 本来就有 SeaBIOS 需要的大部分接口(fw_cfg、ACPI 表、E820),要补的是让 SeaBIOS 认出这台机器:

  • 主机桥装成 Intel i440fx。SeaBIOS 只有看到 QEMU 的主机桥才用 fw_cfg。
  • 固件的最后 128K 同时放在 1 MB 以下。CPU 复位后跳到的地址在那里,bhyve 原来只把固件映射在 4 GB 顶端。
  • ACPI 的入口(RSDP)放进 F 段,其余的表只经 fw_cfg 交给固件。

SeaBIOS 起来之后,Debian 13 第一次就引导到了登录。FreeBSD 却停在引导程序里,查出来的是原版 bhyve 的一个错:

现象 原因 修正
FreeBSD 的 gptboot 转了 35 圈就停住 BIOS 给 PIT 写计数 0(8254 把 0 当成 65536,也就是 18.2 Hz 的时钟滴答)。bhyve 遇到 0 什么都不启动,guest 永远等不到 IRQ 0 先把 0 换成最大计数再启动定时器
SeaBIOS 报 I/O 地址空间用完 探测 BAR 大小时 bhyve 读回的高 16 位是 0,SeaBIOS 把 64 字节的 BAR 算成将近 4 GB 读回写入的高位,和 QEMU 一样

Linux、FreeBSD 内核和 UEFI 固件都写非零的计数,所以 PIT 这个错一直没人碰到。我们在第二台开发机上用原生的 FreeBSD 14.5 做了对照:原版卡住,只加这一个修正就能到登录。FreeBSD 上游在 2026-09-21 也修了同一处。

SeaBIOS 是 LGPLv3,作为单独的固件文件发布,同时提供源码,用户可以换成自己编的。在 keelOS 里只要在虚拟机配置里写 "bootrom": "bios"。

显示:VGA 和 VBE

老系统的画面分三段:开机时的 VGA 文字模式,安装程序的 16 色图形模式,进系统后的 VBE 高分辨率。bhyve 里有一份 VGA 模拟的代码,但原版遇到 vga=on 直接拒绝启动,VBE 则完全没有。

现象 原因 修正
画面全黑 guest 写显存地址 0xB8000 时写进了普通内存,VGA 模拟看不到 低端内存空出 0xA0000–0xBFFFF 给 VGA
bhyve 退出:Failed to emulate instruction 指令模拟器一开头就拒绝实模式 补上实模式和 16 位寻址
FreeBSD 内核起来后屏幕变黑 bhyve 的 ACPI 表里写死了“没有 VGA” BIOS guest 不写这个标志
Windows 10 安装界面黑屏 Windows 8 以后的基本显示驱动要 VBE 的线性帧缓冲 实现 Bochs VBE 接口,配 SeaVGABIOS
日文版 Server 2003 的安装界面只有一条条蓝色块 bhyve 的 VGA 写模式有三处错:AND、OR、XOR 从不执行,旋转计数取错,位掩码以外的值丢失 四种写模式按 VGA 的定义重写
Windows 的显示适配器报代码 12 ACPI 里 PCI 总线的资源窗口没有 VGA 显存,也不包括 SeaBIOS 分配 BAR 的地址 资源窗口补上这两段

日文版的安装程序不用文字模式,它在 640×480 16 色的图形模式里自己画汉字,所以才碰到了写模式的错。英文版 XP 用文字模式,之前一直没暴露。

修代码 12 时还带出一个问题:Windows 用 SSE 指令往显存写数据,指令模拟器不认识,bhyve 直接退出。现在模拟器支持 MOVUPS、MOVDQA 这一组 SSE 移动指令。

VNC 和 RDP 都能看到这些画面,从 SeaBIOS 的启动信息一直到桌面。

磁盘、网卡、鼠标:新写三个设备

三个设备都是照硬件手册和规范从头写的。QEMU 和 KVM 是 GPL 许可,我们拿它们当对照组看行为,不拷它们的代码。

设备 模拟的硬件 依据 谁需要它
IDE 控制器 Intel PIIX3 IDE,硬盘和 ATAPI 光驱,PIO 和 DMA ATA/ATAPI-6、PIIX3 数据手册、Bus Master IDE 规范 Windows XP、Server 2003
网卡 AMD PCnet-PCI II AMD Am79C970A 数据手册 Windows XP、Server 2003
USB 1.1 控制器 Intel PIIX3 UHCI,上面接坐标板(tablet) Intel UHCI 1.1 设计指南 Windows 7、XP、Haiku

为什么是 IDE:XP 自带驱动、SeaBIOS 能引导、接口简单,三条同时满足的只有它。XP 安装盘的光驱也要 ATAPI。网卡同理:XP 不带 e1000 的驱动,自带驱动能用的只有 PCnet。

为什么要 UHCI:Windows 7 没有 xHCI 驱动,PS/2 鼠标又只报相对位移,远程控制台里指针跟不上手。USB 坐标板报的是绝对坐标,接在 UHCI 上,老系统用自带的驱动就能认。

三个设备都支持挂起和恢复。写的过程中还修了这些:

现象 原因 修正
从 IDE 光驱装系统极慢,约 300 KB/s bhyve 模拟 REP INSW 时一个字做一次地址翻译 数据端口一次最多搬 4096 字节,快了 18 倍
Windows 枚举了 IDE 控制器但不启动它 SeaBIOS 分配的 I/O 端口不在 ACPI 声明的窗口里 窗口扩到 0xFFFF
不用 ACPI 的系统收不到设备中断 bhyve 接中断引脚的方式和 SeaBIOS 按 i440fx 算出来的不同 BIOS guest 改用 i440fx 的接法
Windows 7 装完后启动报 A disk read error occurred bhyve 把后端存储的块大小(16K)报成物理扇区,Windows 7 照它格式化出 16K 的簇,自己的引导代码读不了 物理扇区最多报 4K
Haiku 里指针不动 Haiku 把队列头挂在另一个队列头下面,第一版 UHCI 跳过了这种情况 按规范进入下一层队列头

Windows XP:装到桌面之前的五个问题

设备都有了,XP 还是装不完。下面五个问题里,四个是原版 bhyve 和 vmm 偏离了真实硬件的行为,一个是性能。

现象 原因 修正
第一次从硬盘启动蓝屏 STOP 0x7F guest 屏蔽了 8259 的中断请求,vmm 却还记着“有中断”,注入时 PIC 只能给出伪中断向量 PIC 已经没有请求时丢掉这次中断,不注入
安装程序多出一屏“自动配置” bhyve 的 ACPI 里有 SPCR 表,XP 把机器当成了无显示器的服务器 XP 的模板不给 SPCR 表,和 ESXi、QEMU 一样
图形安装阶段极慢,“安装设备”要 9 分半以上 XP 每次升降中断级别都写本地 APIC 的 TPR,每次都要退出到 hypervisor 模拟,每个 vCPU 每秒约 9 万次 打开 APICv,由 CPU 硬件处理 TPR
XP 里没有网卡 XP 的驱动往 PCI 配置空间的只读寄存器写 0。真硬件不理,bhyve 存了下来,设备的厂商 ID 变成 0 只读寄存器写不进去
XP 重启或关机时 bhyve 崩溃 XP 用 NMI 停掉另一个处理器,它的 NMI 是任务门;bhyve 模拟任务切换时把标志寄存器的固定位也清掉了,CPU 拒绝进入 guest 固定位按处理器的规定保留

APICv 的效果最明显。同一段“安装设备”:

平台 用时
KVM(对照) 2 分钟
bhyve,没有 APICv 9 分半以上
bhyve,打开 APICv 2 分半

这也是 APICv 第一次在 keelOS 的真机上跑起来。

查这些问题靠三个办法。第一,同一张安装盘在 KVM 上用同样的虚拟硬件装一遍,比访问序列和时间。第二,把 guest 的磁盘做快照,挂出来读 XP 自己带时间戳的安装日志:进度条不动不等于卡死。第三,用开源的 ReactOS 当替身,它和 XP 同一代内核接口,能把内核日志打到串口上。

现在能跑哪些系统

下面都是在真机上实测的结果(第二台开发机,SeaBIOS,2026-09-29 到 09-30),用的都是系统自带的驱动。

系统 虚拟硬件 结果
Windows XP Professional SP2 IDE、PCnet、UHCI 坐标板 装好:桌面、网络(DHCP)、重启、关机、挂起恢复、RDP
Windows Server 2003 R2(日文版) 同 XP 装好:桌面、网络、重启、关机
Windows 7 SP1 x64 IDE 或 AHCI、UHCI 坐标板 装好:指针跟手,设备管理器里没有不明设备
Windows 10 22H2(BIOS 引导) AHCI、VBE 1024×768 显示正常,显示适配器没有代码 12
Haiku R1/beta6 AHCI、e1000、UHCI 坐标板 装好,从硬盘启动
FreeBSD 14.5、Debian 13(BIOS 引导) virtio-blk、AHCI、IDE 启动到登录:FreeBSD 17 秒,Debian 6–7 秒

老系统同样要过 keelOS 的招牌测试:宿主系统(root)重启时 guest 不关机。我们挑了最难的时刻来测,XP 安装到一半、正在往硬盘拷文件的时候(第一台开发机,2026-09-30):

测试 结果
拷文件时挂起 37 秒再恢复 接着拷,11% 到 23%
拷文件时 root 重新加载 guest 暂停 35.8 秒,接着拷到 43%
图形安装阶段挂起 32 秒再恢复 鼠标指针前后都能动

BIOS guest 的显示状态(VGA 寄存器、256K 显存、VBE 寄存器)也进了检查点:停在文字控制台的 Debian 恢复后画面原样,新的输出接着画。

在 keelOS 里建一台 XP 只要一条命令,模板里已经选好了上面这套硬件:

keel vm create --template windowsxp --alias xp --iso XP.iso --nic bridge0

还没做完的

  • APICv 默认还是关的。打开它时,恢复出来的 guest 会丢电平触发的中断(XP 恢复后鼠标不动)。修正已经写好,还没有上真机测。测过、再跑完各种 guest 的回归和 root 重启,才会改成默认打开。
  • BIOS guest 没有启动顺序。SeaBIOS 现在先试硬盘再试光驱,硬盘上有签名但没有引导代码时会卡住。
  • VBE 模式下的挂起恢复(Windows 桌面)和 guest 停在 SeaBIOS 里时的 root 重启还没测。
  • root 重启之后 XP 拷文件变慢(每秒 0.7 MB 降到 0.15–0.3 MB),原因还没查。
  • ESXi 的其余虚拟硬件。LSI Logic 的 SCSI 控制器先搁置了,PVSCSI、VMXNET3、E1000e 还没做。迁移工具的第一版改用离线修改 guest 注册表的办法。
  • SMBIOS 还是 SeaBIOS 自己生成的那一份,bhyve 的表没有传过去。

界线在哪里

Windows XP 是 keelOS 支持的最老的系统。DOS、Windows 3.x、Windows 9x 不在范围内:它们要软驱、声卡和更快的 VGA,这些是模拟器(DOSBox、86Box、QEMU)的长项,不该跑在硬件虚拟化的 hypervisor 上。keelOS 不放弃的,是还在干活的老系统。

结语

支持老系统,一半是补设备,一半是还债。IDE、PCnet、UHCI 是新写的;PIT 的计数 0、8259 的伪中断、只读的 PCI 寄存器、VGA 的写模式,则是原版一直就有的错,只是新系统从不走那些路径。老系统按当年硬件的实际行为写驱动,虚拟硬件差一点都不行。

修好的地方不只服务老系统:只读的 PCI 寄存器、PIT、伪中断的修正对所有 guest 生效,APICv 对新系统同样是提速。

下一步是把 APICv 改成默认打开,给 BIOS guest 加上启动顺序,然后做从 ESXi 导入虚拟机的工具。

设计、每一步的现象和测量数据见 keelOS 仓库 docs/30(从 VMware 迁移,传统 BIOS guest)。

← 文章 · 为什么选 keelOS