iommu/amd: Allocate memory below 4G for dev table if translation pre-enabled
authorBaoquan He <bhe@redhat.com>
Wed, 9 Aug 2017 08:33:42 +0000 (16:33 +0800)
committerJoerg Roedel <jroedel@suse.de>
Tue, 15 Aug 2017 16:14:40 +0000 (18:14 +0200)
AMD pointed out it's unsafe to update the device-table while iommu
is enabled. It turns out that device-table pointer update is split
up into two 32bit writes in the IOMMU hardware. So updating it while
the IOMMU is enabled could have some nasty side effects.

The safe way to work around this is to always allocate the device-table
below 4G, including the old device-table in normal kernel and the
device-table used for copying the content of the old device-table in kdump
kernel. Meanwhile we need check if the address of old device-table is
above 4G because it might has been touched accidentally in corrupted
1st kernel.

Signed-off-by: Baoquan He <bhe@redhat.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
drivers/iommu/amd_iommu_init.c

index d08ad74..c348732 100644 (file)
@@ -885,11 +885,15 @@ static bool copy_device_table(void)
        }
 
        old_devtb_phys = entry & PAGE_MASK;
+       if (old_devtb_phys >= 0x100000000ULL) {
+               pr_err("The address of old device table is above 4G, not trustworthy!/n");
+               return false;
+       }
        old_devtb = memremap(old_devtb_phys, dev_table_size, MEMREMAP_WB);
        if (!old_devtb)
                return false;
 
-       gfp_flag = GFP_KERNEL | __GFP_ZERO;
+       gfp_flag = GFP_KERNEL | __GFP_ZERO | GFP_DMA32;
        old_dev_tbl_cpy = (void *)__get_free_pages(gfp_flag,
                                get_order(dev_table_size));
        if (old_dev_tbl_cpy == NULL) {
@@ -2432,7 +2436,8 @@ static int __init early_amd_iommu_init(void)
 
        /* Device table - directly used by all IOMMUs */
        ret = -ENOMEM;
-       amd_iommu_dev_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+       amd_iommu_dev_table = (void *)__get_free_pages(
+                                     GFP_KERNEL | __GFP_ZERO | GFP_DMA32,
                                      get_order(dev_table_size));
        if (amd_iommu_dev_table == NULL)
                goto out;