So how do we get primary graphics without VGA? Well, assuming your graphics card isn't too terribly old, it probably already contains support for both UEFI and VBIOS in the ROM and OVMF will use that new entry point to initialize the card. There are some however some additional restrictions in going this route. First, the guest operating system needs to support UEFI. This means a relatively recent version of Linux, Windows 8+, or some of the newer Windows Server versions. A gaming platform is often the target for "enthusiast" use of GPU assignment, so Windows 8/8.1 is probably a good target if you can bear the user interface long enough to start a game. AFAIK, Windows 7 does not support UEFI natively, requiring the CSM (Compatibility Support Module) which I believe defeats the purpose of using UEFI. If one of these guests does not meet your needs, turn away now.
Next up, UEFI doesn't (yet) support the Q35 chipset model. In a previous post I showed Windows 7 happily running on a 440FX QEMU machine with both GeForce and Radeon graphics. The same is true for Windows 8/8.1 here. Linux isn't so happy about this though. The radeon driver in particular will oops blindly looking for an downstream port above the graphics card. You may or may not have better luck with Nvidia, neither nouveau or nvidia play nice with my GT635. The radeon driver problem may be fixable without Q35, but it needs further investigation. fglrx is untested. Therefore, if Windows 8/8.1 is an acceptable guest or you're willing to help test or make Linux guests work, let's move on, otherwise turn back now.
Ok, you're still reading, let's get started. First you need an OVMF binary. You can build this from source using the TianoCore EDK2 tree, but it is a massive pain. Therefore, I recommend using a pre-built binary, like the one Gerd Hoffmann provides. With Gerd's repo setup (or one appropriate to your distribution), you can install the edk2.git-ovmf-x64 package, which gives us the OVMF-pure-efi.fd OVMF image.
Next create a basic libvirt VM using virt-manage or your favorite setup too. We'll need to edit the domain XML before we install, so just point it to a pre-existing (empty) image or whatever gets it to the point that you can have the VM saved without installing it yet. Also, don't assign any devices just yet. Once there, edit the domain XML with virsh edit. To start, we need to add a tag on the first line to make libvirt accept QEMU commandline options. libvirt does not yet have support for natively handling the OVMF image, but it is under active development upstream, so these instructions may change quickly. Make the first line of the XML look like this:
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
The xmlns specification is the part that needs to be added. Next we need to add OVMF, just before the </domain> close at the end of the file, add something like this:
Adjust the path as necessary if you're using a different installation. If you're using selinux, you may with to copy this file to /usr/share/qemu/ and run restorecon on it to setup permissions for QEMU to use it.
Save the XML and you should now be able to start the VM with OVMF. From here, we can do the rest of the VM management using virt-manager. You can add the GPU and audio as a standard PCI assigned device. If you remove the "Graphics" (ie. VNC/Spice) and "Video" (ie. VGA/QXL/Cirrus) devices from the VM, the assigned GPU will be the primary display.
If you are not assigning the audio function or otherwise need to use the host audio for the guest, I recommend using QXL+Spice. When the guest is running you can configure the QXL display off and use the connection only for sound.
UEFI purists will also note that I'm not providing a second flash drive for EFI variable storage. In simple configurations this is unnecessary, but if you find that you've lost your boot options, this is the reason why.
In the video below I'm running two separate Windows 8.1 VMs using OVMF and managed through libvirt. The one on the left is assigned a GeForce GT635, the one on the right a Radeon HD8570. The host processor is an i5-3470T (dual-core + threads) and each VM is given 2 vCPUs, exposed as a single core with 2 threads.
Astute viewers will note that I'm using the version of the Nvidia driver which requires the kvm=off QEMU options. There are two ways around this currently. The first is to add additional qemu:commandline options:
We can do this because QEMU doesn't explode at the second instance of -cpu on the commandline, but it's not an ideal option. The preferred method, and what I'm using, is support that is just going into libvirt which creates a new feature element for this purpose. The syntax there is:
This will add kvm=off to the set of parameters that libvirt uses.
Also note that while Radeon doesn't seem to need any of the devices quirks previously enabled with the x-vga=on option, Nvidia still does. This commit, which will soon be available in qemu.git, enable the necessary quirk anytime an Nvidia VGA device is assigned.
To summarize, for Radeon no non-upstream patches are required. For Nvidia, libvirt will soon be updated for the hidden feature above, until then use the additional commandline option and QEMU will soon be updated to always quirk Nvidia VGA devices, until then the referenced commit will need to be applied. Both cases currently need the qemu:commandline OVMF support, which should also be in libvirt soon.
In the video below, I'm running a stock Fedora 20 kernel. The IGD device is used by the host with full DRI support (as noted by glschool running in the backgroups... which I assume doesn't work when DRI is disabled by using the i915 patch). QEMU is qemu.git plus the above referenced Nvidia quirk enablement patch and libvirt is patched with this, both of which have been accepted upstream and should appear in the development tree at any moment. If you're running OVMF from a source other than above, make sure it includes commit b0bc24af from mid-June of this year. If it doesn't, OVMF will abort when it finds an assigned device with a PCIe capability. Also, you'll likely experience a long (~1 minute) delay on guest reboot. This is due to lack of MTRR initialization on reboot, patches have been accepted upstream and will be in qemu.git shortly.