#include <linux/dmar.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
+#include <linux/hpet.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <asm/io_apic.h>
#include <linux/intel-iommu.h>
#include "intr_remapping.h"
#include <acpi/acpi.h>
+#include <asm/pci-direct.h>
+#include "pci.h"
static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
-static int ir_ioapic_num;
+static struct hpet_scope ir_hpet[MAX_HPET_TBS];
+static int ir_ioapic_num, ir_hpet_num;
int intr_remapping_enabled;
+static int disable_intremap;
+static __init int setup_nointremap(char *str)
+{
+ disable_intremap = 1;
+ return 0;
+}
+early_param("nointremap", setup_nointremap);
+
struct irq_2_iommu {
struct intel_iommu *iommu;
u16 irte_index;
};
#ifdef CONFIG_GENERIC_HARDIRQS
-static struct irq_2_iommu *get_one_free_irq_2_iommu(int cpu)
+static struct irq_2_iommu *get_one_free_irq_2_iommu(int node)
{
struct irq_2_iommu *iommu;
- int node;
-
- node = cpu_to_node(cpu);
iommu = kzalloc_node(sizeof(*iommu), GFP_ATOMIC, node);
- printk(KERN_DEBUG "alloc irq_2_iommu on cpu %d node %d\n", cpu, node);
+ printk(KERN_DEBUG "alloc irq_2_iommu on node %d\n", node);
return iommu;
}
return desc->irq_2_iommu;
}
-static struct irq_2_iommu *irq_2_iommu_alloc_cpu(unsigned int irq, int cpu)
+static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq)
{
struct irq_desc *desc;
struct irq_2_iommu *irq_iommu;
- /*
- * alloc irq desc if not allocated already.
- */
- desc = irq_to_desc_alloc_cpu(irq, cpu);
+ desc = irq_to_desc(irq);
if (!desc) {
printk(KERN_INFO "can not get irq_desc for %d\n", irq);
return NULL;
irq_iommu = desc->irq_2_iommu;
if (!irq_iommu)
- desc->irq_2_iommu = get_one_free_irq_2_iommu(cpu);
+ desc->irq_2_iommu = get_one_free_irq_2_iommu(irq_node(irq));
return desc->irq_2_iommu;
}
-static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq)
-{
- return irq_2_iommu_alloc_cpu(irq, boot_cpu_id);
-}
-
#else /* !CONFIG_SPARSE_IRQ */
static struct irq_2_iommu irq_2_iommuX[NR_IRQS];
index = irq_iommu->irte_index + irq_iommu->sub_handle;
irte = &iommu->ir_table->base[index];
- set_64bit((unsigned long *)irte, irte_modified->low);
+ set_64bit((unsigned long *)&irte->low, irte_modified->low);
+ set_64bit((unsigned long *)&irte->high, irte_modified->high);
__iommu_flush_cache(iommu, irte, sizeof(*irte));
rc = qi_flush_iec(iommu, index, 0);
return rc;
}
+struct intel_iommu *map_hpet_to_ir(u8 hpet_id)
+{
+ int i;
+
+ for (i = 0; i < MAX_HPET_TBS; i++)
+ if (ir_hpet[i].id == hpet_id)
+ return ir_hpet[i].iommu;
+ return NULL;
+}
+
struct intel_iommu *map_ioapic_to_ir(int apic)
{
int i;
return drhd->iommu;
}
+static int clear_entries(struct irq_2_iommu *irq_iommu)
+{
+ struct irte *start, *entry, *end;
+ struct intel_iommu *iommu;
+ int index;
+
+ if (irq_iommu->sub_handle)
+ return 0;
+
+ iommu = irq_iommu->iommu;
+ index = irq_iommu->irte_index + irq_iommu->sub_handle;
+
+ start = iommu->ir_table->base + index;
+ end = start + (1 << irq_iommu->irte_mask);
+
+ for (entry = start; entry < end; entry++) {
+ set_64bit((unsigned long *)&entry->low, 0);
+ set_64bit((unsigned long *)&entry->high, 0);
+ }
+
+ return qi_flush_iec(iommu, index, irq_iommu->irte_mask);
+}
+
int free_irte(int irq)
{
int rc = 0;
- int index, i;
- struct irte *irte;
- struct intel_iommu *iommu;
struct irq_2_iommu *irq_iommu;
unsigned long flags;
return -1;
}
- iommu = irq_iommu->iommu;
-
- index = irq_iommu->irte_index + irq_iommu->sub_handle;
- irte = &iommu->ir_table->base[index];
-
- if (!irq_iommu->sub_handle) {
- for (i = 0; i < (1 << irq_iommu->irte_mask); i++)
- set_64bit((unsigned long *)(irte + i), 0);
- rc = qi_flush_iec(iommu, index, irq_iommu->irte_mask);
- }
+ rc = clear_entries(irq_iommu);
irq_iommu->iommu = NULL;
irq_iommu->irte_index = 0;
return rc;
}
+/*
+ * source validation type
+ */
+#define SVT_NO_VERIFY 0x0 /* no verification is required */
+#define SVT_VERIFY_SID_SQ 0x1 /* verify using SID and SQ fiels */
+#define SVT_VERIFY_BUS 0x2 /* verify bus of request-id */
+
+/*
+ * source-id qualifier
+ */
+#define SQ_ALL_16 0x0 /* verify all 16 bits of request-id */
+#define SQ_13_IGNORE_1 0x1 /* verify most significant 13 bits, ignore
+ * the third least significant bit
+ */
+#define SQ_13_IGNORE_2 0x2 /* verify most significant 13 bits, ignore
+ * the second and third least significant bits
+ */
+#define SQ_13_IGNORE_3 0x3 /* verify most significant 13 bits, ignore
+ * the least three significant bits
+ */
+
+/*
+ * set SVT, SQ and SID fields of irte to verify
+ * source ids of interrupt requests
+ */
+static void set_irte_sid(struct irte *irte, unsigned int svt,
+ unsigned int sq, unsigned int sid)
+{
+ irte->svt = svt;
+ irte->sq = sq;
+ irte->sid = sid;
+}
+
+int set_ioapic_sid(struct irte *irte, int apic)
+{
+ int i;
+ u16 sid = 0;
+
+ if (!irte)
+ return -1;
+
+ for (i = 0; i < MAX_IO_APICS; i++) {
+ if (ir_ioapic[i].id == apic) {
+ sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn;
+ break;
+ }
+ }
+
+ if (sid == 0) {
+ pr_warning("Failed to set source-id of IOAPIC (%d)\n", apic);
+ return -1;
+ }
+
+ set_irte_sid(irte, 1, 0, sid);
+
+ return 0;
+}
+
+int set_hpet_sid(struct irte *irte, u8 id)
+{
+ int i;
+ u16 sid = 0;
+
+ if (!irte)
+ return -1;
+
+ for (i = 0; i < MAX_HPET_TBS; i++) {
+ if (ir_hpet[i].id == id) {
+ sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn;
+ break;
+ }
+ }
+
+ if (sid == 0) {
+ pr_warning("Failed to set source-id of HPET block (%d)\n", id);
+ return -1;
+ }
+
+ /*
+ * Should really use SQ_ALL_16. Some platforms are broken.
+ * While we figure out the right quirks for these broken platforms, use
+ * SQ_13_IGNORE_3 for now.
+ */
+ set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_13_IGNORE_3, sid);
+
+ return 0;
+}
+
+int set_msi_sid(struct irte *irte, struct pci_dev *dev)
+{
+ struct pci_dev *bridge;
+
+ if (!irte || !dev)
+ return -1;
+
+ /* PCIe device or Root Complex integrated PCI device */
+ if (pci_is_pcie(dev) || !dev->bus->parent) {
+ set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
+ (dev->bus->number << 8) | dev->devfn);
+ return 0;
+ }
+
+ bridge = pci_find_upstream_pcie_bridge(dev);
+ if (bridge) {
+ if (pci_is_pcie(bridge))/* this is a PCIe-to-PCI/PCIX bridge */
+ set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
+ (bridge->bus->number << 8) | dev->bus->number);
+ else /* this is a legacy PCI bridge */
+ set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
+ (bridge->bus->number << 8) | bridge->devfn);
+ }
+
+ return 0;
+}
+
static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
{
u64 addr;
readl, (sts & DMA_GSTS_IRTPS), sts);
spin_unlock_irqrestore(&iommu->register_lock, flags);
- if (mode == 0) {
- spin_lock_irqsave(&iommu->register_lock, flags);
-
- /* enable comaptiblity format interrupt pass through */
- iommu->gcmd |= DMA_GCMD_CFI;
- writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
-
- IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
- readl, (sts & DMA_GSTS_CFIS), sts);
-
- spin_unlock_irqrestore(&iommu->register_lock, flags);
- }
-
/*
* global invalidation of interrupt entry cache before enabling
* interrupt-remapping.
if (!iommu->ir_table)
return -ENOMEM;
- pages = alloc_pages(GFP_ATOMIC | __GFP_ZERO, INTR_REMAP_PAGE_ORDER);
+ pages = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO,
+ INTR_REMAP_PAGE_ORDER);
if (!pages) {
printk(KERN_ERR "failed to allocate pages of order %d\n",
spin_unlock_irqrestore(&iommu->register_lock, flags);
}
+int __init intr_remapping_supported(void)
+{
+ struct dmar_drhd_unit *drhd;
+
+ if (disable_intremap)
+ return 0;
+
+ if (!dmar_ir_support())
+ return 0;
+
+ for_each_drhd_unit(drhd) {
+ struct intel_iommu *iommu = drhd->iommu;
+
+ if (!ecap_ir_support(iommu->ecap))
+ return 0;
+ }
+
+ return 1;
+}
+
int __init enable_intr_remapping(int eim)
{
struct dmar_drhd_unit *drhd;
int setup = 0;
+ if (parse_ioapics_under_ir() != 1) {
+ printk(KERN_INFO "Not enable interrupt remapping\n");
+ return -1;
+ }
+
for_each_drhd_unit(drhd) {
struct intel_iommu *iommu = drhd->iommu;
return -1;
}
-static int ir_parse_ioapic_scope(struct acpi_dmar_header *header,
- struct intel_iommu *iommu)
+static void ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope,
+ struct intel_iommu *iommu)
+{
+ struct acpi_dmar_pci_path *path;
+ u8 bus;
+ int count;
+
+ bus = scope->bus;
+ path = (struct acpi_dmar_pci_path *)(scope + 1);
+ count = (scope->length - sizeof(struct acpi_dmar_device_scope))
+ / sizeof(struct acpi_dmar_pci_path);
+
+ while (--count > 0) {
+ /*
+ * Access PCI directly due to the PCI
+ * subsystem isn't initialized yet.
+ */
+ bus = read_pci_config_byte(bus, path->dev, path->fn,
+ PCI_SECONDARY_BUS);
+ path++;
+ }
+ ir_hpet[ir_hpet_num].bus = bus;
+ ir_hpet[ir_hpet_num].devfn = PCI_DEVFN(path->dev, path->fn);
+ ir_hpet[ir_hpet_num].iommu = iommu;
+ ir_hpet[ir_hpet_num].id = scope->enumeration_id;
+ ir_hpet_num++;
+}
+
+static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
+ struct intel_iommu *iommu)
+{
+ struct acpi_dmar_pci_path *path;
+ u8 bus;
+ int count;
+
+ bus = scope->bus;
+ path = (struct acpi_dmar_pci_path *)(scope + 1);
+ count = (scope->length - sizeof(struct acpi_dmar_device_scope))
+ / sizeof(struct acpi_dmar_pci_path);
+
+ while (--count > 0) {
+ /*
+ * Access PCI directly due to the PCI
+ * subsystem isn't initialized yet.
+ */
+ bus = read_pci_config_byte(bus, path->dev, path->fn,
+ PCI_SECONDARY_BUS);
+ path++;
+ }
+
+ ir_ioapic[ir_ioapic_num].bus = bus;
+ ir_ioapic[ir_ioapic_num].devfn = PCI_DEVFN(path->dev, path->fn);
+ ir_ioapic[ir_ioapic_num].iommu = iommu;
+ ir_ioapic[ir_ioapic_num].id = scope->enumeration_id;
+ ir_ioapic_num++;
+}
+
+static int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header,
+ struct intel_iommu *iommu)
{
struct acpi_dmar_hardware_unit *drhd;
struct acpi_dmar_device_scope *scope;
" 0x%Lx\n", scope->enumeration_id,
drhd->address);
- ir_ioapic[ir_ioapic_num].iommu = iommu;
- ir_ioapic[ir_ioapic_num].id = scope->enumeration_id;
- ir_ioapic_num++;
+ ir_parse_one_ioapic_scope(scope, iommu);
+ } else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET) {
+ if (ir_hpet_num == MAX_HPET_TBS) {
+ printk(KERN_WARNING "Exceeded Max HPET blocks\n");
+ return -1;
+ }
+
+ printk(KERN_INFO "HPET id %d under DRHD base"
+ " 0x%Lx\n", scope->enumeration_id,
+ drhd->address);
+
+ ir_parse_one_hpet_scope(scope, iommu);
}
start += scope->length;
}
struct intel_iommu *iommu = drhd->iommu;
if (ecap_ir_support(iommu->ecap)) {
- if (ir_parse_ioapic_scope(drhd->hdr, iommu))
+ if (ir_parse_ioapic_hpet_scope(drhd->hdr, iommu))
return -1;
ir_supported = 1;