From bd5cdad0c8e0f0adcd6e9c582abe4c4233c19b03 Mon Sep 17 00:00:00 2001 From: "Li, Zhen-Hua" Date: Mon, 25 Mar 2013 16:20:52 +0800 Subject: iommu/vt-d: dmar_fault should only clear PPF/PFO field. When there is a dmar irq, dmar_fault is called and all of the fields in FSTS are cleared. But ICE/IQE/ITE should not be cleared here, they need to be processed and cleared in function qi_check_fault. [Minor cleanup by Joerg Roedel] Signed-off-by: Li, Zhen-Hua Signed-off-by: Joerg Roedel --- drivers/iommu/dmar.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index e5cdaf87822c..9f8aa07360ba 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -1204,7 +1204,7 @@ irqreturn_t dmar_fault(int irq, void *dev_id) /* TBD: ignore advanced fault log currently */ if (!(fault_status & DMA_FSTS_PPF)) - goto clear_rest; + goto unlock_exit; fault_index = dma_fsts_fault_record_index(fault_status); reg = cap_fault_reg_offset(iommu->cap); @@ -1245,11 +1245,10 @@ irqreturn_t dmar_fault(int irq, void *dev_id) fault_index = 0; raw_spin_lock_irqsave(&iommu->register_lock, flag); } -clear_rest: - /* clear all the other faults */ - fault_status = readl(iommu->reg + DMAR_FSTS_REG); - writel(fault_status, iommu->reg + DMAR_FSTS_REG); + writel(DMA_FSTS_PFO | DMA_FSTS_PPF, iommu->reg + DMAR_FSTS_REG); + +unlock_exit: raw_spin_unlock_irqrestore(&iommu->register_lock, flag); return IRQ_HANDLED; } @@ -1297,6 +1296,7 @@ int __init enable_drhd_fault_handling(void) for_each_drhd_unit(drhd) { int ret; struct intel_iommu *iommu = drhd->iommu; + u32 fault_status; ret = dmar_set_interrupt(iommu); if (ret) { @@ -1309,6 +1309,8 @@ int __init enable_drhd_fault_handling(void) * Clear any previous faults. */ dmar_fault(iommu->irq, iommu); + fault_status = readl(iommu->reg + DMAR_FSTS_REG); + writel(fault_status, iommu->reg + DMAR_FSTS_REG); } return 0; -- cgit From 925fe08bce38d1ff052fe2209b9e2b8d5fbb7f98 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Wed, 27 Mar 2013 18:51:52 -0500 Subject: iommu/amd: Re-enable IOMMU event log interrupt after handling. Current driver does not clear the IOMMU event log interrupt bit in the IOMMU status register after processing an interrupt. This causes the IOMMU hardware to generate event log interrupt only once. This has been observed in both IOMMU v1 and V2 hardware. This patch clears the bit by writing 1 to bit 1 of the IOMMU status register (MMIO Offset 2020h) Signed-off-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 3 +++ drivers/iommu/amd_iommu_types.h | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index b287ca33833d..d6433e2a3bb4 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -703,6 +703,9 @@ static void iommu_poll_events(struct amd_iommu *iommu) u32 head, tail; unsigned long flags; + /* enable event interrupts again */ + writel(MMIO_STATUS_EVT_INT_MASK, iommu->mmio_base + MMIO_STATUS_OFFSET); + spin_lock_irqsave(&iommu->lock, flags); head = readl(iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index e38ab438bb34..083f98c0488b 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -99,6 +99,7 @@ #define PASID_MASK 0x000fffff /* MMIO status bits */ +#define MMIO_STATUS_EVT_INT_MASK (1 << 1) #define MMIO_STATUS_COM_WAIT_INT_MASK (1 << 2) #define MMIO_STATUS_PPR_INT_MASK (1 << 6) -- cgit From bb5547acfcd842950b8a22aa83f84af93388b9f2 Mon Sep 17 00:00:00 2001 From: Varun Sethi Date: Fri, 29 Mar 2013 01:23:58 +0530 Subject: iommu/fsl: Make iova dma_addr_t in the iommu_iova_to_phys API. This is required in case of PAMU, as it can support a window size of up to 64G (even on 32bit). Signed-off-by: Varun Sethi Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 2 +- drivers/iommu/exynos-iommu.c | 2 +- drivers/iommu/intel-iommu.c | 2 +- drivers/iommu/iommu.c | 3 +-- drivers/iommu/msm_iommu.c | 2 +- drivers/iommu/omap-iommu.c | 2 +- drivers/iommu/shmobile-iommu.c | 2 +- drivers/iommu/tegra-gart.c | 2 +- drivers/iommu/tegra-smmu.c | 2 +- 9 files changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index b287ca33833d..a7f6b04eaa5e 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3410,7 +3410,7 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova, } static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom, - unsigned long iova) + dma_addr_t iova) { struct protection_domain *domain = dom->priv; unsigned long offset_mask; diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 238a3caa949a..3f32d64ab87a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1027,7 +1027,7 @@ done: } static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, - unsigned long iova) + dma_addr_t iova) { struct exynos_iommu_domain *priv = domain->priv; unsigned long *entry; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 0099667a397e..6e0b9ffc79b5 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4111,7 +4111,7 @@ static size_t intel_iommu_unmap(struct iommu_domain *domain, } static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, - unsigned long iova) + dma_addr_t iova) { struct dmar_domain *dmar_domain = domain->priv; struct dma_pte *pte; diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index b972d430d92b..f730ed9d8af9 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -706,8 +706,7 @@ void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group) } EXPORT_SYMBOL_GPL(iommu_detach_group); -phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, - unsigned long iova) +phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { if (unlikely(domain->ops->iova_to_phys == NULL)) return 0; diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index 6a8870a31668..8ab4f41090af 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -554,7 +554,7 @@ fail: } static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain, - unsigned long va) + dma_addr_t va) { struct msm_priv *priv; struct msm_iommu_drvdata *iommu_drvdata; diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index 6ac02fa5910f..e02e5d71745b 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -1219,7 +1219,7 @@ static void omap_iommu_domain_destroy(struct iommu_domain *domain) } static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain, - unsigned long da) + dma_addr_t da) { struct omap_iommu_domain *omap_domain = domain->priv; struct omap_iommu *oiommu = omap_domain->iommu_dev; diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c index b6e8b57cf0a8..d572863dfccd 100644 --- a/drivers/iommu/shmobile-iommu.c +++ b/drivers/iommu/shmobile-iommu.c @@ -296,7 +296,7 @@ done: } static phys_addr_t shmobile_iommu_iova_to_phys(struct iommu_domain *domain, - unsigned long iova) + dma_addr_t iova) { struct shmobile_iommu_domain *sh_domain = domain->priv; uint32_t l1entry = 0, l2entry = 0; diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 86437575f94d..4aec8be38054 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -279,7 +279,7 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, } static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, - unsigned long iova) + dma_addr_t iova) { struct gart_device *gart = domain->priv; unsigned long pte; diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index b34e5fd7fd9e..bc9b59949d09 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -757,7 +757,7 @@ static size_t smmu_iommu_unmap(struct iommu_domain *domain, unsigned long iova, } static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain, - unsigned long iova) + dma_addr_t iova) { struct smmu_as *as = domain->priv; unsigned long *pte; -- cgit From 80f97f0f73b82444f714651ea053838d27779dca Mon Sep 17 00:00:00 2001 From: Varun Sethi Date: Fri, 29 Mar 2013 01:24:00 +0530 Subject: iommu/fsl: Add the window permission flag as a parameter to iommu_window_enable API. Each iommu window can have access permissions associated with it. Extended the window_enable API to incorporate window access permissions. In case of PAMU each window can have its specific set of permissions. Signed-off-by: Varun Sethi Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index f730ed9d8af9..1d72b4f5b006 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -853,12 +853,13 @@ EXPORT_SYMBOL_GPL(iommu_unmap); int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, - phys_addr_t paddr, u64 size) + phys_addr_t paddr, u64 size, int prot) { if (unlikely(domain->ops->domain_window_enable == NULL)) return -ENODEV; - return domain->ops->domain_window_enable(domain, wnd_nr, paddr, size); + return domain->ops->domain_window_enable(domain, wnd_nr, paddr, size, + prot); } EXPORT_SYMBOL_GPL(iommu_domain_window_enable); -- cgit From 03bbcb2e7e292838bb0244f5a7816d194c911d62 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 16 Apr 2013 16:38:32 -0400 Subject: iommu/vt-d: add quirk for broken interrupt remapping on 55XX chipsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A few years back intel published a spec update: http://www.intel.com/content/dam/doc/specification-update/5520-and-5500-chipset-ioh-specification-update.pdf For the 5520 and 5500 chipsets which contained an errata (specificially errata 53), which noted that these chipsets can't properly do interrupt remapping, and as a result the recommend that interrupt remapping be disabled in bios. While many vendors have a bios update to do exactly that, not all do, and of course not all users update their bios to a level that corrects the problem. As a result, occasionally interrupts can arrive at a cpu even after affinity for that interrupt has be moved, leading to lost or spurrious interrupts (usually characterized by the message: kernel: do_IRQ: 7.71 No irq handler for vector (irq -1) There have been several incidents recently of people seeing this error, and investigation has shown that they have system for which their BIOS level is such that this feature was not properly turned off. As such, it would be good to give them a reminder that their systems are vulnurable to this problem. For details of those that reported the problem, please see: https://bugzilla.redhat.com/show_bug.cgi?id=887006 [ Joerg: Removed CONFIG_IRQ_REMAP ifdef from early-quirks.c ] Signed-off-by: Neil Horman CC: Prarit Bhargava CC: Don Zickus CC: Don Dutile CC: Bjorn Helgaas CC: Asit Mallick CC: David Woodhouse CC: linux-pci@vger.kernel.org CC: Joerg Roedel CC: Konrad Rzeszutek Wilk CC: Arkadiusz Miƛkiewicz Signed-off-by: Joerg Roedel --- drivers/iommu/intel_irq_remapping.c | 10 ++++++++++ drivers/iommu/irq_remapping.c | 6 ++++++ drivers/iommu/irq_remapping.h | 2 ++ 3 files changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index f3b8f23b5d8f..5b19b2d6ec2d 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -524,6 +524,16 @@ static int __init intel_irq_remapping_supported(void) if (disable_irq_remap) return 0; + if (irq_remap_broken) { + WARN_TAINT(1, TAINT_FIRMWARE_WORKAROUND, + "This system BIOS has enabled interrupt remapping\n" + "on a chipset that contains an erratum making that\n" + "feature unstable. To maintain system stability\n" + "interrupt remapping is being disabled. Please\n" + "contact your BIOS vendor for an update\n"); + disable_irq_remap = 1; + return 0; + } if (!dmar_ir_support()) return 0; diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c index d56f8c17c5fe..3c11043bdee5 100644 --- a/drivers/iommu/irq_remapping.c +++ b/drivers/iommu/irq_remapping.c @@ -19,6 +19,7 @@ int irq_remapping_enabled; int disable_irq_remap; +int irq_remap_broken; int disable_sourceid_checking; int no_x2apic_optout; @@ -211,6 +212,11 @@ void __init setup_irq_remapping_ops(void) #endif } +void set_irq_remapping_broken(void) +{ + irq_remap_broken = 1; +} + int irq_remapping_supported(void) { if (disable_irq_remap) diff --git a/drivers/iommu/irq_remapping.h b/drivers/iommu/irq_remapping.h index ecb637670405..90c4dae5a46b 100644 --- a/drivers/iommu/irq_remapping.h +++ b/drivers/iommu/irq_remapping.h @@ -32,6 +32,7 @@ struct pci_dev; struct msi_msg; extern int disable_irq_remap; +extern int irq_remap_broken; extern int disable_sourceid_checking; extern int no_x2apic_optout; extern int irq_remapping_enabled; @@ -89,6 +90,7 @@ extern struct irq_remap_ops amd_iommu_irq_ops; #define irq_remapping_enabled 0 #define disable_irq_remap 1 +#define irq_remap_broken 0 #endif /* CONFIG_IRQ_REMAP */ -- cgit From a0e191b23d93464bf8726eba8da081d23c08e185 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 9 Apr 2013 15:04:36 +0200 Subject: iommu/amd: Remove map_sg_no_iommu() This function was intended as a fall-back if the map_sg function is called for a device not mapped by the IOMMU. Since the AMD IOMMU driver uses per-device dma_ops this can never happen. So this function isn't needed anymore. Reviewed-by: Shuah Khan Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index d6433e2a3bb4..93b18746d711 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2841,24 +2841,6 @@ static void unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size, spin_unlock_irqrestore(&domain->lock, flags); } -/* - * This is a special map_sg function which is used if we should map a - * device which is not handled by an AMD IOMMU in the system. - */ -static int map_sg_no_iommu(struct device *dev, struct scatterlist *sglist, - int nelems, int dir) -{ - struct scatterlist *s; - int i; - - for_each_sg(sglist, s, nelems, i) { - s->dma_address = (dma_addr_t)sg_phys(s); - s->dma_length = s->length; - } - - return nelems; -} - /* * The exported map_sg function for dma_ops (handles scatter-gather * lists). @@ -2878,9 +2860,7 @@ static int map_sg(struct device *dev, struct scatterlist *sglist, INC_STATS_COUNTER(cnt_map_sg); domain = get_domain(dev); - if (PTR_ERR(domain) == -EINVAL) - return map_sg_no_iommu(dev, sglist, nelems, dir); - else if (IS_ERR(domain)) + if (IS_ERR(domain)) return 0; dma_mask = *dev->dma_mask; -- cgit From 0dfedd619442f3a64de4fcd8a735664d18e86ee7 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 9 Apr 2013 15:39:16 +0200 Subject: iommu/amd: Use AMD specific data structure for irq remapping For compatibility reasons the irq remapping code for the AMD IOMMU used the same per-irq data structure as the Intel implementation. Now that support for the AMD specific data structure is upstream we can use this one instead. Reviewed-by: Shuah Khan Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 54 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 93b18746d711..b05599dd2c43 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3990,7 +3990,7 @@ static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count) c = 0; if (c == count) { - struct irq_2_iommu *irte_info; + struct irq_2_irte *irte_info; for (; c != 0; --c) table->table[index - c + 1] = IRTE_ALLOCATED; @@ -3998,9 +3998,9 @@ static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count) index -= count - 1; cfg->remapped = 1; - irte_info = &cfg->irq_2_iommu; - irte_info->sub_handle = devid; - irte_info->irte_index = index; + irte_info = &cfg->irq_2_irte; + irte_info->devid = devid; + irte_info->index = index; goto out; } @@ -4081,7 +4081,7 @@ static int setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry, struct io_apic_irq_attr *attr) { struct irq_remap_table *table; - struct irq_2_iommu *irte_info; + struct irq_2_irte *irte_info; struct irq_cfg *cfg; union irte irte; int ioapic_id; @@ -4093,7 +4093,7 @@ static int setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry, if (!cfg) return -EINVAL; - irte_info = &cfg->irq_2_iommu; + irte_info = &cfg->irq_2_irte; ioapic_id = mpc_ioapic_id(attr->ioapic); devid = get_ioapic_devid(ioapic_id); @@ -4108,8 +4108,8 @@ static int setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry, /* Setup IRQ remapping info */ cfg->remapped = 1; - irte_info->sub_handle = devid; - irte_info->irte_index = index; + irte_info->devid = devid; + irte_info->index = index; /* Setup IRTE for IOMMU */ irte.val = 0; @@ -4143,7 +4143,7 @@ static int setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry, static int set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) { - struct irq_2_iommu *irte_info; + struct irq_2_irte *irte_info; unsigned int dest, irq; struct irq_cfg *cfg; union irte irte; @@ -4154,12 +4154,12 @@ static int set_affinity(struct irq_data *data, const struct cpumask *mask, cfg = data->chip_data; irq = data->irq; - irte_info = &cfg->irq_2_iommu; + irte_info = &cfg->irq_2_irte; if (!cpumask_intersects(mask, cpu_online_mask)) return -EINVAL; - if (get_irte(irte_info->sub_handle, irte_info->irte_index, &irte)) + if (get_irte(irte_info->devid, irte_info->index, &irte)) return -EBUSY; if (assign_irq_vector(irq, cfg, mask)) @@ -4175,7 +4175,7 @@ static int set_affinity(struct irq_data *data, const struct cpumask *mask, irte.fields.vector = cfg->vector; irte.fields.destination = dest; - modify_irte(irte_info->sub_handle, irte_info->irte_index, irte); + modify_irte(irte_info->devid, irte_info->index, irte); if (cfg->move_in_progress) send_cleanup_vector(cfg); @@ -4187,16 +4187,16 @@ static int set_affinity(struct irq_data *data, const struct cpumask *mask, static int free_irq(int irq) { - struct irq_2_iommu *irte_info; + struct irq_2_irte *irte_info; struct irq_cfg *cfg; cfg = irq_get_chip_data(irq); if (!cfg) return -EINVAL; - irte_info = &cfg->irq_2_iommu; + irte_info = &cfg->irq_2_irte; - free_irte(irte_info->sub_handle, irte_info->irte_index); + free_irte(irte_info->devid, irte_info->index); return 0; } @@ -4205,7 +4205,7 @@ static void compose_msi_msg(struct pci_dev *pdev, unsigned int irq, unsigned int dest, struct msi_msg *msg, u8 hpet_id) { - struct irq_2_iommu *irte_info; + struct irq_2_irte *irte_info; struct irq_cfg *cfg; union irte irte; @@ -4213,7 +4213,7 @@ static void compose_msi_msg(struct pci_dev *pdev, if (!cfg) return; - irte_info = &cfg->irq_2_iommu; + irte_info = &cfg->irq_2_irte; irte.val = 0; irte.fields.vector = cfg->vector; @@ -4222,11 +4222,11 @@ static void compose_msi_msg(struct pci_dev *pdev, irte.fields.dm = apic->irq_dest_mode; irte.fields.valid = 1; - modify_irte(irte_info->sub_handle, irte_info->irte_index, irte); + modify_irte(irte_info->devid, irte_info->index, irte); msg->address_hi = MSI_ADDR_BASE_HI; msg->address_lo = MSI_ADDR_BASE_LO; - msg->data = irte_info->irte_index; + msg->data = irte_info->index; } static int msi_alloc_irq(struct pci_dev *pdev, int irq, int nvec) @@ -4251,7 +4251,7 @@ static int msi_alloc_irq(struct pci_dev *pdev, int irq, int nvec) static int msi_setup_irq(struct pci_dev *pdev, unsigned int irq, int index, int offset) { - struct irq_2_iommu *irte_info; + struct irq_2_irte *irte_info; struct irq_cfg *cfg; u16 devid; @@ -4266,18 +4266,18 @@ static int msi_setup_irq(struct pci_dev *pdev, unsigned int irq, return 0; devid = get_device_id(&pdev->dev); - irte_info = &cfg->irq_2_iommu; + irte_info = &cfg->irq_2_irte; cfg->remapped = 1; - irte_info->sub_handle = devid; - irte_info->irte_index = index + offset; + irte_info->devid = devid; + irte_info->index = index + offset; return 0; } static int setup_hpet_msi(unsigned int irq, unsigned int id) { - struct irq_2_iommu *irte_info; + struct irq_2_irte *irte_info; struct irq_cfg *cfg; int index, devid; @@ -4285,7 +4285,7 @@ static int setup_hpet_msi(unsigned int irq, unsigned int id) if (!cfg) return -EINVAL; - irte_info = &cfg->irq_2_iommu; + irte_info = &cfg->irq_2_irte; devid = get_hpet_devid(id); if (devid < 0) return devid; @@ -4295,8 +4295,8 @@ static int setup_hpet_msi(unsigned int irq, unsigned int id) return index; cfg->remapped = 1; - irte_info->sub_handle = devid; - irte_info->irte_index = index; + irte_info->devid = devid; + irte_info->index = index; return 0; } -- cgit From 197887f03daecdb3ae21bafeb4155412abad3497 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 9 Apr 2013 21:14:08 +0200 Subject: iommu/amd: Properly initialize irq-table lock Fixes a lockdep warning. Cc: stable@vger.kernel.org # >= v3.7 Reviewed-by: Shuah Khan Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index b05599dd2c43..f42793d1574d 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3930,6 +3930,9 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic) if (!table) goto out; + /* Initialize table spin-lock */ + spin_lock_init(&table->lock); + if (ioapic) /* Keep the first 32 indexes free for IOAPIC interrupts */ table->min_index = 32; -- cgit From d3da2200d0d6096e7c7f9a951cd23bc391403326 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 9 Apr 2013 17:32:34 +0200 Subject: iommu/amd: Move add_special_device() to __init The function is only called by other __init functions, so it can be moved to __init too. Reviewed-by: Shuah Khan Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index e3c2d74b7684..ea94ee644989 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -703,7 +703,7 @@ static void __init set_dev_entry_from_acpi(struct amd_iommu *iommu, set_iommu_for_device(iommu, devid); } -static int add_special_device(u8 type, u8 id, u16 devid) +static int __init add_special_device(u8 type, u8 id, u16 devid) { struct devid_map *entry; struct list_head *list; -- cgit From 31cff67f6b00456cac8890e8fd97694cadc754b8 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 9 Apr 2013 16:53:58 +0200 Subject: iommu/amd: Extend IVRS special device data structure This patch extends the devid_map data structure to allow ioapic and hpet entries in ivrs to be overridden on the kernel command line. Reviewed-by: Shuah Khan Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 30 ++++++++++++++++++++---------- drivers/iommu/amd_iommu_types.h | 1 + 2 files changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index ea94ee644989..3a210f0c1b3f 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -703,25 +703,35 @@ static void __init set_dev_entry_from_acpi(struct amd_iommu *iommu, set_iommu_for_device(iommu, devid); } -static int __init add_special_device(u8 type, u8 id, u16 devid) +static int __init add_special_device(u8 type, u8 id, u16 devid, bool cmd_line) { struct devid_map *entry; struct list_head *list; - if (type != IVHD_SPECIAL_IOAPIC && type != IVHD_SPECIAL_HPET) + if (type == IVHD_SPECIAL_IOAPIC) + list = &ioapic_map; + else if (type == IVHD_SPECIAL_HPET) + list = &hpet_map; + else return -EINVAL; + list_for_each_entry(entry, list, list) { + if (!(entry->id == id && entry->cmd_line)) + continue; + + pr_info("AMD-Vi: Command-line override present for %s id %d - ignoring\n", + type == IVHD_SPECIAL_IOAPIC ? "IOAPIC" : "HPET", id); + + return 0; + } + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; - entry->id = id; - entry->devid = devid; - - if (type == IVHD_SPECIAL_IOAPIC) - list = &ioapic_map; - else - list = &hpet_map; + entry->id = id; + entry->devid = devid; + entry->cmd_line = cmd_line; list_add_tail(&entry->list, list); @@ -929,7 +939,7 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, PCI_FUNC(devid)); set_dev_entry_from_acpi(iommu, devid, e->flags, 0); - ret = add_special_device(type, handle, devid); + ret = add_special_device(type, handle, devid, false); if (ret) return ret; break; diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index 083f98c0488b..b81153fb9e60 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -592,6 +592,7 @@ struct devid_map { struct list_head list; u8 id; u16 devid; + bool cmd_line; }; /* Map HPET and IOAPIC ids to the devid used by the IOMMU */ -- cgit From 235dacbc795bb7ccf69db8ad9ff1587314cf857d Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 9 Apr 2013 17:53:14 +0200 Subject: iommu/amd: Add early maps for ioapic and hpet This is needed in a later patch were ioapic_map and hpet_map entries are created before the slab allocator is initialized (and thus add_special_device() can't be used). Reviewed-by: Shuah Khan Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 3a210f0c1b3f..2a3b1b174d5e 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -213,6 +213,13 @@ enum iommu_init_state { IOMMU_INIT_ERROR, }; +/* Early ioapic and hpet maps from kernel command line */ +#define EARLY_MAP_SIZE 4 +static struct devid_map __initdata early_ioapic_map[EARLY_MAP_SIZE]; +static struct devid_map __initdata early_hpet_map[EARLY_MAP_SIZE]; +static int __initdata early_ioapic_map_size; +static int __initdata early_hpet_map_size; + static enum iommu_init_state init_state = IOMMU_START_STATE; static int amd_iommu_enable_interrupts(void); @@ -738,6 +745,31 @@ static int __init add_special_device(u8 type, u8 id, u16 devid, bool cmd_line) return 0; } +static int __init add_early_maps(void) +{ + int i, ret; + + for (i = 0; i < early_ioapic_map_size; ++i) { + ret = add_special_device(IVHD_SPECIAL_IOAPIC, + early_ioapic_map[i].id, + early_ioapic_map[i].devid, + early_ioapic_map[i].cmd_line); + if (ret) + return ret; + } + + for (i = 0; i < early_hpet_map_size; ++i) { + ret = add_special_device(IVHD_SPECIAL_HPET, + early_hpet_map[i].id, + early_hpet_map[i].devid, + early_hpet_map[i].cmd_line); + if (ret) + return ret; + } + + return 0; +} + /* * Reads the device exclusion range from ACPI and initializes the IOMMU with * it @@ -774,6 +806,12 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, u32 dev_i, ext_flags = 0; bool alias = false; struct ivhd_entry *e; + int ret; + + + ret = add_early_maps(); + if (ret) + return ret; /* * First save the recommended feature enable bits from ACPI -- cgit From 440e899805411d827d4fcce9eb37bf2417c812db Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 9 Apr 2013 16:35:28 +0200 Subject: iommu/amd: Add ioapic and hpet ivrs override Add two new kernel commandline parameters ivrs_ioapic and ivrs_hpet to override the Id->DeviceId mapping from the IVRS ACPI table. This can be used to work around broken BIOSes to get interrupt remapping working on AMD systems. Tested-by: Borislav Petkov Tested-by: Suravee Suthikulanit Reviewed-by: Shuah Khan Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 64 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 2a3b1b174d5e..030d6abf31e7 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -2145,8 +2145,68 @@ static int __init parse_amd_iommu_options(char *str) return 1; } -__setup("amd_iommu_dump", parse_amd_iommu_dump); -__setup("amd_iommu=", parse_amd_iommu_options); +static int __init parse_ivrs_ioapic(char *str) +{ + unsigned int bus, dev, fn; + int ret, id, i; + u16 devid; + + ret = sscanf(str, "[%d]=%x:%x.%x", &id, &bus, &dev, &fn); + + if (ret != 4) { + pr_err("AMD-Vi: Invalid command line: ivrs_ioapic%s\n", str); + return 1; + } + + if (early_ioapic_map_size == EARLY_MAP_SIZE) { + pr_err("AMD-Vi: Early IOAPIC map overflow - ignoring ivrs_ioapic%s\n", + str); + return 1; + } + + devid = ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fn & 0x7); + + i = early_ioapic_map_size++; + early_ioapic_map[i].id = id; + early_ioapic_map[i].devid = devid; + early_ioapic_map[i].cmd_line = true; + + return 1; +} + +static int __init parse_ivrs_hpet(char *str) +{ + unsigned int bus, dev, fn; + int ret, id, i; + u16 devid; + + ret = sscanf(str, "[%d]=%x:%x.%x", &id, &bus, &dev, &fn); + + if (ret != 4) { + pr_err("AMD-Vi: Invalid command line: ivrs_hpet%s\n", str); + return 1; + } + + if (early_hpet_map_size == EARLY_MAP_SIZE) { + pr_err("AMD-Vi: Early HPET map overflow - ignoring ivrs_hpet%s\n", + str); + return 1; + } + + devid = ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fn & 0x7); + + i = early_hpet_map_size++; + early_hpet_map[i].id = id; + early_hpet_map[i].devid = devid; + early_hpet_map[i].cmd_line = true; + + return 1; +} + +__setup("amd_iommu_dump", parse_amd_iommu_dump); +__setup("amd_iommu=", parse_amd_iommu_options); +__setup("ivrs_ioapic", parse_ivrs_ioapic); +__setup("ivrs_hpet", parse_ivrs_hpet); IOMMU_INIT_FINISH(amd_iommu_detect, gart_iommu_hole_init, -- cgit From dfbb6d476de5a7a6e9ed10d43f626caa669cfd28 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 9 Apr 2013 19:06:18 +0200 Subject: iommu/amd: Don't report firmware bugs with cmd-line ivrs overrides When the IVRS entries for IOAPIC and HPET are overridden on the kernel command line, a problem detected in the check function might not be a firmware bug anymore. So disable the firmware bug reporting if the user provided valid ivrs_ioapic or ivrs_hpet entries on the command line. Reviewed-by: Shuah Khan Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 030d6abf31e7..976794166481 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -219,6 +219,7 @@ static struct devid_map __initdata early_ioapic_map[EARLY_MAP_SIZE]; static struct devid_map __initdata early_hpet_map[EARLY_MAP_SIZE]; static int __initdata early_ioapic_map_size; static int __initdata early_hpet_map_size; +static bool __initdata cmdline_maps; static enum iommu_init_state init_state = IOMMU_START_STATE; @@ -1686,18 +1687,28 @@ static void __init free_on_init_error(void) static bool __init check_ioapic_information(void) { + const char *fw_bug = FW_BUG; bool ret, has_sb_ioapic; int idx; has_sb_ioapic = false; ret = false; + /* + * If we have map overrides on the kernel command line the + * messages in this function might not describe firmware bugs + * anymore - so be careful + */ + if (cmdline_maps) + fw_bug = ""; + for (idx = 0; idx < nr_ioapics; idx++) { int devid, id = mpc_ioapic_id(idx); devid = get_ioapic_devid(id); if (devid < 0) { - pr_err(FW_BUG "AMD-Vi: IOAPIC[%d] not in IVRS table\n", id); + pr_err("%sAMD-Vi: IOAPIC[%d] not in IVRS table\n", + fw_bug, id); ret = false; } else if (devid == IOAPIC_SB_DEVID) { has_sb_ioapic = true; @@ -1714,11 +1725,11 @@ static bool __init check_ioapic_information(void) * when the BIOS is buggy and provides us the wrong * device id for the IOAPIC in the system. */ - pr_err(FW_BUG "AMD-Vi: No southbridge IOAPIC found in IVRS table\n"); + pr_err("%sAMD-Vi: No southbridge IOAPIC found\n", fw_bug); } if (!ret) - pr_err("AMD-Vi: Disabling interrupt remapping due to BIOS Bug(s)\n"); + pr_err("AMD-Vi: Disabling interrupt remapping\n"); return ret; } @@ -2166,6 +2177,7 @@ static int __init parse_ivrs_ioapic(char *str) devid = ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fn & 0x7); + cmdline_maps = true; i = early_ioapic_map_size++; early_ioapic_map[i].id = id; early_ioapic_map[i].devid = devid; @@ -2195,6 +2207,7 @@ static int __init parse_ivrs_hpet(char *str) devid = ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fn & 0x7); + cmdline_maps = true; i = early_hpet_map_size++; early_hpet_map[i].id = id; early_hpet_map[i].devid = devid; -- cgit From d3263bc29706e42f74d8800807c2dedf320d77f1 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 18 Apr 2013 17:55:04 +0200 Subject: iommu/amd: Workaround for ERBT1312 Work around an IOMMU hardware bug where clearing the EVT_INT or PPR_INT bit in the status register may race with the hardware trying to set it again. When not handled the bit might not be cleared and we lose all future event or ppr interrupts. Reported-by: Suravee Suthikulpanit Cc: stable@vger.kernel.org Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index f42793d1574d..27792f8c429d 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -700,14 +700,23 @@ retry: static void iommu_poll_events(struct amd_iommu *iommu) { - u32 head, tail; + u32 head, tail, status; unsigned long flags; - /* enable event interrupts again */ - writel(MMIO_STATUS_EVT_INT_MASK, iommu->mmio_base + MMIO_STATUS_OFFSET); - spin_lock_irqsave(&iommu->lock, flags); + /* enable event interrupts again */ + do { + /* + * Workaround for Erratum ERBT1312 + * Clearing the EVT_INT bit may race in the hardware, so read + * it again and make sure it was really cleared + */ + status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); + writel(MMIO_STATUS_EVT_INT_MASK, + iommu->mmio_base + MMIO_STATUS_OFFSET); + } while (status & MMIO_STATUS_EVT_INT_MASK); + head = readl(iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); tail = readl(iommu->mmio_base + MMIO_EVT_TAIL_OFFSET); @@ -744,16 +753,25 @@ static void iommu_handle_ppr_entry(struct amd_iommu *iommu, u64 *raw) static void iommu_poll_ppr_log(struct amd_iommu *iommu) { unsigned long flags; - u32 head, tail; + u32 head, tail, status; if (iommu->ppr_log == NULL) return; - /* enable ppr interrupts again */ - writel(MMIO_STATUS_PPR_INT_MASK, iommu->mmio_base + MMIO_STATUS_OFFSET); - spin_lock_irqsave(&iommu->lock, flags); + /* enable ppr interrupts again */ + do { + /* + * Workaround for Erratum ERBT1312 + * Clearing the PPR_INT bit may race in the hardware, so read + * it again and make sure it was really cleared + */ + status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); + writel(MMIO_STATUS_PPR_INT_MASK, + iommu->mmio_base + MMIO_STATUS_OFFSET); + } while (status & MMIO_STATUS_PPR_INT_MASK); + head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET); tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET); -- cgit From 3f398bc7762adcd860bd2acce18465a106f47325 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Mon, 22 Apr 2013 16:32:34 -0500 Subject: iommu/AMD: Per-thread IOMMU Interrupt Handling In the current interrupt handling scheme, there are as many threads as the number of IOMMUs. Each thread is created and assigned to an IOMMU at the time of registering interrupt handlers (request_threaded_irq). When an IOMMU HW generates an interrupt, the irq handler (top half) wakes up the corresponding thread to process event and PPR logs of all IOMMUs starting from the 1st IOMMU. In the system with multiple IOMMU,this handling scheme complicates the synchronization of the IOMMU data structures and status registers as there could be multiple threads competing for the same IOMMU while the other IOMMU could be left unhandled. To simplify, this patch is proposing a different interrupt handling scheme by having each thread only managing interrupts of the corresponding IOMMU. This can be achieved by passing the struct amd_iommu when registering the interrupt handlers. This structure is unique for each IOMMU and can be used by the bottom half thread to identify the IOMMU to be handled instead of calling for_each_iommu. Besides this also eliminate the needs to lock the IOMMU for processing event and PPR logs. Signed-off-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 82 +++++++++++++++++------------------------- drivers/iommu/amd_iommu_init.c | 2 +- 2 files changed, 34 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 27792f8c429d..c6f3c7e04684 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -700,22 +700,7 @@ retry: static void iommu_poll_events(struct amd_iommu *iommu) { - u32 head, tail, status; - unsigned long flags; - - spin_lock_irqsave(&iommu->lock, flags); - - /* enable event interrupts again */ - do { - /* - * Workaround for Erratum ERBT1312 - * Clearing the EVT_INT bit may race in the hardware, so read - * it again and make sure it was really cleared - */ - status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); - writel(MMIO_STATUS_EVT_INT_MASK, - iommu->mmio_base + MMIO_STATUS_OFFSET); - } while (status & MMIO_STATUS_EVT_INT_MASK); + u32 head, tail; head = readl(iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); tail = readl(iommu->mmio_base + MMIO_EVT_TAIL_OFFSET); @@ -726,8 +711,6 @@ static void iommu_poll_events(struct amd_iommu *iommu) } writel(head, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); - - spin_unlock_irqrestore(&iommu->lock, flags); } static void iommu_handle_ppr_entry(struct amd_iommu *iommu, u64 *raw) @@ -752,26 +735,11 @@ static void iommu_handle_ppr_entry(struct amd_iommu *iommu, u64 *raw) static void iommu_poll_ppr_log(struct amd_iommu *iommu) { - unsigned long flags; - u32 head, tail, status; + u32 head, tail; if (iommu->ppr_log == NULL) return; - spin_lock_irqsave(&iommu->lock, flags); - - /* enable ppr interrupts again */ - do { - /* - * Workaround for Erratum ERBT1312 - * Clearing the PPR_INT bit may race in the hardware, so read - * it again and make sure it was really cleared - */ - status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); - writel(MMIO_STATUS_PPR_INT_MASK, - iommu->mmio_base + MMIO_STATUS_OFFSET); - } while (status & MMIO_STATUS_PPR_INT_MASK); - head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET); tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET); @@ -807,34 +775,50 @@ static void iommu_poll_ppr_log(struct amd_iommu *iommu) head = (head + PPR_ENTRY_SIZE) % PPR_LOG_SIZE; writel(head, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET); - /* - * Release iommu->lock because ppr-handling might need to - * re-acquire it - */ - spin_unlock_irqrestore(&iommu->lock, flags); - /* Handle PPR entry */ iommu_handle_ppr_entry(iommu, entry); - spin_lock_irqsave(&iommu->lock, flags); - /* Refresh ring-buffer information */ head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET); tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET); } - - spin_unlock_irqrestore(&iommu->lock, flags); } irqreturn_t amd_iommu_int_thread(int irq, void *data) { - struct amd_iommu *iommu; + struct amd_iommu *iommu = (struct amd_iommu *) data; + u32 status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); - for_each_iommu(iommu) { - iommu_poll_events(iommu); - iommu_poll_ppr_log(iommu); - } + while (status & (MMIO_STATUS_EVT_INT_MASK | MMIO_STATUS_PPR_INT_MASK)) { + /* Enable EVT and PPR interrupts again */ + writel((MMIO_STATUS_EVT_INT_MASK | MMIO_STATUS_PPR_INT_MASK), + iommu->mmio_base + MMIO_STATUS_OFFSET); + if (status & MMIO_STATUS_EVT_INT_MASK) { + pr_devel("AMD-Vi: Processing IOMMU Event Log\n"); + iommu_poll_events(iommu); + } + + if (status & MMIO_STATUS_PPR_INT_MASK) { + pr_devel("AMD-Vi: Processing IOMMU PPR Log\n"); + iommu_poll_ppr_log(iommu); + } + + /* + * Hardware bug: ERBT1312 + * When re-enabling interrupt (by writing 1 + * to clear the bit), the hardware might also try to set + * the interrupt bit in the event status register. + * In this scenario, the bit will be set, and disable + * subsequent interrupts. + * + * Workaround: The IOMMU driver should read back the + * status register and check if the interrupt bits are cleared. + * If not, driver will need to go through the interrupt handler + * again and re-clear the bits + */ + status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); + } return IRQ_HANDLED; } diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 976794166481..3d3d6cd52d47 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -1324,7 +1324,7 @@ static int iommu_setup_msi(struct amd_iommu *iommu) amd_iommu_int_handler, amd_iommu_int_thread, 0, "AMD-Vi", - iommu->dev); + iommu); if (r) { pci_disable_msi(iommu->dev); -- cgit From 83ed9c13e37e352b5a16caca01798c2766d91c29 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 23 Apr 2013 10:47:44 +0800 Subject: iommu/amd: fix error return code in early_amd_iommu_init() Fix to return -ENOMEM int the memory alloc error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 3d3d6cd52d47..9d23552a9619 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -1860,6 +1860,7 @@ static int __init early_amd_iommu_init(void) * Interrupt remapping enabled, create kmem_cache for the * remapping tables. */ + ret = -ENOMEM; amd_iommu_irq_cache = kmem_cache_create("irq_remap_cache", MAX_IRQS_PER_TABLE * sizeof(u32), IRQ_TABLE_ALIGNMENT, -- cgit From 3a93c841c2b3b14824f7728dd74bd00a1cedb806 Mon Sep 17 00:00:00 2001 From: Takao Indoh Date: Tue, 23 Apr 2013 17:35:03 +0900 Subject: iommu/vt-d: Disable translation if already enabled This patch disables translation(dma-remapping) before its initialization if it is already enabled. This is needed for kexec/kdump boot. If dma-remapping is enabled in the first kernel, it need to be disabled before initializing its page table during second kernel boot. Wei Hu also reported that this is needed when second kernel boots with intel_iommu=off. Basically iommu->gcmd is used to know whether translation is enabled or disabled, but it is always zero at boot time even when translation is enabled since iommu->gcmd is initialized without considering such a case. Therefor this patch synchronizes iommu->gcmd value with global command register when iommu structure is allocated. Signed-off-by: Takao Indoh Signed-off-by: Joerg Roedel --- drivers/iommu/dmar.c | 11 ++++++++++- drivers/iommu/intel-iommu.c | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 9f8aa07360ba..f7890edb22f5 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -645,7 +645,7 @@ out: int alloc_iommu(struct dmar_drhd_unit *drhd) { struct intel_iommu *iommu; - u32 ver; + u32 ver, sts; static int iommu_allocated = 0; int agaw = 0; int msagaw = 0; @@ -695,6 +695,15 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) (unsigned long long)iommu->cap, (unsigned long long)iommu->ecap); + /* Reflect status in gcmd */ + sts = readl(iommu->reg + DMAR_GSTS_REG); + if (sts & DMA_GSTS_IRES) + iommu->gcmd |= DMA_GCMD_IRE; + if (sts & DMA_GSTS_TES) + iommu->gcmd |= DMA_GCMD_TE; + if (sts & DMA_GSTS_QIES) + iommu->gcmd |= DMA_GCMD_QIE; + raw_spin_lock_init(&iommu->register_lock); drhd->iommu = iommu; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 0099667a397e..414d2d20dd59 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3665,6 +3665,7 @@ static struct notifier_block device_nb = { int __init intel_iommu_init(void) { int ret = 0; + struct dmar_drhd_unit *drhd; /* VT-d is required for a TXT/tboot launch, so enforce that */ force_on = tboot_force_iommu(); @@ -3675,6 +3676,20 @@ int __init intel_iommu_init(void) return -ENODEV; } + /* + * Disable translation if already enabled prior to OS handover. + */ + for_each_drhd_unit(drhd) { + struct intel_iommu *iommu; + + if (drhd->ignored) + continue; + + iommu = drhd->iommu; + if (iommu->gcmd & DMA_GCMD_TE) + iommu_disable_translation(iommu); + } + if (dmar_dev_scope_init() < 0) { if (force_on) panic("tboot: Failed to initialize DMAR device scope\n"); -- cgit From 61e015ac5b4d46c2054a78d9bc82c840274929a0 Mon Sep 17 00:00:00 2001 From: Varun Sethi Date: Tue, 23 Apr 2013 10:05:24 +0530 Subject: iommu: Move swap_pci_ref function to drivers/iommu/pci.h. The swap_pci_ref function is used by the IOMMU API code for swapping pci device pointers, while determining the iommu group for the device. Currently this function was being implemented for different IOMMU drivers. This patch moves the function to a new file, drivers/iommu/pci.h so that the implementation can be shared across various IOMMU drivers. Signed-off-by: Varun Sethi Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 7 +------ drivers/iommu/intel-iommu.c | 7 +------ drivers/iommu/pci.h | 29 +++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 drivers/iommu/pci.h (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index b287ca33833d..685f2821333a 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -46,6 +46,7 @@ #include "amd_iommu_proto.h" #include "amd_iommu_types.h" #include "irq_remapping.h" +#include "pci.h" #define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28)) @@ -263,12 +264,6 @@ static bool check_device(struct device *dev) return true; } -static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to) -{ - pci_dev_put(*from); - *from = to; -} - static struct pci_bus *find_hosted_bus(struct pci_bus *bus) { while (!bus->self) { diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 0099667a397e..88329fa13634 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -47,6 +47,7 @@ #include #include "irq_remapping.h" +#include "pci.h" #define ROOT_SIZE VTD_PAGE_SIZE #define CONTEXT_SIZE VTD_PAGE_SIZE @@ -4137,12 +4138,6 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } -static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to) -{ - pci_dev_put(*from); - *from = to; -} - #define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) static int intel_iommu_add_device(struct device *dev) diff --git a/drivers/iommu/pci.h b/drivers/iommu/pci.h new file mode 100644 index 000000000000..352d80ae7443 --- /dev/null +++ b/drivers/iommu/pci.h @@ -0,0 +1,29 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (C) 2013 Red Hat, Inc. + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + */ +#ifndef __IOMMU_PCI_H +#define __IOMMU_PCI_H + +/* Helper function for swapping pci device reference */ +static inline void swap_pci_ref(struct pci_dev **from, struct pci_dev *to) +{ + pci_dev_put(*from); + *from = to; +} + +#endif /* __IOMMU_PCI_H */ -- cgit From ae3e7f3aba4441eca7a1ff5cb3e089824318ba79 Mon Sep 17 00:00:00 2001 From: Linn Crosetto Date: Tue, 23 Apr 2013 12:26:45 -0600 Subject: iommu/vt-d: Remove warning for HPET scope type ACPI_DMAR_SCOPE_TYPE_HPET is parsed by ir_parse_ioapic_hpet_scope() and should not be flagged as an unsupported type. Signed-off-by: Linn Crosetto Signed-off-by: Joerg Roedel --- drivers/iommu/dmar.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index f7890edb22f5..a7967ceb79e6 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -129,7 +129,8 @@ int __init dmar_parse_dev_scope(void *start, void *end, int *cnt, if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT || scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) (*cnt)++; - else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC) { + else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC && + scope->entry_type != ACPI_DMAR_SCOPE_TYPE_HPET) { pr_warn("Unsupported device scope\n"); } start += scope->length; -- cgit From aa16bea929aed6ea854b55d2be8306a9fb40e694 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Mon, 25 Mar 2013 10:23:49 +1100 Subject: iommu: Add a function to find an iommu group by id As IOMMU groups are exposed to the user space by their numbers, the user space can use them in various kernel APIs so the kernel might need an API to find a group by its ID. As an example, QEMU VFIO on PPC64 platform needs it to associate a logical bus number (LIOBN) with a specific IOMMU group in order to support in-kernel handling of DMA map/unmap requests. The patch adds the iommu_group_get_by_id(id) function which performs such search. v2: fixed reference counting. Signed-off-by: Alexey Kardashevskiy Acked-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index b972d430d92b..db01af01ce0a 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -204,6 +204,35 @@ again: } EXPORT_SYMBOL_GPL(iommu_group_alloc); +struct iommu_group *iommu_group_get_by_id(int id) +{ + struct kobject *group_kobj; + struct iommu_group *group; + const char *name; + + if (!iommu_group_kset) + return NULL; + + name = kasprintf(GFP_KERNEL, "%d", id); + if (!name) + return NULL; + + group_kobj = kset_find_obj(iommu_group_kset, name); + kfree(name); + + if (!group_kobj) + return NULL; + + group = container_of(group_kobj, struct iommu_group, kobj); + BUG_ON(group->id != id); + + kobject_get(group->devices_kobj); + kobject_put(&group->kobj); + + return group; +} +EXPORT_SYMBOL_GPL(iommu_group_get_by_id); + /** * iommu_group_get_iommudata - retrieve iommu_data registered for a group * @group: the group -- cgit From 72ca55dbae815745403364ad915d912e92da5b9a Mon Sep 17 00:00:00 2001 From: Varun Sethi Date: Thu, 11 Apr 2013 09:19:44 +0530 Subject: iommu/tegra: Fix printk formats for dma_addr_t Fix printk formats for dma_addr_t: drivers/iommu/tegra-smmu.c: In function 'smmu_iommu_iova_to_phys': >> drivers/iommu/tegra-smmu.c:774:2: warning: format '%lx' expects argument of type 'long unsigned int', but argument 4 has type 'dma_addr_t' [-Wformat] -- drivers/iommu/tegra-gart.c: In function 'gart_iommu_iova_to_phys': >> drivers/iommu/tegra-gart.c:298:3: warning: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'dma_addr_t' [-Wformat] Signed-off-by: Varun Sethi Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 3 ++- drivers/iommu/tegra-smmu.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 86437575f94d..c04727402113 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -295,7 +295,8 @@ static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, pa = (pte & GART_PAGE_MASK); if (!pfn_valid(__phys_to_pfn(pa))) { - dev_err(gart->dev, "No entry for %08lx:%08x\n", iova, pa); + dev_err(gart->dev, "No entry for %08llx:%08x\n", + (unsigned long long)iova, pa); gart_dump_table(gart); return -EINVAL; } diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index b34e5fd7fd9e..1fc359a9189a 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -772,7 +772,8 @@ static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain, pfn = *pte & SMMU_PFN_MASK; WARN_ON(!pfn_valid(pfn)); dev_dbg(as->smmu->dev, - "iova:%08lx pfn:%08lx asid:%d\n", iova, pfn, as->asid); + "iova:%08llx pfn:%08lx asid:%d\n", (unsigned long long)iova, + pfn, as->asid); spin_unlock_irqrestore(&as->lock, flags); return PFN_PHYS(pfn); -- cgit