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.
Unified · Split
| @@ -6,3 +6,13 @@ config VFIO_PCI · 5 unmodified lines | ||
| 6 | 6 | use of PCI drivers using the VFIO framework. |
| 7 | 7 | |
| 8 | 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. | |
| @@ -84,6 +84,11 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) · 83 unmodified lines | ||
| 84 | 84 | } else |
| 85 | 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 | 92 | return 0; |
| 88 | 93 | } |
| 89 | 94 | |
| @@ -285,6 +290,16 @@ static long vfio_pci_ioctl(void *device_data, · 195 unmodified lines | ||
| 285 | 290 | info.flags = VFIO_REGION_INFO_FLAG_READ; |
| 286 | 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 | 303 | default: |
| 289 | 304 | return -EINVAL; |
| 290 | 305 | } |
| @@ -386,6 +401,9 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf, · 95 unmodified lines | ||
| 386 | 401 | |
| 387 | 402 | case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: |
| 388 | 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 | 409 | return -EINVAL; |
| @@ -53,6 +53,7 @@ struct vfio_pci_device { · 52 unmodified lines | ||
| 53 | 53 | bool reset_works; |
| 54 | 54 | bool extended_caps; |
| 55 | 55 | bool bardirty; |
| 56 | + bool has_vga; | |
| 56 | 57 | struct pci_saved_state *pci_saved_state; |
| 57 | 58 | atomic_t refcnt; |
| 58 | 59 | }; |
| @@ -77,6 +78,9 @@ extern ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, · 18 unmodified lines | ||
| 77 | 78 | extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, |
| 78 | 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 | 84 | extern int vfio_pci_init_perm_bits(void); |
| 81 | 85 | extern void vfio_pci_uninit_perm_bits(void); |
| 82 | 86 | |
| @@ -17,6 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <linux/pci.h> |
| 18 | 18 | #include <linux/uaccess.h> |
| 19 | 19 | #include <linux/io.h> |
| 20 | +#include <linux/vgaarb.h> | |
| 20 | 21 | |
| 21 | 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, · 152 unmodified lines | ||
| 175 | 176 | |
| 176 | 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 | +} | |
| @@ -303,6 +303,15 @@ enum { · 302 unmodified lines | ||
| 303 | 303 | VFIO_PCI_BAR5_REGION_INDEX, |
| 304 | 304 | VFIO_PCI_ROM_REGION_INDEX, |
| 305 | 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 | 315 | VFIO_PCI_NUM_REGIONS |
| 307 | 316 | }; |
| 308 | 317 | |