diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 0a19708..f183ac9 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -532,7 +532,7 @@ static int dmar_pci_device_match(struct pci_dev *devices[], int cnt, struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev) { - struct dmar_drhd_unit *dmaru = NULL; + struct dmar_drhd_unit *dmaru, *found = NULL; struct acpi_dmar_hardware_unit *drhd; dev = pci_physfn(dev); @@ -544,14 +544,38 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev) if (dmaru->include_all && drhd->segment == pci_domain_nr(dev->bus)) - return dmaru; - - if (dmar_pci_device_match(dmaru->devices, + found = dmaru; + else if (dmar_pci_device_match(dmaru->devices, dmaru->devices_cnt, dev)) - return dmaru; + found = dmaru; + + + if (found) + break; + } + + /* We know that this device only exists on this chipset, has its + * own IOMMU, and is uniquely identified by bit 54 being set in + * its capability mask. Catch BIOSes that specify the incorrect + * IOMMU unit. + */ + if (found && + dev->vendor == PCI_VENDOR_ID_INTEL && + dev->device == PCI_DEVICE_ID_INTEL_IOAT_SNB && + !test_bit(54, (unsigned long *) &found->iommu->cap)) { + struct intel_iommu *iommu = found->iommu; + + WARN_TAINT_ONCE(1, TAINT_FIRMWARE_WORKAROUND, + "BIOS wrongly assigned I/OAT IOMMU " + "%d: reg_base_addr %llx cap %llx ecap %llx\n", + iommu->seq_id, + (unsigned long long)found->reg_base_addr, + (unsigned long long)iommu->cap, + (unsigned long long)iommu->ecap); + found = NULL; } - return NULL; + return found; } int __init dmar_dev_scope_init(void)