aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/vfio
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2013-02-18 12:11:13 -0500
committerAlex Williamson <alex.williamson@redhat.com>2013-02-18 12:11:13 -0500
commit84237a826b261de7ddd3d09ee53ee68cb4138937 (patch)
treeab0c6d1d07abd74e90db41521258615fc1eaa211 /drivers/vfio
parent2dd1194833de133960f286903ce704cb10fa7eb0 (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>
Diffstat (limited to 'drivers/vfio')
-rw-r--r--drivers/vfio/pci/Kconfig10
-rw-r--r--drivers/vfio/pci/vfio_pci.c18
-rw-r--r--drivers/vfio/pci/vfio_pci_private.h4
-rw-r--r--drivers/vfio/pci/vfio_pci_rdwr.c61
4 files changed, 93 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
10config 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,
77extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, 78extern 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
81extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf,
82 size_t count, loff_t *ppos, bool iswrite);
83
80extern int vfio_pci_init_perm_bits(void); 84extern int vfio_pci_init_perm_bits(void);
81extern void vfio_pci_uninit_perm_bits(void); 85extern 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
180ssize_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}