旧win虚拟化kvm

目标与现状

把旧 Windows 10 LTSC 系统盘(Hyper-V 动态 VHDX)在 NixOS 上以 KVM/libvirt 虚拟机运行,按需进入;原始系统盘文件全程不动。

项值
域max2-win10ltsc(libvirt qemu:///system)
磁盘/s500data/kvm/vms/max2-win10ltsc.qcow2(源 VHDX 的转换副本)
固件 / 机型UEFI(OVMF,libvirt firmware='efi')+ q35
设备磁盘 AHCI(SATA)、网卡 e1000e、SPICE 控制台(只听 127.0.0.1)
资源8 GiB 内存 / 4 vCPU;clock offset='localtime'(Windows 用本地时间)
自启动关闭(按需进入,不随宿主开机启动)

前置事实(实测)

项实测值
源盘/s500data/kvm/max2_win10ltsc_system.VHDX,动态盘,虚拟 2 TB / 实占 198.56 GB
分区表GPT:p1 = EFI 系统分区(vfat);p2 = NTFS(Windows 卷)
ESP 引导\EFI\Boot\bootx64.efi 存在 ⇒ UEFI 可引导
加密无 BitLocker
休眠态无 hiberfil.sys(干净关机)
宿主能力内核 ntfs3 / loop / nbd / kvm_amd 齐备,/dev/kvm 就绪

实现过程

虚拟化栈(声明式落地)

模块:/etc/nixos/modules/system/service/libvirt.nix;在 configuration.nix 的 imports 清单里放开一行即启用(该仓库惯例:一行一模块、打开功能 = 取消注释)。

  • virtualisation.libvirtd.enable = true —— VM 管理守护进程,并创建默认 NAT 网络(virbr0)
  • qemu.swtpm.enable = true —— 预置 TPM(Win10 用不到,日后跑 Win11 直接可用)
  • qemu.vhostUserPackages = [ pkgs.virtiofsd ] —— 宿主目录共享给 guest 所需
  • boot.kernelModules = [ "kvm-amd" ] —— AMD-V(本机 svm)
  • networking.firewall.trustedInterfaces = [ "virbr0" ] —— 信任 NAT 网桥,出网不被挡
  • 系统包:virt-manager、virt-viewer、spice-gtk、qemu、qemu-utils、virt-v2v、virtio-win、cdrkit、libguestfs

生效:

sudo nixos-rebuild switch

注意:NixOS 26.05 已移除 virtualisation.libvirtd.qemu.ovmf 子模块(写进配置会在求值期断言失败);OVMF 固件现随 qemu 默认提供。

磁盘准备(转成自包含副本,源盘不动)

qemu-img convert -p -f vhdx -O qcow2 \
  /s500data/kvm/max2_win10ltsc_system.VHDX \
  /s500data/kvm/vms/max2-win10ltsc.qcow2

产物:virtual size 1.86 TiB、disk size 177 GiB、corrupt: false;源 VHDX 的 size 与 mtime 转前转后一致(确未改动)。

域定义与启动

域 XML:/s500data/kvm/vms/win10-virtual.xml(项目仓库内另存一份留档)。关键形态:

<os firmware='efi'>
<type arch='x86_64' machine='q35'>hvm</type>
</os>
<clock offset='localtime'/>
<disk type='file' device='disk'>…<target dev='sda' bus='sata'/></disk>
<interface type='network'><source network='default'/><model type='e1000e'/></interface>
sudo virsh -c qemu:///system define /s500data/kvm/vms/win10-virtual.xml
sudo virsh -c qemu:///system start max2-win10ltsc

磁盘池:vms → /s500data/kvm/vms。

网络(libvirt 默认 NAT)

NixOS 只把 libvirt 自带的 default.xml 拷到 /var/lib/libvirt/qemu/networks/,还需设一次自动启动:

sudo virsh -c qemu:///system net-autostart default
sudo virsh -c qemu:///system net-start default

之后 virbr0 = 192.168.122.1/24,guest 经 DHCP 取址(实测 guest 拿到 192.168.122.x,可 ping 通)。

管理命令

用途命令
列出域(含未启动)virsh -c qemu:///system list --all
开机 / 优雅关机 / 断电virsh -c qemu:///system start|shutdown|destroy max2-win10ltsc
域信息(含 Autostart)virsh -c qemu:///system dominfo max2-win10ltsc
图形控制台virt-manager(连接 qemu:///system)或 virt-viewer -c qemu:///system max2-win10ltsc
截屏(不必进桌面)virsh -c qemu:///system screenshot max2-win10ltsc /tmp/shot.png
guest 拿到的 IPvirsh -c qemu:///system net-dhcp-leases default
存储池virsh -c qemu:///system pool-list --all
改域定义virsh -c qemu:///system edit max2-win10ltsc
磁盘 I/O 统计virsh -c qemu:///system domblkstat max2-win10ltsc sda

关键点:非 root 的 virsh 默认连 qemu:///session,系统域必须显式 -c qemu:///system,否则 list / net-list 全是空。

踩坑记录

  • 网络看起来没起来:libvirt 的 default 网络不是定义好的就有,需 net-autostart default(它才会建 autostart 符号链接),否则每次 libvirtd 起也不带它。
  • 26.05 的 ovmf 子模块已删:virtualisation.libvirtd.qemu.ovmf 写进配置会在求值期断言失败。
  • 同名域 define 被拒:报 already exists with uuid … —— 名字相同但 XML 里没写或写了不同 <uuid> 时,libvirt 判定为另一个域;改定义必须带原 uuid,或先 virsh undefine --keep-nvram 再重定义。
  • boot 元素冲突:<os><boot dev='hd'/> 与设备上的 <boot order='1'/> 不能同时用(报 per-device boot elements cannot be used together with os/boot elements),二选一。
  • virt-v2v 转换失败(本 guest 特有):在 Inspecting the source 阶段报 hivex: _hivex_get_values: EFAULT, value is not a valid block → inspect_os raised Hivex.Error。成因是 guest 的 SYSTEM hive 在 MountedDevices 处有 hivex 读不了的块(内容问题,不是配置问题,命令行无开关可绕)。⇒ 改用 qemu-img 直接转 qcow2 + 手工域定义(AHCI/e1000e 都是 Win10 自带驱动,不需要注入)。
  • 整盘 raw 直接按 ntfs3 挂载会失败:报 Primary boot signature is not NTFS —— NTFS 卷在分区里,要加 offset=(本档案 1048576 = LBA 2048×512)。
  • ACPI 关机不响应:guest 停在锁屏时 virsh shutdown 可能长时间不退出,必要时用 destroy(硬断电);频繁硬断电会在 guest 侧留下未清理的关机状态。

备注

对硬件有检测要求的 win 软件,可以通过修改虚拟化硬件来调通即可。