PCI hotplug: fix wrong assumption in acpi_get_hp_hw_control_from_firmware
[safe/jmp/linux-2.6] / drivers / pci / dmar.c
index 0f409e2..26c536b 100644 (file)
@@ -188,31 +188,21 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
        return 0;
 }
 
-static int __init
-dmar_parse_dev(struct dmar_drhd_unit *dmaru)
+static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru)
 {
        struct acpi_dmar_hardware_unit *drhd;
-       static int include_all;
-       int ret;
+       int ret = 0;
 
        drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr;
 
-       if (!dmaru->include_all)
-               ret = dmar_parse_dev_scope((void *)(drhd + 1),
+       if (dmaru->include_all)
+               return 0;
+
+       ret = dmar_parse_dev_scope((void *)(drhd + 1),
                                ((void *)drhd) + drhd->header.length,
                                &dmaru->devices_cnt, &dmaru->devices,
                                drhd->segment);
-       else {
-               /* Only allow one INCLUDE_ALL */
-               if (include_all) {
-                       printk(KERN_WARNING PREFIX "Only one INCLUDE_ALL "
-                               "device scope is allowed\n");
-                       ret = -EINVAL;
-               }
-               include_all = 1;
-       }
-
-       if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all)) {
+       if (ret) {
                list_del(&dmaru->list);
                kfree(dmaru);
        }
@@ -277,18 +267,37 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
                drhd = (struct acpi_dmar_hardware_unit *)header;
                printk (KERN_INFO PREFIX
                        "DRHD (flags: 0x%08x)base: 0x%016Lx\n",
-                       drhd->flags, drhd->address);
+                       drhd->flags, (unsigned long long)drhd->address);
                break;
        case ACPI_DMAR_TYPE_RESERVED_MEMORY:
                rmrr = (struct acpi_dmar_reserved_memory *)header;
 
                printk (KERN_INFO PREFIX
                        "RMRR base: 0x%016Lx end: 0x%016Lx\n",
-                       rmrr->base_address, rmrr->end_address);
+                       (unsigned long long)rmrr->base_address,
+                       (unsigned long long)rmrr->end_address);
                break;
        }
 }
 
+/**
+ * dmar_table_detect - checks to see if the platform supports DMAR devices
+ */
+static int __init dmar_table_detect(void)
+{
+       acpi_status status = AE_OK;
+
+       /* if we could find DMAR table, then there are DMAR devices */
+       status = acpi_get_table(ACPI_SIG_DMAR, 0,
+                               (struct acpi_table_header **)&dmar_tbl);
+
+       if (ACPI_SUCCESS(status) && !dmar_tbl) {
+               printk (KERN_WARNING PREFIX "Unable to map DMAR\n");
+               status = AE_NOT_FOUND;
+       }
+
+       return (ACPI_SUCCESS(status) ? 1 : 0);
+}
 
 /**
  * parse_dmar_table - parses the DMA reporting table
@@ -300,11 +309,17 @@ parse_dmar_table(void)
        struct acpi_dmar_header *entry_header;
        int ret = 0;
 
+       /*
+        * Do it again, earlier dmar_tbl mapping could be mapped with
+        * fixed map.
+        */
+       dmar_table_detect();
+
        dmar = (struct acpi_table_dmar *)dmar_tbl;
        if (!dmar)
                return -ENODEV;
 
-       if (dmar->width < PAGE_SHIFT_4K - 1) {
+       if (dmar->width < PAGE_SHIFT - 1) {
                printk(KERN_WARNING PREFIX "Invalid DMAR haw\n");
                return -EINVAL;
        }
@@ -315,6 +330,14 @@ parse_dmar_table(void)
        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");
+                       ret = -EINVAL;
+                       break;
+               }
+
                dmar_table_print_dmar_entry(entry_header);
 
                switch (entry_header->type) {
@@ -360,12 +383,21 @@ int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
 struct dmar_drhd_unit *
 dmar_find_matched_drhd_unit(struct pci_dev *dev)
 {
-       struct dmar_drhd_unit *drhd = NULL;
+       struct dmar_drhd_unit *dmaru = NULL;
+       struct acpi_dmar_hardware_unit *drhd;
+
+       list_for_each_entry(dmaru, &dmar_drhd_units, list) {
+               drhd = container_of(dmaru->hdr,
+                                   struct acpi_dmar_hardware_unit,
+                                   header);
 
-       list_for_each_entry(drhd, &dmar_drhd_units, list) {
-               if (drhd->include_all || dmar_pci_device_match(drhd->devices,
-                                               drhd->devices_cnt, dev))
-                       return drhd;
+               if (dmaru->include_all &&
+                   drhd->segment == pci_domain_nr(dev->bus))
+                       return dmaru;
+
+               if (dmar_pci_device_match(dmaru->devices,
+                                         dmaru->devices_cnt, dev))
+                       return dmaru;
        }
 
        return NULL;
@@ -373,10 +405,10 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev)
 
 int __init dmar_dev_scope_init(void)
 {
-       struct dmar_drhd_unit *drhd;
+       struct dmar_drhd_unit *drhd, *drhd_n;
        int ret = -ENODEV;
 
-       for_each_drhd_unit(drhd) {
+       list_for_each_entry_safe(drhd, drhd_n, &dmar_drhd_units, list) {
                ret = dmar_parse_dev(drhd);
                if (ret)
                        return ret;
@@ -384,8 +416,8 @@ int __init dmar_dev_scope_init(void)
 
 #ifdef CONFIG_DMAR
        {
-               struct dmar_rmrr_unit *rmrr;
-               for_each_rmrr_units(rmrr) {
+               struct dmar_rmrr_unit *rmrr, *rmrr_n;
+               list_for_each_entry_safe(rmrr, rmrr_n, &dmar_rmrr_units, list) {
                        ret = rmrr_parse_dev(rmrr);
                        if (ret)
                                return ret;
@@ -430,33 +462,14 @@ int __init dmar_table_init(void)
        return 0;
 }
 
-/**
- * early_dmar_detect - checks to see if the platform supports DMAR devices
- */
-int __init early_dmar_detect(void)
-{
-       acpi_status status = AE_OK;
-
-       /* if we could find DMAR table, then there are DMAR devices */
-       status = acpi_get_table(ACPI_SIG_DMAR, 0,
-                               (struct acpi_table_header **)&dmar_tbl);
-
-       if (ACPI_SUCCESS(status) && !dmar_tbl) {
-               printk (KERN_WARNING PREFIX "Unable to map DMAR\n");
-               status = AE_NOT_FOUND;
-       }
-
-       return (ACPI_SUCCESS(status) ? 1 : 0);
-}
-
 void __init detect_intel_iommu(void)
 {
        int ret;
 
-       ret = early_dmar_detect();
+       ret = dmar_table_detect();
 
-#ifdef CONFIG_DMAR
        {
+#ifdef CONFIG_INTR_REMAP
                struct acpi_table_dmar *dmar;
                /*
                 * for now we will disable dma-remapping when interrupt
@@ -465,28 +478,18 @@ void __init detect_intel_iommu(void)
                 * is added, we will not need this any more.
                 */
                dmar = (struct acpi_table_dmar *) dmar_tbl;
-               if (ret && cpu_has_x2apic && dmar->flags & 0x1) {
+               if (ret && cpu_has_x2apic && dmar->flags & 0x1)
                        printk(KERN_INFO
                               "Queued invalidation will be enabled to support "
                               "x2apic and Intr-remapping.\n");
-                       printk(KERN_INFO
-                              "Disabling IOMMU detection, because of missing "
-                              "queued invalidation support for IOTLB "
-                              "invalidation\n");
-                       printk(KERN_INFO
-                              "Use \"nox2apic\", if you want to use Intel "
-                              " IOMMU for DMA-remapping and don't care about "
-                              " x2apic support\n");
-
-                       dmar_disabled = 1;
-                       return;
-               }
-
+#endif
+#ifdef CONFIG_DMAR
                if (ret && !no_iommu && !iommu_detected && !swiotlb &&
                    !dmar_disabled)
                        iommu_detected = 1;
-       }
 #endif
+       }
+       dmar_tbl = NULL;
 }
 
 
@@ -496,6 +499,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
        int map_size;
        u32 ver;
        static int iommu_allocated = 0;
+       int agaw = 0;
 
        iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
        if (!iommu)
@@ -503,7 +507,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
 
        iommu->seq_id = iommu_allocated++;
 
-       iommu->reg = ioremap(drhd->reg_base_addr, PAGE_SIZE_4K);
+       iommu->reg = ioremap(drhd->reg_base_addr, VTD_PAGE_SIZE);
        if (!iommu->reg) {
                printk(KERN_ERR "IOMMU: can't map the region\n");
                goto error;
@@ -511,11 +515,22 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
        iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG);
        iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
 
+#ifdef CONFIG_DMAR
+       agaw = iommu_calculate_agaw(iommu);
+       if (agaw < 0) {
+               printk(KERN_ERR
+                       "Cannot get a valid agaw for iommu (seq_id = %d)\n",
+                       iommu->seq_id);
+               goto error;
+       }
+#endif
+       iommu->agaw = agaw;
+
        /* 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));
-       map_size = PAGE_ALIGN_4K(map_size);
-       if (map_size > PAGE_SIZE_4K) {
+       map_size = VTD_PAGE_ALIGN(map_size);
+       if (map_size > VTD_PAGE_SIZE) {
                iounmap(iommu->reg);
                iommu->reg = ioremap(drhd->reg_base_addr, map_size);
                if (!iommu->reg) {
@@ -526,8 +541,10 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
 
        ver = readl(iommu->reg + DMAR_VER_REG);
        pr_debug("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n",
-               drhd->reg_base_addr, DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
-               iommu->cap, iommu->ecap);
+               (unsigned long long)drhd->reg_base_addr,
+               DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
+               (unsigned long long)iommu->cap,
+               (unsigned long long)iommu->ecap);
 
        spin_lock_init(&iommu->register_lock);
 
@@ -564,19 +581,49 @@ static inline void reclaim_free_desc(struct q_inval *qi)
        }
 }
 
+static int qi_check_fault(struct intel_iommu *iommu, int index)
+{
+       u32 fault;
+       int head;
+       struct q_inval *qi = iommu->qi;
+       int wait_index = (index + 1) % QI_LENGTH;
+
+       fault = readl(iommu->reg + DMAR_FSTS_REG);
+
+       /*
+        * If IQE happens, the head points to the descriptor associated
+        * with the error. No new descriptors are fetched until the IQE
+        * is cleared.
+        */
+       if (fault & DMA_FSTS_IQE) {
+               head = readl(iommu->reg + DMAR_IQH_REG);
+               if ((head >> 4) == index) {
+                       memcpy(&qi->desc[index], &qi->desc[wait_index],
+                                       sizeof(struct qi_desc));
+                       __iommu_flush_cache(iommu, &qi->desc[index],
+                                       sizeof(struct qi_desc));
+                       writel(DMA_FSTS_IQE, iommu->reg + DMAR_FSTS_REG);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * Submit the queued invalidation descriptor to the remapping
  * hardware unit and wait for its completion.
  */
-void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
+int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
 {
+       int rc = 0;
        struct q_inval *qi = iommu->qi;
        struct qi_desc *hw, wait_desc;
        int wait_index, index;
        unsigned long flags;
 
        if (!qi)
-               return;
+               return 0;
 
        hw = qi->desc;
 
@@ -594,7 +641,8 @@ void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
 
        hw[index] = *desc;
 
-       wait_desc.low = QI_IWD_STATUS_DATA(2) | QI_IWD_STATUS_WRITE | QI_IWD_TYPE;
+       wait_desc.low = QI_IWD_STATUS_DATA(QI_DONE) |
+                       QI_IWD_STATUS_WRITE | QI_IWD_TYPE;
        wait_desc.high = virt_to_phys(&qi->desc_status[wait_index]);
 
        hw[wait_index] = wait_desc;
@@ -605,13 +653,11 @@ void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
        qi->free_head = (qi->free_head + 2) % QI_LENGTH;
        qi->free_cnt -= 2;
 
-       spin_lock(&iommu->register_lock);
        /*
         * update the HW tail register indicating the presence of
         * new descriptors.
         */
        writel(qi->free_head << 4, iommu->reg + DMAR_IQT_REG);
-       spin_unlock(&iommu->register_lock);
 
        while (qi->desc_status[wait_index] != QI_DONE) {
                /*
@@ -621,15 +667,21 @@ void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
                 * a deadlock where the interrupt context can wait indefinitely
                 * for free slots in the queue.
                 */
+               rc = qi_check_fault(iommu, index);
+               if (rc)
+                       goto out;
+
                spin_unlock(&qi->q_lock);
                cpu_relax();
                spin_lock(&qi->q_lock);
        }
-
-       qi->desc_status[index] = QI_DONE;
+out:
+       qi->desc_status[index] = qi->desc_status[wait_index] = QI_DONE;
 
        reclaim_free_desc(qi);
        spin_unlock_irqrestore(&qi->q_lock, flags);
+
+       return rc;
 }
 
 /*
@@ -642,13 +694,13 @@ void qi_global_iec(struct intel_iommu *iommu)
        desc.low = QI_IEC_TYPE;
        desc.high = 0;
 
+       /* should never fail */
        qi_submit_sync(&desc, iommu);
 }
 
 int qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm,
                     u64 type, int non_present_entry_flush)
 {
-
        struct qi_desc desc;
 
        if (non_present_entry_flush) {
@@ -662,10 +714,7 @@ int qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm,
                        | QI_CC_GRAN(type) | QI_CC_TYPE;
        desc.high = 0;
 
-       qi_submit_sync(&desc, iommu);
-
-       return 0;
-
+       return qi_submit_sync(&desc, iommu);
 }
 
 int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr,
@@ -695,10 +744,7 @@ int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr,
        desc.high = QI_IOTLB_ADDR(addr) | QI_IOTLB_IH(ih)
                | QI_IOTLB_AM(size_order);
 
-       qi_submit_sync(&desc, iommu);
-
-       return 0;
-
+       return qi_submit_sync(&desc, iommu);
 }
 
 /*