I found an HP Z820 in a street trash container on the way back from taking out the rubbish. Factory plastic still on the side panels, side-panel key still attached, one dead spider inside. It threw fan errors and a RAM training failure; a bottle of isopropyl and a reseat later it booted straight into Windows 10.
At 2 AM, looking at a board with four PCIe x16 slots, the obvious stupid idea arrived: a retro LAN party in one box. Two Windows XP VMs and two Windows 7 VMs, simultaneously, each with a real graphics card driving a real monitor and its own keyboard and mouse.
It works. Video: https://www.youtube.com/watch?v=cfP1x4R3XEc — XP seats running 3DMark06, Win7 seats running Unigine Heaven. Three monitors on camera because I only own three; the fourth seat gets switched in partway through, and there’s a laptop showing htop on the host.
Everything below is written up properly in the repo: Jan Zubr / 2AM Idea · GitLab
The machine
| Part | |
|---|---|
| HP Z820 | free, out of the trash |
| 2× Xeon E5-2667 v2 | 8c/16t each, 32 threads, two NUMA nodes |
| 128 GB ECC DDR3 1866 | 8× 16 GB RDIMM |
| 2× Quadro K4000 | the XP seats — official XP drivers exist for these |
| Quadro P2200 + GTX 1060 6GB | the Windows 7 seats |
| Micron 480 GB enterprise SSD | came with the machine |
| StarTech PEXUSB3S44V | four independent USB 3.0 host controllers on one card |
$440 out of pocket on top of the free chassis, itemised in the repo. Biggest single surprises: I needed a second LGA2011 cooler ($80), and I couldn’t fit two dual-slot GPUs, so one card had to be replaced with a slim variant.
The USB card deserves a word, because it’s the least glamorous and most important part. Software USB passthrough routes every click through the host’s translation layer first — fine for Excel, miserable for Counter-Strike. That StarTech board carries four separate host controllers, so each seat gets a whole physical controller through IOMMU passthrough: no emulation, no host-side input queuing, and one seat crashing can’t disturb another.
Windows 7: UefiSeven, and one mandatory setting
Win7 predates practical UEFI graphics — bootvid still expects legacy Int10h/VESA to set a mode, and OVMF is a UEFI Class 3 machine with no Int10h at all, so Setup hangs at “Starting Windows” forever.
UefiSeven fabricates a fake VESA/Int10h handler in low memory and chainloads the real boot manager. Two gotchas that each cost me hours:
- On a Windows 7 DVD there is no
\efi\boot\bootx64.efi. UEFI boots the ISO through its El Torito image, which isefi/microsoft/boot/efisys.bin— the loader inside that FAT image is what must be replaced. Patching the ISO’s directory tree does nothing. efidisk0must beefitype=2m. Proxmox’s 4 MB OVMF build write-protects the legacy VGA ROM window at0xC0000that UefiSeven writes its shim into, and it aborts withUnable to unlock VGA ROM memory at C0000. The 2 MB legacy build leaves it writable. Verified failing on 4m and passing on 2m under both q35 and i440fx — it’s a firmware-build difference, not a chipset one.
Also: install the NVIDIA driver with the card attached but rombar=0, so it’s present-but-un-POSTed. The installer refuses to run with no GPU, but a POSTed card takes the display over before there’s a driver to drive it and you’re left with no screen. Then swap rombar=0 for the full romfile= and it comes up properly.
Windows XP: the actually hard one
SeaBIOS can only initialise the host’s boot_vga card, so every other slot gives XP Code 10, and legacy VGA is a single host-routed resource anyway — inherently one seat.
Route that works: boot OVMF so the card initialises via its own UEFI GOP driver, then hand off to CSMWrap (SeaBIOS as a UEFI application) for the legacy BIOS services XP needs. No x-vga anywhere, so no single-owner resource, so seats can run concurrently.
Then XP bugchecked 0xA5 (0x00000002) — ACPI_ROOT_PCI_RESOURCE_FAILURE — in a loop. Cause: Proxmox adds two pci-bridge devices to every i440fx VM unconditionally, there’s no config option to turn them off, and XP’s acpi.sys chokes on them under CSMWrap. Proven by running PVE’s own generated QEMU command line by hand and deleting only those two lines: XP boots.
The fix is a wrapper that takes /usr/bin/kvm via dpkg-divert and drops those two arguments for allowlisted VMIDs. It self-tests before installing, rolls itself back if kvm --version breaks afterwards, and refuses to start a VM whose remaining devices still reference a bus it removed.
The reason it matters is the bit I’d have appreciated knowing earlier: the usual advice for 0xA5 is “switch XP to a non-ACPI HAL”. Do that and XP loses the ACPI PM timer, falls back to the PIT, and no hypervisor can tick it precisely enough for game engines — games run in slow motion, and no combination of driftfix or lost_tick_policy fixes it. It also pegs a host core per XP vCPU and breaks qm shutdown. Keeping ACPI is the whole point.
Other XP-specific traps: CSMWrap’s SeaBIOS crashes QEMU outright if the CSM boots from xHCI, AHCI or NVMe (its BIOS-proxy helper core enters with ESP = 0 and jumps into unbacked MMIO — racy, and characterised down to registers in the repo), so the CSM disk must be PIIX IDE on i440fx. And CSMWrap permanently reserves one logical CPU that spins forever, so budget a pegged core per XP seat and never give it cores: 1.
The thing that applies to both
Dump each card’s own ROM, and pass it whole. VBIOS revisions differ per physical card — the two K4000s here have different firmware, and using the wrong one produces visible framebuffer corruption. The dump must contain both an x86 legacy image and a UEFI/GOP image; strip either and you get Code 43 or a card OVMF refuses to drive. There’s a tool in the repo that dumps and validates, including through the PROM window for a boot_vga card whose sysfs ROM is a truncated shadow copy.
And: once the real card is POSTed, the Proxmox web console stops updating. Black on XP, a frozen “Starting Windows” frame on Win7. That’s success, not a hang — check the physical monitor before debugging anything. rd_bytes from qm monitor settles it definitively: 0 means firmware never reached the disk, ~13 MB means an early bugcheck, 150 MB+ means a real boot.
What’s in the repo
Two step-by-step guides (every step has a check), the wrapper plus its installer, the ROM dump/validate tool, seats/ with all four VMs’ actual configs and the host-side state, and the full debugging record including the CSMWrap crash forensics and a bare-QEMU reproducer harness. MIT.
All of it verified on this one host — that’s the honest limitation. If anyone tries it on different hardware, especially with AMD cards or a different XP-capable GPU, I’d like to know how it goes.
Next on the list: a pile of LAN games, dedicated server VMs on the spare threads, and a RAM disk for absurd load times. Happy to answer anything.