iommu/amd: Add function copy_dev_tables()
[linux-2.6-microblaze.git] / drivers / iommu / amd_iommu_init.c
index 3723037..e285720 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/export.h>
 #include <linux/iommu.h>
 #include <linux/kmemleak.h>
-#include <linux/crash_dump.h>
 #include <asm/pci-direct.h>
 #include <asm/iommu.h>
 #include <asm/gart.h>
@@ -196,6 +195,11 @@ spinlock_t amd_iommu_pd_lock;
  * page table root pointer.
  */
 struct dev_table_entry *amd_iommu_dev_table;
+/*
+ * Pointer to a device table which the content of old device table
+ * will be copied to. It's only be used in kdump kernel.
+ */
+static struct dev_table_entry *old_dev_tbl_cpy;
 
 /*
  * The alias table is a driver specific data structure which contains the
@@ -258,6 +262,25 @@ static int amd_iommu_enable_interrupts(void);
 static int __init iommu_go_to_state(enum iommu_init_state state);
 static void init_device_table_dma(void);
 
+bool translation_pre_enabled(struct amd_iommu *iommu)
+{
+       return (iommu->flags & AMD_IOMMU_FLAG_TRANS_PRE_ENABLED);
+}
+
+static void clear_translation_pre_enabled(struct amd_iommu *iommu)
+{
+       iommu->flags &= ~AMD_IOMMU_FLAG_TRANS_PRE_ENABLED;
+}
+
+static void init_translation_status(struct amd_iommu *iommu)
+{
+       u32 ctrl;
+
+       ctrl = readl(iommu->mmio_base + MMIO_CONTROL_OFFSET);
+       if (ctrl & (1<<CONTROL_IOMMU_EN))
+               iommu->flags |= AMD_IOMMU_FLAG_TRANS_PRE_ENABLED;
+}
+
 static inline void update_last_devid(u16 devid)
 {
        if (devid > amd_iommu_last_bdf)
@@ -615,6 +638,14 @@ static void iommu_enable_command_buffer(struct amd_iommu *iommu)
        amd_iommu_reset_cmd_buffer(iommu);
 }
 
+/*
+ * This function disables the command buffer
+ */
+static void iommu_disable_command_buffer(struct amd_iommu *iommu)
+{
+       iommu_feature_disable(iommu, CONTROL_CMDBUF_EN);
+}
+
 static void __init free_command_buffer(struct amd_iommu *iommu)
 {
        free_pages((unsigned long)iommu->cmd_buf, get_order(CMD_BUFFER_SIZE));
@@ -647,6 +678,14 @@ static void iommu_enable_event_buffer(struct amd_iommu *iommu)
        iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN);
 }
 
+/*
+ * This function disables the event log buffer
+ */
+static void iommu_disable_event_buffer(struct amd_iommu *iommu)
+{
+       iommu_feature_disable(iommu, CONTROL_EVT_LOG_EN);
+}
+
 static void __init free_event_buffer(struct amd_iommu *iommu)
 {
        free_pages((unsigned long)iommu->evt_buf, get_order(EVT_BUFFER_SIZE));
@@ -808,6 +847,63 @@ static int get_dev_entry_bit(u16 devid, u8 bit)
 }
 
 
+static bool copy_device_table(void)
+{
+       struct dev_table_entry *old_devtb = NULL;
+       u32 lo, hi, devid, old_devtb_size;
+       phys_addr_t old_devtb_phys;
+       u64 entry, last_entry = 0;
+       struct amd_iommu *iommu;
+       u16 dom_id, dte_v;
+       gfp_t gfp_flag;
+
+
+       pr_warn("Translation is already enabled - trying to copy translation structures\n");
+       for_each_iommu(iommu) {
+               /* All IOMMUs should use the same device table with the same size */
+               lo = readl(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET);
+               hi = readl(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET + 4);
+               entry = (((u64) hi) << 32) + lo;
+               if (last_entry && last_entry != entry) {
+                       pr_err("IOMMU:%d should use the same dev table as others!/n",
+                               iommu->index);
+                       return false;
+               }
+               last_entry = entry;
+
+               old_devtb_size = ((entry & ~PAGE_MASK) + 1) << 12;
+               if (old_devtb_size != dev_table_size) {
+                       pr_err("The device table size of IOMMU:%d is not expected!/n",
+                               iommu->index);
+                       return false;
+               }
+       }
+
+       old_devtb_phys = entry & PAGE_MASK;
+       old_devtb = memremap(old_devtb_phys, dev_table_size, MEMREMAP_WB);
+       if (!old_devtb)
+               return false;
+
+       gfp_flag = GFP_KERNEL | __GFP_ZERO;
+       old_dev_tbl_cpy = (void *)__get_free_pages(gfp_flag,
+                               get_order(dev_table_size));
+       if (old_dev_tbl_cpy == NULL) {
+               pr_err("Failed to allocate memory for copying old device table!/n");
+               return false;
+       }
+
+       for (devid = 0; devid <= amd_iommu_last_bdf; ++devid) {
+               old_dev_tbl_cpy[devid] = old_devtb[devid];
+               dom_id = old_devtb[devid].data[1] & DEV_DOMID_MASK;
+               dte_v = old_devtb[devid].data[0] & DTE_FLAG_V;
+               if (dte_v && dom_id)
+                       __set_bit(dom_id, amd_iommu_pd_alloc_bitmap);
+       }
+       memunmap(old_devtb);
+
+       return true;
+}
+
 void amd_iommu_apply_erratum_63(u16 devid)
 {
        int sysmgt;
@@ -1399,6 +1495,11 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
 
        iommu->int_enabled = false;
 
+       init_translation_status(iommu);
+
+       if (translation_pre_enabled(iommu))
+               pr_warn("Translation is already enabled - trying to copy translation structures\n");
+
        ret = init_iommu_from_acpi(iommu, h);
        if (ret)
                return ret;
@@ -1902,14 +2003,6 @@ static void init_device_table_dma(void)
        for (devid = 0; devid <= amd_iommu_last_bdf; ++devid) {
                set_dev_entry_bit(devid, DEV_ENTRY_VALID);
                set_dev_entry_bit(devid, DEV_ENTRY_TRANSLATION);
-               /*
-                * In kdump kernels in-flight DMA from the old kernel might
-                * cause IO_PAGE_FAULTs. There are no reports that a kdump
-                * actually failed because of that, so just disable fault
-                * reporting in the hardware to get rid of the messages
-                */
-               if (is_kdump_kernel())
-                       set_dev_entry_bit(devid, DEV_ENTRY_NO_PAGE_FAULT);
        }
 }
 
@@ -2022,6 +2115,19 @@ static void iommu_enable_ga(struct amd_iommu *iommu)
 #endif
 }
 
+static void early_enable_iommu(struct amd_iommu *iommu)
+{
+       iommu_disable(iommu);
+       iommu_init_flags(iommu);
+       iommu_set_device_table(iommu);
+       iommu_enable_command_buffer(iommu);
+       iommu_enable_event_buffer(iommu);
+       iommu_set_exclusion_range(iommu);
+       iommu_enable_ga(iommu);
+       iommu_enable(iommu);
+       iommu_flush_all_caches(iommu);
+}
+
 /*
  * This function finally enables all IOMMUs found in the system after
  * they have been initialized
@@ -2030,17 +2136,8 @@ static void early_enable_iommus(void)
 {
        struct amd_iommu *iommu;
 
-       for_each_iommu(iommu) {
-               iommu_disable(iommu);
-               iommu_init_flags(iommu);
-               iommu_set_device_table(iommu);
-               iommu_enable_command_buffer(iommu);
-               iommu_enable_event_buffer(iommu);
-               iommu_set_exclusion_range(iommu);
-               iommu_enable_ga(iommu);
-               iommu_enable(iommu);
-               iommu_flush_all_caches(iommu);
-       }
+       for_each_iommu(iommu)
+               early_enable_iommu(iommu);
 
 #ifdef CONFIG_IRQ_REMAP
        if (AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir))