Passthrough Nvidia to Linux VM

You use Linux because you’re a good person. But when it comes to video games, you’re a little more pragmatic. So you bought an Nvidia card and set up a Windows VM. Then Valve announced their new Steam Play feature, and you’ve considered gaming on Linux. Not on your host, of course. Moving your GPU up a slot would be too difficult, so how about you make a second VM for Ubuntu?

Nvidia doesn’t get along with virtualization or Linux, so this guide should help you corral everyone until they get along. This setup was performed on a Fedora 28 workstation running on 1st gen Ryzen hardware, with an Nvidia GTX 1080 passed through to a guest running Ubuntu 18.04.

Virtual Machine Setup

First up, download an ISO file from Ubuntu’s website.

In virt-manager, create a new machine. Select the previously downloaded ISO file as your install method. Customize the system as you like until you hit step 5, then check the box to customize your configuration.

For the firmware, select OVMF. Assuming you have set up Windows before, this should already be an option. If this is your first VFIO setup, why are you reading this guide? Also, the arch wiki should help. And if you have Ryzen hardware, Level1Techs have a great guide as well. You may have to skip this step, but the install won’t boot without OVMF anyways.

Set the chipset to q35. This is, strictly speaking, optional. But I’ve found the i440FX chipset causes problems with Nvidia’s drivers, especially the proprietary ones.

Under CDROM, set the disk bus to SATA.

Select add hardware and add your graphics card. Typically it will be listed as two devices: a VGA component and an audio component. If you have a dedicated hard drive for this VM, be sure to add that here as well.

Remove DisplaySpice and Video QXL.

Ideally, you want 2 networks: bridge network and a virtual network. The former allows the guest to connect to the internet. The latter allows rapid connection from host to guest, which is useful for Steam's In-Home streaming. It eliminates the unneccesary middleman role your router would normally play. One of these should have been setup by default when you create the VM. For the bridge, set the network source to your host device (in my case, eno1). For the virtual network, set it to virtual network default.

Now add some peripherals and boot up. You should see a grub prompt in the console. Select “Install Now” and the Nvidia monitor should display shortly.

Ubuntu Installation

You know how to install Ubuntu. We’re not covering this.

After install, power off the VM and disconnect the iso file from the CDROM device.

Post Install VFIO Configuration

At this point, Ubuntu will launch, but some VFIO optimization is required before switching to Nvidia’s proprietary drivers.

Edit the XML configuration and implement the error 43 fix. Otherwise, that’s going to cause a kernel panic if you decide to use the proprietary drivers. Find the features tag and add hyperv and kvm tags to it, like so:

<features>
  <acpi/>
  <apic/>
  <hyperv>
    <vendor_id state='on' value='SUCKMYDICKNV'/>
  </hyperv>
  <kvm>
    <hidden state='on'/>
  </kvm>
  <vmport state='off'/>
</features>
Linux has been known to be picky about it's PCIe topology, so if your GPU has separate VGA and audio devices, you can opt to put them on the same slot. I've encountered audio issues in this VM, and although I haven't fixed them (in software), any hypothetical fix likely involves this modification. Find the lines below:
<hostdev mode='subsystem' type='pci' managed='yes'>
  <source>
    <address domain='0x0000' bus='0x1e' slot='0x00' function='0x0'/>
  </source>
  <address type='pci' domain='0x0000' bus='0x09' slot='0x00' function='0x0'/>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
  <source>
    <address domain='0x0000' bus='0x1e' slot='0x00' function='0x1'/>
  </source>
  <address type='pci' domain='0x0000' bus='0x0a' slot='0x00' function='0x0'/>
</hostdev>
Your buses and slots may not match. Change the bus and slot of the 2nd device to match the first, and assign it function 1, and enable multifunction on the first device.
<hostdev mode='subsystem' type='pci' managed='yes'>
  <source>
    <address domain='0x0000' bus='0x1e' slot='0x00' function='0x0'/>
  </source>
  <address type='pci' domain='0x0000' bus='0x09' slot='0x00' function='0x0' multifunction='on'/>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
  <source>
    <address domain='0x0000' bus='0x1e' slot='0x00' function='0x1'/>
  </source>
  <address type='pci' domain='0x0000' bus='0x09' slot='0x00' function='0x1'/>
</hostdev>

You can also take this opportunity to change the CPU topology. I’m lazy though, and I’ve never seen the need.

Steam, Nvidia, and CUDA

Steam has an official installer in Ubuntu’s software store. Install that and perform a software upgrade.

To install Nvidia drivers, you’ll need to boot in commandline, to do this, enter

sudo systemctl set-default multi-user.target

and reboot. After login, you can follow the instructions to install Nvidia 410 and CUDA in this forum post. Run the following command and reboot to restore your graphical settings

sudo systemctl set-default graphical.target

Audio Issues

I encountered a lot of audio problems with Ubuntu when virtualized. The traditional passthrough via ich9 was completely useless. Nvidia 390 had no audio when using HDMI. Nvidia 410 had HDMI audio, but with stuttering pops. In the end, I just used an external hardware solution, but if you’re insistent on a software fix, you can pick up where I left off:

If your card is using Intel audio drivers for some reason

Pulseaudio passthrough

Leave a Reply

Your email address will not be published. Required fields are marked *