diff mbox

[Part3,V6,1/8] iommu/vt-d: Introduce helper function dmar_walk_resources()

Message ID 1411103935-31301-2-git-send-email-jiang.liu@linux.intel.com (mailing list archive)
State Superseded
Headers show

Commit Message

Jiang Liu Sept. 19, 2014, 5:18 a.m. UTC
Introduce helper function dmar_walk_resources to walk resource entries
in DMAR table and ACPI buffer object returned by ACPI _DSM method
for IOMMU hot-plug.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 drivers/iommu/dmar.c        |  209 +++++++++++++++++++++++--------------------
 drivers/iommu/intel-iommu.c |    4 +-
 include/linux/dmar.h        |   19 ++--
 3 files changed, 122 insertions(+), 110 deletions(-)

Comments

Yijing Wang Sept. 19, 2014, 6:49 a.m. UTC | #1
On 2014/9/19 13:18, Jiang Liu wrote:
> Introduce helper function dmar_walk_resources to walk resource entries
> in DMAR table and ACPI buffer object returned by ACPI _DSM method
> for IOMMU hot-plug.
> 
> Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>

Reviewed-by: Yijing Wang <wangyijing@huawei.com>

> ---
>  drivers/iommu/dmar.c        |  209 +++++++++++++++++++++++--------------------
>  drivers/iommu/intel-iommu.c |    4 +-
>  include/linux/dmar.h        |   19 ++--
>  3 files changed, 122 insertions(+), 110 deletions(-)
> 
> diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
> index 06d268abe951..a05cf3634efe 100644
> --- a/drivers/iommu/dmar.c
> +++ b/drivers/iommu/dmar.c
> @@ -44,6 +44,14 @@
>  
>  #include "irq_remapping.h"
>  
> +typedef int (*dmar_res_handler_t)(struct acpi_dmar_header *, void *);
> +struct dmar_res_callback {
> +	dmar_res_handler_t	cb[ACPI_DMAR_TYPE_RESERVED];
> +	void			*arg[ACPI_DMAR_TYPE_RESERVED];
> +	bool			ignore_unhandled;
> +	bool			print_entry;
> +};
> +
>  /*
>   * Assumptions:
>   * 1) The hotplug framework guarentees that DMAR unit will be hot-added
> @@ -333,7 +341,7 @@ static struct notifier_block dmar_pci_bus_nb = {
>   * present in the platform
>   */
>  static int __init
> -dmar_parse_one_drhd(struct acpi_dmar_header *header)
> +dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg)
>  {
>  	struct acpi_dmar_hardware_unit *drhd;
>  	struct dmar_drhd_unit *dmaru;
> @@ -364,6 +372,10 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
>  		return ret;
>  	}
>  	dmar_register_drhd_unit(dmaru);
> +
> +	if (arg)
> +		(*(int *)arg)++;
> +
>  	return 0;
>  }
>  
> @@ -376,7 +388,8 @@ static void dmar_free_drhd(struct dmar_drhd_unit *dmaru)
>  	kfree(dmaru);
>  }
>  
> -static int __init dmar_parse_one_andd(struct acpi_dmar_header *header)
> +static int __init dmar_parse_one_andd(struct acpi_dmar_header *header,
> +				      void *arg)
>  {
>  	struct acpi_dmar_andd *andd = (void *)header;
>  
> @@ -398,7 +411,7 @@ static int __init dmar_parse_one_andd(struct acpi_dmar_header *header)
>  
>  #ifdef CONFIG_ACPI_NUMA
>  static int __init
> -dmar_parse_one_rhsa(struct acpi_dmar_header *header)
> +dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg)
>  {
>  	struct acpi_dmar_rhsa *rhsa;
>  	struct dmar_drhd_unit *drhd;
> @@ -425,6 +438,8 @@ dmar_parse_one_rhsa(struct acpi_dmar_header *header)
>  
>  	return 0;
>  }
> +#else
> +#define	dmar_parse_one_rhsa		dmar_res_noop
>  #endif
>  
>  static void __init
> @@ -486,6 +501,52 @@ static int __init dmar_table_detect(void)
>  	return (ACPI_SUCCESS(status) ? 1 : 0);
>  }
>  
> +static int dmar_walk_remapping_entries(struct acpi_dmar_header *start,
> +				       size_t len, struct dmar_res_callback *cb)
> +{
> +	int ret = 0;
> +	struct acpi_dmar_header *iter, *next;
> +	struct acpi_dmar_header *end = ((void *)start) + len;
> +
> +	for (iter = start; iter < end && ret == 0; iter = next) {
> +		next = (void *)iter + iter->length;
> +		if (iter->length == 0) {
> +			/* Avoid looping forever on bad ACPI tables */
> +			pr_debug(FW_BUG "Invalid 0-length structure\n");
> +			break;
> +		} else if (next > end) {
> +			/* Avoid passing table end */
> +			pr_warn(FW_BUG "record passes table end\n");
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		if (cb->print_entry)
> +			dmar_table_print_dmar_entry(iter);
> +
> +		if (iter->type >= ACPI_DMAR_TYPE_RESERVED) {
> +			/* continue for forward compatibility */
> +			pr_debug("Unknown DMAR structure type %d\n",
> +				 iter->type);
> +		} else if (cb->cb[iter->type]) {
> +			ret = cb->cb[iter->type](iter, cb->arg[iter->type]);
> +		} else if (!cb->ignore_unhandled) {
> +			pr_warn("No handler for DMAR structure type %d\n",
> +				iter->type);
> +			ret = -EINVAL;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static inline int dmar_walk_dmar_table(struct acpi_table_dmar *dmar,
> +				       struct dmar_res_callback *cb)
> +{
> +	return dmar_walk_remapping_entries((void *)(dmar + 1),
> +			dmar->header.length - sizeof(*dmar), cb);
> +}
> +
>  /**
>   * parse_dmar_table - parses the DMA reporting table
>   */
> @@ -493,9 +554,18 @@ static int __init
>  parse_dmar_table(void)
>  {
>  	struct acpi_table_dmar *dmar;
> -	struct acpi_dmar_header *entry_header;
>  	int ret = 0;
>  	int drhd_count = 0;
> +	struct dmar_res_callback cb = {
> +		.print_entry = true,
> +		.ignore_unhandled = true,
> +		.arg[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &drhd_count,
> +		.cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_parse_one_drhd,
> +		.cb[ACPI_DMAR_TYPE_RESERVED_MEMORY] = &dmar_parse_one_rmrr,
> +		.cb[ACPI_DMAR_TYPE_ROOT_ATS] = &dmar_parse_one_atsr,
> +		.cb[ACPI_DMAR_TYPE_HARDWARE_AFFINITY] = &dmar_parse_one_rhsa,
> +		.cb[ACPI_DMAR_TYPE_NAMESPACE] = &dmar_parse_one_andd,
> +	};
>  
>  	/*
>  	 * Do it again, earlier dmar_tbl mapping could be mapped with
> @@ -519,51 +589,10 @@ parse_dmar_table(void)
>  	}
>  
>  	pr_info("Host address width %d\n", dmar->width + 1);
> -
> -	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) {
> -			pr_warn("Invalid 0-length structure\n");
> -			ret = -EINVAL;
> -			break;
> -		}
> -
> -		dmar_table_print_dmar_entry(entry_header);
> -
> -		switch (entry_header->type) {
> -		case ACPI_DMAR_TYPE_HARDWARE_UNIT:
> -			drhd_count++;
> -			ret = dmar_parse_one_drhd(entry_header);
> -			break;
> -		case ACPI_DMAR_TYPE_RESERVED_MEMORY:
> -			ret = dmar_parse_one_rmrr(entry_header);
> -			break;
> -		case ACPI_DMAR_TYPE_ROOT_ATS:
> -			ret = dmar_parse_one_atsr(entry_header);
> -			break;
> -		case ACPI_DMAR_TYPE_HARDWARE_AFFINITY:
> -#ifdef CONFIG_ACPI_NUMA
> -			ret = dmar_parse_one_rhsa(entry_header);
> -#endif
> -			break;
> -		case ACPI_DMAR_TYPE_NAMESPACE:
> -			ret = dmar_parse_one_andd(entry_header);
> -			break;
> -		default:
> -			pr_warn("Unknown DMAR structure type %d\n",
> -				entry_header->type);
> -			ret = 0; /* for forward compatibility */
> -			break;
> -		}
> -		if (ret)
> -			break;
> -
> -		entry_header = ((void *)entry_header + entry_header->length);
> -	}
> -	if (drhd_count == 0)
> +	ret = dmar_walk_dmar_table(dmar, &cb);
> +	if (ret == 0 && drhd_count == 0)
>  		pr_warn(FW_BUG "No DRHD structure found in DMAR table\n");
> +
>  	return ret;
>  }
>  
> @@ -761,76 +790,60 @@ static void warn_invalid_dmar(u64 addr, const char *message)
>  		dmi_get_system_info(DMI_PRODUCT_VERSION));
>  }
>  
> -static int __init check_zero_address(void)
> +static int __ref
> +dmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg)
>  {
> -	struct acpi_table_dmar *dmar;
> -	struct acpi_dmar_header *entry_header;
>  	struct acpi_dmar_hardware_unit *drhd;
> +	void __iomem *addr;
> +	u64 cap, ecap;
>  
> -	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) {
> -			pr_warn("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;
> -			}
> +	drhd = (void *)entry;
> +	if (!drhd->address) {
> +		warn_invalid_dmar(0, "");
> +		return -EINVAL;
> +	}
>  
> -			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;
> -			}
> -		}
> +	addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
> +	if (!addr) {
> +		pr_warn("IOMMU: can't validate: %llx\n", drhd->address);
> +		return -EINVAL;
> +	}
> +	cap = dmar_readq(addr + DMAR_CAP_REG);
> +	ecap = dmar_readq(addr + DMAR_ECAP_REG);
> +	early_iounmap(addr, VTD_PAGE_SIZE);
>  
> -		entry_header = ((void *)entry_header + entry_header->length);
> +	if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) {
> +		warn_invalid_dmar(drhd->address, " returns all ones");
> +		return -EINVAL;
>  	}
> -	return 1;
>  
> -failed:
>  	return 0;
>  }
>  
>  int __init detect_intel_iommu(void)
>  {
>  	int ret;
> +	struct dmar_res_callback validate_drhd_cb = {
> +		.cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_validate_one_drhd,
> +		.ignore_unhandled = true,
> +	};
>  
>  	down_write(&dmar_global_lock);
>  	ret = dmar_table_detect();
>  	if (ret)
> -		ret = check_zero_address();
> -	{
> -		if (ret && !no_iommu && !iommu_detected && !dmar_disabled) {
> -			iommu_detected = 1;
> -			/* Make sure ACS will be enabled */
> -			pci_request_acs();
> -		}
> +		ret = !dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl,
> +					    &validate_drhd_cb);
> +	if (ret && !no_iommu && !iommu_detected && !dmar_disabled) {
> +		iommu_detected = 1;
> +		/* Make sure ACS will be enabled */
> +		pci_request_acs();
> +	}
>  
>  #ifdef CONFIG_X86
> -		if (ret)
> -			x86_init.iommu.iommu_init = intel_iommu_init;
> +	if (ret)
> +		x86_init.iommu.iommu_init = intel_iommu_init;
>  #endif
> -	}
> +
>  	early_acpi_os_unmap_memory((void __iomem *)dmar_tbl, dmar_tbl_size);
>  	dmar_tbl = NULL;
>  	up_write(&dmar_global_lock);
> diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
> index 5619f264862d..4af2206e41bc 100644
> --- a/drivers/iommu/intel-iommu.c
> +++ b/drivers/iommu/intel-iommu.c
> @@ -3682,7 +3682,7 @@ static inline void init_iommu_pm_ops(void) {}
>  #endif	/* CONFIG_PM */
>  
>  
> -int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
> +int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg)
>  {
>  	struct acpi_dmar_reserved_memory *rmrr;
>  	struct dmar_rmrr_unit *rmrru;
> @@ -3708,7 +3708,7 @@ int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
>  	return 0;
>  }
>  
> -int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
> +int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr, void *arg)
>  {
>  	struct acpi_dmar_atsr *atsr;
>  	struct dmar_atsr_unit *atsru;
> diff --git a/include/linux/dmar.h b/include/linux/dmar.h
> index 1deece46a0ca..fac8ca34f9a8 100644
> --- a/include/linux/dmar.h
> +++ b/include/linux/dmar.h
> @@ -115,22 +115,21 @@ extern int dmar_remove_dev_scope(struct dmar_pci_notify_info *info,
>  extern int detect_intel_iommu(void);
>  extern int enable_drhd_fault_handling(void);
>  
> +static inline int dmar_res_noop(struct acpi_dmar_header *hdr, void *arg)
> +{
> +	return 0;
> +}
> +
>  #ifdef CONFIG_INTEL_IOMMU
>  extern int iommu_detected, no_iommu;
>  extern int intel_iommu_init(void);
> -extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header);
> -extern int dmar_parse_one_atsr(struct acpi_dmar_header *header);
> +extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg);
> +extern int dmar_parse_one_atsr(struct acpi_dmar_header *header, void *arg);
>  extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info);
>  #else /* !CONFIG_INTEL_IOMMU: */
>  static inline int intel_iommu_init(void) { return -ENODEV; }
> -static inline int dmar_parse_one_rmrr(struct acpi_dmar_header *header)
> -{
> -	return 0;
> -}
> -static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header)
> -{
> -	return 0;
> -}
> +#define	dmar_parse_one_rmrr		dmar_res_noop
> +#define	dmar_parse_one_atsr		dmar_res_noop
>  static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
>  {
>  	return 0;
>
diff mbox

Patch

diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 06d268abe951..a05cf3634efe 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -44,6 +44,14 @@ 
 
 #include "irq_remapping.h"
 
+typedef int (*dmar_res_handler_t)(struct acpi_dmar_header *, void *);
+struct dmar_res_callback {
+	dmar_res_handler_t	cb[ACPI_DMAR_TYPE_RESERVED];
+	void			*arg[ACPI_DMAR_TYPE_RESERVED];
+	bool			ignore_unhandled;
+	bool			print_entry;
+};
+
 /*
  * Assumptions:
  * 1) The hotplug framework guarentees that DMAR unit will be hot-added
@@ -333,7 +341,7 @@  static struct notifier_block dmar_pci_bus_nb = {
  * present in the platform
  */
 static int __init
-dmar_parse_one_drhd(struct acpi_dmar_header *header)
+dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg)
 {
 	struct acpi_dmar_hardware_unit *drhd;
 	struct dmar_drhd_unit *dmaru;
@@ -364,6 +372,10 @@  dmar_parse_one_drhd(struct acpi_dmar_header *header)
 		return ret;
 	}
 	dmar_register_drhd_unit(dmaru);
+
+	if (arg)
+		(*(int *)arg)++;
+
 	return 0;
 }
 
@@ -376,7 +388,8 @@  static void dmar_free_drhd(struct dmar_drhd_unit *dmaru)
 	kfree(dmaru);
 }
 
-static int __init dmar_parse_one_andd(struct acpi_dmar_header *header)
+static int __init dmar_parse_one_andd(struct acpi_dmar_header *header,
+				      void *arg)
 {
 	struct acpi_dmar_andd *andd = (void *)header;
 
@@ -398,7 +411,7 @@  static int __init dmar_parse_one_andd(struct acpi_dmar_header *header)
 
 #ifdef CONFIG_ACPI_NUMA
 static int __init
-dmar_parse_one_rhsa(struct acpi_dmar_header *header)
+dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg)
 {
 	struct acpi_dmar_rhsa *rhsa;
 	struct dmar_drhd_unit *drhd;
@@ -425,6 +438,8 @@  dmar_parse_one_rhsa(struct acpi_dmar_header *header)
 
 	return 0;
 }
+#else
+#define	dmar_parse_one_rhsa		dmar_res_noop
 #endif
 
 static void __init
@@ -486,6 +501,52 @@  static int __init dmar_table_detect(void)
 	return (ACPI_SUCCESS(status) ? 1 : 0);
 }
 
+static int dmar_walk_remapping_entries(struct acpi_dmar_header *start,
+				       size_t len, struct dmar_res_callback *cb)
+{
+	int ret = 0;
+	struct acpi_dmar_header *iter, *next;
+	struct acpi_dmar_header *end = ((void *)start) + len;
+
+	for (iter = start; iter < end && ret == 0; iter = next) {
+		next = (void *)iter + iter->length;
+		if (iter->length == 0) {
+			/* Avoid looping forever on bad ACPI tables */
+			pr_debug(FW_BUG "Invalid 0-length structure\n");
+			break;
+		} else if (next > end) {
+			/* Avoid passing table end */
+			pr_warn(FW_BUG "record passes table end\n");
+			ret = -EINVAL;
+			break;
+		}
+
+		if (cb->print_entry)
+			dmar_table_print_dmar_entry(iter);
+
+		if (iter->type >= ACPI_DMAR_TYPE_RESERVED) {
+			/* continue for forward compatibility */
+			pr_debug("Unknown DMAR structure type %d\n",
+				 iter->type);
+		} else if (cb->cb[iter->type]) {
+			ret = cb->cb[iter->type](iter, cb->arg[iter->type]);
+		} else if (!cb->ignore_unhandled) {
+			pr_warn("No handler for DMAR structure type %d\n",
+				iter->type);
+			ret = -EINVAL;
+		}
+	}
+
+	return ret;
+}
+
+static inline int dmar_walk_dmar_table(struct acpi_table_dmar *dmar,
+				       struct dmar_res_callback *cb)
+{
+	return dmar_walk_remapping_entries((void *)(dmar + 1),
+			dmar->header.length - sizeof(*dmar), cb);
+}
+
 /**
  * parse_dmar_table - parses the DMA reporting table
  */
@@ -493,9 +554,18 @@  static int __init
 parse_dmar_table(void)
 {
 	struct acpi_table_dmar *dmar;
-	struct acpi_dmar_header *entry_header;
 	int ret = 0;
 	int drhd_count = 0;
+	struct dmar_res_callback cb = {
+		.print_entry = true,
+		.ignore_unhandled = true,
+		.arg[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &drhd_count,
+		.cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_parse_one_drhd,
+		.cb[ACPI_DMAR_TYPE_RESERVED_MEMORY] = &dmar_parse_one_rmrr,
+		.cb[ACPI_DMAR_TYPE_ROOT_ATS] = &dmar_parse_one_atsr,
+		.cb[ACPI_DMAR_TYPE_HARDWARE_AFFINITY] = &dmar_parse_one_rhsa,
+		.cb[ACPI_DMAR_TYPE_NAMESPACE] = &dmar_parse_one_andd,
+	};
 
 	/*
 	 * Do it again, earlier dmar_tbl mapping could be mapped with
@@ -519,51 +589,10 @@  parse_dmar_table(void)
 	}
 
 	pr_info("Host address width %d\n", dmar->width + 1);
-
-	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) {
-			pr_warn("Invalid 0-length structure\n");
-			ret = -EINVAL;
-			break;
-		}
-
-		dmar_table_print_dmar_entry(entry_header);
-
-		switch (entry_header->type) {
-		case ACPI_DMAR_TYPE_HARDWARE_UNIT:
-			drhd_count++;
-			ret = dmar_parse_one_drhd(entry_header);
-			break;
-		case ACPI_DMAR_TYPE_RESERVED_MEMORY:
-			ret = dmar_parse_one_rmrr(entry_header);
-			break;
-		case ACPI_DMAR_TYPE_ROOT_ATS:
-			ret = dmar_parse_one_atsr(entry_header);
-			break;
-		case ACPI_DMAR_TYPE_HARDWARE_AFFINITY:
-#ifdef CONFIG_ACPI_NUMA
-			ret = dmar_parse_one_rhsa(entry_header);
-#endif
-			break;
-		case ACPI_DMAR_TYPE_NAMESPACE:
-			ret = dmar_parse_one_andd(entry_header);
-			break;
-		default:
-			pr_warn("Unknown DMAR structure type %d\n",
-				entry_header->type);
-			ret = 0; /* for forward compatibility */
-			break;
-		}
-		if (ret)
-			break;
-
-		entry_header = ((void *)entry_header + entry_header->length);
-	}
-	if (drhd_count == 0)
+	ret = dmar_walk_dmar_table(dmar, &cb);
+	if (ret == 0 && drhd_count == 0)
 		pr_warn(FW_BUG "No DRHD structure found in DMAR table\n");
+
 	return ret;
 }
 
@@ -761,76 +790,60 @@  static void warn_invalid_dmar(u64 addr, const char *message)
 		dmi_get_system_info(DMI_PRODUCT_VERSION));
 }
 
-static int __init check_zero_address(void)
+static int __ref
+dmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg)
 {
-	struct acpi_table_dmar *dmar;
-	struct acpi_dmar_header *entry_header;
 	struct acpi_dmar_hardware_unit *drhd;
+	void __iomem *addr;
+	u64 cap, ecap;
 
-	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) {
-			pr_warn("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;
-			}
+	drhd = (void *)entry;
+	if (!drhd->address) {
+		warn_invalid_dmar(0, "");
+		return -EINVAL;
+	}
 
-			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;
-			}
-		}
+	addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
+	if (!addr) {
+		pr_warn("IOMMU: can't validate: %llx\n", drhd->address);
+		return -EINVAL;
+	}
+	cap = dmar_readq(addr + DMAR_CAP_REG);
+	ecap = dmar_readq(addr + DMAR_ECAP_REG);
+	early_iounmap(addr, VTD_PAGE_SIZE);
 
-		entry_header = ((void *)entry_header + entry_header->length);
+	if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) {
+		warn_invalid_dmar(drhd->address, " returns all ones");
+		return -EINVAL;
 	}
-	return 1;
 
-failed:
 	return 0;
 }
 
 int __init detect_intel_iommu(void)
 {
 	int ret;
+	struct dmar_res_callback validate_drhd_cb = {
+		.cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_validate_one_drhd,
+		.ignore_unhandled = true,
+	};
 
 	down_write(&dmar_global_lock);
 	ret = dmar_table_detect();
 	if (ret)
-		ret = check_zero_address();
-	{
-		if (ret && !no_iommu && !iommu_detected && !dmar_disabled) {
-			iommu_detected = 1;
-			/* Make sure ACS will be enabled */
-			pci_request_acs();
-		}
+		ret = !dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl,
+					    &validate_drhd_cb);
+	if (ret && !no_iommu && !iommu_detected && !dmar_disabled) {
+		iommu_detected = 1;
+		/* Make sure ACS will be enabled */
+		pci_request_acs();
+	}
 
 #ifdef CONFIG_X86
-		if (ret)
-			x86_init.iommu.iommu_init = intel_iommu_init;
+	if (ret)
+		x86_init.iommu.iommu_init = intel_iommu_init;
 #endif
-	}
+
 	early_acpi_os_unmap_memory((void __iomem *)dmar_tbl, dmar_tbl_size);
 	dmar_tbl = NULL;
 	up_write(&dmar_global_lock);
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 5619f264862d..4af2206e41bc 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -3682,7 +3682,7 @@  static inline void init_iommu_pm_ops(void) {}
 #endif	/* CONFIG_PM */
 
 
-int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
+int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg)
 {
 	struct acpi_dmar_reserved_memory *rmrr;
 	struct dmar_rmrr_unit *rmrru;
@@ -3708,7 +3708,7 @@  int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
 	return 0;
 }
 
-int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
+int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr, void *arg)
 {
 	struct acpi_dmar_atsr *atsr;
 	struct dmar_atsr_unit *atsru;
diff --git a/include/linux/dmar.h b/include/linux/dmar.h
index 1deece46a0ca..fac8ca34f9a8 100644
--- a/include/linux/dmar.h
+++ b/include/linux/dmar.h
@@ -115,22 +115,21 @@  extern int dmar_remove_dev_scope(struct dmar_pci_notify_info *info,
 extern int detect_intel_iommu(void);
 extern int enable_drhd_fault_handling(void);
 
+static inline int dmar_res_noop(struct acpi_dmar_header *hdr, void *arg)
+{
+	return 0;
+}
+
 #ifdef CONFIG_INTEL_IOMMU
 extern int iommu_detected, no_iommu;
 extern int intel_iommu_init(void);
-extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header);
-extern int dmar_parse_one_atsr(struct acpi_dmar_header *header);
+extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg);
+extern int dmar_parse_one_atsr(struct acpi_dmar_header *header, void *arg);
 extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info);
 #else /* !CONFIG_INTEL_IOMMU: */
 static inline int intel_iommu_init(void) { return -ENODEV; }
-static inline int dmar_parse_one_rmrr(struct acpi_dmar_header *header)
-{
-	return 0;
-}
-static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header)
-{
-	return 0;
-}
+#define	dmar_parse_one_rmrr		dmar_res_noop
+#define	dmar_parse_one_atsr		dmar_res_noop
 static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
 {
 	return 0;