Virtio is a driver-device communication protocol optimized for virtualization environments. A virtio device is an emulated device exposed to the VM by the hypervisor, in our case QEMU. A virtio driver is part of a guest operating system running in a virtual machine whose purpose is to make the device’s functionality available inside the guest operating system.
This post focuses on QEMU/KVM based Windows virtual machines and gives an introduction to the virtio 1.0 feature and specifically its impact on Windows guests. While it is possible to run Windows VMs with no 3rd party drivers installed, virtio devices are recommended to achieve optimum performance, together with drivers shipped in a package called virtio-win (virtio drivers do not come in-box with any version of Windows at the moment).
The following table lists all virtio devices currently supported in Windows guests; note that the way they are named in the virtio spec is different from the QEMU device names, and different still from the Windows driver names. Starting with virtio-win RPM version 126, all drivers support version 1.0 of the virtio protocol, referred to as “modern”. This is in addition to the previous “legacy” virtio version 0.9/0.95. Depending on the command line arguments passed to QEMU, most virtio devices can be configured to support either one or both of the protocols and the breaking nature of running as 1.0-only necessitated the introduction of new PCI IDs to make sure that old drivers will not try to drive 1.0-only devices.
|spec name||QEMU name||virtio-win name||legacy PCI ID||modern-only PCI ID|
(*) The serial driver has traditionally been provided in a directory named vioserial while the driver itself consists of files named vioser.*.
(**) The input device is relatively new and supports only the virtio 1.0 protocol and modern-only mode.
Table 2 explains what happens at run-time, based on how the device is configured on QEMU command line and which driver version is loaded. The three modes a device can operate in are legacy – supporting only the older version 0.9 of the virtio protocol, transitional – supporting both virtio 0.9 and virtio 1.0, and modern-only – supporting only the new virtio version 1.0.
|QEMU device flags||device mode||protocol used (driver version <126)||protocol used (driver version >=126)||PCI ID||I/O port resources|
|legacy||virtio 0.9||virtio 0.9||legacy||yes|
|transitional||virtio 0.9||virtio 1.0||legacy||yes|
|modern-only||N/A, driver will not load||virtio 1.0||modern-only||no|
As an example, this is what the relevant part of the QEMU command line might look like to add a modern-only balloon device to the VM:
is an invalid combination, at least one of the two must stay off. The more interesting question is what happens if none of the two options are specified. As it turns out, this can result in all three cases from the table above. In general, QEMU aims for natural defaults depending on the machine type and
- If an older pre-2.7 PC (i440fx-based) QEMU machine type is used, devices will be configured as legacy for maximum backward compatibility. Example:
-machine pc-i440fx-2.6 \ -device virtio-balloon-pci,id=balloon0
- If a 2.7 or newer PC (i440fx-based) QEMU machine type is used, devices will be configured as transitional. This is still reasonably backward compatible (old drivers should keep working) while making the new protocol available as well. Example:
-machine pc-i440fx-2.7 \ -device virtio-balloon-pci,id=balloon0
- If a Q35 QEMU machine type is used, devices connected to PCIe (PCI Express) root and downstream ports will be configured as modern-only while integrated devices remain transitional. The reason for this are I/O port space PCIe limitations, note that modern-only is the only device mode that does not use any I/O port resources. Example:
-machine q35 \ -device ioh3420,id=root.0,slot=1 \ -device x3130-upstream,bus=root.0,id=upstream1 \ -device xio3130-downstream,bus=upstream1,id=downstream1,chassis=1 \ -device ioh3420,id=root.1,slot=2 \ -device virtio-balloon-pci,id=balloon0,bus=root.1
FAQ / Troubleshooting
How do I tell what mode a device operates in?
Open the device in Device Manager and switch to the Resources tab. If you don’t see an I/O Port Range resource, the device is operating in modern-only mode and is driven using the virtio 1.0 protocol. Otherwise, if you see an I/O Port Range, look for a Memory Range larger than one page, i.e. with more than just the three last digits of the start and end addresses being different. The presence of such a range usually indicates that the device is operating in transitional mode. On the other hand, the absence of any memory ranges is a reliable indication that the device is operating in legacy mode. This is what it may look like for the balloon device:
Will my Windows VM keep working if I flip the disable-legacy or disable-modern flag?
Most probably not. Going from legacy or transitional to modern-only mode, or vice versa, going from modern-only to legacy or transitional means that the PCI device ID changes (see Table 1). Windows will not realize that it is the same device for which it already had a driver and will be looking for one after it boots. This may be fine if the device is not required in the boot process itself, say, balloon. But if the device provides access to the system disk, the boot is likely going to fail.
Bug Check 0x7B: INACCESSIBLE_BOOT_DEVICE (Windows 7):
The easiest way to fix this is to temporarily attach the system drive to an IDE controller, or any known-to-be-working controller, and attach a non-system drive to the problematic virtio-blk-pci or virtio-scsi-pci instead. Let Windows install the driver on next boot. After that everything can be reverted back to the old setup. Here is the procedure broken down into steps:
- The VM does not boot with:
- Use the IDE trick:
and add a temporary drive:
$ qemu-img create -f qcow2 temp.qcow2 10M -drive file=temp.qcow2,id=drive-temp,format=qcow2,if=none \ -device virtio-blk-pci,scsi=off,drive=drive-temp,id=virtio-disk0,disable-legacy=on
- Start the VM and let it install the driver
$ rm temp.qcow -device virtio-blk-pci,scsi=off,drive=drive-virtio0-0-0,id=virtio-disk0,disable-legacy=on,bootindex=0