mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/3] vfio-pci: VGA and legacy MMIO & I/O port access
@ 2013-01-15  3:38 Alex Williamson
  2013-01-15  3:38 ` [PATCH 1/3] vfio-pci: Cleanup read/write functions Alex Williamson
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Alex Williamson @ 2013-01-15  3:38 UTC (permalink / raw)
  To: alex.williamson; +Cc: aik, benh, linux-kernel, kvm, david

This series includes a couple cleanups that I'll target for 3.9 plus
RFC patch 3/3 that adds device regions for legacy I/O and makes use
of the VGA arbiter for enabling VGA assignment.  I know the POWER
guys don't likely care about VGA support, but Alexey, Ben, and David,
I'd appreciate any feedback on the overal incorporation into vfio
given your history with the design.  VGA is still a work in progress
but with this base I'm able to assign a handlful of desktop cards that
I have on hand to a qemu guest using standard builtin VGA drivers.
Thanks,

Alex

---

Alex Williamson (3):
      vfio-pci: Cleanup read/write functions
      vfio-pci: Cleanup BAR access
      vfio-pci: Add support for legacy MMIO & I/O port towards VGA support


 drivers/vfio/pci/Kconfig            |   10 +
 drivers/vfio/pci/vfio_pci.c         |  106 +++++++++----
 drivers/vfio/pci/vfio_pci_config.c  |    5 -
 drivers/vfio/pci/vfio_pci_private.h |   16 +-
 drivers/vfio/pci/vfio_pci_rdwr.c    |  282 +++++++++++++++++------------------
 include/uapi/linux/vfio.h           |    8 +
 6 files changed, 242 insertions(+), 185 deletions(-)

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] vfio-pci: Cleanup read/write functions
  2013-01-15  3:38 [PATCH 0/3] vfio-pci: VGA and legacy MMIO & I/O port access Alex Williamson
@ 2013-01-15  3:38 ` Alex Williamson
  2013-01-15  3:39 ` [PATCH 2/3] vfio-pci: Cleanup BAR access Alex Williamson
  2013-01-15  3:39 ` [RFC PATCH 3/3] vfio-pci: Add support for legacy MMIO & I/O port towards VGA support Alex Williamson
  2 siblings, 0 replies; 4+ messages in thread
From: Alex Williamson @ 2013-01-15  3:38 UTC (permalink / raw)
  To: alex.williamson; +Cc: aik, benh, linux-kernel, kvm, david

The read and write functions are nearly identical, combine them
and convert to a switch statement.  This also makes it easy to
narrow the scope of when we use the io/mem accessors in case new
regions are added.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 drivers/vfio/pci/vfio_pci.c |   59 +++++++++++++++++++++----------------------
 1 file changed, 29 insertions(+), 30 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 6c11994..27f7665 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -355,8 +355,8 @@ static long vfio_pci_ioctl(void *device_data,
 	return -ENOTTY;
 }
 
-static ssize_t vfio_pci_read(void *device_data, char __user *buf,
-			     size_t count, loff_t *ppos)
+static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
+			   size_t count, loff_t *ppos, bool iswrite)
 {
 	unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
 	struct vfio_pci_device *vdev = device_data;
@@ -365,42 +365,41 @@ static ssize_t vfio_pci_read(void *device_data, char __user *buf,
 	if (index >= VFIO_PCI_NUM_REGIONS)
 		return -EINVAL;
 
-	if (index == VFIO_PCI_CONFIG_REGION_INDEX)
-		return vfio_pci_config_readwrite(vdev, buf, count, ppos, false);
-	else if (index == VFIO_PCI_ROM_REGION_INDEX)
-		return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false);
-	else if (pci_resource_flags(pdev, index) & IORESOURCE_IO)
-		return vfio_pci_io_readwrite(vdev, buf, count, ppos, false);
-	else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM)
+	switch (index) {
+	case VFIO_PCI_CONFIG_REGION_INDEX:
+		return vfio_pci_config_readwrite(vdev, buf, count,
+						 ppos, iswrite);
+	case VFIO_PCI_ROM_REGION_INDEX:
+		if (iswrite)
+			return -EINVAL;
 		return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false);
 
+	case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
+	{
+		unsigned long flags = pci_resource_flags(pdev, index);
+
+		if (flags & IORESOURCE_IO)
+			return vfio_pci_io_readwrite(vdev, buf, count,
+						     ppos, iswrite);
+		if (flags & IORESOURCE_MEM)
+			return vfio_pci_mem_readwrite(vdev, buf, count,
+						      ppos, iswrite);
+	}
+	}
+
 	return -EINVAL;
 }
 
+static ssize_t vfio_pci_read(void *device_data, char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	return vfio_pci_rw(device_data, buf, count, ppos, false);
+}
+
 static ssize_t vfio_pci_write(void *device_data, const char __user *buf,
 			      size_t count, loff_t *ppos)
 {
-	unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
-	struct vfio_pci_device *vdev = device_data;
-	struct pci_dev *pdev = vdev->pdev;
-
-	if (index >= VFIO_PCI_NUM_REGIONS)
-		return -EINVAL;
-
-	if (index == VFIO_PCI_CONFIG_REGION_INDEX)
-		return vfio_pci_config_readwrite(vdev, (char __user *)buf,
-						 count, ppos, true);
-	else if (index == VFIO_PCI_ROM_REGION_INDEX)
-		return -EINVAL;
-	else if (pci_resource_flags(pdev, index) & IORESOURCE_IO)
-		return vfio_pci_io_readwrite(vdev, (char __user *)buf,
-					     count, ppos, true);
-	else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) {
-		return vfio_pci_mem_readwrite(vdev, (char __user *)buf,
-					      count, ppos, true);
-	}
-
-	return -EINVAL;
+	return vfio_pci_rw(device_data, buf, count, ppos, true);
 }
 
 static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/3] vfio-pci: Cleanup BAR access
  2013-01-15  3:38 [PATCH 0/3] vfio-pci: VGA and legacy MMIO & I/O port access Alex Williamson
  2013-01-15  3:38 ` [PATCH 1/3] vfio-pci: Cleanup read/write functions Alex Williamson
@ 2013-01-15  3:39 ` Alex Williamson
  2013-01-15  3:39 ` [RFC PATCH 3/3] vfio-pci: Add support for legacy MMIO & I/O port towards VGA support Alex Williamson
  2 siblings, 0 replies; 4+ messages in thread
From: Alex Williamson @ 2013-01-15  3:39 UTC (permalink / raw)
  To: alex.williamson; +Cc: aik, benh, linux-kernel, kvm, david

We can actually handle MMIO and I/O port from the same access function
since PCI already does abstraction of this.  The ROM BAR only requires
a minor difference, so it gets included too.  vfio_pci_config_readwrite
gets renamed for consistency.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 drivers/vfio/pci/vfio_pci.c         |   26 ++--
 drivers/vfio/pci/vfio_pci_config.c  |    5 -
 drivers/vfio/pci/vfio_pci_private.h |   15 +-
 drivers/vfio/pci/vfio_pci_rdwr.c    |  231 ++++++++++-------------------------
 4 files changed, 88 insertions(+), 189 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 27f7665..781f900 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -360,31 +360,21 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
 {
 	unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
 	struct vfio_pci_device *vdev = device_data;
-	struct pci_dev *pdev = vdev->pdev;
 
 	if (index >= VFIO_PCI_NUM_REGIONS)
 		return -EINVAL;
 
 	switch (index) {
 	case VFIO_PCI_CONFIG_REGION_INDEX:
-		return vfio_pci_config_readwrite(vdev, buf, count,
-						 ppos, iswrite);
+		return vfio_pci_config_rw(vdev, buf, count, ppos, iswrite);
+
 	case VFIO_PCI_ROM_REGION_INDEX:
 		if (iswrite)
 			return -EINVAL;
-		return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false);
+		return vfio_pci_bar_rw(vdev, buf, count, ppos, false);
 
 	case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
-	{
-		unsigned long flags = pci_resource_flags(pdev, index);
-
-		if (flags & IORESOURCE_IO)
-			return vfio_pci_io_readwrite(vdev, buf, count,
-						     ppos, iswrite);
-		if (flags & IORESOURCE_MEM)
-			return vfio_pci_mem_readwrite(vdev, buf, count,
-						      ppos, iswrite);
-	}
+		return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite);
 	}
 
 	return -EINVAL;
@@ -393,13 +383,19 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
 static ssize_t vfio_pci_read(void *device_data, char __user *buf,
 			     size_t count, loff_t *ppos)
 {
+	if (!count)
+		return 0;
+
 	return vfio_pci_rw(device_data, buf, count, ppos, false);
 }
 
 static ssize_t vfio_pci_write(void *device_data, const char __user *buf,
 			      size_t count, loff_t *ppos)
 {
-	return vfio_pci_rw(device_data, buf, count, ppos, true);
+	if (!count)
+		return 0;
+
+	return vfio_pci_rw(device_data, (char __user *)buf, count, ppos, true);
 }
 
 static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index 8b8f7d1..6985149 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -1501,9 +1501,8 @@ static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf,
 	return ret;
 }
 
-ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev,
-				  char __user *buf, size_t count,
-				  loff_t *ppos, bool iswrite)
+ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, char __user *buf,
+			   size_t count, loff_t *ppos, bool iswrite)
 {
 	size_t done = 0;
 	int ret = 0;
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
index 611827c..00d19b9 100644
--- a/drivers/vfio/pci/vfio_pci_private.h
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -70,15 +70,12 @@ extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev,
 				   uint32_t flags, unsigned index,
 				   unsigned start, unsigned count, void *data);
 
-extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev,
-					 char __user *buf, size_t count,
-					 loff_t *ppos, bool iswrite);
-extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev,
-				      char __user *buf, size_t count,
-				      loff_t *ppos, bool iswrite);
-extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev,
-				     char __user *buf, size_t count,
-				     loff_t *ppos, bool iswrite);
+extern ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev,
+				  char __user *buf, size_t count,
+				  loff_t *ppos, bool iswrite);
+
+extern ssize_t vfio_pci_bar_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 void vfio_pci_uninit_perm_bits(void);
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index f72323e..cdb13a9 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -20,250 +20,157 @@
 
 #include "vfio_pci_private.h"
 
-/* I/O Port BAR access */
-ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf,
-			      size_t count, loff_t *ppos, bool iswrite)
+/*
+ * Read or write from an __iomem region (MMIO or I/O port) with an excluded
+ * range which is inaccessible.  The excluded range drops writes and fills
+ * reads with -1.  This is intended for handling MSI-X vector tables and
+ * leftover space for ROM BARs.
+ */
+static size_t do_io_rw(void __iomem *io, char __user *buf, loff_t off,
+		       size_t count, size_t x_start, size_t x_end, bool iswrite)
 {
-	struct pci_dev *pdev = vdev->pdev;
-	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
-	int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
-	void __iomem *io;
 	size_t done = 0;
 
-	if (!pci_resource_start(pdev, bar))
-		return -EINVAL;
-
-	if (pos + count > pci_resource_len(pdev, bar))
-		return -EINVAL;
-
-	if (!vdev->barmap[bar]) {
-		int ret;
-
-		ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
-		if (ret)
-			return ret;
-
-		vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
-
-		if (!vdev->barmap[bar]) {
-			pci_release_selected_regions(pdev, 1 << bar);
-			return -EINVAL;
-		}
-	}
-
-	io = vdev->barmap[bar];
-
 	while (count) {
-		int filled;
+		size_t fillable, filled;
+
+		if (off < x_start)
+			fillable = min(count, (size_t)(x_start - off));
+		else if (off >= x_end)
+			fillable = count;
+		else
+			fillable = 0;
 
-		if (count >= 3 && !(pos % 4)) {
+		if (fillable >= 4 && !(off % 4)) {
 			__le32 val;
 
 			if (iswrite) {
 				if (copy_from_user(&val, buf, 4))
 					return -EFAULT;
 
-				iowrite32(le32_to_cpu(val), io + pos);
+				iowrite32(le32_to_cpu(val), io + off);
 			} else {
-				val = cpu_to_le32(ioread32(io + pos));
+				val = cpu_to_le32(ioread32(io + off));
 
 				if (copy_to_user(buf, &val, 4))
 					return -EFAULT;
 			}
 
 			filled = 4;
-
-		} else if ((pos % 2) == 0 && count >= 2) {
+		} else if (fillable >= 2 && !(off % 2)) {
 			__le16 val;
 
 			if (iswrite) {
 				if (copy_from_user(&val, buf, 2))
 					return -EFAULT;
 
-				iowrite16(le16_to_cpu(val), io + pos);
+				iowrite16(le16_to_cpu(val), io + off);
 			} else {
-				val = cpu_to_le16(ioread16(io + pos));
+				val = cpu_to_le16(ioread16(io + off));
 
 				if (copy_to_user(buf, &val, 2))
 					return -EFAULT;
 			}
 
 			filled = 2;
-		} else {
+		} else if (fillable) {
 			u8 val;
 
 			if (iswrite) {
 				if (copy_from_user(&val, buf, 1))
 					return -EFAULT;
 
-				iowrite8(val, io + pos);
+				iowrite8(val, io + off);
 			} else {
-				val = ioread8(io + pos);
+				val = ioread8(io + off);
 
 				if (copy_to_user(buf, &val, 1))
 					return -EFAULT;
 			}
 
 			filled = 1;
+		} else {
+			/* Fill reads with -1, drop writes */
+			filled = min(count, (size_t)(x_end - off));
+			if (!iswrite) {
+				u8 val = 0xFF;
+				size_t i;
+
+				for (i = 0; i < filled; i++)
+					if (copy_to_user(buf + i, &val, 1))
+						return -EFAULT;
+			}
 		}
 
 		count -= filled;
 		done += filled;
+		off += filled;
 		buf += filled;
-		pos += filled;
 	}
 
-	*ppos += done;
-
 	return done;
 }
 
-/*
- * MMIO BAR access
- * We handle two excluded ranges here as well, if the user tries to read
- * the ROM beyond what PCI tells us is available or the MSI-X table region,
- * we return 0xFF and writes are dropped.
- */
-ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf,
-			       size_t count, loff_t *ppos, bool iswrite)
+ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
+			size_t count, loff_t *ppos, bool iswrite)
 {
 	struct pci_dev *pdev = vdev->pdev;
 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
 	int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
-	void __iomem *io;
+	size_t x_start = 0, x_end = 0;
 	resource_size_t end;
-	size_t done = 0;
-	size_t x_start = 0, x_end = 0; /* excluded range */
+	void __iomem *io;
+	size_t done;
 
 	if (!pci_resource_start(pdev, bar))
 		return -EINVAL;
 
 	end = pci_resource_len(pdev, bar);
 
-	if (pos > end)
+	if (pos >= end)
 		return -EINVAL;
 
-	if (pos == end)
-		return 0;
-
-	if (pos + count > end)
-		count = end - pos;
+	count = min(count, (size_t)(end - pos));
 
 	if (bar == PCI_ROM_RESOURCE) {
+		/*
+		 * The ROM can fill less space than the BAR, so we start the
+		 * excluded range at the end of the actual ROM.  This makes
+		 * filling large ROM BARs much faster.
+		 */
 		io = pci_map_rom(pdev, &x_start);
+		if (!io)
+			return -ENOMEM;
 		x_end = end;
-	} else {
-		if (!vdev->barmap[bar]) {
-			int ret;
-
-			ret = pci_request_selected_regions(pdev, 1 << bar,
-							   "vfio");
-			if (ret)
-				return ret;
+	} else if (!vdev->barmap[bar]) {
+		int ret;
 
-			vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
+		ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
+		if (ret)
+			return ret;
 
-			if (!vdev->barmap[bar]) {
-				pci_release_selected_regions(pdev, 1 << bar);
-				return -EINVAL;
-			}
+		io = pci_iomap(pdev, bar, 0);
+		if (!io) {
+			pci_release_selected_regions(pdev, 1 << bar);
+			return -ENOMEM;
 		}
 
+		vdev->barmap[bar] = io;
+	} else
 		io = vdev->barmap[bar];
 
-		if (bar == vdev->msix_bar) {
-			x_start = vdev->msix_offset;
-			x_end = vdev->msix_offset + vdev->msix_size;
-		}
+	if (bar == vdev->msix_bar) {
+		x_start = vdev->msix_offset;
+		x_end = vdev->msix_offset + vdev->msix_size;
 	}
 
-	if (!io)
-		return -EINVAL;
+	done = do_io_rw(io, buf, pos, count, x_start, x_end, iswrite);
 
-	while (count) {
-		size_t fillable, filled;
+	if (done >= 0)
+		*ppos += done;
 
-		if (pos < x_start)
-			fillable = x_start - pos;
-		else if (pos >= x_end)
-			fillable = end - pos;
-		else
-			fillable = 0;
-
-		if (fillable >= 4 && !(pos % 4) && (count >= 4)) {
-			__le32 val;
-
-			if (iswrite) {
-				if (copy_from_user(&val, buf, 4))
-					goto out;
-
-				iowrite32(le32_to_cpu(val), io + pos);
-			} else {
-				val = cpu_to_le32(ioread32(io + pos));
-
-				if (copy_to_user(buf, &val, 4))
-					goto out;
-			}
-
-			filled = 4;
-		} else if (fillable >= 2 && !(pos % 2) && (count >= 2)) {
-			__le16 val;
-
-			if (iswrite) {
-				if (copy_from_user(&val, buf, 2))
-					goto out;
-
-				iowrite16(le16_to_cpu(val), io + pos);
-			} else {
-				val = cpu_to_le16(ioread16(io + pos));
-
-				if (copy_to_user(buf, &val, 2))
-					goto out;
-			}
-
-			filled = 2;
-		} else if (fillable) {
-			u8 val;
-
-			if (iswrite) {
-				if (copy_from_user(&val, buf, 1))
-					goto out;
-
-				iowrite8(val, io + pos);
-			} else {
-				val = ioread8(io + pos);
-
-				if (copy_to_user(buf, &val, 1))
-					goto out;
-			}
-
-			filled = 1;
-		} else {
-			/* Drop writes, fill reads with FF */
-			filled = min((size_t)(x_end - pos), count);
-			if (!iswrite) {
-				char val = 0xFF;
-				size_t i;
-
-				for (i = 0; i < filled; i++) {
-					if (put_user(val, buf + i))
-						goto out;
-				}
-			}
-
-		}
-
-		count -= filled;
-		done += filled;
-		buf += filled;
-		pos += filled;
-	}
-
-	*ppos += done;
-
-out:
 	if (bar == PCI_ROM_RESOURCE)
 		pci_unmap_rom(pdev, io);
 
-	return count ? -EFAULT : done;
+	return done;
 }


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [RFC PATCH 3/3] vfio-pci: Add support for legacy MMIO & I/O port towards VGA support
  2013-01-15  3:38 [PATCH 0/3] vfio-pci: VGA and legacy MMIO & I/O port access Alex Williamson
  2013-01-15  3:38 ` [PATCH 1/3] vfio-pci: Cleanup read/write functions Alex Williamson
  2013-01-15  3:39 ` [PATCH 2/3] vfio-pci: Cleanup BAR access Alex Williamson
@ 2013-01-15  3:39 ` Alex Williamson
  2 siblings, 0 replies; 4+ messages in thread
From: Alex Williamson @ 2013-01-15  3:39 UTC (permalink / raw)
  To: alex.williamson; +Cc: aik, benh, linux-kernel, kvm, david

Create two new legacy regions, one for MMIO space below 1MB and
another for 64k of I/O port space.  For devices of PCI class VGA
these ranges will be exposed and allow direct access to the device
at the PCI defined VGA addresses, 0xa0000, 0x3b0, 0x3c0.  VFIO
makes use of the host VGA arbiter to manage host chipset config
to route each access to the correct device.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 drivers/vfio/pci/Kconfig            |   10 ++++
 drivers/vfio/pci/vfio_pci.c         |   49 ++++++++++++++++++++
 drivers/vfio/pci/vfio_pci_private.h |    9 ++++
 drivers/vfio/pci/vfio_pci_rdwr.c    |   85 +++++++++++++++++++++++++++++++++++
 include/uapi/linux/vfio.h           |    8 +++
 5 files changed, 160 insertions(+), 1 deletion(-)

diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig
index 5980758..559d807 100644
--- a/drivers/vfio/pci/Kconfig
+++ b/drivers/vfio/pci/Kconfig
@@ -6,3 +6,13 @@ config VFIO_PCI
 	  use of PCI drivers using the VFIO framework.
 
 	  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 extensions to VFIO PCI.  This exposes additional
+	  regions 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.
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 781f900..61346bc 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -81,6 +81,11 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
 	if (ret)
 		goto out;
 
+#ifdef CONFIG_VFIO_PCI_VGA
+	if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
+		vdev->has_vga = true;
+#endif
+
 	return ret;
 
 out:
@@ -193,6 +198,7 @@ static long vfio_pci_ioctl(void *device_data,
 
 	if (cmd == VFIO_DEVICE_GET_INFO) {
 		struct vfio_device_info info;
+		bool legacy_io = false, legacy_mem = false;
 
 		minsz = offsetofend(struct vfio_device_info, num_irqs);
 
@@ -207,9 +213,24 @@ static long vfio_pci_ioctl(void *device_data,
 		if (vdev->reset_works)
 			info.flags |= VFIO_DEVICE_FLAGS_RESET;
 
-		info.num_regions = VFIO_PCI_NUM_REGIONS;
+		info.num_regions = VFIO_PCI_CONFIG_REGION_INDEX + 1;
 		info.num_irqs = VFIO_PCI_NUM_IRQS;
 
+		if (vdev->has_vga) {
+			info.flags |= VFIO_DEVICE_FLAGS_PCI_VGA;
+			legacy_io = legacy_mem = true;
+		}
+
+		if (legacy_io) {
+			info.flags |= VFIO_DEVICE_FLAGS_PCI_LEGACY_IOPORT;
+			info.num_regions++;
+		}
+
+		if (legacy_mem) {
+			info.flags |= VFIO_DEVICE_FLAGS_PCI_LEGACY_MMIO;
+			info.num_regions++;
+		}
+
 		return copy_to_user((void __user *)arg, &info, minsz);
 
 	} else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
@@ -269,6 +290,26 @@ static long vfio_pci_ioctl(void *device_data,
 			info.flags = VFIO_REGION_INFO_FLAG_READ;
 			break;
 		}
+		case VFIO_PCI_LEGACY_MMIO_REGION_INDEX:
+			if (!vdev->has_vga)
+				return -EINVAL;
+
+			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+			info.size = 1024 * 1024;
+			info.flags = VFIO_REGION_INFO_FLAG_READ |
+				     VFIO_REGION_INFO_FLAG_WRITE;
+
+			break;
+		case VFIO_PCI_LEGACY_IOPORT_REGION_INDEX:
+			if (!vdev->has_vga)
+				return -EINVAL;
+
+			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+			info.size = 64 * 1024;
+			info.flags = VFIO_REGION_INFO_FLAG_READ |
+				     VFIO_REGION_INFO_FLAG_WRITE;
+
+			break;
 		default:
 			return -EINVAL;
 		}
@@ -375,6 +416,12 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
 
 	case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
 		return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite);
+
+	case VFIO_PCI_LEGACY_MMIO_REGION_INDEX:
+		return vfio_pci_legacy_mem_rw(vdev, buf, count, ppos, iswrite);
+
+	case VFIO_PCI_LEGACY_IOPORT_REGION_INDEX:
+		return vfio_pci_legacy_io_rw(vdev, buf, count, ppos, iswrite);
 	}
 
 	return -EINVAL;
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
index 00d19b9..453ea69 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 {
 	bool			reset_works;
 	bool			extended_caps;
 	bool			bardirty;
+	bool			has_vga;
 	struct pci_saved_state	*pci_saved_state;
 	atomic_t		refcnt;
 };
@@ -77,6 +78,14 @@ 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,
 			       size_t count, loff_t *ppos, bool iswrite);
 
+extern ssize_t vfio_pci_legacy_mem_rw(struct vfio_pci_device *vdev,
+				      char __user *buf, size_t count,
+				      loff_t *ppos, bool iswrite);
+
+extern ssize_t vfio_pci_legacy_io_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 void vfio_pci_uninit_perm_bits(void);
 
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index cdb13a9..a24ad94 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -17,6 +17,7 @@
 #include <linux/pci.h>
 #include <linux/uaccess.h>
 #include <linux/io.h>
+#include <linux/vgaarb.h>
 
 #include "vfio_pci_private.h"
 
@@ -174,3 +175,87 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
 
 	return done;
 }
+
+ssize_t vfio_pci_legacy_mem_rw(struct vfio_pci_device *vdev, char __user *buf,
+			       size_t count, loff_t *ppos, bool iswrite)
+{
+	int ret;
+	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
+
+	if (vdev->has_vga && pos >= 0xa0000 && pos < 0xc0000) {
+		void __iomem *mem;
+		size_t done;
+
+		count = min(count, (size_t)(0xc0000 - pos));
+
+		mem = ioremap_nocache(0xa0000, 0xc0000 - 0xa0000);
+		if (!mem)
+			return -ENOMEM;
+
+		ret = vga_get_interruptible(vdev->pdev, VGA_RSRC_LEGACY_MEM);
+		if (ret) {
+			iounmap(mem);
+			return ret;
+		}
+
+		done = do_io_rw(mem, buf, pos - 0xa0000, count, 0, 0, iswrite);
+
+		vga_put(vdev->pdev, VGA_RSRC_LEGACY_MEM);
+		iounmap(mem);
+
+		if (done >= 0)
+			*ppos += done;
+
+		return done;
+	}
+
+	return -EINVAL;
+}
+
+ssize_t vfio_pci_legacy_io_rw(struct vfio_pci_device *vdev, char __user *buf,
+			      size_t count, loff_t *ppos, bool iswrite)
+{
+	int ret;
+	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
+
+	if (vdev->has_vga &&
+	    ((pos >= 0x3b0 && pos < 0x3bc) || (pos >= 0x3c0 && pos < 0x3e0))) {
+		void __iomem *io = NULL;
+		loff_t off;
+		size_t done;
+
+		switch (pos) {
+		case 0x3b0 ... 0x3bb:
+			count = min(count, (size_t)(0x3bc - pos));
+			io = ioport_map(0x3b0, 0x3bc - 0x3b0);
+			off = pos - 0x3b0;
+			break;
+		case 0x3c0 ... 0x3df:
+			count = min(count, (size_t)(0x3e0 - pos));
+			io = ioport_map(0x3c0, 0x3e0 - 0x3c0);
+			off = pos - 0x3c0;
+			break;
+		}
+
+		if (!io)
+			return -ENOMEM;
+
+		ret = vga_get_interruptible(vdev->pdev, VGA_RSRC_LEGACY_IO);
+		if (ret) {
+			ioport_unmap(io);
+			return ret;
+		}
+
+		done = do_io_rw(io, buf, off, count, 0, 0, iswrite);
+
+		vga_put(vdev->pdev, VGA_RSRC_LEGACY_IO);
+		ioport_unmap(io);
+
+		if (done >= 0)
+			*ppos += done;
+
+		return done;
+	}
+
+	return -EINVAL;
+}
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 4758d1b..19a90c0 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -147,6 +147,12 @@ struct vfio_device_info {
 	__u32	flags;
 #define VFIO_DEVICE_FLAGS_RESET	(1 << 0)	/* Device supports reset */
 #define VFIO_DEVICE_FLAGS_PCI	(1 << 1)	/* vfio-pci device */
+/* Legacy MMIO 0x0 - 0xfffff via VFIO_PCI_LEGACY_MMIO_REGION_INDEX */
+#define VFIO_DEVICE_FLAGS_PCI_LEGACY_MMIO	(1 << 2)
+/* Legacy I/O Port 0x0 - 0xffff via VFIO_PCI_LEGACY_IOPORT_REGION_INDEX */
+#define VFIO_DEVICE_FLAGS_PCI_LEGACY_IOPORT	(1 << 3)
+/* Supports VGA regions 0xa0000-0xbffff, 0x3b0-0x3bb, and 0x3c0-0x3df */
+#define VFIO_DEVICE_FLAGS_PCI_VGA		(1 << 4)
 	__u32	num_regions;	/* Max region index + 1 */
 	__u32	num_irqs;	/* Max IRQ index + 1 */
 };
@@ -303,6 +309,8 @@ enum {
 	VFIO_PCI_BAR5_REGION_INDEX,
 	VFIO_PCI_ROM_REGION_INDEX,
 	VFIO_PCI_CONFIG_REGION_INDEX,
+	VFIO_PCI_LEGACY_MMIO_REGION_INDEX,
+	VFIO_PCI_LEGACY_IOPORT_REGION_INDEX,
 	VFIO_PCI_NUM_REGIONS
 };
 


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-01-15  4:08 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-15  3:38 [PATCH 0/3] vfio-pci: VGA and legacy MMIO & I/O port access Alex Williamson
2013-01-15  3:38 ` [PATCH 1/3] vfio-pci: Cleanup read/write functions Alex Williamson
2013-01-15  3:39 ` [PATCH 2/3] vfio-pci: Cleanup BAR access Alex Williamson
2013-01-15  3:39 ` [RFC PATCH 3/3] vfio-pci: Add support for legacy MMIO & I/O port towards VGA support Alex Williamson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®