summaryrefslogtreecommitdiffstats
path: root/drivers/iommu/intel_irq_remapping.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iommu/intel_irq_remapping.c')
-rw-r--r--drivers/iommu/intel_irq_remapping.c60
1 files changed, 44 insertions, 16 deletions
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index 9b174893f0f5..0df41f6264f5 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -70,6 +70,11 @@ static int get_irte(int irq, struct irte *entry)
raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
+ if (unlikely(!irq_iommu->iommu)) {
+ raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+ return -1;
+ }
+
index = irq_iommu->irte_index + irq_iommu->sub_handle;
*entry = *(irq_iommu->iommu->ir_table->base + index);
@@ -369,29 +374,52 @@ static int set_hpet_sid(struct irte *irte, u8 id)
return 0;
}
+struct set_msi_sid_data {
+ struct pci_dev *pdev;
+ u16 alias;
+};
+
+static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque)
+{
+ struct set_msi_sid_data *data = opaque;
+
+ data->pdev = pdev;
+ data->alias = alias;
+
+ return 0;
+}
+
static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
{
- struct pci_dev *bridge;
+ struct set_msi_sid_data data;
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;
- }
+ pci_for_each_dma_alias(dev, set_msi_sid_cb, &data);
- 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);
- }
+ /*
+ * DMA alias provides us with a PCI device and alias. The only case
+ * where the it will return an alias on a different bus than the
+ * device is the case of a PCIe-to-PCI bridge, where the alias is for
+ * the subordinate bus. In this case we can only verify the bus.
+ *
+ * If the alias device is on a different bus than our source device
+ * then we have a topology based alias, use it.
+ *
+ * Otherwise, the alias is for a device DMA quirk and we cannot
+ * assume that MSI uses the same requester ID. Therefore use the
+ * original device.
+ */
+ if (PCI_BUS_NUM(data.alias) != data.pdev->bus->number)
+ set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
+ PCI_DEVID(PCI_BUS_NUM(data.alias),
+ dev->bus->number));
+ else if (data.pdev->bus->number != dev->bus->number)
+ set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, data.alias);
+ else
+ set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
+ PCI_DEVID(dev->bus->number, dev->devfn));
return 0;
}