Browse Source

Merge git://git.infradead.org/users/dwmw2/iommu-2.6.32

* git://git.infradead.org/users/dwmw2/iommu-2.6.32:
  intel-iommu: Support PCIe hot-plug
  intel-iommu: Obey coherent_dma_mask for alloc_coherent on passthrough
  intel-iommu: Check for 'DMAR at zero' BIOS error earlier.
Linus Torvalds 15 years ago
parent
commit
a9366e61b0
2 changed files with 77 additions and 11 deletions
  1. 39 10
      drivers/pci/dmar.c
  2. 38 1
      drivers/pci/intel-iommu.c

+ 39 - 10
drivers/pci/dmar.c

@@ -175,15 +175,6 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
 	int ret = 0;
 	int ret = 0;
 
 
 	drhd = (struct acpi_dmar_hardware_unit *)header;
 	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);
 	dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL);
 	if (!dmaru)
 	if (!dmaru)
 		return -ENOMEM;
 		return -ENOMEM;
@@ -591,12 +582,50 @@ int __init dmar_table_init(void)
 	return 0;
 	return 0;
 }
 }
 
 
+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) {
+			drhd = (void *)entry_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 0;
+			}
+			break;
+		}
+
+		entry_header = ((void *)entry_header + entry_header->length);
+	}
+	return 1;
+}
+
 void __init detect_intel_iommu(void)
 void __init detect_intel_iommu(void)
 {
 {
 	int ret;
 	int ret;
 
 
 	ret = dmar_table_detect();
 	ret = dmar_table_detect();
-
+	if (ret)
+		ret = check_zero_address();
 	{
 	{
 #ifdef CONFIG_INTR_REMAP
 #ifdef CONFIG_INTR_REMAP
 		struct acpi_table_dmar *dmar;
 		struct acpi_table_dmar *dmar;

+ 38 - 1
drivers/pci/intel-iommu.c

@@ -2767,7 +2767,15 @@ static void *intel_alloc_coherent(struct device *hwdev, size_t size,
 
 
 	size = PAGE_ALIGN(size);
 	size = PAGE_ALIGN(size);
 	order = get_order(size);
 	order = get_order(size);
-	flags &= ~(GFP_DMA | GFP_DMA32);
+
+	if (!iommu_no_mapping(hwdev))
+		flags &= ~(GFP_DMA | GFP_DMA32);
+	else if (hwdev->coherent_dma_mask < dma_get_required_mask(hwdev)) {
+		if (hwdev->coherent_dma_mask < DMA_BIT_MASK(32))
+			flags |= GFP_DMA;
+		else
+			flags |= GFP_DMA32;
+	}
 
 
 	vaddr = (void *)__get_free_pages(flags, order);
 	vaddr = (void *)__get_free_pages(flags, order);
 	if (!vaddr)
 	if (!vaddr)
@@ -3207,6 +3215,33 @@ static int __init init_iommu_sysfs(void)
 }
 }
 #endif	/* CONFIG_PM */
 #endif	/* CONFIG_PM */
 
 
+/*
+ * Here we only respond to action of unbound device from driver.
+ *
+ * Added device is not attached to its DMAR domain here yet. That will happen
+ * when mapping the device to iova.
+ */
+static int device_notifier(struct notifier_block *nb,
+				  unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct dmar_domain *domain;
+
+	domain = find_domain(pdev);
+	if (!domain)
+		return 0;
+
+	if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through)
+		domain_remove_one_dev_info(domain, pdev);
+
+	return 0;
+}
+
+static struct notifier_block device_nb = {
+	.notifier_call = device_notifier,
+};
+
 int __init intel_iommu_init(void)
 int __init intel_iommu_init(void)
 {
 {
 	int ret = 0;
 	int ret = 0;
@@ -3259,6 +3294,8 @@ int __init intel_iommu_init(void)
 
 
 	register_iommu(&intel_iommu_ops);
 	register_iommu(&intel_iommu_ops);
 
 
+	bus_register_notifier(&pci_bus_type, &device_nb);
+
 	return 0;
 	return 0;
 }
 }