Blame view

drivers/pci/intr_remapping.c 16.8 KB
5aeecaf49   Yinghai Lu   irq: make irq2_io...
1
  #include <linux/interrupt.h>
ad3ad3f6a   Suresh Siddha   x64, x2apic/intr-...
2
  #include <linux/dmar.h>
2ae210106   Suresh Siddha   x64, x2apic/intr-...
3
  #include <linux/spinlock.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
4
  #include <linux/slab.h>
2ae210106   Suresh Siddha   x64, x2apic/intr-...
5
  #include <linux/jiffies.h>
20f3097bf   Suresh Siddha   intr-remap: gener...
6
  #include <linux/hpet.h>
2ae210106   Suresh Siddha   x64, x2apic/intr-...
7
  #include <linux/pci.h>
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
8
  #include <linux/irq.h>
ad3ad3f6a   Suresh Siddha   x64, x2apic/intr-...
9
  #include <asm/io_apic.h>
17483a1f3   Yinghai Lu   sparseirq: fix !S...
10
  #include <asm/smp.h>
6d652ea1d   Jaswinder Singh Rajput   x86: smp.h move b...
11
  #include <asm/cpu.h>
387179464   Kay, Allen M   VT-d: Changes to ...
12
  #include <linux/intel-iommu.h>
ad3ad3f6a   Suresh Siddha   x64, x2apic/intr-...
13
  #include "intr_remapping.h"
46f06b723   Alexander Beregalov   drivers/pci/intr_...
14
  #include <acpi/acpi.h>
f007e99c8   Weidong Han   Intel-IOMMU, intr...
15
16
  #include <asm/pci-direct.h>
  #include "pci.h"
ad3ad3f6a   Suresh Siddha   x64, x2apic/intr-...
17
18
  
  static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
20f3097bf   Suresh Siddha   intr-remap: gener...
19
20
  static struct hpet_scope ir_hpet[MAX_HPET_TBS];
  static int ir_ioapic_num, ir_hpet_num;
2ae210106   Suresh Siddha   x64, x2apic/intr-...
21
  int intr_remapping_enabled;
03ea81550   Weidong Han   x86, intr-remap: ...
22
  static int disable_intremap;
d1423d567   Chris Wright   intr-remap: allow...
23
  static int disable_sourceid_checking;
03ea81550   Weidong Han   x86, intr-remap: ...
24
25
26
27
28
29
  static __init int setup_nointremap(char *str)
  {
  	disable_intremap = 1;
  	return 0;
  }
  early_param("nointremap", setup_nointremap);
d1423d567   Chris Wright   intr-remap: allow...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  static __init int setup_intremap(char *str)
  {
  	if (!str)
  		return -EINVAL;
  
  	if (!strncmp(str, "on", 2))
  		disable_intremap = 0;
  	else if (!strncmp(str, "off", 3))
  		disable_intremap = 1;
  	else if (!strncmp(str, "nosid", 5))
  		disable_sourceid_checking = 1;
  
  	return 0;
  }
  early_param("intremap", setup_intremap);
d585d060b   Thomas Gleixner   intr_remap: Simpl...
45
  static DEFINE_SPINLOCK(irq_2_ir_lock);
e420dfb40   Yinghai Lu   x86: put irq_2_io...
46
47
  static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
  {
dced35aeb   Thomas Gleixner   drivers: Final ir...
48
  	struct irq_cfg *cfg = irq_get_chip_data(irq);
349d67673   Thomas Gleixner   intr_remap: Use i...
49
  	return cfg ? &cfg->irq_2_iommu : NULL;
0b8f1efad   Yinghai Lu   sparse irq_desc[]...
50
  }
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
51
52
  int get_irte(int irq, struct irte *entry)
  {
d585d060b   Thomas Gleixner   intr_remap: Simpl...
53
  	struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
54
  	unsigned long flags;
d585d060b   Thomas Gleixner   intr_remap: Simpl...
55
  	int index;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
56

d585d060b   Thomas Gleixner   intr_remap: Simpl...
57
  	if (!entry || !irq_iommu)
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
58
  		return -1;
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
59
  	spin_lock_irqsave(&irq_2_ir_lock, flags);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
60

e420dfb40   Yinghai Lu   x86: put irq_2_io...
61
62
  	index = irq_iommu->irte_index + irq_iommu->sub_handle;
  	*entry = *(irq_iommu->iommu->ir_table->base + index);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
63

4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
64
  	spin_unlock_irqrestore(&irq_2_ir_lock, flags);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
65
66
67
68
69
70
  	return 0;
  }
  
  int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
  {
  	struct ir_table *table = iommu->ir_table;
d585d060b   Thomas Gleixner   intr_remap: Simpl...
71
  	struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
72
73
  	u16 index, start_index;
  	unsigned int mask = 0;
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
74
  	unsigned long flags;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
75
  	int i;
d585d060b   Thomas Gleixner   intr_remap: Simpl...
76
  	if (!count || !irq_iommu)
e420dfb40   Yinghai Lu   x86: put irq_2_io...
77
  		return -1;
e420dfb40   Yinghai Lu   x86: put irq_2_io...
78

b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  	/*
  	 * start the IRTE search from index 0.
  	 */
  	index = start_index = 0;
  
  	if (count > 1) {
  		count = __roundup_pow_of_two(count);
  		mask = ilog2(count);
  	}
  
  	if (mask > ecap_max_handle_mask(iommu->ecap)) {
  		printk(KERN_ERR
  		       "Requested mask %x exceeds the max invalidation handle"
  		       " mask value %Lx
  ", mask,
  		       ecap_max_handle_mask(iommu->ecap));
  		return -1;
  	}
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
97
  	spin_lock_irqsave(&irq_2_ir_lock, flags);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
98
99
100
101
102
103
104
105
106
107
108
  	do {
  		for (i = index; i < index + count; i++)
  			if  (table->base[i].present)
  				break;
  		/* empty index found */
  		if (i == index + count)
  			break;
  
  		index = (index + count) % INTR_REMAP_TABLE_ENTRIES;
  
  		if (index == start_index) {
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
109
  			spin_unlock_irqrestore(&irq_2_ir_lock, flags);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
110
111
112
113
114
115
116
117
  			printk(KERN_ERR "can't allocate an IRTE
  ");
  			return -1;
  		}
  	} while (1);
  
  	for (i = index; i < index + count; i++)
  		table->base[i].present = 1;
e420dfb40   Yinghai Lu   x86: put irq_2_io...
118
119
120
121
  	irq_iommu->iommu = iommu;
  	irq_iommu->irte_index =  index;
  	irq_iommu->sub_handle = 0;
  	irq_iommu->irte_mask = mask;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
122

4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
123
  	spin_unlock_irqrestore(&irq_2_ir_lock, flags);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
124
125
126
  
  	return index;
  }
704126ad8   Yu Zhao   VT-d: handle Inva...
127
  static int qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
128
129
130
131
132
133
  {
  	struct qi_desc desc;
  
  	desc.low = QI_IEC_IIDEX(index) | QI_IEC_TYPE | QI_IEC_IM(mask)
  		   | QI_IEC_SELECTIVE;
  	desc.high = 0;
704126ad8   Yu Zhao   VT-d: handle Inva...
134
  	return qi_submit_sync(&desc, iommu);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
135
136
137
138
  }
  
  int map_irq_to_irte_handle(int irq, u16 *sub_handle)
  {
d585d060b   Thomas Gleixner   intr_remap: Simpl...
139
  	struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
140
  	unsigned long flags;
d585d060b   Thomas Gleixner   intr_remap: Simpl...
141
  	int index;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
142

d585d060b   Thomas Gleixner   intr_remap: Simpl...
143
  	if (!irq_iommu)
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
144
  		return -1;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
145

d585d060b   Thomas Gleixner   intr_remap: Simpl...
146
  	spin_lock_irqsave(&irq_2_ir_lock, flags);
e420dfb40   Yinghai Lu   x86: put irq_2_io...
147
148
  	*sub_handle = irq_iommu->sub_handle;
  	index = irq_iommu->irte_index;
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
149
  	spin_unlock_irqrestore(&irq_2_ir_lock, flags);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
150
151
152
153
154
  	return index;
  }
  
  int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle)
  {
d585d060b   Thomas Gleixner   intr_remap: Simpl...
155
  	struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
156
  	unsigned long flags;
e420dfb40   Yinghai Lu   x86: put irq_2_io...
157

d585d060b   Thomas Gleixner   intr_remap: Simpl...
158
  	if (!irq_iommu)
0b8f1efad   Yinghai Lu   sparse irq_desc[]...
159
  		return -1;
d585d060b   Thomas Gleixner   intr_remap: Simpl...
160
161
  
  	spin_lock_irqsave(&irq_2_ir_lock, flags);
0b8f1efad   Yinghai Lu   sparse irq_desc[]...
162

e420dfb40   Yinghai Lu   x86: put irq_2_io...
163
164
165
166
  	irq_iommu->iommu = iommu;
  	irq_iommu->irte_index = index;
  	irq_iommu->sub_handle = subhandle;
  	irq_iommu->irte_mask = 0;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
167

4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
168
  	spin_unlock_irqrestore(&irq_2_ir_lock, flags);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
169
170
171
  
  	return 0;
  }
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
172
173
  int modify_irte(int irq, struct irte *irte_modified)
  {
d585d060b   Thomas Gleixner   intr_remap: Simpl...
174
  	struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
175
  	struct intel_iommu *iommu;
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
176
  	unsigned long flags;
d585d060b   Thomas Gleixner   intr_remap: Simpl...
177
178
  	struct irte *irte;
  	int rc, index;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
179

d585d060b   Thomas Gleixner   intr_remap: Simpl...
180
  	if (!irq_iommu)
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
181
  		return -1;
d585d060b   Thomas Gleixner   intr_remap: Simpl...
182
183
  
  	spin_lock_irqsave(&irq_2_ir_lock, flags);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
184

e420dfb40   Yinghai Lu   x86: put irq_2_io...
185
  	iommu = irq_iommu->iommu;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
186

e420dfb40   Yinghai Lu   x86: put irq_2_io...
187
  	index = irq_iommu->irte_index + irq_iommu->sub_handle;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
188
  	irte = &iommu->ir_table->base[index];
c513b67e6   Linus Torvalds   pci: fix type war...
189
190
  	set_64bit(&irte->low, irte_modified->low);
  	set_64bit(&irte->high, irte_modified->high);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
191
  	__iommu_flush_cache(iommu, irte, sizeof(*irte));
704126ad8   Yu Zhao   VT-d: handle Inva...
192
  	rc = qi_flush_iec(iommu, index, 0);
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
193
  	spin_unlock_irqrestore(&irq_2_ir_lock, flags);
704126ad8   Yu Zhao   VT-d: handle Inva...
194
195
  
  	return rc;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
196
  }
20f3097bf   Suresh Siddha   intr-remap: gener...
197
198
199
200
201
202
203
204
205
  struct intel_iommu *map_hpet_to_ir(u8 hpet_id)
  {
  	int i;
  
  	for (i = 0; i < MAX_HPET_TBS; i++)
  		if (ir_hpet[i].id == hpet_id)
  			return ir_hpet[i].iommu;
  	return NULL;
  }
89027d35a   Suresh Siddha   x64, x2apic/intr-...
206
207
208
209
210
211
212
213
214
  struct intel_iommu *map_ioapic_to_ir(int apic)
  {
  	int i;
  
  	for (i = 0; i < MAX_IO_APICS; i++)
  		if (ir_ioapic[i].id == apic)
  			return ir_ioapic[i].iommu;
  	return NULL;
  }
75c46fa61   Suresh Siddha   x64, x2apic/intr-...
215
216
217
218
219
220
221
222
223
224
  struct intel_iommu *map_dev_to_ir(struct pci_dev *dev)
  {
  	struct dmar_drhd_unit *drhd;
  
  	drhd = dmar_find_matched_drhd_unit(dev);
  	if (!drhd)
  		return NULL;
  
  	return drhd->iommu;
  }
c4658b4e7   Weidong Han   Intel-IOMMU, intr...
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
  static int clear_entries(struct irq_2_iommu *irq_iommu)
  {
  	struct irte *start, *entry, *end;
  	struct intel_iommu *iommu;
  	int index;
  
  	if (irq_iommu->sub_handle)
  		return 0;
  
  	iommu = irq_iommu->iommu;
  	index = irq_iommu->irte_index + irq_iommu->sub_handle;
  
  	start = iommu->ir_table->base + index;
  	end = start + (1 << irq_iommu->irte_mask);
  
  	for (entry = start; entry < end; entry++) {
c513b67e6   Linus Torvalds   pci: fix type war...
241
242
  		set_64bit(&entry->low, 0);
  		set_64bit(&entry->high, 0);
c4658b4e7   Weidong Han   Intel-IOMMU, intr...
243
244
245
246
  	}
  
  	return qi_flush_iec(iommu, index, irq_iommu->irte_mask);
  }
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
247
248
  int free_irte(int irq)
  {
d585d060b   Thomas Gleixner   intr_remap: Simpl...
249
  	struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
250
  	unsigned long flags;
d585d060b   Thomas Gleixner   intr_remap: Simpl...
251
  	int rc;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
252

d585d060b   Thomas Gleixner   intr_remap: Simpl...
253
  	if (!irq_iommu)
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
254
  		return -1;
d585d060b   Thomas Gleixner   intr_remap: Simpl...
255
256
  
  	spin_lock_irqsave(&irq_2_ir_lock, flags);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
257

c4658b4e7   Weidong Han   Intel-IOMMU, intr...
258
  	rc = clear_entries(irq_iommu);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
259

e420dfb40   Yinghai Lu   x86: put irq_2_io...
260
261
262
263
  	irq_iommu->iommu = NULL;
  	irq_iommu->irte_index = 0;
  	irq_iommu->sub_handle = 0;
  	irq_iommu->irte_mask = 0;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
264

4c5502b1c   Suresh Siddha   x86, x2apic: fix ...
265
  	spin_unlock_irqrestore(&irq_2_ir_lock, flags);
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
266

704126ad8   Yu Zhao   VT-d: handle Inva...
267
  	return rc;
b6fcb33ad   Suresh Siddha   x64, x2apic/intr-...
268
  }
f007e99c8   Weidong Han   Intel-IOMMU, intr...
269
270
271
272
  /*
   * source validation type
   */
  #define SVT_NO_VERIFY		0x0  /* no verification is required */
25985edce   Lucas De Marchi   Fix common misspe...
273
  #define SVT_VERIFY_SID_SQ	0x1  /* verify using SID and SQ fields */
f007e99c8   Weidong Han   Intel-IOMMU, intr...
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  #define SVT_VERIFY_BUS		0x2  /* verify bus of request-id */
  
  /*
   * source-id qualifier
   */
  #define SQ_ALL_16	0x0  /* verify all 16 bits of request-id */
  #define SQ_13_IGNORE_1	0x1  /* verify most significant 13 bits, ignore
  			      * the third least significant bit
  			      */
  #define SQ_13_IGNORE_2	0x2  /* verify most significant 13 bits, ignore
  			      * the second and third least significant bits
  			      */
  #define SQ_13_IGNORE_3	0x3  /* verify most significant 13 bits, ignore
  			      * the least three significant bits
  			      */
  
  /*
   * set SVT, SQ and SID fields of irte to verify
   * source ids of interrupt requests
   */
  static void set_irte_sid(struct irte *irte, unsigned int svt,
  			 unsigned int sq, unsigned int sid)
  {
d1423d567   Chris Wright   intr-remap: allow...
297
298
  	if (disable_sourceid_checking)
  		svt = SVT_NO_VERIFY;
f007e99c8   Weidong Han   Intel-IOMMU, intr...
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  	irte->svt = svt;
  	irte->sq = sq;
  	irte->sid = sid;
  }
  
  int set_ioapic_sid(struct irte *irte, int apic)
  {
  	int i;
  	u16 sid = 0;
  
  	if (!irte)
  		return -1;
  
  	for (i = 0; i < MAX_IO_APICS; i++) {
  		if (ir_ioapic[i].id == apic) {
  			sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn;
  			break;
  		}
  	}
  
  	if (sid == 0) {
  		pr_warning("Failed to set source-id of IOAPIC (%d)
  ", apic);
  		return -1;
  	}
  
  	set_irte_sid(irte, 1, 0, sid);
  
  	return 0;
  }
20f3097bf   Suresh Siddha   intr-remap: gener...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
  int set_hpet_sid(struct irte *irte, u8 id)
  {
  	int i;
  	u16 sid = 0;
  
  	if (!irte)
  		return -1;
  
  	for (i = 0; i < MAX_HPET_TBS; i++) {
  		if (ir_hpet[i].id == id) {
  			sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn;
  			break;
  		}
  	}
  
  	if (sid == 0) {
  		pr_warning("Failed to set source-id of HPET block (%d)
  ", id);
  		return -1;
  	}
  
  	/*
  	 * Should really use SQ_ALL_16. Some platforms are broken.
  	 * While we figure out the right quirks for these broken platforms, use
  	 * SQ_13_IGNORE_3 for now.
  	 */
  	set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_13_IGNORE_3, sid);
  
  	return 0;
  }
f007e99c8   Weidong Han   Intel-IOMMU, intr...
359
360
361
362
363
364
365
366
  int set_msi_sid(struct irte *irte, struct pci_dev *dev)
  {
  	struct pci_dev *bridge;
  
  	if (!irte || !dev)
  		return -1;
  
  	/* PCIe device or Root Complex integrated PCI device */
5f4d91a12   Kenji Kaneshige   PCI: use pci_is_p...
367
  	if (pci_is_pcie(dev) || !dev->bus->parent) {
f007e99c8   Weidong Han   Intel-IOMMU, intr...
368
369
370
371
372
373
374
  		set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
  			     (dev->bus->number << 8) | dev->devfn);
  		return 0;
  	}
  
  	bridge = pci_find_upstream_pcie_bridge(dev);
  	if (bridge) {
45e829ea4   Stefan Assmann   PCI: change PCI n...
375
  		if (pci_is_pcie(bridge))/* this is a PCIe-to-PCI/PCIX bridge */
f007e99c8   Weidong Han   Intel-IOMMU, intr...
376
377
378
379
380
381
382
383
384
  			set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
  				(bridge->bus->number << 8) | dev->bus->number);
  		else /* this is a legacy PCI bridge */
  			set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
  				(bridge->bus->number << 8) | bridge->devfn);
  	}
  
  	return 0;
  }
2ae210106   Suresh Siddha   x64, x2apic/intr-...
385
386
387
  static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
  {
  	u64 addr;
c416daa98   David Woodhouse   intel-iommu: Tidy...
388
  	u32 sts;
2ae210106   Suresh Siddha   x64, x2apic/intr-...
389
390
391
392
393
394
395
396
397
398
  	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 */
161fde083   Han, Weidong   intel-iommu: set ...
399
  	iommu->gcmd |= DMA_GCMD_SIRTP;
c416daa98   David Woodhouse   intel-iommu: Tidy...
400
  	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
2ae210106   Suresh Siddha   x64, x2apic/intr-...
401
402
403
404
405
406
407
408
409
410
411
412
413
414
  
  	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 */
2ae210106   Suresh Siddha   x64, x2apic/intr-...
415
  	iommu->gcmd |= DMA_GCMD_IRE;
c416daa98   David Woodhouse   intel-iommu: Tidy...
416
  	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
2ae210106   Suresh Siddha   x64, x2apic/intr-...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
  
  	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),
fa4b57cc0   Suresh Siddha   x86, dmar: use at...
431
  					     GFP_ATOMIC);
2ae210106   Suresh Siddha   x64, x2apic/intr-...
432
433
434
  
  	if (!iommu->ir_table)
  		return -ENOMEM;
824cd75bf   Suresh Siddha   intr_remap: Alloc...
435
436
  	pages = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO,
  				 INTR_REMAP_PAGE_ORDER);
2ae210106   Suresh Siddha   x64, x2apic/intr-...
437
438
439
440
441
442
443
444
445
446
447
448
449
450
  
  	if (!pages) {
  		printk(KERN_ERR "failed to allocate pages of order %d
  ",
  		       INTR_REMAP_PAGE_ORDER);
  		kfree(iommu->ir_table);
  		return -ENOMEM;
  	}
  
  	ir_table->base = page_address(pages);
  
  	iommu_set_intr_remapping(iommu, mode);
  	return 0;
  }
eba67e5da   Suresh Siddha   x86, dmar: routin...
451
452
453
  /*
   * Disable Interrupt Remapping.
   */
b24696bc5   Fenghua Yu   Intel IOMMU Suspe...
454
  static void iommu_disable_intr_remapping(struct intel_iommu *iommu)
eba67e5da   Suresh Siddha   x86, dmar: routin...
455
456
457
458
459
460
  {
  	unsigned long flags;
  	u32 sts;
  
  	if (!ecap_ir_support(iommu->ecap))
  		return;
b24696bc5   Fenghua Yu   Intel IOMMU Suspe...
461
462
463
464
465
  	/*
  	 * global invalidation of interrupt entry cache before disabling
  	 * interrupt-remapping.
  	 */
  	qi_global_iec(iommu);
eba67e5da   Suresh Siddha   x86, dmar: routin...
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
  	spin_lock_irqsave(&iommu->register_lock, flags);
  
  	sts = dmar_readq(iommu->reg + DMAR_GSTS_REG);
  	if (!(sts & DMA_GSTS_IRES))
  		goto end;
  
  	iommu->gcmd &= ~DMA_GCMD_IRE;
  	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
  
  	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
  		      readl, !(sts & DMA_GSTS_IRES), sts);
  
  end:
  	spin_unlock_irqrestore(&iommu->register_lock, flags);
  }
937582382   Weidong Han   x86, intr-remap: ...
481
482
483
  int __init intr_remapping_supported(void)
  {
  	struct dmar_drhd_unit *drhd;
03ea81550   Weidong Han   x86, intr-remap: ...
484
485
  	if (disable_intremap)
  		return 0;
074835f01   Youquan Song   intel-iommu: Fix ...
486
487
  	if (!dmar_ir_support())
  		return 0;
937582382   Weidong Han   x86, intr-remap: ...
488
489
490
491
492
493
494
495
496
  	for_each_drhd_unit(drhd) {
  		struct intel_iommu *iommu = drhd->iommu;
  
  		if (!ecap_ir_support(iommu->ecap))
  			return 0;
  	}
  
  	return 1;
  }
2ae210106   Suresh Siddha   x64, x2apic/intr-...
497
498
499
500
  int __init enable_intr_remapping(int eim)
  {
  	struct dmar_drhd_unit *drhd;
  	int setup = 0;
e936d0773   Youquan Song   intel-iommu: Disa...
501
502
503
504
505
  	if (parse_ioapics_under_ir() != 1) {
  		printk(KERN_INFO "Not enable interrupt remapping
  ");
  		return -1;
  	}
1531a6a6b   Suresh Siddha   x86, dmar: start ...
506
507
508
509
  	for_each_drhd_unit(drhd) {
  		struct intel_iommu *iommu = drhd->iommu;
  
  		/*
34aaaa948   Han, Weidong   x86, dmar: check ...
510
511
512
513
514
515
516
  		 * If the queued invalidation is already initialized,
  		 * shouldn't disable it.
  		 */
  		if (iommu->qi)
  			continue;
  
  		/*
1531a6a6b   Suresh Siddha   x86, dmar: start ...
517
518
519
520
521
522
523
524
  		 * Clear previous faults.
  		 */
  		dmar_fault(-1, iommu);
  
  		/*
  		 * Disable intr remapping and queued invalidation, if already
  		 * enabled prior to OS handover.
  		 */
b24696bc5   Fenghua Yu   Intel IOMMU Suspe...
525
  		iommu_disable_intr_remapping(iommu);
1531a6a6b   Suresh Siddha   x86, dmar: start ...
526
527
528
  
  		dmar_disable_qi(iommu);
  	}
2ae210106   Suresh Siddha   x64, x2apic/intr-...
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
  	/*
  	 * 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
  ", 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
  ",
  			       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;
  }
ad3ad3f6a   Suresh Siddha   x64, x2apic/intr-...
591

20f3097bf   Suresh Siddha   intr-remap: gener...
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
  static void ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope,
  				      struct intel_iommu *iommu)
  {
  	struct acpi_dmar_pci_path *path;
  	u8 bus;
  	int count;
  
  	bus = scope->bus;
  	path = (struct acpi_dmar_pci_path *)(scope + 1);
  	count = (scope->length - sizeof(struct acpi_dmar_device_scope))
  		/ sizeof(struct acpi_dmar_pci_path);
  
  	while (--count > 0) {
  		/*
  		 * Access PCI directly due to the PCI
  		 * subsystem isn't initialized yet.
  		 */
  		bus = read_pci_config_byte(bus, path->dev, path->fn,
  					   PCI_SECONDARY_BUS);
  		path++;
  	}
  	ir_hpet[ir_hpet_num].bus   = bus;
  	ir_hpet[ir_hpet_num].devfn = PCI_DEVFN(path->dev, path->fn);
  	ir_hpet[ir_hpet_num].iommu = iommu;
  	ir_hpet[ir_hpet_num].id    = scope->enumeration_id;
  	ir_hpet_num++;
  }
f007e99c8   Weidong Han   Intel-IOMMU, intr...
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
  static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
  				      struct intel_iommu *iommu)
  {
  	struct acpi_dmar_pci_path *path;
  	u8 bus;
  	int count;
  
  	bus = scope->bus;
  	path = (struct acpi_dmar_pci_path *)(scope + 1);
  	count = (scope->length - sizeof(struct acpi_dmar_device_scope))
  		/ sizeof(struct acpi_dmar_pci_path);
  
  	while (--count > 0) {
  		/*
  		 * Access PCI directly due to the PCI
  		 * subsystem isn't initialized yet.
  		 */
  		bus = read_pci_config_byte(bus, path->dev, path->fn,
  					   PCI_SECONDARY_BUS);
  		path++;
  	}
  
  	ir_ioapic[ir_ioapic_num].bus   = bus;
  	ir_ioapic[ir_ioapic_num].devfn = PCI_DEVFN(path->dev, path->fn);
  	ir_ioapic[ir_ioapic_num].iommu = iommu;
  	ir_ioapic[ir_ioapic_num].id    = scope->enumeration_id;
  	ir_ioapic_num++;
  }
20f3097bf   Suresh Siddha   intr-remap: gener...
647
648
  static int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header,
  				      struct intel_iommu *iommu)
ad3ad3f6a   Suresh Siddha   x64, x2apic/intr-...
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
  {
  	struct acpi_dmar_hardware_unit *drhd;
  	struct acpi_dmar_device_scope *scope;
  	void *start, *end;
  
  	drhd = (struct acpi_dmar_hardware_unit *)header;
  
  	start = (void *)(drhd + 1);
  	end = ((void *)drhd) + header->length;
  
  	while (start < end) {
  		scope = start;
  		if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC) {
  			if (ir_ioapic_num == MAX_IO_APICS) {
  				printk(KERN_WARNING "Exceeded Max IO APICS
  ");
  				return -1;
  			}
680a75246   Yinghai Lu   intel-iommu: Prin...
667
668
669
670
  			printk(KERN_INFO "IOAPIC id %d under DRHD base "
  			       " 0x%Lx IOMMU %d
  ", scope->enumeration_id,
  			       drhd->address, iommu->seq_id);
ad3ad3f6a   Suresh Siddha   x64, x2apic/intr-...
671

f007e99c8   Weidong Han   Intel-IOMMU, intr...
672
  			ir_parse_one_ioapic_scope(scope, iommu);
20f3097bf   Suresh Siddha   intr-remap: gener...
673
674
675
676
677
678
679
680
681
682
683
684
685
  		} else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET) {
  			if (ir_hpet_num == MAX_HPET_TBS) {
  				printk(KERN_WARNING "Exceeded Max HPET blocks
  ");
  				return -1;
  			}
  
  			printk(KERN_INFO "HPET id %d under DRHD base"
  			       " 0x%Lx
  ", scope->enumeration_id,
  			       drhd->address);
  
  			ir_parse_one_hpet_scope(scope, iommu);
ad3ad3f6a   Suresh Siddha   x64, x2apic/intr-...
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
  		}
  		start += scope->length;
  	}
  
  	return 0;
  }
  
  /*
   * Finds the assocaition between IOAPIC's and its Interrupt-remapping
   * hardware unit.
   */
  int __init parse_ioapics_under_ir(void)
  {
  	struct dmar_drhd_unit *drhd;
  	int ir_supported = 0;
  
  	for_each_drhd_unit(drhd) {
  		struct intel_iommu *iommu = drhd->iommu;
  
  		if (ecap_ir_support(iommu->ecap)) {
20f3097bf   Suresh Siddha   intr-remap: gener...
706
  			if (ir_parse_ioapic_hpet_scope(drhd->hdr, iommu))
ad3ad3f6a   Suresh Siddha   x64, x2apic/intr-...
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
  				return -1;
  
  			ir_supported = 1;
  		}
  	}
  
  	if (ir_supported && ir_ioapic_num != nr_ioapics) {
  		printk(KERN_WARNING
  		       "Not all IO-APIC's listed under remapping hardware
  ");
  		return -1;
  	}
  
  	return ir_supported;
  }
b24696bc5   Fenghua Yu   Intel IOMMU Suspe...
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
  
  void disable_intr_remapping(void)
  {
  	struct dmar_drhd_unit *drhd;
  	struct intel_iommu *iommu = NULL;
  
  	/*
  	 * Disable Interrupt-remapping for all the DRHD's now.
  	 */
  	for_each_iommu(iommu, drhd) {
  		if (!ecap_ir_support(iommu->ecap))
  			continue;
  
  		iommu_disable_intr_remapping(iommu);
  	}
  }
  
  int reenable_intr_remapping(int eim)
  {
  	struct dmar_drhd_unit *drhd;
  	int setup = 0;
  	struct intel_iommu *iommu = NULL;
  
  	for_each_iommu(iommu, drhd)
  		if (iommu->qi)
  			dmar_reenable_qi(iommu);
  
  	/*
  	 * Setup Interrupt-remapping for all the DRHD's now.
  	 */
  	for_each_iommu(iommu, drhd) {
  		if (!ecap_ir_support(iommu->ecap))
  			continue;
  
  		/* Set up interrupt remapping for iommu.*/
  		iommu_set_intr_remapping(iommu, eim);
  		setup = 1;
  	}
  
  	if (!setup)
  		goto error;
  
  	return 0;
  
  error:
  	/*
  	 * handle error condition gracefully here!
  	 */
  	return -1;
  }