vfio-pci: Add support for VGA region access
PCI defines display class VGA regions at I/O port address 0x3b0, 0x3c0 and MMIO address 0xa0000. As these are non-overlapping, we can ignore the I/O port vs MMIO difference and expose them both in a single region. We make use of the VGA arbiter around each access to configure chipset access as necessary. Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
This commit is contained in:
parent
2dd1194833
commit
84237a826b
@ -6,3 +6,13 @@ config VFIO_PCI
|
||||
use of PCI drivers using the VFIO framework.
|
||||
|
||||
If you don't know what to do here, say N.
|
||||
|
||||
config VFIO_PCI_VGA
|
||||
bool "VFIO PCI support for VGA devices"
|
||||
depends on VFIO_PCI && X86 && VGA_ARB && EXPERIMENTAL
|
||||
help
|
||||
Support for VGA extension to VFIO PCI. This exposes an additional
|
||||
region on VGA devices for accessing legacy VGA addresses used by
|
||||
BIOS and generic video drivers.
|
||||
|
||||
If you don't know what to do here, say N.
|
||||
|
@ -84,6 +84,11 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
|
||||
} else
|
||||
vdev->msix_bar = 0xFF;
|
||||
|
||||
#ifdef CONFIG_VFIO_PCI_VGA
|
||||
if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
|
||||
vdev->has_vga = true;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -285,6 +290,16 @@ static long vfio_pci_ioctl(void *device_data,
|
||||
info.flags = VFIO_REGION_INFO_FLAG_READ;
|
||||
break;
|
||||
}
|
||||
case VFIO_PCI_VGA_REGION_INDEX:
|
||||
if (!vdev->has_vga)
|
||||
return -EINVAL;
|
||||
|
||||
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
|
||||
info.size = 0xc0000;
|
||||
info.flags = VFIO_REGION_INFO_FLAG_READ |
|
||||
VFIO_REGION_INFO_FLAG_WRITE;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -386,6 +401,9 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
|
||||
|
||||
case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
|
||||
return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite);
|
||||
|
||||
case VFIO_PCI_VGA_REGION_INDEX:
|
||||
return vfio_pci_vga_rw(vdev, buf, count, ppos, iswrite);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
@ -53,6 +53,7 @@ struct vfio_pci_device {
|
||||
bool reset_works;
|
||||
bool extended_caps;
|
||||
bool bardirty;
|
||||
bool has_vga;
|
||||
struct pci_saved_state *pci_saved_state;
|
||||
atomic_t refcnt;
|
||||
};
|
||||
@ -77,6 +78,9 @@ extern ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev,
|
||||
extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
|
||||
size_t count, loff_t *ppos, bool iswrite);
|
||||
|
||||
extern ssize_t vfio_pci_vga_rw(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);
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/vgaarb.h>
|
||||
|
||||
#include "vfio_pci_private.h"
|
||||
|
||||
@ -175,3 +176,63 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf,
|
||||
size_t count, loff_t *ppos, bool iswrite)
|
||||
{
|
||||
int ret;
|
||||
loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK;
|
||||
void __iomem *iomem = NULL;
|
||||
unsigned int rsrc;
|
||||
bool is_ioport;
|
||||
ssize_t done;
|
||||
|
||||
if (!vdev->has_vga)
|
||||
return -EINVAL;
|
||||
|
||||
switch (pos) {
|
||||
case 0xa0000 ... 0xbffff:
|
||||
count = min(count, (size_t)(0xc0000 - pos));
|
||||
iomem = ioremap_nocache(0xa0000, 0xbffff - 0xa0000 + 1);
|
||||
off = pos - 0xa0000;
|
||||
rsrc = VGA_RSRC_LEGACY_MEM;
|
||||
is_ioport = false;
|
||||
break;
|
||||
case 0x3b0 ... 0x3bb:
|
||||
count = min(count, (size_t)(0x3bc - pos));
|
||||
iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1);
|
||||
off = pos - 0x3b0;
|
||||
rsrc = VGA_RSRC_LEGACY_IO;
|
||||
is_ioport = true;
|
||||
break;
|
||||
case 0x3c0 ... 0x3df:
|
||||
count = min(count, (size_t)(0x3e0 - pos));
|
||||
iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1);
|
||||
off = pos - 0x3c0;
|
||||
rsrc = VGA_RSRC_LEGACY_IO;
|
||||
is_ioport = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!iomem)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = vga_get_interruptible(vdev->pdev, rsrc);
|
||||
if (ret) {
|
||||
is_ioport ? ioport_unmap(iomem) : iounmap(iomem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
done = do_io_rw(iomem, buf, off, count, 0, 0, iswrite);
|
||||
|
||||
vga_put(vdev->pdev, rsrc);
|
||||
|
||||
is_ioport ? ioport_unmap(iomem) : iounmap(iomem);
|
||||
|
||||
if (done >= 0)
|
||||
*ppos += done;
|
||||
|
||||
return done;
|
||||
}
|
||||
|
@ -303,6 +303,15 @@ enum {
|
||||
VFIO_PCI_BAR5_REGION_INDEX,
|
||||
VFIO_PCI_ROM_REGION_INDEX,
|
||||
VFIO_PCI_CONFIG_REGION_INDEX,
|
||||
/*
|
||||
* Expose VGA regions defined for PCI base class 03, subclass 00.
|
||||
* This includes I/O port ranges 0x3b0 to 0x3bb and 0x3c0 to 0x3df
|
||||
* as well as the MMIO range 0xa0000 to 0xbffff. Each implemented
|
||||
* range is found at it's identity mapped offset from the region
|
||||
* offset, for example 0x3b0 is region_info.offset + 0x3b0. Areas
|
||||
* between described ranges are unimplemented.
|
||||
*/
|
||||
VFIO_PCI_VGA_REGION_INDEX,
|
||||
VFIO_PCI_NUM_REGIONS
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user