iommu/amd: Check if IOAPIC information is correct
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / iommu / amd_iommu_init.c
index 073ed5d..f8a222b 100644 (file)
@@ -33,6 +33,7 @@
 #include <asm/gart.h>
 #include <asm/x86_init.h>
 #include <asm/iommu_table.h>
+#include <asm/io_apic.h>
 
 #include "amd_iommu_proto.h"
 #include "amd_iommu_types.h"
@@ -184,6 +185,12 @@ u16 *amd_iommu_alias_table;
 struct amd_iommu **amd_iommu_rlookup_table;
 
 /*
+ * This table is used to find the irq remapping table for a given device id
+ * quickly.
+ */
+struct irq_remap_table **irq_lookup_table;
+
+/*
  * AMD IOMMU allows up to 2^16 differend protection domains. This is a bitmap
  * to know which ones are already in use.
  */
@@ -1532,9 +1539,13 @@ static struct syscore_ops amd_iommu_syscore_ops = {
 
 static void __init free_on_init_error(void)
 {
+       free_pages((unsigned long)irq_lookup_table,
+                  get_order(rlookup_table_size));
+
        if (amd_iommu_irq_cache) {
                kmem_cache_destroy(amd_iommu_irq_cache);
                amd_iommu_irq_cache = NULL;
+
        }
 
        amd_iommu_uninit_devices();
@@ -1565,6 +1576,23 @@ static void __init free_on_init_error(void)
 #endif
 }
 
+static bool __init check_ioapic_information(void)
+{
+       int idx;
+
+       for (idx = 0; idx < nr_ioapics; idx++) {
+               int id = mpc_ioapic_id(idx);
+
+               if (get_ioapic_devid(id) < 0) {
+                       pr_err(FW_BUG "AMD-Vi: IO-APIC[%d] not in IVRS table\n", id);
+                       pr_err("AMD-Vi: Disabling interrupt remapping due to BIOS Bug\n");
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 /*
  * This is the hardware init function for AMD IOMMU in the system.
  * This function is called either from amd_iommu_init or from the interrupt
@@ -1651,9 +1679,6 @@ static int __init early_amd_iommu_init(void)
        if (amd_iommu_pd_alloc_bitmap == NULL)
                goto out;
 
-       /* init the device table */
-       init_device_table();
-
        /*
         * let all alias entries point to itself
         */
@@ -1676,6 +1701,9 @@ static int __init early_amd_iommu_init(void)
        if (ret)
                goto out;
 
+       if (amd_iommu_irq_remap)
+               amd_iommu_irq_remap = check_ioapic_information();
+
        if (amd_iommu_irq_remap) {
                /*
                 * Interrupt remapping enabled, create kmem_cache for the
@@ -1687,12 +1715,21 @@ static int __init early_amd_iommu_init(void)
                                0, NULL);
                if (!amd_iommu_irq_cache)
                        goto out;
+
+               irq_lookup_table = (void *)__get_free_pages(
+                               GFP_KERNEL | __GFP_ZERO,
+                               get_order(rlookup_table_size));
+               if (!irq_lookup_table)
+                       goto out;
        }
 
        ret = init_memory_definitions(ivrs_base);
        if (ret)
                goto out;
 
+       /* init the device table */
+       init_device_table();
+
 out:
        /* Don't leak any ACPI memory */
        early_acpi_os_unmap_memory((char __iomem *)ivrs_base, ivrs_size);