diff options
-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 | 317 | ||