18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#define pr_fmt(fmt)     "DMAR-IR: " fmt
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
68c2ecf20Sopenharmony_ci#include <linux/dmar.h>
78c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
108c2ecf20Sopenharmony_ci#include <linux/hpet.h>
118c2ecf20Sopenharmony_ci#include <linux/pci.h>
128c2ecf20Sopenharmony_ci#include <linux/irq.h>
138c2ecf20Sopenharmony_ci#include <linux/intel-iommu.h>
148c2ecf20Sopenharmony_ci#include <linux/acpi.h>
158c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
168c2ecf20Sopenharmony_ci#include <linux/crash_dump.h>
178c2ecf20Sopenharmony_ci#include <asm/io_apic.h>
188c2ecf20Sopenharmony_ci#include <asm/apic.h>
198c2ecf20Sopenharmony_ci#include <asm/smp.h>
208c2ecf20Sopenharmony_ci#include <asm/cpu.h>
218c2ecf20Sopenharmony_ci#include <asm/irq_remapping.h>
228c2ecf20Sopenharmony_ci#include <asm/pci-direct.h>
238c2ecf20Sopenharmony_ci#include <asm/msidef.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "../irq_remapping.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cienum irq_mode {
288c2ecf20Sopenharmony_ci	IRQ_REMAPPING,
298c2ecf20Sopenharmony_ci	IRQ_POSTING,
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct ioapic_scope {
338c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
348c2ecf20Sopenharmony_ci	unsigned int id;
358c2ecf20Sopenharmony_ci	unsigned int bus;	/* PCI bus number */
368c2ecf20Sopenharmony_ci	unsigned int devfn;	/* PCI devfn number */
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct hpet_scope {
408c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
418c2ecf20Sopenharmony_ci	u8 id;
428c2ecf20Sopenharmony_ci	unsigned int bus;
438c2ecf20Sopenharmony_ci	unsigned int devfn;
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct irq_2_iommu {
478c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
488c2ecf20Sopenharmony_ci	u16 irte_index;
498c2ecf20Sopenharmony_ci	u16 sub_handle;
508c2ecf20Sopenharmony_ci	u8  irte_mask;
518c2ecf20Sopenharmony_ci	enum irq_mode mode;
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistruct intel_ir_data {
558c2ecf20Sopenharmony_ci	struct irq_2_iommu			irq_2_iommu;
568c2ecf20Sopenharmony_ci	struct irte				irte_entry;
578c2ecf20Sopenharmony_ci	union {
588c2ecf20Sopenharmony_ci		struct msi_msg			msi_entry;
598c2ecf20Sopenharmony_ci	};
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
638c2ecf20Sopenharmony_ci#define IRTE_DEST(dest) ((eim_mode) ? dest : dest << 8)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int __read_mostly eim_mode;
668c2ecf20Sopenharmony_cistatic struct ioapic_scope ir_ioapic[MAX_IO_APICS];
678c2ecf20Sopenharmony_cistatic struct hpet_scope ir_hpet[MAX_HPET_TBS];
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * Lock ordering:
718c2ecf20Sopenharmony_ci * ->dmar_global_lock
728c2ecf20Sopenharmony_ci *	->irq_2_ir_lock
738c2ecf20Sopenharmony_ci *		->qi->q_lock
748c2ecf20Sopenharmony_ci *	->iommu->register_lock
758c2ecf20Sopenharmony_ci * Note:
768c2ecf20Sopenharmony_ci * intel_irq_remap_ops.{supported,prepare,enable,disable,reenable} are called
778c2ecf20Sopenharmony_ci * in single-threaded environment with interrupt disabled, so no need to tabke
788c2ecf20Sopenharmony_ci * the dmar_global_lock.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_ciDEFINE_RAW_SPINLOCK(irq_2_ir_lock);
818c2ecf20Sopenharmony_cistatic const struct irq_domain_ops intel_ir_domain_ops;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void iommu_disable_irq_remapping(struct intel_iommu *iommu);
848c2ecf20Sopenharmony_cistatic int __init parse_ioapics_under_ir(void);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic bool ir_pre_enabled(struct intel_iommu *iommu)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	return (iommu->flags & VTD_FLAG_IRQ_REMAP_PRE_ENABLED);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void clear_ir_pre_enabled(struct intel_iommu *iommu)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	iommu->flags &= ~VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void init_ir_status(struct intel_iommu *iommu)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	u32 gsts;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	gsts = readl(iommu->reg + DMAR_GSTS_REG);
1018c2ecf20Sopenharmony_ci	if (gsts & DMA_GSTS_IRES)
1028c2ecf20Sopenharmony_ci		iommu->flags |= VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int alloc_irte(struct intel_iommu *iommu,
1068c2ecf20Sopenharmony_ci		      struct irq_2_iommu *irq_iommu, u16 count)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct ir_table *table = iommu->ir_table;
1098c2ecf20Sopenharmony_ci	unsigned int mask = 0;
1108c2ecf20Sopenharmony_ci	unsigned long flags;
1118c2ecf20Sopenharmony_ci	int index;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (!count || !irq_iommu)
1148c2ecf20Sopenharmony_ci		return -1;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (count > 1) {
1178c2ecf20Sopenharmony_ci		count = __roundup_pow_of_two(count);
1188c2ecf20Sopenharmony_ci		mask = ilog2(count);
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (mask > ecap_max_handle_mask(iommu->ecap)) {
1228c2ecf20Sopenharmony_ci		pr_err("Requested mask %x exceeds the max invalidation handle"
1238c2ecf20Sopenharmony_ci		       " mask value %Lx\n", mask,
1248c2ecf20Sopenharmony_ci		       ecap_max_handle_mask(iommu->ecap));
1258c2ecf20Sopenharmony_ci		return -1;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
1298c2ecf20Sopenharmony_ci	index = bitmap_find_free_region(table->bitmap,
1308c2ecf20Sopenharmony_ci					INTR_REMAP_TABLE_ENTRIES, mask);
1318c2ecf20Sopenharmony_ci	if (index < 0) {
1328c2ecf20Sopenharmony_ci		pr_warn("IR%d: can't allocate an IRTE\n", iommu->seq_id);
1338c2ecf20Sopenharmony_ci	} else {
1348c2ecf20Sopenharmony_ci		irq_iommu->iommu = iommu;
1358c2ecf20Sopenharmony_ci		irq_iommu->irte_index =  index;
1368c2ecf20Sopenharmony_ci		irq_iommu->sub_handle = 0;
1378c2ecf20Sopenharmony_ci		irq_iommu->irte_mask = mask;
1388c2ecf20Sopenharmony_ci		irq_iommu->mode = IRQ_REMAPPING;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return index;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	struct qi_desc desc;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	desc.qw0 = QI_IEC_IIDEX(index) | QI_IEC_TYPE | QI_IEC_IM(mask)
1508c2ecf20Sopenharmony_ci		   | QI_IEC_SELECTIVE;
1518c2ecf20Sopenharmony_ci	desc.qw1 = 0;
1528c2ecf20Sopenharmony_ci	desc.qw2 = 0;
1538c2ecf20Sopenharmony_ci	desc.qw3 = 0;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	return qi_submit_sync(iommu, &desc, 1, 0);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int modify_irte(struct irq_2_iommu *irq_iommu,
1598c2ecf20Sopenharmony_ci		       struct irte *irte_modified)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
1628c2ecf20Sopenharmony_ci	unsigned long flags;
1638c2ecf20Sopenharmony_ci	struct irte *irte;
1648c2ecf20Sopenharmony_ci	int rc, index;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (!irq_iommu)
1678c2ecf20Sopenharmony_ci		return -1;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	iommu = irq_iommu->iommu;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	index = irq_iommu->irte_index + irq_iommu->sub_handle;
1748c2ecf20Sopenharmony_ci	irte = &iommu->ir_table->base[index];
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE)
1778c2ecf20Sopenharmony_ci	if ((irte->pst == 1) || (irte_modified->pst == 1)) {
1788c2ecf20Sopenharmony_ci		bool ret;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		ret = cmpxchg_double(&irte->low, &irte->high,
1818c2ecf20Sopenharmony_ci				     irte->low, irte->high,
1828c2ecf20Sopenharmony_ci				     irte_modified->low, irte_modified->high);
1838c2ecf20Sopenharmony_ci		/*
1848c2ecf20Sopenharmony_ci		 * We use cmpxchg16 to atomically update the 128-bit IRTE,
1858c2ecf20Sopenharmony_ci		 * and it cannot be updated by the hardware or other processors
1868c2ecf20Sopenharmony_ci		 * behind us, so the return value of cmpxchg16 should be the
1878c2ecf20Sopenharmony_ci		 * same as the old value.
1888c2ecf20Sopenharmony_ci		 */
1898c2ecf20Sopenharmony_ci		WARN_ON(!ret);
1908c2ecf20Sopenharmony_ci	} else
1918c2ecf20Sopenharmony_ci#endif
1928c2ecf20Sopenharmony_ci	{
1938c2ecf20Sopenharmony_ci		set_64bit(&irte->low, irte_modified->low);
1948c2ecf20Sopenharmony_ci		set_64bit(&irte->high, irte_modified->high);
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci	__iommu_flush_cache(iommu, irte, sizeof(*irte));
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	rc = qi_flush_iec(iommu, index, 0);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/* Update iommu mode according to the IRTE mode */
2018c2ecf20Sopenharmony_ci	irq_iommu->mode = irte->pst ? IRQ_POSTING : IRQ_REMAPPING;
2028c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return rc;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic struct irq_domain *map_hpet_to_ir(u8 hpet_id)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	int i;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_HPET_TBS; i++) {
2128c2ecf20Sopenharmony_ci		if (ir_hpet[i].id == hpet_id && ir_hpet[i].iommu)
2138c2ecf20Sopenharmony_ci			return ir_hpet[i].iommu->ir_domain;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci	return NULL;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic struct intel_iommu *map_ioapic_to_iommu(int apic)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	int i;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_IO_APICS; i++) {
2238c2ecf20Sopenharmony_ci		if (ir_ioapic[i].id == apic && ir_ioapic[i].iommu)
2248c2ecf20Sopenharmony_ci			return ir_ioapic[i].iommu;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci	return NULL;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic struct irq_domain *map_ioapic_to_ir(int apic)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct intel_iommu *iommu = map_ioapic_to_iommu(apic);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return iommu ? iommu->ir_domain : NULL;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic struct irq_domain *map_dev_to_ir(struct pci_dev *dev)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd = dmar_find_matched_drhd_unit(dev);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return drhd ? drhd->iommu->ir_msi_domain : NULL;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int clear_entries(struct irq_2_iommu *irq_iommu)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct irte *start, *entry, *end;
2468c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
2478c2ecf20Sopenharmony_ci	int index;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (irq_iommu->sub_handle)
2508c2ecf20Sopenharmony_ci		return 0;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	iommu = irq_iommu->iommu;
2538c2ecf20Sopenharmony_ci	index = irq_iommu->irte_index;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	start = iommu->ir_table->base + index;
2568c2ecf20Sopenharmony_ci	end = start + (1 << irq_iommu->irte_mask);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	for (entry = start; entry < end; entry++) {
2598c2ecf20Sopenharmony_ci		set_64bit(&entry->low, 0);
2608c2ecf20Sopenharmony_ci		set_64bit(&entry->high, 0);
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci	bitmap_release_region(iommu->ir_table->bitmap, index,
2638c2ecf20Sopenharmony_ci			      irq_iommu->irte_mask);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return qi_flush_iec(iommu, index, irq_iommu->irte_mask);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/*
2698c2ecf20Sopenharmony_ci * source validation type
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_ci#define SVT_NO_VERIFY		0x0  /* no verification is required */
2728c2ecf20Sopenharmony_ci#define SVT_VERIFY_SID_SQ	0x1  /* verify using SID and SQ fields */
2738c2ecf20Sopenharmony_ci#define SVT_VERIFY_BUS		0x2  /* verify bus of request-id */
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/*
2768c2ecf20Sopenharmony_ci * source-id qualifier
2778c2ecf20Sopenharmony_ci */
2788c2ecf20Sopenharmony_ci#define SQ_ALL_16	0x0  /* verify all 16 bits of request-id */
2798c2ecf20Sopenharmony_ci#define SQ_13_IGNORE_1	0x1  /* verify most significant 13 bits, ignore
2808c2ecf20Sopenharmony_ci			      * the third least significant bit
2818c2ecf20Sopenharmony_ci			      */
2828c2ecf20Sopenharmony_ci#define SQ_13_IGNORE_2	0x2  /* verify most significant 13 bits, ignore
2838c2ecf20Sopenharmony_ci			      * the second and third least significant bits
2848c2ecf20Sopenharmony_ci			      */
2858c2ecf20Sopenharmony_ci#define SQ_13_IGNORE_3	0x3  /* verify most significant 13 bits, ignore
2868c2ecf20Sopenharmony_ci			      * the least three significant bits
2878c2ecf20Sopenharmony_ci			      */
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci/*
2908c2ecf20Sopenharmony_ci * set SVT, SQ and SID fields of irte to verify
2918c2ecf20Sopenharmony_ci * source ids of interrupt requests
2928c2ecf20Sopenharmony_ci */
2938c2ecf20Sopenharmony_cistatic void set_irte_sid(struct irte *irte, unsigned int svt,
2948c2ecf20Sopenharmony_ci			 unsigned int sq, unsigned int sid)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	if (disable_sourceid_checking)
2978c2ecf20Sopenharmony_ci		svt = SVT_NO_VERIFY;
2988c2ecf20Sopenharmony_ci	irte->svt = svt;
2998c2ecf20Sopenharmony_ci	irte->sq = sq;
3008c2ecf20Sopenharmony_ci	irte->sid = sid;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/*
3048c2ecf20Sopenharmony_ci * Set an IRTE to match only the bus number. Interrupt requests that reference
3058c2ecf20Sopenharmony_ci * this IRTE must have a requester-id whose bus number is between or equal
3068c2ecf20Sopenharmony_ci * to the start_bus and end_bus arguments.
3078c2ecf20Sopenharmony_ci */
3088c2ecf20Sopenharmony_cistatic void set_irte_verify_bus(struct irte *irte, unsigned int start_bus,
3098c2ecf20Sopenharmony_ci				unsigned int end_bus)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
3128c2ecf20Sopenharmony_ci		     (start_bus << 8) | end_bus);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic int set_ioapic_sid(struct irte *irte, int apic)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	int i;
3188c2ecf20Sopenharmony_ci	u16 sid = 0;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (!irte)
3218c2ecf20Sopenharmony_ci		return -1;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	down_read(&dmar_global_lock);
3248c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_IO_APICS; i++) {
3258c2ecf20Sopenharmony_ci		if (ir_ioapic[i].iommu && ir_ioapic[i].id == apic) {
3268c2ecf20Sopenharmony_ci			sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn;
3278c2ecf20Sopenharmony_ci			break;
3288c2ecf20Sopenharmony_ci		}
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci	up_read(&dmar_global_lock);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (sid == 0) {
3338c2ecf20Sopenharmony_ci		pr_warn("Failed to set source-id of IOAPIC (%d)\n", apic);
3348c2ecf20Sopenharmony_ci		return -1;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, sid);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return 0;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic int set_hpet_sid(struct irte *irte, u8 id)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	int i;
3458c2ecf20Sopenharmony_ci	u16 sid = 0;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!irte)
3488c2ecf20Sopenharmony_ci		return -1;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	down_read(&dmar_global_lock);
3518c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_HPET_TBS; i++) {
3528c2ecf20Sopenharmony_ci		if (ir_hpet[i].iommu && ir_hpet[i].id == id) {
3538c2ecf20Sopenharmony_ci			sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn;
3548c2ecf20Sopenharmony_ci			break;
3558c2ecf20Sopenharmony_ci		}
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci	up_read(&dmar_global_lock);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (sid == 0) {
3608c2ecf20Sopenharmony_ci		pr_warn("Failed to set source-id of HPET block (%d)\n", id);
3618c2ecf20Sopenharmony_ci		return -1;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/*
3658c2ecf20Sopenharmony_ci	 * Should really use SQ_ALL_16. Some platforms are broken.
3668c2ecf20Sopenharmony_ci	 * While we figure out the right quirks for these broken platforms, use
3678c2ecf20Sopenharmony_ci	 * SQ_13_IGNORE_3 for now.
3688c2ecf20Sopenharmony_ci	 */
3698c2ecf20Sopenharmony_ci	set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_13_IGNORE_3, sid);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return 0;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistruct set_msi_sid_data {
3758c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
3768c2ecf20Sopenharmony_ci	u16 alias;
3778c2ecf20Sopenharmony_ci	int count;
3788c2ecf20Sopenharmony_ci	int busmatch_count;
3798c2ecf20Sopenharmony_ci};
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct set_msi_sid_data *data = opaque;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (data->count == 0 || PCI_BUS_NUM(alias) == PCI_BUS_NUM(data->alias))
3868c2ecf20Sopenharmony_ci		data->busmatch_count++;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	data->pdev = pdev;
3898c2ecf20Sopenharmony_ci	data->alias = alias;
3908c2ecf20Sopenharmony_ci	data->count++;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return 0;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int set_msi_sid(struct irte *irte, struct pci_dev *dev)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct set_msi_sid_data data;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	if (!irte || !dev)
4008c2ecf20Sopenharmony_ci		return -1;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	data.count = 0;
4038c2ecf20Sopenharmony_ci	data.busmatch_count = 0;
4048c2ecf20Sopenharmony_ci	pci_for_each_dma_alias(dev, set_msi_sid_cb, &data);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/*
4078c2ecf20Sopenharmony_ci	 * DMA alias provides us with a PCI device and alias.  The only case
4088c2ecf20Sopenharmony_ci	 * where the it will return an alias on a different bus than the
4098c2ecf20Sopenharmony_ci	 * device is the case of a PCIe-to-PCI bridge, where the alias is for
4108c2ecf20Sopenharmony_ci	 * the subordinate bus.  In this case we can only verify the bus.
4118c2ecf20Sopenharmony_ci	 *
4128c2ecf20Sopenharmony_ci	 * If there are multiple aliases, all with the same bus number,
4138c2ecf20Sopenharmony_ci	 * then all we can do is verify the bus. This is typical in NTB
4148c2ecf20Sopenharmony_ci	 * hardware which use proxy IDs where the device will generate traffic
4158c2ecf20Sopenharmony_ci	 * from multiple devfn numbers on the same bus.
4168c2ecf20Sopenharmony_ci	 *
4178c2ecf20Sopenharmony_ci	 * If the alias device is on a different bus than our source device
4188c2ecf20Sopenharmony_ci	 * then we have a topology based alias, use it.
4198c2ecf20Sopenharmony_ci	 *
4208c2ecf20Sopenharmony_ci	 * Otherwise, the alias is for a device DMA quirk and we cannot
4218c2ecf20Sopenharmony_ci	 * assume that MSI uses the same requester ID.  Therefore use the
4228c2ecf20Sopenharmony_ci	 * original device.
4238c2ecf20Sopenharmony_ci	 */
4248c2ecf20Sopenharmony_ci	if (PCI_BUS_NUM(data.alias) != data.pdev->bus->number)
4258c2ecf20Sopenharmony_ci		set_irte_verify_bus(irte, PCI_BUS_NUM(data.alias),
4268c2ecf20Sopenharmony_ci				    dev->bus->number);
4278c2ecf20Sopenharmony_ci	else if (data.count >= 2 && data.busmatch_count == data.count)
4288c2ecf20Sopenharmony_ci		set_irte_verify_bus(irte, dev->bus->number, dev->bus->number);
4298c2ecf20Sopenharmony_ci	else if (data.pdev->bus->number != dev->bus->number)
4308c2ecf20Sopenharmony_ci		set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, data.alias);
4318c2ecf20Sopenharmony_ci	else
4328c2ecf20Sopenharmony_ci		set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
4338c2ecf20Sopenharmony_ci			     pci_dev_id(dev));
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	return 0;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic int iommu_load_old_irte(struct intel_iommu *iommu)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct irte *old_ir_table;
4418c2ecf20Sopenharmony_ci	phys_addr_t irt_phys;
4428c2ecf20Sopenharmony_ci	unsigned int i;
4438c2ecf20Sopenharmony_ci	size_t size;
4448c2ecf20Sopenharmony_ci	u64 irta;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* Check whether the old ir-table has the same size as ours */
4478c2ecf20Sopenharmony_ci	irta = dmar_readq(iommu->reg + DMAR_IRTA_REG);
4488c2ecf20Sopenharmony_ci	if ((irta & INTR_REMAP_TABLE_REG_SIZE_MASK)
4498c2ecf20Sopenharmony_ci	     != INTR_REMAP_TABLE_REG_SIZE)
4508c2ecf20Sopenharmony_ci		return -EINVAL;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	irt_phys = irta & VTD_PAGE_MASK;
4538c2ecf20Sopenharmony_ci	size     = INTR_REMAP_TABLE_ENTRIES*sizeof(struct irte);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	/* Map the old IR table */
4568c2ecf20Sopenharmony_ci	old_ir_table = memremap(irt_phys, size, MEMREMAP_WB);
4578c2ecf20Sopenharmony_ci	if (!old_ir_table)
4588c2ecf20Sopenharmony_ci		return -ENOMEM;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* Copy data over */
4618c2ecf20Sopenharmony_ci	memcpy(iommu->ir_table->base, old_ir_table, size);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	__iommu_flush_cache(iommu, iommu->ir_table->base, size);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	/*
4668c2ecf20Sopenharmony_ci	 * Now check the table for used entries and mark those as
4678c2ecf20Sopenharmony_ci	 * allocated in the bitmap
4688c2ecf20Sopenharmony_ci	 */
4698c2ecf20Sopenharmony_ci	for (i = 0; i < INTR_REMAP_TABLE_ENTRIES; i++) {
4708c2ecf20Sopenharmony_ci		if (iommu->ir_table->base[i].present)
4718c2ecf20Sopenharmony_ci			bitmap_set(iommu->ir_table->bitmap, i, 1);
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	memunmap(old_ir_table);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	return 0;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic void iommu_set_irq_remapping(struct intel_iommu *iommu, int mode)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	unsigned long flags;
4838c2ecf20Sopenharmony_ci	u64 addr;
4848c2ecf20Sopenharmony_ci	u32 sts;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	addr = virt_to_phys((void *)iommu->ir_table->base);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flags);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	dmar_writeq(iommu->reg + DMAR_IRTA_REG,
4918c2ecf20Sopenharmony_ci		    (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* Set interrupt-remapping table pointer */
4948c2ecf20Sopenharmony_ci	writel(iommu->gcmd | DMA_GCMD_SIRTP, iommu->reg + DMAR_GCMD_REG);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
4978c2ecf20Sopenharmony_ci		      readl, (sts & DMA_GSTS_IRTPS), sts);
4988c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	/*
5018c2ecf20Sopenharmony_ci	 * Global invalidation of interrupt entry cache to make sure the
5028c2ecf20Sopenharmony_ci	 * hardware uses the new irq remapping table.
5038c2ecf20Sopenharmony_ci	 */
5048c2ecf20Sopenharmony_ci	qi_global_iec(iommu);
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void iommu_enable_irq_remapping(struct intel_iommu *iommu)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	unsigned long flags;
5108c2ecf20Sopenharmony_ci	u32 sts;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flags);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	/* Enable interrupt-remapping */
5158c2ecf20Sopenharmony_ci	iommu->gcmd |= DMA_GCMD_IRE;
5168c2ecf20Sopenharmony_ci	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
5178c2ecf20Sopenharmony_ci	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
5188c2ecf20Sopenharmony_ci		      readl, (sts & DMA_GSTS_IRES), sts);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	/* Block compatibility-format MSIs */
5218c2ecf20Sopenharmony_ci	if (sts & DMA_GSTS_CFIS) {
5228c2ecf20Sopenharmony_ci		iommu->gcmd &= ~DMA_GCMD_CFI;
5238c2ecf20Sopenharmony_ci		writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
5248c2ecf20Sopenharmony_ci		IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
5258c2ecf20Sopenharmony_ci			      readl, !(sts & DMA_GSTS_CFIS), sts);
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/*
5298c2ecf20Sopenharmony_ci	 * With CFI clear in the Global Command register, we should be
5308c2ecf20Sopenharmony_ci	 * protected from dangerous (i.e. compatibility) interrupts
5318c2ecf20Sopenharmony_ci	 * regardless of x2apic status.  Check just to be sure.
5328c2ecf20Sopenharmony_ci	 */
5338c2ecf20Sopenharmony_ci	if (sts & DMA_GSTS_CFIS)
5348c2ecf20Sopenharmony_ci		WARN(1, KERN_WARNING
5358c2ecf20Sopenharmony_ci			"Compatibility-format IRQs enabled despite intr remapping;\n"
5368c2ecf20Sopenharmony_ci			"you are vulnerable to IRQ injection.\n");
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic int intel_setup_irq_remapping(struct intel_iommu *iommu)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	struct ir_table *ir_table;
5448c2ecf20Sopenharmony_ci	struct fwnode_handle *fn;
5458c2ecf20Sopenharmony_ci	unsigned long *bitmap;
5468c2ecf20Sopenharmony_ci	struct page *pages;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	if (iommu->ir_table)
5498c2ecf20Sopenharmony_ci		return 0;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	ir_table = kzalloc(sizeof(struct ir_table), GFP_KERNEL);
5528c2ecf20Sopenharmony_ci	if (!ir_table)
5538c2ecf20Sopenharmony_ci		return -ENOMEM;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	pages = alloc_pages_node(iommu->node, GFP_KERNEL | __GFP_ZERO,
5568c2ecf20Sopenharmony_ci				 INTR_REMAP_PAGE_ORDER);
5578c2ecf20Sopenharmony_ci	if (!pages) {
5588c2ecf20Sopenharmony_ci		pr_err("IR%d: failed to allocate pages of order %d\n",
5598c2ecf20Sopenharmony_ci		       iommu->seq_id, INTR_REMAP_PAGE_ORDER);
5608c2ecf20Sopenharmony_ci		goto out_free_table;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	bitmap = bitmap_zalloc(INTR_REMAP_TABLE_ENTRIES, GFP_ATOMIC);
5648c2ecf20Sopenharmony_ci	if (bitmap == NULL) {
5658c2ecf20Sopenharmony_ci		pr_err("IR%d: failed to allocate bitmap\n", iommu->seq_id);
5668c2ecf20Sopenharmony_ci		goto out_free_pages;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	fn = irq_domain_alloc_named_id_fwnode("INTEL-IR", iommu->seq_id);
5708c2ecf20Sopenharmony_ci	if (!fn)
5718c2ecf20Sopenharmony_ci		goto out_free_bitmap;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	iommu->ir_domain =
5748c2ecf20Sopenharmony_ci		irq_domain_create_hierarchy(arch_get_ir_parent_domain(),
5758c2ecf20Sopenharmony_ci					    0, INTR_REMAP_TABLE_ENTRIES,
5768c2ecf20Sopenharmony_ci					    fn, &intel_ir_domain_ops,
5778c2ecf20Sopenharmony_ci					    iommu);
5788c2ecf20Sopenharmony_ci	if (!iommu->ir_domain) {
5798c2ecf20Sopenharmony_ci		pr_err("IR%d: failed to allocate irqdomain\n", iommu->seq_id);
5808c2ecf20Sopenharmony_ci		goto out_free_fwnode;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci	iommu->ir_msi_domain =
5838c2ecf20Sopenharmony_ci		arch_create_remap_msi_irq_domain(iommu->ir_domain,
5848c2ecf20Sopenharmony_ci						 "INTEL-IR-MSI",
5858c2ecf20Sopenharmony_ci						 iommu->seq_id);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	ir_table->base = page_address(pages);
5888c2ecf20Sopenharmony_ci	ir_table->bitmap = bitmap;
5898c2ecf20Sopenharmony_ci	iommu->ir_table = ir_table;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	/*
5928c2ecf20Sopenharmony_ci	 * If the queued invalidation is already initialized,
5938c2ecf20Sopenharmony_ci	 * shouldn't disable it.
5948c2ecf20Sopenharmony_ci	 */
5958c2ecf20Sopenharmony_ci	if (!iommu->qi) {
5968c2ecf20Sopenharmony_ci		/*
5978c2ecf20Sopenharmony_ci		 * Clear previous faults.
5988c2ecf20Sopenharmony_ci		 */
5998c2ecf20Sopenharmony_ci		dmar_fault(-1, iommu);
6008c2ecf20Sopenharmony_ci		dmar_disable_qi(iommu);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		if (dmar_enable_qi(iommu)) {
6038c2ecf20Sopenharmony_ci			pr_err("Failed to enable queued invalidation\n");
6048c2ecf20Sopenharmony_ci			goto out_free_ir_domain;
6058c2ecf20Sopenharmony_ci		}
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	init_ir_status(iommu);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	if (ir_pre_enabled(iommu)) {
6118c2ecf20Sopenharmony_ci		if (!is_kdump_kernel()) {
6128c2ecf20Sopenharmony_ci			pr_warn("IRQ remapping was enabled on %s but we are not in kdump mode\n",
6138c2ecf20Sopenharmony_ci				iommu->name);
6148c2ecf20Sopenharmony_ci			clear_ir_pre_enabled(iommu);
6158c2ecf20Sopenharmony_ci			iommu_disable_irq_remapping(iommu);
6168c2ecf20Sopenharmony_ci		} else if (iommu_load_old_irte(iommu))
6178c2ecf20Sopenharmony_ci			pr_err("Failed to copy IR table for %s from previous kernel\n",
6188c2ecf20Sopenharmony_ci			       iommu->name);
6198c2ecf20Sopenharmony_ci		else
6208c2ecf20Sopenharmony_ci			pr_info("Copied IR table for %s from previous kernel\n",
6218c2ecf20Sopenharmony_ci				iommu->name);
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	iommu_set_irq_remapping(iommu, eim_mode);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	return 0;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ciout_free_ir_domain:
6298c2ecf20Sopenharmony_ci	if (iommu->ir_msi_domain)
6308c2ecf20Sopenharmony_ci		irq_domain_remove(iommu->ir_msi_domain);
6318c2ecf20Sopenharmony_ci	iommu->ir_msi_domain = NULL;
6328c2ecf20Sopenharmony_ci	irq_domain_remove(iommu->ir_domain);
6338c2ecf20Sopenharmony_ci	iommu->ir_domain = NULL;
6348c2ecf20Sopenharmony_ciout_free_fwnode:
6358c2ecf20Sopenharmony_ci	irq_domain_free_fwnode(fn);
6368c2ecf20Sopenharmony_ciout_free_bitmap:
6378c2ecf20Sopenharmony_ci	bitmap_free(bitmap);
6388c2ecf20Sopenharmony_ciout_free_pages:
6398c2ecf20Sopenharmony_ci	__free_pages(pages, INTR_REMAP_PAGE_ORDER);
6408c2ecf20Sopenharmony_ciout_free_table:
6418c2ecf20Sopenharmony_ci	kfree(ir_table);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	iommu->ir_table  = NULL;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	return -ENOMEM;
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic void intel_teardown_irq_remapping(struct intel_iommu *iommu)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	struct fwnode_handle *fn;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	if (iommu && iommu->ir_table) {
6538c2ecf20Sopenharmony_ci		if (iommu->ir_msi_domain) {
6548c2ecf20Sopenharmony_ci			fn = iommu->ir_msi_domain->fwnode;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci			irq_domain_remove(iommu->ir_msi_domain);
6578c2ecf20Sopenharmony_ci			irq_domain_free_fwnode(fn);
6588c2ecf20Sopenharmony_ci			iommu->ir_msi_domain = NULL;
6598c2ecf20Sopenharmony_ci		}
6608c2ecf20Sopenharmony_ci		if (iommu->ir_domain) {
6618c2ecf20Sopenharmony_ci			fn = iommu->ir_domain->fwnode;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci			irq_domain_remove(iommu->ir_domain);
6648c2ecf20Sopenharmony_ci			irq_domain_free_fwnode(fn);
6658c2ecf20Sopenharmony_ci			iommu->ir_domain = NULL;
6668c2ecf20Sopenharmony_ci		}
6678c2ecf20Sopenharmony_ci		free_pages((unsigned long)iommu->ir_table->base,
6688c2ecf20Sopenharmony_ci			   INTR_REMAP_PAGE_ORDER);
6698c2ecf20Sopenharmony_ci		bitmap_free(iommu->ir_table->bitmap);
6708c2ecf20Sopenharmony_ci		kfree(iommu->ir_table);
6718c2ecf20Sopenharmony_ci		iommu->ir_table = NULL;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci/*
6768c2ecf20Sopenharmony_ci * Disable Interrupt Remapping.
6778c2ecf20Sopenharmony_ci */
6788c2ecf20Sopenharmony_cistatic void iommu_disable_irq_remapping(struct intel_iommu *iommu)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	unsigned long flags;
6818c2ecf20Sopenharmony_ci	u32 sts;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (!ecap_ir_support(iommu->ecap))
6848c2ecf20Sopenharmony_ci		return;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/*
6878c2ecf20Sopenharmony_ci	 * global invalidation of interrupt entry cache before disabling
6888c2ecf20Sopenharmony_ci	 * interrupt-remapping.
6898c2ecf20Sopenharmony_ci	 */
6908c2ecf20Sopenharmony_ci	qi_global_iec(iommu);
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flags);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	sts = readl(iommu->reg + DMAR_GSTS_REG);
6958c2ecf20Sopenharmony_ci	if (!(sts & DMA_GSTS_IRES))
6968c2ecf20Sopenharmony_ci		goto end;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	iommu->gcmd &= ~DMA_GCMD_IRE;
6998c2ecf20Sopenharmony_ci	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
7028c2ecf20Sopenharmony_ci		      readl, !(sts & DMA_GSTS_IRES), sts);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ciend:
7058c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_cistatic int __init dmar_x2apic_optout(void)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	struct acpi_table_dmar *dmar;
7118c2ecf20Sopenharmony_ci	dmar = (struct acpi_table_dmar *)dmar_tbl;
7128c2ecf20Sopenharmony_ci	if (!dmar || no_x2apic_optout)
7138c2ecf20Sopenharmony_ci		return 0;
7148c2ecf20Sopenharmony_ci	return dmar->flags & DMAR_X2APIC_OPT_OUT;
7158c2ecf20Sopenharmony_ci}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_cistatic void __init intel_cleanup_irq_remapping(void)
7188c2ecf20Sopenharmony_ci{
7198c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
7208c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	for_each_iommu(iommu, drhd) {
7238c2ecf20Sopenharmony_ci		if (ecap_ir_support(iommu->ecap)) {
7248c2ecf20Sopenharmony_ci			iommu_disable_irq_remapping(iommu);
7258c2ecf20Sopenharmony_ci			intel_teardown_irq_remapping(iommu);
7268c2ecf20Sopenharmony_ci		}
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	if (x2apic_supported())
7308c2ecf20Sopenharmony_ci		pr_warn("Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n");
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic int __init intel_prepare_irq_remapping(void)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
7368c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
7378c2ecf20Sopenharmony_ci	int eim = 0;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	if (irq_remap_broken) {
7408c2ecf20Sopenharmony_ci		pr_warn("This system BIOS has enabled interrupt remapping\n"
7418c2ecf20Sopenharmony_ci			"on a chipset that contains an erratum making that\n"
7428c2ecf20Sopenharmony_ci			"feature unstable.  To maintain system stability\n"
7438c2ecf20Sopenharmony_ci			"interrupt remapping is being disabled.  Please\n"
7448c2ecf20Sopenharmony_ci			"contact your BIOS vendor for an update\n");
7458c2ecf20Sopenharmony_ci		add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
7468c2ecf20Sopenharmony_ci		return -ENODEV;
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if (dmar_table_init() < 0)
7508c2ecf20Sopenharmony_ci		return -ENODEV;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	if (!dmar_ir_support())
7538c2ecf20Sopenharmony_ci		return -ENODEV;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (parse_ioapics_under_ir()) {
7568c2ecf20Sopenharmony_ci		pr_info("Not enabling interrupt remapping\n");
7578c2ecf20Sopenharmony_ci		goto error;
7588c2ecf20Sopenharmony_ci	}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	/* First make sure all IOMMUs support IRQ remapping */
7618c2ecf20Sopenharmony_ci	for_each_iommu(iommu, drhd)
7628c2ecf20Sopenharmony_ci		if (!ecap_ir_support(iommu->ecap))
7638c2ecf20Sopenharmony_ci			goto error;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	/* Detect remapping mode: lapic or x2apic */
7668c2ecf20Sopenharmony_ci	if (x2apic_supported()) {
7678c2ecf20Sopenharmony_ci		eim = !dmar_x2apic_optout();
7688c2ecf20Sopenharmony_ci		if (!eim) {
7698c2ecf20Sopenharmony_ci			pr_info("x2apic is disabled because BIOS sets x2apic opt out bit.");
7708c2ecf20Sopenharmony_ci			pr_info("Use 'intremap=no_x2apic_optout' to override the BIOS setting.\n");
7718c2ecf20Sopenharmony_ci		}
7728c2ecf20Sopenharmony_ci	}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	for_each_iommu(iommu, drhd) {
7758c2ecf20Sopenharmony_ci		if (eim && !ecap_eim_support(iommu->ecap)) {
7768c2ecf20Sopenharmony_ci			pr_info("%s does not support EIM\n", iommu->name);
7778c2ecf20Sopenharmony_ci			eim = 0;
7788c2ecf20Sopenharmony_ci		}
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	eim_mode = eim;
7828c2ecf20Sopenharmony_ci	if (eim)
7838c2ecf20Sopenharmony_ci		pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	/* Do the initializations early */
7868c2ecf20Sopenharmony_ci	for_each_iommu(iommu, drhd) {
7878c2ecf20Sopenharmony_ci		if (intel_setup_irq_remapping(iommu)) {
7888c2ecf20Sopenharmony_ci			pr_err("Failed to setup irq remapping for %s\n",
7898c2ecf20Sopenharmony_ci			       iommu->name);
7908c2ecf20Sopenharmony_ci			goto error;
7918c2ecf20Sopenharmony_ci		}
7928c2ecf20Sopenharmony_ci	}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	return 0;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_cierror:
7978c2ecf20Sopenharmony_ci	intel_cleanup_irq_remapping();
7988c2ecf20Sopenharmony_ci	return -ENODEV;
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci/*
8028c2ecf20Sopenharmony_ci * Set Posted-Interrupts capability.
8038c2ecf20Sopenharmony_ci */
8048c2ecf20Sopenharmony_cistatic inline void set_irq_posting_cap(void)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
8078c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	if (!disable_irq_post) {
8108c2ecf20Sopenharmony_ci		/*
8118c2ecf20Sopenharmony_ci		 * If IRTE is in posted format, the 'pda' field goes across the
8128c2ecf20Sopenharmony_ci		 * 64-bit boundary, we need use cmpxchg16b to atomically update
8138c2ecf20Sopenharmony_ci		 * it. We only expose posted-interrupt when X86_FEATURE_CX16
8148c2ecf20Sopenharmony_ci		 * is supported. Actually, hardware platforms supporting PI
8158c2ecf20Sopenharmony_ci		 * should have X86_FEATURE_CX16 support, this has been confirmed
8168c2ecf20Sopenharmony_ci		 * with Intel hardware guys.
8178c2ecf20Sopenharmony_ci		 */
8188c2ecf20Sopenharmony_ci		if (boot_cpu_has(X86_FEATURE_CX16))
8198c2ecf20Sopenharmony_ci			intel_irq_remap_ops.capability |= 1 << IRQ_POSTING_CAP;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci		for_each_iommu(iommu, drhd)
8228c2ecf20Sopenharmony_ci			if (!cap_pi_support(iommu->cap)) {
8238c2ecf20Sopenharmony_ci				intel_irq_remap_ops.capability &=
8248c2ecf20Sopenharmony_ci						~(1 << IRQ_POSTING_CAP);
8258c2ecf20Sopenharmony_ci				break;
8268c2ecf20Sopenharmony_ci			}
8278c2ecf20Sopenharmony_ci	}
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_cistatic int __init intel_enable_irq_remapping(void)
8318c2ecf20Sopenharmony_ci{
8328c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
8338c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
8348c2ecf20Sopenharmony_ci	bool setup = false;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	/*
8378c2ecf20Sopenharmony_ci	 * Setup Interrupt-remapping for all the DRHD's now.
8388c2ecf20Sopenharmony_ci	 */
8398c2ecf20Sopenharmony_ci	for_each_iommu(iommu, drhd) {
8408c2ecf20Sopenharmony_ci		if (!ir_pre_enabled(iommu))
8418c2ecf20Sopenharmony_ci			iommu_enable_irq_remapping(iommu);
8428c2ecf20Sopenharmony_ci		setup = true;
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	if (!setup)
8468c2ecf20Sopenharmony_ci		goto error;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	irq_remapping_enabled = 1;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	set_irq_posting_cap();
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	pr_info("Enabled IRQ remapping in %s mode\n", eim_mode ? "x2apic" : "xapic");
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	return eim_mode ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_cierror:
8578c2ecf20Sopenharmony_ci	intel_cleanup_irq_remapping();
8588c2ecf20Sopenharmony_ci	return -1;
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_cistatic int ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope,
8628c2ecf20Sopenharmony_ci				   struct intel_iommu *iommu,
8638c2ecf20Sopenharmony_ci				   struct acpi_dmar_hardware_unit *drhd)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	struct acpi_dmar_pci_path *path;
8668c2ecf20Sopenharmony_ci	u8 bus;
8678c2ecf20Sopenharmony_ci	int count, free = -1;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	bus = scope->bus;
8708c2ecf20Sopenharmony_ci	path = (struct acpi_dmar_pci_path *)(scope + 1);
8718c2ecf20Sopenharmony_ci	count = (scope->length - sizeof(struct acpi_dmar_device_scope))
8728c2ecf20Sopenharmony_ci		/ sizeof(struct acpi_dmar_pci_path);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	while (--count > 0) {
8758c2ecf20Sopenharmony_ci		/*
8768c2ecf20Sopenharmony_ci		 * Access PCI directly due to the PCI
8778c2ecf20Sopenharmony_ci		 * subsystem isn't initialized yet.
8788c2ecf20Sopenharmony_ci		 */
8798c2ecf20Sopenharmony_ci		bus = read_pci_config_byte(bus, path->device, path->function,
8808c2ecf20Sopenharmony_ci					   PCI_SECONDARY_BUS);
8818c2ecf20Sopenharmony_ci		path++;
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	for (count = 0; count < MAX_HPET_TBS; count++) {
8858c2ecf20Sopenharmony_ci		if (ir_hpet[count].iommu == iommu &&
8868c2ecf20Sopenharmony_ci		    ir_hpet[count].id == scope->enumeration_id)
8878c2ecf20Sopenharmony_ci			return 0;
8888c2ecf20Sopenharmony_ci		else if (ir_hpet[count].iommu == NULL && free == -1)
8898c2ecf20Sopenharmony_ci			free = count;
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci	if (free == -1) {
8928c2ecf20Sopenharmony_ci		pr_warn("Exceeded Max HPET blocks\n");
8938c2ecf20Sopenharmony_ci		return -ENOSPC;
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	ir_hpet[free].iommu = iommu;
8978c2ecf20Sopenharmony_ci	ir_hpet[free].id    = scope->enumeration_id;
8988c2ecf20Sopenharmony_ci	ir_hpet[free].bus   = bus;
8998c2ecf20Sopenharmony_ci	ir_hpet[free].devfn = PCI_DEVFN(path->device, path->function);
9008c2ecf20Sopenharmony_ci	pr_info("HPET id %d under DRHD base 0x%Lx\n",
9018c2ecf20Sopenharmony_ci		scope->enumeration_id, drhd->address);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	return 0;
9048c2ecf20Sopenharmony_ci}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cistatic int ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
9078c2ecf20Sopenharmony_ci				     struct intel_iommu *iommu,
9088c2ecf20Sopenharmony_ci				     struct acpi_dmar_hardware_unit *drhd)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	struct acpi_dmar_pci_path *path;
9118c2ecf20Sopenharmony_ci	u8 bus;
9128c2ecf20Sopenharmony_ci	int count, free = -1;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	bus = scope->bus;
9158c2ecf20Sopenharmony_ci	path = (struct acpi_dmar_pci_path *)(scope + 1);
9168c2ecf20Sopenharmony_ci	count = (scope->length - sizeof(struct acpi_dmar_device_scope))
9178c2ecf20Sopenharmony_ci		/ sizeof(struct acpi_dmar_pci_path);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	while (--count > 0) {
9208c2ecf20Sopenharmony_ci		/*
9218c2ecf20Sopenharmony_ci		 * Access PCI directly due to the PCI
9228c2ecf20Sopenharmony_ci		 * subsystem isn't initialized yet.
9238c2ecf20Sopenharmony_ci		 */
9248c2ecf20Sopenharmony_ci		bus = read_pci_config_byte(bus, path->device, path->function,
9258c2ecf20Sopenharmony_ci					   PCI_SECONDARY_BUS);
9268c2ecf20Sopenharmony_ci		path++;
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	for (count = 0; count < MAX_IO_APICS; count++) {
9308c2ecf20Sopenharmony_ci		if (ir_ioapic[count].iommu == iommu &&
9318c2ecf20Sopenharmony_ci		    ir_ioapic[count].id == scope->enumeration_id)
9328c2ecf20Sopenharmony_ci			return 0;
9338c2ecf20Sopenharmony_ci		else if (ir_ioapic[count].iommu == NULL && free == -1)
9348c2ecf20Sopenharmony_ci			free = count;
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci	if (free == -1) {
9378c2ecf20Sopenharmony_ci		pr_warn("Exceeded Max IO APICS\n");
9388c2ecf20Sopenharmony_ci		return -ENOSPC;
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	ir_ioapic[free].bus   = bus;
9428c2ecf20Sopenharmony_ci	ir_ioapic[free].devfn = PCI_DEVFN(path->device, path->function);
9438c2ecf20Sopenharmony_ci	ir_ioapic[free].iommu = iommu;
9448c2ecf20Sopenharmony_ci	ir_ioapic[free].id    = scope->enumeration_id;
9458c2ecf20Sopenharmony_ci	pr_info("IOAPIC id %d under DRHD base  0x%Lx IOMMU %d\n",
9468c2ecf20Sopenharmony_ci		scope->enumeration_id, drhd->address, iommu->seq_id);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	return 0;
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header,
9528c2ecf20Sopenharmony_ci				      struct intel_iommu *iommu)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	int ret = 0;
9558c2ecf20Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
9568c2ecf20Sopenharmony_ci	struct acpi_dmar_device_scope *scope;
9578c2ecf20Sopenharmony_ci	void *start, *end;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	drhd = (struct acpi_dmar_hardware_unit *)header;
9608c2ecf20Sopenharmony_ci	start = (void *)(drhd + 1);
9618c2ecf20Sopenharmony_ci	end = ((void *)drhd) + header->length;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	while (start < end && ret == 0) {
9648c2ecf20Sopenharmony_ci		scope = start;
9658c2ecf20Sopenharmony_ci		if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC)
9668c2ecf20Sopenharmony_ci			ret = ir_parse_one_ioapic_scope(scope, iommu, drhd);
9678c2ecf20Sopenharmony_ci		else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET)
9688c2ecf20Sopenharmony_ci			ret = ir_parse_one_hpet_scope(scope, iommu, drhd);
9698c2ecf20Sopenharmony_ci		start += scope->length;
9708c2ecf20Sopenharmony_ci	}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	return ret;
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cistatic void ir_remove_ioapic_hpet_scope(struct intel_iommu *iommu)
9768c2ecf20Sopenharmony_ci{
9778c2ecf20Sopenharmony_ci	int i;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_HPET_TBS; i++)
9808c2ecf20Sopenharmony_ci		if (ir_hpet[i].iommu == iommu)
9818c2ecf20Sopenharmony_ci			ir_hpet[i].iommu = NULL;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_IO_APICS; i++)
9848c2ecf20Sopenharmony_ci		if (ir_ioapic[i].iommu == iommu)
9858c2ecf20Sopenharmony_ci			ir_ioapic[i].iommu = NULL;
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci/*
9898c2ecf20Sopenharmony_ci * Finds the assocaition between IOAPIC's and its Interrupt-remapping
9908c2ecf20Sopenharmony_ci * hardware unit.
9918c2ecf20Sopenharmony_ci */
9928c2ecf20Sopenharmony_cistatic int __init parse_ioapics_under_ir(void)
9938c2ecf20Sopenharmony_ci{
9948c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
9958c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
9968c2ecf20Sopenharmony_ci	bool ir_supported = false;
9978c2ecf20Sopenharmony_ci	int ioapic_idx;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	for_each_iommu(iommu, drhd) {
10008c2ecf20Sopenharmony_ci		int ret;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci		if (!ecap_ir_support(iommu->ecap))
10038c2ecf20Sopenharmony_ci			continue;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci		ret = ir_parse_ioapic_hpet_scope(drhd->hdr, iommu);
10068c2ecf20Sopenharmony_ci		if (ret)
10078c2ecf20Sopenharmony_ci			return ret;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci		ir_supported = true;
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	if (!ir_supported)
10138c2ecf20Sopenharmony_ci		return -ENODEV;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	for (ioapic_idx = 0; ioapic_idx < nr_ioapics; ioapic_idx++) {
10168c2ecf20Sopenharmony_ci		int ioapic_id = mpc_ioapic_id(ioapic_idx);
10178c2ecf20Sopenharmony_ci		if (!map_ioapic_to_iommu(ioapic_id)) {
10188c2ecf20Sopenharmony_ci			pr_err(FW_BUG "ioapic %d has no mapping iommu, "
10198c2ecf20Sopenharmony_ci			       "interrupt remapping will be disabled\n",
10208c2ecf20Sopenharmony_ci			       ioapic_id);
10218c2ecf20Sopenharmony_ci			return -1;
10228c2ecf20Sopenharmony_ci		}
10238c2ecf20Sopenharmony_ci	}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	return 0;
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_cistatic int __init ir_dev_scope_init(void)
10298c2ecf20Sopenharmony_ci{
10308c2ecf20Sopenharmony_ci	int ret;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	if (!irq_remapping_enabled)
10338c2ecf20Sopenharmony_ci		return 0;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	down_write(&dmar_global_lock);
10368c2ecf20Sopenharmony_ci	ret = dmar_dev_scope_init();
10378c2ecf20Sopenharmony_ci	up_write(&dmar_global_lock);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	return ret;
10408c2ecf20Sopenharmony_ci}
10418c2ecf20Sopenharmony_cirootfs_initcall(ir_dev_scope_init);
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_cistatic void disable_irq_remapping(void)
10448c2ecf20Sopenharmony_ci{
10458c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
10468c2ecf20Sopenharmony_ci	struct intel_iommu *iommu = NULL;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	/*
10498c2ecf20Sopenharmony_ci	 * Disable Interrupt-remapping for all the DRHD's now.
10508c2ecf20Sopenharmony_ci	 */
10518c2ecf20Sopenharmony_ci	for_each_iommu(iommu, drhd) {
10528c2ecf20Sopenharmony_ci		if (!ecap_ir_support(iommu->ecap))
10538c2ecf20Sopenharmony_ci			continue;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci		iommu_disable_irq_remapping(iommu);
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	/*
10598c2ecf20Sopenharmony_ci	 * Clear Posted-Interrupts capability.
10608c2ecf20Sopenharmony_ci	 */
10618c2ecf20Sopenharmony_ci	if (!disable_irq_post)
10628c2ecf20Sopenharmony_ci		intel_irq_remap_ops.capability &= ~(1 << IRQ_POSTING_CAP);
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic int reenable_irq_remapping(int eim)
10668c2ecf20Sopenharmony_ci{
10678c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
10688c2ecf20Sopenharmony_ci	bool setup = false;
10698c2ecf20Sopenharmony_ci	struct intel_iommu *iommu = NULL;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	for_each_iommu(iommu, drhd)
10728c2ecf20Sopenharmony_ci		if (iommu->qi)
10738c2ecf20Sopenharmony_ci			dmar_reenable_qi(iommu);
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	/*
10768c2ecf20Sopenharmony_ci	 * Setup Interrupt-remapping for all the DRHD's now.
10778c2ecf20Sopenharmony_ci	 */
10788c2ecf20Sopenharmony_ci	for_each_iommu(iommu, drhd) {
10798c2ecf20Sopenharmony_ci		if (!ecap_ir_support(iommu->ecap))
10808c2ecf20Sopenharmony_ci			continue;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci		/* Set up interrupt remapping for iommu.*/
10838c2ecf20Sopenharmony_ci		iommu_set_irq_remapping(iommu, eim);
10848c2ecf20Sopenharmony_ci		iommu_enable_irq_remapping(iommu);
10858c2ecf20Sopenharmony_ci		setup = true;
10868c2ecf20Sopenharmony_ci	}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	if (!setup)
10898c2ecf20Sopenharmony_ci		goto error;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	set_irq_posting_cap();
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	return 0;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_cierror:
10968c2ecf20Sopenharmony_ci	/*
10978c2ecf20Sopenharmony_ci	 * handle error condition gracefully here!
10988c2ecf20Sopenharmony_ci	 */
10998c2ecf20Sopenharmony_ci	return -1;
11008c2ecf20Sopenharmony_ci}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci/*
11038c2ecf20Sopenharmony_ci * Store the MSI remapping domain pointer in the device if enabled.
11048c2ecf20Sopenharmony_ci *
11058c2ecf20Sopenharmony_ci * This is called from dmar_pci_bus_add_dev() so it works even when DMA
11068c2ecf20Sopenharmony_ci * remapping is disabled. Only update the pointer if the device is not
11078c2ecf20Sopenharmony_ci * already handled by a non default PCI/MSI interrupt domain. This protects
11088c2ecf20Sopenharmony_ci * e.g. VMD devices.
11098c2ecf20Sopenharmony_ci */
11108c2ecf20Sopenharmony_civoid intel_irq_remap_add_device(struct dmar_pci_notify_info *info)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	if (!irq_remapping_enabled || pci_dev_has_special_msi_domain(info->dev))
11138c2ecf20Sopenharmony_ci		return;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	dev_set_msi_domain(&info->dev->dev, map_dev_to_ir(info->dev));
11168c2ecf20Sopenharmony_ci}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_cistatic void prepare_irte(struct irte *irte, int vector, unsigned int dest)
11198c2ecf20Sopenharmony_ci{
11208c2ecf20Sopenharmony_ci	memset(irte, 0, sizeof(*irte));
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	irte->present = 1;
11238c2ecf20Sopenharmony_ci	irte->dst_mode = apic->irq_dest_mode;
11248c2ecf20Sopenharmony_ci	/*
11258c2ecf20Sopenharmony_ci	 * Trigger mode in the IRTE will always be edge, and for IO-APIC, the
11268c2ecf20Sopenharmony_ci	 * actual level or edge trigger will be setup in the IO-APIC
11278c2ecf20Sopenharmony_ci	 * RTE. This will help simplify level triggered irq migration.
11288c2ecf20Sopenharmony_ci	 * For more details, see the comments (in io_apic.c) explainig IO-APIC
11298c2ecf20Sopenharmony_ci	 * irq migration in the presence of interrupt-remapping.
11308c2ecf20Sopenharmony_ci	*/
11318c2ecf20Sopenharmony_ci	irte->trigger_mode = 0;
11328c2ecf20Sopenharmony_ci	irte->dlvry_mode = apic->irq_delivery_mode;
11338c2ecf20Sopenharmony_ci	irte->vector = vector;
11348c2ecf20Sopenharmony_ci	irte->dest_id = IRTE_DEST(dest);
11358c2ecf20Sopenharmony_ci	irte->redir_hint = 1;
11368c2ecf20Sopenharmony_ci}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_cistatic struct irq_domain *intel_get_irq_domain(struct irq_alloc_info *info)
11398c2ecf20Sopenharmony_ci{
11408c2ecf20Sopenharmony_ci	if (!info)
11418c2ecf20Sopenharmony_ci		return NULL;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	switch (info->type) {
11448c2ecf20Sopenharmony_ci	case X86_IRQ_ALLOC_TYPE_IOAPIC_GET_PARENT:
11458c2ecf20Sopenharmony_ci		return map_ioapic_to_ir(info->devid);
11468c2ecf20Sopenharmony_ci	case X86_IRQ_ALLOC_TYPE_HPET_GET_PARENT:
11478c2ecf20Sopenharmony_ci		return map_hpet_to_ir(info->devid);
11488c2ecf20Sopenharmony_ci	default:
11498c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
11508c2ecf20Sopenharmony_ci		return NULL;
11518c2ecf20Sopenharmony_ci	}
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_cistruct irq_remap_ops intel_irq_remap_ops = {
11558c2ecf20Sopenharmony_ci	.prepare		= intel_prepare_irq_remapping,
11568c2ecf20Sopenharmony_ci	.enable			= intel_enable_irq_remapping,
11578c2ecf20Sopenharmony_ci	.disable		= disable_irq_remapping,
11588c2ecf20Sopenharmony_ci	.reenable		= reenable_irq_remapping,
11598c2ecf20Sopenharmony_ci	.enable_faulting	= enable_drhd_fault_handling,
11608c2ecf20Sopenharmony_ci	.get_irq_domain		= intel_get_irq_domain,
11618c2ecf20Sopenharmony_ci};
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_cistatic void intel_ir_reconfigure_irte(struct irq_data *irqd, bool force)
11648c2ecf20Sopenharmony_ci{
11658c2ecf20Sopenharmony_ci	struct intel_ir_data *ir_data = irqd->chip_data;
11668c2ecf20Sopenharmony_ci	struct irte *irte = &ir_data->irte_entry;
11678c2ecf20Sopenharmony_ci	struct irq_cfg *cfg = irqd_cfg(irqd);
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	/*
11708c2ecf20Sopenharmony_ci	 * Atomically updates the IRTE with the new destination, vector
11718c2ecf20Sopenharmony_ci	 * and flushes the interrupt entry cache.
11728c2ecf20Sopenharmony_ci	 */
11738c2ecf20Sopenharmony_ci	irte->vector = cfg->vector;
11748c2ecf20Sopenharmony_ci	irte->dest_id = IRTE_DEST(cfg->dest_apicid);
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	/* Update the hardware only if the interrupt is in remapped mode. */
11778c2ecf20Sopenharmony_ci	if (force || ir_data->irq_2_iommu.mode == IRQ_REMAPPING)
11788c2ecf20Sopenharmony_ci		modify_irte(&ir_data->irq_2_iommu, irte);
11798c2ecf20Sopenharmony_ci}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci/*
11828c2ecf20Sopenharmony_ci * Migrate the IO-APIC irq in the presence of intr-remapping.
11838c2ecf20Sopenharmony_ci *
11848c2ecf20Sopenharmony_ci * For both level and edge triggered, irq migration is a simple atomic
11858c2ecf20Sopenharmony_ci * update(of vector and cpu destination) of IRTE and flush the hardware cache.
11868c2ecf20Sopenharmony_ci *
11878c2ecf20Sopenharmony_ci * For level triggered, we eliminate the io-apic RTE modification (with the
11888c2ecf20Sopenharmony_ci * updated vector information), by using a virtual vector (io-apic pin number).
11898c2ecf20Sopenharmony_ci * Real vector that is used for interrupting cpu will be coming from
11908c2ecf20Sopenharmony_ci * the interrupt-remapping table entry.
11918c2ecf20Sopenharmony_ci *
11928c2ecf20Sopenharmony_ci * As the migration is a simple atomic update of IRTE, the same mechanism
11938c2ecf20Sopenharmony_ci * is used to migrate MSI irq's in the presence of interrupt-remapping.
11948c2ecf20Sopenharmony_ci */
11958c2ecf20Sopenharmony_cistatic int
11968c2ecf20Sopenharmony_ciintel_ir_set_affinity(struct irq_data *data, const struct cpumask *mask,
11978c2ecf20Sopenharmony_ci		      bool force)
11988c2ecf20Sopenharmony_ci{
11998c2ecf20Sopenharmony_ci	struct irq_data *parent = data->parent_data;
12008c2ecf20Sopenharmony_ci	struct irq_cfg *cfg = irqd_cfg(data);
12018c2ecf20Sopenharmony_ci	int ret;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	ret = parent->chip->irq_set_affinity(parent, mask, force);
12048c2ecf20Sopenharmony_ci	if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
12058c2ecf20Sopenharmony_ci		return ret;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	intel_ir_reconfigure_irte(data, false);
12088c2ecf20Sopenharmony_ci	/*
12098c2ecf20Sopenharmony_ci	 * After this point, all the interrupts will start arriving
12108c2ecf20Sopenharmony_ci	 * at the new destination. So, time to cleanup the previous
12118c2ecf20Sopenharmony_ci	 * vector allocation.
12128c2ecf20Sopenharmony_ci	 */
12138c2ecf20Sopenharmony_ci	send_cleanup_vector(cfg);
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	return IRQ_SET_MASK_OK_DONE;
12168c2ecf20Sopenharmony_ci}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_cistatic void intel_ir_compose_msi_msg(struct irq_data *irq_data,
12198c2ecf20Sopenharmony_ci				     struct msi_msg *msg)
12208c2ecf20Sopenharmony_ci{
12218c2ecf20Sopenharmony_ci	struct intel_ir_data *ir_data = irq_data->chip_data;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	*msg = ir_data->msi_entry;
12248c2ecf20Sopenharmony_ci}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_cistatic int intel_ir_set_vcpu_affinity(struct irq_data *data, void *info)
12278c2ecf20Sopenharmony_ci{
12288c2ecf20Sopenharmony_ci	struct intel_ir_data *ir_data = data->chip_data;
12298c2ecf20Sopenharmony_ci	struct vcpu_data *vcpu_pi_info = info;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	/* stop posting interrupts, back to remapping mode */
12328c2ecf20Sopenharmony_ci	if (!vcpu_pi_info) {
12338c2ecf20Sopenharmony_ci		modify_irte(&ir_data->irq_2_iommu, &ir_data->irte_entry);
12348c2ecf20Sopenharmony_ci	} else {
12358c2ecf20Sopenharmony_ci		struct irte irte_pi;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci		/*
12388c2ecf20Sopenharmony_ci		 * We are not caching the posted interrupt entry. We
12398c2ecf20Sopenharmony_ci		 * copy the data from the remapped entry and modify
12408c2ecf20Sopenharmony_ci		 * the fields which are relevant for posted mode. The
12418c2ecf20Sopenharmony_ci		 * cached remapped entry is used for switching back to
12428c2ecf20Sopenharmony_ci		 * remapped mode.
12438c2ecf20Sopenharmony_ci		 */
12448c2ecf20Sopenharmony_ci		memset(&irte_pi, 0, sizeof(irte_pi));
12458c2ecf20Sopenharmony_ci		dmar_copy_shared_irte(&irte_pi, &ir_data->irte_entry);
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci		/* Update the posted mode fields */
12488c2ecf20Sopenharmony_ci		irte_pi.p_pst = 1;
12498c2ecf20Sopenharmony_ci		irte_pi.p_urgent = 0;
12508c2ecf20Sopenharmony_ci		irte_pi.p_vector = vcpu_pi_info->vector;
12518c2ecf20Sopenharmony_ci		irte_pi.pda_l = (vcpu_pi_info->pi_desc_addr >>
12528c2ecf20Sopenharmony_ci				(32 - PDA_LOW_BIT)) & ~(-1UL << PDA_LOW_BIT);
12538c2ecf20Sopenharmony_ci		irte_pi.pda_h = (vcpu_pi_info->pi_desc_addr >> 32) &
12548c2ecf20Sopenharmony_ci				~(-1UL << PDA_HIGH_BIT);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci		modify_irte(&ir_data->irq_2_iommu, &irte_pi);
12578c2ecf20Sopenharmony_ci	}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	return 0;
12608c2ecf20Sopenharmony_ci}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_cistatic struct irq_chip intel_ir_chip = {
12638c2ecf20Sopenharmony_ci	.name			= "INTEL-IR",
12648c2ecf20Sopenharmony_ci	.irq_ack		= apic_ack_irq,
12658c2ecf20Sopenharmony_ci	.irq_set_affinity	= intel_ir_set_affinity,
12668c2ecf20Sopenharmony_ci	.irq_compose_msi_msg	= intel_ir_compose_msi_msg,
12678c2ecf20Sopenharmony_ci	.irq_set_vcpu_affinity	= intel_ir_set_vcpu_affinity,
12688c2ecf20Sopenharmony_ci};
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_cistatic void intel_irq_remapping_prepare_irte(struct intel_ir_data *data,
12718c2ecf20Sopenharmony_ci					     struct irq_cfg *irq_cfg,
12728c2ecf20Sopenharmony_ci					     struct irq_alloc_info *info,
12738c2ecf20Sopenharmony_ci					     int index, int sub_handle)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	struct IR_IO_APIC_route_entry *entry;
12768c2ecf20Sopenharmony_ci	struct irte *irte = &data->irte_entry;
12778c2ecf20Sopenharmony_ci	struct msi_msg *msg = &data->msi_entry;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	prepare_irte(irte, irq_cfg->vector, irq_cfg->dest_apicid);
12808c2ecf20Sopenharmony_ci	switch (info->type) {
12818c2ecf20Sopenharmony_ci	case X86_IRQ_ALLOC_TYPE_IOAPIC:
12828c2ecf20Sopenharmony_ci		/* Set source-id of interrupt request */
12838c2ecf20Sopenharmony_ci		set_ioapic_sid(irte, info->devid);
12848c2ecf20Sopenharmony_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",
12858c2ecf20Sopenharmony_ci			info->devid, irte->present, irte->fpd,
12868c2ecf20Sopenharmony_ci			irte->dst_mode, irte->redir_hint,
12878c2ecf20Sopenharmony_ci			irte->trigger_mode, irte->dlvry_mode,
12888c2ecf20Sopenharmony_ci			irte->avail, irte->vector, irte->dest_id,
12898c2ecf20Sopenharmony_ci			irte->sid, irte->sq, irte->svt);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci		entry = (struct IR_IO_APIC_route_entry *)info->ioapic.entry;
12928c2ecf20Sopenharmony_ci		info->ioapic.entry = NULL;
12938c2ecf20Sopenharmony_ci		memset(entry, 0, sizeof(*entry));
12948c2ecf20Sopenharmony_ci		entry->index2	= (index >> 15) & 0x1;
12958c2ecf20Sopenharmony_ci		entry->zero	= 0;
12968c2ecf20Sopenharmony_ci		entry->format	= 1;
12978c2ecf20Sopenharmony_ci		entry->index	= (index & 0x7fff);
12988c2ecf20Sopenharmony_ci		/*
12998c2ecf20Sopenharmony_ci		 * IO-APIC RTE will be configured with virtual vector.
13008c2ecf20Sopenharmony_ci		 * irq handler will do the explicit EOI to the io-apic.
13018c2ecf20Sopenharmony_ci		 */
13028c2ecf20Sopenharmony_ci		entry->vector	= info->ioapic.pin;
13038c2ecf20Sopenharmony_ci		entry->mask	= 0;			/* enable IRQ */
13048c2ecf20Sopenharmony_ci		entry->trigger	= info->ioapic.trigger;
13058c2ecf20Sopenharmony_ci		entry->polarity	= info->ioapic.polarity;
13068c2ecf20Sopenharmony_ci		if (info->ioapic.trigger)
13078c2ecf20Sopenharmony_ci			entry->mask = 1; /* Mask level triggered irqs. */
13088c2ecf20Sopenharmony_ci		break;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	case X86_IRQ_ALLOC_TYPE_HPET:
13118c2ecf20Sopenharmony_ci	case X86_IRQ_ALLOC_TYPE_PCI_MSI:
13128c2ecf20Sopenharmony_ci	case X86_IRQ_ALLOC_TYPE_PCI_MSIX:
13138c2ecf20Sopenharmony_ci		if (info->type == X86_IRQ_ALLOC_TYPE_HPET)
13148c2ecf20Sopenharmony_ci			set_hpet_sid(irte, info->devid);
13158c2ecf20Sopenharmony_ci		else
13168c2ecf20Sopenharmony_ci			set_msi_sid(irte, msi_desc_to_pci_dev(info->desc));
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci		msg->address_hi = MSI_ADDR_BASE_HI;
13198c2ecf20Sopenharmony_ci		msg->data = sub_handle;
13208c2ecf20Sopenharmony_ci		msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
13218c2ecf20Sopenharmony_ci				  MSI_ADDR_IR_SHV |
13228c2ecf20Sopenharmony_ci				  MSI_ADDR_IR_INDEX1(index) |
13238c2ecf20Sopenharmony_ci				  MSI_ADDR_IR_INDEX2(index);
13248c2ecf20Sopenharmony_ci		break;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	default:
13278c2ecf20Sopenharmony_ci		BUG_ON(1);
13288c2ecf20Sopenharmony_ci		break;
13298c2ecf20Sopenharmony_ci	}
13308c2ecf20Sopenharmony_ci}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_cistatic void intel_free_irq_resources(struct irq_domain *domain,
13338c2ecf20Sopenharmony_ci				     unsigned int virq, unsigned int nr_irqs)
13348c2ecf20Sopenharmony_ci{
13358c2ecf20Sopenharmony_ci	struct irq_data *irq_data;
13368c2ecf20Sopenharmony_ci	struct intel_ir_data *data;
13378c2ecf20Sopenharmony_ci	struct irq_2_iommu *irq_iommu;
13388c2ecf20Sopenharmony_ci	unsigned long flags;
13398c2ecf20Sopenharmony_ci	int i;
13408c2ecf20Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
13418c2ecf20Sopenharmony_ci		irq_data = irq_domain_get_irq_data(domain, virq  + i);
13428c2ecf20Sopenharmony_ci		if (irq_data && irq_data->chip_data) {
13438c2ecf20Sopenharmony_ci			data = irq_data->chip_data;
13448c2ecf20Sopenharmony_ci			irq_iommu = &data->irq_2_iommu;
13458c2ecf20Sopenharmony_ci			raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
13468c2ecf20Sopenharmony_ci			clear_entries(irq_iommu);
13478c2ecf20Sopenharmony_ci			raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
13488c2ecf20Sopenharmony_ci			irq_domain_reset_irq_data(irq_data);
13498c2ecf20Sopenharmony_ci			kfree(data);
13508c2ecf20Sopenharmony_ci		}
13518c2ecf20Sopenharmony_ci	}
13528c2ecf20Sopenharmony_ci}
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_cistatic int intel_irq_remapping_alloc(struct irq_domain *domain,
13558c2ecf20Sopenharmony_ci				     unsigned int virq, unsigned int nr_irqs,
13568c2ecf20Sopenharmony_ci				     void *arg)
13578c2ecf20Sopenharmony_ci{
13588c2ecf20Sopenharmony_ci	struct intel_iommu *iommu = domain->host_data;
13598c2ecf20Sopenharmony_ci	struct irq_alloc_info *info = arg;
13608c2ecf20Sopenharmony_ci	struct intel_ir_data *data, *ird;
13618c2ecf20Sopenharmony_ci	struct irq_data *irq_data;
13628c2ecf20Sopenharmony_ci	struct irq_cfg *irq_cfg;
13638c2ecf20Sopenharmony_ci	int i, ret, index;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	if (!info || !iommu)
13668c2ecf20Sopenharmony_ci		return -EINVAL;
13678c2ecf20Sopenharmony_ci	if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_PCI_MSI &&
13688c2ecf20Sopenharmony_ci	    info->type != X86_IRQ_ALLOC_TYPE_PCI_MSIX)
13698c2ecf20Sopenharmony_ci		return -EINVAL;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	/*
13728c2ecf20Sopenharmony_ci	 * With IRQ remapping enabled, don't need contiguous CPU vectors
13738c2ecf20Sopenharmony_ci	 * to support multiple MSI interrupts.
13748c2ecf20Sopenharmony_ci	 */
13758c2ecf20Sopenharmony_ci	if (info->type == X86_IRQ_ALLOC_TYPE_PCI_MSI)
13768c2ecf20Sopenharmony_ci		info->flags &= ~X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
13798c2ecf20Sopenharmony_ci	if (ret < 0)
13808c2ecf20Sopenharmony_ci		return ret;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	ret = -ENOMEM;
13838c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
13848c2ecf20Sopenharmony_ci	if (!data)
13858c2ecf20Sopenharmony_ci		goto out_free_parent;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	down_read(&dmar_global_lock);
13888c2ecf20Sopenharmony_ci	index = alloc_irte(iommu, &data->irq_2_iommu, nr_irqs);
13898c2ecf20Sopenharmony_ci	up_read(&dmar_global_lock);
13908c2ecf20Sopenharmony_ci	if (index < 0) {
13918c2ecf20Sopenharmony_ci		pr_warn("Failed to allocate IRTE\n");
13928c2ecf20Sopenharmony_ci		kfree(data);
13938c2ecf20Sopenharmony_ci		goto out_free_parent;
13948c2ecf20Sopenharmony_ci	}
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
13978c2ecf20Sopenharmony_ci		irq_data = irq_domain_get_irq_data(domain, virq + i);
13988c2ecf20Sopenharmony_ci		irq_cfg = irqd_cfg(irq_data);
13998c2ecf20Sopenharmony_ci		if (!irq_data || !irq_cfg) {
14008c2ecf20Sopenharmony_ci			if (!i)
14018c2ecf20Sopenharmony_ci				kfree(data);
14028c2ecf20Sopenharmony_ci			ret = -EINVAL;
14038c2ecf20Sopenharmony_ci			goto out_free_data;
14048c2ecf20Sopenharmony_ci		}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci		if (i > 0) {
14078c2ecf20Sopenharmony_ci			ird = kzalloc(sizeof(*ird), GFP_KERNEL);
14088c2ecf20Sopenharmony_ci			if (!ird)
14098c2ecf20Sopenharmony_ci				goto out_free_data;
14108c2ecf20Sopenharmony_ci			/* Initialize the common data */
14118c2ecf20Sopenharmony_ci			ird->irq_2_iommu = data->irq_2_iommu;
14128c2ecf20Sopenharmony_ci			ird->irq_2_iommu.sub_handle = i;
14138c2ecf20Sopenharmony_ci		} else {
14148c2ecf20Sopenharmony_ci			ird = data;
14158c2ecf20Sopenharmony_ci		}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci		irq_data->hwirq = (index << 16) + i;
14188c2ecf20Sopenharmony_ci		irq_data->chip_data = ird;
14198c2ecf20Sopenharmony_ci		irq_data->chip = &intel_ir_chip;
14208c2ecf20Sopenharmony_ci		intel_irq_remapping_prepare_irte(ird, irq_cfg, info, index, i);
14218c2ecf20Sopenharmony_ci		irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT);
14228c2ecf20Sopenharmony_ci	}
14238c2ecf20Sopenharmony_ci	return 0;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ciout_free_data:
14268c2ecf20Sopenharmony_ci	intel_free_irq_resources(domain, virq, i);
14278c2ecf20Sopenharmony_ciout_free_parent:
14288c2ecf20Sopenharmony_ci	irq_domain_free_irqs_common(domain, virq, nr_irqs);
14298c2ecf20Sopenharmony_ci	return ret;
14308c2ecf20Sopenharmony_ci}
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_cistatic void intel_irq_remapping_free(struct irq_domain *domain,
14338c2ecf20Sopenharmony_ci				     unsigned int virq, unsigned int nr_irqs)
14348c2ecf20Sopenharmony_ci{
14358c2ecf20Sopenharmony_ci	intel_free_irq_resources(domain, virq, nr_irqs);
14368c2ecf20Sopenharmony_ci	irq_domain_free_irqs_common(domain, virq, nr_irqs);
14378c2ecf20Sopenharmony_ci}
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_cistatic int intel_irq_remapping_activate(struct irq_domain *domain,
14408c2ecf20Sopenharmony_ci					struct irq_data *irq_data, bool reserve)
14418c2ecf20Sopenharmony_ci{
14428c2ecf20Sopenharmony_ci	intel_ir_reconfigure_irte(irq_data, true);
14438c2ecf20Sopenharmony_ci	return 0;
14448c2ecf20Sopenharmony_ci}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_cistatic void intel_irq_remapping_deactivate(struct irq_domain *domain,
14478c2ecf20Sopenharmony_ci					   struct irq_data *irq_data)
14488c2ecf20Sopenharmony_ci{
14498c2ecf20Sopenharmony_ci	struct intel_ir_data *data = irq_data->chip_data;
14508c2ecf20Sopenharmony_ci	struct irte entry;
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	memset(&entry, 0, sizeof(entry));
14538c2ecf20Sopenharmony_ci	modify_irte(&data->irq_2_iommu, &entry);
14548c2ecf20Sopenharmony_ci}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_cistatic const struct irq_domain_ops intel_ir_domain_ops = {
14578c2ecf20Sopenharmony_ci	.alloc = intel_irq_remapping_alloc,
14588c2ecf20Sopenharmony_ci	.free = intel_irq_remapping_free,
14598c2ecf20Sopenharmony_ci	.activate = intel_irq_remapping_activate,
14608c2ecf20Sopenharmony_ci	.deactivate = intel_irq_remapping_deactivate,
14618c2ecf20Sopenharmony_ci};
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci/*
14648c2ecf20Sopenharmony_ci * Support of Interrupt Remapping Unit Hotplug
14658c2ecf20Sopenharmony_ci */
14668c2ecf20Sopenharmony_cistatic int dmar_ir_add(struct dmar_drhd_unit *dmaru, struct intel_iommu *iommu)
14678c2ecf20Sopenharmony_ci{
14688c2ecf20Sopenharmony_ci	int ret;
14698c2ecf20Sopenharmony_ci	int eim = x2apic_enabled();
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	if (eim && !ecap_eim_support(iommu->ecap)) {
14728c2ecf20Sopenharmony_ci		pr_info("DRHD %Lx: EIM not supported by DRHD, ecap %Lx\n",
14738c2ecf20Sopenharmony_ci			iommu->reg_phys, iommu->ecap);
14748c2ecf20Sopenharmony_ci		return -ENODEV;
14758c2ecf20Sopenharmony_ci	}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	if (ir_parse_ioapic_hpet_scope(dmaru->hdr, iommu)) {
14788c2ecf20Sopenharmony_ci		pr_warn("DRHD %Lx: failed to parse managed IOAPIC/HPET\n",
14798c2ecf20Sopenharmony_ci			iommu->reg_phys);
14808c2ecf20Sopenharmony_ci		return -ENODEV;
14818c2ecf20Sopenharmony_ci	}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	/* TODO: check all IOAPICs are covered by IOMMU */
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	/* Setup Interrupt-remapping now. */
14868c2ecf20Sopenharmony_ci	ret = intel_setup_irq_remapping(iommu);
14878c2ecf20Sopenharmony_ci	if (ret) {
14888c2ecf20Sopenharmony_ci		pr_err("Failed to setup irq remapping for %s\n",
14898c2ecf20Sopenharmony_ci		       iommu->name);
14908c2ecf20Sopenharmony_ci		intel_teardown_irq_remapping(iommu);
14918c2ecf20Sopenharmony_ci		ir_remove_ioapic_hpet_scope(iommu);
14928c2ecf20Sopenharmony_ci	} else {
14938c2ecf20Sopenharmony_ci		iommu_enable_irq_remapping(iommu);
14948c2ecf20Sopenharmony_ci	}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	return ret;
14978c2ecf20Sopenharmony_ci}
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ciint dmar_ir_hotplug(struct dmar_drhd_unit *dmaru, bool insert)
15008c2ecf20Sopenharmony_ci{
15018c2ecf20Sopenharmony_ci	int ret = 0;
15028c2ecf20Sopenharmony_ci	struct intel_iommu *iommu = dmaru->iommu;
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	if (!irq_remapping_enabled)
15058c2ecf20Sopenharmony_ci		return 0;
15068c2ecf20Sopenharmony_ci	if (iommu == NULL)
15078c2ecf20Sopenharmony_ci		return -EINVAL;
15088c2ecf20Sopenharmony_ci	if (!ecap_ir_support(iommu->ecap))
15098c2ecf20Sopenharmony_ci		return 0;
15108c2ecf20Sopenharmony_ci	if (irq_remapping_cap(IRQ_POSTING_CAP) &&
15118c2ecf20Sopenharmony_ci	    !cap_pi_support(iommu->cap))
15128c2ecf20Sopenharmony_ci		return -EBUSY;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	if (insert) {
15158c2ecf20Sopenharmony_ci		if (!iommu->ir_table)
15168c2ecf20Sopenharmony_ci			ret = dmar_ir_add(dmaru, iommu);
15178c2ecf20Sopenharmony_ci	} else {
15188c2ecf20Sopenharmony_ci		if (iommu->ir_table) {
15198c2ecf20Sopenharmony_ci			if (!bitmap_empty(iommu->ir_table->bitmap,
15208c2ecf20Sopenharmony_ci					  INTR_REMAP_TABLE_ENTRIES)) {
15218c2ecf20Sopenharmony_ci				ret = -EBUSY;
15228c2ecf20Sopenharmony_ci			} else {
15238c2ecf20Sopenharmony_ci				iommu_disable_irq_remapping(iommu);
15248c2ecf20Sopenharmony_ci				intel_teardown_irq_remapping(iommu);
15258c2ecf20Sopenharmony_ci				ir_remove_ioapic_hpet_scope(iommu);
15268c2ecf20Sopenharmony_ci			}
15278c2ecf20Sopenharmony_ci		}
15288c2ecf20Sopenharmony_ci	}
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	return ret;
15318c2ecf20Sopenharmony_ci}
1532