diff options
author | Suresh Siddha <suresh.b.siddha@intel.com> | 2008-07-10 14:16:43 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-12 02:44:53 -0400 |
commit | 2ae21010694e56461a63bfc80e960090ce0a5ed9 (patch) | |
tree | d4ecdb710c4361df473b063eda9e1426fcf5c309 /drivers/pci | |
parent | fe962e90cb17a8426e144dee970e77ed789d98ee (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>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/dma_remapping.h | 2 | ||||
-rw-r--r-- | drivers/pci/dmar.c | 16 | ||||
-rw-r--r-- | drivers/pci/intel-iommu.c | 21 | ||||
-rw-r--r-- | drivers/pci/intel-iommu.h | 24 | ||||
-rw-r--r-- | drivers/pci/intr_remapping.c | 137 | ||||
-rw-r--r-- | drivers/pci/intr_remapping.h | 2 |
6 files changed, 187 insertions, 15 deletions
diff --git a/drivers/pci/dma_remapping.h b/drivers/pci/dma_remapping.h index 05aac8ef96c..bff5c65f81d 100644 --- a/drivers/pci/dma_remapping.h +++ b/drivers/pci/dma_remapping.h | |||
@@ -145,6 +145,8 @@ struct device_domain_info { | |||
145 | extern int init_dmars(void); | 145 | extern int init_dmars(void); |
146 | extern void free_dmar_iommu(struct intel_iommu *iommu); | 146 | extern void free_dmar_iommu(struct intel_iommu *iommu); |
147 | 147 | ||
148 | extern int dmar_disabled; | ||
149 | |||
148 | #ifndef CONFIG_DMAR_GFX_WA | 150 | #ifndef CONFIG_DMAR_GFX_WA |
149 | static inline void iommu_prepare_gfx_mapping(void) | 151 | static inline void iommu_prepare_gfx_mapping(void) |
150 | { | 152 | { |
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index aba151ca6d2..23a119e6485 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 | ||
452 | void __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 | |||
452 | int alloc_iommu(struct dmar_drhd_unit *drhd) | 468 | int 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 347bf2e4716..ffccf2341b9 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c | |||
@@ -76,7 +76,7 @@ static long list_size; | |||
76 | 76 | ||
77 | static void domain_remove_dev_info(struct dmar_domain *domain); | 77 | static void domain_remove_dev_info(struct dmar_domain *domain); |
78 | 78 | ||
79 | static int dmar_disabled; | 79 | int dmar_disabled; |
80 | static int __initdata dmar_map_gfx = 1; | 80 | static int __initdata dmar_map_gfx = 1; |
81 | static int dmar_forcedac; | 81 | static int dmar_forcedac; |
82 | static int intel_iommu_strict; | 82 | static int intel_iommu_strict; |
@@ -2238,15 +2238,6 @@ static void __init iommu_exit_mempool(void) | |||
2238 | 2238 | ||
2239 | } | 2239 | } |
2240 | 2240 | ||
2241 | void __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 | |||
2250 | static void __init init_no_remapping_devices(void) | 2241 | static 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 2983ce89535..a81a74e2bd9 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 | |||
258 | struct ir_table { | ||
259 | struct irte *base; | ||
260 | }; | ||
261 | #endif | ||
262 | |||
248 | struct intel_iommu { | 263 | struct 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 | ||
271 | static inline void __iommu_flush_cache( | 289 | static 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 | ||
280 | extern int alloc_iommu(struct dmar_drhd_unit *drhd); | 298 | extern int alloc_iommu(struct dmar_drhd_unit *drhd); |
281 | extern void free_iommu(struct intel_iommu *iommu); | 299 | extern void free_iommu(struct intel_iommu *iommu); |
300 | extern int dmar_enable_qi(struct intel_iommu *iommu); | ||
301 | extern 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 a80b87921c6..3d10cdc9031 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 | ||
6 | static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; | 9 | static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; |
7 | static int ir_ioapic_num; | 10 | static int ir_ioapic_num; |
11 | int intr_remapping_enabled; | ||
12 | |||
13 | static 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 | |||
54 | static 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 | |||
80 | int __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 | |||
139 | error: | ||
140 | /* | ||
141 | * handle error condition gracefully here! | ||
142 | */ | ||
143 | return -1; | ||
144 | } | ||
8 | 145 | ||
9 | static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, | 146 | static 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 c4a40b2f33f..05f2635bbe4 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) | ||