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>hifive-unleashed-5.1
parent
2dd1194833
commit
84237a826b
|
@ -6,3 +6,13 @@ config VFIO_PCI
|
||||||
use of PCI drivers using the VFIO framework.
|
use of PCI drivers using the VFIO framework.
|
||||||
|
|
||||||
If you don't know what to do here, say N.
|
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
|
} else
|
||||||
vdev->msix_bar = 0xFF;
|
vdev->msix_bar = 0xFF;
|
||||||
|
|
||||||
|
#ifdef CONFIG_VFIO_PCI_VGA
|
||||||
|
if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
|
||||||
|
vdev->has_vga = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +290,16 @@ static long vfio_pci_ioctl(void *device_data,
|
||||||
info.flags = VFIO_REGION_INFO_FLAG_READ;
|
info.flags = VFIO_REGION_INFO_FLAG_READ;
|
||||||
break;
|
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:
|
default:
|
||||||
return -EINVAL;
|
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:
|
case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
|
||||||
return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite);
|
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;
|
return -EINVAL;
|
||||||
|
|
|
@ -53,6 +53,7 @@ struct vfio_pci_device {
|
||||||
bool reset_works;
|
bool reset_works;
|
||||||
bool extended_caps;
|
bool extended_caps;
|
||||||
bool bardirty;
|
bool bardirty;
|
||||||
|
bool has_vga;
|
||||||
struct pci_saved_state *pci_saved_state;
|
struct pci_saved_state *pci_saved_state;
|
||||||
atomic_t refcnt;
|
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,
|
extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
|
||||||
size_t count, loff_t *ppos, bool iswrite);
|
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 int vfio_pci_init_perm_bits(void);
|
||||||
extern void vfio_pci_uninit_perm_bits(void);
|
extern void vfio_pci_uninit_perm_bits(void);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/vgaarb.h>
|
||||||
|
|
||||||
#include "vfio_pci_private.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;
|
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_BAR5_REGION_INDEX,
|
||||||
VFIO_PCI_ROM_REGION_INDEX,
|
VFIO_PCI_ROM_REGION_INDEX,
|
||||||
VFIO_PCI_CONFIG_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
|
VFIO_PCI_NUM_REGIONS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue