vfio-pci: Towards VGA support
Subsystems (2)
cover vfio-pci: Towards VGA support
vfio makes a nice interface to start looking at supporting VGA devices assigned to virtual machines (ie. userspace drivers) because we can so easily add additional ranges for a device. In this patch we add legacy MMIO (below 1MB) and I/O port (64k) to devices with PCI class code VGA. We can then use the kernel VGA arbiter service to change chipset routing for each access to the VGA ranges defined in the PCI spec. The rest of the region space not used by VGA is left inaccessible until we add future feature that needs some other legacy range.
There's also a qemu userspace companion series to this which learns how to look for this new feature flag and setup ranges. Together they get a step closer to supporting vfio-based VGA assignment, but it doesn't yet work. I'm posting in this broken state both for archival purposes as well as the hope that someone has ideas of what might be missing or be able to pick up and run with this code.
Some cards are able to get through execution of their VGA BIOS with these patches, but none that I've seen sync the monitor to VGA text mode from seabios. With a hack in qemu for a card specific backdoor on a Radeon HD5450 I've been able to get syslinux graphics mode to work and Windows will use it during normal bootup. I have no idea what might be missing for VGA text mode. Thanks,
Alex
---
Alex Williamson (1):
vfio-pci: [NOT FOR COMMIT] Add support for legacy MMIO & I/O port towards VGA support
drivers/vfio/pci/vfio_pci.c | 74 ++++++++++++--- drivers/vfio/pci/vfio_pci_private.h | 6 + drivers/vfio/pci/vfio_pci_rdwr.c | 170 +++++++++++++++++++++++++++-------- include/uapi/linux/vfio.h | 3 + 4 files changed, 197 insertions(+), 56 deletions(-)
PATCH 1/1 vfio-pci: [NOT FOR COMMIT] Add support for legacy MMIO & I/O port towards VGA support
Create two new legacy regions, one for MMIO space below 1MB and another for 64k of I/O port space. For devices of PCI class VGA these ranges will be exposed and allow direct access to the device at the PCI defined VGA addresses, 0xa0000, 0x3b0, 0x3c0. VFIO makes use of the host VGA arbiter to manage host chipset config to route each access to the correct device.
--- drivers/vfio/pci/vfio_pci.c | 74 ++++++++++++--- drivers/vfio/pci/vfio_pci_private.h | 6 + drivers/vfio/pci/vfio_pci_rdwr.c | 170 +++++++++++++++++++++++++++-------- include/uapi/linux/vfio.h | 3 + 4 files changed, 197 insertions(+), 56 deletions(-)
drivers/vfio/pci/vfio_pci.c +59 -15
@@ -223,9 +223,14 @@ static long vfio_pci_ioctl(void *device_data,if (vdev->reset_works) info.flags |=VFIO_DEVICE_FLAGS_RESET ; - info.num_regions =VFIO_PCI_NUM_REGIONS ; + info.num_regions =VFIO_PCI_CONFIG_REGION_INDEX +1 ; info.num_irqs =VFIO_PCI_NUM_IRQS ; +if ((vdev->pdev->class >>8 ) ==PCI_CLASS_DISPLAY_VGA ) { + info.flags |=VFIO_DEVICE_FLAGS_VGA ; + info.num_regions +=2 ; + } +return copy_to_user ((void __user *)arg, &info, minsz); }else if (cmd ==VFIO_DEVICE_GET_REGION_INFO ) {
@@ -285,6 +290,26 @@ static long vfio_pci_ioctl(void *device_data, info.flags =VFIO_REGION_INFO_FLAG_READ ;break ; } +case VFIO_PCI_LEGACY_MMIO_REGION_INDEX : +if ((pdev->class >>8 ) !=PCI_CLASS_DISPLAY_VGA ) +return -EINVAL ; + + info.offset =VFIO_PCI_INDEX_TO_OFFSET (info.index); + info.size =1024 *1024 ; + info.flags =VFIO_REGION_INFO_FLAG_READ | +VFIO_REGION_INFO_FLAG_WRITE ; + +break ; +case VFIO_PCI_LEGACY_IOPORT_REGION_INDEX : +if ((pdev->class >>8 ) !=PCI_CLASS_DISPLAY_VGA ) +return -EINVAL ; + + info.offset =VFIO_PCI_INDEX_TO_OFFSET (info.index); + info.size =64 *1024 ; + info.flags =VFIO_REGION_INFO_FLAG_READ | +VFIO_REGION_INFO_FLAG_WRITE ; + +break ;default :return -EINVAL ; }
@@ -376,14 +401,25 @@ static ssize_t vfio_pci_read(void *device_data, char __user *buf,if (index >=VFIO_PCI_NUM_REGIONS )return -EINVAL ; -if (index ==VFIO_PCI_CONFIG_REGION_INDEX ) +switch (index) { +case VFIO_PCI_CONFIG_REGION_INDEX :return vfio_pci_config_readwrite (vdev, buf, count, ppos, false); -else if (index ==VFIO_PCI_ROM_REGION_INDEX ) -return vfio_pci_mem_readwrite (vdev, buf, count, ppos, false); -else if (pci_resource_flags (pdev, index) &IORESOURCE_IO ) -return vfio_pci_io_readwrite (vdev, buf, count, ppos, false); -else if (pci_resource_flags (pdev, index) &IORESOURCE_MEM ) +case VFIO_PCI_ROM_REGION_INDEX :return vfio_pci_mem_readwrite (vdev, buf, count, ppos, false); +case VFIO_PCI_LEGACY_MMIO_REGION_INDEX : +return vfio_pci_legacy_mem_readwrite (vdev, buf, count, + ppos, false); +case VFIO_PCI_LEGACY_IOPORT_REGION_INDEX : +return vfio_pci_legacy_io_readwrite (vdev, buf, count, + ppos, false); +default : +if (pci_resource_flags (pdev, index) &IORESOURCE_IO ) +return vfio_pci_io_readwrite (vdev, buf, count, + ppos, false); +if (pci_resource_flags (pdev, index) &IORESOURCE_MEM ) +return vfio_pci_mem_readwrite (vdev, buf, count, + ppos, false); + }return -EINVAL ; }
@@ -398,17 +434,25 @@ static ssize_t vfio_pci_write(void *device_data, const char __user *buf,if (index >=VFIO_PCI_NUM_REGIONS )return -EINVAL ; -if (index ==VFIO_PCI_CONFIG_REGION_INDEX ) +switch (index) { +case VFIO_PCI_CONFIG_REGION_INDEX :return vfio_pci_config_readwrite (vdev, (char __user *)buf, count, ppos, true); -else if (index ==VFIO_PCI_ROM_REGION_INDEX ) +case VFIO_PCI_ROM_REGION_INDEX :return -EINVAL ; -else if (pci_resource_flags (pdev, index) &IORESOURCE_IO ) -return vfio_pci_io_readwrite (vdev, (char __user *)buf, - count, ppos, true); -else if (pci_resource_flags (pdev, index) &IORESOURCE_MEM ) { -return vfio_pci_mem_readwrite (vdev, (char __user *)buf, - count, ppos, true); +case VFIO_PCI_LEGACY_MMIO_REGION_INDEX : +return vfio_pci_legacy_mem_readwrite (vdev, buf, count, + ppos, false); +case VFIO_PCI_LEGACY_IOPORT_REGION_INDEX : +return vfio_pci_legacy_io_readwrite (vdev, buf, count, + ppos, false); +default : +if (pci_resource_flags (pdev, index) &IORESOURCE_IO ) +return vfio_pci_io_readwrite (vdev, (char __user *)buf, + count, ppos, true); +if (pci_resource_flags (pdev, index) &IORESOURCE_MEM ) +return vfio_pci_mem_readwrite (vdev, (char __user *)buf, + count, ppos, true); }return -EINVAL ;
drivers/vfio/pci/vfio_pci_private.h +6 -0
@@ -79,6 +79,12 @@ extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev,extern ssize_t vfio_pci_io_readwrite (struct vfio_pci_device *vdev,char __user *buf,size_t count,loff_t *ppos,bool iswrite); +extern ssize_t vfio_pci_legacy_mem_readwrite (struct vfio_pci_device *vdev, +char __user *buf,size_t count, +loff_t *ppos,bool iswrite); +extern ssize_t vfio_pci_legacy_io_readwrite (struct vfio_pci_device *vdev, +char __user *buf,size_t count, +loff_t *ppos,bool iswrite);extern int vfio_pci_init_perm_bits (void );extern void vfio_pci_uninit_perm_bits (void );
drivers/vfio/pci/vfio_pci_rdwr.c +129 -41
224 lines not on this page · raw message
include/uapi/linux/vfio.h +3 -0
@@ -147,6 +147,7 @@ struct vfio_device_info {__u32 flags;#define VFIO_DEVICE_FLAGS_RESET (1 << 0)/* Device supports reset */ #define VFIO_DEVICE_FLAGS_PCI (1 << 1)/* vfio-pci device */ +#define VFIO_DEVICE_FLAGS_VGA (1 << 2)/* Device supports VGA ranges */ __u32 num_regions;/* Max region index + 1 */ __u32 num_irqs;/* Max IRQ index + 1 */ };
@@ -303,6 +304,8 @@ enum {VFIO_PCI_BAR5_REGION_INDEX ,VFIO_PCI_ROM_REGION_INDEX ,VFIO_PCI_CONFIG_REGION_INDEX , +VFIO_PCI_LEGACY_MMIO_REGION_INDEX , +VFIO_PCI_LEGACY_IOPORT_REGION_INDEX , VFIO_PCI_NUM_REGIONS };