Joerg Roedel 0d5a41
From: David Woodhouse <dwmw@amazon.co.uk>
Joerg Roedel 0d5a41
Date: Thu, 24 Sep 2020 15:08:42 +0100
Joerg Roedel 0d5a41
Subject: iommu/vt-d: Gracefully handle DMAR units with no supported address
Joerg Roedel 0d5a41
 widths
Joerg Roedel 0d5a41
Git-commit: c40aaaac1018ff1382f2d35df5129a6bcea3df6b
Joerg Roedel 0d5a41
Patch-mainline: v5.10-rc1
Joerg Roedel 0d5a41
References: bsc#1181001, jsc#ECO-3191
Joerg Roedel 0d5a41
Joerg Roedel 0d5a41
Instead of bailing out completely, such a unit can still be used for
Joerg Roedel 0d5a41
interrupt remapping.
Joerg Roedel 0d5a41
Joerg Roedel 0d5a41
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Joerg Roedel 0d5a41
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Joerg Roedel 0d5a41
Link: https://lore.kernel.org/linux-iommu/549928db2de6532117f36c9c810373c14cf76f51.camel@infradead.org/
Joerg Roedel 0d5a41
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Joerg Roedel 0d5a41
---
Joerg Roedel 0d5a41
 drivers/iommu/dmar.c |   46 +++++++++++++++++++++++++++++++---------------
Joerg Roedel 0d5a41
 1 file changed, 31 insertions(+), 15 deletions(-)
Joerg Roedel 0d5a41
Joerg Roedel 0d5a41
--- a/drivers/iommu/dmar.c
Joerg Roedel 0d5a41
+++ b/drivers/iommu/dmar.c
Joerg Roedel 0d5a41
@@ -1031,8 +1031,8 @@ static int alloc_iommu(struct dmar_drhd_
Joerg Roedel 0d5a41
 {
Joerg Roedel 0d5a41
 	struct intel_iommu *iommu;
Joerg Roedel 0d5a41
 	u32 ver, sts;
Joerg Roedel 0d5a41
-	int agaw = 0;
Joerg Roedel 0d5a41
-	int msagaw = 0;
Joerg Roedel 0d5a41
+	int agaw = -1;
Joerg Roedel 0d5a41
+	int msagaw = -1;
Joerg Roedel 0d5a41
 	int err;
Joerg Roedel 0d5a41
 
Joerg Roedel 0d5a41
 	if (!drhd->reg_base_addr) {
Joerg Roedel 0d5a41
@@ -1057,17 +1057,28 @@ static int alloc_iommu(struct dmar_drhd_
Joerg Roedel 0d5a41
 	}
Joerg Roedel 0d5a41
 
Joerg Roedel 0d5a41
 	err = -EINVAL;
Joerg Roedel 0d5a41
-	agaw = iommu_calculate_agaw(iommu);
Joerg Roedel 0d5a41
-	if (agaw < 0) {
Joerg Roedel 0d5a41
-		pr_err("Cannot get a valid agaw for iommu (seq_id = %d)\n",
Joerg Roedel 0d5a41
-			iommu->seq_id);
Joerg Roedel 0d5a41
-		goto err_unmap;
Joerg Roedel 0d5a41
-	}
Joerg Roedel 0d5a41
-	msagaw = iommu_calculate_max_sagaw(iommu);
Joerg Roedel 0d5a41
-	if (msagaw < 0) {
Joerg Roedel 0d5a41
-		pr_err("Cannot get a valid max agaw for iommu (seq_id = %d)\n",
Joerg Roedel 0d5a41
-			iommu->seq_id);
Joerg Roedel 0d5a41
-		goto err_unmap;
Joerg Roedel 0d5a41
+	if (cap_sagaw(iommu->cap) == 0) {
Joerg Roedel 0d5a41
+		pr_info("%s: No supported address widths. Not attempting DMA translation.\n",
Joerg Roedel 0d5a41
+			iommu->name);
Joerg Roedel 0d5a41
+		drhd->ignored = 1;
Joerg Roedel 0d5a41
+	}
Joerg Roedel 0d5a41
+
Joerg Roedel 0d5a41
+	if (!drhd->ignored) {
Joerg Roedel 0d5a41
+		agaw = iommu_calculate_agaw(iommu);
Joerg Roedel 0d5a41
+		if (agaw < 0) {
Joerg Roedel 0d5a41
+			pr_err("Cannot get a valid agaw for iommu (seq_id = %d)\n",
Joerg Roedel 0d5a41
+			       iommu->seq_id);
Joerg Roedel 0d5a41
+			drhd->ignored = 1;
Joerg Roedel 0d5a41
+		}
Joerg Roedel 0d5a41
+	}
Joerg Roedel 0d5a41
+	if (!drhd->ignored) {
Joerg Roedel 0d5a41
+		msagaw = iommu_calculate_max_sagaw(iommu);
Joerg Roedel 0d5a41
+		if (msagaw < 0) {
Joerg Roedel 0d5a41
+			pr_err("Cannot get a valid max agaw for iommu (seq_id = %d)\n",
Joerg Roedel 0d5a41
+			       iommu->seq_id);
Joerg Roedel 0d5a41
+			drhd->ignored = 1;
Joerg Roedel 0d5a41
+			agaw = -1;
Joerg Roedel 0d5a41
+		}
Joerg Roedel 0d5a41
 	}
Joerg Roedel 0d5a41
 	iommu->agaw = agaw;
Joerg Roedel 0d5a41
 	iommu->msagaw = msagaw;
Joerg Roedel 0d5a41
@@ -1094,7 +1105,12 @@ static int alloc_iommu(struct dmar_drhd_
Joerg Roedel 0d5a41
 
Joerg Roedel 0d5a41
 	raw_spin_lock_init(&iommu->register_lock);
Joerg Roedel 0d5a41
 
Joerg Roedel 0d5a41
-	if (intel_iommu_enabled) {
Joerg Roedel 0d5a41
+	/*
Joerg Roedel 0d5a41
+	 * This is only for hotplug; at boot time intel_iommu_enabled won't
Joerg Roedel 0d5a41
+	 * be set yet. When intel_iommu_init() runs, it registers the units
Joerg Roedel 0d5a41
+	 * present at boot time, then sets intel_iommu_enabled.
Joerg Roedel 0d5a41
+	 */
Joerg Roedel 0d5a41
+	if (intel_iommu_enabled && !drhd->ignored) {
Joerg Roedel 0d5a41
 		err = iommu_device_sysfs_add(&iommu->iommu, NULL,
Joerg Roedel 0d5a41
 					     intel_iommu_groups,
Joerg Roedel 0d5a41
 					     "%s", iommu->name);
Joerg Roedel 0d5a41
@@ -1123,7 +1139,7 @@ error:
Joerg Roedel 0d5a41
 
Joerg Roedel 0d5a41
 static void free_iommu(struct intel_iommu *iommu)
Joerg Roedel 0d5a41
 {
Joerg Roedel 0d5a41
-	if (intel_iommu_enabled) {
Joerg Roedel 0d5a41
+	if (intel_iommu_enabled && iommu->iommu.ops) {
Joerg Roedel 0d5a41
 		iommu_device_unregister(&iommu->iommu);
Joerg Roedel 0d5a41
 		iommu_device_sysfs_remove(&iommu->iommu);
Joerg Roedel 0d5a41
 	}