]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
x86/amd-iommu: Fix boot crash with hidden PCI devices
authorJoerg Roedel <joerg.roedel@amd.com>
Mon, 6 Jun 2011 14:50:14 +0000 (16:50 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 23 Jun 2011 22:05:36 +0000 (15:05 -0700)
commit 26018874e3584f1658570d41d57d4c34f6a53aa0 upstream.

Some PCIe cards ship with a PCI-PCIe bridge which is not
visible as a PCI device in Linux. But the device-id of the
bridge is present in the IOMMU tables which causes a boot
crash in the IOMMU driver.
This patch fixes by removing these cards from the IOMMU
handling. This is a pure -stable fix, a real fix to handle
this situation appriatly will follow for the next merge
window.

Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
arch/x86/kernel/amd_iommu.c

index 297b1f5c323651016f05afff701ca09183f21768..e7115c818ba9e24ee4589e7450b46564aeaf4897 100644 (file)
@@ -154,6 +154,10 @@ static int iommu_init_device(struct device *dev)
        pdev = pci_get_bus_and_slot(PCI_BUS(alias), alias & 0xff);
        if (pdev)
                dev_data->alias = &pdev->dev;
+       else {
+               kfree(dev_data);
+               return -ENOTSUPP;
+       }
 
        atomic_set(&dev_data->bind, 0);
 
@@ -163,6 +167,20 @@ static int iommu_init_device(struct device *dev)
        return 0;
 }
 
+static void iommu_ignore_device(struct device *dev)
+{
+       u16 devid, alias;
+
+       devid = get_device_id(dev);
+       alias = amd_iommu_alias_table[devid];
+
+       memset(&amd_iommu_dev_table[devid], 0, sizeof(struct dev_table_entry));
+       memset(&amd_iommu_dev_table[alias], 0, sizeof(struct dev_table_entry));
+
+       amd_iommu_rlookup_table[devid] = NULL;
+       amd_iommu_rlookup_table[alias] = NULL;
+}
+
 static void iommu_uninit_device(struct device *dev)
 {
        kfree(dev->archdata.iommu);
@@ -192,7 +210,9 @@ int __init amd_iommu_init_devices(void)
                        continue;
 
                ret = iommu_init_device(&pdev->dev);
-               if (ret)
+               if (ret == -ENOTSUPP)
+                       iommu_ignore_device(&pdev->dev);
+               else if (ret)
                        goto out_free;
        }