9 minute read


Even with SteamOS, Steam for Linux and WINE, many gamers who use Linux tend to switch to Windows for playing games. If you wish to try out some of the newest triple-A titles, you have no other choice than running them on a Windows installation. That’s a fact. This is why so many gamers dual-boot. Having installed two operating systems on the same machine and using Windows for playing game and Linux for virtually anything else might seem handy and easy to accomplish but brings a lot of problems.

Even if we ignore the fact that Windows doesn’t like GRUB that much, or that Windows might override the boot-order when using UEFI, it’s really annoying to reboot your computer for switching the context.

The Solution: A virtual machine

No, wait!

Don’t go just because you read the word virtual! KVM and QEMU have been around for quite some time and are able to reach about 95% of Windows’ native gaming performance while still leaving some room for optimizations.

How? This is where PCI passthrough comes in.

The Idea is basically to allow the VM to directly access specific parts of your hardware and let it load it’s own drivers for it. This way we’re bypassing the huge bottleneck of simulating a GPU.

Well and that’s the reason for this post. I’ll show you how I made the switch and provide a starting point for setting up your own Windows VM.

Important Note

This is not a trivial task. While some guys claim they reached a perfect setup within one day others struggle a lot. My setup took about a day until it reached a “usable” state and about one week of optimization. Also don’t expect to just follow this Guide and everything will magically work. Things might break, kill your cat and call you names.

Since Trump is about to become president I’ll make my point even clearer:


Bare metal

Your hardware needs to meet some requirements.

The most important thing is to have at least two GPU’s.
That’s because we will reserve one of them for the VM.

The second most important thing is that your CPU supports VT-x or AMD-V. Otherwise you won’t be able to emulate anything at remotely playable performance. (Chess and Solitair might work though)

This is my current setup:

CPU: Intel(R) Core(tm) i7-4790k
GPU: Intel(R) HD Graphics 4400 (Used by Linux only)
GPU: Palit NVIDIA GTX1060 6GB (Used by the VM only)
RAM: 32GB Kingston HyperX FURY
SSD: Samsung 850 Pro SSD (256GB) [Linux /]
SSD: Samsung 850 Pro SSD (256GB) [Windows C:]
HDD: WD Black (2TB)              [Steam. Just Steam.]
HDD: WD Green (3TB)              [NVIDIA ShadowPlay and other stuff]

The advantage of installing Windows on a seperate harddrive is that you are still able to dual-boot to Windows if your VM commits suicide or just won’t work. I will pass-through the two WD harddrives and the SSD instead of mounting/caching them for maximum performance.

This guide assumes Hardware capable of running vfio-pci and Linux Kernel 4.1+. If you use older/incompatible hardware/software you’ll have to rely on pcistub instead. pcistub is not part of this guide though.

You can check if you setup supports vfio-pci by running this command:

modinfo vfio-pci



While this concept should work on virtually any Linux that runs KVM, my guide is tailored for usage with Arch Linux. If you use a different OS you might need to use other commands/paths/…

I can confirm that Gaming-VMs work on Ubuntu 16.04+ and some people claim to have fun on Fedora.

Whatever you do - just don’t use Debian :P


To ensure your CPU supports proper hardware virtualization execute one of the following commands:

egrep --color=auto 'vmx|svm|0xc0f' /proc/cpuinfo

Your processor supports virtualization only if there is a line telling you so.

KVM modules

You can check if the necessary modules (kvm and one of kvm_amd, kvm_intel) are available in your kernel with the following command:

zgrep CONFIG_KVM /proc/config.gz

The module is only available if it is set to either y or m.

VirtIO modules

Check if they are present using

zgrep VIRTIO /proc/config.gz

Again: A module is only available if it is set to either y or m.


You will also have to enable iommu support in the kernel itself through a bootloader kernel option. Depending on your type of CPU, use either intel_iommu=on for Intel CPUs (VT-d) or amd_iommu=on for AMD CPUs (AMD-Vi).

After rebooting, check dmesg to confirm that IOMMU has been correctly enabled:

dmesg | grep -e DMAR -e IOMMU


Let’s do this.

First we install QEMU, libvirt and some dependencies

yay -S qemu libvirt ovmf-git ebtables dnsmasq virt-manager


Next we need to get the ID of the GPU. Run lspci -nn and look for it.

Sample output:

01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP106 [GeForce GTX 1060] [10de:1c03] (rev a1)
01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10f1] (rev a1)

Then edit /etc/modprobe.d/vfio.conf and add the corresponding vendor:device pairs:

options vfio-pci ids=10de:1c03,10de:10f1

Edit /etc/mkinitcpio.conf and add the following modules before any graphic drivers:

MODULES="... vfio vfio_iommu_type1 vfio_pci vfio_virqfd ..."

Also, ensure that the modconf hook is included in the HOOKS list of /etc/mkinitcpio.conf:

HOOKS="... modconf ..."

That’s it for our initramfs. Let’s regenerate it:

mkinitcpio -p linux

Before we’re rebooting we still need to edit the kernel flags. So fire up a console and edit /etc/default/grub (assuming you’re a grub user) and add the following to your CMDLINE:


(Depending on your type of CPU, use either intel_iommu=on for Intel CPUs (VT-d) or amd_iommu=on for AMD CPUs (AMD-Vi)).

Finally write your GRUB config and reboot!

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

Creating the VM

Add the OVMF to your NVRAM at /etc/libvirt/qemu.conf:

nvram = [

Now enable libvirt’s daemon and the logger:

systemctl enable --now libvirtd
systemctl enable virtlogd.socket

Finally start virt-manager and create your VM.
The process is self-explainatory as most of the process comes with pretty on-screen instructions. You should pay attention to these specific steps though:

  • When the wizard asks you to name the VM ensure Customize before install is checked.
  • In the “Overview” section, set your firmware to “UEFI”
  • In the “Processor” section, change your CPU model to “host-passthrough”. If it is not in the list, you will have to type it by hand.

Attaching the PCI devices

This is pretty simple too. Just click on “Add Hardware” and select the PCI device(s).

If your GPU supports HDMI there will be a second PCI device for HD-Audio.

Mounting the Harddrives

We’re doing this through libvirt’s XML interface because virt-manager does not offer all options we need. Open a shell and enter this command:

virsh edit Gaming-VM

Then add these lines for every harddrive you want to mount:

<disk type="block" device="disk">
    <driver name="qemu" type="raw" cache="none" io="native"/>
    <source dev="/dev/sdX"/>
    <target dev="vdX" bus="sata"/>

Also add one SATA bus that the HDD’s will use:

<controller type="sata" index="0"></controller>

Getting sound

This one is tricky.
Basically you just need to add a ich9 or ich6 soundcard in virt-manager.
Then tell QEMU your active audio-server using an env-var:

  • Execute virsh edit Gaming-VM
  • Add xmlns:qemu='' to your <domain> element.
  • Add this node:
<!-- QEMU_AUDIO_DRV can be either "pa" (pulseaudio) or "alsa" -->
    <qemu:env name='QEMU_AUDIO_DRV' value='pa'/>

That’s it!
Windows ships with a driver for these cards so compatiblity shouldn’t be an issue. BUT: There are lots of pitfalls in this topic and only a few sites on the internet with actual solutions. So here’s a list of common pitfalls and how to fix them:

No more audio after the QXL graphics are removed

This is because QEMU only enables audio if a graphical frontend is configured. The solution is to set nographics_allow_host_audio = 1 in /etc/libvirt/qemu.conf and restart libvirtd.service afterwards.

Running the VM on qemu:///system causes silence

The solution is to tell QEMU which audio-socket it should use. This requires no config files but can be archieved using environment variables. Open your vm-domain using virsh edit Gaming-VM and add this node to your <qemu:commandline> element:

<qemu:env name='QEMU_PA_SERVER' value='/run/user/1000/pulse/native'/>

Note: Replace 1000 with the id of your user.

Running the VM on qemu:///system with QEMU_PA_SERVER but still silence

Force QEMU to run as your user to solve this problem.
Open /etc/libvirt/qemu.conf and change these lines to your username and a group you have access to:


Then restart libvirtd.service.

The sound works but lags or stutters

Try to delay your sound a bit.
30ms worked well for me but feel free to play with this value.
Open your vm-domain using virsh edit Gaming-VM and add this node to your <qemu:commandline> element:

<qemu:env name='PULSE_LATENCY_MSEC' value='30'/>

Tuning your VM

This section is dedicated to optimization of a working VM.
Please don’t try any of the tips until the VM is fully functional.
Always remember to make snapshots of your VM before doing critical operations.

CPU Model

Changing the CPU model might bring a slight performance increase and also helps fighting against NVIDIA’s KVM detection.
Just add this to your XML to copy your host’s CPU name and features:

<cpu mode='host-model'>
    <model fallback='allow'/>

vCPU Pinning

QEMU is constantly trying to link your VM’s CPU Cores to real cores based on niceness, load, and other factors. This is usually fast enough to be unnoticeable but in heavy-load situations (like games) this will produce a heavy overhead. vCPU pinning bypasses this problem by explicitly mapping your VM’s cores to your host. To enable it just add these lines to your VM’s XML using virsh edit Gaming-VM:

<vcpu placement='static'>4</vcpu>
    <vcpupin vcpu='0' cpuset='4'/>
    <vcpupin vcpu='1' cpuset='5'/>
    <vcpupin vcpu='2' cpuset='6'/>
    <vcpupin vcpu='3' cpuset='7'/>

This example maps cores 0,1,2,3 of your VM to 4,5,6,7 of your host.

NVIDIA driver crashes with “Error 43”

This is a “bug” (read: “feature to get us to buy more expensive hardware”) in the NVIDIA driver that only appears when using non-quadro cards iside of VMs. To bypass this, open your domain’s XML and add this node to your <features>:

    <hidden state='on'/>

Also make sure to remove everything that contains the word hyperv from your XML and that you copy your host’s CPU model.

VM crash when using Fastboot/Suspend

Be sure to turn off the suspension stuff and fastboot in Windows. Also add these lines to your XML:

    <suspend-to-mem enabled='no'/>
    <suspend-to-disk enabled='no'/>

TL;DR - Linux is awesome


SteamVR Performance Test

Catzilla 4k (720p Settings)

If you like when I write words, you can fund future blogging (and other cool things) by throwing some spare money into my dev-fund.
It's very much appreciated.

comments powered by Disqus