在qemu/hw/vfio 中的common.c 中
int vfio_get_region_info(VFIODevice *vbasedev, int index,
struct vfio_region_info **info)
{
size_t argsz = sizeof(struct vfio_region_info);
*info = g_malloc0(argsz);
(*info)->index = index;
retry:
(*info)->argsz = argsz;
if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) {
g_free(*info);
*info = NULL;
return -errno;
}
return 0;
}
vfio_get_region_info 中是通过VFIO_DEVICE_GET_REGION_INFO 来得到设备io的信息,这里的info是是一个字符串。从
device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:06:0d.0");中可以看出info 通过bus:device:fn表示一个pcie的device.
VFIO_DEVICE_GET_REGION_INFO 这个ioctl对应kernel中的vfio_pci_ioctl 中的命令
static long vfio_pci_ioctl(void *device_data,
unsigned int cmd, unsigned long arg)
{
else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
struct pci_dev *pdev = vdev->pdev;
struct vfio_region_info info;
struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
int i, ret;
minsz = offsetofend(struct vfio_region_info, offset);
if (copy_from_user(&info, (void __user *)arg, minsz))
return -EFAULT;
if (info.argsz < minsz)
return -EINVAL;
switch (info.index) {
case VFIO_PCI_CONFIG_REGION_INDEX:
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
info.size = pdev->cfg_size;
info.flags = VFIO_REGION_INFO_FLAG_READ |
VFIO_REGION_INFO_FLAG_WRITE;
break;
case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
info.size = pci_resource_len(pdev, info.index);
if (!info.size) {
info.flags = 0;
break;
}
info.flags = VFIO_REGION_INFO_FLAG_READ |
VFIO_REGION_INFO_FLAG_WRITE;
if (vdev->bar_mmap_supported[info.index]) {
info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
if (info.index == vdev->msix_bar) {
ret = msix_sparse_mmap_cap(vdev, &caps);
if (ret)
return ret;
}
}
break;
case VFIO_PCI_ROM_REGION_INDEX:
case VFIO_PCI_VGA_REGION_INDEX:
return copy_to_user((void __user *)arg, &info, minsz) ?
-EFAULT : 0;
}
}
从中可以看到 io总共分为9种
enum {
VFIO_PCI_BAR0_REGION_INDEX,
VFIO_PCI_BAR1_REGION_INDEX,
VFIO_PCI_BAR2_REGION_INDEX,
VFIO_PCI_BAR3_REGION_INDEX,
VFIO_PCI_BAR4_REGION_INDEX,
VFIO_PCI_BAR5_REGION_INDEX,
VFIO_PCI_ROM_REGION_INDEX,
VFIO_PCI_CONFIG_REGION_INDEX,
VFIO_PCI_VGA_REGION_INDEX,
VFIO_PCI_NUM_REGIONS = 9 /* Fixed user ABI, region indexes >=9 use */
/* device specific cap to define content. */
};
我们这里以VFIO_PCI_BAR0_REGION_INDEX 为例,在这里只是准备好了struct vfio_region_info info的offset和size,并设置了info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
最终的通过vfio_pci_mmap中的remap_pfn_range来将bar空间映射到user space,这样就可以在user space直接访问pcie的bar空间.
总结一下io空间都是通过ioctl先拿到io的信息后,通过mmap 到user space,让其直接时候io 空间
int vfio_get_region_info(VFIODevice *vbasedev, int index,
struct vfio_region_info **info)
{
size_t argsz = sizeof(struct vfio_region_info);
*info = g_malloc0(argsz);
(*info)->index = index;
retry:
(*info)->argsz = argsz;
if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) {
g_free(*info);
*info = NULL;
return -errno;
}
return 0;
}
vfio_get_region_info 中是通过VFIO_DEVICE_GET_REGION_INFO 来得到设备io的信息,这里的info是是一个字符串。从
device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:06:0d.0");中可以看出info 通过bus:device:fn表示一个pcie的device.
VFIO_DEVICE_GET_REGION_INFO 这个ioctl对应kernel中的vfio_pci_ioctl 中的命令
static long vfio_pci_ioctl(void *device_data,
unsigned int cmd, unsigned long arg)
{
else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
struct pci_dev *pdev = vdev->pdev;
struct vfio_region_info info;
struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
int i, ret;
minsz = offsetofend(struct vfio_region_info, offset);
if (copy_from_user(&info, (void __user *)arg, minsz))
return -EFAULT;
if (info.argsz < minsz)
return -EINVAL;
switch (info.index) {
case VFIO_PCI_CONFIG_REGION_INDEX:
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
info.size = pdev->cfg_size;
info.flags = VFIO_REGION_INFO_FLAG_READ |
VFIO_REGION_INFO_FLAG_WRITE;
break;
case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
info.size = pci_resource_len(pdev, info.index);
if (!info.size) {
info.flags = 0;
break;
}
info.flags = VFIO_REGION_INFO_FLAG_READ |
VFIO_REGION_INFO_FLAG_WRITE;
if (vdev->bar_mmap_supported[info.index]) {
info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
if (info.index == vdev->msix_bar) {
ret = msix_sparse_mmap_cap(vdev, &caps);
if (ret)
return ret;
}
}
break;
case VFIO_PCI_ROM_REGION_INDEX:
case VFIO_PCI_VGA_REGION_INDEX:
return copy_to_user((void __user *)arg, &info, minsz) ?
-EFAULT : 0;
}
}
从中可以看到 io总共分为9种
enum {
VFIO_PCI_BAR0_REGION_INDEX,
VFIO_PCI_BAR1_REGION_INDEX,
VFIO_PCI_BAR2_REGION_INDEX,
VFIO_PCI_BAR3_REGION_INDEX,
VFIO_PCI_BAR4_REGION_INDEX,
VFIO_PCI_BAR5_REGION_INDEX,
VFIO_PCI_ROM_REGION_INDEX,
VFIO_PCI_CONFIG_REGION_INDEX,
VFIO_PCI_VGA_REGION_INDEX,
VFIO_PCI_NUM_REGIONS = 9 /* Fixed user ABI, region indexes >=9 use */
/* device specific cap to define content. */
};
我们这里以VFIO_PCI_BAR0_REGION_INDEX 为例,在这里只是准备好了struct vfio_region_info info的offset和size,并设置了info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
最终的通过vfio_pci_mmap中的remap_pfn_range来将bar空间映射到user space,这样就可以在user space直接访问pcie的bar空间.
总结一下io空间都是通过ioctl先拿到io的信息后,通过mmap 到user space,让其直接时候io 空间