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