diff mbox

[1/2] vfio: Fix config space virtualization

Message ID 20101105173021.1638.59462.stgit@s20.home (mailing list archive)
State New, archived
Headers show

Commit Message

Alex Williamson Nov. 5, 2010, 5:30 p.m. UTC
None
diff mbox

Patch

different that unwritable bits.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---

 drivers/vfio/vfio_pci_config.c |  118 ++++++++++------------------------------
 1 files changed, 29 insertions(+), 89 deletions(-)

diff --git a/drivers/vfio/vfio_pci_config.c b/drivers/vfio/vfio_pci_config.c
index 8304316..bb38dbe 100644
--- a/drivers/vfio/vfio_pci_config.c
+++ b/drivers/vfio/vfio_pci_config.c
@@ -820,21 +820,10 @@  static inline int vfio_write_config_byte(struct vfio_dev *vdev,
 }
 
 /* handle virtualized fields in the basic config space */
-static u8 vfio_virt_basic(struct vfio_dev *vdev, int write,
+static void vfio_virt_basic(struct vfio_dev *vdev, int write,
 				u16 pos, u16 off, u8 val, u8 newval)
 {
-	switch (off) {
-	/*
-	 * vendor and device are virt because they don't
-	 * show up otherwise for sr-iov vfs
-	 */
-	case PCI_VENDOR_ID:
-	case PCI_VENDOR_ID + 1:
-	case PCI_DEVICE_ID:
-	case PCI_DEVICE_ID + 1:
-		/* read only */
-		val = vdev->vconfig[pos];
-		break;
+	switch (pos) {
 	case PCI_COMMAND:
 		/*
 		 * If the real mem or IO enable bits are zero
@@ -842,11 +831,15 @@  static u8 vfio_virt_basic(struct vfio_dev *vdev, int write,
 		 * Restore the real BARs before allowing those
 		 * bits to re-enable
 		 */
-		if (vdev->pdev->is_virtfn)
-			val |= PCI_COMMAND_MEMORY;
 		if (write) {
 			int upd = 0;
 
+			if (vfio_read_config_byte(vdev, pos, &val) < 0)
+				return;
+
+			if (vdev->pdev->is_virtfn)
+				val |= PCI_COMMAND_MEMORY;
+
 			upd = (newval & PCI_COMMAND_MEMORY) >
 			      (val & PCI_COMMAND_MEMORY);
 			upd += (newval & PCI_COMMAND_IO) >
@@ -856,61 +849,26 @@  static u8 vfio_virt_basic(struct vfio_dev *vdev, int write,
 			vfio_write_config_byte(vdev, pos, newval);
 		}
 		break;
-	case PCI_BASE_ADDRESS_0:
-	case PCI_BASE_ADDRESS_0+1:
-	case PCI_BASE_ADDRESS_0+2:
-	case PCI_BASE_ADDRESS_0+3:
-	case PCI_BASE_ADDRESS_1:
-	case PCI_BASE_ADDRESS_1+1:
-	case PCI_BASE_ADDRESS_1+2:
-	case PCI_BASE_ADDRESS_1+3:
-	case PCI_BASE_ADDRESS_2:
-	case PCI_BASE_ADDRESS_2+1:
-	case PCI_BASE_ADDRESS_2+2:
-	case PCI_BASE_ADDRESS_2+3:
-	case PCI_BASE_ADDRESS_3:
-	case PCI_BASE_ADDRESS_3+1:
-	case PCI_BASE_ADDRESS_3+2:
-	case PCI_BASE_ADDRESS_3+3:
-	case PCI_BASE_ADDRESS_4:
-	case PCI_BASE_ADDRESS_4+1:
-	case PCI_BASE_ADDRESS_4+2:
-	case PCI_BASE_ADDRESS_4+3:
-	case PCI_BASE_ADDRESS_5:
-	case PCI_BASE_ADDRESS_5+1:
-	case PCI_BASE_ADDRESS_5+2:
-	case PCI_BASE_ADDRESS_5+3:
-	case PCI_ROM_ADDRESS:
-	case PCI_ROM_ADDRESS+1:
-	case PCI_ROM_ADDRESS+2:
-	case PCI_ROM_ADDRESS+3:
+	case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_5 + 3:
+	case PCI_ROM_ADDRESS ... PCI_ROM_ADDRESS + 3:
 		if (write) {
-			vdev->vconfig[pos] = newval;
 			vdev->bardirty = 1;
 		} else {
 			if (vdev->bardirty)
 				vfio_bar_fixup(vdev);
-			val = vdev->vconfig[pos];
 		}
 		break;
-	default:
-		if (write)
-			vdev->vconfig[pos] = newval;
-		else
-			val = vdev->vconfig[pos];
-		break;
 	}
-	return val;
 }
 
 /*
  * handle virtualized fields in msi capability
  * easy, except for multiple-msi fields in flags byte
  */
-static u8 vfio_virt_msi(struct vfio_dev *vdev, int write,
+static void vfio_virt_msi(struct vfio_dev *vdev, int write,
 				u16 pos, u16 off, u8 val, u8 newval)
 {
-	if (off == PCI_MSI_FLAGS) {
+	if (pos == PCI_MSI_FLAGS) {
 		u8 num;
 
 		if (write) {
@@ -924,18 +882,12 @@  static u8 vfio_virt_msi(struct vfio_dev *vdev, int write,
 			vfio_write_config_byte(vdev, pos, newval);
 		} else {
 			if (vfio_read_config_byte(vdev, pos, &val) < 0)
-				return 0;
+				return;
 			val &= ~PCI_MSI_FLAGS_QMASK;
 			val |= vdev->msi_qmax << 1;
+			vdev->vconfig[pos] = val;
 		}
-		return val;
 	}
-
-	if (write)
-		vdev->vconfig[pos] = newval;
-	else
-		val = vdev->vconfig[pos];
-	return val;
 }
 
 static int vfio_config_rwbyte(int write,
@@ -1014,25 +966,25 @@  static int vfio_config_rwbyte(int write,
 		return 0;
 	}
 
+	val = vdev->vconfig[pos];
 	if (write) {
+		u8 pval, rbits = (~virt) & wr;
+
 		if (copy_from_user(&newval, buf, 1))
 			return -EFAULT;
-	}
-	/*
-	 * We get here if there are some virt bits
-	 * handle remaining real bits, if any
-	 */
-	if (~virt) {
-		u8 rbits = (~virt) & wr;
 
-		ret = vfio_read_config_byte(vdev, pos, &val);
+		ret = vfio_read_config_byte(vdev, pos, &pval);
 		if (ret < 0)
 			return ret;
-		if (write && rbits) {
-			val &= ~rbits;
-			val |= (newval & rbits);
-			vfio_write_config_byte(vdev, pos, val);
+
+		if (rbits) {
+			pval &= ~rbits;
+			pval |= (newval & rbits);
+			pci_user_write_config_byte(vdev->pdev, pos, pval);
 		}
+		vdev->vconfig[pos] = (pval & ~(virt | wr)) |
+				     (val & (virt & ~wr)) |
+				     (newval & wr);
 	}
 	/*
 	 * Now handle entirely virtual fields
@@ -1040,32 +992,20 @@  static int vfio_config_rwbyte(int write,
 	if (pos < PCI_CFG_SPACE_SIZE) {
 		switch (cap) {
 		case PCI_CAP_ID_BASIC:	/* virtualize BARs */
-			val = vfio_virt_basic(vdev, write,
-						pos, off, val, newval);
+			vfio_virt_basic(vdev, write, pos, off, val, newval);
 			break;
 		case PCI_CAP_ID_MSI:	/* virtualize (parts of) MSI */
-			val = vfio_virt_msi(vdev, write,
-						pos, off, val, newval);
-			break;
-		default:
-			if (write)
-				vdev->vconfig[pos] = newval;
-			else
-				val = vdev->vconfig[pos];
+			vfio_virt_msi(vdev, write, pos, off, val, newval);
 			break;
 		}
 	} else {
 		/* no virt fields yet in ecaps */
 		switch (cap) {	/* extended capabilities */
 		default:
-			if (write)
-				vdev->vconfig[pos] = newval;
-			else
-				val = vdev->vconfig[pos];
 			break;
 		}
 	}
-	if (!write && copy_to_user(buf, &val, 1))
+	if (!write && copy_to_user(buf, &vdev->vconfig[pos], 1))
 		return -EFAULT;
 	return 0;
 }