--- /dev/null
+From: Suresh Siddha <suresh.b.siddha@intel.com>
+Subject: x64, x2apic/intr-remap: Interrupt remapping infrastructure
+References: fate #303948 and fate #303984
+Patch-Mainline: queued for .28
+Commit-ID: 2ae21010694e56461a63bfc80e960090ce0a5ed9
+
+Signed-off-by: Thomas Renninger <trenn@suse.de>
+
+Interrupt remapping (part of Intel Virtualization Tech for directed I/O)
+infrastructure.
+
+Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
+Cc: akpm@linux-foundation.org
+Cc: arjan@linux.intel.com
+Cc: andi@firstfloor.org
+Cc: ebiederm@xmission.com
+Cc: jbarnes@virtuousgeek.org
+Cc: steiner@sgi.com
+Signed-off-by: Ingo Molnar <mingo@elte.hu>
+
+---
+ drivers/pci/dma_remapping.h | 2
+ drivers/pci/dmar.c | 16 +++++
+ drivers/pci/intel-iommu.c | 25 ++-----
+ drivers/pci/intel-iommu.h | 24 ++++++-
+ drivers/pci/intr_remapping.c | 137 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/pci/intr_remapping.h | 2
+ include/linux/dmar.h | 120 ++++++++++++++++++++++++++-----------
+ 7 files changed, 272 insertions(+), 54 deletions(-)
+
+--- a/drivers/pci/dmar.c
++++ b/drivers/pci/dmar.c
+@@ -451,6 +451,22 @@ int __init early_dmar_detect(void)
+ return (ACPI_SUCCESS(status) ? 1 : 0);
+ }
+
++void __init detect_intel_iommu(void)
++{
++ int ret;
++
++ ret = early_dmar_detect();
++
++#ifdef CONFIG_DMAR
++ {
++ if (ret && !no_iommu && !iommu_detected && !swiotlb &&
++ !dmar_disabled)
++ iommu_detected = 1;
++ }
++#endif
++}
++
++
+ int alloc_iommu(struct dmar_drhd_unit *drhd)
+ {
+ struct intel_iommu *iommu;
+--- a/drivers/pci/dma_remapping.h
++++ b/drivers/pci/dma_remapping.h
+@@ -145,6 +145,8 @@ struct device_domain_info {
+ extern int init_dmars(void);
+ extern void free_dmar_iommu(struct intel_iommu *iommu);
+
++extern int dmar_disabled;
++
+ #ifndef CONFIG_DMAR_GFX_WA
+ static inline void iommu_prepare_gfx_mapping(void)
+ {
+--- a/drivers/pci/intel-iommu.c
++++ b/drivers/pci/intel-iommu.c
+@@ -78,7 +78,7 @@ static long list_size;
+
+ static void domain_remove_dev_info(struct dmar_domain *domain);
+
+-static int dmar_disabled;
++int dmar_disabled;
+ static int __initdata dmar_map_gfx = 1;
+ static int dmar_forcedac;
+ static int intel_iommu_strict;
+@@ -2259,19 +2259,6 @@ static struct dmi_system_id __initdata i
+ { }
+ };
+
+-
+-void __init detect_intel_iommu(void)
+-{
+- if (swiotlb || no_iommu || iommu_detected || dmar_disabled)
+- return;
+- if (early_dmar_detect()) {
+- dmi_check_system(intel_iommu_dmi_table);
+- if (dmar_disabled)
+- return;
+- iommu_detected = 1;
+- }
+-}
+-
+ static void __init init_no_remapping_devices(void)
+ {
+ struct dmar_drhd_unit *drhd;
+@@ -2318,15 +2305,19 @@ int __init intel_iommu_init(void)
+ {
+ int ret = 0;
+
+- if (no_iommu || swiotlb || dmar_disabled)
+- return -ENODEV;
+-
+ if (dmar_table_init())
+ return -ENODEV;
+
+ if (dmar_dev_scope_init())
+ return -ENODEV;
+
++ /*
++ * Check the need for DMA-remapping initialization now.
++ * Above initialization will also be used by Interrupt-remapping.
++ */
++ if (no_iommu || swiotlb || dmar_disabled)
++ return -ENODEV;
++
+ iommu_init_mempool();
+ dmar_init_reserved_ranges();
+
+--- a/drivers/pci/intel-iommu.h
++++ b/drivers/pci/intel-iommu.h
+@@ -56,6 +56,7 @@
+ #define DMAR_IQT_REG 0x88 /* Invalidation queue tail register */
+ #define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */
+ #define DMAR_ICS_REG 0x98 /* Invalidation complete status register */
++#define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr register */
+
+ #define OFFSET_STRIDE (9)
+ /*
+@@ -157,16 +158,20 @@ static inline void dmar_writeq(void __io
+ #define DMA_GCMD_SRTP (((u32)1) << 30)
+ #define DMA_GCMD_SFL (((u32)1) << 29)
+ #define DMA_GCMD_EAFL (((u32)1) << 28)
+-#define DMA_GCMD_QIE (((u32)1) << 26)
+ #define DMA_GCMD_WBF (((u32)1) << 27)
++#define DMA_GCMD_QIE (((u32)1) << 26)
++#define DMA_GCMD_SIRTP (((u32)1) << 24)
++#define DMA_GCMD_IRE (((u32) 1) << 25)
+
+ /* GSTS_REG */
+ #define DMA_GSTS_TES (((u32)1) << 31)
+ #define DMA_GSTS_RTPS (((u32)1) << 30)
+ #define DMA_GSTS_FLS (((u32)1) << 29)
+ #define DMA_GSTS_AFLS (((u32)1) << 28)
+-#define DMA_GSTS_QIES (((u32)1) << 26)
+ #define DMA_GSTS_WBFS (((u32)1) << 27)
++#define DMA_GSTS_QIES (((u32)1) << 26)
++#define DMA_GSTS_IRTPS (((u32)1) << 24)
++#define DMA_GSTS_IRES (((u32)1) << 25)
+
+ /* CCMD_REG */
+ #define DMA_CCMD_ICC (((u64)1) << 63)
+@@ -245,6 +250,16 @@ struct q_inval {
+ int free_cnt;
+ };
+
++#ifdef CONFIG_INTR_REMAP
++/* 1MB - maximum possible interrupt remapping table size */
++#define INTR_REMAP_PAGE_ORDER 8
++#define INTR_REMAP_TABLE_REG_SIZE 0xf
++
++struct ir_table {
++ struct irte *base;
++};
++#endif
++
+ struct intel_iommu {
+ void __iomem *reg; /* Pointer to hardware regs, virtual addr */
+ u64 cap;
+@@ -266,6 +281,9 @@ struct intel_iommu {
+ struct sys_device sysdev;
+ #endif
+ struct q_inval *qi; /* Queued invalidation info */
++#ifdef CONFIG_INTR_REMAP
++ struct ir_table *ir_table; /* Interrupt remapping info */
++#endif
+ };
+
+ static inline void __iommu_flush_cache(
+@@ -279,5 +297,7 @@ extern struct dmar_drhd_unit * dmar_find
+
+ extern int alloc_iommu(struct dmar_drhd_unit *drhd);
+ extern void free_iommu(struct intel_iommu *iommu);
++extern int dmar_enable_qi(struct intel_iommu *iommu);
++extern void qi_global_iec(struct intel_iommu *iommu);
+
+ #endif
+--- a/drivers/pci/intr_remapping.c
++++ b/drivers/pci/intr_remapping.c
+@@ -1,10 +1,147 @@
+ #include <linux/dmar.h>
++#include <linux/spinlock.h>
++#include <linux/jiffies.h>
++#include <linux/pci.h>
+ #include <asm/io_apic.h>
+ #include "intel-iommu.h"
+ #include "intr_remapping.h"
+
+ static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
+ static int ir_ioapic_num;
++int intr_remapping_enabled;
++
++static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
++{
++ u64 addr;
++ u32 cmd, sts;
++ unsigned long flags;
++
++ addr = virt_to_phys((void *)iommu->ir_table->base);
++
++ spin_lock_irqsave(&iommu->register_lock, flags);
++
++ dmar_writeq(iommu->reg + DMAR_IRTA_REG,
++ (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE);
++
++ /* Set interrupt-remapping table pointer */
++ cmd = iommu->gcmd | DMA_GCMD_SIRTP;
++ writel(cmd, iommu->reg + DMAR_GCMD_REG);
++
++ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
++ readl, (sts & DMA_GSTS_IRTPS), sts);
++ spin_unlock_irqrestore(&iommu->register_lock, flags);
++
++ /*
++ * global invalidation of interrupt entry cache before enabling
++ * interrupt-remapping.
++ */
++ qi_global_iec(iommu);
++
++ spin_lock_irqsave(&iommu->register_lock, flags);
++
++ /* Enable interrupt-remapping */
++ cmd = iommu->gcmd | DMA_GCMD_IRE;
++ iommu->gcmd |= DMA_GCMD_IRE;
++ writel(cmd, iommu->reg + DMAR_GCMD_REG);
++
++ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
++ readl, (sts & DMA_GSTS_IRES), sts);
++
++ spin_unlock_irqrestore(&iommu->register_lock, flags);
++}
++
++
++static int setup_intr_remapping(struct intel_iommu *iommu, int mode)
++{
++ struct ir_table *ir_table;
++ struct page *pages;
++
++ ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table),
++ GFP_KERNEL);
++
++ if (!iommu->ir_table)
++ return -ENOMEM;
++
++ pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, INTR_REMAP_PAGE_ORDER);
++
++ if (!pages) {
++ printk(KERN_ERR "failed to allocate pages of order %d\n",
++ INTR_REMAP_PAGE_ORDER);
++ kfree(iommu->ir_table);
++ return -ENOMEM;
++ }
++
++ ir_table->base = page_address(pages);
++
++ iommu_set_intr_remapping(iommu, mode);
++ return 0;
++}
++
++int __init enable_intr_remapping(int eim)
++{
++ struct dmar_drhd_unit *drhd;
++ int setup = 0;
++
++ /*
++ * check for the Interrupt-remapping support
++ */
++ for_each_drhd_unit(drhd) {
++ struct intel_iommu *iommu = drhd->iommu;
++
++ if (!ecap_ir_support(iommu->ecap))
++ continue;
++
++ if (eim && !ecap_eim_support(iommu->ecap)) {
++ printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, "
++ " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap);
++ return -1;
++ }
++ }
++
++ /*
++ * Enable queued invalidation for all the DRHD's.
++ */
++ for_each_drhd_unit(drhd) {
++ int ret;
++ struct intel_iommu *iommu = drhd->iommu;
++ ret = dmar_enable_qi(iommu);
++
++ if (ret) {
++ printk(KERN_ERR "DRHD %Lx: failed to enable queued, "
++ " invalidation, ecap %Lx, ret %d\n",
++ drhd->reg_base_addr, iommu->ecap, ret);
++ return -1;
++ }
++ }
++
++ /*
++ * Setup Interrupt-remapping for all the DRHD's now.
++ */
++ for_each_drhd_unit(drhd) {
++ struct intel_iommu *iommu = drhd->iommu;
++
++ if (!ecap_ir_support(iommu->ecap))
++ continue;
++
++ if (setup_intr_remapping(iommu, eim))
++ goto error;
++
++ setup = 1;
++ }
++
++ if (!setup)
++ goto error;
++
++ intr_remapping_enabled = 1;
++
++ return 0;
++
++error:
++ /*
++ * handle error condition gracefully here!
++ */
++ return -1;
++}
+
+ static int ir_parse_ioapic_scope(struct acpi_dmar_header *header,
+ struct intel_iommu *iommu)
+--- a/drivers/pci/intr_remapping.h
++++ b/drivers/pci/intr_remapping.h
+@@ -4,3 +4,5 @@ struct ioapic_scope {
+ struct intel_iommu *iommu;
+ unsigned int id;
+ };
++
++#define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
+--- a/include/linux/dmar.h
++++ b/include/linux/dmar.h
+@@ -25,9 +25,85 @@
+ #include <linux/types.h>
+ #include <linux/msi.h>
+
+-#ifdef CONFIG_DMAR
++#if defined(CONFIG_DMAR) || defined(CONFIG_INTR_REMAP)
+ struct intel_iommu;
+
++struct dmar_drhd_unit {
++ struct list_head list; /* list of drhd units */
++ struct acpi_dmar_header *hdr; /* ACPI header */
++ u64 reg_base_addr; /* register base address*/
++ struct pci_dev **devices; /* target device array */
++ int devices_cnt; /* target device count */
++ u8 ignored:1; /* ignore drhd */
++ u8 include_all:1;
++ struct intel_iommu *iommu;
++};
++
++extern struct list_head dmar_drhd_units;
++
++#define for_each_drhd_unit(drhd) \
++ list_for_each_entry(drhd, &dmar_drhd_units, list)
++
++extern int dmar_table_init(void);
++extern int early_dmar_detect(void);
++extern int dmar_dev_scope_init(void);
++
++/* Intel IOMMU detection */
++extern void detect_intel_iommu(void);
++
++
++extern int parse_ioapics_under_ir(void);
++extern int alloc_iommu(struct dmar_drhd_unit *);
++#else
++static inline void detect_intel_iommu(void)
++{
++ return;
++}
++
++static inline int dmar_table_init(void)
++{
++ return -ENODEV;
++}
++#endif /* !CONFIG_DMAR && !CONFIG_INTR_REMAP */
++
++#ifdef CONFIG_INTR_REMAP
++extern int intr_remapping_enabled;
++extern int enable_intr_remapping(int);
++
++struct irte {
++ union {
++ struct {
++ __u64 present : 1,
++ fpd : 1,
++ dst_mode : 1,
++ redir_hint : 1,
++ trigger_mode : 1,
++ dlvry_mode : 3,
++ avail : 4,
++ __reserved_1 : 4,
++ vector : 8,
++ __reserved_2 : 8,
++ dest_id : 32;
++ };
++ __u64 low;
++ };
++
++ union {
++ struct {
++ __u64 sid : 16,
++ sq : 2,
++ svt : 2,
++ __reserved_3 : 44;
++ };
++ __u64 high;
++ };
++};
++#else
++#define enable_intr_remapping(mode) (-1)
++#define intr_remapping_enabled (0)
++#endif
++
++#ifdef CONFIG_DMAR
+ extern const char *dmar_get_fault_reason(u8 fault_reason);
+
+ /* Can't use the common MSI interrupt functions
+@@ -40,29 +116,8 @@ extern void dmar_msi_write(int irq, stru
+ extern int dmar_set_interrupt(struct intel_iommu *iommu);
+ extern int arch_setup_dmar_msi(unsigned int irq);
+
+-/* Intel IOMMU detection and initialization functions */
+-extern void detect_intel_iommu(void);
+-extern int intel_iommu_init(void);
+-
+-extern int dmar_table_init(void);
+-extern int early_dmar_detect(void);
+-extern int dmar_dev_scope_init(void);
+-extern int parse_ioapics_under_ir(void);
+-
+-extern struct list_head dmar_drhd_units;
++extern int iommu_detected, no_iommu;
+ extern struct list_head dmar_rmrr_units;
+-
+-struct dmar_drhd_unit {
+- struct list_head list; /* list of drhd units */
+- struct acpi_dmar_header *hdr; /* ACPI header */
+- u64 reg_base_addr; /* register base address*/
+- struct pci_dev **devices; /* target device array */
+- int devices_cnt; /* target device count */
+- u8 ignored:1; /* ignore drhd */
+- u8 include_all:1;
+- struct intel_iommu *iommu;
+-};
+-
+ struct dmar_rmrr_unit {
+ struct list_head list; /* list of rmrr units */
+ struct acpi_dmar_header *hdr; /* ACPI header */
+@@ -72,24 +127,19 @@ struct dmar_rmrr_unit {
+ int devices_cnt; /* target device count */
+ };
+
+-#define for_each_drhd_unit(drhd) \
+- list_for_each_entry(drhd, &dmar_drhd_units, list)
+ #define for_each_rmrr_units(rmrr) \
+ list_for_each_entry(rmrr, &dmar_rmrr_units, list)
+-
+-extern int alloc_iommu(struct dmar_drhd_unit *);
++/* Intel DMAR initialization functions */
++extern int intel_iommu_init(void);
++extern int dmar_disabled;
+ #else
+-static inline void detect_intel_iommu(void)
+-{
+- return;
+-}
+ static inline int intel_iommu_init(void)
+ {
++#ifdef CONFIG_INTR_REMAP
++ return dmar_dev_scope_init();
++#else
+ return -ENODEV;
+-}
+-static inline int dmar_table_init(void)
+-{
+- return -ENODEV;
++#endif
+ }
+ #endif /* !CONFIG_DMAR */
+ #endif /* __DMAR_H__ */