windows/pci: vIOMMU for nested passthrough, and a data disk built with the image

Three things, all in service of putting Proxmox in a VM that can still hand a
GPU to its own guests, and of a Windows VM whose profile survives its OS disk.

pci.viommu.enable emits `-device intel-iommu,intremap=on,caching-mode=on` and
forces kernel-irqchip=split, which interrupt remapping requires. Without an
IOMMU of its own a guest cannot bind a passed-through device to vfio-pci, so
it can never forward one on. The device leads the command line because QEMU
realizes devices in order and intel-iommu must precede what it translates.

pci.vgaPassthrough (default true, so nothing changes for existing VMs) makes
x-vga=on optional. It was forced on the first passthrough device, which is
wrong for a card the guest only forwards onward: it claims the VGA path the
emulated console adapter needs.

customizeImage gains extraDisk, a blank disk attached for the Audit Mode boot
and emitted as the derivation's `data` output. generalize uses it for dataDisk
and profilesDirectory, so the disk is partitioned and the profile relocated
under OOBE in the build VM. That is what removes the need for delayOobeRun --
previously the volume ProfilesDirectory names could not exist until the image
reached real hardware. A second output rather than a directory keeps ${image}
meaning the OS qcow2 for every existing consumer.

generalize also picks up staticIP and profilesDirectory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
This commit is contained in:
Git Sagar 2026-09-09 22:27:44 -03:00
parent 9784736260
commit c213fc4db9
4 changed files with 122 additions and 4 deletions

View file

@ -262,11 +262,36 @@ with lib;
default = [];
description = "PCI device addresses to passthrough via VFIO (e.g. [\"0000:03:00.0\" \"0000:03:00.1\"]).";
};
pci.vgaPassthrough = mkOption {
type = types.bool;
default = true;
description = ''
Route legacy VGA to the first passthrough device (x-vga=on), which a
guest needs in order to drive that card as its own display.
Turn it off when the guest only forwards the device onward to a nested
guest: x-vga=on claims the VGA path the emulated adapter wants, and the
nested guest does its own routing anyway.
'';
};
pci.romFile = mkOption {
type = types.nullOr types.path;
default = null;
description = "GPU VBIOS ROM file for the first passthrough device. Required when GPU PCI ROM BAR doesn't expose the full VBIOS (common with AMD Navi+).";
};
pci.viommu.enable = mkOption {
type = types.bool;
default = false;
description = ''
Give the guest a virtual Intel IOMMU, so a guest that is itself a
hypervisor can bind a passed-through device to vfio-pci and hand it on
to a nested guest. Without one the guest sees no IOMMU and cannot
re-assign anything it was given.
Implies kernel-irqchip=split, which interrupt remapping requires and
which replaces the full in-kernel irqchip cpu.hideVirtualized asks for.
'';
};
usb.hostDevices = mkOption {
default = [];