virtio-pci设备的bar空间
旧的virtio协议采用 lagacy模式,将virtio设备相关的配置放在bar-0中。
新的virtio协议modern 模式中的配置结构共有五种,分别为:
/* Common configuration */
#define VIRTIO_PCI_CAP_COMMON_CFG 1
/* Notifications */
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2
/* ISR Status */
#define VIRTIO_PCI_CAP_ISR_CFG 3
/* Device specific configuration */
#define VIRTIO_PCI_CAP_DEVICE_CFG 4
/* PCI configuration access */
#define VIRTIO_PCI_CAP_PCI_CFG 5
5种配置结构都存放在设备的bar空间中,可以有不同的偏移。可以通过结构体struct virtio_pci_cap 中指定的bar-index和offset找到。
结构体struct virtio_pci_cap 如下,cft_type为上述的五种类型:
struct virtio_pci_cap {
u8 cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR = 0x9 */
u8 cap_next; /* Generic PCI field: next ptr. */
u8 cap_len; /* Generic PCI field: capability length */
u8 cfg_type; /* Identifies the structure. */
u8 bar; /* Where to find it. */
u8 padding[3]; /* Pad to full dword. */
le32 offset; /* Offset within bar. */
le32 length; /* Length of the structure, in bytes. */
};
- 在qemu中,为了向前兼容,bar-0依旧用作存放legacy模式下的配置信息。
- notify_io config modern-io MR bar-2
- notify/isr/device/ common cfg modern-mem MR bar-4
- msix-table/pending-table bar-1
- cloud-hypervisor只支持medern模式,几种config对应bar-0中不同的偏移,cloud-hypervisor中定义的偏移分别为
const COMMON_CONFIG_BAR_OFFSET: u64 = 0x0000;
const COMMON_CONFIG_SIZE: u64 = 56;
const ISR_CONFIG_BAR_OFFSET: u64 = 0x2000;
const ISR_CONFIG_SIZE: u64 = 1;
const DEVICE_CONFIG_BAR_OFFSET: u64 = 0x4000;
const DEVICE_CONFIG_SIZE: u64 = 0x1000;
const NOTIFICATION_BAR_OFFSET: u64 = 0x6000;
const NOTIFICATION_SIZE: u64 = 0x1000;
const MSIX_TABLE_BAR_OFFSET: u64 = 0x8000;
const MSIX_TABLE_SIZE: u64 = 0x40000;
const MSIX_PBA_BAR_OFFSET: u64 = 0x48000;
// The size is 2KiB because the Pending Bit Array has one bit per vector and it
// can support up to 2048 vectors.
const MSIX_PBA_SIZE: u64 = 0x800;
//bar-0 的大小
const CAPABILITY_BAR_SIZE: u64 = 0x80000;
cloud-hypervisor的初始化过程:
-
bar-0初始化结束之后,会将bar-0的地址(在cloud-hypervisor中,地址是分配的)设置到pci configuration中
-
然后,设置virtio设备相关的配置结构: 指定在bar-0中的偏移和大小,即(cfg-type, bar-index, offset, size)
并将该配置,设置到PCI configuration中的capability-list中
观察read_bar()函数,会根据offset属于哪个配置结构,决定操作设备的哪个cfg
内核驱动加载时,除非强制选择legacy模式, 否则都选择modern模式。