162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#define pr_fmt(fmt)     "DMAR-IR: " fmt
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/interrupt.h>
662306a36Sopenharmony_ci#include <linux/dmar.h>
762306a36Sopenharmony_ci#include <linux/spinlock.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/jiffies.h>
1062306a36Sopenharmony_ci#include <linux/hpet.h>
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci#include <linux/irq.h>
1362306a36Sopenharmony_ci#include <linux/acpi.h>
1462306a36Sopenharmony_ci#include <linux/irqdomain.h>
1562306a36Sopenharmony_ci#include <linux/crash_dump.h>
1662306a36Sopenharmony_ci#include <asm/io_apic.h>
1762306a36Sopenharmony_ci#include <asm/apic.h>
1862306a36Sopenharmony_ci#include <asm/smp.h>
1962306a36Sopenharmony_ci#include <asm/cpu.h>
2062306a36Sopenharmony_ci#include <asm/irq_remapping.h>
2162306a36Sopenharmony_ci#include <asm/pci-direct.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "iommu.h"
2462306a36Sopenharmony_ci#include "../irq_remapping.h"
2562306a36Sopenharmony_ci#include "cap_audit.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cienum irq_mode {
2862306a36Sopenharmony_ci	IRQ_REMAPPING,
2962306a36Sopenharmony_ci	IRQ_POSTING,
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct ioapic_scope {
3362306a36Sopenharmony_ci	struct intel_iommu *iommu;
3462306a36Sopenharmony_ci	unsigned int id;
3562306a36Sopenharmony_ci	unsigned int bus;	/* PCI bus number */
3662306a36Sopenharmony_ci	unsigned int devfn;	/* PCI devfn number */
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct hpet_scope {
4062306a36Sopenharmony_ci	struct intel_iommu *iommu;
4162306a36Sopenharmony_ci	u8 id;
4262306a36Sopenharmony_ci	unsigned int bus;
4362306a36Sopenharmony_ci	unsigned int devfn;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct irq_2_iommu {
4762306a36Sopenharmony_ci	struct intel_iommu *iommu;
4862306a36Sopenharmony_ci	u16 irte_index;
4962306a36Sopenharmony_ci	u16 sub_handle;
5062306a36Sopenharmony_ci	u8  irte_mask;
5162306a36Sopenharmony_ci	enum irq_mode mode;
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct intel_ir_data {
5562306a36Sopenharmony_ci	struct irq_2_iommu			irq_2_iommu;
5662306a36Sopenharmony_ci	struct irte				irte_entry;
5762306a36Sopenharmony_ci	union {
5862306a36Sopenharmony_ci		struct msi_msg			msi_entry;
5962306a36Sopenharmony_ci	};
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
6362306a36Sopenharmony_ci#define IRTE_DEST(dest) ((eim_mode) ? dest : dest << 8)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int __read_mostly eim_mode;
6662306a36Sopenharmony_cistatic struct ioapic_scope ir_ioapic[MAX_IO_APICS];
6762306a36Sopenharmony_cistatic struct hpet_scope ir_hpet[MAX_HPET_TBS];
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * Lock ordering:
7162306a36Sopenharmony_ci * ->dmar_global_lock
7262306a36Sopenharmony_ci *	->irq_2_ir_lock
7362306a36Sopenharmony_ci *		->qi->q_lock
7462306a36Sopenharmony_ci *	->iommu->register_lock
7562306a36Sopenharmony_ci * Note:
7662306a36Sopenharmony_ci * intel_irq_remap_ops.{supported,prepare,enable,disable,reenable} are called
7762306a36Sopenharmony_ci * in single-threaded environment with interrupt disabled, so no need to tabke
7862306a36Sopenharmony_ci * the dmar_global_lock.
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_ciDEFINE_RAW_SPINLOCK(irq_2_ir_lock);
8162306a36Sopenharmony_cistatic const struct irq_domain_ops intel_ir_domain_ops;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void iommu_disable_irq_remapping(struct intel_iommu *iommu);
8462306a36Sopenharmony_cistatic int __init parse_ioapics_under_ir(void);
8562306a36Sopenharmony_cistatic const struct msi_parent_ops dmar_msi_parent_ops, virt_dmar_msi_parent_ops;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic bool ir_pre_enabled(struct intel_iommu *iommu)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	return (iommu->flags & VTD_FLAG_IRQ_REMAP_PRE_ENABLED);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void clear_ir_pre_enabled(struct intel_iommu *iommu)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	iommu->flags &= ~VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void init_ir_status(struct intel_iommu *iommu)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	u32 gsts;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	gsts = readl(iommu->reg + DMAR_GSTS_REG);
10262306a36Sopenharmony_ci	if (gsts & DMA_GSTS_IRES)
10362306a36Sopenharmony_ci		iommu->flags |= VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int alloc_irte(struct intel_iommu *iommu,
10762306a36Sopenharmony_ci		      struct irq_2_iommu *irq_iommu, u16 count)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct ir_table *table = iommu->ir_table;
11062306a36Sopenharmony_ci	unsigned int mask = 0;
11162306a36Sopenharmony_ci	unsigned long flags;
11262306a36Sopenharmony_ci	int index;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (!count || !irq_iommu)
11562306a36Sopenharmony_ci		return -1;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (count > 1) {
11862306a36Sopenharmony_ci		count = __roundup_pow_of_two(count);
11962306a36Sopenharmony_ci		mask = ilog2(count);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (mask > ecap_max_handle_mask(iommu->ecap)) {
12362306a36Sopenharmony_ci		pr_err("Requested mask %x exceeds the max invalidation handle"
12462306a36Sopenharmony_ci		       " mask value %Lx\n", mask,
12562306a36Sopenharmony_ci		       ecap_max_handle_mask(iommu->ecap));
12662306a36Sopenharmony_ci		return -1;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
13062306a36Sopenharmony_ci	index = bitmap_find_free_region(table->bitmap,
13162306a36Sopenharmony_ci					INTR_REMAP_TABLE_ENTRIES, mask);
13262306a36Sopenharmony_ci	if (index < 0) {
13362306a36Sopenharmony_ci		pr_warn("IR%d: can't allocate an IRTE\n", iommu->seq_id);
13462306a36Sopenharmony_ci	} else {
13562306a36Sopenharmony_ci		irq_iommu->iommu = iommu;
13662306a36Sopenharmony_ci		irq_iommu->irte_index =  index;
13762306a36Sopenharmony_ci		irq_iommu->sub_handle = 0;
13862306a36Sopenharmony_ci		irq_iommu->irte_mask = mask;
13962306a36Sopenharmony_ci		irq_iommu->mode = IRQ_REMAPPING;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return index;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct qi_desc desc;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	desc.qw0 = QI_IEC_IIDEX(index) | QI_IEC_TYPE | QI_IEC_IM(mask)
15162306a36Sopenharmony_ci		   | QI_IEC_SELECTIVE;
15262306a36Sopenharmony_ci	desc.qw1 = 0;
15362306a36Sopenharmony_ci	desc.qw2 = 0;
15462306a36Sopenharmony_ci	desc.qw3 = 0;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return qi_submit_sync(iommu, &desc, 1, 0);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic int modify_irte(struct irq_2_iommu *irq_iommu,
16062306a36Sopenharmony_ci		       struct irte *irte_modified)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct intel_iommu *iommu;
16362306a36Sopenharmony_ci	unsigned long flags;
16462306a36Sopenharmony_ci	struct irte *irte;
16562306a36Sopenharmony_ci	int rc, index;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!irq_iommu)
16862306a36Sopenharmony_ci		return -1;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	iommu = irq_iommu->iommu;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	index = irq_iommu->irte_index + irq_iommu->sub_handle;
17562306a36Sopenharmony_ci	irte = &iommu->ir_table->base[index];
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if ((irte->pst == 1) || (irte_modified->pst == 1)) {
17862306a36Sopenharmony_ci		/*
17962306a36Sopenharmony_ci		 * We use cmpxchg16 to atomically update the 128-bit IRTE,
18062306a36Sopenharmony_ci		 * and it cannot be updated by the hardware or other processors
18162306a36Sopenharmony_ci		 * behind us, so the return value of cmpxchg16 should be the
18262306a36Sopenharmony_ci		 * same as the old value.
18362306a36Sopenharmony_ci		 */
18462306a36Sopenharmony_ci		u128 old = irte->irte;
18562306a36Sopenharmony_ci		WARN_ON(!try_cmpxchg128(&irte->irte, &old, irte_modified->irte));
18662306a36Sopenharmony_ci	} else {
18762306a36Sopenharmony_ci		WRITE_ONCE(irte->low, irte_modified->low);
18862306a36Sopenharmony_ci		WRITE_ONCE(irte->high, irte_modified->high);
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci	__iommu_flush_cache(iommu, irte, sizeof(*irte));
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	rc = qi_flush_iec(iommu, index, 0);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* Update iommu mode according to the IRTE mode */
19562306a36Sopenharmony_ci	irq_iommu->mode = irte->pst ? IRQ_POSTING : IRQ_REMAPPING;
19662306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return rc;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic struct intel_iommu *map_hpet_to_iommu(u8 hpet_id)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int i;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	for (i = 0; i < MAX_HPET_TBS; i++) {
20662306a36Sopenharmony_ci		if (ir_hpet[i].id == hpet_id && ir_hpet[i].iommu)
20762306a36Sopenharmony_ci			return ir_hpet[i].iommu;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	return NULL;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic struct intel_iommu *map_ioapic_to_iommu(int apic)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	int i;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	for (i = 0; i < MAX_IO_APICS; i++) {
21762306a36Sopenharmony_ci		if (ir_ioapic[i].id == apic && ir_ioapic[i].iommu)
21862306a36Sopenharmony_ci			return ir_ioapic[i].iommu;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci	return NULL;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic struct irq_domain *map_dev_to_ir(struct pci_dev *dev)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct dmar_drhd_unit *drhd = dmar_find_matched_drhd_unit(dev);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return drhd ? drhd->iommu->ir_domain : NULL;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic int clear_entries(struct irq_2_iommu *irq_iommu)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct irte *start, *entry, *end;
23362306a36Sopenharmony_ci	struct intel_iommu *iommu;
23462306a36Sopenharmony_ci	int index;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (irq_iommu->sub_handle)
23762306a36Sopenharmony_ci		return 0;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	iommu = irq_iommu->iommu;
24062306a36Sopenharmony_ci	index = irq_iommu->irte_index;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	start = iommu->ir_table->base + index;
24362306a36Sopenharmony_ci	end = start + (1 << irq_iommu->irte_mask);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	for (entry = start; entry < end; entry++) {
24662306a36Sopenharmony_ci		WRITE_ONCE(entry->low, 0);
24762306a36Sopenharmony_ci		WRITE_ONCE(entry->high, 0);
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	bitmap_release_region(iommu->ir_table->bitmap, index,
25062306a36Sopenharmony_ci			      irq_iommu->irte_mask);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return qi_flush_iec(iommu, index, irq_iommu->irte_mask);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci/*
25662306a36Sopenharmony_ci * source validation type
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_ci#define SVT_NO_VERIFY		0x0  /* no verification is required */
25962306a36Sopenharmony_ci#define SVT_VERIFY_SID_SQ	0x1  /* verify using SID and SQ fields */
26062306a36Sopenharmony_ci#define SVT_VERIFY_BUS		0x2  /* verify bus of request-id */
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/*
26362306a36Sopenharmony_ci * source-id qualifier
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_ci#define SQ_ALL_16	0x0  /* verify all 16 bits of request-id */
26662306a36Sopenharmony_ci#define SQ_13_IGNORE_1	0x1  /* verify most significant 13 bits, ignore
26762306a36Sopenharmony_ci			      * the third least significant bit
26862306a36Sopenharmony_ci			      */
26962306a36Sopenharmony_ci#define SQ_13_IGNORE_2	0x2  /* verify most significant 13 bits, ignore
27062306a36Sopenharmony_ci			      * the second and third least significant bits
27162306a36Sopenharmony_ci			      */
27262306a36Sopenharmony_ci#define SQ_13_IGNORE_3	0x3  /* verify most significant 13 bits, ignore
27362306a36Sopenharmony_ci			      * the least three significant bits
27462306a36Sopenharmony_ci			      */
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * set SVT, SQ and SID fields of irte to verify
27862306a36Sopenharmony_ci * source ids of interrupt requests
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_cistatic void set_irte_sid(struct irte *irte, unsigned int svt,
28162306a36Sopenharmony_ci			 unsigned int sq, unsigned int sid)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	if (disable_sourceid_checking)
28462306a36Sopenharmony_ci		svt = SVT_NO_VERIFY;
28562306a36Sopenharmony_ci	irte->svt = svt;
28662306a36Sopenharmony_ci	irte->sq = sq;
28762306a36Sopenharmony_ci	irte->sid = sid;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci/*
29162306a36Sopenharmony_ci * Set an IRTE to match only the bus number. Interrupt requests that reference
29262306a36Sopenharmony_ci * this IRTE must have a requester-id whose bus number is between or equal
29362306a36Sopenharmony_ci * to the start_bus and end_bus arguments.
29462306a36Sopenharmony_ci */
29562306a36Sopenharmony_cistatic void set_irte_verify_bus(struct irte *irte, unsigned int start_bus,
29662306a36Sopenharmony_ci				unsigned int end_bus)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
29962306a36Sopenharmony_ci		     (start_bus << 8) | end_bus);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int set_ioapic_sid(struct irte *irte, int apic)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	int i;
30562306a36Sopenharmony_ci	u16 sid = 0;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (!irte)
30862306a36Sopenharmony_ci		return -1;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	for (i = 0; i < MAX_IO_APICS; i++) {
31162306a36Sopenharmony_ci		if (ir_ioapic[i].iommu && ir_ioapic[i].id == apic) {
31262306a36Sopenharmony_ci			sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn;
31362306a36Sopenharmony_ci			break;
31462306a36Sopenharmony_ci		}
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (sid == 0) {
31862306a36Sopenharmony_ci		pr_warn("Failed to set source-id of IOAPIC (%d)\n", apic);
31962306a36Sopenharmony_ci		return -1;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, sid);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return 0;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int set_hpet_sid(struct irte *irte, u8 id)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	int i;
33062306a36Sopenharmony_ci	u16 sid = 0;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (!irte)
33362306a36Sopenharmony_ci		return -1;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	for (i = 0; i < MAX_HPET_TBS; i++) {
33662306a36Sopenharmony_ci		if (ir_hpet[i].iommu && ir_hpet[i].id == id) {
33762306a36Sopenharmony_ci			sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn;
33862306a36Sopenharmony_ci			break;
33962306a36Sopenharmony_ci		}
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (sid == 0) {
34362306a36Sopenharmony_ci		pr_warn("Failed to set source-id of HPET block (%d)\n", id);
34462306a36Sopenharmony_ci		return -1;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/*
34862306a36Sopenharmony_ci	 * Should really use SQ_ALL_16. Some platforms are broken.
34962306a36Sopenharmony_ci	 * While we figure out the right quirks for these broken platforms, use
35062306a36Sopenharmony_ci	 * SQ_13_IGNORE_3 for now.
35162306a36Sopenharmony_ci	 */
35262306a36Sopenharmony_ci	set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_13_IGNORE_3, sid);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return 0;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistruct set_msi_sid_data {
35862306a36Sopenharmony_ci	struct pci_dev *pdev;
35962306a36Sopenharmony_ci	u16 alias;
36062306a36Sopenharmony_ci	int count;
36162306a36Sopenharmony_ci	int busmatch_count;
36262306a36Sopenharmony_ci};
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct set_msi_sid_data *data = opaque;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (data->count == 0 || PCI_BUS_NUM(alias) == PCI_BUS_NUM(data->alias))
36962306a36Sopenharmony_ci		data->busmatch_count++;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	data->pdev = pdev;
37262306a36Sopenharmony_ci	data->alias = alias;
37362306a36Sopenharmony_ci	data->count++;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	return 0;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic int set_msi_sid(struct irte *irte, struct pci_dev *dev)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	struct set_msi_sid_data data;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (!irte || !dev)
38362306a36Sopenharmony_ci		return -1;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	data.count = 0;
38662306a36Sopenharmony_ci	data.busmatch_count = 0;
38762306a36Sopenharmony_ci	pci_for_each_dma_alias(dev, set_msi_sid_cb, &data);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/*
39062306a36Sopenharmony_ci	 * DMA alias provides us with a PCI device and alias.  The only case
39162306a36Sopenharmony_ci	 * where the it will return an alias on a different bus than the
39262306a36Sopenharmony_ci	 * device is the case of a PCIe-to-PCI bridge, where the alias is for
39362306a36Sopenharmony_ci	 * the subordinate bus.  In this case we can only verify the bus.
39462306a36Sopenharmony_ci	 *
39562306a36Sopenharmony_ci	 * If there are multiple aliases, all with the same bus number,
39662306a36Sopenharmony_ci	 * then all we can do is verify the bus. This is typical in NTB
39762306a36Sopenharmony_ci	 * hardware which use proxy IDs where the device will generate traffic
39862306a36Sopenharmony_ci	 * from multiple devfn numbers on the same bus.
39962306a36Sopenharmony_ci	 *
40062306a36Sopenharmony_ci	 * If the alias device is on a different bus than our source device
40162306a36Sopenharmony_ci	 * then we have a topology based alias, use it.
40262306a36Sopenharmony_ci	 *
40362306a36Sopenharmony_ci	 * Otherwise, the alias is for a device DMA quirk and we cannot
40462306a36Sopenharmony_ci	 * assume that MSI uses the same requester ID.  Therefore use the
40562306a36Sopenharmony_ci	 * original device.
40662306a36Sopenharmony_ci	 */
40762306a36Sopenharmony_ci	if (PCI_BUS_NUM(data.alias) != data.pdev->bus->number)
40862306a36Sopenharmony_ci		set_irte_verify_bus(irte, PCI_BUS_NUM(data.alias),
40962306a36Sopenharmony_ci				    dev->bus->number);
41062306a36Sopenharmony_ci	else if (data.count >= 2 && data.busmatch_count == data.count)
41162306a36Sopenharmony_ci		set_irte_verify_bus(irte, dev->bus->number, dev->bus->number);
41262306a36Sopenharmony_ci	else if (data.pdev->bus->number != dev->bus->number)
41362306a36Sopenharmony_ci		set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, data.alias);
41462306a36Sopenharmony_ci	else
41562306a36Sopenharmony_ci		set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
41662306a36Sopenharmony_ci			     pci_dev_id(dev));
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	return 0;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic int iommu_load_old_irte(struct intel_iommu *iommu)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct irte *old_ir_table;
42462306a36Sopenharmony_ci	phys_addr_t irt_phys;
42562306a36Sopenharmony_ci	unsigned int i;
42662306a36Sopenharmony_ci	size_t size;
42762306a36Sopenharmony_ci	u64 irta;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* Check whether the old ir-table has the same size as ours */
43062306a36Sopenharmony_ci	irta = dmar_readq(iommu->reg + DMAR_IRTA_REG);
43162306a36Sopenharmony_ci	if ((irta & INTR_REMAP_TABLE_REG_SIZE_MASK)
43262306a36Sopenharmony_ci	     != INTR_REMAP_TABLE_REG_SIZE)
43362306a36Sopenharmony_ci		return -EINVAL;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	irt_phys = irta & VTD_PAGE_MASK;
43662306a36Sopenharmony_ci	size     = INTR_REMAP_TABLE_ENTRIES*sizeof(struct irte);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* Map the old IR table */
43962306a36Sopenharmony_ci	old_ir_table = memremap(irt_phys, size, MEMREMAP_WB);
44062306a36Sopenharmony_ci	if (!old_ir_table)
44162306a36Sopenharmony_ci		return -ENOMEM;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/* Copy data over */
44462306a36Sopenharmony_ci	memcpy(iommu->ir_table->base, old_ir_table, size);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	__iommu_flush_cache(iommu, iommu->ir_table->base, size);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/*
44962306a36Sopenharmony_ci	 * Now check the table for used entries and mark those as
45062306a36Sopenharmony_ci	 * allocated in the bitmap
45162306a36Sopenharmony_ci	 */
45262306a36Sopenharmony_ci	for (i = 0; i < INTR_REMAP_TABLE_ENTRIES; i++) {
45362306a36Sopenharmony_ci		if (iommu->ir_table->base[i].present)
45462306a36Sopenharmony_ci			bitmap_set(iommu->ir_table->bitmap, i, 1);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	memunmap(old_ir_table);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return 0;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic void iommu_set_irq_remapping(struct intel_iommu *iommu, int mode)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	unsigned long flags;
46662306a36Sopenharmony_ci	u64 addr;
46762306a36Sopenharmony_ci	u32 sts;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	addr = virt_to_phys((void *)iommu->ir_table->base);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flags);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	dmar_writeq(iommu->reg + DMAR_IRTA_REG,
47462306a36Sopenharmony_ci		    (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	/* Set interrupt-remapping table pointer */
47762306a36Sopenharmony_ci	writel(iommu->gcmd | DMA_GCMD_SIRTP, iommu->reg + DMAR_GCMD_REG);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
48062306a36Sopenharmony_ci		      readl, (sts & DMA_GSTS_IRTPS), sts);
48162306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/*
48462306a36Sopenharmony_ci	 * Global invalidation of interrupt entry cache to make sure the
48562306a36Sopenharmony_ci	 * hardware uses the new irq remapping table.
48662306a36Sopenharmony_ci	 */
48762306a36Sopenharmony_ci	if (!cap_esirtps(iommu->cap))
48862306a36Sopenharmony_ci		qi_global_iec(iommu);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic void iommu_enable_irq_remapping(struct intel_iommu *iommu)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	unsigned long flags;
49462306a36Sopenharmony_ci	u32 sts;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flags);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/* Enable interrupt-remapping */
49962306a36Sopenharmony_ci	iommu->gcmd |= DMA_GCMD_IRE;
50062306a36Sopenharmony_ci	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
50162306a36Sopenharmony_ci	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
50262306a36Sopenharmony_ci		      readl, (sts & DMA_GSTS_IRES), sts);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* Block compatibility-format MSIs */
50562306a36Sopenharmony_ci	if (sts & DMA_GSTS_CFIS) {
50662306a36Sopenharmony_ci		iommu->gcmd &= ~DMA_GCMD_CFI;
50762306a36Sopenharmony_ci		writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
50862306a36Sopenharmony_ci		IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
50962306a36Sopenharmony_ci			      readl, !(sts & DMA_GSTS_CFIS), sts);
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/*
51362306a36Sopenharmony_ci	 * With CFI clear in the Global Command register, we should be
51462306a36Sopenharmony_ci	 * protected from dangerous (i.e. compatibility) interrupts
51562306a36Sopenharmony_ci	 * regardless of x2apic status.  Check just to be sure.
51662306a36Sopenharmony_ci	 */
51762306a36Sopenharmony_ci	if (sts & DMA_GSTS_CFIS)
51862306a36Sopenharmony_ci		WARN(1, KERN_WARNING
51962306a36Sopenharmony_ci			"Compatibility-format IRQs enabled despite intr remapping;\n"
52062306a36Sopenharmony_ci			"you are vulnerable to IRQ injection.\n");
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic int intel_setup_irq_remapping(struct intel_iommu *iommu)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct ir_table *ir_table;
52862306a36Sopenharmony_ci	struct fwnode_handle *fn;
52962306a36Sopenharmony_ci	unsigned long *bitmap;
53062306a36Sopenharmony_ci	struct page *pages;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (iommu->ir_table)
53362306a36Sopenharmony_ci		return 0;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	ir_table = kzalloc(sizeof(struct ir_table), GFP_KERNEL);
53662306a36Sopenharmony_ci	if (!ir_table)
53762306a36Sopenharmony_ci		return -ENOMEM;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	pages = alloc_pages_node(iommu->node, GFP_KERNEL | __GFP_ZERO,
54062306a36Sopenharmony_ci				 INTR_REMAP_PAGE_ORDER);
54162306a36Sopenharmony_ci	if (!pages) {
54262306a36Sopenharmony_ci		pr_err("IR%d: failed to allocate pages of order %d\n",
54362306a36Sopenharmony_ci		       iommu->seq_id, INTR_REMAP_PAGE_ORDER);
54462306a36Sopenharmony_ci		goto out_free_table;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	bitmap = bitmap_zalloc(INTR_REMAP_TABLE_ENTRIES, GFP_KERNEL);
54862306a36Sopenharmony_ci	if (bitmap == NULL) {
54962306a36Sopenharmony_ci		pr_err("IR%d: failed to allocate bitmap\n", iommu->seq_id);
55062306a36Sopenharmony_ci		goto out_free_pages;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	fn = irq_domain_alloc_named_id_fwnode("INTEL-IR", iommu->seq_id);
55462306a36Sopenharmony_ci	if (!fn)
55562306a36Sopenharmony_ci		goto out_free_bitmap;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	iommu->ir_domain =
55862306a36Sopenharmony_ci		irq_domain_create_hierarchy(arch_get_ir_parent_domain(),
55962306a36Sopenharmony_ci					    0, INTR_REMAP_TABLE_ENTRIES,
56062306a36Sopenharmony_ci					    fn, &intel_ir_domain_ops,
56162306a36Sopenharmony_ci					    iommu);
56262306a36Sopenharmony_ci	if (!iommu->ir_domain) {
56362306a36Sopenharmony_ci		pr_err("IR%d: failed to allocate irqdomain\n", iommu->seq_id);
56462306a36Sopenharmony_ci		goto out_free_fwnode;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	irq_domain_update_bus_token(iommu->ir_domain,  DOMAIN_BUS_DMAR);
56862306a36Sopenharmony_ci	iommu->ir_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT |
56962306a36Sopenharmony_ci				   IRQ_DOMAIN_FLAG_ISOLATED_MSI;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if (cap_caching_mode(iommu->cap))
57262306a36Sopenharmony_ci		iommu->ir_domain->msi_parent_ops = &virt_dmar_msi_parent_ops;
57362306a36Sopenharmony_ci	else
57462306a36Sopenharmony_ci		iommu->ir_domain->msi_parent_ops = &dmar_msi_parent_ops;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	ir_table->base = page_address(pages);
57762306a36Sopenharmony_ci	ir_table->bitmap = bitmap;
57862306a36Sopenharmony_ci	iommu->ir_table = ir_table;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/*
58162306a36Sopenharmony_ci	 * If the queued invalidation is already initialized,
58262306a36Sopenharmony_ci	 * shouldn't disable it.
58362306a36Sopenharmony_ci	 */
58462306a36Sopenharmony_ci	if (!iommu->qi) {
58562306a36Sopenharmony_ci		/*
58662306a36Sopenharmony_ci		 * Clear previous faults.
58762306a36Sopenharmony_ci		 */
58862306a36Sopenharmony_ci		dmar_fault(-1, iommu);
58962306a36Sopenharmony_ci		dmar_disable_qi(iommu);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		if (dmar_enable_qi(iommu)) {
59262306a36Sopenharmony_ci			pr_err("Failed to enable queued invalidation\n");
59362306a36Sopenharmony_ci			goto out_free_ir_domain;
59462306a36Sopenharmony_ci		}
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	init_ir_status(iommu);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (ir_pre_enabled(iommu)) {
60062306a36Sopenharmony_ci		if (!is_kdump_kernel()) {
60162306a36Sopenharmony_ci			pr_warn("IRQ remapping was enabled on %s but we are not in kdump mode\n",
60262306a36Sopenharmony_ci				iommu->name);
60362306a36Sopenharmony_ci			clear_ir_pre_enabled(iommu);
60462306a36Sopenharmony_ci			iommu_disable_irq_remapping(iommu);
60562306a36Sopenharmony_ci		} else if (iommu_load_old_irte(iommu))
60662306a36Sopenharmony_ci			pr_err("Failed to copy IR table for %s from previous kernel\n",
60762306a36Sopenharmony_ci			       iommu->name);
60862306a36Sopenharmony_ci		else
60962306a36Sopenharmony_ci			pr_info("Copied IR table for %s from previous kernel\n",
61062306a36Sopenharmony_ci				iommu->name);
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	iommu_set_irq_remapping(iommu, eim_mode);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return 0;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ciout_free_ir_domain:
61862306a36Sopenharmony_ci	irq_domain_remove(iommu->ir_domain);
61962306a36Sopenharmony_ci	iommu->ir_domain = NULL;
62062306a36Sopenharmony_ciout_free_fwnode:
62162306a36Sopenharmony_ci	irq_domain_free_fwnode(fn);
62262306a36Sopenharmony_ciout_free_bitmap:
62362306a36Sopenharmony_ci	bitmap_free(bitmap);
62462306a36Sopenharmony_ciout_free_pages:
62562306a36Sopenharmony_ci	__free_pages(pages, INTR_REMAP_PAGE_ORDER);
62662306a36Sopenharmony_ciout_free_table:
62762306a36Sopenharmony_ci	kfree(ir_table);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	iommu->ir_table  = NULL;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	return -ENOMEM;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic void intel_teardown_irq_remapping(struct intel_iommu *iommu)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct fwnode_handle *fn;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (iommu && iommu->ir_table) {
63962306a36Sopenharmony_ci		if (iommu->ir_domain) {
64062306a36Sopenharmony_ci			fn = iommu->ir_domain->fwnode;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci			irq_domain_remove(iommu->ir_domain);
64362306a36Sopenharmony_ci			irq_domain_free_fwnode(fn);
64462306a36Sopenharmony_ci			iommu->ir_domain = NULL;
64562306a36Sopenharmony_ci		}
64662306a36Sopenharmony_ci		free_pages((unsigned long)iommu->ir_table->base,
64762306a36Sopenharmony_ci			   INTR_REMAP_PAGE_ORDER);
64862306a36Sopenharmony_ci		bitmap_free(iommu->ir_table->bitmap);
64962306a36Sopenharmony_ci		kfree(iommu->ir_table);
65062306a36Sopenharmony_ci		iommu->ir_table = NULL;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci/*
65562306a36Sopenharmony_ci * Disable Interrupt Remapping.
65662306a36Sopenharmony_ci */
65762306a36Sopenharmony_cistatic void iommu_disable_irq_remapping(struct intel_iommu *iommu)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	unsigned long flags;
66062306a36Sopenharmony_ci	u32 sts;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (!ecap_ir_support(iommu->ecap))
66362306a36Sopenharmony_ci		return;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	/*
66662306a36Sopenharmony_ci	 * global invalidation of interrupt entry cache before disabling
66762306a36Sopenharmony_ci	 * interrupt-remapping.
66862306a36Sopenharmony_ci	 */
66962306a36Sopenharmony_ci	if (!cap_esirtps(iommu->cap))
67062306a36Sopenharmony_ci		qi_global_iec(iommu);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flags);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	sts = readl(iommu->reg + DMAR_GSTS_REG);
67562306a36Sopenharmony_ci	if (!(sts & DMA_GSTS_IRES))
67662306a36Sopenharmony_ci		goto end;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	iommu->gcmd &= ~DMA_GCMD_IRE;
67962306a36Sopenharmony_ci	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
68262306a36Sopenharmony_ci		      readl, !(sts & DMA_GSTS_IRES), sts);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ciend:
68562306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic int __init dmar_x2apic_optout(void)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	struct acpi_table_dmar *dmar;
69162306a36Sopenharmony_ci	dmar = (struct acpi_table_dmar *)dmar_tbl;
69262306a36Sopenharmony_ci	if (!dmar || no_x2apic_optout)
69362306a36Sopenharmony_ci		return 0;
69462306a36Sopenharmony_ci	return dmar->flags & DMAR_X2APIC_OPT_OUT;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic void __init intel_cleanup_irq_remapping(void)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct dmar_drhd_unit *drhd;
70062306a36Sopenharmony_ci	struct intel_iommu *iommu;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	for_each_iommu(iommu, drhd) {
70362306a36Sopenharmony_ci		if (ecap_ir_support(iommu->ecap)) {
70462306a36Sopenharmony_ci			iommu_disable_irq_remapping(iommu);
70562306a36Sopenharmony_ci			intel_teardown_irq_remapping(iommu);
70662306a36Sopenharmony_ci		}
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (x2apic_supported())
71062306a36Sopenharmony_ci		pr_warn("Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n");
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic int __init intel_prepare_irq_remapping(void)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct dmar_drhd_unit *drhd;
71662306a36Sopenharmony_ci	struct intel_iommu *iommu;
71762306a36Sopenharmony_ci	int eim = 0;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (irq_remap_broken) {
72062306a36Sopenharmony_ci		pr_warn("This system BIOS has enabled interrupt remapping\n"
72162306a36Sopenharmony_ci			"on a chipset that contains an erratum making that\n"
72262306a36Sopenharmony_ci			"feature unstable.  To maintain system stability\n"
72362306a36Sopenharmony_ci			"interrupt remapping is being disabled.  Please\n"
72462306a36Sopenharmony_ci			"contact your BIOS vendor for an update\n");
72562306a36Sopenharmony_ci		add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
72662306a36Sopenharmony_ci		return -ENODEV;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (dmar_table_init() < 0)
73062306a36Sopenharmony_ci		return -ENODEV;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (intel_cap_audit(CAP_AUDIT_STATIC_IRQR, NULL))
73362306a36Sopenharmony_ci		return -ENODEV;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	if (!dmar_ir_support())
73662306a36Sopenharmony_ci		return -ENODEV;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (parse_ioapics_under_ir()) {
73962306a36Sopenharmony_ci		pr_info("Not enabling interrupt remapping\n");
74062306a36Sopenharmony_ci		goto error;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	/* First make sure all IOMMUs support IRQ remapping */
74462306a36Sopenharmony_ci	for_each_iommu(iommu, drhd)
74562306a36Sopenharmony_ci		if (!ecap_ir_support(iommu->ecap))
74662306a36Sopenharmony_ci			goto error;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	/* Detect remapping mode: lapic or x2apic */
74962306a36Sopenharmony_ci	if (x2apic_supported()) {
75062306a36Sopenharmony_ci		eim = !dmar_x2apic_optout();
75162306a36Sopenharmony_ci		if (!eim) {
75262306a36Sopenharmony_ci			pr_info("x2apic is disabled because BIOS sets x2apic opt out bit.");
75362306a36Sopenharmony_ci			pr_info("Use 'intremap=no_x2apic_optout' to override the BIOS setting.\n");
75462306a36Sopenharmony_ci		}
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	for_each_iommu(iommu, drhd) {
75862306a36Sopenharmony_ci		if (eim && !ecap_eim_support(iommu->ecap)) {
75962306a36Sopenharmony_ci			pr_info("%s does not support EIM\n", iommu->name);
76062306a36Sopenharmony_ci			eim = 0;
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	eim_mode = eim;
76562306a36Sopenharmony_ci	if (eim)
76662306a36Sopenharmony_ci		pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/* Do the initializations early */
76962306a36Sopenharmony_ci	for_each_iommu(iommu, drhd) {
77062306a36Sopenharmony_ci		if (intel_setup_irq_remapping(iommu)) {
77162306a36Sopenharmony_ci			pr_err("Failed to setup irq remapping for %s\n",
77262306a36Sopenharmony_ci			       iommu->name);
77362306a36Sopenharmony_ci			goto error;
77462306a36Sopenharmony_ci		}
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	return 0;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cierror:
78062306a36Sopenharmony_ci	intel_cleanup_irq_remapping();
78162306a36Sopenharmony_ci	return -ENODEV;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci/*
78562306a36Sopenharmony_ci * Set Posted-Interrupts capability.
78662306a36Sopenharmony_ci */
78762306a36Sopenharmony_cistatic inline void set_irq_posting_cap(void)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	struct dmar_drhd_unit *drhd;
79062306a36Sopenharmony_ci	struct intel_iommu *iommu;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (!disable_irq_post) {
79362306a36Sopenharmony_ci		/*
79462306a36Sopenharmony_ci		 * If IRTE is in posted format, the 'pda' field goes across the
79562306a36Sopenharmony_ci		 * 64-bit boundary, we need use cmpxchg16b to atomically update
79662306a36Sopenharmony_ci		 * it. We only expose posted-interrupt when X86_FEATURE_CX16
79762306a36Sopenharmony_ci		 * is supported. Actually, hardware platforms supporting PI
79862306a36Sopenharmony_ci		 * should have X86_FEATURE_CX16 support, this has been confirmed
79962306a36Sopenharmony_ci		 * with Intel hardware guys.
80062306a36Sopenharmony_ci		 */
80162306a36Sopenharmony_ci		if (boot_cpu_has(X86_FEATURE_CX16))
80262306a36Sopenharmony_ci			intel_irq_remap_ops.capability |= 1 << IRQ_POSTING_CAP;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		for_each_iommu(iommu, drhd)
80562306a36Sopenharmony_ci			if (!cap_pi_support(iommu->cap)) {
80662306a36Sopenharmony_ci				intel_irq_remap_ops.capability &=
80762306a36Sopenharmony_ci						~(1 << IRQ_POSTING_CAP);
80862306a36Sopenharmony_ci				break;
80962306a36Sopenharmony_ci			}
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_cistatic int __init intel_enable_irq_remapping(void)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	struct dmar_drhd_unit *drhd;
81662306a36Sopenharmony_ci	struct intel_iommu *iommu;
81762306a36Sopenharmony_ci	bool setup = false;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	/*
82062306a36Sopenharmony_ci	 * Setup Interrupt-remapping for all the DRHD's now.
82162306a36Sopenharmony_ci	 */
82262306a36Sopenharmony_ci	for_each_iommu(iommu, drhd) {
82362306a36Sopenharmony_ci		if (!ir_pre_enabled(iommu))
82462306a36Sopenharmony_ci			iommu_enable_irq_remapping(iommu);
82562306a36Sopenharmony_ci		setup = true;
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (!setup)
82962306a36Sopenharmony_ci		goto error;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	irq_remapping_enabled = 1;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	set_irq_posting_cap();
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	pr_info("Enabled IRQ remapping in %s mode\n", eim_mode ? "x2apic" : "xapic");
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	return eim_mode ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cierror:
84062306a36Sopenharmony_ci	intel_cleanup_irq_remapping();
84162306a36Sopenharmony_ci	return -1;
84262306a36Sopenharmony_ci}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_cistatic int ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope,
84562306a36Sopenharmony_ci				   struct intel_iommu *iommu,
84662306a36Sopenharmony_ci				   struct acpi_dmar_hardware_unit *drhd)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct acpi_dmar_pci_path *path;
84962306a36Sopenharmony_ci	u8 bus;
85062306a36Sopenharmony_ci	int count, free = -1;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	bus = scope->bus;
85362306a36Sopenharmony_ci	path = (struct acpi_dmar_pci_path *)(scope + 1);
85462306a36Sopenharmony_ci	count = (scope->length - sizeof(struct acpi_dmar_device_scope))
85562306a36Sopenharmony_ci		/ sizeof(struct acpi_dmar_pci_path);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	while (--count > 0) {
85862306a36Sopenharmony_ci		/*
85962306a36Sopenharmony_ci		 * Access PCI directly due to the PCI
86062306a36Sopenharmony_ci		 * subsystem isn't initialized yet.
86162306a36Sopenharmony_ci		 */
86262306a36Sopenharmony_ci		bus = read_pci_config_byte(bus, path->device, path->function,
86362306a36Sopenharmony_ci					   PCI_SECONDARY_BUS);
86462306a36Sopenharmony_ci		path++;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	for (count = 0; count < MAX_HPET_TBS; count++) {
86862306a36Sopenharmony_ci		if (ir_hpet[count].iommu == iommu &&
86962306a36Sopenharmony_ci		    ir_hpet[count].id == scope->enumeration_id)
87062306a36Sopenharmony_ci			return 0;
87162306a36Sopenharmony_ci		else if (ir_hpet[count].iommu == NULL && free == -1)
87262306a36Sopenharmony_ci			free = count;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci	if (free == -1) {
87562306a36Sopenharmony_ci		pr_warn("Exceeded Max HPET blocks\n");
87662306a36Sopenharmony_ci		return -ENOSPC;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	ir_hpet[free].iommu = iommu;
88062306a36Sopenharmony_ci	ir_hpet[free].id    = scope->enumeration_id;
88162306a36Sopenharmony_ci	ir_hpet[free].bus   = bus;
88262306a36Sopenharmony_ci	ir_hpet[free].devfn = PCI_DEVFN(path->device, path->function);
88362306a36Sopenharmony_ci	pr_info("HPET id %d under DRHD base 0x%Lx\n",
88462306a36Sopenharmony_ci		scope->enumeration_id, drhd->address);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	return 0;
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic int ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
89062306a36Sopenharmony_ci				     struct intel_iommu *iommu,
89162306a36Sopenharmony_ci				     struct acpi_dmar_hardware_unit *drhd)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct acpi_dmar_pci_path *path;
89462306a36Sopenharmony_ci	u8 bus;
89562306a36Sopenharmony_ci	int count, free = -1;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	bus = scope->bus;
89862306a36Sopenharmony_ci	path = (struct acpi_dmar_pci_path *)(scope + 1);
89962306a36Sopenharmony_ci	count = (scope->length - sizeof(struct acpi_dmar_device_scope))
90062306a36Sopenharmony_ci		/ sizeof(struct acpi_dmar_pci_path);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	while (--count > 0) {
90362306a36Sopenharmony_ci		/*
90462306a36Sopenharmony_ci		 * Access PCI directly due to the PCI
90562306a36Sopenharmony_ci		 * subsystem isn't initialized yet.
90662306a36Sopenharmony_ci		 */
90762306a36Sopenharmony_ci		bus = read_pci_config_byte(bus, path->device, path->function,
90862306a36Sopenharmony_ci					   PCI_SECONDARY_BUS);
90962306a36Sopenharmony_ci		path++;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	for (count = 0; count < MAX_IO_APICS; count++) {
91362306a36Sopenharmony_ci		if (ir_ioapic[count].iommu == iommu &&
91462306a36Sopenharmony_ci		    ir_ioapic[count].id == scope->enumeration_id)
91562306a36Sopenharmony_ci			return 0;
91662306a36Sopenharmony_ci		else if (ir_ioapic[count].iommu == NULL && free == -1)
91762306a36Sopenharmony_ci			free = count;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci	if (free == -1) {
92062306a36Sopenharmony_ci		pr_warn("Exceeded Max IO APICS\n");
92162306a36Sopenharmony_ci		return -ENOSPC;
92262306a36Sopenharmony_ci	}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	ir_ioapic[free].bus   = bus;
92562306a36Sopenharmony_ci	ir_ioapic[free].devfn = PCI_DEVFN(path->device, path->function);
92662306a36Sopenharmony_ci	ir_ioapic[free].iommu = iommu;
92762306a36Sopenharmony_ci	ir_ioapic[free].id    = scope->enumeration_id;
92862306a36Sopenharmony_ci	pr_info("IOAPIC id %d under DRHD base  0x%Lx IOMMU %d\n",
92962306a36Sopenharmony_ci		scope->enumeration_id, drhd->address, iommu->seq_id);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	return 0;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header,
93562306a36Sopenharmony_ci				      struct intel_iommu *iommu)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	int ret = 0;
93862306a36Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
93962306a36Sopenharmony_ci	struct acpi_dmar_device_scope *scope;
94062306a36Sopenharmony_ci	void *start, *end;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	drhd = (struct acpi_dmar_hardware_unit *)header;
94362306a36Sopenharmony_ci	start = (void *)(drhd + 1);
94462306a36Sopenharmony_ci	end = ((void *)drhd) + header->length;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	while (start < end && ret == 0) {
94762306a36Sopenharmony_ci		scope = start;
94862306a36Sopenharmony_ci		if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC)
94962306a36Sopenharmony_ci			ret = ir_parse_one_ioapic_scope(scope, iommu, drhd);
95062306a36Sopenharmony_ci		else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET)
95162306a36Sopenharmony_ci			ret = ir_parse_one_hpet_scope(scope, iommu, drhd);
95262306a36Sopenharmony_ci		start += scope->length;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	return ret;
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_cistatic void ir_remove_ioapic_hpet_scope(struct intel_iommu *iommu)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	int i;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	for (i = 0; i < MAX_HPET_TBS; i++)
96362306a36Sopenharmony_ci		if (ir_hpet[i].iommu == iommu)
96462306a36Sopenharmony_ci			ir_hpet[i].iommu = NULL;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	for (i = 0; i < MAX_IO_APICS; i++)
96762306a36Sopenharmony_ci		if (ir_ioapic[i].iommu == iommu)
96862306a36Sopenharmony_ci			ir_ioapic[i].iommu = NULL;
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci/*
97262306a36Sopenharmony_ci * Finds the assocaition between IOAPIC's and its Interrupt-remapping
97362306a36Sopenharmony_ci * hardware unit.
97462306a36Sopenharmony_ci */
97562306a36Sopenharmony_cistatic int __init parse_ioapics_under_ir(void)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct dmar_drhd_unit *drhd;
97862306a36Sopenharmony_ci	struct intel_iommu *iommu;
97962306a36Sopenharmony_ci	bool ir_supported = false;
98062306a36Sopenharmony_ci	int ioapic_idx;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	for_each_iommu(iommu, drhd) {
98362306a36Sopenharmony_ci		int ret;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		if (!ecap_ir_support(iommu->ecap))
98662306a36Sopenharmony_ci			continue;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		ret = ir_parse_ioapic_hpet_scope(drhd->hdr, iommu);
98962306a36Sopenharmony_ci		if (ret)
99062306a36Sopenharmony_ci			return ret;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		ir_supported = true;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	if (!ir_supported)
99662306a36Sopenharmony_ci		return -ENODEV;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	for (ioapic_idx = 0; ioapic_idx < nr_ioapics; ioapic_idx++) {
99962306a36Sopenharmony_ci		int ioapic_id = mpc_ioapic_id(ioapic_idx);
100062306a36Sopenharmony_ci		if (!map_ioapic_to_iommu(ioapic_id)) {
100162306a36Sopenharmony_ci			pr_err(FW_BUG "ioapic %d has no mapping iommu, "
100262306a36Sopenharmony_ci			       "interrupt remapping will be disabled\n",
100362306a36Sopenharmony_ci			       ioapic_id);
100462306a36Sopenharmony_ci			return -1;
100562306a36Sopenharmony_ci		}
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	return 0;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic int __init ir_dev_scope_init(void)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	int ret;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (!irq_remapping_enabled)
101662306a36Sopenharmony_ci		return 0;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	down_write(&dmar_global_lock);
101962306a36Sopenharmony_ci	ret = dmar_dev_scope_init();
102062306a36Sopenharmony_ci	up_write(&dmar_global_lock);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	return ret;
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_cirootfs_initcall(ir_dev_scope_init);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistatic void disable_irq_remapping(void)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	struct dmar_drhd_unit *drhd;
102962306a36Sopenharmony_ci	struct intel_iommu *iommu = NULL;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	/*
103262306a36Sopenharmony_ci	 * Disable Interrupt-remapping for all the DRHD's now.
103362306a36Sopenharmony_ci	 */
103462306a36Sopenharmony_ci	for_each_iommu(iommu, drhd) {
103562306a36Sopenharmony_ci		if (!ecap_ir_support(iommu->ecap))
103662306a36Sopenharmony_ci			continue;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		iommu_disable_irq_remapping(iommu);
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/*
104262306a36Sopenharmony_ci	 * Clear Posted-Interrupts capability.
104362306a36Sopenharmony_ci	 */
104462306a36Sopenharmony_ci	if (!disable_irq_post)
104562306a36Sopenharmony_ci		intel_irq_remap_ops.capability &= ~(1 << IRQ_POSTING_CAP);
104662306a36Sopenharmony_ci}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_cistatic int reenable_irq_remapping(int eim)
104962306a36Sopenharmony_ci{
105062306a36Sopenharmony_ci	struct dmar_drhd_unit *drhd;
105162306a36Sopenharmony_ci	bool setup = false;
105262306a36Sopenharmony_ci	struct intel_iommu *iommu = NULL;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	for_each_iommu(iommu, drhd)
105562306a36Sopenharmony_ci		if (iommu->qi)
105662306a36Sopenharmony_ci			dmar_reenable_qi(iommu);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	/*
105962306a36Sopenharmony_ci	 * Setup Interrupt-remapping for all the DRHD's now.
106062306a36Sopenharmony_ci	 */
106162306a36Sopenharmony_ci	for_each_iommu(iommu, drhd) {
106262306a36Sopenharmony_ci		if (!ecap_ir_support(iommu->ecap))
106362306a36Sopenharmony_ci			continue;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci		/* Set up interrupt remapping for iommu.*/
106662306a36Sopenharmony_ci		iommu_set_irq_remapping(iommu, eim);
106762306a36Sopenharmony_ci		iommu_enable_irq_remapping(iommu);
106862306a36Sopenharmony_ci		setup = true;
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	if (!setup)
107262306a36Sopenharmony_ci		goto error;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	set_irq_posting_cap();
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	return 0;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_cierror:
107962306a36Sopenharmony_ci	/*
108062306a36Sopenharmony_ci	 * handle error condition gracefully here!
108162306a36Sopenharmony_ci	 */
108262306a36Sopenharmony_ci	return -1;
108362306a36Sopenharmony_ci}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci/*
108662306a36Sopenharmony_ci * Store the MSI remapping domain pointer in the device if enabled.
108762306a36Sopenharmony_ci *
108862306a36Sopenharmony_ci * This is called from dmar_pci_bus_add_dev() so it works even when DMA
108962306a36Sopenharmony_ci * remapping is disabled. Only update the pointer if the device is not
109062306a36Sopenharmony_ci * already handled by a non default PCI/MSI interrupt domain. This protects
109162306a36Sopenharmony_ci * e.g. VMD devices.
109262306a36Sopenharmony_ci */
109362306a36Sopenharmony_civoid intel_irq_remap_add_device(struct dmar_pci_notify_info *info)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	if (!irq_remapping_enabled || !pci_dev_has_default_msi_parent_domain(info->dev))
109662306a36Sopenharmony_ci		return;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	dev_set_msi_domain(&info->dev->dev, map_dev_to_ir(info->dev));
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_cistatic void prepare_irte(struct irte *irte, int vector, unsigned int dest)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	memset(irte, 0, sizeof(*irte));
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	irte->present = 1;
110662306a36Sopenharmony_ci	irte->dst_mode = apic->dest_mode_logical;
110762306a36Sopenharmony_ci	/*
110862306a36Sopenharmony_ci	 * Trigger mode in the IRTE will always be edge, and for IO-APIC, the
110962306a36Sopenharmony_ci	 * actual level or edge trigger will be setup in the IO-APIC
111062306a36Sopenharmony_ci	 * RTE. This will help simplify level triggered irq migration.
111162306a36Sopenharmony_ci	 * For more details, see the comments (in io_apic.c) explainig IO-APIC
111262306a36Sopenharmony_ci	 * irq migration in the presence of interrupt-remapping.
111362306a36Sopenharmony_ci	*/
111462306a36Sopenharmony_ci	irte->trigger_mode = 0;
111562306a36Sopenharmony_ci	irte->dlvry_mode = apic->delivery_mode;
111662306a36Sopenharmony_ci	irte->vector = vector;
111762306a36Sopenharmony_ci	irte->dest_id = IRTE_DEST(dest);
111862306a36Sopenharmony_ci	irte->redir_hint = 1;
111962306a36Sopenharmony_ci}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_cistruct irq_remap_ops intel_irq_remap_ops = {
112262306a36Sopenharmony_ci	.prepare		= intel_prepare_irq_remapping,
112362306a36Sopenharmony_ci	.enable			= intel_enable_irq_remapping,
112462306a36Sopenharmony_ci	.disable		= disable_irq_remapping,
112562306a36Sopenharmony_ci	.reenable		= reenable_irq_remapping,
112662306a36Sopenharmony_ci	.enable_faulting	= enable_drhd_fault_handling,
112762306a36Sopenharmony_ci};
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic void intel_ir_reconfigure_irte(struct irq_data *irqd, bool force)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	struct intel_ir_data *ir_data = irqd->chip_data;
113262306a36Sopenharmony_ci	struct irte *irte = &ir_data->irte_entry;
113362306a36Sopenharmony_ci	struct irq_cfg *cfg = irqd_cfg(irqd);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	/*
113662306a36Sopenharmony_ci	 * Atomically updates the IRTE with the new destination, vector
113762306a36Sopenharmony_ci	 * and flushes the interrupt entry cache.
113862306a36Sopenharmony_ci	 */
113962306a36Sopenharmony_ci	irte->vector = cfg->vector;
114062306a36Sopenharmony_ci	irte->dest_id = IRTE_DEST(cfg->dest_apicid);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	/* Update the hardware only if the interrupt is in remapped mode. */
114362306a36Sopenharmony_ci	if (force || ir_data->irq_2_iommu.mode == IRQ_REMAPPING)
114462306a36Sopenharmony_ci		modify_irte(&ir_data->irq_2_iommu, irte);
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci/*
114862306a36Sopenharmony_ci * Migrate the IO-APIC irq in the presence of intr-remapping.
114962306a36Sopenharmony_ci *
115062306a36Sopenharmony_ci * For both level and edge triggered, irq migration is a simple atomic
115162306a36Sopenharmony_ci * update(of vector and cpu destination) of IRTE and flush the hardware cache.
115262306a36Sopenharmony_ci *
115362306a36Sopenharmony_ci * For level triggered, we eliminate the io-apic RTE modification (with the
115462306a36Sopenharmony_ci * updated vector information), by using a virtual vector (io-apic pin number).
115562306a36Sopenharmony_ci * Real vector that is used for interrupting cpu will be coming from
115662306a36Sopenharmony_ci * the interrupt-remapping table entry.
115762306a36Sopenharmony_ci *
115862306a36Sopenharmony_ci * As the migration is a simple atomic update of IRTE, the same mechanism
115962306a36Sopenharmony_ci * is used to migrate MSI irq's in the presence of interrupt-remapping.
116062306a36Sopenharmony_ci */
116162306a36Sopenharmony_cistatic int
116262306a36Sopenharmony_ciintel_ir_set_affinity(struct irq_data *data, const struct cpumask *mask,
116362306a36Sopenharmony_ci		      bool force)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	struct irq_data *parent = data->parent_data;
116662306a36Sopenharmony_ci	struct irq_cfg *cfg = irqd_cfg(data);
116762306a36Sopenharmony_ci	int ret;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	ret = parent->chip->irq_set_affinity(parent, mask, force);
117062306a36Sopenharmony_ci	if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
117162306a36Sopenharmony_ci		return ret;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	intel_ir_reconfigure_irte(data, false);
117462306a36Sopenharmony_ci	/*
117562306a36Sopenharmony_ci	 * After this point, all the interrupts will start arriving
117662306a36Sopenharmony_ci	 * at the new destination. So, time to cleanup the previous
117762306a36Sopenharmony_ci	 * vector allocation.
117862306a36Sopenharmony_ci	 */
117962306a36Sopenharmony_ci	vector_schedule_cleanup(cfg);
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	return IRQ_SET_MASK_OK_DONE;
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic void intel_ir_compose_msi_msg(struct irq_data *irq_data,
118562306a36Sopenharmony_ci				     struct msi_msg *msg)
118662306a36Sopenharmony_ci{
118762306a36Sopenharmony_ci	struct intel_ir_data *ir_data = irq_data->chip_data;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	*msg = ir_data->msi_entry;
119062306a36Sopenharmony_ci}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_cistatic int intel_ir_set_vcpu_affinity(struct irq_data *data, void *info)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	struct intel_ir_data *ir_data = data->chip_data;
119562306a36Sopenharmony_ci	struct vcpu_data *vcpu_pi_info = info;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	/* stop posting interrupts, back to remapping mode */
119862306a36Sopenharmony_ci	if (!vcpu_pi_info) {
119962306a36Sopenharmony_ci		modify_irte(&ir_data->irq_2_iommu, &ir_data->irte_entry);
120062306a36Sopenharmony_ci	} else {
120162306a36Sopenharmony_ci		struct irte irte_pi;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci		/*
120462306a36Sopenharmony_ci		 * We are not caching the posted interrupt entry. We
120562306a36Sopenharmony_ci		 * copy the data from the remapped entry and modify
120662306a36Sopenharmony_ci		 * the fields which are relevant for posted mode. The
120762306a36Sopenharmony_ci		 * cached remapped entry is used for switching back to
120862306a36Sopenharmony_ci		 * remapped mode.
120962306a36Sopenharmony_ci		 */
121062306a36Sopenharmony_ci		memset(&irte_pi, 0, sizeof(irte_pi));
121162306a36Sopenharmony_ci		dmar_copy_shared_irte(&irte_pi, &ir_data->irte_entry);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci		/* Update the posted mode fields */
121462306a36Sopenharmony_ci		irte_pi.p_pst = 1;
121562306a36Sopenharmony_ci		irte_pi.p_urgent = 0;
121662306a36Sopenharmony_ci		irte_pi.p_vector = vcpu_pi_info->vector;
121762306a36Sopenharmony_ci		irte_pi.pda_l = (vcpu_pi_info->pi_desc_addr >>
121862306a36Sopenharmony_ci				(32 - PDA_LOW_BIT)) & ~(-1UL << PDA_LOW_BIT);
121962306a36Sopenharmony_ci		irte_pi.pda_h = (vcpu_pi_info->pi_desc_addr >> 32) &
122062306a36Sopenharmony_ci				~(-1UL << PDA_HIGH_BIT);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		modify_irte(&ir_data->irq_2_iommu, &irte_pi);
122362306a36Sopenharmony_ci	}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	return 0;
122662306a36Sopenharmony_ci}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_cistatic struct irq_chip intel_ir_chip = {
122962306a36Sopenharmony_ci	.name			= "INTEL-IR",
123062306a36Sopenharmony_ci	.irq_ack		= apic_ack_irq,
123162306a36Sopenharmony_ci	.irq_set_affinity	= intel_ir_set_affinity,
123262306a36Sopenharmony_ci	.irq_compose_msi_msg	= intel_ir_compose_msi_msg,
123362306a36Sopenharmony_ci	.irq_set_vcpu_affinity	= intel_ir_set_vcpu_affinity,
123462306a36Sopenharmony_ci};
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_cistatic void fill_msi_msg(struct msi_msg *msg, u32 index, u32 subhandle)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	memset(msg, 0, sizeof(*msg));
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	msg->arch_addr_lo.dmar_base_address = X86_MSI_BASE_ADDRESS_LOW;
124162306a36Sopenharmony_ci	msg->arch_addr_lo.dmar_subhandle_valid = true;
124262306a36Sopenharmony_ci	msg->arch_addr_lo.dmar_format = true;
124362306a36Sopenharmony_ci	msg->arch_addr_lo.dmar_index_0_14 = index & 0x7FFF;
124462306a36Sopenharmony_ci	msg->arch_addr_lo.dmar_index_15 = !!(index & 0x8000);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	msg->address_hi = X86_MSI_BASE_ADDRESS_HIGH;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	msg->arch_data.dmar_subhandle = subhandle;
124962306a36Sopenharmony_ci}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_cistatic void intel_irq_remapping_prepare_irte(struct intel_ir_data *data,
125262306a36Sopenharmony_ci					     struct irq_cfg *irq_cfg,
125362306a36Sopenharmony_ci					     struct irq_alloc_info *info,
125462306a36Sopenharmony_ci					     int index, int sub_handle)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct irte *irte = &data->irte_entry;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	prepare_irte(irte, irq_cfg->vector, irq_cfg->dest_apicid);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	switch (info->type) {
126162306a36Sopenharmony_ci	case X86_IRQ_ALLOC_TYPE_IOAPIC:
126262306a36Sopenharmony_ci		/* Set source-id of interrupt request */
126362306a36Sopenharmony_ci		set_ioapic_sid(irte, info->devid);
126462306a36Sopenharmony_ci		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",
126562306a36Sopenharmony_ci			info->devid, irte->present, irte->fpd,
126662306a36Sopenharmony_ci			irte->dst_mode, irte->redir_hint,
126762306a36Sopenharmony_ci			irte->trigger_mode, irte->dlvry_mode,
126862306a36Sopenharmony_ci			irte->avail, irte->vector, irte->dest_id,
126962306a36Sopenharmony_ci			irte->sid, irte->sq, irte->svt);
127062306a36Sopenharmony_ci		sub_handle = info->ioapic.pin;
127162306a36Sopenharmony_ci		break;
127262306a36Sopenharmony_ci	case X86_IRQ_ALLOC_TYPE_HPET:
127362306a36Sopenharmony_ci		set_hpet_sid(irte, info->devid);
127462306a36Sopenharmony_ci		break;
127562306a36Sopenharmony_ci	case X86_IRQ_ALLOC_TYPE_PCI_MSI:
127662306a36Sopenharmony_ci	case X86_IRQ_ALLOC_TYPE_PCI_MSIX:
127762306a36Sopenharmony_ci		set_msi_sid(irte,
127862306a36Sopenharmony_ci			    pci_real_dma_dev(msi_desc_to_pci_dev(info->desc)));
127962306a36Sopenharmony_ci		break;
128062306a36Sopenharmony_ci	default:
128162306a36Sopenharmony_ci		BUG_ON(1);
128262306a36Sopenharmony_ci		break;
128362306a36Sopenharmony_ci	}
128462306a36Sopenharmony_ci	fill_msi_msg(&data->msi_entry, index, sub_handle);
128562306a36Sopenharmony_ci}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_cistatic void intel_free_irq_resources(struct irq_domain *domain,
128862306a36Sopenharmony_ci				     unsigned int virq, unsigned int nr_irqs)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	struct irq_data *irq_data;
129162306a36Sopenharmony_ci	struct intel_ir_data *data;
129262306a36Sopenharmony_ci	struct irq_2_iommu *irq_iommu;
129362306a36Sopenharmony_ci	unsigned long flags;
129462306a36Sopenharmony_ci	int i;
129562306a36Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
129662306a36Sopenharmony_ci		irq_data = irq_domain_get_irq_data(domain, virq  + i);
129762306a36Sopenharmony_ci		if (irq_data && irq_data->chip_data) {
129862306a36Sopenharmony_ci			data = irq_data->chip_data;
129962306a36Sopenharmony_ci			irq_iommu = &data->irq_2_iommu;
130062306a36Sopenharmony_ci			raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
130162306a36Sopenharmony_ci			clear_entries(irq_iommu);
130262306a36Sopenharmony_ci			raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
130362306a36Sopenharmony_ci			irq_domain_reset_irq_data(irq_data);
130462306a36Sopenharmony_ci			kfree(data);
130562306a36Sopenharmony_ci		}
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic int intel_irq_remapping_alloc(struct irq_domain *domain,
131062306a36Sopenharmony_ci				     unsigned int virq, unsigned int nr_irqs,
131162306a36Sopenharmony_ci				     void *arg)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	struct intel_iommu *iommu = domain->host_data;
131462306a36Sopenharmony_ci	struct irq_alloc_info *info = arg;
131562306a36Sopenharmony_ci	struct intel_ir_data *data, *ird;
131662306a36Sopenharmony_ci	struct irq_data *irq_data;
131762306a36Sopenharmony_ci	struct irq_cfg *irq_cfg;
131862306a36Sopenharmony_ci	int i, ret, index;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	if (!info || !iommu)
132162306a36Sopenharmony_ci		return -EINVAL;
132262306a36Sopenharmony_ci	if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_PCI_MSI)
132362306a36Sopenharmony_ci		return -EINVAL;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
132662306a36Sopenharmony_ci	if (ret < 0)
132762306a36Sopenharmony_ci		return ret;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	ret = -ENOMEM;
133062306a36Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
133162306a36Sopenharmony_ci	if (!data)
133262306a36Sopenharmony_ci		goto out_free_parent;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	index = alloc_irte(iommu, &data->irq_2_iommu, nr_irqs);
133562306a36Sopenharmony_ci	if (index < 0) {
133662306a36Sopenharmony_ci		pr_warn("Failed to allocate IRTE\n");
133762306a36Sopenharmony_ci		kfree(data);
133862306a36Sopenharmony_ci		goto out_free_parent;
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
134262306a36Sopenharmony_ci		irq_data = irq_domain_get_irq_data(domain, virq + i);
134362306a36Sopenharmony_ci		irq_cfg = irqd_cfg(irq_data);
134462306a36Sopenharmony_ci		if (!irq_data || !irq_cfg) {
134562306a36Sopenharmony_ci			if (!i)
134662306a36Sopenharmony_ci				kfree(data);
134762306a36Sopenharmony_ci			ret = -EINVAL;
134862306a36Sopenharmony_ci			goto out_free_data;
134962306a36Sopenharmony_ci		}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci		if (i > 0) {
135262306a36Sopenharmony_ci			ird = kzalloc(sizeof(*ird), GFP_KERNEL);
135362306a36Sopenharmony_ci			if (!ird)
135462306a36Sopenharmony_ci				goto out_free_data;
135562306a36Sopenharmony_ci			/* Initialize the common data */
135662306a36Sopenharmony_ci			ird->irq_2_iommu = data->irq_2_iommu;
135762306a36Sopenharmony_ci			ird->irq_2_iommu.sub_handle = i;
135862306a36Sopenharmony_ci		} else {
135962306a36Sopenharmony_ci			ird = data;
136062306a36Sopenharmony_ci		}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci		irq_data->hwirq = (index << 16) + i;
136362306a36Sopenharmony_ci		irq_data->chip_data = ird;
136462306a36Sopenharmony_ci		irq_data->chip = &intel_ir_chip;
136562306a36Sopenharmony_ci		intel_irq_remapping_prepare_irte(ird, irq_cfg, info, index, i);
136662306a36Sopenharmony_ci		irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT);
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci	return 0;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ciout_free_data:
137162306a36Sopenharmony_ci	intel_free_irq_resources(domain, virq, i);
137262306a36Sopenharmony_ciout_free_parent:
137362306a36Sopenharmony_ci	irq_domain_free_irqs_common(domain, virq, nr_irqs);
137462306a36Sopenharmony_ci	return ret;
137562306a36Sopenharmony_ci}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_cistatic void intel_irq_remapping_free(struct irq_domain *domain,
137862306a36Sopenharmony_ci				     unsigned int virq, unsigned int nr_irqs)
137962306a36Sopenharmony_ci{
138062306a36Sopenharmony_ci	intel_free_irq_resources(domain, virq, nr_irqs);
138162306a36Sopenharmony_ci	irq_domain_free_irqs_common(domain, virq, nr_irqs);
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistatic int intel_irq_remapping_activate(struct irq_domain *domain,
138562306a36Sopenharmony_ci					struct irq_data *irq_data, bool reserve)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	intel_ir_reconfigure_irte(irq_data, true);
138862306a36Sopenharmony_ci	return 0;
138962306a36Sopenharmony_ci}
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_cistatic void intel_irq_remapping_deactivate(struct irq_domain *domain,
139262306a36Sopenharmony_ci					   struct irq_data *irq_data)
139362306a36Sopenharmony_ci{
139462306a36Sopenharmony_ci	struct intel_ir_data *data = irq_data->chip_data;
139562306a36Sopenharmony_ci	struct irte entry;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	memset(&entry, 0, sizeof(entry));
139862306a36Sopenharmony_ci	modify_irte(&data->irq_2_iommu, &entry);
139962306a36Sopenharmony_ci}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cistatic int intel_irq_remapping_select(struct irq_domain *d,
140262306a36Sopenharmony_ci				      struct irq_fwspec *fwspec,
140362306a36Sopenharmony_ci				      enum irq_domain_bus_token bus_token)
140462306a36Sopenharmony_ci{
140562306a36Sopenharmony_ci	struct intel_iommu *iommu = NULL;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	if (x86_fwspec_is_ioapic(fwspec))
140862306a36Sopenharmony_ci		iommu = map_ioapic_to_iommu(fwspec->param[0]);
140962306a36Sopenharmony_ci	else if (x86_fwspec_is_hpet(fwspec))
141062306a36Sopenharmony_ci		iommu = map_hpet_to_iommu(fwspec->param[0]);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	return iommu && d == iommu->ir_domain;
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_cistatic const struct irq_domain_ops intel_ir_domain_ops = {
141662306a36Sopenharmony_ci	.select = intel_irq_remapping_select,
141762306a36Sopenharmony_ci	.alloc = intel_irq_remapping_alloc,
141862306a36Sopenharmony_ci	.free = intel_irq_remapping_free,
141962306a36Sopenharmony_ci	.activate = intel_irq_remapping_activate,
142062306a36Sopenharmony_ci	.deactivate = intel_irq_remapping_deactivate,
142162306a36Sopenharmony_ci};
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_cistatic const struct msi_parent_ops dmar_msi_parent_ops = {
142462306a36Sopenharmony_ci	.supported_flags	= X86_VECTOR_MSI_FLAGS_SUPPORTED |
142562306a36Sopenharmony_ci				  MSI_FLAG_MULTI_PCI_MSI |
142662306a36Sopenharmony_ci				  MSI_FLAG_PCI_IMS,
142762306a36Sopenharmony_ci	.prefix			= "IR-",
142862306a36Sopenharmony_ci	.init_dev_msi_info	= msi_parent_init_dev_msi_info,
142962306a36Sopenharmony_ci};
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_cistatic const struct msi_parent_ops virt_dmar_msi_parent_ops = {
143262306a36Sopenharmony_ci	.supported_flags	= X86_VECTOR_MSI_FLAGS_SUPPORTED |
143362306a36Sopenharmony_ci				  MSI_FLAG_MULTI_PCI_MSI,
143462306a36Sopenharmony_ci	.prefix			= "vIR-",
143562306a36Sopenharmony_ci	.init_dev_msi_info	= msi_parent_init_dev_msi_info,
143662306a36Sopenharmony_ci};
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci/*
143962306a36Sopenharmony_ci * Support of Interrupt Remapping Unit Hotplug
144062306a36Sopenharmony_ci */
144162306a36Sopenharmony_cistatic int dmar_ir_add(struct dmar_drhd_unit *dmaru, struct intel_iommu *iommu)
144262306a36Sopenharmony_ci{
144362306a36Sopenharmony_ci	int ret;
144462306a36Sopenharmony_ci	int eim = x2apic_enabled();
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	ret = intel_cap_audit(CAP_AUDIT_HOTPLUG_IRQR, iommu);
144762306a36Sopenharmony_ci	if (ret)
144862306a36Sopenharmony_ci		return ret;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	if (eim && !ecap_eim_support(iommu->ecap)) {
145162306a36Sopenharmony_ci		pr_info("DRHD %Lx: EIM not supported by DRHD, ecap %Lx\n",
145262306a36Sopenharmony_ci			iommu->reg_phys, iommu->ecap);
145362306a36Sopenharmony_ci		return -ENODEV;
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	if (ir_parse_ioapic_hpet_scope(dmaru->hdr, iommu)) {
145762306a36Sopenharmony_ci		pr_warn("DRHD %Lx: failed to parse managed IOAPIC/HPET\n",
145862306a36Sopenharmony_ci			iommu->reg_phys);
145962306a36Sopenharmony_ci		return -ENODEV;
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	/* TODO: check all IOAPICs are covered by IOMMU */
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	/* Setup Interrupt-remapping now. */
146562306a36Sopenharmony_ci	ret = intel_setup_irq_remapping(iommu);
146662306a36Sopenharmony_ci	if (ret) {
146762306a36Sopenharmony_ci		pr_err("Failed to setup irq remapping for %s\n",
146862306a36Sopenharmony_ci		       iommu->name);
146962306a36Sopenharmony_ci		intel_teardown_irq_remapping(iommu);
147062306a36Sopenharmony_ci		ir_remove_ioapic_hpet_scope(iommu);
147162306a36Sopenharmony_ci	} else {
147262306a36Sopenharmony_ci		iommu_enable_irq_remapping(iommu);
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	return ret;
147662306a36Sopenharmony_ci}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ciint dmar_ir_hotplug(struct dmar_drhd_unit *dmaru, bool insert)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	int ret = 0;
148162306a36Sopenharmony_ci	struct intel_iommu *iommu = dmaru->iommu;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	if (!irq_remapping_enabled)
148462306a36Sopenharmony_ci		return 0;
148562306a36Sopenharmony_ci	if (iommu == NULL)
148662306a36Sopenharmony_ci		return -EINVAL;
148762306a36Sopenharmony_ci	if (!ecap_ir_support(iommu->ecap))
148862306a36Sopenharmony_ci		return 0;
148962306a36Sopenharmony_ci	if (irq_remapping_cap(IRQ_POSTING_CAP) &&
149062306a36Sopenharmony_ci	    !cap_pi_support(iommu->cap))
149162306a36Sopenharmony_ci		return -EBUSY;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	if (insert) {
149462306a36Sopenharmony_ci		if (!iommu->ir_table)
149562306a36Sopenharmony_ci			ret = dmar_ir_add(dmaru, iommu);
149662306a36Sopenharmony_ci	} else {
149762306a36Sopenharmony_ci		if (iommu->ir_table) {
149862306a36Sopenharmony_ci			if (!bitmap_empty(iommu->ir_table->bitmap,
149962306a36Sopenharmony_ci					  INTR_REMAP_TABLE_ENTRIES)) {
150062306a36Sopenharmony_ci				ret = -EBUSY;
150162306a36Sopenharmony_ci			} else {
150262306a36Sopenharmony_ci				iommu_disable_irq_remapping(iommu);
150362306a36Sopenharmony_ci				intel_teardown_irq_remapping(iommu);
150462306a36Sopenharmony_ci				ir_remove_ioapic_hpet_scope(iommu);
150562306a36Sopenharmony_ci			}
150662306a36Sopenharmony_ci		}
150762306a36Sopenharmony_ci	}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	return ret;
151062306a36Sopenharmony_ci}
1511