Guest与Virtio Netdev的交互方式

Qemu为virtio设备分配了专门的pci设备ID,device IDs (vendor ID 0x1AF4) from 0x1000 through 0x10FF,而pci子系统中的厂商ID和设备ID就成为了virtio类型和厂商域的组成,所以PCI驱动是不需要知道virtio设备类型的真正含义,对于Kernel来说只是注册了一个struct virtio_device,并挂载到了virtio bus类型总线上,并由virtio driver来驱动。

virtio设备对于Linux Kernel中的设备类型来说是作为pci设备被使用的,因此具有pci设备的所有属性,所以其也具备了PCI配置空间。

PCI配置空间域

Linux在PCI设备启动初始化过程中通过IO mapping方式对配置空间的访问地址进行了映射。如下宏定义了每一个配置寄存器相对于PIC配置io基址的偏移。

而通过对如下的相关配置域的配置实现通用的Guest特性、队列、中断等操作。

file: /include/uapi/linux, line: 45
/* A 32-bit r/o bitmask of the features supported by the host */
#define VIRTIO_PCI_HOST_FEATURES 0
/* A 32-bit r/w bitmask of features activated by the guest */
#define VIRTIO_PCI_GUEST_FEATURES 4
/* A 32-bit r/w PFN for the currently selected queue */
#define VIRTIO_PCI_QUEUE_PFN 8
/* A 16-bit r/o queue size for the currently selected queue */
#define VIRTIO_PCI_QUEUE_NUM 12
/* A 16-bit r/w queue selector */
#define VIRTIO_PCI_QUEUE_SEL 14
/* A 16-bit r/w queue notifier */
#define VIRTIO_PCI_QUEUE_NOTIFY 16
/* An 8-bit device status register. */
#define VIRTIO_PCI_STATUS 18
/* An 8-bit r/o interrupt status register. Reading the value will return the
* current contents of the ISR and will also clear it. This is effectively
* a read-and-acknowledge. */

#define VIRTIO_PCI_ISR 19
/* The bit of the ISR which indicates a device configuration change. */
#define VIRTIO_PCI_ISR_CONFIG 0x2
/* MSI-X registers: only enabled if MSI-X is enabled. */
/* A 16-bit vector for configuration changes. */
#define VIRTIO_MSI_CONFIG_VECTOR 20
/* A 16-bit vector for selected queue notifications. */
#define VIRTIO_MSI_QUEUE_VECTOR 22
/* Vector value used to disable MSI for queue */
#define VIRTIO_MSI_NO_VECTOR 0xffff

Guest中的操作方式

Guest中virtio驱动通过iowrite/ioread等操作,比如:iowrite16(index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL); 与virtio设备进行交互,而该io操作会被VMM截获,从而转移至Host中进行处理。

而在QEMU中为支持virtio设备的模拟提供了virtio_portio函数注册结构来响应guest的不同读写请求。

file:/hw/virtio-pci.c, line: 464
static const MemoryRegionPortio virtio_portio[] = {
{ 0, 0x10000, 1, .write = virtio_pci_config_writeb, },
{ 0, 0x10000, 2, .write = virtio_pci_config_writew, },
{ 0, 0x10000, 4, .write = virtio_pci_config_writel, },
{ 0, 0x10000, 1, .read = virtio_pci_config_readb, },
{ 0, 0x10000, 2, .read = virtio_pci_config_readw, },
{ 0, 0x10000, 4, .read = virtio_pci_config_readl, },
PORTIO_END_OF_LIST()
};