diff options
| -rw-r--r-- | drivers/pci/dmar.c | 49 | ||||
| -rw-r--r-- | drivers/pci/intel-iommu.c | 39 | 
2 files changed, 77 insertions, 11 deletions
| diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 22b02c6df854..e5f8fc164fd3 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -175,15 +175,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; @@ -591,12 +582,50 @@ int __init dmar_table_init(void)  	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)  {  	int ret;  	ret = dmar_table_detect(); - +	if (ret) +		ret = check_zero_address();  	{  #ifdef CONFIG_INTR_REMAP  		struct acpi_table_dmar *dmar; diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index b1e97e682500..1840a0578a42 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -2767,7 +2767,15 @@ static void *intel_alloc_coherent(struct device *hwdev, size_t size,  	size = PAGE_ALIGN(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);  	if (!vaddr) @@ -3207,6 +3215,33 @@ static int __init init_iommu_sysfs(void)  }  #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 ret = 0; @@ -3259,6 +3294,8 @@ int __init intel_iommu_init(void)  	register_iommu(&intel_iommu_ops); +	bus_register_notifier(&pci_bus_type, &device_nb); +  	return 0;  } | 
