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