该文是通过对virtio-1.2官方文档翻译生成的,文档的下载地址为: http://docs.oasis-open.org/virtio/virtio/v1.2/
Virtual environments without PCI support (a common situation in embedded devices models) might use simple memory mapped device (“virtio-mmio”) instead of the PCI device.
不支持PCI的虚拟环境(嵌入式设备模型中的常见情况)可能使用简单的内存映射设备(“virtio-mmio”)取代PCI设备。
The memory mapped virtio device behaviour is based on the PCI device specification. Therefore most operations including device initialization, queues configuration and buffer transfers are nearly identical. Existing differences are described in the following sections.
内存映射的virtio设备行为是基于PCI设备规范的。因此,包括设备初始化、队列配置和缓冲区传输在内的大多数操作几乎都是相同的。现有的差异将在下面的部分中进行描述。
Unlike PCI, MMIO provides no generic device discovery mechanism. For each device, the guest OS will need to know the location of the registers and interrupt(s) used. The suggested binding for systems using flattened device trees is shown in this example:
与PCI不同,MMIO不提供通用的设备发现机制。对于每个设备,guest操作系统将需要知道所使用的寄存器和中断的位置。使用扁平设备树的系统的建议绑定如下:
// EXAMPLE: virtio_block device taking 512 bytes at 0x1e000, interrupt 42.
virtio_block@1e000 {
compatible = "virtio,mmio";
reg = <0x1e000 0x200>;
interrupts = <42>;
}
MMIO virtio devices provide a set of memory mapped control registers followed by a device-specific configuration space, described in the table 4.1.
MMIO virtio设备提供了一组内存映射的控制寄存器,然后是一组设备特定的配置空间,详见表4.1。
All register values are organized as Little Endian.
所有的寄存器值都被组织为小端。
MagicValue 0x000: Magic value 0x74726976 (a Little Endian equivalent of the “virt” string)
Version 0x004:Device version number 0x2
Note: Legacy devices (see 4.2.4 Legacy interface) used 0x1.
遗留设备(见4.2.4遗留接口)使用了0x1。
DeviceID 0x008:Virtio Subsystem Device ID
See 5 Device Types for possible values. Value zero (0x0) is used to define a system memory map with placeholder devices at static, well known addresses, assigning functions to them depending on user’s needs
有关可能的值,请参见5个设备类型。值0(0x0)用于定义一个系统内存映射,其中具有静态占位符设备的知名地址,并根据用户的需要为它们分配函数。
VendorID 0x00c:Virtio Subsystem Vendor ID
DeviceFeatures 0x010:Flags representing features the device supports
Reading from this register returns 32 consecutive flag bits, the least significant bit depending on the last value written to DeviceFeaturesSel. Access to this register returns bits DeviceFeaturesSel * 32 to (DeviceFeaturesSel * 32)+31, eg. feature bits 0 to 31 if DeviceFeaturesSel is set to 0 and features
bits 32 to 63 if DeviceFeaturesSel is set to 1. Also see 2.2 Feature Bits.
DeviceFeaturesSel 0x014:Device (host) features word selection.
Writing to this register selects a set of 32 device feature bits accessible by reading from DeviceFeatures.
DriverFeatures 0x020:Flags representing device features understood and activated by the
driver
Writing to this register sets 32 consecutive flag bits, the least significant bit depending on the last value written to DriverFeaturesSel. Access to this register sets bits DriverFeaturesSel ∗ 32 to (DriverFeaturesSel ∗ 32) + 31, eg. feature bits 0 to 31 if DriverFeaturesSel is set to 0 and features bits 32 to 63 if DriverFeaturesSel is set to 1. Also see 2.2 Feature Bits.
DriverFeaturesSel 0x024:Activated (guest) features word selection
Writing to this register selects a set of 32 activated feature bits accessible by writing to DriverFeatures.
QueueSel 0x030:Virtual queue index
Writing to this register selects the virtual queue that the following operations on QueueNumMax, QueueNum, QueueReady, QueueDescLow, QueueDescHigh, QueueDriverlLow, QueueDriverHigh, QueueDeviceLow, QueueDeviceHigh and QueueReset apply to. The index number of the first queue is zero (0x0).
QueueNumMax 0x034:Maximum virtual queue size
Reading from the register returns the maximum size (number of elements) of the queue the device is ready to process or zero (0x0) if the queue is not available. This applies to the queue selected by writing to QueueSel.
QueueNum 0x038:Virtual queue size
Queue size is the number of elements in the queue. Writing to this register notifies the device what size of the queue the driver will use. This applies to the queue selected by writing to QueueSel.
QueueReady 0x044:Virtual queue ready bit
Writing one (0x1) to this register notifies the device that it can execute re- quests from this virtual queue. Reading from this register returns the last value written to it. Both read and write accesses apply to the queue selected by writing to QueueSel.
QueueNotify 0x050:Queue notifier
Writing a value to this register notifies the device that there are new buffers to process in a queue.
When VIRTIO_F_NOTIFICATION_DATA has not been negotiated, the value written is the queue index.
When VIRTIO_F_NOTIFICATION_DATA has been negotiated, the Notifi- cation data value has the following format:
le32 {
vqn : 16;
next_off : 15;
next_wrap : 1;
};
See 2.9 Driver Notifications for the definition of the components.
InterruptStatus 0x60:Interrupt status
Reading from this register returns a bit mask of events that caused the de- vice interrupt to be asserted. The following events are possible:
Used Buffer Notification - bit 0 - the interrupt was asserted because the device has used a buffer in at least one of the active virtual queues.
Configuration Change Notification - bit 1 - the interrupt was asserted be- cause the configuration of the device has changed.
InterruptACK 0x064:Interrupt acknowledge
Writing a value with bits set as defined in InterruptStatus to this register notifies the device that events causing the interrupt have been handled.
Status 0x070:Device status
Reading from this register returns the current device status flags. Writing non-zero values to this register sets the status flags, indicating the driver progress. Writing zero (0x0) to this register triggers a device reset. See also p. 4.2.3.1 Device Initialization.
QueueDescLow 0x080:
QueueDescHigh 0x084:Virtual queue’s Descriptor Area 64 bit long physical address
Writing to these two registers (lower 32 bits of the address to QueueDescLow, higher 32 bits to QueueDescHigh) notifies the device about location of the Descriptor Area of the queue selected by writing to QueueSel register.
QueueDriverLow 0x090:
QueueDriverHigh 0x094:Virtual queue’s Driver Area 64 bit long physical address
Writing to these two registers (lower 32 bits of the address to QueueDriver- Low, higher 32 bits to QueueDriverHigh) notifies the device about location of the Driver Area of the queue selected by writing to QueueSel.
QueueDeviceLow 0x0a0:
QueueDeviceHigh 0x0a4:Virtual queue’s Device Area 64 bit long physical address
Writing to these two registers (lower 32 bits of the address to QueueDe- viceLow, higher 32 bits to QueueDeviceHigh) notifies the device about lo- cation of the Device Area of the queue selected by writing to QueueSel.
SHMSel 0x0ac:Shared memory id
Writing to this register selects the shared memory region 2.10 following op- erations on SHMLenLow, SHMLenHigh, SHMBaseLow and SHMBaseHigh apply to.
SHMLenLow 0x0b0:
SHMLenHigh 0x0b4:Shared memory region 64 bit long length
These registers return the length of the shared memory region in bytes, as defined by the device for the region selected by the SHMSel register. The lower 32 bits of the length are read from SHMLenLow and the higher 32 bits from SHMLenHigh. Reading from a non-existent region (i.e. where the ID written to SHMSel is unused) results in a length of - 1.
SHMBaseLow 0x0b8:
SHMBaseHigh 0x0bc:Shared memory region 64 bit long physical address
The driver reads these registers to discover the base address of the region in physical address space. This address is chosen by the device (or other part of the VMM). The lower 32 bits of the address are read from SHMBaseLow with the higher 32 bits from SHMBaseHigh. Reading from a non-existent region (i.e. where the ID written to SHMSel is unused) results in a base
address of 0xffffffffffffffff.
QueueReset 0x0c0:Virtual queue reset bit
If VIRTIO_F_RING_RESET has been negotiated, writing one (0x1) to this register selectively resets the queue. Both read and write accesses apply to the queue selected by writing to QueueSel.
ConfigGeneration 0x0fc:Configuration atomicity value
Reading from this register returns a value describing a version of the device- specific configuration space (see Config). The driver can then access the configuration space and, when finished, read ConfigGeneration again. If no part of the configuration space has changed between these two ConfigGen- eration reads, the returned values are identical. If the values are different, the configuration space accesses were not atomic and the driver has to perform the operations again. See also 2.5.
Config 0x100:Configuration space
Device-specific configuration space starts at the offset 0x100 and is ac- cessed with byte alignment. Its meaning and size depend on the device and the driver.
The device MUST return 0x74726976 in MagicValue.
The device MUST return value 0x2 in Version.
The device MUST present each event by setting the corresponding bit in InterruptStatus from the moment it takes place, until the driver acknowledges the interrupt by writing a corresponding bit mask to the Inter- ruptACK register. Bits which do not represent events which took place MUST be zero.
Upon reset, the device MUST clear all bits in InterruptStatus and ready bits in the QueueReady register for all queues in the device.
The device MUST change value returned in ConfigGeneration if there is any risk of a driver seeing an inconsistent configuration state.
The device MUST NOT access virtual queue contents when QueueReady is zero (0x0). If VIRTIO_F_RING_RESET has been negotiated, the device MUST present a 0 in QueueReset on reset.
If VIRTIO_F_RING_RESET has been negotiated, The device MUST present a 0 in QueueReset after the virtqueue is enabled with QueueReady.
The device MUST reset the queue when 1 is written to QueueReset. The device MUST continue to present 1 in QueueReset as long as the queue reset is ongoing. The device MUST present 0 in both QueueReset and QueueReady when queue reset has completed. (see 2.6.1).
The driver MUST NOT access memory locations not described in the table 4.1 (or, in case of the configuration space, described in the device specification), MUST NOT write to the read-only registers (direction R) and MUST NOT read from the write-only registers (direction W).
The driver MUST only use 32 bit wide and aligned reads and writes to access the control registers described in table 4.1. For the device-specific configuration space, the driver MUST use 8 bit wide accesses for 8 bit
wide fields, 16 bit wide and aligned accesses for 16 bit wide fields and 32 bit wide and aligned accesses for 32 and 64 bit wide fields.
The driver MUST ignore a device with MagicValue which is not 0x74726976, although it MAY report an error. The driver MUST ignore a device with Version which is not 0x2, although it MAY report an error. The driver MUST ignore a device with DeviceID 0x0, but MUST NOT report any error.
Before reading from DeviceFeatures, the driver MUST write a value to DeviceFeaturesSel.
Before writing to the DriverFeatures register, the driver MUST write a value to the DriverFeaturesSel register.
The driver MUST write a value to QueueNum which is less than or equal to the value presented by the device in QueueNumMax.
When QueueReady is not zero, the driver MUST NOT access QueueNum, QueueDescLow, QueueDescHigh, QueueDriverLow, QueueDriverHigh, QueueDeviceLow, QueueDeviceHigh.
To stop using the queue the driver MUST write zero (0x0) to this QueueReady and MUST read the value back to ensure synchronization.
The driver MUST ignore undefined bits in InterruptStatus.
The driver MUST write a value with a bit mask describing events it handled into InterruptACK when it finishes handling an interrupt and MUST NOT set any of the undefined bits in the value.
If VIRTIO_F_RING_RESET has been negotiated, after the driver writes 1 to QueueReset to reset the queue, the driver MUST NOT consider queue reset to be complete until it reads back 0 in QueueReset. The driver MAY re-enable the queue by writing 1 to QueueReady after ensuring that other virtqueue fields have been set up correctly. The driver MAY set driver-writeable queue configuration values to different values than those that were used before the queue reset. (see 2.6.1).
The driver MUST start the device initialization by reading and checking values from MagicValue and Version. If both values are valid, it MUST read DeviceID and if its value is zero (0x0) MUST abort initialization and MUST NOT access any other register.
Drivers not expecting shared memory MUST NOT use the shared memory registers.
Further initialization MUST follow the procedure described in 3.1 Device Initialization.
The driver will typically initialize the virtual queue in the following way:
When VIRTIO_F_NOTIFICATION_DATA has not been negotiated, the driver sends an available buffer no- tification to the device by writing the 16-bit virtqueue index of the queue to be notified to QueueNotify.
When VIRTIO_F_NOTIFICATION_DATA has been negotiated, the driver sends an available buffer notifica- tion to the device by writing the following 32-bit value to QueueNotify:
le32 {
vqn : 16;
next_off : 15;
next_wrap : 1;
};
See 2.9 Driver Notifications for the definition of the components.
The memory mapped virtio device is using a single, dedicated interrupt signal, which is asserted when at least one of the bits described in the description of InterruptStatus is set. This is how the device sends a used buffer notification or a configuration change notification to the device.
After receiving an interrupt, the driver MUST read InterruptStatus to check what caused the interrupt (see the register description). The used buffer notification bit being set SHOULD be interpreted as a used buffer notification for each active virtqueue. After the interrupt is handled, the driver MUST acknowledge it by writing a bit mask corresponding to the handled events to the InterruptACK register.
The legacy MMIO transport used page-based addressing, resulting in a slightly different control register layout, the device initialization and the virtual queue configuration procedure.
Table 4.2 presents control registers layout, omitting descriptions of registers which did not change their function nor behaviour:
The virtual queue page size is defined by writing to GuestPageSize, as written by the guest. The driver does this before the virtual queues are configured.
The virtual queue layout follows p. 2.7.2 Legacy Interfaces: A Note on Virtqueue Layout, with the alignment defined in QueueAlign.
The virtual queue is configured as follows: