Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c2a0b538 authored by Jiang Liu's avatar Jiang Liu Committed by Joerg Roedel
Browse files

iommu/vt-d: Introduce helper function dmar_walk_resources()



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: default avatarJiang Liu <jiang.liu@linux.intel.com>
Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 1a2262f9
Loading
Loading
Loading
Loading
+111 −98
Original line number Diff line number Diff line
@@ -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
@@ -350,7 +358,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;
@@ -381,6 +389,10 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
		return ret;
	}
	dmar_register_drhd_unit(dmaru);

	if (arg)
		(*(int *)arg)++;

	return 0;
}

@@ -393,7 +405,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;

@@ -415,7 +428,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;
@@ -442,6 +455,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
@@ -503,6 +518,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
 */
@@ -510,9 +571,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
@@ -536,51 +606,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;
}

@@ -778,65 +807,49 @@ 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;

	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;
	drhd = (void *)entry;
	if (!drhd->address) {
		warn_invalid_dmar(0, "");
				goto failed;
		return -EINVAL;
	}

	addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
	if (!addr) {
				printk("IOMMU: can't validate: %llx\n", drhd->address);
				goto failed;
		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);
			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);
	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();
	{
		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 */
@@ -847,7 +860,7 @@ int __init detect_intel_iommu(void)
	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);
+2 −2
Original line number Diff line number Diff line
@@ -3684,7 +3684,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;
@@ -3710,7 +3710,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;
+9 −10
Original line number Diff line number Diff line
@@ -121,22 +121,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;