xref: /openbmc/linux/drivers/iommu/intel/irq_remapping.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1672cf6dfSJoerg Roedel // SPDX-License-Identifier: GPL-2.0
2672cf6dfSJoerg Roedel 
3672cf6dfSJoerg Roedel #define pr_fmt(fmt)     "DMAR-IR: " fmt
4672cf6dfSJoerg Roedel 
5672cf6dfSJoerg Roedel #include <linux/interrupt.h>
6672cf6dfSJoerg Roedel #include <linux/dmar.h>
7672cf6dfSJoerg Roedel #include <linux/spinlock.h>
8672cf6dfSJoerg Roedel #include <linux/slab.h>
9672cf6dfSJoerg Roedel #include <linux/jiffies.h>
10672cf6dfSJoerg Roedel #include <linux/hpet.h>
11672cf6dfSJoerg Roedel #include <linux/pci.h>
12672cf6dfSJoerg Roedel #include <linux/irq.h>
13672cf6dfSJoerg Roedel #include <linux/acpi.h>
14672cf6dfSJoerg Roedel #include <linux/irqdomain.h>
15672cf6dfSJoerg Roedel #include <linux/crash_dump.h>
16672cf6dfSJoerg Roedel #include <asm/io_apic.h>
1713c01139SIngo Molnar #include <asm/apic.h>
18672cf6dfSJoerg Roedel #include <asm/smp.h>
19672cf6dfSJoerg Roedel #include <asm/cpu.h>
20672cf6dfSJoerg Roedel #include <asm/irq_remapping.h>
21672cf6dfSJoerg Roedel #include <asm/pci-direct.h>
22672cf6dfSJoerg Roedel 
232585a279SLu Baolu #include "iommu.h"
24672cf6dfSJoerg Roedel #include "../irq_remapping.h"
25ad3d1902SKyung Min Park #include "cap_audit.h"
26672cf6dfSJoerg Roedel 
27672cf6dfSJoerg Roedel enum irq_mode {
28672cf6dfSJoerg Roedel 	IRQ_REMAPPING,
29672cf6dfSJoerg Roedel 	IRQ_POSTING,
30672cf6dfSJoerg Roedel };
31672cf6dfSJoerg Roedel 
32672cf6dfSJoerg Roedel struct ioapic_scope {
33672cf6dfSJoerg Roedel 	struct intel_iommu *iommu;
34672cf6dfSJoerg Roedel 	unsigned int id;
35672cf6dfSJoerg Roedel 	unsigned int bus;	/* PCI bus number */
36672cf6dfSJoerg Roedel 	unsigned int devfn;	/* PCI devfn number */
37672cf6dfSJoerg Roedel };
38672cf6dfSJoerg Roedel 
39672cf6dfSJoerg Roedel struct hpet_scope {
40672cf6dfSJoerg Roedel 	struct intel_iommu *iommu;
41672cf6dfSJoerg Roedel 	u8 id;
42672cf6dfSJoerg Roedel 	unsigned int bus;
43672cf6dfSJoerg Roedel 	unsigned int devfn;
44672cf6dfSJoerg Roedel };
45672cf6dfSJoerg Roedel 
46672cf6dfSJoerg Roedel struct irq_2_iommu {
47672cf6dfSJoerg Roedel 	struct intel_iommu *iommu;
48672cf6dfSJoerg Roedel 	u16 irte_index;
49672cf6dfSJoerg Roedel 	u16 sub_handle;
50672cf6dfSJoerg Roedel 	u8  irte_mask;
51672cf6dfSJoerg Roedel 	enum irq_mode mode;
52672cf6dfSJoerg Roedel };
53672cf6dfSJoerg Roedel 
54672cf6dfSJoerg Roedel struct intel_ir_data {
55672cf6dfSJoerg Roedel 	struct irq_2_iommu			irq_2_iommu;
56672cf6dfSJoerg Roedel 	struct irte				irte_entry;
57672cf6dfSJoerg Roedel 	union {
58672cf6dfSJoerg Roedel 		struct msi_msg			msi_entry;
59672cf6dfSJoerg Roedel 	};
60672cf6dfSJoerg Roedel };
61672cf6dfSJoerg Roedel 
62672cf6dfSJoerg Roedel #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
63672cf6dfSJoerg Roedel #define IRTE_DEST(dest) ((eim_mode) ? dest : dest << 8)
64672cf6dfSJoerg Roedel 
65672cf6dfSJoerg Roedel static int __read_mostly eim_mode;
66672cf6dfSJoerg Roedel static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
67672cf6dfSJoerg Roedel static struct hpet_scope ir_hpet[MAX_HPET_TBS];
68672cf6dfSJoerg Roedel 
69672cf6dfSJoerg Roedel /*
70672cf6dfSJoerg Roedel  * Lock ordering:
71672cf6dfSJoerg Roedel  * ->dmar_global_lock
72672cf6dfSJoerg Roedel  *	->irq_2_ir_lock
73672cf6dfSJoerg Roedel  *		->qi->q_lock
74672cf6dfSJoerg Roedel  *	->iommu->register_lock
75672cf6dfSJoerg Roedel  * Note:
76672cf6dfSJoerg Roedel  * intel_irq_remap_ops.{supported,prepare,enable,disable,reenable} are called
77672cf6dfSJoerg Roedel  * in single-threaded environment with interrupt disabled, so no need to tabke
78672cf6dfSJoerg Roedel  * the dmar_global_lock.
79672cf6dfSJoerg Roedel  */
80672cf6dfSJoerg Roedel DEFINE_RAW_SPINLOCK(irq_2_ir_lock);
81672cf6dfSJoerg Roedel static const struct irq_domain_ops intel_ir_domain_ops;
82672cf6dfSJoerg Roedel 
83672cf6dfSJoerg Roedel static void iommu_disable_irq_remapping(struct intel_iommu *iommu);
84672cf6dfSJoerg Roedel static int __init parse_ioapics_under_ir(void);
85810531a1SThomas Gleixner static const struct msi_parent_ops dmar_msi_parent_ops, virt_dmar_msi_parent_ops;
86672cf6dfSJoerg Roedel 
ir_pre_enabled(struct intel_iommu * iommu)87672cf6dfSJoerg Roedel static bool ir_pre_enabled(struct intel_iommu *iommu)
88672cf6dfSJoerg Roedel {
89672cf6dfSJoerg Roedel 	return (iommu->flags & VTD_FLAG_IRQ_REMAP_PRE_ENABLED);
90672cf6dfSJoerg Roedel }
91672cf6dfSJoerg Roedel 
clear_ir_pre_enabled(struct intel_iommu * iommu)92672cf6dfSJoerg Roedel static void clear_ir_pre_enabled(struct intel_iommu *iommu)
93672cf6dfSJoerg Roedel {
94672cf6dfSJoerg Roedel 	iommu->flags &= ~VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
95672cf6dfSJoerg Roedel }
96672cf6dfSJoerg Roedel 
init_ir_status(struct intel_iommu * iommu)97672cf6dfSJoerg Roedel static void init_ir_status(struct intel_iommu *iommu)
98672cf6dfSJoerg Roedel {
99672cf6dfSJoerg Roedel 	u32 gsts;
100672cf6dfSJoerg Roedel 
101672cf6dfSJoerg Roedel 	gsts = readl(iommu->reg + DMAR_GSTS_REG);
102672cf6dfSJoerg Roedel 	if (gsts & DMA_GSTS_IRES)
103672cf6dfSJoerg Roedel 		iommu->flags |= VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
104672cf6dfSJoerg Roedel }
105672cf6dfSJoerg Roedel 
alloc_irte(struct intel_iommu * iommu,struct irq_2_iommu * irq_iommu,u16 count)106672cf6dfSJoerg Roedel static int alloc_irte(struct intel_iommu *iommu,
107672cf6dfSJoerg Roedel 		      struct irq_2_iommu *irq_iommu, u16 count)
108672cf6dfSJoerg Roedel {
109672cf6dfSJoerg Roedel 	struct ir_table *table = iommu->ir_table;
110672cf6dfSJoerg Roedel 	unsigned int mask = 0;
111672cf6dfSJoerg Roedel 	unsigned long flags;
112672cf6dfSJoerg Roedel 	int index;
113672cf6dfSJoerg Roedel 
114672cf6dfSJoerg Roedel 	if (!count || !irq_iommu)
115672cf6dfSJoerg Roedel 		return -1;
116672cf6dfSJoerg Roedel 
117672cf6dfSJoerg Roedel 	if (count > 1) {
118672cf6dfSJoerg Roedel 		count = __roundup_pow_of_two(count);
119672cf6dfSJoerg Roedel 		mask = ilog2(count);
120672cf6dfSJoerg Roedel 	}
121672cf6dfSJoerg Roedel 
122672cf6dfSJoerg Roedel 	if (mask > ecap_max_handle_mask(iommu->ecap)) {
123672cf6dfSJoerg Roedel 		pr_err("Requested mask %x exceeds the max invalidation handle"
124672cf6dfSJoerg Roedel 		       " mask value %Lx\n", mask,
125672cf6dfSJoerg Roedel 		       ecap_max_handle_mask(iommu->ecap));
126672cf6dfSJoerg Roedel 		return -1;
127672cf6dfSJoerg Roedel 	}
128672cf6dfSJoerg Roedel 
129672cf6dfSJoerg Roedel 	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
130672cf6dfSJoerg Roedel 	index = bitmap_find_free_region(table->bitmap,
131672cf6dfSJoerg Roedel 					INTR_REMAP_TABLE_ENTRIES, mask);
132672cf6dfSJoerg Roedel 	if (index < 0) {
133672cf6dfSJoerg Roedel 		pr_warn("IR%d: can't allocate an IRTE\n", iommu->seq_id);
134672cf6dfSJoerg Roedel 	} else {
135672cf6dfSJoerg Roedel 		irq_iommu->iommu = iommu;
136672cf6dfSJoerg Roedel 		irq_iommu->irte_index =  index;
137672cf6dfSJoerg Roedel 		irq_iommu->sub_handle = 0;
138672cf6dfSJoerg Roedel 		irq_iommu->irte_mask = mask;
139672cf6dfSJoerg Roedel 		irq_iommu->mode = IRQ_REMAPPING;
140672cf6dfSJoerg Roedel 	}
141672cf6dfSJoerg Roedel 	raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
142672cf6dfSJoerg Roedel 
143672cf6dfSJoerg Roedel 	return index;
144672cf6dfSJoerg Roedel }
145672cf6dfSJoerg Roedel 
qi_flush_iec(struct intel_iommu * iommu,int index,int mask)146672cf6dfSJoerg Roedel static int qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
147672cf6dfSJoerg Roedel {
148672cf6dfSJoerg Roedel 	struct qi_desc desc;
149672cf6dfSJoerg Roedel 
150672cf6dfSJoerg Roedel 	desc.qw0 = QI_IEC_IIDEX(index) | QI_IEC_TYPE | QI_IEC_IM(mask)
151672cf6dfSJoerg Roedel 		   | QI_IEC_SELECTIVE;
152672cf6dfSJoerg Roedel 	desc.qw1 = 0;
153672cf6dfSJoerg Roedel 	desc.qw2 = 0;
154672cf6dfSJoerg Roedel 	desc.qw3 = 0;
155672cf6dfSJoerg Roedel 
156672cf6dfSJoerg Roedel 	return qi_submit_sync(iommu, &desc, 1, 0);
157672cf6dfSJoerg Roedel }
158672cf6dfSJoerg Roedel 
modify_irte(struct irq_2_iommu * irq_iommu,struct irte * irte_modified)159672cf6dfSJoerg Roedel static int modify_irte(struct irq_2_iommu *irq_iommu,
160672cf6dfSJoerg Roedel 		       struct irte *irte_modified)
161672cf6dfSJoerg Roedel {
162672cf6dfSJoerg Roedel 	struct intel_iommu *iommu;
163672cf6dfSJoerg Roedel 	unsigned long flags;
164672cf6dfSJoerg Roedel 	struct irte *irte;
165672cf6dfSJoerg Roedel 	int rc, index;
166672cf6dfSJoerg Roedel 
167672cf6dfSJoerg Roedel 	if (!irq_iommu)
168672cf6dfSJoerg Roedel 		return -1;
169672cf6dfSJoerg Roedel 
170672cf6dfSJoerg Roedel 	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
171672cf6dfSJoerg Roedel 
172672cf6dfSJoerg Roedel 	iommu = irq_iommu->iommu;
173672cf6dfSJoerg Roedel 
174672cf6dfSJoerg Roedel 	index = irq_iommu->irte_index + irq_iommu->sub_handle;
175672cf6dfSJoerg Roedel 	irte = &iommu->ir_table->base[index];
176672cf6dfSJoerg Roedel 
177672cf6dfSJoerg Roedel 	if ((irte->pst == 1) || (irte_modified->pst == 1)) {
178672cf6dfSJoerg Roedel 		/*
179672cf6dfSJoerg Roedel 		 * We use cmpxchg16 to atomically update the 128-bit IRTE,
180672cf6dfSJoerg Roedel 		 * and it cannot be updated by the hardware or other processors
181672cf6dfSJoerg Roedel 		 * behind us, so the return value of cmpxchg16 should be the
182672cf6dfSJoerg Roedel 		 * same as the old value.
183672cf6dfSJoerg Roedel 		 */
184b1fe7f2cSPeter Zijlstra 		u128 old = irte->irte;
185b1fe7f2cSPeter Zijlstra 		WARN_ON(!try_cmpxchg128(&irte->irte, &old, irte_modified->irte));
1869ee850acSPeter Zijlstra 	} else {
1879ee850acSPeter Zijlstra 		WRITE_ONCE(irte->low, irte_modified->low);
1889ee850acSPeter Zijlstra 		WRITE_ONCE(irte->high, irte_modified->high);
189672cf6dfSJoerg Roedel 	}
190672cf6dfSJoerg Roedel 	__iommu_flush_cache(iommu, irte, sizeof(*irte));
191672cf6dfSJoerg Roedel 
192672cf6dfSJoerg Roedel 	rc = qi_flush_iec(iommu, index, 0);
193672cf6dfSJoerg Roedel 
194672cf6dfSJoerg Roedel 	/* Update iommu mode according to the IRTE mode */
195672cf6dfSJoerg Roedel 	irq_iommu->mode = irte->pst ? IRQ_POSTING : IRQ_REMAPPING;
196672cf6dfSJoerg Roedel 	raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
197672cf6dfSJoerg Roedel 
198672cf6dfSJoerg Roedel 	return rc;
199672cf6dfSJoerg Roedel }
200672cf6dfSJoerg Roedel 
map_hpet_to_iommu(u8 hpet_id)20179eb3581SDavid Woodhouse static struct intel_iommu *map_hpet_to_iommu(u8 hpet_id)
202672cf6dfSJoerg Roedel {
203672cf6dfSJoerg Roedel 	int i;
204672cf6dfSJoerg Roedel 
20560e5a939SThomas Gleixner 	for (i = 0; i < MAX_HPET_TBS; i++) {
206672cf6dfSJoerg Roedel 		if (ir_hpet[i].id == hpet_id && ir_hpet[i].iommu)
20779eb3581SDavid Woodhouse 			return ir_hpet[i].iommu;
20860e5a939SThomas Gleixner 	}
209672cf6dfSJoerg Roedel 	return NULL;
210672cf6dfSJoerg Roedel }
211672cf6dfSJoerg Roedel 
map_ioapic_to_iommu(int apic)21260e5a939SThomas Gleixner static struct intel_iommu *map_ioapic_to_iommu(int apic)
213672cf6dfSJoerg Roedel {
214672cf6dfSJoerg Roedel 	int i;
215672cf6dfSJoerg Roedel 
21660e5a939SThomas Gleixner 	for (i = 0; i < MAX_IO_APICS; i++) {
217672cf6dfSJoerg Roedel 		if (ir_ioapic[i].id == apic && ir_ioapic[i].iommu)
218672cf6dfSJoerg Roedel 			return ir_ioapic[i].iommu;
21960e5a939SThomas Gleixner 	}
220672cf6dfSJoerg Roedel 	return NULL;
221672cf6dfSJoerg Roedel }
222672cf6dfSJoerg Roedel 
map_dev_to_ir(struct pci_dev * dev)22360e5a939SThomas Gleixner static struct irq_domain *map_dev_to_ir(struct pci_dev *dev)
22460e5a939SThomas Gleixner {
22560e5a939SThomas Gleixner 	struct dmar_drhd_unit *drhd = dmar_find_matched_drhd_unit(dev);
22660e5a939SThomas Gleixner 
2279a945234SThomas Gleixner 	return drhd ? drhd->iommu->ir_domain : NULL;
228672cf6dfSJoerg Roedel }
229672cf6dfSJoerg Roedel 
clear_entries(struct irq_2_iommu * irq_iommu)230672cf6dfSJoerg Roedel static int clear_entries(struct irq_2_iommu *irq_iommu)
231672cf6dfSJoerg Roedel {
232672cf6dfSJoerg Roedel 	struct irte *start, *entry, *end;
233672cf6dfSJoerg Roedel 	struct intel_iommu *iommu;
234672cf6dfSJoerg Roedel 	int index;
235672cf6dfSJoerg Roedel 
236672cf6dfSJoerg Roedel 	if (irq_iommu->sub_handle)
237672cf6dfSJoerg Roedel 		return 0;
238672cf6dfSJoerg Roedel 
239672cf6dfSJoerg Roedel 	iommu = irq_iommu->iommu;
240672cf6dfSJoerg Roedel 	index = irq_iommu->irte_index;
241672cf6dfSJoerg Roedel 
242672cf6dfSJoerg Roedel 	start = iommu->ir_table->base + index;
243672cf6dfSJoerg Roedel 	end = start + (1 << irq_iommu->irte_mask);
244672cf6dfSJoerg Roedel 
245672cf6dfSJoerg Roedel 	for (entry = start; entry < end; entry++) {
2469ee850acSPeter Zijlstra 		WRITE_ONCE(entry->low, 0);
2479ee850acSPeter Zijlstra 		WRITE_ONCE(entry->high, 0);
248672cf6dfSJoerg Roedel 	}
249672cf6dfSJoerg Roedel 	bitmap_release_region(iommu->ir_table->bitmap, index,
250672cf6dfSJoerg Roedel 			      irq_iommu->irte_mask);
251672cf6dfSJoerg Roedel 
252672cf6dfSJoerg Roedel 	return qi_flush_iec(iommu, index, irq_iommu->irte_mask);
253672cf6dfSJoerg Roedel }
254672cf6dfSJoerg Roedel 
255672cf6dfSJoerg Roedel /*
256672cf6dfSJoerg Roedel  * source validation type
257672cf6dfSJoerg Roedel  */
258672cf6dfSJoerg Roedel #define SVT_NO_VERIFY		0x0  /* no verification is required */
259672cf6dfSJoerg Roedel #define SVT_VERIFY_SID_SQ	0x1  /* verify using SID and SQ fields */
260672cf6dfSJoerg Roedel #define SVT_VERIFY_BUS		0x2  /* verify bus of request-id */
261672cf6dfSJoerg Roedel 
262672cf6dfSJoerg Roedel /*
263672cf6dfSJoerg Roedel  * source-id qualifier
264672cf6dfSJoerg Roedel  */
265672cf6dfSJoerg Roedel #define SQ_ALL_16	0x0  /* verify all 16 bits of request-id */
266672cf6dfSJoerg Roedel #define SQ_13_IGNORE_1	0x1  /* verify most significant 13 bits, ignore
267672cf6dfSJoerg Roedel 			      * the third least significant bit
268672cf6dfSJoerg Roedel 			      */
269672cf6dfSJoerg Roedel #define SQ_13_IGNORE_2	0x2  /* verify most significant 13 bits, ignore
270672cf6dfSJoerg Roedel 			      * the second and third least significant bits
271672cf6dfSJoerg Roedel 			      */
272672cf6dfSJoerg Roedel #define SQ_13_IGNORE_3	0x3  /* verify most significant 13 bits, ignore
273672cf6dfSJoerg Roedel 			      * the least three significant bits
274672cf6dfSJoerg Roedel 			      */
275672cf6dfSJoerg Roedel 
276672cf6dfSJoerg Roedel /*
277672cf6dfSJoerg Roedel  * set SVT, SQ and SID fields of irte to verify
278672cf6dfSJoerg Roedel  * source ids of interrupt requests
279672cf6dfSJoerg Roedel  */
set_irte_sid(struct irte * irte,unsigned int svt,unsigned int sq,unsigned int sid)280672cf6dfSJoerg Roedel static void set_irte_sid(struct irte *irte, unsigned int svt,
281672cf6dfSJoerg Roedel 			 unsigned int sq, unsigned int sid)
282672cf6dfSJoerg Roedel {
283672cf6dfSJoerg Roedel 	if (disable_sourceid_checking)
284672cf6dfSJoerg Roedel 		svt = SVT_NO_VERIFY;
285672cf6dfSJoerg Roedel 	irte->svt = svt;
286672cf6dfSJoerg Roedel 	irte->sq = sq;
287672cf6dfSJoerg Roedel 	irte->sid = sid;
288672cf6dfSJoerg Roedel }
289672cf6dfSJoerg Roedel 
290672cf6dfSJoerg Roedel /*
291672cf6dfSJoerg Roedel  * Set an IRTE to match only the bus number. Interrupt requests that reference
292672cf6dfSJoerg Roedel  * this IRTE must have a requester-id whose bus number is between or equal
293672cf6dfSJoerg Roedel  * to the start_bus and end_bus arguments.
294672cf6dfSJoerg Roedel  */
set_irte_verify_bus(struct irte * irte,unsigned int start_bus,unsigned int end_bus)295672cf6dfSJoerg Roedel static void set_irte_verify_bus(struct irte *irte, unsigned int start_bus,
296672cf6dfSJoerg Roedel 				unsigned int end_bus)
297672cf6dfSJoerg Roedel {
298672cf6dfSJoerg Roedel 	set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
299672cf6dfSJoerg Roedel 		     (start_bus << 8) | end_bus);
300672cf6dfSJoerg Roedel }
301672cf6dfSJoerg Roedel 
set_ioapic_sid(struct irte * irte,int apic)302672cf6dfSJoerg Roedel static int set_ioapic_sid(struct irte *irte, int apic)
303672cf6dfSJoerg Roedel {
304672cf6dfSJoerg Roedel 	int i;
305672cf6dfSJoerg Roedel 	u16 sid = 0;
306672cf6dfSJoerg Roedel 
307672cf6dfSJoerg Roedel 	if (!irte)
308672cf6dfSJoerg Roedel 		return -1;
309672cf6dfSJoerg Roedel 
310672cf6dfSJoerg Roedel 	for (i = 0; i < MAX_IO_APICS; i++) {
311672cf6dfSJoerg Roedel 		if (ir_ioapic[i].iommu && ir_ioapic[i].id == apic) {
312672cf6dfSJoerg Roedel 			sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn;
313672cf6dfSJoerg Roedel 			break;
314672cf6dfSJoerg Roedel 		}
315672cf6dfSJoerg Roedel 	}
316672cf6dfSJoerg Roedel 
317672cf6dfSJoerg Roedel 	if (sid == 0) {
318672cf6dfSJoerg Roedel 		pr_warn("Failed to set source-id of IOAPIC (%d)\n", apic);
319672cf6dfSJoerg Roedel 		return -1;
320672cf6dfSJoerg Roedel 	}
321672cf6dfSJoerg Roedel 
322672cf6dfSJoerg Roedel 	set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, sid);
323672cf6dfSJoerg Roedel 
324672cf6dfSJoerg Roedel 	return 0;
325672cf6dfSJoerg Roedel }
326672cf6dfSJoerg Roedel 
set_hpet_sid(struct irte * irte,u8 id)327672cf6dfSJoerg Roedel static int set_hpet_sid(struct irte *irte, u8 id)
328672cf6dfSJoerg Roedel {
329672cf6dfSJoerg Roedel 	int i;
330672cf6dfSJoerg Roedel 	u16 sid = 0;
331672cf6dfSJoerg Roedel 
332672cf6dfSJoerg Roedel 	if (!irte)
333672cf6dfSJoerg Roedel 		return -1;
334672cf6dfSJoerg Roedel 
335672cf6dfSJoerg Roedel 	for (i = 0; i < MAX_HPET_TBS; i++) {
336672cf6dfSJoerg Roedel 		if (ir_hpet[i].iommu && ir_hpet[i].id == id) {
337672cf6dfSJoerg Roedel 			sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn;
338672cf6dfSJoerg Roedel 			break;
339672cf6dfSJoerg Roedel 		}
340672cf6dfSJoerg Roedel 	}
341672cf6dfSJoerg Roedel 
342672cf6dfSJoerg Roedel 	if (sid == 0) {
343672cf6dfSJoerg Roedel 		pr_warn("Failed to set source-id of HPET block (%d)\n", id);
344672cf6dfSJoerg Roedel 		return -1;
345672cf6dfSJoerg Roedel 	}
346672cf6dfSJoerg Roedel 
347672cf6dfSJoerg Roedel 	/*
348672cf6dfSJoerg Roedel 	 * Should really use SQ_ALL_16. Some platforms are broken.
349672cf6dfSJoerg Roedel 	 * While we figure out the right quirks for these broken platforms, use
350672cf6dfSJoerg Roedel 	 * SQ_13_IGNORE_3 for now.
351672cf6dfSJoerg Roedel 	 */
352672cf6dfSJoerg Roedel 	set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_13_IGNORE_3, sid);
353672cf6dfSJoerg Roedel 
354672cf6dfSJoerg Roedel 	return 0;
355672cf6dfSJoerg Roedel }
356672cf6dfSJoerg Roedel 
357672cf6dfSJoerg Roedel struct set_msi_sid_data {
358672cf6dfSJoerg Roedel 	struct pci_dev *pdev;
359672cf6dfSJoerg Roedel 	u16 alias;
360672cf6dfSJoerg Roedel 	int count;
361672cf6dfSJoerg Roedel 	int busmatch_count;
362672cf6dfSJoerg Roedel };
363672cf6dfSJoerg Roedel 
set_msi_sid_cb(struct pci_dev * pdev,u16 alias,void * opaque)364672cf6dfSJoerg Roedel static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque)
365672cf6dfSJoerg Roedel {
366672cf6dfSJoerg Roedel 	struct set_msi_sid_data *data = opaque;
367672cf6dfSJoerg Roedel 
368672cf6dfSJoerg Roedel 	if (data->count == 0 || PCI_BUS_NUM(alias) == PCI_BUS_NUM(data->alias))
369672cf6dfSJoerg Roedel 		data->busmatch_count++;
370672cf6dfSJoerg Roedel 
371672cf6dfSJoerg Roedel 	data->pdev = pdev;
372672cf6dfSJoerg Roedel 	data->alias = alias;
373672cf6dfSJoerg Roedel 	data->count++;
374672cf6dfSJoerg Roedel 
375672cf6dfSJoerg Roedel 	return 0;
376672cf6dfSJoerg Roedel }
377672cf6dfSJoerg Roedel 
set_msi_sid(struct irte * irte,struct pci_dev * dev)378672cf6dfSJoerg Roedel static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
379672cf6dfSJoerg Roedel {
380672cf6dfSJoerg Roedel 	struct set_msi_sid_data data;
381672cf6dfSJoerg Roedel 
382672cf6dfSJoerg Roedel 	if (!irte || !dev)
383672cf6dfSJoerg Roedel 		return -1;
384672cf6dfSJoerg Roedel 
385672cf6dfSJoerg Roedel 	data.count = 0;
386672cf6dfSJoerg Roedel 	data.busmatch_count = 0;
387672cf6dfSJoerg Roedel 	pci_for_each_dma_alias(dev, set_msi_sid_cb, &data);
388672cf6dfSJoerg Roedel 
389672cf6dfSJoerg Roedel 	/*
390672cf6dfSJoerg Roedel 	 * DMA alias provides us with a PCI device and alias.  The only case
391672cf6dfSJoerg Roedel 	 * where the it will return an alias on a different bus than the
392672cf6dfSJoerg Roedel 	 * device is the case of a PCIe-to-PCI bridge, where the alias is for
393672cf6dfSJoerg Roedel 	 * the subordinate bus.  In this case we can only verify the bus.
394672cf6dfSJoerg Roedel 	 *
395672cf6dfSJoerg Roedel 	 * If there are multiple aliases, all with the same bus number,
396672cf6dfSJoerg Roedel 	 * then all we can do is verify the bus. This is typical in NTB
397672cf6dfSJoerg Roedel 	 * hardware which use proxy IDs where the device will generate traffic
398672cf6dfSJoerg Roedel 	 * from multiple devfn numbers on the same bus.
399672cf6dfSJoerg Roedel 	 *
400672cf6dfSJoerg Roedel 	 * If the alias device is on a different bus than our source device
401672cf6dfSJoerg Roedel 	 * then we have a topology based alias, use it.
402672cf6dfSJoerg Roedel 	 *
403672cf6dfSJoerg Roedel 	 * Otherwise, the alias is for a device DMA quirk and we cannot
404672cf6dfSJoerg Roedel 	 * assume that MSI uses the same requester ID.  Therefore use the
405672cf6dfSJoerg Roedel 	 * original device.
406672cf6dfSJoerg Roedel 	 */
407672cf6dfSJoerg Roedel 	if (PCI_BUS_NUM(data.alias) != data.pdev->bus->number)
408672cf6dfSJoerg Roedel 		set_irte_verify_bus(irte, PCI_BUS_NUM(data.alias),
409672cf6dfSJoerg Roedel 				    dev->bus->number);
410672cf6dfSJoerg Roedel 	else if (data.count >= 2 && data.busmatch_count == data.count)
411672cf6dfSJoerg Roedel 		set_irte_verify_bus(irte, dev->bus->number, dev->bus->number);
412672cf6dfSJoerg Roedel 	else if (data.pdev->bus->number != dev->bus->number)
413672cf6dfSJoerg Roedel 		set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, data.alias);
414672cf6dfSJoerg Roedel 	else
415672cf6dfSJoerg Roedel 		set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
416672cf6dfSJoerg Roedel 			     pci_dev_id(dev));
417672cf6dfSJoerg Roedel 
418672cf6dfSJoerg Roedel 	return 0;
419672cf6dfSJoerg Roedel }
420672cf6dfSJoerg Roedel 
iommu_load_old_irte(struct intel_iommu * iommu)421672cf6dfSJoerg Roedel static int iommu_load_old_irte(struct intel_iommu *iommu)
422672cf6dfSJoerg Roedel {
423672cf6dfSJoerg Roedel 	struct irte *old_ir_table;
424672cf6dfSJoerg Roedel 	phys_addr_t irt_phys;
425672cf6dfSJoerg Roedel 	unsigned int i;
426672cf6dfSJoerg Roedel 	size_t size;
427672cf6dfSJoerg Roedel 	u64 irta;
428672cf6dfSJoerg Roedel 
429672cf6dfSJoerg Roedel 	/* Check whether the old ir-table has the same size as ours */
430672cf6dfSJoerg Roedel 	irta = dmar_readq(iommu->reg + DMAR_IRTA_REG);
431672cf6dfSJoerg Roedel 	if ((irta & INTR_REMAP_TABLE_REG_SIZE_MASK)
432672cf6dfSJoerg Roedel 	     != INTR_REMAP_TABLE_REG_SIZE)
433672cf6dfSJoerg Roedel 		return -EINVAL;
434672cf6dfSJoerg Roedel 
435672cf6dfSJoerg Roedel 	irt_phys = irta & VTD_PAGE_MASK;
436672cf6dfSJoerg Roedel 	size     = INTR_REMAP_TABLE_ENTRIES*sizeof(struct irte);
437672cf6dfSJoerg Roedel 
438672cf6dfSJoerg Roedel 	/* Map the old IR table */
439672cf6dfSJoerg Roedel 	old_ir_table = memremap(irt_phys, size, MEMREMAP_WB);
440672cf6dfSJoerg Roedel 	if (!old_ir_table)
441672cf6dfSJoerg Roedel 		return -ENOMEM;
442672cf6dfSJoerg Roedel 
443672cf6dfSJoerg Roedel 	/* Copy data over */
444672cf6dfSJoerg Roedel 	memcpy(iommu->ir_table->base, old_ir_table, size);
445672cf6dfSJoerg Roedel 
446672cf6dfSJoerg Roedel 	__iommu_flush_cache(iommu, iommu->ir_table->base, size);
447672cf6dfSJoerg Roedel 
448672cf6dfSJoerg Roedel 	/*
449672cf6dfSJoerg Roedel 	 * Now check the table for used entries and mark those as
450672cf6dfSJoerg Roedel 	 * allocated in the bitmap
451672cf6dfSJoerg Roedel 	 */
452672cf6dfSJoerg Roedel 	for (i = 0; i < INTR_REMAP_TABLE_ENTRIES; i++) {
453672cf6dfSJoerg Roedel 		if (iommu->ir_table->base[i].present)
454672cf6dfSJoerg Roedel 			bitmap_set(iommu->ir_table->bitmap, i, 1);
455672cf6dfSJoerg Roedel 	}
456672cf6dfSJoerg Roedel 
457672cf6dfSJoerg Roedel 	memunmap(old_ir_table);
458672cf6dfSJoerg Roedel 
459672cf6dfSJoerg Roedel 	return 0;
460672cf6dfSJoerg Roedel }
461672cf6dfSJoerg Roedel 
462672cf6dfSJoerg Roedel 
iommu_set_irq_remapping(struct intel_iommu * iommu,int mode)463672cf6dfSJoerg Roedel static void iommu_set_irq_remapping(struct intel_iommu *iommu, int mode)
464672cf6dfSJoerg Roedel {
465672cf6dfSJoerg Roedel 	unsigned long flags;
466672cf6dfSJoerg Roedel 	u64 addr;
467672cf6dfSJoerg Roedel 	u32 sts;
468672cf6dfSJoerg Roedel 
469672cf6dfSJoerg Roedel 	addr = virt_to_phys((void *)iommu->ir_table->base);
470672cf6dfSJoerg Roedel 
471672cf6dfSJoerg Roedel 	raw_spin_lock_irqsave(&iommu->register_lock, flags);
472672cf6dfSJoerg Roedel 
473672cf6dfSJoerg Roedel 	dmar_writeq(iommu->reg + DMAR_IRTA_REG,
474672cf6dfSJoerg Roedel 		    (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE);
475672cf6dfSJoerg Roedel 
476672cf6dfSJoerg Roedel 	/* Set interrupt-remapping table pointer */
477672cf6dfSJoerg Roedel 	writel(iommu->gcmd | DMA_GCMD_SIRTP, iommu->reg + DMAR_GCMD_REG);
478672cf6dfSJoerg Roedel 
479672cf6dfSJoerg Roedel 	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
480672cf6dfSJoerg Roedel 		      readl, (sts & DMA_GSTS_IRTPS), sts);
481672cf6dfSJoerg Roedel 	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
482672cf6dfSJoerg Roedel 
483672cf6dfSJoerg Roedel 	/*
484672cf6dfSJoerg Roedel 	 * Global invalidation of interrupt entry cache to make sure the
485672cf6dfSJoerg Roedel 	 * hardware uses the new irq remapping table.
486672cf6dfSJoerg Roedel 	 */
487eb5b2011SLu Baolu 	if (!cap_esirtps(iommu->cap))
488672cf6dfSJoerg Roedel 		qi_global_iec(iommu);
489672cf6dfSJoerg Roedel }
490672cf6dfSJoerg Roedel 
iommu_enable_irq_remapping(struct intel_iommu * iommu)491672cf6dfSJoerg Roedel static void iommu_enable_irq_remapping(struct intel_iommu *iommu)
492672cf6dfSJoerg Roedel {
493672cf6dfSJoerg Roedel 	unsigned long flags;
494672cf6dfSJoerg Roedel 	u32 sts;
495672cf6dfSJoerg Roedel 
496672cf6dfSJoerg Roedel 	raw_spin_lock_irqsave(&iommu->register_lock, flags);
497672cf6dfSJoerg Roedel 
498672cf6dfSJoerg Roedel 	/* Enable interrupt-remapping */
499672cf6dfSJoerg Roedel 	iommu->gcmd |= DMA_GCMD_IRE;
500672cf6dfSJoerg Roedel 	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
501672cf6dfSJoerg Roedel 	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
502672cf6dfSJoerg Roedel 		      readl, (sts & DMA_GSTS_IRES), sts);
503672cf6dfSJoerg Roedel 
5046e4e9ec6SLu Baolu 	/* Block compatibility-format MSIs */
5056e4e9ec6SLu Baolu 	if (sts & DMA_GSTS_CFIS) {
5066e4e9ec6SLu Baolu 		iommu->gcmd &= ~DMA_GCMD_CFI;
5076e4e9ec6SLu Baolu 		writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
5086e4e9ec6SLu Baolu 		IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
5096e4e9ec6SLu Baolu 			      readl, !(sts & DMA_GSTS_CFIS), sts);
5106e4e9ec6SLu Baolu 	}
5116e4e9ec6SLu Baolu 
512672cf6dfSJoerg Roedel 	/*
513672cf6dfSJoerg Roedel 	 * With CFI clear in the Global Command register, we should be
514672cf6dfSJoerg Roedel 	 * protected from dangerous (i.e. compatibility) interrupts
515672cf6dfSJoerg Roedel 	 * regardless of x2apic status.  Check just to be sure.
516672cf6dfSJoerg Roedel 	 */
517672cf6dfSJoerg Roedel 	if (sts & DMA_GSTS_CFIS)
518672cf6dfSJoerg Roedel 		WARN(1, KERN_WARNING
519672cf6dfSJoerg Roedel 			"Compatibility-format IRQs enabled despite intr remapping;\n"
520672cf6dfSJoerg Roedel 			"you are vulnerable to IRQ injection.\n");
521672cf6dfSJoerg Roedel 
522672cf6dfSJoerg Roedel 	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
523672cf6dfSJoerg Roedel }
524672cf6dfSJoerg Roedel 
intel_setup_irq_remapping(struct intel_iommu * iommu)525672cf6dfSJoerg Roedel static int intel_setup_irq_remapping(struct intel_iommu *iommu)
526672cf6dfSJoerg Roedel {
527672cf6dfSJoerg Roedel 	struct ir_table *ir_table;
528672cf6dfSJoerg Roedel 	struct fwnode_handle *fn;
529672cf6dfSJoerg Roedel 	unsigned long *bitmap;
530672cf6dfSJoerg Roedel 	struct page *pages;
531672cf6dfSJoerg Roedel 
532672cf6dfSJoerg Roedel 	if (iommu->ir_table)
533672cf6dfSJoerg Roedel 		return 0;
534672cf6dfSJoerg Roedel 
535672cf6dfSJoerg Roedel 	ir_table = kzalloc(sizeof(struct ir_table), GFP_KERNEL);
536672cf6dfSJoerg Roedel 	if (!ir_table)
537672cf6dfSJoerg Roedel 		return -ENOMEM;
538672cf6dfSJoerg Roedel 
539672cf6dfSJoerg Roedel 	pages = alloc_pages_node(iommu->node, GFP_KERNEL | __GFP_ZERO,
540672cf6dfSJoerg Roedel 				 INTR_REMAP_PAGE_ORDER);
541672cf6dfSJoerg Roedel 	if (!pages) {
542672cf6dfSJoerg Roedel 		pr_err("IR%d: failed to allocate pages of order %d\n",
543672cf6dfSJoerg Roedel 		       iommu->seq_id, INTR_REMAP_PAGE_ORDER);
544672cf6dfSJoerg Roedel 		goto out_free_table;
545672cf6dfSJoerg Roedel 	}
546672cf6dfSJoerg Roedel 
54741d71e09SChristophe JAILLET 	bitmap = bitmap_zalloc(INTR_REMAP_TABLE_ENTRIES, GFP_KERNEL);
548672cf6dfSJoerg Roedel 	if (bitmap == NULL) {
549672cf6dfSJoerg Roedel 		pr_err("IR%d: failed to allocate bitmap\n", iommu->seq_id);
550672cf6dfSJoerg Roedel 		goto out_free_pages;
551672cf6dfSJoerg Roedel 	}
552672cf6dfSJoerg Roedel 
553672cf6dfSJoerg Roedel 	fn = irq_domain_alloc_named_id_fwnode("INTEL-IR", iommu->seq_id);
554672cf6dfSJoerg Roedel 	if (!fn)
555672cf6dfSJoerg Roedel 		goto out_free_bitmap;
556672cf6dfSJoerg Roedel 
557672cf6dfSJoerg Roedel 	iommu->ir_domain =
558672cf6dfSJoerg Roedel 		irq_domain_create_hierarchy(arch_get_ir_parent_domain(),
559672cf6dfSJoerg Roedel 					    0, INTR_REMAP_TABLE_ENTRIES,
560672cf6dfSJoerg Roedel 					    fn, &intel_ir_domain_ops,
561672cf6dfSJoerg Roedel 					    iommu);
562672cf6dfSJoerg Roedel 	if (!iommu->ir_domain) {
563672cf6dfSJoerg Roedel 		pr_err("IR%d: failed to allocate irqdomain\n", iommu->seq_id);
56499e675d4SGuoqing Jiang 		goto out_free_fwnode;
565672cf6dfSJoerg Roedel 	}
5669a945234SThomas Gleixner 
5679a945234SThomas Gleixner 	irq_domain_update_bus_token(iommu->ir_domain,  DOMAIN_BUS_DMAR);
568f188bdb5SJason Gunthorpe 	iommu->ir_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT |
569f188bdb5SJason Gunthorpe 				   IRQ_DOMAIN_FLAG_ISOLATED_MSI;
570810531a1SThomas Gleixner 
571810531a1SThomas Gleixner 	if (cap_caching_mode(iommu->cap))
572810531a1SThomas Gleixner 		iommu->ir_domain->msi_parent_ops = &virt_dmar_msi_parent_ops;
573810531a1SThomas Gleixner 	else
5749a945234SThomas Gleixner 		iommu->ir_domain->msi_parent_ops = &dmar_msi_parent_ops;
575672cf6dfSJoerg Roedel 
576672cf6dfSJoerg Roedel 	ir_table->base = page_address(pages);
577672cf6dfSJoerg Roedel 	ir_table->bitmap = bitmap;
578672cf6dfSJoerg Roedel 	iommu->ir_table = ir_table;
579672cf6dfSJoerg Roedel 
580672cf6dfSJoerg Roedel 	/*
581672cf6dfSJoerg Roedel 	 * If the queued invalidation is already initialized,
582672cf6dfSJoerg Roedel 	 * shouldn't disable it.
583672cf6dfSJoerg Roedel 	 */
584672cf6dfSJoerg Roedel 	if (!iommu->qi) {
585672cf6dfSJoerg Roedel 		/*
586672cf6dfSJoerg Roedel 		 * Clear previous faults.
587672cf6dfSJoerg Roedel 		 */
588672cf6dfSJoerg Roedel 		dmar_fault(-1, iommu);
589672cf6dfSJoerg Roedel 		dmar_disable_qi(iommu);
590672cf6dfSJoerg Roedel 
591672cf6dfSJoerg Roedel 		if (dmar_enable_qi(iommu)) {
592672cf6dfSJoerg Roedel 			pr_err("Failed to enable queued invalidation\n");
59399e675d4SGuoqing Jiang 			goto out_free_ir_domain;
594672cf6dfSJoerg Roedel 		}
595672cf6dfSJoerg Roedel 	}
596672cf6dfSJoerg Roedel 
597672cf6dfSJoerg Roedel 	init_ir_status(iommu);
598672cf6dfSJoerg Roedel 
599672cf6dfSJoerg Roedel 	if (ir_pre_enabled(iommu)) {
600672cf6dfSJoerg Roedel 		if (!is_kdump_kernel()) {
601672cf6dfSJoerg Roedel 			pr_warn("IRQ remapping was enabled on %s but we are not in kdump mode\n",
602672cf6dfSJoerg Roedel 				iommu->name);
603672cf6dfSJoerg Roedel 			clear_ir_pre_enabled(iommu);
604672cf6dfSJoerg Roedel 			iommu_disable_irq_remapping(iommu);
605672cf6dfSJoerg Roedel 		} else if (iommu_load_old_irte(iommu))
606672cf6dfSJoerg Roedel 			pr_err("Failed to copy IR table for %s from previous kernel\n",
607672cf6dfSJoerg Roedel 			       iommu->name);
608672cf6dfSJoerg Roedel 		else
609672cf6dfSJoerg Roedel 			pr_info("Copied IR table for %s from previous kernel\n",
610672cf6dfSJoerg Roedel 				iommu->name);
611672cf6dfSJoerg Roedel 	}
612672cf6dfSJoerg Roedel 
613672cf6dfSJoerg Roedel 	iommu_set_irq_remapping(iommu, eim_mode);
614672cf6dfSJoerg Roedel 
615672cf6dfSJoerg Roedel 	return 0;
616672cf6dfSJoerg Roedel 
61799e675d4SGuoqing Jiang out_free_ir_domain:
61899e675d4SGuoqing Jiang 	irq_domain_remove(iommu->ir_domain);
61999e675d4SGuoqing Jiang 	iommu->ir_domain = NULL;
62099e675d4SGuoqing Jiang out_free_fwnode:
62199e675d4SGuoqing Jiang 	irq_domain_free_fwnode(fn);
622672cf6dfSJoerg Roedel out_free_bitmap:
623672cf6dfSJoerg Roedel 	bitmap_free(bitmap);
624672cf6dfSJoerg Roedel out_free_pages:
625672cf6dfSJoerg Roedel 	__free_pages(pages, INTR_REMAP_PAGE_ORDER);
626672cf6dfSJoerg Roedel out_free_table:
627672cf6dfSJoerg Roedel 	kfree(ir_table);
628672cf6dfSJoerg Roedel 
629672cf6dfSJoerg Roedel 	iommu->ir_table  = NULL;
630672cf6dfSJoerg Roedel 
631672cf6dfSJoerg Roedel 	return -ENOMEM;
632672cf6dfSJoerg Roedel }
633672cf6dfSJoerg Roedel 
intel_teardown_irq_remapping(struct intel_iommu * iommu)634672cf6dfSJoerg Roedel static void intel_teardown_irq_remapping(struct intel_iommu *iommu)
635672cf6dfSJoerg Roedel {
636ec016089SJon Derrick 	struct fwnode_handle *fn;
637ec016089SJon Derrick 
638672cf6dfSJoerg Roedel 	if (iommu && iommu->ir_table) {
639672cf6dfSJoerg Roedel 		if (iommu->ir_domain) {
640ec016089SJon Derrick 			fn = iommu->ir_domain->fwnode;
641ec016089SJon Derrick 
642672cf6dfSJoerg Roedel 			irq_domain_remove(iommu->ir_domain);
643ec016089SJon Derrick 			irq_domain_free_fwnode(fn);
644672cf6dfSJoerg Roedel 			iommu->ir_domain = NULL;
645672cf6dfSJoerg Roedel 		}
646672cf6dfSJoerg Roedel 		free_pages((unsigned long)iommu->ir_table->base,
647672cf6dfSJoerg Roedel 			   INTR_REMAP_PAGE_ORDER);
648672cf6dfSJoerg Roedel 		bitmap_free(iommu->ir_table->bitmap);
649672cf6dfSJoerg Roedel 		kfree(iommu->ir_table);
650672cf6dfSJoerg Roedel 		iommu->ir_table = NULL;
651672cf6dfSJoerg Roedel 	}
652672cf6dfSJoerg Roedel }
653672cf6dfSJoerg Roedel 
654672cf6dfSJoerg Roedel /*
655672cf6dfSJoerg Roedel  * Disable Interrupt Remapping.
656672cf6dfSJoerg Roedel  */
iommu_disable_irq_remapping(struct intel_iommu * iommu)657672cf6dfSJoerg Roedel static void iommu_disable_irq_remapping(struct intel_iommu *iommu)
658672cf6dfSJoerg Roedel {
659672cf6dfSJoerg Roedel 	unsigned long flags;
660672cf6dfSJoerg Roedel 	u32 sts;
661672cf6dfSJoerg Roedel 
662672cf6dfSJoerg Roedel 	if (!ecap_ir_support(iommu->ecap))
663672cf6dfSJoerg Roedel 		return;
664672cf6dfSJoerg Roedel 
665672cf6dfSJoerg Roedel 	/*
666672cf6dfSJoerg Roedel 	 * global invalidation of interrupt entry cache before disabling
667672cf6dfSJoerg Roedel 	 * interrupt-remapping.
668672cf6dfSJoerg Roedel 	 */
669eb5b2011SLu Baolu 	if (!cap_esirtps(iommu->cap))
670672cf6dfSJoerg Roedel 		qi_global_iec(iommu);
671672cf6dfSJoerg Roedel 
672672cf6dfSJoerg Roedel 	raw_spin_lock_irqsave(&iommu->register_lock, flags);
673672cf6dfSJoerg Roedel 
674672cf6dfSJoerg Roedel 	sts = readl(iommu->reg + DMAR_GSTS_REG);
675672cf6dfSJoerg Roedel 	if (!(sts & DMA_GSTS_IRES))
676672cf6dfSJoerg Roedel 		goto end;
677672cf6dfSJoerg Roedel 
678672cf6dfSJoerg Roedel 	iommu->gcmd &= ~DMA_GCMD_IRE;
679672cf6dfSJoerg Roedel 	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
680672cf6dfSJoerg Roedel 
681672cf6dfSJoerg Roedel 	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
682672cf6dfSJoerg Roedel 		      readl, !(sts & DMA_GSTS_IRES), sts);
683672cf6dfSJoerg Roedel 
684672cf6dfSJoerg Roedel end:
685672cf6dfSJoerg Roedel 	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
686672cf6dfSJoerg Roedel }
687672cf6dfSJoerg Roedel 
dmar_x2apic_optout(void)688672cf6dfSJoerg Roedel static int __init dmar_x2apic_optout(void)
689672cf6dfSJoerg Roedel {
690672cf6dfSJoerg Roedel 	struct acpi_table_dmar *dmar;
691672cf6dfSJoerg Roedel 	dmar = (struct acpi_table_dmar *)dmar_tbl;
692672cf6dfSJoerg Roedel 	if (!dmar || no_x2apic_optout)
693672cf6dfSJoerg Roedel 		return 0;
694672cf6dfSJoerg Roedel 	return dmar->flags & DMAR_X2APIC_OPT_OUT;
695672cf6dfSJoerg Roedel }
696672cf6dfSJoerg Roedel 
intel_cleanup_irq_remapping(void)697672cf6dfSJoerg Roedel static void __init intel_cleanup_irq_remapping(void)
698672cf6dfSJoerg Roedel {
699672cf6dfSJoerg Roedel 	struct dmar_drhd_unit *drhd;
700672cf6dfSJoerg Roedel 	struct intel_iommu *iommu;
701672cf6dfSJoerg Roedel 
702672cf6dfSJoerg Roedel 	for_each_iommu(iommu, drhd) {
703672cf6dfSJoerg Roedel 		if (ecap_ir_support(iommu->ecap)) {
704672cf6dfSJoerg Roedel 			iommu_disable_irq_remapping(iommu);
705672cf6dfSJoerg Roedel 			intel_teardown_irq_remapping(iommu);
706672cf6dfSJoerg Roedel 		}
707672cf6dfSJoerg Roedel 	}
708672cf6dfSJoerg Roedel 
709672cf6dfSJoerg Roedel 	if (x2apic_supported())
710672cf6dfSJoerg Roedel 		pr_warn("Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n");
711672cf6dfSJoerg Roedel }
712672cf6dfSJoerg Roedel 
intel_prepare_irq_remapping(void)713672cf6dfSJoerg Roedel static int __init intel_prepare_irq_remapping(void)
714672cf6dfSJoerg Roedel {
715672cf6dfSJoerg Roedel 	struct dmar_drhd_unit *drhd;
716672cf6dfSJoerg Roedel 	struct intel_iommu *iommu;
717672cf6dfSJoerg Roedel 	int eim = 0;
718672cf6dfSJoerg Roedel 
719672cf6dfSJoerg Roedel 	if (irq_remap_broken) {
720672cf6dfSJoerg Roedel 		pr_warn("This system BIOS has enabled interrupt remapping\n"
721672cf6dfSJoerg Roedel 			"on a chipset that contains an erratum making that\n"
722672cf6dfSJoerg Roedel 			"feature unstable.  To maintain system stability\n"
723672cf6dfSJoerg Roedel 			"interrupt remapping is being disabled.  Please\n"
724672cf6dfSJoerg Roedel 			"contact your BIOS vendor for an update\n");
725672cf6dfSJoerg Roedel 		add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
726672cf6dfSJoerg Roedel 		return -ENODEV;
727672cf6dfSJoerg Roedel 	}
728672cf6dfSJoerg Roedel 
729672cf6dfSJoerg Roedel 	if (dmar_table_init() < 0)
730672cf6dfSJoerg Roedel 		return -ENODEV;
731672cf6dfSJoerg Roedel 
732ad3d1902SKyung Min Park 	if (intel_cap_audit(CAP_AUDIT_STATIC_IRQR, NULL))
733745610c4SChristophe JAILLET 		return -ENODEV;
734ad3d1902SKyung Min Park 
735672cf6dfSJoerg Roedel 	if (!dmar_ir_support())
736672cf6dfSJoerg Roedel 		return -ENODEV;
737672cf6dfSJoerg Roedel 
738672cf6dfSJoerg Roedel 	if (parse_ioapics_under_ir()) {
739672cf6dfSJoerg Roedel 		pr_info("Not enabling interrupt remapping\n");
740672cf6dfSJoerg Roedel 		goto error;
741672cf6dfSJoerg Roedel 	}
742672cf6dfSJoerg Roedel 
743672cf6dfSJoerg Roedel 	/* First make sure all IOMMUs support IRQ remapping */
744672cf6dfSJoerg Roedel 	for_each_iommu(iommu, drhd)
745672cf6dfSJoerg Roedel 		if (!ecap_ir_support(iommu->ecap))
746672cf6dfSJoerg Roedel 			goto error;
747672cf6dfSJoerg Roedel 
748672cf6dfSJoerg Roedel 	/* Detect remapping mode: lapic or x2apic */
749672cf6dfSJoerg Roedel 	if (x2apic_supported()) {
750672cf6dfSJoerg Roedel 		eim = !dmar_x2apic_optout();
751672cf6dfSJoerg Roedel 		if (!eim) {
752672cf6dfSJoerg Roedel 			pr_info("x2apic is disabled because BIOS sets x2apic opt out bit.");
753672cf6dfSJoerg Roedel 			pr_info("Use 'intremap=no_x2apic_optout' to override the BIOS setting.\n");
754672cf6dfSJoerg Roedel 		}
755672cf6dfSJoerg Roedel 	}
756672cf6dfSJoerg Roedel 
757672cf6dfSJoerg Roedel 	for_each_iommu(iommu, drhd) {
758672cf6dfSJoerg Roedel 		if (eim && !ecap_eim_support(iommu->ecap)) {
759672cf6dfSJoerg Roedel 			pr_info("%s does not support EIM\n", iommu->name);
760672cf6dfSJoerg Roedel 			eim = 0;
761672cf6dfSJoerg Roedel 		}
762672cf6dfSJoerg Roedel 	}
763672cf6dfSJoerg Roedel 
764672cf6dfSJoerg Roedel 	eim_mode = eim;
765672cf6dfSJoerg Roedel 	if (eim)
766672cf6dfSJoerg Roedel 		pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");
767672cf6dfSJoerg Roedel 
768672cf6dfSJoerg Roedel 	/* Do the initializations early */
769672cf6dfSJoerg Roedel 	for_each_iommu(iommu, drhd) {
770672cf6dfSJoerg Roedel 		if (intel_setup_irq_remapping(iommu)) {
771672cf6dfSJoerg Roedel 			pr_err("Failed to setup irq remapping for %s\n",
772672cf6dfSJoerg Roedel 			       iommu->name);
773672cf6dfSJoerg Roedel 			goto error;
774672cf6dfSJoerg Roedel 		}
775672cf6dfSJoerg Roedel 	}
776672cf6dfSJoerg Roedel 
777672cf6dfSJoerg Roedel 	return 0;
778672cf6dfSJoerg Roedel 
779672cf6dfSJoerg Roedel error:
780672cf6dfSJoerg Roedel 	intel_cleanup_irq_remapping();
781672cf6dfSJoerg Roedel 	return -ENODEV;
782672cf6dfSJoerg Roedel }
783672cf6dfSJoerg Roedel 
784672cf6dfSJoerg Roedel /*
785672cf6dfSJoerg Roedel  * Set Posted-Interrupts capability.
786672cf6dfSJoerg Roedel  */
set_irq_posting_cap(void)787672cf6dfSJoerg Roedel static inline void set_irq_posting_cap(void)
788672cf6dfSJoerg Roedel {
789672cf6dfSJoerg Roedel 	struct dmar_drhd_unit *drhd;
790672cf6dfSJoerg Roedel 	struct intel_iommu *iommu;
791672cf6dfSJoerg Roedel 
792672cf6dfSJoerg Roedel 	if (!disable_irq_post) {
793672cf6dfSJoerg Roedel 		/*
794672cf6dfSJoerg Roedel 		 * If IRTE is in posted format, the 'pda' field goes across the
795672cf6dfSJoerg Roedel 		 * 64-bit boundary, we need use cmpxchg16b to atomically update
796672cf6dfSJoerg Roedel 		 * it. We only expose posted-interrupt when X86_FEATURE_CX16
797672cf6dfSJoerg Roedel 		 * is supported. Actually, hardware platforms supporting PI
798672cf6dfSJoerg Roedel 		 * should have X86_FEATURE_CX16 support, this has been confirmed
799672cf6dfSJoerg Roedel 		 * with Intel hardware guys.
800672cf6dfSJoerg Roedel 		 */
801672cf6dfSJoerg Roedel 		if (boot_cpu_has(X86_FEATURE_CX16))
802672cf6dfSJoerg Roedel 			intel_irq_remap_ops.capability |= 1 << IRQ_POSTING_CAP;
803672cf6dfSJoerg Roedel 
804672cf6dfSJoerg Roedel 		for_each_iommu(iommu, drhd)
805672cf6dfSJoerg Roedel 			if (!cap_pi_support(iommu->cap)) {
806672cf6dfSJoerg Roedel 				intel_irq_remap_ops.capability &=
807672cf6dfSJoerg Roedel 						~(1 << IRQ_POSTING_CAP);
808672cf6dfSJoerg Roedel 				break;
809672cf6dfSJoerg Roedel 			}
810672cf6dfSJoerg Roedel 	}
811672cf6dfSJoerg Roedel }
812672cf6dfSJoerg Roedel 
intel_enable_irq_remapping(void)813672cf6dfSJoerg Roedel static int __init intel_enable_irq_remapping(void)
814672cf6dfSJoerg Roedel {
815672cf6dfSJoerg Roedel 	struct dmar_drhd_unit *drhd;
816672cf6dfSJoerg Roedel 	struct intel_iommu *iommu;
817672cf6dfSJoerg Roedel 	bool setup = false;
818672cf6dfSJoerg Roedel 
819672cf6dfSJoerg Roedel 	/*
820672cf6dfSJoerg Roedel 	 * Setup Interrupt-remapping for all the DRHD's now.
821672cf6dfSJoerg Roedel 	 */
822672cf6dfSJoerg Roedel 	for_each_iommu(iommu, drhd) {
823672cf6dfSJoerg Roedel 		if (!ir_pre_enabled(iommu))
824672cf6dfSJoerg Roedel 			iommu_enable_irq_remapping(iommu);
825672cf6dfSJoerg Roedel 		setup = true;
826672cf6dfSJoerg Roedel 	}
827672cf6dfSJoerg Roedel 
828672cf6dfSJoerg Roedel 	if (!setup)
829672cf6dfSJoerg Roedel 		goto error;
830672cf6dfSJoerg Roedel 
831672cf6dfSJoerg Roedel 	irq_remapping_enabled = 1;
832672cf6dfSJoerg Roedel 
833672cf6dfSJoerg Roedel 	set_irq_posting_cap();
834672cf6dfSJoerg Roedel 
835672cf6dfSJoerg Roedel 	pr_info("Enabled IRQ remapping in %s mode\n", eim_mode ? "x2apic" : "xapic");
836672cf6dfSJoerg Roedel 
837672cf6dfSJoerg Roedel 	return eim_mode ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
838672cf6dfSJoerg Roedel 
839672cf6dfSJoerg Roedel error:
840672cf6dfSJoerg Roedel 	intel_cleanup_irq_remapping();
841672cf6dfSJoerg Roedel 	return -1;
842672cf6dfSJoerg Roedel }
843672cf6dfSJoerg Roedel 
ir_parse_one_hpet_scope(struct acpi_dmar_device_scope * scope,struct intel_iommu * iommu,struct acpi_dmar_hardware_unit * drhd)844672cf6dfSJoerg Roedel static int ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope,
845672cf6dfSJoerg Roedel 				   struct intel_iommu *iommu,
846672cf6dfSJoerg Roedel 				   struct acpi_dmar_hardware_unit *drhd)
847672cf6dfSJoerg Roedel {
848672cf6dfSJoerg Roedel 	struct acpi_dmar_pci_path *path;
849672cf6dfSJoerg Roedel 	u8 bus;
850672cf6dfSJoerg Roedel 	int count, free = -1;
851672cf6dfSJoerg Roedel 
852672cf6dfSJoerg Roedel 	bus = scope->bus;
853672cf6dfSJoerg Roedel 	path = (struct acpi_dmar_pci_path *)(scope + 1);
854672cf6dfSJoerg Roedel 	count = (scope->length - sizeof(struct acpi_dmar_device_scope))
855672cf6dfSJoerg Roedel 		/ sizeof(struct acpi_dmar_pci_path);
856672cf6dfSJoerg Roedel 
857672cf6dfSJoerg Roedel 	while (--count > 0) {
858672cf6dfSJoerg Roedel 		/*
859672cf6dfSJoerg Roedel 		 * Access PCI directly due to the PCI
860672cf6dfSJoerg Roedel 		 * subsystem isn't initialized yet.
861672cf6dfSJoerg Roedel 		 */
862672cf6dfSJoerg Roedel 		bus = read_pci_config_byte(bus, path->device, path->function,
863672cf6dfSJoerg Roedel 					   PCI_SECONDARY_BUS);
864672cf6dfSJoerg Roedel 		path++;
865672cf6dfSJoerg Roedel 	}
866672cf6dfSJoerg Roedel 
867672cf6dfSJoerg Roedel 	for (count = 0; count < MAX_HPET_TBS; count++) {
868672cf6dfSJoerg Roedel 		if (ir_hpet[count].iommu == iommu &&
869672cf6dfSJoerg Roedel 		    ir_hpet[count].id == scope->enumeration_id)
870672cf6dfSJoerg Roedel 			return 0;
871672cf6dfSJoerg Roedel 		else if (ir_hpet[count].iommu == NULL && free == -1)
872672cf6dfSJoerg Roedel 			free = count;
873672cf6dfSJoerg Roedel 	}
874672cf6dfSJoerg Roedel 	if (free == -1) {
875672cf6dfSJoerg Roedel 		pr_warn("Exceeded Max HPET blocks\n");
876672cf6dfSJoerg Roedel 		return -ENOSPC;
877672cf6dfSJoerg Roedel 	}
878672cf6dfSJoerg Roedel 
879672cf6dfSJoerg Roedel 	ir_hpet[free].iommu = iommu;
880672cf6dfSJoerg Roedel 	ir_hpet[free].id    = scope->enumeration_id;
881672cf6dfSJoerg Roedel 	ir_hpet[free].bus   = bus;
882672cf6dfSJoerg Roedel 	ir_hpet[free].devfn = PCI_DEVFN(path->device, path->function);
883672cf6dfSJoerg Roedel 	pr_info("HPET id %d under DRHD base 0x%Lx\n",
884672cf6dfSJoerg Roedel 		scope->enumeration_id, drhd->address);
885672cf6dfSJoerg Roedel 
886672cf6dfSJoerg Roedel 	return 0;
887672cf6dfSJoerg Roedel }
888672cf6dfSJoerg Roedel 
ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope * scope,struct intel_iommu * iommu,struct acpi_dmar_hardware_unit * drhd)889672cf6dfSJoerg Roedel static int ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
890672cf6dfSJoerg Roedel 				     struct intel_iommu *iommu,
891672cf6dfSJoerg Roedel 				     struct acpi_dmar_hardware_unit *drhd)
892672cf6dfSJoerg Roedel {
893672cf6dfSJoerg Roedel 	struct acpi_dmar_pci_path *path;
894672cf6dfSJoerg Roedel 	u8 bus;
895672cf6dfSJoerg Roedel 	int count, free = -1;
896672cf6dfSJoerg Roedel 
897672cf6dfSJoerg Roedel 	bus = scope->bus;
898672cf6dfSJoerg Roedel 	path = (struct acpi_dmar_pci_path *)(scope + 1);
899672cf6dfSJoerg Roedel 	count = (scope->length - sizeof(struct acpi_dmar_device_scope))
900672cf6dfSJoerg Roedel 		/ sizeof(struct acpi_dmar_pci_path);
901672cf6dfSJoerg Roedel 
902672cf6dfSJoerg Roedel 	while (--count > 0) {
903672cf6dfSJoerg Roedel 		/*
904672cf6dfSJoerg Roedel 		 * Access PCI directly due to the PCI
905672cf6dfSJoerg Roedel 		 * subsystem isn't initialized yet.
906672cf6dfSJoerg Roedel 		 */
907672cf6dfSJoerg Roedel 		bus = read_pci_config_byte(bus, path->device, path->function,
908672cf6dfSJoerg Roedel 					   PCI_SECONDARY_BUS);
909672cf6dfSJoerg Roedel 		path++;
910672cf6dfSJoerg Roedel 	}
911672cf6dfSJoerg Roedel 
912672cf6dfSJoerg Roedel 	for (count = 0; count < MAX_IO_APICS; count++) {
913672cf6dfSJoerg Roedel 		if (ir_ioapic[count].iommu == iommu &&
914672cf6dfSJoerg Roedel 		    ir_ioapic[count].id == scope->enumeration_id)
915672cf6dfSJoerg Roedel 			return 0;
916672cf6dfSJoerg Roedel 		else if (ir_ioapic[count].iommu == NULL && free == -1)
917672cf6dfSJoerg Roedel 			free = count;
918672cf6dfSJoerg Roedel 	}
919672cf6dfSJoerg Roedel 	if (free == -1) {
920672cf6dfSJoerg Roedel 		pr_warn("Exceeded Max IO APICS\n");
921672cf6dfSJoerg Roedel 		return -ENOSPC;
922672cf6dfSJoerg Roedel 	}
923672cf6dfSJoerg Roedel 
924672cf6dfSJoerg Roedel 	ir_ioapic[free].bus   = bus;
925672cf6dfSJoerg Roedel 	ir_ioapic[free].devfn = PCI_DEVFN(path->device, path->function);
926672cf6dfSJoerg Roedel 	ir_ioapic[free].iommu = iommu;
927672cf6dfSJoerg Roedel 	ir_ioapic[free].id    = scope->enumeration_id;
928672cf6dfSJoerg Roedel 	pr_info("IOAPIC id %d under DRHD base  0x%Lx IOMMU %d\n",
929672cf6dfSJoerg Roedel 		scope->enumeration_id, drhd->address, iommu->seq_id);
930672cf6dfSJoerg Roedel 
931672cf6dfSJoerg Roedel 	return 0;
932672cf6dfSJoerg Roedel }
933672cf6dfSJoerg Roedel 
ir_parse_ioapic_hpet_scope(struct acpi_dmar_header * header,struct intel_iommu * iommu)934672cf6dfSJoerg Roedel static int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header,
935672cf6dfSJoerg Roedel 				      struct intel_iommu *iommu)
936672cf6dfSJoerg Roedel {
937672cf6dfSJoerg Roedel 	int ret = 0;
938672cf6dfSJoerg Roedel 	struct acpi_dmar_hardware_unit *drhd;
939672cf6dfSJoerg Roedel 	struct acpi_dmar_device_scope *scope;
940672cf6dfSJoerg Roedel 	void *start, *end;
941672cf6dfSJoerg Roedel 
942672cf6dfSJoerg Roedel 	drhd = (struct acpi_dmar_hardware_unit *)header;
943672cf6dfSJoerg Roedel 	start = (void *)(drhd + 1);
944672cf6dfSJoerg Roedel 	end = ((void *)drhd) + header->length;
945672cf6dfSJoerg Roedel 
946672cf6dfSJoerg Roedel 	while (start < end && ret == 0) {
947672cf6dfSJoerg Roedel 		scope = start;
948672cf6dfSJoerg Roedel 		if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC)
949672cf6dfSJoerg Roedel 			ret = ir_parse_one_ioapic_scope(scope, iommu, drhd);
950672cf6dfSJoerg Roedel 		else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET)
951672cf6dfSJoerg Roedel 			ret = ir_parse_one_hpet_scope(scope, iommu, drhd);
952672cf6dfSJoerg Roedel 		start += scope->length;
953672cf6dfSJoerg Roedel 	}
954672cf6dfSJoerg Roedel 
955672cf6dfSJoerg Roedel 	return ret;
956672cf6dfSJoerg Roedel }
957672cf6dfSJoerg Roedel 
ir_remove_ioapic_hpet_scope(struct intel_iommu * iommu)958672cf6dfSJoerg Roedel static void ir_remove_ioapic_hpet_scope(struct intel_iommu *iommu)
959672cf6dfSJoerg Roedel {
960672cf6dfSJoerg Roedel 	int i;
961672cf6dfSJoerg Roedel 
962672cf6dfSJoerg Roedel 	for (i = 0; i < MAX_HPET_TBS; i++)
963672cf6dfSJoerg Roedel 		if (ir_hpet[i].iommu == iommu)
964672cf6dfSJoerg Roedel 			ir_hpet[i].iommu = NULL;
965672cf6dfSJoerg Roedel 
966672cf6dfSJoerg Roedel 	for (i = 0; i < MAX_IO_APICS; i++)
967672cf6dfSJoerg Roedel 		if (ir_ioapic[i].iommu == iommu)
968672cf6dfSJoerg Roedel 			ir_ioapic[i].iommu = NULL;
969672cf6dfSJoerg Roedel }
970672cf6dfSJoerg Roedel 
971672cf6dfSJoerg Roedel /*
972672cf6dfSJoerg Roedel  * Finds the assocaition between IOAPIC's and its Interrupt-remapping
973672cf6dfSJoerg Roedel  * hardware unit.
974672cf6dfSJoerg Roedel  */
parse_ioapics_under_ir(void)975672cf6dfSJoerg Roedel static int __init parse_ioapics_under_ir(void)
976672cf6dfSJoerg Roedel {
977672cf6dfSJoerg Roedel 	struct dmar_drhd_unit *drhd;
978672cf6dfSJoerg Roedel 	struct intel_iommu *iommu;
979672cf6dfSJoerg Roedel 	bool ir_supported = false;
980672cf6dfSJoerg Roedel 	int ioapic_idx;
981672cf6dfSJoerg Roedel 
982672cf6dfSJoerg Roedel 	for_each_iommu(iommu, drhd) {
983672cf6dfSJoerg Roedel 		int ret;
984672cf6dfSJoerg Roedel 
985672cf6dfSJoerg Roedel 		if (!ecap_ir_support(iommu->ecap))
986672cf6dfSJoerg Roedel 			continue;
987672cf6dfSJoerg Roedel 
988672cf6dfSJoerg Roedel 		ret = ir_parse_ioapic_hpet_scope(drhd->hdr, iommu);
989672cf6dfSJoerg Roedel 		if (ret)
990672cf6dfSJoerg Roedel 			return ret;
991672cf6dfSJoerg Roedel 
992672cf6dfSJoerg Roedel 		ir_supported = true;
993672cf6dfSJoerg Roedel 	}
994672cf6dfSJoerg Roedel 
995672cf6dfSJoerg Roedel 	if (!ir_supported)
996672cf6dfSJoerg Roedel 		return -ENODEV;
997672cf6dfSJoerg Roedel 
998672cf6dfSJoerg Roedel 	for (ioapic_idx = 0; ioapic_idx < nr_ioapics; ioapic_idx++) {
999672cf6dfSJoerg Roedel 		int ioapic_id = mpc_ioapic_id(ioapic_idx);
100060e5a939SThomas Gleixner 		if (!map_ioapic_to_iommu(ioapic_id)) {
1001672cf6dfSJoerg Roedel 			pr_err(FW_BUG "ioapic %d has no mapping iommu, "
1002672cf6dfSJoerg Roedel 			       "interrupt remapping will be disabled\n",
1003672cf6dfSJoerg Roedel 			       ioapic_id);
1004672cf6dfSJoerg Roedel 			return -1;
1005672cf6dfSJoerg Roedel 		}
1006672cf6dfSJoerg Roedel 	}
1007672cf6dfSJoerg Roedel 
1008672cf6dfSJoerg Roedel 	return 0;
1009672cf6dfSJoerg Roedel }
1010672cf6dfSJoerg Roedel 
ir_dev_scope_init(void)1011672cf6dfSJoerg Roedel static int __init ir_dev_scope_init(void)
1012672cf6dfSJoerg Roedel {
1013672cf6dfSJoerg Roedel 	int ret;
1014672cf6dfSJoerg Roedel 
1015672cf6dfSJoerg Roedel 	if (!irq_remapping_enabled)
1016672cf6dfSJoerg Roedel 		return 0;
1017672cf6dfSJoerg Roedel 
1018672cf6dfSJoerg Roedel 	down_write(&dmar_global_lock);
1019672cf6dfSJoerg Roedel 	ret = dmar_dev_scope_init();
1020672cf6dfSJoerg Roedel 	up_write(&dmar_global_lock);
1021672cf6dfSJoerg Roedel 
1022672cf6dfSJoerg Roedel 	return ret;
1023672cf6dfSJoerg Roedel }
1024672cf6dfSJoerg Roedel rootfs_initcall(ir_dev_scope_init);
1025672cf6dfSJoerg Roedel 
disable_irq_remapping(void)1026672cf6dfSJoerg Roedel static void disable_irq_remapping(void)
1027672cf6dfSJoerg Roedel {
1028672cf6dfSJoerg Roedel 	struct dmar_drhd_unit *drhd;
1029672cf6dfSJoerg Roedel 	struct intel_iommu *iommu = NULL;
1030672cf6dfSJoerg Roedel 
1031672cf6dfSJoerg Roedel 	/*
1032672cf6dfSJoerg Roedel 	 * Disable Interrupt-remapping for all the DRHD's now.
1033672cf6dfSJoerg Roedel 	 */
1034672cf6dfSJoerg Roedel 	for_each_iommu(iommu, drhd) {
1035672cf6dfSJoerg Roedel 		if (!ecap_ir_support(iommu->ecap))
1036672cf6dfSJoerg Roedel 			continue;
1037672cf6dfSJoerg Roedel 
1038672cf6dfSJoerg Roedel 		iommu_disable_irq_remapping(iommu);
1039672cf6dfSJoerg Roedel 	}
1040672cf6dfSJoerg Roedel 
1041672cf6dfSJoerg Roedel 	/*
1042672cf6dfSJoerg Roedel 	 * Clear Posted-Interrupts capability.
1043672cf6dfSJoerg Roedel 	 */
1044672cf6dfSJoerg Roedel 	if (!disable_irq_post)
1045672cf6dfSJoerg Roedel 		intel_irq_remap_ops.capability &= ~(1 << IRQ_POSTING_CAP);
1046672cf6dfSJoerg Roedel }
1047672cf6dfSJoerg Roedel 
reenable_irq_remapping(int eim)1048672cf6dfSJoerg Roedel static int reenable_irq_remapping(int eim)
1049672cf6dfSJoerg Roedel {
1050672cf6dfSJoerg Roedel 	struct dmar_drhd_unit *drhd;
1051672cf6dfSJoerg Roedel 	bool setup = false;
1052672cf6dfSJoerg Roedel 	struct intel_iommu *iommu = NULL;
1053672cf6dfSJoerg Roedel 
1054672cf6dfSJoerg Roedel 	for_each_iommu(iommu, drhd)
1055672cf6dfSJoerg Roedel 		if (iommu->qi)
1056672cf6dfSJoerg Roedel 			dmar_reenable_qi(iommu);
1057672cf6dfSJoerg Roedel 
1058672cf6dfSJoerg Roedel 	/*
1059672cf6dfSJoerg Roedel 	 * Setup Interrupt-remapping for all the DRHD's now.
1060672cf6dfSJoerg Roedel 	 */
1061672cf6dfSJoerg Roedel 	for_each_iommu(iommu, drhd) {
1062672cf6dfSJoerg Roedel 		if (!ecap_ir_support(iommu->ecap))
1063672cf6dfSJoerg Roedel 			continue;
1064672cf6dfSJoerg Roedel 
1065672cf6dfSJoerg Roedel 		/* Set up interrupt remapping for iommu.*/
1066672cf6dfSJoerg Roedel 		iommu_set_irq_remapping(iommu, eim);
1067672cf6dfSJoerg Roedel 		iommu_enable_irq_remapping(iommu);
1068672cf6dfSJoerg Roedel 		setup = true;
1069672cf6dfSJoerg Roedel 	}
1070672cf6dfSJoerg Roedel 
1071672cf6dfSJoerg Roedel 	if (!setup)
1072672cf6dfSJoerg Roedel 		goto error;
1073672cf6dfSJoerg Roedel 
1074672cf6dfSJoerg Roedel 	set_irq_posting_cap();
1075672cf6dfSJoerg Roedel 
1076672cf6dfSJoerg Roedel 	return 0;
1077672cf6dfSJoerg Roedel 
1078672cf6dfSJoerg Roedel error:
1079672cf6dfSJoerg Roedel 	/*
1080672cf6dfSJoerg Roedel 	 * handle error condition gracefully here!
1081672cf6dfSJoerg Roedel 	 */
1082672cf6dfSJoerg Roedel 	return -1;
1083672cf6dfSJoerg Roedel }
1084672cf6dfSJoerg Roedel 
108585a8dfc5SThomas Gleixner /*
108685a8dfc5SThomas Gleixner  * Store the MSI remapping domain pointer in the device if enabled.
108785a8dfc5SThomas Gleixner  *
108885a8dfc5SThomas Gleixner  * This is called from dmar_pci_bus_add_dev() so it works even when DMA
108985a8dfc5SThomas Gleixner  * remapping is disabled. Only update the pointer if the device is not
109085a8dfc5SThomas Gleixner  * already handled by a non default PCI/MSI interrupt domain. This protects
109185a8dfc5SThomas Gleixner  * e.g. VMD devices.
109285a8dfc5SThomas Gleixner  */
intel_irq_remap_add_device(struct dmar_pci_notify_info * info)109385a8dfc5SThomas Gleixner void intel_irq_remap_add_device(struct dmar_pci_notify_info *info)
109485a8dfc5SThomas Gleixner {
1095b6d5fc3aSThomas Gleixner 	if (!irq_remapping_enabled || !pci_dev_has_default_msi_parent_domain(info->dev))
109685a8dfc5SThomas Gleixner 		return;
109785a8dfc5SThomas Gleixner 
109885a8dfc5SThomas Gleixner 	dev_set_msi_domain(&info->dev->dev, map_dev_to_ir(info->dev));
109985a8dfc5SThomas Gleixner }
110085a8dfc5SThomas Gleixner 
prepare_irte(struct irte * irte,int vector,unsigned int dest)1101672cf6dfSJoerg Roedel static void prepare_irte(struct irte *irte, int vector, unsigned int dest)
1102672cf6dfSJoerg Roedel {
1103672cf6dfSJoerg Roedel 	memset(irte, 0, sizeof(*irte));
1104672cf6dfSJoerg Roedel 
1105672cf6dfSJoerg Roedel 	irte->present = 1;
11068c44963bSThomas Gleixner 	irte->dst_mode = apic->dest_mode_logical;
1107672cf6dfSJoerg Roedel 	/*
1108672cf6dfSJoerg Roedel 	 * Trigger mode in the IRTE will always be edge, and for IO-APIC, the
1109672cf6dfSJoerg Roedel 	 * actual level or edge trigger will be setup in the IO-APIC
1110672cf6dfSJoerg Roedel 	 * RTE. This will help simplify level triggered irq migration.
1111672cf6dfSJoerg Roedel 	 * For more details, see the comments (in io_apic.c) explainig IO-APIC
1112672cf6dfSJoerg Roedel 	 * irq migration in the presence of interrupt-remapping.
1113672cf6dfSJoerg Roedel 	*/
1114672cf6dfSJoerg Roedel 	irte->trigger_mode = 0;
111572161299SThomas Gleixner 	irte->dlvry_mode = apic->delivery_mode;
1116672cf6dfSJoerg Roedel 	irte->vector = vector;
1117672cf6dfSJoerg Roedel 	irte->dest_id = IRTE_DEST(dest);
1118672cf6dfSJoerg Roedel 	irte->redir_hint = 1;
1119672cf6dfSJoerg Roedel }
1120672cf6dfSJoerg Roedel 
1121672cf6dfSJoerg Roedel struct irq_remap_ops intel_irq_remap_ops = {
1122672cf6dfSJoerg Roedel 	.prepare		= intel_prepare_irq_remapping,
1123672cf6dfSJoerg Roedel 	.enable			= intel_enable_irq_remapping,
1124672cf6dfSJoerg Roedel 	.disable		= disable_irq_remapping,
1125672cf6dfSJoerg Roedel 	.reenable		= reenable_irq_remapping,
1126672cf6dfSJoerg Roedel 	.enable_faulting	= enable_drhd_fault_handling,
1127672cf6dfSJoerg Roedel };
1128672cf6dfSJoerg Roedel 
intel_ir_reconfigure_irte(struct irq_data * irqd,bool force)1129672cf6dfSJoerg Roedel static void intel_ir_reconfigure_irte(struct irq_data *irqd, bool force)
1130672cf6dfSJoerg Roedel {
1131672cf6dfSJoerg Roedel 	struct intel_ir_data *ir_data = irqd->chip_data;
1132672cf6dfSJoerg Roedel 	struct irte *irte = &ir_data->irte_entry;
1133672cf6dfSJoerg Roedel 	struct irq_cfg *cfg = irqd_cfg(irqd);
1134672cf6dfSJoerg Roedel 
1135672cf6dfSJoerg Roedel 	/*
1136672cf6dfSJoerg Roedel 	 * Atomically updates the IRTE with the new destination, vector
1137672cf6dfSJoerg Roedel 	 * and flushes the interrupt entry cache.
1138672cf6dfSJoerg Roedel 	 */
1139672cf6dfSJoerg Roedel 	irte->vector = cfg->vector;
1140672cf6dfSJoerg Roedel 	irte->dest_id = IRTE_DEST(cfg->dest_apicid);
1141672cf6dfSJoerg Roedel 
1142672cf6dfSJoerg Roedel 	/* Update the hardware only if the interrupt is in remapped mode. */
1143672cf6dfSJoerg Roedel 	if (force || ir_data->irq_2_iommu.mode == IRQ_REMAPPING)
1144672cf6dfSJoerg Roedel 		modify_irte(&ir_data->irq_2_iommu, irte);
1145672cf6dfSJoerg Roedel }
1146672cf6dfSJoerg Roedel 
1147672cf6dfSJoerg Roedel /*
1148672cf6dfSJoerg Roedel  * Migrate the IO-APIC irq in the presence of intr-remapping.
1149672cf6dfSJoerg Roedel  *
1150672cf6dfSJoerg Roedel  * For both level and edge triggered, irq migration is a simple atomic
1151672cf6dfSJoerg Roedel  * update(of vector and cpu destination) of IRTE and flush the hardware cache.
1152672cf6dfSJoerg Roedel  *
1153672cf6dfSJoerg Roedel  * For level triggered, we eliminate the io-apic RTE modification (with the
1154672cf6dfSJoerg Roedel  * updated vector information), by using a virtual vector (io-apic pin number).
1155672cf6dfSJoerg Roedel  * Real vector that is used for interrupting cpu will be coming from
1156672cf6dfSJoerg Roedel  * the interrupt-remapping table entry.
1157672cf6dfSJoerg Roedel  *
1158672cf6dfSJoerg Roedel  * As the migration is a simple atomic update of IRTE, the same mechanism
1159672cf6dfSJoerg Roedel  * is used to migrate MSI irq's in the presence of interrupt-remapping.
1160672cf6dfSJoerg Roedel  */
1161672cf6dfSJoerg Roedel static int
intel_ir_set_affinity(struct irq_data * data,const struct cpumask * mask,bool force)1162672cf6dfSJoerg Roedel intel_ir_set_affinity(struct irq_data *data, const struct cpumask *mask,
1163672cf6dfSJoerg Roedel 		      bool force)
1164672cf6dfSJoerg Roedel {
1165672cf6dfSJoerg Roedel 	struct irq_data *parent = data->parent_data;
1166672cf6dfSJoerg Roedel 	struct irq_cfg *cfg = irqd_cfg(data);
1167672cf6dfSJoerg Roedel 	int ret;
1168672cf6dfSJoerg Roedel 
1169672cf6dfSJoerg Roedel 	ret = parent->chip->irq_set_affinity(parent, mask, force);
1170672cf6dfSJoerg Roedel 	if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
1171672cf6dfSJoerg Roedel 		return ret;
1172672cf6dfSJoerg Roedel 
1173672cf6dfSJoerg Roedel 	intel_ir_reconfigure_irte(data, false);
1174672cf6dfSJoerg Roedel 	/*
1175672cf6dfSJoerg Roedel 	 * After this point, all the interrupts will start arriving
1176672cf6dfSJoerg Roedel 	 * at the new destination. So, time to cleanup the previous
1177672cf6dfSJoerg Roedel 	 * vector allocation.
1178672cf6dfSJoerg Roedel 	 */
1179*a539cc86SThomas Gleixner 	vector_schedule_cleanup(cfg);
1180672cf6dfSJoerg Roedel 
1181672cf6dfSJoerg Roedel 	return IRQ_SET_MASK_OK_DONE;
1182672cf6dfSJoerg Roedel }
1183672cf6dfSJoerg Roedel 
intel_ir_compose_msi_msg(struct irq_data * irq_data,struct msi_msg * msg)1184672cf6dfSJoerg Roedel static void intel_ir_compose_msi_msg(struct irq_data *irq_data,
1185672cf6dfSJoerg Roedel 				     struct msi_msg *msg)
1186672cf6dfSJoerg Roedel {
1187672cf6dfSJoerg Roedel 	struct intel_ir_data *ir_data = irq_data->chip_data;
1188672cf6dfSJoerg Roedel 
1189672cf6dfSJoerg Roedel 	*msg = ir_data->msi_entry;
1190672cf6dfSJoerg Roedel }
1191672cf6dfSJoerg Roedel 
intel_ir_set_vcpu_affinity(struct irq_data * data,void * info)1192672cf6dfSJoerg Roedel static int intel_ir_set_vcpu_affinity(struct irq_data *data, void *info)
1193672cf6dfSJoerg Roedel {
1194672cf6dfSJoerg Roedel 	struct intel_ir_data *ir_data = data->chip_data;
1195672cf6dfSJoerg Roedel 	struct vcpu_data *vcpu_pi_info = info;
1196672cf6dfSJoerg Roedel 
1197672cf6dfSJoerg Roedel 	/* stop posting interrupts, back to remapping mode */
1198672cf6dfSJoerg Roedel 	if (!vcpu_pi_info) {
1199672cf6dfSJoerg Roedel 		modify_irte(&ir_data->irq_2_iommu, &ir_data->irte_entry);
1200672cf6dfSJoerg Roedel 	} else {
1201672cf6dfSJoerg Roedel 		struct irte irte_pi;
1202672cf6dfSJoerg Roedel 
1203672cf6dfSJoerg Roedel 		/*
1204672cf6dfSJoerg Roedel 		 * We are not caching the posted interrupt entry. We
1205672cf6dfSJoerg Roedel 		 * copy the data from the remapped entry and modify
1206672cf6dfSJoerg Roedel 		 * the fields which are relevant for posted mode. The
1207672cf6dfSJoerg Roedel 		 * cached remapped entry is used for switching back to
1208672cf6dfSJoerg Roedel 		 * remapped mode.
1209672cf6dfSJoerg Roedel 		 */
1210672cf6dfSJoerg Roedel 		memset(&irte_pi, 0, sizeof(irte_pi));
1211672cf6dfSJoerg Roedel 		dmar_copy_shared_irte(&irte_pi, &ir_data->irte_entry);
1212672cf6dfSJoerg Roedel 
1213672cf6dfSJoerg Roedel 		/* Update the posted mode fields */
1214672cf6dfSJoerg Roedel 		irte_pi.p_pst = 1;
1215672cf6dfSJoerg Roedel 		irte_pi.p_urgent = 0;
1216672cf6dfSJoerg Roedel 		irte_pi.p_vector = vcpu_pi_info->vector;
1217672cf6dfSJoerg Roedel 		irte_pi.pda_l = (vcpu_pi_info->pi_desc_addr >>
1218672cf6dfSJoerg Roedel 				(32 - PDA_LOW_BIT)) & ~(-1UL << PDA_LOW_BIT);
1219672cf6dfSJoerg Roedel 		irte_pi.pda_h = (vcpu_pi_info->pi_desc_addr >> 32) &
1220672cf6dfSJoerg Roedel 				~(-1UL << PDA_HIGH_BIT);
1221672cf6dfSJoerg Roedel 
1222672cf6dfSJoerg Roedel 		modify_irte(&ir_data->irq_2_iommu, &irte_pi);
1223672cf6dfSJoerg Roedel 	}
1224672cf6dfSJoerg Roedel 
1225672cf6dfSJoerg Roedel 	return 0;
1226672cf6dfSJoerg Roedel }
1227672cf6dfSJoerg Roedel 
1228672cf6dfSJoerg Roedel static struct irq_chip intel_ir_chip = {
1229672cf6dfSJoerg Roedel 	.name			= "INTEL-IR",
1230672cf6dfSJoerg Roedel 	.irq_ack		= apic_ack_irq,
1231672cf6dfSJoerg Roedel 	.irq_set_affinity	= intel_ir_set_affinity,
1232672cf6dfSJoerg Roedel 	.irq_compose_msi_msg	= intel_ir_compose_msi_msg,
1233672cf6dfSJoerg Roedel 	.irq_set_vcpu_affinity	= intel_ir_set_vcpu_affinity,
1234672cf6dfSJoerg Roedel };
1235672cf6dfSJoerg Roedel 
fill_msi_msg(struct msi_msg * msg,u32 index,u32 subhandle)12365c0d0e2cSThomas Gleixner static void fill_msi_msg(struct msi_msg *msg, u32 index, u32 subhandle)
12375c0d0e2cSThomas Gleixner {
12385c0d0e2cSThomas Gleixner 	memset(msg, 0, sizeof(*msg));
12395c0d0e2cSThomas Gleixner 
12405c0d0e2cSThomas Gleixner 	msg->arch_addr_lo.dmar_base_address = X86_MSI_BASE_ADDRESS_LOW;
12415c0d0e2cSThomas Gleixner 	msg->arch_addr_lo.dmar_subhandle_valid = true;
12425c0d0e2cSThomas Gleixner 	msg->arch_addr_lo.dmar_format = true;
12435c0d0e2cSThomas Gleixner 	msg->arch_addr_lo.dmar_index_0_14 = index & 0x7FFF;
12445c0d0e2cSThomas Gleixner 	msg->arch_addr_lo.dmar_index_15 = !!(index & 0x8000);
12455c0d0e2cSThomas Gleixner 
12465c0d0e2cSThomas Gleixner 	msg->address_hi = X86_MSI_BASE_ADDRESS_HIGH;
12475c0d0e2cSThomas Gleixner 
12485c0d0e2cSThomas Gleixner 	msg->arch_data.dmar_subhandle = subhandle;
12495c0d0e2cSThomas Gleixner }
12505c0d0e2cSThomas Gleixner 
intel_irq_remapping_prepare_irte(struct intel_ir_data * data,struct irq_cfg * irq_cfg,struct irq_alloc_info * info,int index,int sub_handle)1251672cf6dfSJoerg Roedel static void intel_irq_remapping_prepare_irte(struct intel_ir_data *data,
1252672cf6dfSJoerg Roedel 					     struct irq_cfg *irq_cfg,
1253672cf6dfSJoerg Roedel 					     struct irq_alloc_info *info,
1254672cf6dfSJoerg Roedel 					     int index, int sub_handle)
1255672cf6dfSJoerg Roedel {
1256672cf6dfSJoerg Roedel 	struct irte *irte = &data->irte_entry;
1257672cf6dfSJoerg Roedel 
1258672cf6dfSJoerg Roedel 	prepare_irte(irte, irq_cfg->vector, irq_cfg->dest_apicid);
12595d5a9713SDavid Woodhouse 
1260672cf6dfSJoerg Roedel 	switch (info->type) {
1261672cf6dfSJoerg Roedel 	case X86_IRQ_ALLOC_TYPE_IOAPIC:
1262672cf6dfSJoerg Roedel 		/* Set source-id of interrupt request */
126333a65ba4SThomas Gleixner 		set_ioapic_sid(irte, info->devid);
1264672cf6dfSJoerg Roedel 		apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: Set IRTE entry (P:%d FPD:%d Dst_Mode:%d Redir_hint:%d Trig_Mode:%d Dlvry_Mode:%X Avail:%X Vector:%02X Dest:%08X SID:%04X SQ:%X SVT:%X)\n",
126533a65ba4SThomas Gleixner 			info->devid, irte->present, irte->fpd,
1266672cf6dfSJoerg Roedel 			irte->dst_mode, irte->redir_hint,
1267672cf6dfSJoerg Roedel 			irte->trigger_mode, irte->dlvry_mode,
1268672cf6dfSJoerg Roedel 			irte->avail, irte->vector, irte->dest_id,
1269672cf6dfSJoerg Roedel 			irte->sid, irte->sq, irte->svt);
12705d5a9713SDavid Woodhouse 		sub_handle = info->ioapic.pin;
1271672cf6dfSJoerg Roedel 		break;
1272672cf6dfSJoerg Roedel 	case X86_IRQ_ALLOC_TYPE_HPET:
12735d5a9713SDavid Woodhouse 		set_hpet_sid(irte, info->devid);
12745d5a9713SDavid Woodhouse 		break;
1275801b5e4cSThomas Gleixner 	case X86_IRQ_ALLOC_TYPE_PCI_MSI:
1276801b5e4cSThomas Gleixner 	case X86_IRQ_ALLOC_TYPE_PCI_MSIX:
12779b4a824bSJon Derrick 		set_msi_sid(irte,
12789b4a824bSJon Derrick 			    pci_real_dma_dev(msi_desc_to_pci_dev(info->desc)));
1279672cf6dfSJoerg Roedel 		break;
1280672cf6dfSJoerg Roedel 	default:
1281672cf6dfSJoerg Roedel 		BUG_ON(1);
1282672cf6dfSJoerg Roedel 		break;
1283672cf6dfSJoerg Roedel 	}
12845d5a9713SDavid Woodhouse 	fill_msi_msg(&data->msi_entry, index, sub_handle);
1285672cf6dfSJoerg Roedel }
1286672cf6dfSJoerg Roedel 
intel_free_irq_resources(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs)1287672cf6dfSJoerg Roedel static void intel_free_irq_resources(struct irq_domain *domain,
1288672cf6dfSJoerg Roedel 				     unsigned int virq, unsigned int nr_irqs)
1289672cf6dfSJoerg Roedel {
1290672cf6dfSJoerg Roedel 	struct irq_data *irq_data;
1291672cf6dfSJoerg Roedel 	struct intel_ir_data *data;
1292672cf6dfSJoerg Roedel 	struct irq_2_iommu *irq_iommu;
1293672cf6dfSJoerg Roedel 	unsigned long flags;
1294672cf6dfSJoerg Roedel 	int i;
1295672cf6dfSJoerg Roedel 	for (i = 0; i < nr_irqs; i++) {
1296672cf6dfSJoerg Roedel 		irq_data = irq_domain_get_irq_data(domain, virq  + i);
1297672cf6dfSJoerg Roedel 		if (irq_data && irq_data->chip_data) {
1298672cf6dfSJoerg Roedel 			data = irq_data->chip_data;
1299672cf6dfSJoerg Roedel 			irq_iommu = &data->irq_2_iommu;
1300672cf6dfSJoerg Roedel 			raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
1301672cf6dfSJoerg Roedel 			clear_entries(irq_iommu);
1302672cf6dfSJoerg Roedel 			raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
1303672cf6dfSJoerg Roedel 			irq_domain_reset_irq_data(irq_data);
1304672cf6dfSJoerg Roedel 			kfree(data);
1305672cf6dfSJoerg Roedel 		}
1306672cf6dfSJoerg Roedel 	}
1307672cf6dfSJoerg Roedel }
1308672cf6dfSJoerg Roedel 
intel_irq_remapping_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs,void * arg)1309672cf6dfSJoerg Roedel static int intel_irq_remapping_alloc(struct irq_domain *domain,
1310672cf6dfSJoerg Roedel 				     unsigned int virq, unsigned int nr_irqs,
1311672cf6dfSJoerg Roedel 				     void *arg)
1312672cf6dfSJoerg Roedel {
1313672cf6dfSJoerg Roedel 	struct intel_iommu *iommu = domain->host_data;
1314672cf6dfSJoerg Roedel 	struct irq_alloc_info *info = arg;
1315672cf6dfSJoerg Roedel 	struct intel_ir_data *data, *ird;
1316672cf6dfSJoerg Roedel 	struct irq_data *irq_data;
1317672cf6dfSJoerg Roedel 	struct irq_cfg *irq_cfg;
1318672cf6dfSJoerg Roedel 	int i, ret, index;
1319672cf6dfSJoerg Roedel 
1320672cf6dfSJoerg Roedel 	if (!info || !iommu)
1321672cf6dfSJoerg Roedel 		return -EINVAL;
1322527f378cSThomas Gleixner 	if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_PCI_MSI)
1323672cf6dfSJoerg Roedel 		return -EINVAL;
1324672cf6dfSJoerg Roedel 
1325672cf6dfSJoerg Roedel 	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
1326672cf6dfSJoerg Roedel 	if (ret < 0)
1327672cf6dfSJoerg Roedel 		return ret;
1328672cf6dfSJoerg Roedel 
1329672cf6dfSJoerg Roedel 	ret = -ENOMEM;
1330672cf6dfSJoerg Roedel 	data = kzalloc(sizeof(*data), GFP_KERNEL);
1331672cf6dfSJoerg Roedel 	if (!data)
1332672cf6dfSJoerg Roedel 		goto out_free_parent;
1333672cf6dfSJoerg Roedel 
1334672cf6dfSJoerg Roedel 	index = alloc_irte(iommu, &data->irq_2_iommu, nr_irqs);
1335672cf6dfSJoerg Roedel 	if (index < 0) {
1336672cf6dfSJoerg Roedel 		pr_warn("Failed to allocate IRTE\n");
1337672cf6dfSJoerg Roedel 		kfree(data);
1338672cf6dfSJoerg Roedel 		goto out_free_parent;
1339672cf6dfSJoerg Roedel 	}
1340672cf6dfSJoerg Roedel 
1341672cf6dfSJoerg Roedel 	for (i = 0; i < nr_irqs; i++) {
1342672cf6dfSJoerg Roedel 		irq_data = irq_domain_get_irq_data(domain, virq + i);
1343672cf6dfSJoerg Roedel 		irq_cfg = irqd_cfg(irq_data);
1344672cf6dfSJoerg Roedel 		if (!irq_data || !irq_cfg) {
1345ff2b46d7SDinghao Liu 			if (!i)
1346ff2b46d7SDinghao Liu 				kfree(data);
1347672cf6dfSJoerg Roedel 			ret = -EINVAL;
1348672cf6dfSJoerg Roedel 			goto out_free_data;
1349672cf6dfSJoerg Roedel 		}
1350672cf6dfSJoerg Roedel 
1351672cf6dfSJoerg Roedel 		if (i > 0) {
1352672cf6dfSJoerg Roedel 			ird = kzalloc(sizeof(*ird), GFP_KERNEL);
1353672cf6dfSJoerg Roedel 			if (!ird)
1354672cf6dfSJoerg Roedel 				goto out_free_data;
1355672cf6dfSJoerg Roedel 			/* Initialize the common data */
1356672cf6dfSJoerg Roedel 			ird->irq_2_iommu = data->irq_2_iommu;
1357672cf6dfSJoerg Roedel 			ird->irq_2_iommu.sub_handle = i;
1358672cf6dfSJoerg Roedel 		} else {
1359672cf6dfSJoerg Roedel 			ird = data;
1360672cf6dfSJoerg Roedel 		}
1361672cf6dfSJoerg Roedel 
1362672cf6dfSJoerg Roedel 		irq_data->hwirq = (index << 16) + i;
1363672cf6dfSJoerg Roedel 		irq_data->chip_data = ird;
1364672cf6dfSJoerg Roedel 		irq_data->chip = &intel_ir_chip;
1365672cf6dfSJoerg Roedel 		intel_irq_remapping_prepare_irte(ird, irq_cfg, info, index, i);
1366672cf6dfSJoerg Roedel 		irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT);
1367672cf6dfSJoerg Roedel 	}
1368672cf6dfSJoerg Roedel 	return 0;
1369672cf6dfSJoerg Roedel 
1370672cf6dfSJoerg Roedel out_free_data:
1371672cf6dfSJoerg Roedel 	intel_free_irq_resources(domain, virq, i);
1372672cf6dfSJoerg Roedel out_free_parent:
1373672cf6dfSJoerg Roedel 	irq_domain_free_irqs_common(domain, virq, nr_irqs);
1374672cf6dfSJoerg Roedel 	return ret;
1375672cf6dfSJoerg Roedel }
1376672cf6dfSJoerg Roedel 
intel_irq_remapping_free(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs)1377672cf6dfSJoerg Roedel static void intel_irq_remapping_free(struct irq_domain *domain,
1378672cf6dfSJoerg Roedel 				     unsigned int virq, unsigned int nr_irqs)
1379672cf6dfSJoerg Roedel {
1380672cf6dfSJoerg Roedel 	intel_free_irq_resources(domain, virq, nr_irqs);
1381672cf6dfSJoerg Roedel 	irq_domain_free_irqs_common(domain, virq, nr_irqs);
1382672cf6dfSJoerg Roedel }
1383672cf6dfSJoerg Roedel 
intel_irq_remapping_activate(struct irq_domain * domain,struct irq_data * irq_data,bool reserve)1384672cf6dfSJoerg Roedel static int intel_irq_remapping_activate(struct irq_domain *domain,
1385672cf6dfSJoerg Roedel 					struct irq_data *irq_data, bool reserve)
1386672cf6dfSJoerg Roedel {
1387672cf6dfSJoerg Roedel 	intel_ir_reconfigure_irte(irq_data, true);
1388672cf6dfSJoerg Roedel 	return 0;
1389672cf6dfSJoerg Roedel }
1390672cf6dfSJoerg Roedel 
intel_irq_remapping_deactivate(struct irq_domain * domain,struct irq_data * irq_data)1391672cf6dfSJoerg Roedel static void intel_irq_remapping_deactivate(struct irq_domain *domain,
1392672cf6dfSJoerg Roedel 					   struct irq_data *irq_data)
1393672cf6dfSJoerg Roedel {
1394672cf6dfSJoerg Roedel 	struct intel_ir_data *data = irq_data->chip_data;
1395672cf6dfSJoerg Roedel 	struct irte entry;
1396672cf6dfSJoerg Roedel 
1397672cf6dfSJoerg Roedel 	memset(&entry, 0, sizeof(entry));
1398672cf6dfSJoerg Roedel 	modify_irte(&data->irq_2_iommu, &entry);
1399672cf6dfSJoerg Roedel }
1400672cf6dfSJoerg Roedel 
intel_irq_remapping_select(struct irq_domain * d,struct irq_fwspec * fwspec,enum irq_domain_bus_token bus_token)1401a87fb465SDavid Woodhouse static int intel_irq_remapping_select(struct irq_domain *d,
1402a87fb465SDavid Woodhouse 				      struct irq_fwspec *fwspec,
1403a87fb465SDavid Woodhouse 				      enum irq_domain_bus_token bus_token)
1404a87fb465SDavid Woodhouse {
140579eb3581SDavid Woodhouse 	struct intel_iommu *iommu = NULL;
1406a87fb465SDavid Woodhouse 
140779eb3581SDavid Woodhouse 	if (x86_fwspec_is_ioapic(fwspec))
140879eb3581SDavid Woodhouse 		iommu = map_ioapic_to_iommu(fwspec->param[0]);
140979eb3581SDavid Woodhouse 	else if (x86_fwspec_is_hpet(fwspec))
141079eb3581SDavid Woodhouse 		iommu = map_hpet_to_iommu(fwspec->param[0]);
141179eb3581SDavid Woodhouse 
141279eb3581SDavid Woodhouse 	return iommu && d == iommu->ir_domain;
1413a87fb465SDavid Woodhouse }
1414a87fb465SDavid Woodhouse 
1415672cf6dfSJoerg Roedel static const struct irq_domain_ops intel_ir_domain_ops = {
1416a87fb465SDavid Woodhouse 	.select = intel_irq_remapping_select,
1417672cf6dfSJoerg Roedel 	.alloc = intel_irq_remapping_alloc,
1418672cf6dfSJoerg Roedel 	.free = intel_irq_remapping_free,
1419672cf6dfSJoerg Roedel 	.activate = intel_irq_remapping_activate,
1420672cf6dfSJoerg Roedel 	.deactivate = intel_irq_remapping_deactivate,
1421672cf6dfSJoerg Roedel };
1422672cf6dfSJoerg Roedel 
14239a945234SThomas Gleixner static const struct msi_parent_ops dmar_msi_parent_ops = {
1424810531a1SThomas Gleixner 	.supported_flags	= X86_VECTOR_MSI_FLAGS_SUPPORTED |
1425810531a1SThomas Gleixner 				  MSI_FLAG_MULTI_PCI_MSI |
1426810531a1SThomas Gleixner 				  MSI_FLAG_PCI_IMS,
14279a945234SThomas Gleixner 	.prefix			= "IR-",
14289a945234SThomas Gleixner 	.init_dev_msi_info	= msi_parent_init_dev_msi_info,
14299a945234SThomas Gleixner };
14309a945234SThomas Gleixner 
1431810531a1SThomas Gleixner static const struct msi_parent_ops virt_dmar_msi_parent_ops = {
1432810531a1SThomas Gleixner 	.supported_flags	= X86_VECTOR_MSI_FLAGS_SUPPORTED |
1433810531a1SThomas Gleixner 				  MSI_FLAG_MULTI_PCI_MSI,
1434810531a1SThomas Gleixner 	.prefix			= "vIR-",
1435810531a1SThomas Gleixner 	.init_dev_msi_info	= msi_parent_init_dev_msi_info,
1436810531a1SThomas Gleixner };
1437810531a1SThomas Gleixner 
1438672cf6dfSJoerg Roedel /*
1439672cf6dfSJoerg Roedel  * Support of Interrupt Remapping Unit Hotplug
1440672cf6dfSJoerg Roedel  */
dmar_ir_add(struct dmar_drhd_unit * dmaru,struct intel_iommu * iommu)1441672cf6dfSJoerg Roedel static int dmar_ir_add(struct dmar_drhd_unit *dmaru, struct intel_iommu *iommu)
1442672cf6dfSJoerg Roedel {
1443672cf6dfSJoerg Roedel 	int ret;
1444672cf6dfSJoerg Roedel 	int eim = x2apic_enabled();
1445672cf6dfSJoerg Roedel 
1446ad3d1902SKyung Min Park 	ret = intel_cap_audit(CAP_AUDIT_HOTPLUG_IRQR, iommu);
1447ad3d1902SKyung Min Park 	if (ret)
1448ad3d1902SKyung Min Park 		return ret;
1449ad3d1902SKyung Min Park 
1450672cf6dfSJoerg Roedel 	if (eim && !ecap_eim_support(iommu->ecap)) {
1451672cf6dfSJoerg Roedel 		pr_info("DRHD %Lx: EIM not supported by DRHD, ecap %Lx\n",
1452672cf6dfSJoerg Roedel 			iommu->reg_phys, iommu->ecap);
1453672cf6dfSJoerg Roedel 		return -ENODEV;
1454672cf6dfSJoerg Roedel 	}
1455672cf6dfSJoerg Roedel 
1456672cf6dfSJoerg Roedel 	if (ir_parse_ioapic_hpet_scope(dmaru->hdr, iommu)) {
1457672cf6dfSJoerg Roedel 		pr_warn("DRHD %Lx: failed to parse managed IOAPIC/HPET\n",
1458672cf6dfSJoerg Roedel 			iommu->reg_phys);
1459672cf6dfSJoerg Roedel 		return -ENODEV;
1460672cf6dfSJoerg Roedel 	}
1461672cf6dfSJoerg Roedel 
1462672cf6dfSJoerg Roedel 	/* TODO: check all IOAPICs are covered by IOMMU */
1463672cf6dfSJoerg Roedel 
1464672cf6dfSJoerg Roedel 	/* Setup Interrupt-remapping now. */
1465672cf6dfSJoerg Roedel 	ret = intel_setup_irq_remapping(iommu);
1466672cf6dfSJoerg Roedel 	if (ret) {
1467672cf6dfSJoerg Roedel 		pr_err("Failed to setup irq remapping for %s\n",
1468672cf6dfSJoerg Roedel 		       iommu->name);
1469672cf6dfSJoerg Roedel 		intel_teardown_irq_remapping(iommu);
1470672cf6dfSJoerg Roedel 		ir_remove_ioapic_hpet_scope(iommu);
1471672cf6dfSJoerg Roedel 	} else {
1472672cf6dfSJoerg Roedel 		iommu_enable_irq_remapping(iommu);
1473672cf6dfSJoerg Roedel 	}
1474672cf6dfSJoerg Roedel 
1475672cf6dfSJoerg Roedel 	return ret;
1476672cf6dfSJoerg Roedel }
1477672cf6dfSJoerg Roedel 
dmar_ir_hotplug(struct dmar_drhd_unit * dmaru,bool insert)1478672cf6dfSJoerg Roedel int dmar_ir_hotplug(struct dmar_drhd_unit *dmaru, bool insert)
1479672cf6dfSJoerg Roedel {
1480672cf6dfSJoerg Roedel 	int ret = 0;
1481672cf6dfSJoerg Roedel 	struct intel_iommu *iommu = dmaru->iommu;
1482672cf6dfSJoerg Roedel 
1483672cf6dfSJoerg Roedel 	if (!irq_remapping_enabled)
1484672cf6dfSJoerg Roedel 		return 0;
1485672cf6dfSJoerg Roedel 	if (iommu == NULL)
1486672cf6dfSJoerg Roedel 		return -EINVAL;
1487672cf6dfSJoerg Roedel 	if (!ecap_ir_support(iommu->ecap))
1488672cf6dfSJoerg Roedel 		return 0;
1489672cf6dfSJoerg Roedel 	if (irq_remapping_cap(IRQ_POSTING_CAP) &&
1490672cf6dfSJoerg Roedel 	    !cap_pi_support(iommu->cap))
1491672cf6dfSJoerg Roedel 		return -EBUSY;
1492672cf6dfSJoerg Roedel 
1493672cf6dfSJoerg Roedel 	if (insert) {
1494672cf6dfSJoerg Roedel 		if (!iommu->ir_table)
1495672cf6dfSJoerg Roedel 			ret = dmar_ir_add(dmaru, iommu);
1496672cf6dfSJoerg Roedel 	} else {
1497672cf6dfSJoerg Roedel 		if (iommu->ir_table) {
1498672cf6dfSJoerg Roedel 			if (!bitmap_empty(iommu->ir_table->bitmap,
1499672cf6dfSJoerg Roedel 					  INTR_REMAP_TABLE_ENTRIES)) {
1500672cf6dfSJoerg Roedel 				ret = -EBUSY;
1501672cf6dfSJoerg Roedel 			} else {
1502672cf6dfSJoerg Roedel 				iommu_disable_irq_remapping(iommu);
1503672cf6dfSJoerg Roedel 				intel_teardown_irq_remapping(iommu);
1504672cf6dfSJoerg Roedel 				ir_remove_ioapic_hpet_scope(iommu);
1505672cf6dfSJoerg Roedel 			}
1506672cf6dfSJoerg Roedel 		}
1507672cf6dfSJoerg Roedel 	}
1508672cf6dfSJoerg Roedel 
1509672cf6dfSJoerg Roedel 	return ret;
1510672cf6dfSJoerg Roedel }
1511