服务:kvm的qcow2文件重新生成启动蓝屏

问题:对物理盘上的 s500win(旧 Win10 系统分区)精简后,重新生成 /s500data/kvm/win10-old/win10-old.qcow2 用于 KVM 虚拟机,开机蓝屏 INACCESSIBLE_BOOT_DEVICE(停止代码 0x7B)。

根因(已确证)

VM 的磁盘控制器与这台 Windows 上次运行时的控制器不一致。

  • 物理机系统盘是 NVMe(驱动 stornvme);q35 虚拟机默认把系统盘挂在 IDE/SATA(设备 ide-hd、bus sata)。
  • 被物理机跑过的这套 Windows,其存储配置会把“物理机上不存在的控制器”当无用设备清理掉 ⇒ 内核在 VM 里挂载系统卷失败 ⇒ 0x7B。
  • 关键判据:能进 xorboot 菜单并选中 Windows 项、转圈一会儿后仍 0x7B ⇒ 引导链(ESP / BCD / xorboot)没有问题,问题在控制器 / 驱动层。
  • 反证:同一块盘的“移动前旧镜像”(未在物理机 NVMe 上跑过的新配置)在 IDE/SATA 下能起;只有精简后(在物理 NVMe 上运行过)的才 0x7B。

解决办法

把 qcow2 的系统盘从 SATA 改成 NVMe,对齐物理机。无需重建 qcow2,改域 XML 即可:

<!-- win10-old.xml(默认;half / full 方案同样改) -->
<disk type='file' device='disk'>
  <driver name='qemu' type='qcow2' cache='none' io='native' discard='unmap'/>
  <source file='/s500data/kvm/win10-old/win10-old.qcow2'/>
  <target dev='nvme0n1' bus='nvme'/>   <!-- 原为 dev='sda' bus='sata' -->
</disk>
<controller type='nvme' index='0'/>      <!-- 新增 -->

改后执行 virsh -c qemu:///system define /s500data/kvm/win10-old/win10-old.xml 即生效;实测开机正常进系统(DHCP 租约 hostname max2-at)。

注意:NVMe 下原来的 <qemu:commandline> -global ide-hd.model=… </qemu:commandline>(SATA 磁盘型号伪造)失效,已从三份 XML 去掉——接受该反检测项丢失,换取能引导。

排查过程中的避坑要点

  • 设备名会翻转:重启后 /dev/nvme0n1 与 nvme1n1 可能互换 ⇒ 一律按 /dev/disk/by-label/…(或 PARTUUID)定位,勿写死 nvmeX nY。
  • 整盘几何:Windows 分区在盘尾(装 NixOS 时从盘首移过去的);旧 VHDX / 旧 qcow2 是移动前(盘首)的镜像,两者几何不同,不能照抄。
  • 重建 qcow2 的正确姿势:ntfsclone 只拷已用簇 → 用 python 按 SEEK_DATA/SEEK_HOLE 精确落位组装 raw → qemu-img convert。不要用 dd conv=sparse 配 oflag=seek_bytes(在块设备和普通文件上都会静默错位,导致“分区落点对但内容错”)。
  • qemu-nbd 的 stale 分区节点会报 EINVAL(跨 attach 残留,partx -a 会提示“添加分区出错”)⇒ 换新 nbd 号并先 partx -d。
  • 引导失败的 qcow2 不要删:只在其内部 ESP(551M)上改,反复试,别每次整体重写 100G+。
  • xorboot 的默认项由 ESP 里 \EFI\xorboot\XORBOOT.ini 的 LastBoot=N 控制;条目定义在二进制 xorboot.xor(含 disk/partition GUID + UTF-16 路径)。

相关

技能:windows-vm-qcow2-rebuild(devops);项目台账 docs/legacy-win-vm.md §8/§9。三份方案 XML:win10-old.xml / win10-old-half.xml / win10-old-full.xml(均已改 NVMe)。