PCI: acs p2p upsteram forwarding enabling
Allen Kay [Wed, 7 Oct 2009 17:27:17 +0000 (10:27 -0700)]
Note: dom0 checking in v4 has been separated out into 2/2.

This patch enables P2P upstream forwarding in ACS capable PCIe switches.
It solves two potential problems in virtualization environment where a PCIe
device is assigned to a guest domain using a HW iommu such as VT-d:

1) Unintentional failure caused by guest physical address programmed
   into the device's DMA that happens to match the memory address range
   of other downstream ports in the same PCIe switch.  This causes the PCI
   transaction to go to the matching downstream port instead of go to the
   root complex to get translated by VT-d as it should be.

2) Malicious guest software intentionally attacks another downstream
   PCIe device by programming the DMA address into the assigned device
   that matches memory address range of the downstream PCIe port.

We are in process of implementing device filtering software in KVM/XEN
management software to allow device assignment of PCIe devices behind a PCIe
switch only if it has ACS capability and with the P2P upstream forwarding bits
enabled.  This patch is intended to work for both KVM and Xen environments.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
Reviewed-by: Mathew Wilcox <willy@linux.intel.com>
Reviewed-by: Chris Wright <chris@sous-sol.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/probe.c
include/linux/pci_regs.h

index 4859669..5572182 100644 (file)
@@ -1546,6 +1546,41 @@ void pci_enable_ari(struct pci_dev *dev)
 }
 
 /**
+ * pci_enable_acs - enable ACS if hardware support it
+ * @dev: the PCI device
+ */
+void pci_enable_acs(struct pci_dev *dev)
+{
+       int pos;
+       u16 cap;
+       u16 ctrl;
+
+       if (!dev->is_pcie)
+               return;
+
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
+       if (!pos)
+               return;
+
+       pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap);
+       pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
+
+       /* Source Validation */
+       ctrl |= (cap & PCI_ACS_SV);
+
+       /* P2P Request Redirect */
+       ctrl |= (cap & PCI_ACS_RR);
+
+       /* P2P Completion Redirect */
+       ctrl |= (cap & PCI_ACS_CR);
+
+       /* Upstream Forwarding */
+       ctrl |= (cap & PCI_ACS_UF);
+
+       pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
+}
+
+/**
  * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge
  * @dev: the PCI device
  * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTD, 4=INTD)
index d92d195..33ed8e0 100644 (file)
@@ -311,4 +311,6 @@ static inline int pci_resource_alignment(struct pci_dev *dev,
        return resource_alignment(res);
 }
 
+extern void pci_enable_acs(struct pci_dev *dev);
+
 #endif /* DRIVERS_PCI_H */
index 2adb475..aac5b15 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/cpumask.h>
 #include <linux/pci-aspm.h>
+#include <linux/iommu.h>
 #include "pci.h"
 
 #define CARDBUS_LATENCY_TIMER  176     /* secondary latency timer */
@@ -1004,6 +1005,10 @@ static void pci_init_capabilities(struct pci_dev *dev)
 
        /* Single Root I/O Virtualization */
        pci_iov_init(dev);
+
+       /* Enable ACS P2P upstream forwarding */
+       if (iommu_found())
+               pci_enable_acs(dev);
 }
 
 void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
index dd0bed4..d798770 100644 (file)
 #define PCI_EXT_CAP_ID_VC      2
 #define PCI_EXT_CAP_ID_DSN     3
 #define PCI_EXT_CAP_ID_PWR     4
+#define PCI_EXT_CAP_ID_ACS     13
 #define PCI_EXT_CAP_ID_ARI     14
 #define PCI_EXT_CAP_ID_ATS     15
 #define PCI_EXT_CAP_ID_SRIOV   16
 #define  PCI_SRIOV_VFM_MO      0x2     /* Active.MigrateOut */
 #define  PCI_SRIOV_VFM_AV      0x3     /* Active.Available */
 
+/* Access Control Service */
+#define PCI_ACS_CAP            0x04    /* ACS Capability Register */
+#define  PCI_ACS_SV            0x01    /* Source Validation */
+#define  PCI_ACS_TB            0x02    /* Translation Blocking */
+#define  PCI_ACS_RR            0x04    /* P2P Request Redirect */
+#define  PCI_ACS_CR            0x08    /* P2P Completion Redirect */
+#define  PCI_ACS_UF            0x10    /* Upstream Forwarding */
+#define  PCI_ACS_EC            0x20    /* P2P Egress Control */
+#define  PCI_ACS_DT            0x40    /* Direct Translated P2P */
+#define PCI_ACS_CTRL           0x06    /* ACS Control Register */
+#define PCI_ACS_EGRESS_CTL_V   0x08    /* ACS Egress Control Vector */
+
 #endif /* LINUX_PCI_REGS_H */