hyper2kvm

Step-by-step: first boot validation with SATA + UEFI (libvirt)

This is the safest “first boot” profile for Windows images that were converted from VMware, because it avoids the classic trap:

Windows boots once with a conservative storage controller (SATA/AHCI), then you install VirtIO inside the guest, then you switch the disk bus to VirtIO.

That sequence prevents INACCESSIBLE_BOOT_DEVICE when the VirtIO storage driver isn’t boot-ready yet.

Table of Contents


Prerequisites

For Windows VM migration, you need:

0) Host prerequisites (Fedora/RHEL family)

sudo dnf install -y \
  libvirt \
  qemu-kvm \
  virt-viewer \
  edk2-ovmf

sudo systemctl enable --now libvirtd
sudo virsh net-start default 2>/dev/null || true
```bash

### 1) Put the qcow2 where libvirt/qemu can read it

Libvirt often runs QEMU under a confined user/service context. Even if the qcow2 file is `644`, the *parent directories* can still block traversal.

**Recommended**: move/copy to a libvirt-friendly location:

```bash
sudo mkdir -p /var/lib/libvirt/images
sudo cp -a /home/ssahani/tt/hyper2kvm/out/windows10-fixed.qcow2 /var/lib/libvirt/images/
sudo chmod 644 /var/lib/libvirt/images/windows10-fixed.qcow2
```bash

(If you *must* keep it under `/home/...`, ensure directory execute bits allow traversal; on SELinux systems you may also need proper labeling.)

### 2) Create a per-VM OVMF VARS file

UEFI NVRAM state must be unique per VM.

```bash
sudo cp -a /usr/share/edk2/ovmf/OVMF_VARS.fd /var/tmp/win10-fixed-sata_VARS.fd
sudo chmod 644 /var/tmp/win10-fixed-sata_VARS.fd
```bash

### 3) Save the domain XML

Create `win10-sata-uefi.xml` with the contents below.

**Important**: I changed the `<source file=...>` to point at `/var/lib/libvirt/images/windows10-fixed.qcow2` to match step (1). If you keep your original path, update it consistently.

```xml
<!-- win10-sata-uefi.xml -->
<domain type='kvm'>
  <name>win10-fixed-sata</name>
  <memory unit='MiB'>4096</memory>
  <currentMemory unit='MiB'>4096</currentMemory>
  <vcpu placement='static'>4</vcpu>

  <os>
    <type arch='x86_64' machine='pc-q35-8.2'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/edk2/ovmf/OVMF_CODE.fd</loader>
    <nvram>/var/tmp/win10-fixed-sata_VARS.fd</nvram>
    <boot dev='hd'/>
  </os>

  <features>
    <acpi/>
    <apic/>
    <hyperv mode='custom'>
      <relaxed state='on'/>
      <vapic state='on'/>
      <spinlocks state='on' retries='8191'/>
    </hyperv>
    <vmport state='off'/>
  </features>

  <cpu mode='host-passthrough' check='none'/>
  <clock offset='localtime'>
    <timer name='rtc' tickpolicy='catchup'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
  </clock>

  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>restart</on_crash>

  <devices>
    <emulator>/usr/bin/qemu-system-x86_64</emulator>

    <!-- SATA/AHCI controller -->
    <controller type='sata' index='0'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
    </controller>

    <!-- Disk on SATA (safe first boot) -->
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2' cache='none' io='native'/>
      <source file='/var/lib/libvirt/images/windows10-fixed.qcow2'/>
      <target dev='sda' bus='sata'/>
    </disk>

    <!-- Network: e1000e for max compatibility; switch to virtio later -->
    <interface type='network'>
      <source network='default'/>
      <model type='e1000e'/>
    </interface>

    <!-- USB + input -->
    <controller type='usb' model='qemu-xhci'/>
    <input type='tablet' bus='usb'/>
    <input type='mouse' bus='ps2'/>
    <input type='keyboard' bus='ps2'/>

    <!-- Graphics -->
    <graphics type='spice' autoport='yes' listen='127.0.0.1'/>
    <video>
      <model type='qxl' ram='65536' vram='65536' heads='1'/>
    </video>

    <!-- Sound -->
    <sound model='ich9'/>

    <!-- Serial console -->
    <serial type='pty'>
      <target port='0'/>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
  </devices>
</domain>
```bash

### 4) Define and boot the VM

```bash
sudo virsh define win10-sata-uefi.xml
sudo virsh start win10-fixed-sata
```bash

### 5) Connect a viewer

```bash
virt-viewer win10-fixed-sata
```bash

You should now see the Windows boot sequence. On the first boot, expect Windows to do some hardware detection and driver setup.

---

## After Windows boots: install VirtIO drivers (so you can switch to VirtIO safely)

Once you can log in successfully, install VirtIO inside Windows. The usual approach is to attach a VirtIO driver ISO (virtio-win) as a CDROM and run the installer, or use Device Manager to point to the mounted ISO.

After VirtIO storage drivers are installed, you can switch:

* disk bus: `sata``virtio`
* network model: `e1000e``virtio`

(Do this in a *copy* of the XML or via `virsh edit win10-fixed-sata`.)

---

## Troubleshooting quick hits

### VM won’t start / “Permission denied”

Typical causes:

* qcow2 is in a path QEMU can’t traverse (`/home/...` with restrictive directory perms)
* SELinux labeling mismatch on the file/dir
* wrong permissions on NVRAM vars file

Fastest fix is what we already do above: keep disks under `/var/lib/libvirt/images/` and ensure:

* `chmod 644` on qcow2 and vars file
* parent directories are traversable (execute bit)

### Black screen

* Make sure OVMF paths exist (`/usr/share/edk2/ovmf/OVMF_CODE.fd` and `OVMF_VARS.fd` may differ by distro)
* Try removing any leftover conflicting NVRAM file and recreating it

### INACCESSIBLE_BOOT_DEVICE on VirtIO boot

That’s exactly why we start with SATA.
Boot with SATA, install VirtIO drivers in Windows, then switch.

---

If you want, I can also add the **follow-up section** that shows the *VirtIO version* of the same XML (disk bus virtio + virtio-net) and a minimal snippet for attaching a `virtio-win.iso` CDROM in libvirt.
# WINDOWS.md — Windows Migration Deep Dive

This document explains how `hyper2kvm` handles Windows guests and **why each step exists**.

---

## Problem statement

Windows does not boot based on:
- filesystem correctness
- disk visibility
- registry sanity alone

It boots based on **boot-critical drivers registered correctly**.

---

## Driver injection model

### Supported driver classes
- Storage: viostor / vioscsi
- Network: netkvm
- Balloon: balloon
- Optional: virtiofs, input, GPU

Drivers are copied to:
```bash
Windows/System32/drivers/
```bash

---

## Registry editing (offline)

### SYSTEM hive operations
- Opened via `hivex`
- Correct ControlSet detected
- Services created under:

HKLM\SYSTEM\ControlSetXXX\Services ```

Required service values


CriticalDeviceDatabase (CDD)

Without CDD entries, Windows may:

Entries are created under: bash HKLM\SYSTEM\ControlSetXXX\Control\CriticalDeviceDatabase bash

Mapped by PCI vendor/device IDs.


BCD handling

What we do

What we do NOT do

This avoids corrupting boot metadata.


Common failure prevented

INACCESSIBLE_BOOT_DEVICE

Caused by:

hyper2kvm fixes all three before first boot.

Next Steps

For Windows migrations:

Getting Help