These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / vfio / pci / vfio_pci_config.c
index ff75ca3..fe2b470 100644 (file)
@@ -46,7 +46,7 @@
  *   0: Removed from the user visible capability list
  *   FF: Variable length
  */
-static u8 pci_cap_length[] = {
+static const u8 pci_cap_length[PCI_CAP_ID_MAX + 1] = {
        [PCI_CAP_ID_BASIC]      = PCI_STD_HEADER_SIZEOF, /* pci config header */
        [PCI_CAP_ID_PM]         = PCI_PM_SIZEOF,
        [PCI_CAP_ID_AGP]        = PCI_AGP_SIZEOF,
@@ -74,7 +74,7 @@ static u8 pci_cap_length[] = {
  *   0: Removed or masked from the user visible capabilty list
  *   FF: Variable length
  */
-static u16 pci_ext_cap_length[] = {
+static const u16 pci_ext_cap_length[PCI_EXT_CAP_ID_MAX + 1] = {
        [PCI_EXT_CAP_ID_ERR]    =       PCI_ERR_ROOT_COMMAND,
        [PCI_EXT_CAP_ID_VC]     =       0xFF,
        [PCI_EXT_CAP_ID_DSN]    =       PCI_EXT_CAP_DSN_SIZEOF,
@@ -671,6 +671,73 @@ static int __init init_pci_cap_pm_perm(struct perm_bits *perm)
        return 0;
 }
 
+static int vfio_vpd_config_write(struct vfio_pci_device *vdev, int pos,
+                                int count, struct perm_bits *perm,
+                                int offset, __le32 val)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       __le16 *paddr = (__le16 *)(vdev->vconfig + pos - offset + PCI_VPD_ADDR);
+       __le32 *pdata = (__le32 *)(vdev->vconfig + pos - offset + PCI_VPD_DATA);
+       u16 addr;
+       u32 data;
+
+       /*
+        * Write through to emulation.  If the write includes the upper byte
+        * of PCI_VPD_ADDR, then the PCI_VPD_ADDR_F bit is written and we
+        * have work to do.
+        */
+       count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+       if (count < 0 || offset > PCI_VPD_ADDR + 1 ||
+           offset + count <= PCI_VPD_ADDR + 1)
+               return count;
+
+       addr = le16_to_cpu(*paddr);
+
+       if (addr & PCI_VPD_ADDR_F) {
+               data = le32_to_cpu(*pdata);
+               if (pci_write_vpd(pdev, addr & ~PCI_VPD_ADDR_F, 4, &data) != 4)
+                       return count;
+       } else {
+               if (pci_read_vpd(pdev, addr, 4, &data) != 4)
+                       return count;
+               *pdata = cpu_to_le32(data);
+       }
+
+       /*
+        * Toggle PCI_VPD_ADDR_F in the emulated PCI_VPD_ADDR register to
+        * signal completion.  If an error occurs above, we assume that not
+        * toggling this bit will induce a driver timeout.
+        */
+       addr ^= PCI_VPD_ADDR_F;
+       *paddr = cpu_to_le16(addr);
+
+       return count;
+}
+
+/* Permissions for Vital Product Data capability */
+static int __init init_pci_cap_vpd_perm(struct perm_bits *perm)
+{
+       if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_VPD]))
+               return -ENOMEM;
+
+       perm->writefn = vfio_vpd_config_write;
+
+       /*
+        * We always virtualize the next field so we can remove
+        * capabilities from the chain if we want to.
+        */
+       p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+       /*
+        * Both the address and data registers are virtualized to
+        * enable access through the pci_vpd_read/write functions
+        */
+       p_setw(perm, PCI_VPD_ADDR, (u16)ALL_VIRT, (u16)ALL_WRITE);
+       p_setd(perm, PCI_VPD_DATA, ALL_VIRT, ALL_WRITE);
+
+       return 0;
+}
+
 /* Permissions for PCI-X capability */
 static int __init init_pci_cap_pcix_perm(struct perm_bits *perm)
 {
@@ -790,6 +857,7 @@ void vfio_pci_uninit_perm_bits(void)
        free_perm_bits(&cap_perms[PCI_CAP_ID_BASIC]);
 
        free_perm_bits(&cap_perms[PCI_CAP_ID_PM]);
+       free_perm_bits(&cap_perms[PCI_CAP_ID_VPD]);
        free_perm_bits(&cap_perms[PCI_CAP_ID_PCIX]);
        free_perm_bits(&cap_perms[PCI_CAP_ID_EXP]);
        free_perm_bits(&cap_perms[PCI_CAP_ID_AF]);
@@ -807,7 +875,7 @@ int __init vfio_pci_init_perm_bits(void)
 
        /* Capabilities */
        ret |= init_pci_cap_pm_perm(&cap_perms[PCI_CAP_ID_PM]);
-       cap_perms[PCI_CAP_ID_VPD].writefn = vfio_raw_config_write;
+       ret |= init_pci_cap_vpd_perm(&cap_perms[PCI_CAP_ID_VPD]);
        ret |= init_pci_cap_pcix_perm(&cap_perms[PCI_CAP_ID_PCIX]);
        cap_perms[PCI_CAP_ID_VNDR].writefn = vfio_raw_config_write;
        ret |= init_pci_cap_exp_perm(&cap_perms[PCI_CAP_ID_EXP]);