iommu/amd: Introduce global variable for storing common EFR and EFR2
authorSuravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Wed, 13 Jul 2022 22:56:45 +0000 (17:56 -0500)
committerJoerg Roedel <jroedel@suse.de>
Fri, 15 Jul 2022 08:41:02 +0000 (10:41 +0200)
Some IOMMU features require that all IOMMUs must support the feature,
which is determined by checking the support bit in the Extended Feature
Register 1 and 2 (EFR/EFR2) on all IOMMUs. This check is done by the
function check_feature_on_all_iommus(), which iterates through all
IOMMUs everytime it is called.

Instead, introduce a global variable to store common EFR/EFR2 among all
IOMMUs. In case of inconsistent EFR/EFR2 masks are detected on an IOMMU,
a FW_BUG warning is reported.

Suggested-by: Joerg Roedel <joro@8bytes.org>
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Link: https://lore.kernel.org/r/20220713225651.20758-4-suravee.suthikulpanit@amd.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
drivers/iommu/amd/amd_iommu.h
drivers/iommu/amd/init.c

index 9b70921..1b945d4 100644 (file)
@@ -137,4 +137,7 @@ static inline void amd_iommu_apply_ivrs_quirks(void) { }
 extern void amd_iommu_domain_set_pgtable(struct protection_domain *domain,
                                         u64 *root, int mode);
 extern struct dev_table_entry *get_dev_table(struct amd_iommu *iommu);
+
+extern u64 amd_iommu_efr;
+extern u64 amd_iommu_efr2;
 #endif
index 15a836b..3ab0fc3 100644 (file)
@@ -164,6 +164,10 @@ static bool amd_iommu_disabled __initdata;
 static bool amd_iommu_force_enable __initdata;
 static int amd_iommu_target_ivhd_type;
 
+/* Global EFR and EFR2 registers */
+u64 amd_iommu_efr;
+u64 amd_iommu_efr2;
+
 LIST_HEAD(amd_iommu_pci_seg_list);     /* list of all PCI segments */
 LIST_HEAD(amd_iommu_list);             /* list of all AMD IOMMUs in the
                                           system */
@@ -259,21 +263,46 @@ int amd_iommu_get_num_iommus(void)
        return amd_iommus_present;
 }
 
-#ifdef CONFIG_IRQ_REMAP
-static bool check_feature_on_all_iommus(u64 mask)
+/*
+ * Iterate through all the IOMMUs to get common EFR
+ * masks among all IOMMUs and warn if found inconsistency.
+ */
+static void get_global_efr(void)
 {
-       bool ret = false;
        struct amd_iommu *iommu;
 
        for_each_iommu(iommu) {
-               ret = iommu_feature(iommu, mask);
-               if (!ret)
-                       return false;
+               u64 tmp = iommu->features;
+               u64 tmp2 = iommu->features2;
+
+               if (list_is_first(&iommu->list, &amd_iommu_list)) {
+                       amd_iommu_efr = tmp;
+                       amd_iommu_efr2 = tmp2;
+                       continue;
+               }
+
+               if (amd_iommu_efr == tmp &&
+                   amd_iommu_efr2 == tmp2)
+                       continue;
+
+               pr_err(FW_BUG
+                      "Found inconsistent EFR/EFR2 %#llx,%#llx (global %#llx,%#llx) on iommu%d (%04x:%02x:%02x.%01x).\n",
+                      tmp, tmp2, amd_iommu_efr, amd_iommu_efr2,
+                      iommu->index, iommu->pci_seg->id,
+                      PCI_BUS_NUM(iommu->devid), PCI_SLOT(iommu->devid),
+                      PCI_FUNC(iommu->devid));
+
+               amd_iommu_efr &= tmp;
+               amd_iommu_efr2 &= tmp2;
        }
 
-       return true;
+       pr_info("Using global IVHD EFR:%#llx, EFR2:%#llx\n", amd_iommu_efr, amd_iommu_efr2);
+}
+
+static bool check_feature_on_all_iommus(u64 mask)
+{
+       return !!(amd_iommu_efr & mask);
 }
-#endif
 
 /*
  * For IVHD type 0x11/0x40, EFR is also available via IVHD.