PCI: Add "pci=noats" boot parameter
authorGil Kupfer <gilkup@gmail.com>
Thu, 10 May 2018 22:56:02 +0000 (17:56 -0500)
committerBjorn Helgaas <helgaas@kernel.org>
Thu, 10 May 2018 22:56:02 +0000 (17:56 -0500)
Adds a "pci=noats" boot parameter.  When supplied, all ATS related
functions fail immediately and the IOMMU is configured to not use
device-IOTLB.

Any function that checks for ATS capabilities directly against the devices
should also check this flag.  Currently, such functions exist only in IOMMU
drivers, and they are covered by this patch.

The motivation behind this patch is the existence of malicious devices.
Lots of research has been done about how to use the IOMMU as protection
from such devices.  When ATS is supported, any I/O device can access any
physical address by faking device-IOTLB entries.  Adding the ability to
ignore these entries lets sysadmins enhance system security.

Signed-off-by: Gil Kupfer <gilkup@cs.technion.ac.il>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Joerg Roedel <jroedel@suse.de>
Documentation/admin-guide/kernel-parameters.txt
drivers/iommu/amd_iommu.c
drivers/iommu/intel-iommu.c
drivers/pci/ats.c
drivers/pci/pci.c
include/linux/pci.h

index 11fc28ecdb6d9f2ea1ce28807caf59c96c93c164..a19ccac3b4c7bb7f7a3a4717821b097f94e70593 100644 (file)
                                on: Turn realloc on
                realloc         same as realloc=on
                noari           do not use PCIe ARI.
+               noats           [PCIE, Intel-IOMMU, AMD-IOMMU]
+                               do not use PCIe ATS (and IOMMU device IOTLB).
                pcie_scan_all   Scan all possible PCIe devices.  Otherwise we
                                only look for one device below a PCIe downstream
                                port.
index 2a99f0f14795549eacb6d88b87e30cc0ca78272e..56da1c6121d324c9215edaca85fa0a2782710922 100644 (file)
@@ -355,6 +355,9 @@ static bool pci_iommuv2_capable(struct pci_dev *pdev)
        };
        int i, pos;
 
+       if (pci_ats_disabled())
+               return false;
+
        for (i = 0; i < 3; ++i) {
                pos = pci_find_ext_capability(pdev, caps[i]);
                if (pos == 0)
@@ -3524,9 +3527,11 @@ int amd_iommu_device_info(struct pci_dev *pdev,
 
        memset(info, 0, sizeof(*info));
 
-       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ATS);
-       if (pos)
-               info->flags |= AMD_IOMMU_DEVICE_FLAG_ATS_SUP;
+       if (!pci_ats_disabled()) {
+               pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ATS);
+               if (pos)
+                       info->flags |= AMD_IOMMU_DEVICE_FLAG_ATS_SUP;
+       }
 
        pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
        if (pos)
index 749d8f2353466832ae3df9ace44c125f703267c0..772b404a6604b3b315ff77de14115d76a3e8d677 100644 (file)
@@ -2459,7 +2459,8 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
        if (dev && dev_is_pci(dev)) {
                struct pci_dev *pdev = to_pci_dev(info->dev);
 
-               if (ecap_dev_iotlb_support(iommu->ecap) &&
+               if (!pci_ats_disabled() &&
+                   ecap_dev_iotlb_support(iommu->ecap) &&
                    pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ATS) &&
                    dmar_find_matched_atsr_unit(pdev))
                        info->ats_supported = 1;
index 89305b569d3d8899e1ea093474010b73e6468669..4923a2a8e14b55f953a3ca3a134e3819997c5236 100644 (file)
@@ -20,6 +20,9 @@ void pci_ats_init(struct pci_dev *dev)
 {
        int pos;
 
+       if (pci_ats_disabled())
+               return;
+
        pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
        if (!pos)
                return;
index e597655a5643b6bc92be2a40ed3fa2b11bd093dd..789ce36be341ce2819bd04a2110238a47fb869c6 100644 (file)
@@ -112,6 +112,14 @@ unsigned int pcibios_max_latency = 255;
 /* If set, the PCIe ARI capability will not be used. */
 static bool pcie_ari_disabled;
 
+/* If set, the PCIe ATS capability will not be used. */
+static bool pcie_ats_disabled;
+
+bool pci_ats_disabled(void)
+{
+       return pcie_ats_disabled;
+}
+
 /* Disable bridge_d3 for all PCIe ports */
 static bool pci_bridge_d3_disable;
 /* Force bridge_d3 for all PCIe ports */
@@ -5793,6 +5801,9 @@ static int __init pci_setup(char *str)
                if (*str && (str = pcibios_setup(str)) && *str) {
                        if (!strcmp(str, "nomsi")) {
                                pci_no_msi();
+                       } else if (!strncmp(str, "noats", 5)) {
+                               pr_info("PCIe: ATS is disabled\n");
+                               pcie_ats_disabled = true;
                        } else if (!strcmp(str, "noaer")) {
                                pci_no_aer();
                        } else if (!strncmp(str, "realloc=", 8)) {
index 911f9098a466b74b0b4b345d46be217d8fdb9e0a..aa9c27e129d41d4f3176a9c6117e367c141f6c4b 100644 (file)
@@ -1479,6 +1479,8 @@ static inline void pcie_set_ecrc_checking(struct pci_dev *dev) { }
 static inline void pcie_ecrc_get_policy(char *str) { }
 #endif
 
+bool pci_ats_disabled(void);
+
 #ifdef CONFIG_PCI_ATS
 /* Address Translation Service */
 void pci_ats_init(struct pci_dev *dev);