Skip to main content

GPU Passtrough on Linuxmint 21

Install virt-manager

sudo apt-get install virt-manager bridge-utils

Create a Linux Bridge

add the following to /etc/network/interfaces

auto enp4s0

iface enp4s0 inet dhcp

add the following to /etc/network/interfaces.d/br0

# static ip config file for br0 ##
auto br0
iface br0 inet static
        address 192.168.178.10
        broadcast 192.168.178.255
        netmask 255.255.255.0
        gateway 192.168.178.1
        # If the resolvconf package is installed, you should not edit 
        # the resolv.conf configuration file manually. Set name server here
        dns-nameservers 192.168.178.1
        # If you have muliple interfaces such as eth0 and eth1
        # bridge_ports eth0 eth1  
        bridge_ports enp4s0
        bridge_stp off       # disable Spanning Tree Protocol
        bridge_waitport 0    # no delay before a port becomes available
        bridge_fd 0          # no forwarding delay

Step 1: Enable IOMMU

edit /etc/default/grub at the line GRUB_CMDLINE_LINUX_DEFAULT so that it reads like

GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt"

Then reconfigure Grub

sudo grub-mkconfig -o /boot/grub/grub.cfg

Step 2: Tell VFIO we want to pass through the NVIDIA card

lspci -nnk

In my case the id of the GPU is 10de:17c8 and the HDMI sound output is 10de:0fb0. Note the part beneath the GPU where it says Kernel driver in use: nouveau.. If everything works correctly that should change by the time we're done. To flag the card for use by VFIO, create the file sudo nano /etc/default/grub at the line GRUB_CMDLINE_LINUX_DEFAULT with the contents:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash amd_iommu=on iommu=pt kvm.ignore_msrs=1 vfio-pci.ids=10de:17c8,10de:0fb0"

Then reconfigure Grub

sudo grub-mkconfig -o /boot/grub/grub.cfg

Step 3: Create the Windows VM without GPU passthrough

I recommend using virt-manager and setting up a regular Windows 10 VM using the default QXL video card before trying to do any passthrough stuff. When creating the VM, make sure to select "Customize before install" and set the Firmware option to "UEFI". Create the VM and go through the Windows installer until you have a working Windows 10 installation with no GPU passthrough, then shut down the VM.

Step 4: Fix the Windows Code 43 error

This error seems to happen because the NVIDIA driver realizes that it's running inside a VM and will disable itself. Since we don't want that we need to "hide" the fact that there's a VM from the driver. KVM has a mechanism for doing that but it's not exposed in virt-manager, so we'll need to edit the XML config for the virtual machine manually. To do that, run:

sudo virsh edit win10

where win10 is the name of the VM that you gave when you created it inside virt-manager. You'll need to edit the contents of the tag in the following way:

Inside the tag: add the line:

<vendor_id state='on' value='1234567890ab'/>

(the actual value of the vendor_id is arbitrary, but it should be a 12 digit hex number).

Inside the tag: add the line:

<hidden state='on'/>

Inside the tag: add the line:

<ioapic driver='kvm'/>

The end result should look something like:

  <features>
    <acpi/>
    <apic/>
    <hyperv>
      <relaxed state='on'/>
      <vapic state='on'/>
      <spinlocks state='on' retries='8191'/>
      <vendor_id state='on' value='1234567890ab'/>
    </hyperv>
    <kvm>
      <hidden state='on'/>
    </kvm>
    <vmport state='off'/>
    <ioapic driver='kvm'/>
  </features>

If you boot the machine up again, the NVIDIA driver should actually work! Windows will probably default to using the GPU as the primary card, which means that the Windows login prompt will likely appear on the display connected to the video card rather than the QXL display that you can see in virt-manager.

Step 5: evdev keyboard and mouse switching

See usb devices that you want to pass through (usualy the ones with event in the name)

ls -l /dev/input/by-id/
<qemu:commandline>
<qemu:arg value='-object'/>
<qemu:arg value='input-linux,id=mouse1,evdev=/dev/input/by-id/usb-Logitech_Gaming_Mouse_G400-event-mouse'/>
<qemu:arg value='-object'/>
<qemu:arg value='input-linux,id=kbd2,evdev=/dev/input/by-id/usb-04d9_USB_Keyboard-event-if02'/>
<qemu:arg value='-object'/>
<qemu:arg value='input-linux,id=kbd1,evdev=/dev/input/by-id/usb-04d9_USB_Keyboard-event-kbd,grab_all=on,repeat'/>
</qemu:commandline>

Also put the following or add it via virt manager GUI

<input type='mouse' bus='virtio'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x0e' function='0x0'/>
    </input>
    <input type='keyboard' bus='virtio'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x0f' function='0x0'/>
    </input>
    <input type='mouse' bus='ps2'/>
    <input type='keyboard' bus='ps2'/>

To allow evdev for qemu edit /etc/libvirt/qemu.conf to look something like this:

user = "anon"
group = "kvm"
cgroup_device_acl = [
        "/dev/null", "/dev/full", "/dev/zero",
        "/dev/random", "/dev/urandom",
        "/dev/ptmx", "/dev/kvm", "/dev/kqemu",
        "/dev/rtc","/dev/hpet", "/dev/sev",
        "/dev/input/by-id/usb-04d9_USB_Keyboard-event-kbd",
        "/dev/input/by-id/usb-04d9_USB_Keyboard-event-if02",
        "/dev/input/by-id/usb-Logitech_Gaming_Mouse_G400-mouse",
        "/dev/input/by-id/usb-Logitech_Gaming_Mouse_G400-event-mouse"
]

Make sure the user is a part of the input and kvm group

sudo usermod -aG kvm,input anon

Now restart libvirtd

systemctl restart libvitd