aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuresh Siddha <suresh.b.siddha@intel.com>2008-07-10 14:16:43 -0400
committerIngo Molnar <mingo@elte.hu>2008-07-12 02:44:53 -0400
commit2ae21010694e56461a63bfc80e960090ce0a5ed9 (patch)
treed4ecdb710c4361df473b063eda9e1426fcf5c309
parentfe962e90cb17a8426e144dee970e77ed789d98ee (diff)
x64, x2apic/intr-remap: Interrupt remapping infrastructure
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>
-rw-r--r--drivers/pci/dma_remapping.h2
-rw-r--r--drivers/pci/dmar.c16
-rw-r--r--drivers/pci/intel-iommu.c21
-rw-r--r--drivers/pci/intel-iommu.h24
-rw-r--r--drivers/pci/intr_remapping.c137
-rw-r--r--drivers/pci/intr_remapping.h2
-rw-r--r--include/linux/dmar.h120
7 files changed, 272 insertions, 50 deletions
diff --git a/drivers/pci/dma_remapping.h b/drivers/pci/dma_remapping.h
index 05aac8ef96c7..bff5c65f81dc 100644
--- a/drivers/pci/dma_remapping.h
+++ b/drivers/pci/dma_remapping.h
@@ -145,6 +145,8 @@ struct device_domain_info {
145extern int init_dmars(void); 145extern int init_dmars(void);
146extern void free_dmar_iommu(struct intel_iommu *iommu); 146extern void free_dmar_iommu(struct intel_iommu *iommu);
147 147
148extern int dmar_disabled;
149
148#ifndef CONFIG_DMAR_GFX_WA 150#ifndef CONFIG_DMAR_GFX_WA
149static inline void iommu_prepare_gfx_mapping(void) 151static inline void iommu_prepare_gfx_mapping(void)
150{ 152{
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
index aba151ca6d26..23a119e6485e 100644
--- a/drivers/pci/dmar.c
+++ b/drivers/pci/dmar.c
@@ -449,6 +449,22 @@ int __init early_dmar_detect(void)
449 return (ACPI_SUCCESS(status) ? 1 : 0); 449 return (ACPI_SUCCESS(status) ? 1 : 0);
450} 450}
451 451
452void __init detect_intel_iommu(void)
453{
454 int ret;
455
456 ret = early_dmar_detect();
457
458#ifdef CONFIG_DMAR
459 {
460 if (ret && !no_iommu && !iommu_detected && !swiotlb &&
461 !dmar_disabled)
462 iommu_detected = 1;
463 }
464#endif
465}
466
467
452int alloc_iommu(struct dmar_drhd_unit *drhd) 468int alloc_iommu(struct dmar_drhd_unit *drhd)
453{ 469{
454 struct intel_iommu *iommu; 470 struct intel_iommu *iommu;
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 347bf2e47168..ffccf2341b98 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -76,7 +76,7 @@ static long list_size;
76 76
77static void domain_remove_dev_info(struct dmar_domain *domain); 77static void domain_remove_dev_info(struct dmar_domain *domain);
78 78
79static int dmar_disabled; 79int dmar_disabled;
80static int __initdata dmar_map_gfx = 1; 80static int __initdata dmar_map_gfx = 1;
81static int dmar_forcedac; 81static int dmar_forcedac;
82static int intel_iommu_strict; 82static int intel_iommu_strict;
@@ -2238,15 +2238,6 @@ static void __init iommu_exit_mempool(void)
2238 2238
2239} 2239}
2240 2240
2241void __init detect_intel_iommu(void)
2242{
2243 if (swiotlb || no_iommu || iommu_detected || dmar_disabled)
2244 return;
2245 if (early_dmar_detect()) {
2246 iommu_detected = 1;
2247 }
2248}
2249
2250static void __init init_no_remapping_devices(void) 2241static void __init init_no_remapping_devices(void)
2251{ 2242{
2252 struct dmar_drhd_unit *drhd; 2243 struct dmar_drhd_unit *drhd;
@@ -2293,15 +2284,19 @@ int __init intel_iommu_init(void)
2293{ 2284{
2294 int ret = 0; 2285 int ret = 0;
2295 2286
2296 if (no_iommu || swiotlb || dmar_disabled)
2297 return -ENODEV;
2298
2299 if (dmar_table_init()) 2287 if (dmar_table_init())
2300 return -ENODEV; 2288 return -ENODEV;
2301 2289
2302 if (dmar_dev_scope_init()) 2290 if (dmar_dev_scope_init())
2303 return -ENODEV; 2291 return -ENODEV;
2304 2292
2293 /*
2294 * Check the need for DMA-remapping initialization now.
2295 * Above initialization will also be used by Interrupt-remapping.
2296 */
2297 if (no_iommu || swiotlb || dmar_disabled)
2298 return -ENODEV;
2299
2305 iommu_init_mempool(); 2300 iommu_init_mempool();
2306 dmar_init_reserved_ranges(); 2301 dmar_init_reserved_ranges();
2307 2302
diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h
index 2983ce895353..a81a74e2bd9e 100644
--- a/drivers/pci/intel-iommu.h
+++ b/drivers/pci/intel-iommu.h
@@ -56,6 +56,7 @@
56#define DMAR_IQT_REG 0x88 /* Invalidation queue tail register */ 56#define DMAR_IQT_REG 0x88 /* Invalidation queue tail register */
57#define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */ 57#define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */
58#define DMAR_ICS_REG 0x98 /* Invalidation complete status register */ 58#define DMAR_ICS_REG 0x98 /* Invalidation complete status register */
59#define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr register */
59 60
60#define OFFSET_STRIDE (9) 61#define OFFSET_STRIDE (9)
61/* 62/*
@@ -157,16 +158,20 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
157#define DMA_GCMD_SRTP (((u32)1) << 30) 158#define DMA_GCMD_SRTP (((u32)1) << 30)
158#define DMA_GCMD_SFL (((u32)1) << 29) 159#define DMA_GCMD_SFL (((u32)1) << 29)
159#define DMA_GCMD_EAFL (((u32)1) << 28) 160#define DMA_GCMD_EAFL (((u32)1) << 28)
160#define DMA_GCMD_QIE (((u32)1) << 26)
161#define DMA_GCMD_WBF (((u32)1) << 27) 161#define DMA_GCMD_WBF (((u32)1) << 27)
162#define DMA_GCMD_QIE (((u32)1) << 26)
163#define DMA_GCMD_SIRTP (((u32)1) << 24)
164#define DMA_GCMD_IRE (((u32) 1) << 25)
162 165
163/* GSTS_REG */ 166/* GSTS_REG */
164#define DMA_GSTS_TES (((u32)1) << 31) 167#define DMA_GSTS_TES (((u32)1) << 31)
165#define DMA_GSTS_RTPS (((u32)1) << 30) 168#define DMA_GSTS_RTPS (((u32)1) << 30)
166#define DMA_GSTS_FLS (((u32)1) << 29) 169#define DMA_GSTS_FLS (((u32)1) << 29)
167#define DMA_GSTS_AFLS (((u32)1) << 28) 170#define DMA_GSTS_AFLS (((u32)1) << 28)
168#define DMA_GSTS_QIES (((u32)1) << 26)
169#define DMA_GSTS_WBFS (((u32)1) << 27) 171#define DMA_GSTS_WBFS (((u32)1) << 27)
172#define DMA_GSTS_QIES (((u32)1) << 26)
173#define DMA_GSTS_IRTPS (((u32)1) << 24)
174#define DMA_GSTS_IRES (((u32)1) << 25)
170 175
171/* CCMD_REG */ 176/* CCMD_REG */
172#define DMA_CCMD_ICC (((u64)1) << 63) 177#define DMA_CCMD_ICC (((u64)1) << 63)
@@ -245,6 +250,16 @@ struct q_inval {
245 int free_cnt; 250 int free_cnt;
246}; 251};
247 252
253#ifdef CONFIG_INTR_REMAP
254/* 1MB - maximum possible interrupt remapping table size */
255#define INTR_REMAP_PAGE_ORDER 8
256#define INTR_REMAP_TABLE_REG_SIZE 0xf
257
258struct ir_table {
259 struct irte *base;
260};
261#endif
262
248struct intel_iommu { 263struct intel_iommu {
249 void __iomem *reg; /* Pointer to hardware regs, virtual addr */ 264 void __iomem *reg; /* Pointer to hardware regs, virtual addr */
250 u64 cap; 265 u64 cap;
@@ -266,6 +281,9 @@ struct intel_iommu {
266 struct sys_device sysdev; 281 struct sys_device sysdev;
267#endif 282#endif
268 struct q_inval *qi; /* Queued invalidation info */ 283 struct q_inval *qi; /* Queued invalidation info */
284#ifdef CONFIG_INTR_REMAP
285 struct ir_table *ir_table; /* Interrupt remapping info */
286#endif
269}; 287};
270 288
271static inline void __iommu_flush_cache( 289static inline void __iommu_flush_cache(
@@ -279,5 +297,7 @@ extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev);
279 297
280extern int alloc_iommu(struct dmar_drhd_unit *drhd); 298extern int alloc_iommu(struct dmar_drhd_unit *drhd);
281extern void free_iommu(struct intel_iommu *iommu); 299extern void free_iommu(struct intel_iommu *iommu);
300extern int dmar_enable_qi(struct intel_iommu *iommu);
301extern void qi_global_iec(struct intel_iommu *iommu);
282 302
283#endif 303#endif
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c
index a80b87921c68..3d10cdc90314 100644
--- a/drivers/pci/intr_remapping.c
+++ b/drivers/pci/intr_remapping.c
@@ -1,10 +1,147 @@
1#include <linux/dmar.h> 1#include <linux/dmar.h>
2#include <linux/spinlock.h>
3#include <linux/jiffies.h>
4#include <linux/pci.h>
2#include <asm/io_apic.h> 5#include <asm/io_apic.h>
3#include "intel-iommu.h" 6#include "intel-iommu.h"
4#include "intr_remapping.h" 7#include "intr_remapping.h"
5 8
6static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; 9static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
7static int ir_ioapic_num; 10static int ir_ioapic_num;
11int intr_remapping_enabled;
12
13static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
14{
15 u64 addr;
16 u32 cmd, sts;
17 unsigned long flags;
18
19 addr = virt_to_phys((void *)iommu->ir_table->base);
20
21 spin_lock_irqsave(&iommu->register_lock, flags);
22
23 dmar_writeq(iommu->reg + DMAR_IRTA_REG,
24 (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE);
25
26 /* Set interrupt-remapping table pointer */
27 cmd = iommu->gcmd | DMA_GCMD_SIRTP;
28 writel(cmd, iommu->reg + DMAR_GCMD_REG);
29
30 IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
31 readl, (sts & DMA_GSTS_IRTPS), sts);
32 spin_unlock_irqrestore(&iommu->register_lock, flags);
33
34 /*
35 * global invalidation of interrupt entry cache before enabling
36 * interrupt-remapping.
37 */
38 qi_global_iec(iommu);
39
40 spin_lock_irqsave(&iommu->register_lock, flags);
41
42 /* Enable interrupt-remapping */
43 cmd = iommu->gcmd | DMA_GCMD_IRE;
44 iommu->gcmd |= DMA_GCMD_IRE;
45 writel(cmd, iommu->reg + DMAR_GCMD_REG);
46
47 IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
48 readl, (sts & DMA_GSTS_IRES), sts);
49
50 spin_unlock_irqrestore(&iommu->register_lock, flags);
51}
52
53
54static int setup_intr_remapping(struct intel_iommu *iommu, int mode)
55{
56 struct ir_table *ir_table;
57 struct page *pages;
58
59 ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table),
60 GFP_KERNEL);
61
62 if (!iommu->ir_table)
63 return -ENOMEM;
64
65 pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, INTR_REMAP_PAGE_ORDER);
66
67 if (!pages) {
68 printk(KERN_ERR "failed to allocate pages of order %d\n",
69 INTR_REMAP_PAGE_ORDER);
70 kfree(iommu->ir_table);
71 return -ENOMEM;
72 }
73
74 ir_table->base = page_address(pages);
75
76 iommu_set_intr_remapping(iommu, mode);
77 return 0;
78}
79
80int __init enable_intr_remapping(int eim)
81{
82 struct dmar_drhd_unit *drhd;
83 int setup = 0;
84
85 /*
86 * check for the Interrupt-remapping support
87 */
88 for_each_drhd_unit(drhd) {
89 struct intel_iommu *iommu = drhd->iommu;
90
91 if (!ecap_ir_support(iommu->ecap))
92 continue;
93
94 if (eim && !ecap_eim_support(iommu->ecap)) {
95 printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, "
96 " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap);
97 return -1;
98 }
99 }
100
101 /*
102 * Enable queued invalidation for all the DRHD's.
103 */
104 for_each_drhd_unit(drhd) {
105 int ret;
106 struct intel_iommu *iommu = drhd->iommu;
107 ret = dmar_enable_qi(iommu);
108
109 if (ret) {
110 printk(KERN_ERR "DRHD %Lx: failed to enable queued, "
111 " invalidation, ecap %Lx, ret %d\n",
112 drhd->reg_base_addr, iommu->ecap, ret);
113 return -1;
114 }
115 }
116
117 /*
118 * Setup Interrupt-remapping for all the DRHD's now.
119 */
120 for_each_drhd_unit(drhd) {
121 struct intel_iommu *iommu = drhd->iommu;
122
123 if (!ecap_ir_support(iommu->ecap))
124 continue;
125
126 if (setup_intr_remapping(iommu, eim))
127 goto error;
128
129 setup = 1;
130 }
131
132 if (!setup)
133 goto error;
134
135 intr_remapping_enabled = 1;
136
137 return 0;
138
139error:
140 /*
141 * handle error condition gracefully here!
142 */
143 return -1;
144}
8 145
9static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, 146static int ir_parse_ioapic_scope(struct acpi_dmar_header *header,
10 struct intel_iommu *iommu) 147 struct intel_iommu *iommu)
diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h
index c4a40b2f33fa..05f2635bbe4e 100644
--- a/drivers/pci/intr_remapping.h
+++ b/drivers/pci/intr_remapping.h
@@ -4,3 +4,5 @@ struct ioapic_scope {
4 struct intel_iommu *iommu; 4 struct intel_iommu *iommu;
5 unsigned int id; 5 unsigned int id;
6}; 6};
7
8#define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
diff --git a/include/linux/dmar.h b/include/linux/dmar.h
index c4e96eb29617..8a0238dd2c11 100644
--- a/include/linux/dmar.h
+++ b/include/linux/dmar.h
@@ -25,9 +25,85 @@
25#include <linux/types.h> 25#include <linux/types.h>
26#include <linux/msi.h> 26#include <linux/msi.h>
27 27
28#ifdef CONFIG_DMAR 28#if defined(CONFIG_DMAR) || defined(CONFIG_INTR_REMAP)
29struct intel_iommu; 29struct intel_iommu;
30 30
31struct dmar_drhd_unit {
32 struct list_head list; /* list of drhd units */
33 struct acpi_dmar_header *hdr; /* ACPI header */
34 u64 reg_base_addr; /* register base address*/
35 struct pci_dev **devices; /* target device array */
36 int devices_cnt; /* target device count */
37 u8 ignored:1; /* ignore drhd */
38 u8 include_all:1;
39 struct intel_iommu *iommu;
40};
41
42extern struct list_head dmar_drhd_units;
43
44#define for_each_drhd_unit(drhd) \
45 list_for_each_entry(drhd, &dmar_drhd_units, list)
46
47extern int dmar_table_init(void);
48extern int early_dmar_detect(void);
49extern int dmar_dev_scope_init(void);
50
51/* Intel IOMMU detection */
52extern void detect_intel_iommu(void);
53
54
55extern int parse_ioapics_under_ir(void);
56extern int alloc_iommu(struct dmar_drhd_unit *);
57#else
58static inline void detect_intel_iommu(void)
59{
60 return;
61}
62
63static inline int dmar_table_init(void)
64{
65 return -ENODEV;
66}
67#endif /* !CONFIG_DMAR && !CONFIG_INTR_REMAP */
68
69#ifdef CONFIG_INTR_REMAP
70extern int intr_remapping_enabled;
71extern int enable_intr_remapping(int);
72
73struct irte {
74 union {
75 struct {
76 __u64 present : 1,
77 fpd : 1,
78 dst_mode : 1,
79 redir_hint : 1,
80 trigger_mode : 1,
81 dlvry_mode : 3,
82 avail : 4,
83 __reserved_1 : 4,
84 vector : 8,
85 __reserved_2 : 8,
86 dest_id : 32;
87 };
88 __u64 low;
89 };
90
91 union {
92 struct {
93 __u64 sid : 16,
94 sq : 2,
95 svt : 2,
96 __reserved_3 : 44;
97 };
98 __u64 high;
99 };
100};
101#else
102#define enable_intr_remapping(mode) (-1)
103#define intr_remapping_enabled (0)
104#endif
105
106#ifdef CONFIG_DMAR
31extern const char *dmar_get_fault_reason(u8 fault_reason); 107extern const char *dmar_get_fault_reason(u8 fault_reason);
32 108
33/* Can't use the common MSI interrupt functions 109/* Can't use the common MSI interrupt functions
@@ -40,29 +116,8 @@ extern void dmar_msi_write(int irq, struct msi_msg *msg);
40extern int dmar_set_interrupt(struct intel_iommu *iommu); 116extern int dmar_set_interrupt(struct intel_iommu *iommu);
41extern int arch_setup_dmar_msi(unsigned int irq); 117extern int arch_setup_dmar_msi(unsigned int irq);
42 118
43/* Intel IOMMU detection and initialization functions */ 119extern int iommu_detected, no_iommu;
44extern void detect_intel_iommu(void);
45extern int intel_iommu_init(void);
46
47extern int dmar_table_init(void);
48extern int early_dmar_detect(void);
49extern int dmar_dev_scope_init(void);
50extern int parse_ioapics_under_ir(void);
51
52extern struct list_head dmar_drhd_units;
53extern struct list_head dmar_rmrr_units; 120extern struct list_head dmar_rmrr_units;
54
55struct dmar_drhd_unit {
56 struct list_head list; /* list of drhd units */
57 struct acpi_dmar_header *hdr; /* ACPI header */
58 u64 reg_base_addr; /* register base address*/
59 struct pci_dev **devices; /* target device array */
60 int devices_cnt; /* target device count */
61 u8 ignored:1; /* ignore drhd */
62 u8 include_all:1;
63 struct intel_iommu *iommu;
64};
65
66struct dmar_rmrr_unit { 121struct dmar_rmrr_unit {
67 struct list_head list; /* list of rmrr units */ 122 struct list_head list; /* list of rmrr units */
68 struct acpi_dmar_header *hdr; /* ACPI header */ 123 struct acpi_dmar_header *hdr; /* ACPI header */
@@ -72,24 +127,19 @@ struct dmar_rmrr_unit {
72 int devices_cnt; /* target device count */ 127 int devices_cnt; /* target device count */
73}; 128};
74 129
75#define for_each_drhd_unit(drhd) \
76 list_for_each_entry(drhd, &dmar_drhd_units, list)
77#define for_each_rmrr_units(rmrr) \ 130#define for_each_rmrr_units(rmrr) \
78 list_for_each_entry(rmrr, &dmar_rmrr_units, list) 131 list_for_each_entry(rmrr, &dmar_rmrr_units, list)
79 132/* Intel DMAR initialization functions */
80extern int alloc_iommu(struct dmar_drhd_unit *); 133extern int intel_iommu_init(void);
134extern int dmar_disabled;
81#else 135#else
82static inline void detect_intel_iommu(void)
83{
84 return;
85}
86static inline int intel_iommu_init(void) 136static inline int intel_iommu_init(void)
87{ 137{
138#ifdef CONFIG_INTR_REMAP
139 return dmar_dev_scope_init();
140#else
88 return -ENODEV; 141 return -ENODEV;
89} 142#endif
90static inline int dmar_table_init(void)
91{
92 return -ENODEV;
93} 143}
94#endif /* !CONFIG_DMAR */ 144#endif /* !CONFIG_DMAR */
95#endif /* __DMAR_H__ */ 145#endif /* __DMAR_H__ */