mfd: Add TPS6507x support
[safe/jmp/linux-2.6] / drivers / pci / dmar.c
index 0e98f6b..0a19708 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/interrupt.h>
 #include <linux/tboot.h>
 #include <linux/dmi.h>
+#include <linux/slab.h>
 
 #define PREFIX "DMAR: "
 
@@ -130,9 +131,10 @@ static int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
                if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
                    scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE)
                        (*cnt)++;
-               else
+               else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC) {
                        printk(KERN_WARNING PREFIX
-                               "Unsupported device scope\n");
+                              "Unsupported device scope\n");
+               }
                start += scope->length;
        }
        if (*cnt == 0)
@@ -175,15 +177,6 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
        int ret = 0;
 
        drhd = (struct acpi_dmar_hardware_unit *)header;
-       if (!drhd->address) {
-               /* Promote an attitude of violence to a BIOS engineer today */
-               WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n"
-                    "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
-                    dmi_get_system_info(DMI_BIOS_VENDOR),
-                    dmi_get_system_info(DMI_BIOS_VERSION),
-                    dmi_get_system_info(DMI_PRODUCT_VERSION));
-               return -ENODEV;
-       }
        dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL);
        if (!dmaru)
                return -ENOMEM;
@@ -317,6 +310,8 @@ int dmar_find_matched_atsr_unit(struct pci_dev *dev)
        struct acpi_dmar_atsr *atsr;
        struct dmar_atsr_unit *atsru;
 
+       dev = pci_physfn(dev);
+
        list_for_each_entry(atsru, &dmar_atsr_units, list) {
                atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
                if (atsr->segment == pci_domain_nr(dev->bus))
@@ -348,6 +343,37 @@ found:
 }
 #endif
 
+#ifdef CONFIG_ACPI_NUMA
+static int __init
+dmar_parse_one_rhsa(struct acpi_dmar_header *header)
+{
+       struct acpi_dmar_rhsa *rhsa;
+       struct dmar_drhd_unit *drhd;
+
+       rhsa = (struct acpi_dmar_rhsa *)header;
+       for_each_drhd_unit(drhd) {
+               if (drhd->reg_base_addr == rhsa->base_address) {
+                       int node = acpi_map_pxm_to_node(rhsa->proximity_domain);
+
+                       if (!node_online(node))
+                               node = -1;
+                       drhd->iommu->node = node;
+                       return 0;
+               }
+       }
+       WARN_TAINT(
+               1, TAINT_FIRMWARE_WORKAROUND,
+               "Your BIOS is broken; RHSA refers to non-existent DMAR unit at %llx\n"
+               "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
+               drhd->reg_base_addr,
+               dmi_get_system_info(DMI_BIOS_VENDOR),
+               dmi_get_system_info(DMI_BIOS_VERSION),
+               dmi_get_system_info(DMI_PRODUCT_VERSION));
+
+       return 0;
+}
+#endif
+
 static void __init
 dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
 {
@@ -467,7 +493,9 @@ parse_dmar_table(void)
 #endif
                        break;
                case ACPI_DMAR_HARDWARE_AFFINITY:
-                       /* We don't do anything with RHSA (yet?) */
+#ifdef CONFIG_ACPI_NUMA
+                       ret = dmar_parse_one_rhsa(entry_header);
+#endif
                        break;
                default:
                        printk(KERN_WARNING PREFIX
@@ -484,7 +512,7 @@ parse_dmar_table(void)
        return ret;
 }
 
-int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
+static int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
                          struct pci_dev *dev)
 {
        int index;
@@ -507,6 +535,8 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev)
        struct dmar_drhd_unit *dmaru = NULL;
        struct acpi_dmar_hardware_unit *drhd;
 
+       dev = pci_physfn(dev);
+
        list_for_each_entry(dmaru, &dmar_drhd_units, list) {
                drhd = container_of(dmaru->hdr,
                                    struct acpi_dmar_hardware_unit,
@@ -591,12 +621,79 @@ int __init dmar_table_init(void)
        return 0;
 }
 
+static void warn_invalid_dmar(u64 addr, const char *message)
+{
+       WARN_TAINT_ONCE(
+               1, TAINT_FIRMWARE_WORKAROUND,
+               "Your BIOS is broken; DMAR reported at address %llx%s!\n"
+               "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
+               addr, message,
+               dmi_get_system_info(DMI_BIOS_VENDOR),
+               dmi_get_system_info(DMI_BIOS_VERSION),
+               dmi_get_system_info(DMI_PRODUCT_VERSION));
+}
+
+int __init check_zero_address(void)
+{
+       struct acpi_table_dmar *dmar;
+       struct acpi_dmar_header *entry_header;
+       struct acpi_dmar_hardware_unit *drhd;
+
+       dmar = (struct acpi_table_dmar *)dmar_tbl;
+       entry_header = (struct acpi_dmar_header *)(dmar + 1);
+
+       while (((unsigned long)entry_header) <
+                       (((unsigned long)dmar) + dmar_tbl->length)) {
+               /* Avoid looping forever on bad ACPI tables */
+               if (entry_header->length == 0) {
+                       printk(KERN_WARNING PREFIX
+                               "Invalid 0-length structure\n");
+                       return 0;
+               }
+
+               if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) {
+                       void __iomem *addr;
+                       u64 cap, ecap;
+
+                       drhd = (void *)entry_header;
+                       if (!drhd->address) {
+                               warn_invalid_dmar(0, "");
+                               goto failed;
+                       }
+
+                       addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
+                       if (!addr ) {
+                               printk("IOMMU: can't validate: %llx\n", drhd->address);
+                               goto failed;
+                       }
+                       cap = dmar_readq(addr + DMAR_CAP_REG);
+                       ecap = dmar_readq(addr + DMAR_ECAP_REG);
+                       early_iounmap(addr, VTD_PAGE_SIZE);
+                       if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) {
+                               warn_invalid_dmar(drhd->address,
+                                                 " returns all ones");
+                               goto failed;
+                       }
+               }
+
+               entry_header = ((void *)entry_header + entry_header->length);
+       }
+       return 1;
+
+failed:
+#ifdef CONFIG_DMAR
+       dmar_disabled = 1;
+#endif
+       return 0;
+}
+
 void __init detect_intel_iommu(void)
 {
        int ret;
 
        ret = dmar_table_detect();
-
+       if (ret)
+               ret = check_zero_address();
        {
 #ifdef CONFIG_INTR_REMAP
                struct acpi_table_dmar *dmar;
@@ -613,13 +710,16 @@ void __init detect_intel_iommu(void)
                               "x2apic and Intr-remapping.\n");
 #endif
 #ifdef CONFIG_DMAR
-               if (ret && !no_iommu && !iommu_detected && !swiotlb &&
-                   !dmar_disabled) {
+               if (ret && !no_iommu && !iommu_detected && !dmar_disabled) {
                        iommu_detected = 1;
                        /* Make sure ACS will be enabled */
                        pci_request_acs();
                }
 #endif
+#ifdef CONFIG_X86
+               if (ret)
+                       x86_init.iommu.iommu_init = intel_iommu_init;
+#endif
        }
        early_acpi_os_unmap_memory(dmar_tbl, dmar_tbl_size);
        dmar_tbl = NULL;
@@ -635,6 +735,11 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
        int agaw = 0;
        int msagaw = 0;
 
+       if (!drhd->reg_base_addr) {
+               warn_invalid_dmar(0, "");
+               return -EINVAL;
+       }
+
        iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
        if (!iommu)
                return -ENOMEM;
@@ -651,13 +756,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
        iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
 
        if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) {
-               /* Promote an attitude of violence to a BIOS engineer today */
-               WARN(1, "Your BIOS is broken; DMAR reported at address %llx returns all ones!\n"
-                    "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
-                    drhd->reg_base_addr,
-                    dmi_get_system_info(DMI_BIOS_VENDOR),
-                    dmi_get_system_info(DMI_BIOS_VERSION),
-                    dmi_get_system_info(DMI_PRODUCT_VERSION));
+               warn_invalid_dmar(drhd->reg_base_addr, " returns all ones");
                goto err_unmap;
        }
 
@@ -680,6 +779,8 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
        iommu->agaw = agaw;
        iommu->msagaw = msagaw;
 
+       iommu->node = -1;
+
        /* the registers might be more than one page */
        map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap),
                cap_max_fault_reg_offset(iommu->cap));
@@ -694,7 +795,8 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
        }
 
        ver = readl(iommu->reg + DMAR_VER_REG);
-       pr_info("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n",
+       pr_info("IOMMU %d: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n",
+               iommu->seq_id,
                (unsigned long long)drhd->reg_base_addr,
                DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
                (unsigned long long)iommu->cap,
@@ -1021,6 +1123,7 @@ static void __dmar_enable_qi(struct intel_iommu *iommu)
 int dmar_enable_qi(struct intel_iommu *iommu)
 {
        struct q_inval *qi;
+       struct page *desc_page;
 
        if (!ecap_qis(iommu->ecap))
                return -ENOENT;
@@ -1037,13 +1140,16 @@ int dmar_enable_qi(struct intel_iommu *iommu)
 
        qi = iommu->qi;
 
-       qi->desc = (void *)(get_zeroed_page(GFP_ATOMIC));
-       if (!qi->desc) {
+
+       desc_page = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO, 0);
+       if (!desc_page) {
                kfree(qi);
                iommu->qi = 0;
                return -ENOMEM;
        }
 
+       qi->desc = page_address(desc_page);
+
        qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_ATOMIC);
        if (!qi->desc_status) {
                free_page((unsigned long) qi->desc);
@@ -1341,9 +1447,11 @@ int dmar_reenable_qi(struct intel_iommu *iommu)
 /*
  * Check interrupt remapping support in DMAR table description.
  */
-int dmar_ir_support(void)
+int __init dmar_ir_support(void)
 {
        struct acpi_table_dmar *dmar;
        dmar = (struct acpi_table_dmar *)dmar_tbl;
+       if (!dmar)
+               return 0;
        return dmar->flags & 0x1;
 }