diff options
| author | Alex Williamson <alex.williamson@redhat.com> | 2013-02-18 12:11:13 -0500 |
|---|---|---|
| committer | Alex Williamson <alex.williamson@redhat.com> | 2013-02-18 12:11:13 -0500 |
| commit | 84237a826b261de7ddd3d09ee53ee68cb4138937 (patch) | |
| tree | ab0c6d1d07abd74e90db41521258615fc1eaa211 | |
| parent | 2dd1194833de133960f286903ce704cb10fa7eb0 (diff) | |
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>
| -rw-r--r-- | drivers/vfio/pci/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/vfio/pci/vfio_pci.c | 18 | ||||
| -rw-r--r-- | drivers/vfio/pci/vfio_pci_private.h | 4 | ||||
| -rw-r--r-- | drivers/vfio/pci/vfio_pci_rdwr.c | 61 | ||||
| -rw-r--r-- | include/uapi/linux/vfio.h | 9 |
5 files changed, 102 insertions, 0 deletions
diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index 5980758563eb..e84300b268b6 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig | |||
| @@ -6,3 +6,13 @@ config VFIO_PCI | |||
| 6 | use of PCI drivers using the VFIO framework. | 6 | use of PCI drivers using the VFIO framework. |
| 7 | 7 | ||
| 8 | If you don't know what to do here, say N. | 8 | If you don't know what to do here, say N. |
| 9 | |||
| 10 | config VFIO_PCI_VGA | ||
| 11 | bool "VFIO PCI support for VGA devices" | ||
| 12 | depends on VFIO_PCI && X86 && VGA_ARB && EXPERIMENTAL | ||
| 13 | help | ||
| 14 | Support for VGA extension to VFIO PCI. This exposes an additional | ||
| 15 | region on VGA devices for accessing legacy VGA addresses used by | ||
| 16 | BIOS and generic video drivers. | ||
| 17 | |||
| 18 | If you don't know what to do here, say N. | ||
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index bb8c8c2be960..8189cb6a86af 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c | |||
| @@ -84,6 +84,11 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) | |||
| 84 | } else | 84 | } else |
| 85 | vdev->msix_bar = 0xFF; | 85 | vdev->msix_bar = 0xFF; |
| 86 | 86 | ||
| 87 | #ifdef CONFIG_VFIO_PCI_VGA | ||
| 88 | if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) | ||
| 89 | vdev->has_vga = true; | ||
| 90 | #endif | ||
| 91 | |||
| 87 | return 0; | 92 | return 0; |
| 88 | } | 93 | } |
| 89 | 94 | ||
| @@ -285,6 +290,16 @@ static long vfio_pci_ioctl(void *device_data, | |||
| 285 | info.flags = VFIO_REGION_INFO_FLAG_READ; | 290 | info.flags = VFIO_REGION_INFO_FLAG_READ; |
| 286 | break; | 291 | break; |
| 287 | } | 292 | } |
| 293 | case VFIO_PCI_VGA_REGION_INDEX: | ||
| 294 | if (!vdev->has_vga) | ||
| 295 | return -EINVAL; | ||
| 296 | |||
| 297 | info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); | ||
| 298 | info.size = 0xc0000; | ||
| 299 | info.flags = VFIO_REGION_INFO_FLAG_READ | | ||
| 300 | VFIO_REGION_INFO_FLAG_WRITE; | ||
| 301 | |||
| 302 | break; | ||
| 288 | default: | 303 | default: |
| 289 | return -EINVAL; | 304 | return -EINVAL; |
| 290 | } | 305 | } |
| @@ -386,6 +401,9 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf, | |||
| 386 | 401 | ||
| 387 | case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: | 402 | case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: |
| 388 | return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite); | 403 | return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite); |
| 404 | |||
| 405 | case VFIO_PCI_VGA_REGION_INDEX: | ||
| 406 | return vfio_pci_vga_rw(vdev, buf, count, ppos, iswrite); | ||
| 389 | } | 407 | } |
| 390 | 408 | ||
| 391 | return -EINVAL; | 409 | return -EINVAL; |
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index 00d19b953ce4..d7e55d03f49e 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h | |||
| @@ -53,6 +53,7 @@ struct vfio_pci_device { | |||
| 53 | bool reset_works; | 53 | bool reset_works; |
| 54 | bool extended_caps; | 54 | bool extended_caps; |
| 55 | bool bardirty; | 55 | bool bardirty; |
| 56 | bool has_vga; | ||
| 56 | struct pci_saved_state *pci_saved_state; | 57 | struct pci_saved_state *pci_saved_state; |
| 57 | atomic_t refcnt; | 58 | atomic_t refcnt; |
| 58 | }; | 59 | }; |
| @@ -77,6 +78,9 @@ extern ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, | |||
| 77 | extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, | 78 | extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, |
| 78 | size_t count, loff_t *ppos, bool iswrite); | 79 | size_t count, loff_t *ppos, bool iswrite); |
| 79 | 80 | ||
| 81 | extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, | ||
| 82 | size_t count, loff_t *ppos, bool iswrite); | ||
| 83 | |||
| 80 | extern int vfio_pci_init_perm_bits(void); | 84 | extern int vfio_pci_init_perm_bits(void); |
| 81 | extern void vfio_pci_uninit_perm_bits(void); | 85 | extern void vfio_pci_uninit_perm_bits(void); |
| 82 | 86 | ||
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c index e9d78eb91ed7..210db24d2204 100644 --- a/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/drivers/vfio/pci/vfio_pci_rdwr.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/pci.h> | 17 | #include <linux/pci.h> |
| 18 | #include <linux/uaccess.h> | 18 | #include <linux/uaccess.h> |
| 19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
| 20 | #include <linux/vgaarb.h> | ||
| 20 | 21 | ||
| 21 | #include "vfio_pci_private.h" | 22 | #include "vfio_pci_private.h" |
| 22 | 23 | ||
| @@ -175,3 +176,63 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, | |||
| 175 | 176 | ||
| 176 | return done; | 177 | return done; |
| 177 | } | 178 | } |
| 179 | |||
| 180 | ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, | ||
| 181 | size_t count, loff_t *ppos, bool iswrite) | ||
| 182 | { | ||
| 183 | int ret; | ||
| 184 | loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK; | ||
| 185 | void __iomem *iomem = NULL; | ||
| 186 | unsigned int rsrc; | ||
| 187 | bool is_ioport; | ||
| 188 | ssize_t done; | ||
| 189 | |||
| 190 | if (!vdev->has_vga) | ||
| 191 | return -EINVAL; | ||
| 192 | |||
| 193 | switch (pos) { | ||
| 194 | case 0xa0000 ... 0xbffff: | ||
| 195 | count = min(count, (size_t)(0xc0000 - pos)); | ||
| 196 | iomem = ioremap_nocache(0xa0000, 0xbffff - 0xa0000 + 1); | ||
| 197 | off = pos - 0xa0000; | ||
| 198 | rsrc = VGA_RSRC_LEGACY_MEM; | ||
| 199 | is_ioport = false; | ||
| 200 | break; | ||
| 201 | case 0x3b0 ... 0x3bb: | ||
| 202 | count = min(count, (size_t)(0x3bc - pos)); | ||
| 203 | iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1); | ||
| 204 | off = pos - 0x3b0; | ||
| 205 | rsrc = VGA_RSRC_LEGACY_IO; | ||
| 206 | is_ioport = true; | ||
| 207 | break; | ||
| 208 | case 0x3c0 ... 0x3df: | ||
| 209 | count = min(count, (size_t)(0x3e0 - pos)); | ||
| 210 | iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1); | ||
| 211 | off = pos - 0x3c0; | ||
| 212 | rsrc = VGA_RSRC_LEGACY_IO; | ||
| 213 | is_ioport = true; | ||
| 214 | break; | ||
| 215 | default: | ||
| 216 | return -EINVAL; | ||
| 217 | } | ||
| 218 | |||
| 219 | if (!iomem) | ||
| 220 | return -ENOMEM; | ||
| 221 | |||
| 222 | ret = vga_get_interruptible(vdev->pdev, rsrc); | ||
| 223 | if (ret) { | ||
| 224 | is_ioport ? ioport_unmap(iomem) : iounmap(iomem); | ||
| 225 | return ret; | ||
| 226 | } | ||
| 227 | |||
| 228 | done = do_io_rw(iomem, buf, off, count, 0, 0, iswrite); | ||
| 229 | |||
| 230 | vga_put(vdev->pdev, rsrc); | ||
| 231 | |||
| 232 | is_ioport ? ioport_unmap(iomem) : iounmap(iomem); | ||
| 233 | |||
| 234 | if (done >= 0) | ||
| 235 | *ppos += done; | ||
| 236 | |||
| 237 | return done; | ||
| 238 | } | ||
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 4758d1bfcf41..4f41f309911e 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h | |||
| @@ -303,6 +303,15 @@ enum { | |||
| 303 | VFIO_PCI_BAR5_REGION_INDEX, | 303 | VFIO_PCI_BAR5_REGION_INDEX, |
| 304 | VFIO_PCI_ROM_REGION_INDEX, | 304 | VFIO_PCI_ROM_REGION_INDEX, |
| 305 | VFIO_PCI_CONFIG_REGION_INDEX, | 305 | VFIO_PCI_CONFIG_REGION_INDEX, |
| 306 | /* | ||
| 307 | * Expose VGA regions defined for PCI base class 03, subclass 00. | ||
| 308 | * This includes I/O port ranges 0x3b0 to 0x3bb and 0x3c0 to 0x3df | ||
| 309 | * as well as the MMIO range 0xa0000 to 0xbffff. Each implemented | ||
| 310 | * range is found at it's identity mapped offset from the region | ||
| 311 | * offset, for example 0x3b0 is region_info.offset + 0x3b0. Areas | ||
| 312 | * between described ranges are unimplemented. | ||
| 313 | */ | ||
| 314 | VFIO_PCI_VGA_REGION_INDEX, | ||
| 306 | VFIO_PCI_NUM_REGIONS | 315 | VFIO_PCI_NUM_REGIONS |
| 307 | }; | 316 | }; |
| 308 | |||
