Joerg Roedel d89aa8
From: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Joerg Roedel d89aa8
Date: Wed, 27 Jun 2018 10:31:22 -0500
Joerg Roedel d89aa8
Subject: iommu/amd: Add support for IOMMU XT mode
Joerg Roedel d89aa8
Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
Joerg Roedel d89aa8
Git-commit: 90fcffd9cf5e7cc593169f529799f3e3c5437e75
Joerg Roedel d89aa8
Patch-mainline: queued, upstream in v4.19-rc1
Joerg Roedel d89aa8
References: fate#324429
Joerg Roedel d89aa8
Joerg Roedel d89aa8
The AMD IOMMU XT mode enables interrupt remapping with 32-bit destination
Joerg Roedel d89aa8
APIC ID, which is required for x2APIC. The feature is available when
Joerg Roedel d89aa8
the XTSup bit is set in the IOMMU Extended Feature register
Joerg Roedel d89aa8
and/or the IVHD Type 10h IOMMU Feature Reporting field.
Joerg Roedel d89aa8
Joerg Roedel d89aa8
For more information, please see section "IOMMU x2APIC Support" of
Joerg Roedel d89aa8
the AMD I/O Virtualization Technology (IOMMU) Specification.
Joerg Roedel d89aa8
Joerg Roedel d89aa8
Cc: Joerg Roedel <jroedel@suse.de>
Joerg Roedel d89aa8
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Joerg Roedel d89aa8
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Joerg Roedel d89aa8
---
Joerg Roedel d89aa8
 drivers/iommu/amd_iommu.c       | 21 ++++++++++++++++-----
Joerg Roedel d89aa8
 drivers/iommu/amd_iommu_init.c  | 25 +++++++++++++++++++++++--
Joerg Roedel d89aa8
 drivers/iommu/amd_iommu_types.h | 17 +++++++++++------
Joerg Roedel d89aa8
 3 files changed, 50 insertions(+), 13 deletions(-)
Joerg Roedel d89aa8
Joerg Roedel d89aa8
--- a/drivers/iommu/amd_iommu.c
Joerg Roedel d89aa8
+++ b/drivers/iommu/amd_iommu.c
Joerg Roedel d89aa8
@@ -3809,7 +3809,8 @@ static void irte_ga_prepare(void *entry,
Joerg Roedel d89aa8
 	irte->lo.fields_remap.int_type    = delivery_mode;
Joerg Roedel d89aa8
 	irte->lo.fields_remap.dm          = dest_mode;
Joerg Roedel d89aa8
 	irte->hi.fields.vector            = vector;
Joerg Roedel d89aa8
-	irte->lo.fields_remap.destination = dest_apicid;
Joerg Roedel d89aa8
+	irte->lo.fields_remap.destination = APICID_TO_IRTE_DEST_LO(dest_apicid);
Joerg Roedel d89aa8
+	irte->hi.fields.destination       = APICID_TO_IRTE_DEST_HI(dest_apicid);
Joerg Roedel d89aa8
 	irte->lo.fields_remap.valid       = 1;
Joerg Roedel d89aa8
 }
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
@@ -3864,7 +3865,10 @@ static void irte_ga_set_affinity(void *e
Joerg Roedel d89aa8
 	if (!dev_data || !dev_data->use_vapic ||
Joerg Roedel d89aa8
 	    !irte->lo.fields_remap.guest_mode) {
Joerg Roedel d89aa8
 		irte->hi.fields.vector = vector;
Joerg Roedel d89aa8
-		irte->lo.fields_remap.destination = dest_apicid;
Joerg Roedel d89aa8
+		irte->lo.fields_remap.destination =
Joerg Roedel d89aa8
+					APICID_TO_IRTE_DEST_LO(dest_apicid);
Joerg Roedel d89aa8
+		irte->hi.fields.destination =
Joerg Roedel d89aa8
+					APICID_TO_IRTE_DEST_HI(dest_apicid);
Joerg Roedel d89aa8
 		modify_irte_ga(devid, index, irte, NULL);
Joerg Roedel d89aa8
 	}
Joerg Roedel d89aa8
 }
Joerg Roedel d89aa8
@@ -4255,7 +4259,10 @@ static int amd_ir_set_vcpu_affinity(stru
Joerg Roedel d89aa8
 		irte->lo.val = 0;
Joerg Roedel d89aa8
 		irte->hi.fields.vector = cfg->vector;
Joerg Roedel d89aa8
 		irte->lo.fields_remap.guest_mode = 0;
Joerg Roedel d89aa8
-		irte->lo.fields_remap.destination = cfg->dest_apicid;
Joerg Roedel d89aa8
+		irte->lo.fields_remap.destination =
Joerg Roedel d89aa8
+				APICID_TO_IRTE_DEST_LO(cfg->dest_apicid);
Joerg Roedel d89aa8
+		irte->hi.fields.destination =
Joerg Roedel d89aa8
+				APICID_TO_IRTE_DEST_HI(cfg->dest_apicid);
Joerg Roedel d89aa8
 		irte->lo.fields_remap.int_type = apic->irq_delivery_mode;
Joerg Roedel d89aa8
 		irte->lo.fields_remap.dm = apic->irq_dest_mode;
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
@@ -4354,8 +4361,12 @@ int amd_iommu_update_ga(int cpu, bool is
Joerg Roedel d89aa8
 	spin_lock_irqsave(&irt->lock, flags);
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
 	if (ref->lo.fields_vapic.guest_mode) {
Joerg Roedel d89aa8
-		if (cpu >= 0)
Joerg Roedel d89aa8
-			ref->lo.fields_vapic.destination = cpu;
Joerg Roedel d89aa8
+		if (cpu >= 0) {
Joerg Roedel d89aa8
+			ref->lo.fields_vapic.destination =
Joerg Roedel d89aa8
+						APICID_TO_IRTE_DEST_LO(cpu);
Joerg Roedel d89aa8
+			ref->hi.fields.destination =
Joerg Roedel d89aa8
+						APICID_TO_IRTE_DEST_HI(cpu);
Joerg Roedel d89aa8
+		}
Joerg Roedel d89aa8
 		ref->lo.fields_vapic.is_run = is_run;
Joerg Roedel d89aa8
 		barrier();
Joerg Roedel d89aa8
 	}
Joerg Roedel d89aa8
--- a/drivers/iommu/amd_iommu_init.c
Joerg Roedel d89aa8
+++ b/drivers/iommu/amd_iommu_init.c
Joerg Roedel d89aa8
@@ -153,6 +153,7 @@ bool amd_iommu_dump;
Joerg Roedel d89aa8
 bool amd_iommu_irq_remap __read_mostly;
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
 int amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_VAPIC;
Joerg Roedel d89aa8
+static int amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE;
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
 static bool amd_iommu_detected;
Joerg Roedel d89aa8
 static bool __initdata amd_iommu_disabled;
Joerg Roedel d89aa8
@@ -827,6 +828,19 @@ static int iommu_init_ga(struct amd_iomm
Joerg Roedel d89aa8
 	return ret;
Joerg Roedel d89aa8
 }
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
+static void iommu_enable_xt(struct amd_iommu *iommu)
Joerg Roedel d89aa8
+{
Joerg Roedel d89aa8
+#ifdef CONFIG_IRQ_REMAP
Joerg Roedel d89aa8
+	/*
Joerg Roedel d89aa8
+	 * XT mode (32-bit APIC destination ID) requires
Joerg Roedel d89aa8
+	 * GA mode (128-bit IRTE support) as a prerequisite.
Joerg Roedel d89aa8
+	 */
Joerg Roedel d89aa8
+	if (AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir) &&
Joerg Roedel d89aa8
+	    amd_iommu_xt_mode == IRQ_REMAP_X2APIC_MODE)
Joerg Roedel d89aa8
+		iommu_feature_enable(iommu, CONTROL_XT_EN);
Joerg Roedel d89aa8
+#endif /* CONFIG_IRQ_REMAP */
Joerg Roedel d89aa8
+}
Joerg Roedel d89aa8
+
Joerg Roedel d89aa8
 static void iommu_enable_gt(struct amd_iommu *iommu)
Joerg Roedel d89aa8
 {
Joerg Roedel d89aa8
 	if (!iommu_feature(iommu, FEATURE_GT))
Joerg Roedel d89aa8
@@ -1507,6 +1521,8 @@ static int __init init_iommu_one(struct
Joerg Roedel d89aa8
 			iommu->mmio_phys_end = MMIO_CNTR_CONF_OFFSET;
Joerg Roedel d89aa8
 		if (((h->efr_attr & (0x1 << IOMMU_FEAT_GASUP_SHIFT)) == 0))
Joerg Roedel d89aa8
 			amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY;
Joerg Roedel d89aa8
+		if (((h->efr_attr & (0x1 << IOMMU_FEAT_XTSUP_SHIFT)) == 0))
Joerg Roedel d89aa8
+			amd_iommu_xt_mode = IRQ_REMAP_XAPIC_MODE;
Joerg Roedel d89aa8
 		break;
Joerg Roedel d89aa8
 	case 0x11:
Joerg Roedel d89aa8
 	case 0x40:
Joerg Roedel d89aa8
@@ -1516,6 +1532,8 @@ static int __init init_iommu_one(struct
Joerg Roedel d89aa8
 			iommu->mmio_phys_end = MMIO_CNTR_CONF_OFFSET;
Joerg Roedel d89aa8
 		if (((h->efr_reg & (0x1 << IOMMU_EFR_GASUP_SHIFT)) == 0))
Joerg Roedel d89aa8
 			amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY;
Joerg Roedel d89aa8
+		if (((h->efr_reg & (0x1 << IOMMU_EFR_XTSUP_SHIFT)) == 0))
Joerg Roedel d89aa8
+			amd_iommu_xt_mode = IRQ_REMAP_XAPIC_MODE;
Joerg Roedel d89aa8
 		break;
Joerg Roedel d89aa8
 	default:
Joerg Roedel d89aa8
 		return -EINVAL;
Joerg Roedel d89aa8
@@ -1831,6 +1849,8 @@ static void print_iommu_info(void)
Joerg Roedel d89aa8
 		pr_info("AMD-Vi: Interrupt remapping enabled\n");
Joerg Roedel d89aa8
 		if (AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir))
Joerg Roedel d89aa8
 			pr_info("AMD-Vi: virtual APIC enabled\n");
Joerg Roedel d89aa8
+		if (amd_iommu_xt_mode == IRQ_REMAP_X2APIC_MODE)
Joerg Roedel d89aa8
+			pr_info("AMD-Vi: X2APIC enabled\n");
Joerg Roedel d89aa8
 	}
Joerg Roedel d89aa8
 }
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
@@ -2167,6 +2187,7 @@ static void early_enable_iommu(struct am
Joerg Roedel d89aa8
 	iommu_enable_event_buffer(iommu);
Joerg Roedel d89aa8
 	iommu_set_exclusion_range(iommu);
Joerg Roedel d89aa8
 	iommu_enable_ga(iommu);
Joerg Roedel d89aa8
+	iommu_enable_xt(iommu);
Joerg Roedel d89aa8
 	iommu_enable(iommu);
Joerg Roedel d89aa8
 	iommu_flush_all_caches(iommu);
Joerg Roedel d89aa8
 }
Joerg Roedel d89aa8
@@ -2211,6 +2232,7 @@ static void early_enable_iommus(void)
Joerg Roedel d89aa8
 			iommu_enable_command_buffer(iommu);
Joerg Roedel d89aa8
 			iommu_enable_event_buffer(iommu);
Joerg Roedel d89aa8
 			iommu_enable_ga(iommu);
Joerg Roedel d89aa8
+			iommu_enable_xt(iommu);
Joerg Roedel d89aa8
 			iommu_set_device_table(iommu);
Joerg Roedel d89aa8
 			iommu_flush_all_caches(iommu);
Joerg Roedel d89aa8
 		}
Joerg Roedel d89aa8
@@ -2690,8 +2712,7 @@ int __init amd_iommu_enable(void)
Joerg Roedel d89aa8
 		return ret;
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
 	irq_remapping_enabled = 1;
Joerg Roedel d89aa8
-
Joerg Roedel d89aa8
-	return 0;
Joerg Roedel d89aa8
+	return amd_iommu_xt_mode;
Joerg Roedel d89aa8
 }
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
 void amd_iommu_disable(void)
Joerg Roedel d89aa8
--- a/drivers/iommu/amd_iommu_types.h
Joerg Roedel d89aa8
+++ b/drivers/iommu/amd_iommu_types.h
Joerg Roedel d89aa8
@@ -159,6 +159,7 @@
Joerg Roedel d89aa8
 #define CONTROL_GAM_EN          0x19ULL
Joerg Roedel d89aa8
 #define CONTROL_GALOG_EN        0x1CULL
Joerg Roedel d89aa8
 #define CONTROL_GAINT_EN        0x1DULL
Joerg Roedel d89aa8
+#define CONTROL_XT_EN           0x32ULL
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
 #define CTRL_INV_TO_MASK	(7 << CONTROL_INV_TIMEOUT)
Joerg Roedel d89aa8
 #define CTRL_INV_TO_NONE	0
Joerg Roedel d89aa8
@@ -375,9 +376,11 @@
Joerg Roedel d89aa8
 #define IOMMU_CAP_EFR     27
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
 /* IOMMU Feature Reporting Field (for IVHD type 10h */
Joerg Roedel d89aa8
+#define IOMMU_FEAT_XTSUP_SHIFT	0
Joerg Roedel d89aa8
 #define IOMMU_FEAT_GASUP_SHIFT	6
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
 /* IOMMU Extended Feature Register (EFR) */
Joerg Roedel d89aa8
+#define IOMMU_EFR_XTSUP_SHIFT	2
Joerg Roedel d89aa8
 #define IOMMU_EFR_GASUP_SHIFT	7
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
 #define MAX_DOMAIN_ID 65536
Joerg Roedel d89aa8
@@ -434,7 +437,6 @@ extern struct kmem_cache *amd_iommu_irq_
Joerg Roedel d89aa8
 #define APERTURE_RANGE_INDEX(a)	((a) >> APERTURE_RANGE_SHIFT)
Joerg Roedel d89aa8
 #define APERTURE_PAGE_INDEX(a)	(((a) >> 21) & 0x3fULL)
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
-
Joerg Roedel d89aa8
 /*
Joerg Roedel d89aa8
  * This struct is used to pass information about
Joerg Roedel d89aa8
  * incoming PPR faults around.
Joerg Roedel d89aa8
@@ -807,6 +809,9 @@ union irte {
Joerg Roedel d89aa8
 	} fields;
Joerg Roedel d89aa8
 };
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
+#define APICID_TO_IRTE_DEST_LO(x)    (x & 0xffffff)
Joerg Roedel d89aa8
+#define APICID_TO_IRTE_DEST_HI(x)    ((x >> 24) & 0xff)
Joerg Roedel d89aa8
+
Joerg Roedel d89aa8
 union irte_ga_lo {
Joerg Roedel d89aa8
 	u64 val;
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
@@ -820,8 +825,8 @@ union irte_ga_lo {
Joerg Roedel d89aa8
 		    dm		: 1,
Joerg Roedel d89aa8
 		    /* ------ */
Joerg Roedel d89aa8
 		    guest_mode	: 1,
Joerg Roedel d89aa8
-		    destination	: 8,
Joerg Roedel d89aa8
-		    rsvd	: 48;
Joerg Roedel d89aa8
+		    destination	: 24,
Joerg Roedel d89aa8
+		    ga_tag	: 32;
Joerg Roedel d89aa8
 	} fields_remap;
Joerg Roedel d89aa8
 
Joerg Roedel d89aa8
 	/* For guest vAPIC */
Joerg Roedel d89aa8
@@ -834,8 +839,7 @@ union irte_ga_lo {
Joerg Roedel d89aa8
 		    is_run	: 1,
Joerg Roedel d89aa8
 		    /* ------ */
Joerg Roedel d89aa8
 		    guest_mode	: 1,
Joerg Roedel d89aa8
-		    destination	: 8,
Joerg Roedel d89aa8
-		    rsvd2	: 16,
Joerg Roedel d89aa8
+		    destination	: 24,
Joerg Roedel d89aa8
 		    ga_tag	: 32;
Joerg Roedel d89aa8
 	} fields_vapic;
Joerg Roedel d89aa8
 };
Joerg Roedel d89aa8
@@ -846,7 +850,8 @@ union irte_ga_hi {
Joerg Roedel d89aa8
 		u64 vector	: 8,
Joerg Roedel d89aa8
 		    rsvd_1	: 4,
Joerg Roedel d89aa8
 		    ga_root_ptr	: 40,
Joerg Roedel d89aa8
-		    rsvd_2	: 12;
Joerg Roedel d89aa8
+		    rsvd_2	: 4,
Joerg Roedel d89aa8
+		    destination : 8;
Joerg Roedel d89aa8
 	} fields;
Joerg Roedel d89aa8
 };
Joerg Roedel d89aa8