162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2006, Intel Corporation.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006-2008 Intel Corporation
662306a36Sopenharmony_ci * Author: Ashok Raj <ashok.raj@intel.com>
762306a36Sopenharmony_ci * Author: Shaohua Li <shaohua.li@intel.com>
862306a36Sopenharmony_ci * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This file implements early detection/parsing of Remapping Devices
1162306a36Sopenharmony_ci * reported to OS through BIOS via DMA remapping reporting (DMAR) ACPI
1262306a36Sopenharmony_ci * tables.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * These routines are used by both DMA-remapping and Interrupt-remapping
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define pr_fmt(fmt)     "DMAR: " fmt
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/pci.h>
2062306a36Sopenharmony_ci#include <linux/dmar.h>
2162306a36Sopenharmony_ci#include <linux/iova.h>
2262306a36Sopenharmony_ci#include <linux/timer.h>
2362306a36Sopenharmony_ci#include <linux/irq.h>
2462306a36Sopenharmony_ci#include <linux/interrupt.h>
2562306a36Sopenharmony_ci#include <linux/tboot.h>
2662306a36Sopenharmony_ci#include <linux/dmi.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/iommu.h>
2962306a36Sopenharmony_ci#include <linux/numa.h>
3062306a36Sopenharmony_ci#include <linux/limits.h>
3162306a36Sopenharmony_ci#include <asm/irq_remapping.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "iommu.h"
3462306a36Sopenharmony_ci#include "../irq_remapping.h"
3562306a36Sopenharmony_ci#include "perf.h"
3662306a36Sopenharmony_ci#include "trace.h"
3762306a36Sopenharmony_ci#include "perfmon.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_citypedef int (*dmar_res_handler_t)(struct acpi_dmar_header *, void *);
4062306a36Sopenharmony_cistruct dmar_res_callback {
4162306a36Sopenharmony_ci	dmar_res_handler_t	cb[ACPI_DMAR_TYPE_RESERVED];
4262306a36Sopenharmony_ci	void			*arg[ACPI_DMAR_TYPE_RESERVED];
4362306a36Sopenharmony_ci	bool			ignore_unhandled;
4462306a36Sopenharmony_ci	bool			print_entry;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * Assumptions:
4962306a36Sopenharmony_ci * 1) The hotplug framework guarentees that DMAR unit will be hot-added
5062306a36Sopenharmony_ci *    before IO devices managed by that unit.
5162306a36Sopenharmony_ci * 2) The hotplug framework guarantees that DMAR unit will be hot-removed
5262306a36Sopenharmony_ci *    after IO devices managed by that unit.
5362306a36Sopenharmony_ci * 3) Hotplug events are rare.
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * Locking rules for DMA and interrupt remapping related global data structures:
5662306a36Sopenharmony_ci * 1) Use dmar_global_lock in process context
5762306a36Sopenharmony_ci * 2) Use RCU in interrupt context
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ciDECLARE_RWSEM(dmar_global_lock);
6062306a36Sopenharmony_ciLIST_HEAD(dmar_drhd_units);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct acpi_table_header * __initdata dmar_tbl;
6362306a36Sopenharmony_cistatic int dmar_dev_scope_status = 1;
6462306a36Sopenharmony_cistatic DEFINE_IDA(dmar_seq_ids);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int alloc_iommu(struct dmar_drhd_unit *drhd);
6762306a36Sopenharmony_cistatic void free_iommu(struct intel_iommu *iommu);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	/*
7262306a36Sopenharmony_ci	 * add INCLUDE_ALL at the tail, so scan the list will find it at
7362306a36Sopenharmony_ci	 * the very end.
7462306a36Sopenharmony_ci	 */
7562306a36Sopenharmony_ci	if (drhd->include_all)
7662306a36Sopenharmony_ci		list_add_tail_rcu(&drhd->list, &dmar_drhd_units);
7762306a36Sopenharmony_ci	else
7862306a36Sopenharmony_ci		list_add_rcu(&drhd->list, &dmar_drhd_units);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_civoid *dmar_alloc_dev_scope(void *start, void *end, int *cnt)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct acpi_dmar_device_scope *scope;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	*cnt = 0;
8662306a36Sopenharmony_ci	while (start < end) {
8762306a36Sopenharmony_ci		scope = start;
8862306a36Sopenharmony_ci		if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_NAMESPACE ||
8962306a36Sopenharmony_ci		    scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
9062306a36Sopenharmony_ci		    scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE)
9162306a36Sopenharmony_ci			(*cnt)++;
9262306a36Sopenharmony_ci		else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC &&
9362306a36Sopenharmony_ci			scope->entry_type != ACPI_DMAR_SCOPE_TYPE_HPET) {
9462306a36Sopenharmony_ci			pr_warn("Unsupported device scope\n");
9562306a36Sopenharmony_ci		}
9662306a36Sopenharmony_ci		start += scope->length;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci	if (*cnt == 0)
9962306a36Sopenharmony_ci		return NULL;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return kcalloc(*cnt, sizeof(struct dmar_dev_scope), GFP_KERNEL);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_civoid dmar_free_dev_scope(struct dmar_dev_scope **devices, int *cnt)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int i;
10762306a36Sopenharmony_ci	struct device *tmp_dev;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (*devices && *cnt) {
11062306a36Sopenharmony_ci		for_each_active_dev_scope(*devices, *cnt, i, tmp_dev)
11162306a36Sopenharmony_ci			put_device(tmp_dev);
11262306a36Sopenharmony_ci		kfree(*devices);
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	*devices = NULL;
11662306a36Sopenharmony_ci	*cnt = 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/* Optimize out kzalloc()/kfree() for normal cases */
12062306a36Sopenharmony_cistatic char dmar_pci_notify_info_buf[64];
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic struct dmar_pci_notify_info *
12362306a36Sopenharmony_cidmar_alloc_pci_notify_info(struct pci_dev *dev, unsigned long event)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	int level = 0;
12662306a36Sopenharmony_ci	size_t size;
12762306a36Sopenharmony_ci	struct pci_dev *tmp;
12862306a36Sopenharmony_ci	struct dmar_pci_notify_info *info;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/*
13162306a36Sopenharmony_ci	 * Ignore devices that have a domain number higher than what can
13262306a36Sopenharmony_ci	 * be looked up in DMAR, e.g. VMD subdevices with domain 0x10000
13362306a36Sopenharmony_ci	 */
13462306a36Sopenharmony_ci	if (pci_domain_nr(dev->bus) > U16_MAX)
13562306a36Sopenharmony_ci		return NULL;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Only generate path[] for device addition event */
13862306a36Sopenharmony_ci	if (event == BUS_NOTIFY_ADD_DEVICE)
13962306a36Sopenharmony_ci		for (tmp = dev; tmp; tmp = tmp->bus->self)
14062306a36Sopenharmony_ci			level++;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	size = struct_size(info, path, level);
14362306a36Sopenharmony_ci	if (size <= sizeof(dmar_pci_notify_info_buf)) {
14462306a36Sopenharmony_ci		info = (struct dmar_pci_notify_info *)dmar_pci_notify_info_buf;
14562306a36Sopenharmony_ci	} else {
14662306a36Sopenharmony_ci		info = kzalloc(size, GFP_KERNEL);
14762306a36Sopenharmony_ci		if (!info) {
14862306a36Sopenharmony_ci			if (dmar_dev_scope_status == 0)
14962306a36Sopenharmony_ci				dmar_dev_scope_status = -ENOMEM;
15062306a36Sopenharmony_ci			return NULL;
15162306a36Sopenharmony_ci		}
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	info->event = event;
15562306a36Sopenharmony_ci	info->dev = dev;
15662306a36Sopenharmony_ci	info->seg = pci_domain_nr(dev->bus);
15762306a36Sopenharmony_ci	info->level = level;
15862306a36Sopenharmony_ci	if (event == BUS_NOTIFY_ADD_DEVICE) {
15962306a36Sopenharmony_ci		for (tmp = dev; tmp; tmp = tmp->bus->self) {
16062306a36Sopenharmony_ci			level--;
16162306a36Sopenharmony_ci			info->path[level].bus = tmp->bus->number;
16262306a36Sopenharmony_ci			info->path[level].device = PCI_SLOT(tmp->devfn);
16362306a36Sopenharmony_ci			info->path[level].function = PCI_FUNC(tmp->devfn);
16462306a36Sopenharmony_ci			if (pci_is_root_bus(tmp->bus))
16562306a36Sopenharmony_ci				info->bus = tmp->bus->number;
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return info;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic inline void dmar_free_pci_notify_info(struct dmar_pci_notify_info *info)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	if ((void *)info != dmar_pci_notify_info_buf)
17562306a36Sopenharmony_ci		kfree(info);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic bool dmar_match_pci_path(struct dmar_pci_notify_info *info, int bus,
17962306a36Sopenharmony_ci				struct acpi_dmar_pci_path *path, int count)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	int i;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (info->bus != bus)
18462306a36Sopenharmony_ci		goto fallback;
18562306a36Sopenharmony_ci	if (info->level != count)
18662306a36Sopenharmony_ci		goto fallback;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
18962306a36Sopenharmony_ci		if (path[i].device != info->path[i].device ||
19062306a36Sopenharmony_ci		    path[i].function != info->path[i].function)
19162306a36Sopenharmony_ci			goto fallback;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return true;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cifallback:
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (count != 1)
19962306a36Sopenharmony_ci		return false;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	i = info->level - 1;
20262306a36Sopenharmony_ci	if (bus              == info->path[i].bus &&
20362306a36Sopenharmony_ci	    path[0].device   == info->path[i].device &&
20462306a36Sopenharmony_ci	    path[0].function == info->path[i].function) {
20562306a36Sopenharmony_ci		pr_info(FW_BUG "RMRR entry for device %02x:%02x.%x is broken - applying workaround\n",
20662306a36Sopenharmony_ci			bus, path[0].device, path[0].function);
20762306a36Sopenharmony_ci		return true;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return false;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/* Return: > 0 if match found, 0 if no match found, < 0 if error happens */
21462306a36Sopenharmony_ciint dmar_insert_dev_scope(struct dmar_pci_notify_info *info,
21562306a36Sopenharmony_ci			  void *start, void*end, u16 segment,
21662306a36Sopenharmony_ci			  struct dmar_dev_scope *devices,
21762306a36Sopenharmony_ci			  int devices_cnt)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	int i, level;
22062306a36Sopenharmony_ci	struct device *tmp, *dev = &info->dev->dev;
22162306a36Sopenharmony_ci	struct acpi_dmar_device_scope *scope;
22262306a36Sopenharmony_ci	struct acpi_dmar_pci_path *path;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (segment != info->seg)
22562306a36Sopenharmony_ci		return 0;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	for (; start < end; start += scope->length) {
22862306a36Sopenharmony_ci		scope = start;
22962306a36Sopenharmony_ci		if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_ENDPOINT &&
23062306a36Sopenharmony_ci		    scope->entry_type != ACPI_DMAR_SCOPE_TYPE_BRIDGE)
23162306a36Sopenharmony_ci			continue;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		path = (struct acpi_dmar_pci_path *)(scope + 1);
23462306a36Sopenharmony_ci		level = (scope->length - sizeof(*scope)) / sizeof(*path);
23562306a36Sopenharmony_ci		if (!dmar_match_pci_path(info, scope->bus, path, level))
23662306a36Sopenharmony_ci			continue;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		/*
23962306a36Sopenharmony_ci		 * We expect devices with endpoint scope to have normal PCI
24062306a36Sopenharmony_ci		 * headers, and devices with bridge scope to have bridge PCI
24162306a36Sopenharmony_ci		 * headers.  However PCI NTB devices may be listed in the
24262306a36Sopenharmony_ci		 * DMAR table with bridge scope, even though they have a
24362306a36Sopenharmony_ci		 * normal PCI header.  NTB devices are identified by class
24462306a36Sopenharmony_ci		 * "BRIDGE_OTHER" (0680h) - we don't declare a socpe mismatch
24562306a36Sopenharmony_ci		 * for this special case.
24662306a36Sopenharmony_ci		 */
24762306a36Sopenharmony_ci		if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT &&
24862306a36Sopenharmony_ci		     info->dev->hdr_type != PCI_HEADER_TYPE_NORMAL) ||
24962306a36Sopenharmony_ci		    (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE &&
25062306a36Sopenharmony_ci		     (info->dev->hdr_type == PCI_HEADER_TYPE_NORMAL &&
25162306a36Sopenharmony_ci		      info->dev->class >> 16 != PCI_BASE_CLASS_BRIDGE))) {
25262306a36Sopenharmony_ci			pr_warn("Device scope type does not match for %s\n",
25362306a36Sopenharmony_ci				pci_name(info->dev));
25462306a36Sopenharmony_ci			return -EINVAL;
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		for_each_dev_scope(devices, devices_cnt, i, tmp)
25862306a36Sopenharmony_ci			if (tmp == NULL) {
25962306a36Sopenharmony_ci				devices[i].bus = info->dev->bus->number;
26062306a36Sopenharmony_ci				devices[i].devfn = info->dev->devfn;
26162306a36Sopenharmony_ci				rcu_assign_pointer(devices[i].dev,
26262306a36Sopenharmony_ci						   get_device(dev));
26362306a36Sopenharmony_ci				return 1;
26462306a36Sopenharmony_ci			}
26562306a36Sopenharmony_ci		if (WARN_ON(i >= devices_cnt))
26662306a36Sopenharmony_ci			return -EINVAL;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ciint dmar_remove_dev_scope(struct dmar_pci_notify_info *info, u16 segment,
27362306a36Sopenharmony_ci			  struct dmar_dev_scope *devices, int count)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	int index;
27662306a36Sopenharmony_ci	struct device *tmp;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (info->seg != segment)
27962306a36Sopenharmony_ci		return 0;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	for_each_active_dev_scope(devices, count, index, tmp)
28262306a36Sopenharmony_ci		if (tmp == &info->dev->dev) {
28362306a36Sopenharmony_ci			RCU_INIT_POINTER(devices[index].dev, NULL);
28462306a36Sopenharmony_ci			synchronize_rcu();
28562306a36Sopenharmony_ci			put_device(tmp);
28662306a36Sopenharmony_ci			return 1;
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return 0;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic int dmar_pci_bus_add_dev(struct dmar_pci_notify_info *info)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	int ret = 0;
29562306a36Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
29662306a36Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	for_each_drhd_unit(dmaru) {
29962306a36Sopenharmony_ci		if (dmaru->include_all)
30062306a36Sopenharmony_ci			continue;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		drhd = container_of(dmaru->hdr,
30362306a36Sopenharmony_ci				    struct acpi_dmar_hardware_unit, header);
30462306a36Sopenharmony_ci		ret = dmar_insert_dev_scope(info, (void *)(drhd + 1),
30562306a36Sopenharmony_ci				((void *)drhd) + drhd->header.length,
30662306a36Sopenharmony_ci				dmaru->segment,
30762306a36Sopenharmony_ci				dmaru->devices, dmaru->devices_cnt);
30862306a36Sopenharmony_ci		if (ret)
30962306a36Sopenharmony_ci			break;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci	if (ret >= 0)
31262306a36Sopenharmony_ci		ret = dmar_iommu_notify_scope_dev(info);
31362306a36Sopenharmony_ci	if (ret < 0 && dmar_dev_scope_status == 0)
31462306a36Sopenharmony_ci		dmar_dev_scope_status = ret;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (ret >= 0)
31762306a36Sopenharmony_ci		intel_irq_remap_add_device(info);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return ret;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic void  dmar_pci_bus_del_dev(struct dmar_pci_notify_info *info)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	for_each_drhd_unit(dmaru)
32762306a36Sopenharmony_ci		if (dmar_remove_dev_scope(info, dmaru->segment,
32862306a36Sopenharmony_ci			dmaru->devices, dmaru->devices_cnt))
32962306a36Sopenharmony_ci			break;
33062306a36Sopenharmony_ci	dmar_iommu_notify_scope_dev(info);
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic inline void vf_inherit_msi_domain(struct pci_dev *pdev)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct pci_dev *physfn = pci_physfn(pdev);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	dev_set_msi_domain(&pdev->dev, dev_get_msi_domain(&physfn->dev));
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic int dmar_pci_bus_notifier(struct notifier_block *nb,
34162306a36Sopenharmony_ci				 unsigned long action, void *data)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(data);
34462306a36Sopenharmony_ci	struct dmar_pci_notify_info *info;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* Only care about add/remove events for physical functions.
34762306a36Sopenharmony_ci	 * For VFs we actually do the lookup based on the corresponding
34862306a36Sopenharmony_ci	 * PF in device_to_iommu() anyway. */
34962306a36Sopenharmony_ci	if (pdev->is_virtfn) {
35062306a36Sopenharmony_ci		/*
35162306a36Sopenharmony_ci		 * Ensure that the VF device inherits the irq domain of the
35262306a36Sopenharmony_ci		 * PF device. Ideally the device would inherit the domain
35362306a36Sopenharmony_ci		 * from the bus, but DMAR can have multiple units per bus
35462306a36Sopenharmony_ci		 * which makes this impossible. The VF 'bus' could inherit
35562306a36Sopenharmony_ci		 * from the PF device, but that's yet another x86'sism to
35662306a36Sopenharmony_ci		 * inflict on everybody else.
35762306a36Sopenharmony_ci		 */
35862306a36Sopenharmony_ci		if (action == BUS_NOTIFY_ADD_DEVICE)
35962306a36Sopenharmony_ci			vf_inherit_msi_domain(pdev);
36062306a36Sopenharmony_ci		return NOTIFY_DONE;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (action != BUS_NOTIFY_ADD_DEVICE &&
36462306a36Sopenharmony_ci	    action != BUS_NOTIFY_REMOVED_DEVICE)
36562306a36Sopenharmony_ci		return NOTIFY_DONE;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	info = dmar_alloc_pci_notify_info(pdev, action);
36862306a36Sopenharmony_ci	if (!info)
36962306a36Sopenharmony_ci		return NOTIFY_DONE;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	down_write(&dmar_global_lock);
37262306a36Sopenharmony_ci	if (action == BUS_NOTIFY_ADD_DEVICE)
37362306a36Sopenharmony_ci		dmar_pci_bus_add_dev(info);
37462306a36Sopenharmony_ci	else if (action == BUS_NOTIFY_REMOVED_DEVICE)
37562306a36Sopenharmony_ci		dmar_pci_bus_del_dev(info);
37662306a36Sopenharmony_ci	up_write(&dmar_global_lock);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	dmar_free_pci_notify_info(info);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return NOTIFY_OK;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic struct notifier_block dmar_pci_bus_nb = {
38462306a36Sopenharmony_ci	.notifier_call = dmar_pci_bus_notifier,
38562306a36Sopenharmony_ci	.priority = 1,
38662306a36Sopenharmony_ci};
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic struct dmar_drhd_unit *
38962306a36Sopenharmony_cidmar_find_dmaru(struct acpi_dmar_hardware_unit *drhd)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	list_for_each_entry_rcu(dmaru, &dmar_drhd_units, list,
39462306a36Sopenharmony_ci				dmar_rcu_check())
39562306a36Sopenharmony_ci		if (dmaru->segment == drhd->segment &&
39662306a36Sopenharmony_ci		    dmaru->reg_base_addr == drhd->address)
39762306a36Sopenharmony_ci			return dmaru;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return NULL;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci/*
40362306a36Sopenharmony_ci * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition
40462306a36Sopenharmony_ci * structure which uniquely represent one DMA remapping hardware unit
40562306a36Sopenharmony_ci * present in the platform
40662306a36Sopenharmony_ci */
40762306a36Sopenharmony_cistatic int dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
41062306a36Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
41162306a36Sopenharmony_ci	int ret;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	drhd = (struct acpi_dmar_hardware_unit *)header;
41462306a36Sopenharmony_ci	dmaru = dmar_find_dmaru(drhd);
41562306a36Sopenharmony_ci	if (dmaru)
41662306a36Sopenharmony_ci		goto out;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	dmaru = kzalloc(sizeof(*dmaru) + header->length, GFP_KERNEL);
41962306a36Sopenharmony_ci	if (!dmaru)
42062306a36Sopenharmony_ci		return -ENOMEM;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/*
42362306a36Sopenharmony_ci	 * If header is allocated from slab by ACPI _DSM method, we need to
42462306a36Sopenharmony_ci	 * copy the content because the memory buffer will be freed on return.
42562306a36Sopenharmony_ci	 */
42662306a36Sopenharmony_ci	dmaru->hdr = (void *)(dmaru + 1);
42762306a36Sopenharmony_ci	memcpy(dmaru->hdr, header, header->length);
42862306a36Sopenharmony_ci	dmaru->reg_base_addr = drhd->address;
42962306a36Sopenharmony_ci	dmaru->segment = drhd->segment;
43062306a36Sopenharmony_ci	/* The size of the register set is 2 ^ N 4 KB pages. */
43162306a36Sopenharmony_ci	dmaru->reg_size = 1UL << (drhd->size + 12);
43262306a36Sopenharmony_ci	dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
43362306a36Sopenharmony_ci	dmaru->devices = dmar_alloc_dev_scope((void *)(drhd + 1),
43462306a36Sopenharmony_ci					      ((void *)drhd) + drhd->header.length,
43562306a36Sopenharmony_ci					      &dmaru->devices_cnt);
43662306a36Sopenharmony_ci	if (dmaru->devices_cnt && dmaru->devices == NULL) {
43762306a36Sopenharmony_ci		kfree(dmaru);
43862306a36Sopenharmony_ci		return -ENOMEM;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	ret = alloc_iommu(dmaru);
44262306a36Sopenharmony_ci	if (ret) {
44362306a36Sopenharmony_ci		dmar_free_dev_scope(&dmaru->devices,
44462306a36Sopenharmony_ci				    &dmaru->devices_cnt);
44562306a36Sopenharmony_ci		kfree(dmaru);
44662306a36Sopenharmony_ci		return ret;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci	dmar_register_drhd_unit(dmaru);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ciout:
45162306a36Sopenharmony_ci	if (arg)
45262306a36Sopenharmony_ci		(*(int *)arg)++;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic void dmar_free_drhd(struct dmar_drhd_unit *dmaru)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	if (dmaru->devices && dmaru->devices_cnt)
46062306a36Sopenharmony_ci		dmar_free_dev_scope(&dmaru->devices, &dmaru->devices_cnt);
46162306a36Sopenharmony_ci	if (dmaru->iommu)
46262306a36Sopenharmony_ci		free_iommu(dmaru->iommu);
46362306a36Sopenharmony_ci	kfree(dmaru);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int __init dmar_parse_one_andd(struct acpi_dmar_header *header,
46762306a36Sopenharmony_ci				      void *arg)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct acpi_dmar_andd *andd = (void *)header;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* Check for NUL termination within the designated length */
47262306a36Sopenharmony_ci	if (strnlen(andd->device_name, header->length - 8) == header->length - 8) {
47362306a36Sopenharmony_ci		pr_warn(FW_BUG
47462306a36Sopenharmony_ci			   "Your BIOS is broken; ANDD object name is not NUL-terminated\n"
47562306a36Sopenharmony_ci			   "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
47662306a36Sopenharmony_ci			   dmi_get_system_info(DMI_BIOS_VENDOR),
47762306a36Sopenharmony_ci			   dmi_get_system_info(DMI_BIOS_VERSION),
47862306a36Sopenharmony_ci			   dmi_get_system_info(DMI_PRODUCT_VERSION));
47962306a36Sopenharmony_ci		add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
48062306a36Sopenharmony_ci		return -EINVAL;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci	pr_info("ANDD device: %x name: %s\n", andd->device_number,
48362306a36Sopenharmony_ci		andd->device_name);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return 0;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci#ifdef CONFIG_ACPI_NUMA
48962306a36Sopenharmony_cistatic int dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct acpi_dmar_rhsa *rhsa;
49262306a36Sopenharmony_ci	struct dmar_drhd_unit *drhd;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	rhsa = (struct acpi_dmar_rhsa *)header;
49562306a36Sopenharmony_ci	for_each_drhd_unit(drhd) {
49662306a36Sopenharmony_ci		if (drhd->reg_base_addr == rhsa->base_address) {
49762306a36Sopenharmony_ci			int node = pxm_to_node(rhsa->proximity_domain);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci			if (node != NUMA_NO_NODE && !node_online(node))
50062306a36Sopenharmony_ci				node = NUMA_NO_NODE;
50162306a36Sopenharmony_ci			drhd->iommu->node = node;
50262306a36Sopenharmony_ci			return 0;
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci	pr_warn(FW_BUG
50662306a36Sopenharmony_ci		"Your BIOS is broken; RHSA refers to non-existent DMAR unit at %llx\n"
50762306a36Sopenharmony_ci		"BIOS vendor: %s; Ver: %s; Product Version: %s\n",
50862306a36Sopenharmony_ci		rhsa->base_address,
50962306a36Sopenharmony_ci		dmi_get_system_info(DMI_BIOS_VENDOR),
51062306a36Sopenharmony_ci		dmi_get_system_info(DMI_BIOS_VERSION),
51162306a36Sopenharmony_ci		dmi_get_system_info(DMI_PRODUCT_VERSION));
51262306a36Sopenharmony_ci	add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return 0;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci#else
51762306a36Sopenharmony_ci#define	dmar_parse_one_rhsa		dmar_res_noop
51862306a36Sopenharmony_ci#endif
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic void
52162306a36Sopenharmony_cidmar_table_print_dmar_entry(struct acpi_dmar_header *header)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
52462306a36Sopenharmony_ci	struct acpi_dmar_reserved_memory *rmrr;
52562306a36Sopenharmony_ci	struct acpi_dmar_atsr *atsr;
52662306a36Sopenharmony_ci	struct acpi_dmar_rhsa *rhsa;
52762306a36Sopenharmony_ci	struct acpi_dmar_satc *satc;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	switch (header->type) {
53062306a36Sopenharmony_ci	case ACPI_DMAR_TYPE_HARDWARE_UNIT:
53162306a36Sopenharmony_ci		drhd = container_of(header, struct acpi_dmar_hardware_unit,
53262306a36Sopenharmony_ci				    header);
53362306a36Sopenharmony_ci		pr_info("DRHD base: %#016Lx flags: %#x\n",
53462306a36Sopenharmony_ci			(unsigned long long)drhd->address, drhd->flags);
53562306a36Sopenharmony_ci		break;
53662306a36Sopenharmony_ci	case ACPI_DMAR_TYPE_RESERVED_MEMORY:
53762306a36Sopenharmony_ci		rmrr = container_of(header, struct acpi_dmar_reserved_memory,
53862306a36Sopenharmony_ci				    header);
53962306a36Sopenharmony_ci		pr_info("RMRR base: %#016Lx end: %#016Lx\n",
54062306a36Sopenharmony_ci			(unsigned long long)rmrr->base_address,
54162306a36Sopenharmony_ci			(unsigned long long)rmrr->end_address);
54262306a36Sopenharmony_ci		break;
54362306a36Sopenharmony_ci	case ACPI_DMAR_TYPE_ROOT_ATS:
54462306a36Sopenharmony_ci		atsr = container_of(header, struct acpi_dmar_atsr, header);
54562306a36Sopenharmony_ci		pr_info("ATSR flags: %#x\n", atsr->flags);
54662306a36Sopenharmony_ci		break;
54762306a36Sopenharmony_ci	case ACPI_DMAR_TYPE_HARDWARE_AFFINITY:
54862306a36Sopenharmony_ci		rhsa = container_of(header, struct acpi_dmar_rhsa, header);
54962306a36Sopenharmony_ci		pr_info("RHSA base: %#016Lx proximity domain: %#x\n",
55062306a36Sopenharmony_ci		       (unsigned long long)rhsa->base_address,
55162306a36Sopenharmony_ci		       rhsa->proximity_domain);
55262306a36Sopenharmony_ci		break;
55362306a36Sopenharmony_ci	case ACPI_DMAR_TYPE_NAMESPACE:
55462306a36Sopenharmony_ci		/* We don't print this here because we need to sanity-check
55562306a36Sopenharmony_ci		   it first. So print it in dmar_parse_one_andd() instead. */
55662306a36Sopenharmony_ci		break;
55762306a36Sopenharmony_ci	case ACPI_DMAR_TYPE_SATC:
55862306a36Sopenharmony_ci		satc = container_of(header, struct acpi_dmar_satc, header);
55962306a36Sopenharmony_ci		pr_info("SATC flags: 0x%x\n", satc->flags);
56062306a36Sopenharmony_ci		break;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci/**
56562306a36Sopenharmony_ci * dmar_table_detect - checks to see if the platform supports DMAR devices
56662306a36Sopenharmony_ci */
56762306a36Sopenharmony_cistatic int __init dmar_table_detect(void)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	acpi_status status = AE_OK;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* if we could find DMAR table, then there are DMAR devices */
57262306a36Sopenharmony_ci	status = acpi_get_table(ACPI_SIG_DMAR, 0, &dmar_tbl);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (ACPI_SUCCESS(status) && !dmar_tbl) {
57562306a36Sopenharmony_ci		pr_warn("Unable to map DMAR\n");
57662306a36Sopenharmony_ci		status = AE_NOT_FOUND;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return ACPI_SUCCESS(status) ? 0 : -ENOENT;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int dmar_walk_remapping_entries(struct acpi_dmar_header *start,
58362306a36Sopenharmony_ci				       size_t len, struct dmar_res_callback *cb)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct acpi_dmar_header *iter, *next;
58662306a36Sopenharmony_ci	struct acpi_dmar_header *end = ((void *)start) + len;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	for (iter = start; iter < end; iter = next) {
58962306a36Sopenharmony_ci		next = (void *)iter + iter->length;
59062306a36Sopenharmony_ci		if (iter->length == 0) {
59162306a36Sopenharmony_ci			/* Avoid looping forever on bad ACPI tables */
59262306a36Sopenharmony_ci			pr_debug(FW_BUG "Invalid 0-length structure\n");
59362306a36Sopenharmony_ci			break;
59462306a36Sopenharmony_ci		} else if (next > end) {
59562306a36Sopenharmony_ci			/* Avoid passing table end */
59662306a36Sopenharmony_ci			pr_warn(FW_BUG "Record passes table end\n");
59762306a36Sopenharmony_ci			return -EINVAL;
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		if (cb->print_entry)
60162306a36Sopenharmony_ci			dmar_table_print_dmar_entry(iter);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		if (iter->type >= ACPI_DMAR_TYPE_RESERVED) {
60462306a36Sopenharmony_ci			/* continue for forward compatibility */
60562306a36Sopenharmony_ci			pr_debug("Unknown DMAR structure type %d\n",
60662306a36Sopenharmony_ci				 iter->type);
60762306a36Sopenharmony_ci		} else if (cb->cb[iter->type]) {
60862306a36Sopenharmony_ci			int ret;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci			ret = cb->cb[iter->type](iter, cb->arg[iter->type]);
61162306a36Sopenharmony_ci			if (ret)
61262306a36Sopenharmony_ci				return ret;
61362306a36Sopenharmony_ci		} else if (!cb->ignore_unhandled) {
61462306a36Sopenharmony_ci			pr_warn("No handler for DMAR structure type %d\n",
61562306a36Sopenharmony_ci				iter->type);
61662306a36Sopenharmony_ci			return -EINVAL;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	return 0;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic inline int dmar_walk_dmar_table(struct acpi_table_dmar *dmar,
62462306a36Sopenharmony_ci				       struct dmar_res_callback *cb)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	return dmar_walk_remapping_entries((void *)(dmar + 1),
62762306a36Sopenharmony_ci			dmar->header.length - sizeof(*dmar), cb);
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci/**
63162306a36Sopenharmony_ci * parse_dmar_table - parses the DMA reporting table
63262306a36Sopenharmony_ci */
63362306a36Sopenharmony_cistatic int __init
63462306a36Sopenharmony_ciparse_dmar_table(void)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct acpi_table_dmar *dmar;
63762306a36Sopenharmony_ci	int drhd_count = 0;
63862306a36Sopenharmony_ci	int ret;
63962306a36Sopenharmony_ci	struct dmar_res_callback cb = {
64062306a36Sopenharmony_ci		.print_entry = true,
64162306a36Sopenharmony_ci		.ignore_unhandled = true,
64262306a36Sopenharmony_ci		.arg[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &drhd_count,
64362306a36Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_parse_one_drhd,
64462306a36Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_RESERVED_MEMORY] = &dmar_parse_one_rmrr,
64562306a36Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_ROOT_ATS] = &dmar_parse_one_atsr,
64662306a36Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_HARDWARE_AFFINITY] = &dmar_parse_one_rhsa,
64762306a36Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_NAMESPACE] = &dmar_parse_one_andd,
64862306a36Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_SATC] = &dmar_parse_one_satc,
64962306a36Sopenharmony_ci	};
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	/*
65262306a36Sopenharmony_ci	 * Do it again, earlier dmar_tbl mapping could be mapped with
65362306a36Sopenharmony_ci	 * fixed map.
65462306a36Sopenharmony_ci	 */
65562306a36Sopenharmony_ci	dmar_table_detect();
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/*
65862306a36Sopenharmony_ci	 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
65962306a36Sopenharmony_ci	 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
66062306a36Sopenharmony_ci	 */
66162306a36Sopenharmony_ci	dmar_tbl = tboot_get_dmar_table(dmar_tbl);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	dmar = (struct acpi_table_dmar *)dmar_tbl;
66462306a36Sopenharmony_ci	if (!dmar)
66562306a36Sopenharmony_ci		return -ENODEV;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (dmar->width < PAGE_SHIFT - 1) {
66862306a36Sopenharmony_ci		pr_warn("Invalid DMAR haw\n");
66962306a36Sopenharmony_ci		return -EINVAL;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	pr_info("Host address width %d\n", dmar->width + 1);
67362306a36Sopenharmony_ci	ret = dmar_walk_dmar_table(dmar, &cb);
67462306a36Sopenharmony_ci	if (ret == 0 && drhd_count == 0)
67562306a36Sopenharmony_ci		pr_warn(FW_BUG "No DRHD structure found in DMAR table\n");
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	return ret;
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_cistatic int dmar_pci_device_match(struct dmar_dev_scope devices[],
68162306a36Sopenharmony_ci				 int cnt, struct pci_dev *dev)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	int index;
68462306a36Sopenharmony_ci	struct device *tmp;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	while (dev) {
68762306a36Sopenharmony_ci		for_each_active_dev_scope(devices, cnt, index, tmp)
68862306a36Sopenharmony_ci			if (dev_is_pci(tmp) && dev == to_pci_dev(tmp))
68962306a36Sopenharmony_ci				return 1;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci		/* Check our parent */
69262306a36Sopenharmony_ci		dev = dev->bus->self;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistruct dmar_drhd_unit *
69962306a36Sopenharmony_cidmar_find_matched_drhd_unit(struct pci_dev *dev)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
70262306a36Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	dev = pci_physfn(dev);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	rcu_read_lock();
70762306a36Sopenharmony_ci	for_each_drhd_unit(dmaru) {
70862306a36Sopenharmony_ci		drhd = container_of(dmaru->hdr,
70962306a36Sopenharmony_ci				    struct acpi_dmar_hardware_unit,
71062306a36Sopenharmony_ci				    header);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		if (dmaru->include_all &&
71362306a36Sopenharmony_ci		    drhd->segment == pci_domain_nr(dev->bus))
71462306a36Sopenharmony_ci			goto out;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci		if (dmar_pci_device_match(dmaru->devices,
71762306a36Sopenharmony_ci					  dmaru->devices_cnt, dev))
71862306a36Sopenharmony_ci			goto out;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci	dmaru = NULL;
72162306a36Sopenharmony_ciout:
72262306a36Sopenharmony_ci	rcu_read_unlock();
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	return dmaru;
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic void __init dmar_acpi_insert_dev_scope(u8 device_number,
72862306a36Sopenharmony_ci					      struct acpi_device *adev)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
73162306a36Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
73262306a36Sopenharmony_ci	struct acpi_dmar_device_scope *scope;
73362306a36Sopenharmony_ci	struct device *tmp;
73462306a36Sopenharmony_ci	int i;
73562306a36Sopenharmony_ci	struct acpi_dmar_pci_path *path;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	for_each_drhd_unit(dmaru) {
73862306a36Sopenharmony_ci		drhd = container_of(dmaru->hdr,
73962306a36Sopenharmony_ci				    struct acpi_dmar_hardware_unit,
74062306a36Sopenharmony_ci				    header);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		for (scope = (void *)(drhd + 1);
74362306a36Sopenharmony_ci		     (unsigned long)scope < ((unsigned long)drhd) + drhd->header.length;
74462306a36Sopenharmony_ci		     scope = ((void *)scope) + scope->length) {
74562306a36Sopenharmony_ci			if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_NAMESPACE)
74662306a36Sopenharmony_ci				continue;
74762306a36Sopenharmony_ci			if (scope->enumeration_id != device_number)
74862306a36Sopenharmony_ci				continue;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci			path = (void *)(scope + 1);
75162306a36Sopenharmony_ci			pr_info("ACPI device \"%s\" under DMAR at %llx as %02x:%02x.%d\n",
75262306a36Sopenharmony_ci				dev_name(&adev->dev), dmaru->reg_base_addr,
75362306a36Sopenharmony_ci				scope->bus, path->device, path->function);
75462306a36Sopenharmony_ci			for_each_dev_scope(dmaru->devices, dmaru->devices_cnt, i, tmp)
75562306a36Sopenharmony_ci				if (tmp == NULL) {
75662306a36Sopenharmony_ci					dmaru->devices[i].bus = scope->bus;
75762306a36Sopenharmony_ci					dmaru->devices[i].devfn = PCI_DEVFN(path->device,
75862306a36Sopenharmony_ci									    path->function);
75962306a36Sopenharmony_ci					rcu_assign_pointer(dmaru->devices[i].dev,
76062306a36Sopenharmony_ci							   get_device(&adev->dev));
76162306a36Sopenharmony_ci					return;
76262306a36Sopenharmony_ci				}
76362306a36Sopenharmony_ci			BUG_ON(i >= dmaru->devices_cnt);
76462306a36Sopenharmony_ci		}
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci	pr_warn("No IOMMU scope found for ANDD enumeration ID %d (%s)\n",
76762306a36Sopenharmony_ci		device_number, dev_name(&adev->dev));
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic int __init dmar_acpi_dev_scope_init(void)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct acpi_dmar_andd *andd;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (dmar_tbl == NULL)
77562306a36Sopenharmony_ci		return -ENODEV;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	for (andd = (void *)dmar_tbl + sizeof(struct acpi_table_dmar);
77862306a36Sopenharmony_ci	     ((unsigned long)andd) < ((unsigned long)dmar_tbl) + dmar_tbl->length;
77962306a36Sopenharmony_ci	     andd = ((void *)andd) + andd->header.length) {
78062306a36Sopenharmony_ci		if (andd->header.type == ACPI_DMAR_TYPE_NAMESPACE) {
78162306a36Sopenharmony_ci			acpi_handle h;
78262306a36Sopenharmony_ci			struct acpi_device *adev;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci			if (!ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT,
78562306a36Sopenharmony_ci							  andd->device_name,
78662306a36Sopenharmony_ci							  &h))) {
78762306a36Sopenharmony_ci				pr_err("Failed to find handle for ACPI object %s\n",
78862306a36Sopenharmony_ci				       andd->device_name);
78962306a36Sopenharmony_ci				continue;
79062306a36Sopenharmony_ci			}
79162306a36Sopenharmony_ci			adev = acpi_fetch_acpi_dev(h);
79262306a36Sopenharmony_ci			if (!adev) {
79362306a36Sopenharmony_ci				pr_err("Failed to get device for ACPI object %s\n",
79462306a36Sopenharmony_ci				       andd->device_name);
79562306a36Sopenharmony_ci				continue;
79662306a36Sopenharmony_ci			}
79762306a36Sopenharmony_ci			dmar_acpi_insert_dev_scope(andd->device_number, adev);
79862306a36Sopenharmony_ci		}
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci	return 0;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ciint __init dmar_dev_scope_init(void)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct pci_dev *dev = NULL;
80662306a36Sopenharmony_ci	struct dmar_pci_notify_info *info;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	if (dmar_dev_scope_status != 1)
80962306a36Sopenharmony_ci		return dmar_dev_scope_status;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (list_empty(&dmar_drhd_units)) {
81262306a36Sopenharmony_ci		dmar_dev_scope_status = -ENODEV;
81362306a36Sopenharmony_ci	} else {
81462306a36Sopenharmony_ci		dmar_dev_scope_status = 0;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		dmar_acpi_dev_scope_init();
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		for_each_pci_dev(dev) {
81962306a36Sopenharmony_ci			if (dev->is_virtfn)
82062306a36Sopenharmony_ci				continue;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci			info = dmar_alloc_pci_notify_info(dev,
82362306a36Sopenharmony_ci					BUS_NOTIFY_ADD_DEVICE);
82462306a36Sopenharmony_ci			if (!info) {
82562306a36Sopenharmony_ci				pci_dev_put(dev);
82662306a36Sopenharmony_ci				return dmar_dev_scope_status;
82762306a36Sopenharmony_ci			} else {
82862306a36Sopenharmony_ci				dmar_pci_bus_add_dev(info);
82962306a36Sopenharmony_ci				dmar_free_pci_notify_info(info);
83062306a36Sopenharmony_ci			}
83162306a36Sopenharmony_ci		}
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	return dmar_dev_scope_status;
83562306a36Sopenharmony_ci}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_civoid __init dmar_register_bus_notifier(void)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb);
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ciint __init dmar_table_init(void)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	static int dmar_table_initialized;
84662306a36Sopenharmony_ci	int ret;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (dmar_table_initialized == 0) {
84962306a36Sopenharmony_ci		ret = parse_dmar_table();
85062306a36Sopenharmony_ci		if (ret < 0) {
85162306a36Sopenharmony_ci			if (ret != -ENODEV)
85262306a36Sopenharmony_ci				pr_info("Parse DMAR table failure.\n");
85362306a36Sopenharmony_ci		} else  if (list_empty(&dmar_drhd_units)) {
85462306a36Sopenharmony_ci			pr_info("No DMAR devices found\n");
85562306a36Sopenharmony_ci			ret = -ENODEV;
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		if (ret < 0)
85962306a36Sopenharmony_ci			dmar_table_initialized = ret;
86062306a36Sopenharmony_ci		else
86162306a36Sopenharmony_ci			dmar_table_initialized = 1;
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	return dmar_table_initialized < 0 ? dmar_table_initialized : 0;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic void warn_invalid_dmar(u64 addr, const char *message)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	pr_warn_once(FW_BUG
87062306a36Sopenharmony_ci		"Your BIOS is broken; DMAR reported at address %llx%s!\n"
87162306a36Sopenharmony_ci		"BIOS vendor: %s; Ver: %s; Product Version: %s\n",
87262306a36Sopenharmony_ci		addr, message,
87362306a36Sopenharmony_ci		dmi_get_system_info(DMI_BIOS_VENDOR),
87462306a36Sopenharmony_ci		dmi_get_system_info(DMI_BIOS_VERSION),
87562306a36Sopenharmony_ci		dmi_get_system_info(DMI_PRODUCT_VERSION));
87662306a36Sopenharmony_ci	add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic int __ref
88062306a36Sopenharmony_cidmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
88362306a36Sopenharmony_ci	void __iomem *addr;
88462306a36Sopenharmony_ci	u64 cap, ecap;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	drhd = (void *)entry;
88762306a36Sopenharmony_ci	if (!drhd->address) {
88862306a36Sopenharmony_ci		warn_invalid_dmar(0, "");
88962306a36Sopenharmony_ci		return -EINVAL;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (arg)
89362306a36Sopenharmony_ci		addr = ioremap(drhd->address, VTD_PAGE_SIZE);
89462306a36Sopenharmony_ci	else
89562306a36Sopenharmony_ci		addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
89662306a36Sopenharmony_ci	if (!addr) {
89762306a36Sopenharmony_ci		pr_warn("Can't validate DRHD address: %llx\n", drhd->address);
89862306a36Sopenharmony_ci		return -EINVAL;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	cap = dmar_readq(addr + DMAR_CAP_REG);
90262306a36Sopenharmony_ci	ecap = dmar_readq(addr + DMAR_ECAP_REG);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if (arg)
90562306a36Sopenharmony_ci		iounmap(addr);
90662306a36Sopenharmony_ci	else
90762306a36Sopenharmony_ci		early_iounmap(addr, VTD_PAGE_SIZE);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) {
91062306a36Sopenharmony_ci		warn_invalid_dmar(drhd->address, " returns all ones");
91162306a36Sopenharmony_ci		return -EINVAL;
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	return 0;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_civoid __init detect_intel_iommu(void)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	int ret;
92062306a36Sopenharmony_ci	struct dmar_res_callback validate_drhd_cb = {
92162306a36Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_validate_one_drhd,
92262306a36Sopenharmony_ci		.ignore_unhandled = true,
92362306a36Sopenharmony_ci	};
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	down_write(&dmar_global_lock);
92662306a36Sopenharmony_ci	ret = dmar_table_detect();
92762306a36Sopenharmony_ci	if (!ret)
92862306a36Sopenharmony_ci		ret = dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl,
92962306a36Sopenharmony_ci					   &validate_drhd_cb);
93062306a36Sopenharmony_ci	if (!ret && !no_iommu && !iommu_detected &&
93162306a36Sopenharmony_ci	    (!dmar_disabled || dmar_platform_optin())) {
93262306a36Sopenharmony_ci		iommu_detected = 1;
93362306a36Sopenharmony_ci		/* Make sure ACS will be enabled */
93462306a36Sopenharmony_ci		pci_request_acs();
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci#ifdef CONFIG_X86
93862306a36Sopenharmony_ci	if (!ret) {
93962306a36Sopenharmony_ci		x86_init.iommu.iommu_init = intel_iommu_init;
94062306a36Sopenharmony_ci		x86_platform.iommu_shutdown = intel_iommu_shutdown;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci#endif
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (dmar_tbl) {
94662306a36Sopenharmony_ci		acpi_put_table(dmar_tbl);
94762306a36Sopenharmony_ci		dmar_tbl = NULL;
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci	up_write(&dmar_global_lock);
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_cistatic void unmap_iommu(struct intel_iommu *iommu)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	iounmap(iommu->reg);
95562306a36Sopenharmony_ci	release_mem_region(iommu->reg_phys, iommu->reg_size);
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci/**
95962306a36Sopenharmony_ci * map_iommu: map the iommu's registers
96062306a36Sopenharmony_ci * @iommu: the iommu to map
96162306a36Sopenharmony_ci * @drhd: DMA remapping hardware definition structure
96262306a36Sopenharmony_ci *
96362306a36Sopenharmony_ci * Memory map the iommu's registers.  Start w/ a single page, and
96462306a36Sopenharmony_ci * possibly expand if that turns out to be insufficent.
96562306a36Sopenharmony_ci */
96662306a36Sopenharmony_cistatic int map_iommu(struct intel_iommu *iommu, struct dmar_drhd_unit *drhd)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	u64 phys_addr = drhd->reg_base_addr;
96962306a36Sopenharmony_ci	int map_size, err=0;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	iommu->reg_phys = phys_addr;
97262306a36Sopenharmony_ci	iommu->reg_size = drhd->reg_size;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	if (!request_mem_region(iommu->reg_phys, iommu->reg_size, iommu->name)) {
97562306a36Sopenharmony_ci		pr_err("Can't reserve memory\n");
97662306a36Sopenharmony_ci		err = -EBUSY;
97762306a36Sopenharmony_ci		goto out;
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size);
98162306a36Sopenharmony_ci	if (!iommu->reg) {
98262306a36Sopenharmony_ci		pr_err("Can't map the region\n");
98362306a36Sopenharmony_ci		err = -ENOMEM;
98462306a36Sopenharmony_ci		goto release;
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG);
98862306a36Sopenharmony_ci	iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) {
99162306a36Sopenharmony_ci		err = -EINVAL;
99262306a36Sopenharmony_ci		warn_invalid_dmar(phys_addr, " returns all ones");
99362306a36Sopenharmony_ci		goto unmap;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	/* the registers might be more than one page */
99762306a36Sopenharmony_ci	map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap),
99862306a36Sopenharmony_ci			 cap_max_fault_reg_offset(iommu->cap));
99962306a36Sopenharmony_ci	map_size = VTD_PAGE_ALIGN(map_size);
100062306a36Sopenharmony_ci	if (map_size > iommu->reg_size) {
100162306a36Sopenharmony_ci		iounmap(iommu->reg);
100262306a36Sopenharmony_ci		release_mem_region(iommu->reg_phys, iommu->reg_size);
100362306a36Sopenharmony_ci		iommu->reg_size = map_size;
100462306a36Sopenharmony_ci		if (!request_mem_region(iommu->reg_phys, iommu->reg_size,
100562306a36Sopenharmony_ci					iommu->name)) {
100662306a36Sopenharmony_ci			pr_err("Can't reserve memory\n");
100762306a36Sopenharmony_ci			err = -EBUSY;
100862306a36Sopenharmony_ci			goto out;
100962306a36Sopenharmony_ci		}
101062306a36Sopenharmony_ci		iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size);
101162306a36Sopenharmony_ci		if (!iommu->reg) {
101262306a36Sopenharmony_ci			pr_err("Can't map the region\n");
101362306a36Sopenharmony_ci			err = -ENOMEM;
101462306a36Sopenharmony_ci			goto release;
101562306a36Sopenharmony_ci		}
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	if (cap_ecmds(iommu->cap)) {
101962306a36Sopenharmony_ci		int i;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci		for (i = 0; i < DMA_MAX_NUM_ECMDCAP; i++) {
102262306a36Sopenharmony_ci			iommu->ecmdcap[i] = dmar_readq(iommu->reg + DMAR_ECCAP_REG +
102362306a36Sopenharmony_ci						       i * DMA_ECMD_REG_STEP);
102462306a36Sopenharmony_ci		}
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	err = 0;
102862306a36Sopenharmony_ci	goto out;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ciunmap:
103162306a36Sopenharmony_ci	iounmap(iommu->reg);
103262306a36Sopenharmony_cirelease:
103362306a36Sopenharmony_ci	release_mem_region(iommu->reg_phys, iommu->reg_size);
103462306a36Sopenharmony_ciout:
103562306a36Sopenharmony_ci	return err;
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic int alloc_iommu(struct dmar_drhd_unit *drhd)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	struct intel_iommu *iommu;
104162306a36Sopenharmony_ci	u32 ver, sts;
104262306a36Sopenharmony_ci	int agaw = -1;
104362306a36Sopenharmony_ci	int msagaw = -1;
104462306a36Sopenharmony_ci	int err;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	if (!drhd->reg_base_addr) {
104762306a36Sopenharmony_ci		warn_invalid_dmar(0, "");
104862306a36Sopenharmony_ci		return -EINVAL;
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
105262306a36Sopenharmony_ci	if (!iommu)
105362306a36Sopenharmony_ci		return -ENOMEM;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	iommu->seq_id = ida_alloc_range(&dmar_seq_ids, 0,
105662306a36Sopenharmony_ci					DMAR_UNITS_SUPPORTED - 1, GFP_KERNEL);
105762306a36Sopenharmony_ci	if (iommu->seq_id < 0) {
105862306a36Sopenharmony_ci		pr_err("Failed to allocate seq_id\n");
105962306a36Sopenharmony_ci		err = iommu->seq_id;
106062306a36Sopenharmony_ci		goto error;
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci	sprintf(iommu->name, "dmar%d", iommu->seq_id);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	err = map_iommu(iommu, drhd);
106562306a36Sopenharmony_ci	if (err) {
106662306a36Sopenharmony_ci		pr_err("Failed to map %s\n", iommu->name);
106762306a36Sopenharmony_ci		goto error_free_seq_id;
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	err = -EINVAL;
107162306a36Sopenharmony_ci	if (!cap_sagaw(iommu->cap) &&
107262306a36Sopenharmony_ci	    (!ecap_smts(iommu->ecap) || ecap_slts(iommu->ecap))) {
107362306a36Sopenharmony_ci		pr_info("%s: No supported address widths. Not attempting DMA translation.\n",
107462306a36Sopenharmony_ci			iommu->name);
107562306a36Sopenharmony_ci		drhd->ignored = 1;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	if (!drhd->ignored) {
107962306a36Sopenharmony_ci		agaw = iommu_calculate_agaw(iommu);
108062306a36Sopenharmony_ci		if (agaw < 0) {
108162306a36Sopenharmony_ci			pr_err("Cannot get a valid agaw for iommu (seq_id = %d)\n",
108262306a36Sopenharmony_ci			       iommu->seq_id);
108362306a36Sopenharmony_ci			drhd->ignored = 1;
108462306a36Sopenharmony_ci		}
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci	if (!drhd->ignored) {
108762306a36Sopenharmony_ci		msagaw = iommu_calculate_max_sagaw(iommu);
108862306a36Sopenharmony_ci		if (msagaw < 0) {
108962306a36Sopenharmony_ci			pr_err("Cannot get a valid max agaw for iommu (seq_id = %d)\n",
109062306a36Sopenharmony_ci			       iommu->seq_id);
109162306a36Sopenharmony_ci			drhd->ignored = 1;
109262306a36Sopenharmony_ci			agaw = -1;
109362306a36Sopenharmony_ci		}
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci	iommu->agaw = agaw;
109662306a36Sopenharmony_ci	iommu->msagaw = msagaw;
109762306a36Sopenharmony_ci	iommu->segment = drhd->segment;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	iommu->node = NUMA_NO_NODE;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	ver = readl(iommu->reg + DMAR_VER_REG);
110262306a36Sopenharmony_ci	pr_info("%s: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n",
110362306a36Sopenharmony_ci		iommu->name,
110462306a36Sopenharmony_ci		(unsigned long long)drhd->reg_base_addr,
110562306a36Sopenharmony_ci		DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
110662306a36Sopenharmony_ci		(unsigned long long)iommu->cap,
110762306a36Sopenharmony_ci		(unsigned long long)iommu->ecap);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	/* Reflect status in gcmd */
111062306a36Sopenharmony_ci	sts = readl(iommu->reg + DMAR_GSTS_REG);
111162306a36Sopenharmony_ci	if (sts & DMA_GSTS_IRES)
111262306a36Sopenharmony_ci		iommu->gcmd |= DMA_GCMD_IRE;
111362306a36Sopenharmony_ci	if (sts & DMA_GSTS_TES)
111462306a36Sopenharmony_ci		iommu->gcmd |= DMA_GCMD_TE;
111562306a36Sopenharmony_ci	if (sts & DMA_GSTS_QIES)
111662306a36Sopenharmony_ci		iommu->gcmd |= DMA_GCMD_QIE;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	if (alloc_iommu_pmu(iommu))
111962306a36Sopenharmony_ci		pr_debug("Cannot alloc PMU for iommu (seq_id = %d)\n", iommu->seq_id);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	raw_spin_lock_init(&iommu->register_lock);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	/*
112462306a36Sopenharmony_ci	 * A value of N in PSS field of eCap register indicates hardware
112562306a36Sopenharmony_ci	 * supports PASID field of N+1 bits.
112662306a36Sopenharmony_ci	 */
112762306a36Sopenharmony_ci	if (pasid_supported(iommu))
112862306a36Sopenharmony_ci		iommu->iommu.max_pasids = 2UL << ecap_pss(iommu->ecap);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	/*
113162306a36Sopenharmony_ci	 * This is only for hotplug; at boot time intel_iommu_enabled won't
113262306a36Sopenharmony_ci	 * be set yet. When intel_iommu_init() runs, it registers the units
113362306a36Sopenharmony_ci	 * present at boot time, then sets intel_iommu_enabled.
113462306a36Sopenharmony_ci	 */
113562306a36Sopenharmony_ci	if (intel_iommu_enabled && !drhd->ignored) {
113662306a36Sopenharmony_ci		err = iommu_device_sysfs_add(&iommu->iommu, NULL,
113762306a36Sopenharmony_ci					     intel_iommu_groups,
113862306a36Sopenharmony_ci					     "%s", iommu->name);
113962306a36Sopenharmony_ci		if (err)
114062306a36Sopenharmony_ci			goto err_unmap;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci		err = iommu_device_register(&iommu->iommu, &intel_iommu_ops, NULL);
114362306a36Sopenharmony_ci		if (err)
114462306a36Sopenharmony_ci			goto err_sysfs;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		iommu_pmu_register(iommu);
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	drhd->iommu = iommu;
115062306a36Sopenharmony_ci	iommu->drhd = drhd;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	return 0;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cierr_sysfs:
115562306a36Sopenharmony_ci	iommu_device_sysfs_remove(&iommu->iommu);
115662306a36Sopenharmony_cierr_unmap:
115762306a36Sopenharmony_ci	free_iommu_pmu(iommu);
115862306a36Sopenharmony_ci	unmap_iommu(iommu);
115962306a36Sopenharmony_cierror_free_seq_id:
116062306a36Sopenharmony_ci	ida_free(&dmar_seq_ids, iommu->seq_id);
116162306a36Sopenharmony_cierror:
116262306a36Sopenharmony_ci	kfree(iommu);
116362306a36Sopenharmony_ci	return err;
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_cistatic void free_iommu(struct intel_iommu *iommu)
116762306a36Sopenharmony_ci{
116862306a36Sopenharmony_ci	if (intel_iommu_enabled && !iommu->drhd->ignored) {
116962306a36Sopenharmony_ci		iommu_pmu_unregister(iommu);
117062306a36Sopenharmony_ci		iommu_device_unregister(&iommu->iommu);
117162306a36Sopenharmony_ci		iommu_device_sysfs_remove(&iommu->iommu);
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	free_iommu_pmu(iommu);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	if (iommu->irq) {
117762306a36Sopenharmony_ci		if (iommu->pr_irq) {
117862306a36Sopenharmony_ci			free_irq(iommu->pr_irq, iommu);
117962306a36Sopenharmony_ci			dmar_free_hwirq(iommu->pr_irq);
118062306a36Sopenharmony_ci			iommu->pr_irq = 0;
118162306a36Sopenharmony_ci		}
118262306a36Sopenharmony_ci		free_irq(iommu->irq, iommu);
118362306a36Sopenharmony_ci		dmar_free_hwirq(iommu->irq);
118462306a36Sopenharmony_ci		iommu->irq = 0;
118562306a36Sopenharmony_ci	}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (iommu->qi) {
118862306a36Sopenharmony_ci		free_page((unsigned long)iommu->qi->desc);
118962306a36Sopenharmony_ci		kfree(iommu->qi->desc_status);
119062306a36Sopenharmony_ci		kfree(iommu->qi);
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	if (iommu->reg)
119462306a36Sopenharmony_ci		unmap_iommu(iommu);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	ida_free(&dmar_seq_ids, iommu->seq_id);
119762306a36Sopenharmony_ci	kfree(iommu);
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci/*
120162306a36Sopenharmony_ci * Reclaim all the submitted descriptors which have completed its work.
120262306a36Sopenharmony_ci */
120362306a36Sopenharmony_cistatic inline void reclaim_free_desc(struct q_inval *qi)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	while (qi->desc_status[qi->free_tail] == QI_DONE ||
120662306a36Sopenharmony_ci	       qi->desc_status[qi->free_tail] == QI_ABORT) {
120762306a36Sopenharmony_ci		qi->desc_status[qi->free_tail] = QI_FREE;
120862306a36Sopenharmony_ci		qi->free_tail = (qi->free_tail + 1) % QI_LENGTH;
120962306a36Sopenharmony_ci		qi->free_cnt++;
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_cistatic const char *qi_type_string(u8 type)
121462306a36Sopenharmony_ci{
121562306a36Sopenharmony_ci	switch (type) {
121662306a36Sopenharmony_ci	case QI_CC_TYPE:
121762306a36Sopenharmony_ci		return "Context-cache Invalidation";
121862306a36Sopenharmony_ci	case QI_IOTLB_TYPE:
121962306a36Sopenharmony_ci		return "IOTLB Invalidation";
122062306a36Sopenharmony_ci	case QI_DIOTLB_TYPE:
122162306a36Sopenharmony_ci		return "Device-TLB Invalidation";
122262306a36Sopenharmony_ci	case QI_IEC_TYPE:
122362306a36Sopenharmony_ci		return "Interrupt Entry Cache Invalidation";
122462306a36Sopenharmony_ci	case QI_IWD_TYPE:
122562306a36Sopenharmony_ci		return "Invalidation Wait";
122662306a36Sopenharmony_ci	case QI_EIOTLB_TYPE:
122762306a36Sopenharmony_ci		return "PASID-based IOTLB Invalidation";
122862306a36Sopenharmony_ci	case QI_PC_TYPE:
122962306a36Sopenharmony_ci		return "PASID-cache Invalidation";
123062306a36Sopenharmony_ci	case QI_DEIOTLB_TYPE:
123162306a36Sopenharmony_ci		return "PASID-based Device-TLB Invalidation";
123262306a36Sopenharmony_ci	case QI_PGRP_RESP_TYPE:
123362306a36Sopenharmony_ci		return "Page Group Response";
123462306a36Sopenharmony_ci	default:
123562306a36Sopenharmony_ci		return "UNKNOWN";
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic void qi_dump_fault(struct intel_iommu *iommu, u32 fault)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	unsigned int head = dmar_readl(iommu->reg + DMAR_IQH_REG);
124262306a36Sopenharmony_ci	u64 iqe_err = dmar_readq(iommu->reg + DMAR_IQER_REG);
124362306a36Sopenharmony_ci	struct qi_desc *desc = iommu->qi->desc + head;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	if (fault & DMA_FSTS_IQE)
124662306a36Sopenharmony_ci		pr_err("VT-d detected Invalidation Queue Error: Reason %llx",
124762306a36Sopenharmony_ci		       DMAR_IQER_REG_IQEI(iqe_err));
124862306a36Sopenharmony_ci	if (fault & DMA_FSTS_ITE)
124962306a36Sopenharmony_ci		pr_err("VT-d detected Invalidation Time-out Error: SID %llx",
125062306a36Sopenharmony_ci		       DMAR_IQER_REG_ITESID(iqe_err));
125162306a36Sopenharmony_ci	if (fault & DMA_FSTS_ICE)
125262306a36Sopenharmony_ci		pr_err("VT-d detected Invalidation Completion Error: SID %llx",
125362306a36Sopenharmony_ci		       DMAR_IQER_REG_ICESID(iqe_err));
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	pr_err("QI HEAD: %s qw0 = 0x%llx, qw1 = 0x%llx\n",
125662306a36Sopenharmony_ci	       qi_type_string(desc->qw0 & 0xf),
125762306a36Sopenharmony_ci	       (unsigned long long)desc->qw0,
125862306a36Sopenharmony_ci	       (unsigned long long)desc->qw1);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	head = ((head >> qi_shift(iommu)) + QI_LENGTH - 1) % QI_LENGTH;
126162306a36Sopenharmony_ci	head <<= qi_shift(iommu);
126262306a36Sopenharmony_ci	desc = iommu->qi->desc + head;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	pr_err("QI PRIOR: %s qw0 = 0x%llx, qw1 = 0x%llx\n",
126562306a36Sopenharmony_ci	       qi_type_string(desc->qw0 & 0xf),
126662306a36Sopenharmony_ci	       (unsigned long long)desc->qw0,
126762306a36Sopenharmony_ci	       (unsigned long long)desc->qw1);
126862306a36Sopenharmony_ci}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_cistatic int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
127162306a36Sopenharmony_ci{
127262306a36Sopenharmony_ci	u32 fault;
127362306a36Sopenharmony_ci	int head, tail;
127462306a36Sopenharmony_ci	struct q_inval *qi = iommu->qi;
127562306a36Sopenharmony_ci	int shift = qi_shift(iommu);
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	if (qi->desc_status[wait_index] == QI_ABORT)
127862306a36Sopenharmony_ci		return -EAGAIN;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	fault = readl(iommu->reg + DMAR_FSTS_REG);
128162306a36Sopenharmony_ci	if (fault & (DMA_FSTS_IQE | DMA_FSTS_ITE | DMA_FSTS_ICE))
128262306a36Sopenharmony_ci		qi_dump_fault(iommu, fault);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	/*
128562306a36Sopenharmony_ci	 * If IQE happens, the head points to the descriptor associated
128662306a36Sopenharmony_ci	 * with the error. No new descriptors are fetched until the IQE
128762306a36Sopenharmony_ci	 * is cleared.
128862306a36Sopenharmony_ci	 */
128962306a36Sopenharmony_ci	if (fault & DMA_FSTS_IQE) {
129062306a36Sopenharmony_ci		head = readl(iommu->reg + DMAR_IQH_REG);
129162306a36Sopenharmony_ci		if ((head >> shift) == index) {
129262306a36Sopenharmony_ci			struct qi_desc *desc = qi->desc + head;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci			/*
129562306a36Sopenharmony_ci			 * desc->qw2 and desc->qw3 are either reserved or
129662306a36Sopenharmony_ci			 * used by software as private data. We won't print
129762306a36Sopenharmony_ci			 * out these two qw's for security consideration.
129862306a36Sopenharmony_ci			 */
129962306a36Sopenharmony_ci			memcpy(desc, qi->desc + (wait_index << shift),
130062306a36Sopenharmony_ci			       1 << shift);
130162306a36Sopenharmony_ci			writel(DMA_FSTS_IQE, iommu->reg + DMAR_FSTS_REG);
130262306a36Sopenharmony_ci			pr_info("Invalidation Queue Error (IQE) cleared\n");
130362306a36Sopenharmony_ci			return -EINVAL;
130462306a36Sopenharmony_ci		}
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	/*
130862306a36Sopenharmony_ci	 * If ITE happens, all pending wait_desc commands are aborted.
130962306a36Sopenharmony_ci	 * No new descriptors are fetched until the ITE is cleared.
131062306a36Sopenharmony_ci	 */
131162306a36Sopenharmony_ci	if (fault & DMA_FSTS_ITE) {
131262306a36Sopenharmony_ci		head = readl(iommu->reg + DMAR_IQH_REG);
131362306a36Sopenharmony_ci		head = ((head >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
131462306a36Sopenharmony_ci		head |= 1;
131562306a36Sopenharmony_ci		tail = readl(iommu->reg + DMAR_IQT_REG);
131662306a36Sopenharmony_ci		tail = ((tail >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci		writel(DMA_FSTS_ITE, iommu->reg + DMAR_FSTS_REG);
131962306a36Sopenharmony_ci		pr_info("Invalidation Time-out Error (ITE) cleared\n");
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci		do {
132262306a36Sopenharmony_ci			if (qi->desc_status[head] == QI_IN_USE)
132362306a36Sopenharmony_ci				qi->desc_status[head] = QI_ABORT;
132462306a36Sopenharmony_ci			head = (head - 2 + QI_LENGTH) % QI_LENGTH;
132562306a36Sopenharmony_ci		} while (head != tail);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci		if (qi->desc_status[wait_index] == QI_ABORT)
132862306a36Sopenharmony_ci			return -EAGAIN;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	if (fault & DMA_FSTS_ICE) {
133262306a36Sopenharmony_ci		writel(DMA_FSTS_ICE, iommu->reg + DMAR_FSTS_REG);
133362306a36Sopenharmony_ci		pr_info("Invalidation Completion Error (ICE) cleared\n");
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	return 0;
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci/*
134062306a36Sopenharmony_ci * Function to submit invalidation descriptors of all types to the queued
134162306a36Sopenharmony_ci * invalidation interface(QI). Multiple descriptors can be submitted at a
134262306a36Sopenharmony_ci * time, a wait descriptor will be appended to each submission to ensure
134362306a36Sopenharmony_ci * hardware has completed the invalidation before return. Wait descriptors
134462306a36Sopenharmony_ci * can be part of the submission but it will not be polled for completion.
134562306a36Sopenharmony_ci */
134662306a36Sopenharmony_ciint qi_submit_sync(struct intel_iommu *iommu, struct qi_desc *desc,
134762306a36Sopenharmony_ci		   unsigned int count, unsigned long options)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct q_inval *qi = iommu->qi;
135062306a36Sopenharmony_ci	s64 devtlb_start_ktime = 0;
135162306a36Sopenharmony_ci	s64 iotlb_start_ktime = 0;
135262306a36Sopenharmony_ci	s64 iec_start_ktime = 0;
135362306a36Sopenharmony_ci	struct qi_desc wait_desc;
135462306a36Sopenharmony_ci	int wait_index, index;
135562306a36Sopenharmony_ci	unsigned long flags;
135662306a36Sopenharmony_ci	int offset, shift;
135762306a36Sopenharmony_ci	int rc, i;
135862306a36Sopenharmony_ci	u64 type;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	if (!qi)
136162306a36Sopenharmony_ci		return 0;
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	type = desc->qw0 & GENMASK_ULL(3, 0);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	if ((type == QI_IOTLB_TYPE || type == QI_EIOTLB_TYPE) &&
136662306a36Sopenharmony_ci	    dmar_latency_enabled(iommu, DMAR_LATENCY_INV_IOTLB))
136762306a36Sopenharmony_ci		iotlb_start_ktime = ktime_to_ns(ktime_get());
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	if ((type == QI_DIOTLB_TYPE || type == QI_DEIOTLB_TYPE) &&
137062306a36Sopenharmony_ci	    dmar_latency_enabled(iommu, DMAR_LATENCY_INV_DEVTLB))
137162306a36Sopenharmony_ci		devtlb_start_ktime = ktime_to_ns(ktime_get());
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (type == QI_IEC_TYPE &&
137462306a36Sopenharmony_ci	    dmar_latency_enabled(iommu, DMAR_LATENCY_INV_IEC))
137562306a36Sopenharmony_ci		iec_start_ktime = ktime_to_ns(ktime_get());
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_cirestart:
137862306a36Sopenharmony_ci	rc = 0;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&qi->q_lock, flags);
138162306a36Sopenharmony_ci	/*
138262306a36Sopenharmony_ci	 * Check if we have enough empty slots in the queue to submit,
138362306a36Sopenharmony_ci	 * the calculation is based on:
138462306a36Sopenharmony_ci	 * # of desc + 1 wait desc + 1 space between head and tail
138562306a36Sopenharmony_ci	 */
138662306a36Sopenharmony_ci	while (qi->free_cnt < count + 2) {
138762306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&qi->q_lock, flags);
138862306a36Sopenharmony_ci		cpu_relax();
138962306a36Sopenharmony_ci		raw_spin_lock_irqsave(&qi->q_lock, flags);
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	index = qi->free_head;
139362306a36Sopenharmony_ci	wait_index = (index + count) % QI_LENGTH;
139462306a36Sopenharmony_ci	shift = qi_shift(iommu);
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
139762306a36Sopenharmony_ci		offset = ((index + i) % QI_LENGTH) << shift;
139862306a36Sopenharmony_ci		memcpy(qi->desc + offset, &desc[i], 1 << shift);
139962306a36Sopenharmony_ci		qi->desc_status[(index + i) % QI_LENGTH] = QI_IN_USE;
140062306a36Sopenharmony_ci		trace_qi_submit(iommu, desc[i].qw0, desc[i].qw1,
140162306a36Sopenharmony_ci				desc[i].qw2, desc[i].qw3);
140262306a36Sopenharmony_ci	}
140362306a36Sopenharmony_ci	qi->desc_status[wait_index] = QI_IN_USE;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	wait_desc.qw0 = QI_IWD_STATUS_DATA(QI_DONE) |
140662306a36Sopenharmony_ci			QI_IWD_STATUS_WRITE | QI_IWD_TYPE;
140762306a36Sopenharmony_ci	if (options & QI_OPT_WAIT_DRAIN)
140862306a36Sopenharmony_ci		wait_desc.qw0 |= QI_IWD_PRQ_DRAIN;
140962306a36Sopenharmony_ci	wait_desc.qw1 = virt_to_phys(&qi->desc_status[wait_index]);
141062306a36Sopenharmony_ci	wait_desc.qw2 = 0;
141162306a36Sopenharmony_ci	wait_desc.qw3 = 0;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	offset = wait_index << shift;
141462306a36Sopenharmony_ci	memcpy(qi->desc + offset, &wait_desc, 1 << shift);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	qi->free_head = (qi->free_head + count + 1) % QI_LENGTH;
141762306a36Sopenharmony_ci	qi->free_cnt -= count + 1;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	/*
142062306a36Sopenharmony_ci	 * update the HW tail register indicating the presence of
142162306a36Sopenharmony_ci	 * new descriptors.
142262306a36Sopenharmony_ci	 */
142362306a36Sopenharmony_ci	writel(qi->free_head << shift, iommu->reg + DMAR_IQT_REG);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	while (qi->desc_status[wait_index] != QI_DONE) {
142662306a36Sopenharmony_ci		/*
142762306a36Sopenharmony_ci		 * We will leave the interrupts disabled, to prevent interrupt
142862306a36Sopenharmony_ci		 * context to queue another cmd while a cmd is already submitted
142962306a36Sopenharmony_ci		 * and waiting for completion on this cpu. This is to avoid
143062306a36Sopenharmony_ci		 * a deadlock where the interrupt context can wait indefinitely
143162306a36Sopenharmony_ci		 * for free slots in the queue.
143262306a36Sopenharmony_ci		 */
143362306a36Sopenharmony_ci		rc = qi_check_fault(iommu, index, wait_index);
143462306a36Sopenharmony_ci		if (rc)
143562306a36Sopenharmony_ci			break;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci		raw_spin_unlock(&qi->q_lock);
143862306a36Sopenharmony_ci		cpu_relax();
143962306a36Sopenharmony_ci		raw_spin_lock(&qi->q_lock);
144062306a36Sopenharmony_ci	}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	for (i = 0; i < count; i++)
144362306a36Sopenharmony_ci		qi->desc_status[(index + i) % QI_LENGTH] = QI_DONE;
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	reclaim_free_desc(qi);
144662306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&qi->q_lock, flags);
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	if (rc == -EAGAIN)
144962306a36Sopenharmony_ci		goto restart;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	if (iotlb_start_ktime)
145262306a36Sopenharmony_ci		dmar_latency_update(iommu, DMAR_LATENCY_INV_IOTLB,
145362306a36Sopenharmony_ci				ktime_to_ns(ktime_get()) - iotlb_start_ktime);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	if (devtlb_start_ktime)
145662306a36Sopenharmony_ci		dmar_latency_update(iommu, DMAR_LATENCY_INV_DEVTLB,
145762306a36Sopenharmony_ci				ktime_to_ns(ktime_get()) - devtlb_start_ktime);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	if (iec_start_ktime)
146062306a36Sopenharmony_ci		dmar_latency_update(iommu, DMAR_LATENCY_INV_IEC,
146162306a36Sopenharmony_ci				ktime_to_ns(ktime_get()) - iec_start_ktime);
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	return rc;
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci/*
146762306a36Sopenharmony_ci * Flush the global interrupt entry cache.
146862306a36Sopenharmony_ci */
146962306a36Sopenharmony_civoid qi_global_iec(struct intel_iommu *iommu)
147062306a36Sopenharmony_ci{
147162306a36Sopenharmony_ci	struct qi_desc desc;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	desc.qw0 = QI_IEC_TYPE;
147462306a36Sopenharmony_ci	desc.qw1 = 0;
147562306a36Sopenharmony_ci	desc.qw2 = 0;
147662306a36Sopenharmony_ci	desc.qw3 = 0;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	/* should never fail */
147962306a36Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
148062306a36Sopenharmony_ci}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_civoid qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm,
148362306a36Sopenharmony_ci		      u64 type)
148462306a36Sopenharmony_ci{
148562306a36Sopenharmony_ci	struct qi_desc desc;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	desc.qw0 = QI_CC_FM(fm) | QI_CC_SID(sid) | QI_CC_DID(did)
148862306a36Sopenharmony_ci			| QI_CC_GRAN(type) | QI_CC_TYPE;
148962306a36Sopenharmony_ci	desc.qw1 = 0;
149062306a36Sopenharmony_ci	desc.qw2 = 0;
149162306a36Sopenharmony_ci	desc.qw3 = 0;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_civoid qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr,
149762306a36Sopenharmony_ci		    unsigned int size_order, u64 type)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	u8 dw = 0, dr = 0;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	struct qi_desc desc;
150262306a36Sopenharmony_ci	int ih = 0;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	if (cap_write_drain(iommu->cap))
150562306a36Sopenharmony_ci		dw = 1;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	if (cap_read_drain(iommu->cap))
150862306a36Sopenharmony_ci		dr = 1;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	desc.qw0 = QI_IOTLB_DID(did) | QI_IOTLB_DR(dr) | QI_IOTLB_DW(dw)
151162306a36Sopenharmony_ci		| QI_IOTLB_GRAN(type) | QI_IOTLB_TYPE;
151262306a36Sopenharmony_ci	desc.qw1 = QI_IOTLB_ADDR(addr) | QI_IOTLB_IH(ih)
151362306a36Sopenharmony_ci		| QI_IOTLB_AM(size_order);
151462306a36Sopenharmony_ci	desc.qw2 = 0;
151562306a36Sopenharmony_ci	desc.qw3 = 0;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_civoid qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 pfsid,
152162306a36Sopenharmony_ci			u16 qdep, u64 addr, unsigned mask)
152262306a36Sopenharmony_ci{
152362306a36Sopenharmony_ci	struct qi_desc desc;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	/*
152662306a36Sopenharmony_ci	 * VT-d spec, section 4.3:
152762306a36Sopenharmony_ci	 *
152862306a36Sopenharmony_ci	 * Software is recommended to not submit any Device-TLB invalidation
152962306a36Sopenharmony_ci	 * requests while address remapping hardware is disabled.
153062306a36Sopenharmony_ci	 */
153162306a36Sopenharmony_ci	if (!(iommu->gcmd & DMA_GCMD_TE))
153262306a36Sopenharmony_ci		return;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	if (mask) {
153562306a36Sopenharmony_ci		addr |= (1ULL << (VTD_PAGE_SHIFT + mask - 1)) - 1;
153662306a36Sopenharmony_ci		desc.qw1 = QI_DEV_IOTLB_ADDR(addr) | QI_DEV_IOTLB_SIZE;
153762306a36Sopenharmony_ci	} else
153862306a36Sopenharmony_ci		desc.qw1 = QI_DEV_IOTLB_ADDR(addr);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	if (qdep >= QI_DEV_IOTLB_MAX_INVS)
154162306a36Sopenharmony_ci		qdep = 0;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	desc.qw0 = QI_DEV_IOTLB_SID(sid) | QI_DEV_IOTLB_QDEP(qdep) |
154462306a36Sopenharmony_ci		   QI_DIOTLB_TYPE | QI_DEV_IOTLB_PFSID(pfsid);
154562306a36Sopenharmony_ci	desc.qw2 = 0;
154662306a36Sopenharmony_ci	desc.qw3 = 0;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
154962306a36Sopenharmony_ci}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci/* PASID-based IOTLB invalidation */
155262306a36Sopenharmony_civoid qi_flush_piotlb(struct intel_iommu *iommu, u16 did, u32 pasid, u64 addr,
155362306a36Sopenharmony_ci		     unsigned long npages, bool ih)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	struct qi_desc desc = {.qw2 = 0, .qw3 = 0};
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	/*
155862306a36Sopenharmony_ci	 * npages == -1 means a PASID-selective invalidation, otherwise,
155962306a36Sopenharmony_ci	 * a positive value for Page-selective-within-PASID invalidation.
156062306a36Sopenharmony_ci	 * 0 is not a valid input.
156162306a36Sopenharmony_ci	 */
156262306a36Sopenharmony_ci	if (WARN_ON(!npages)) {
156362306a36Sopenharmony_ci		pr_err("Invalid input npages = %ld\n", npages);
156462306a36Sopenharmony_ci		return;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	if (npages == -1) {
156862306a36Sopenharmony_ci		desc.qw0 = QI_EIOTLB_PASID(pasid) |
156962306a36Sopenharmony_ci				QI_EIOTLB_DID(did) |
157062306a36Sopenharmony_ci				QI_EIOTLB_GRAN(QI_GRAN_NONG_PASID) |
157162306a36Sopenharmony_ci				QI_EIOTLB_TYPE;
157262306a36Sopenharmony_ci		desc.qw1 = 0;
157362306a36Sopenharmony_ci	} else {
157462306a36Sopenharmony_ci		int mask = ilog2(__roundup_pow_of_two(npages));
157562306a36Sopenharmony_ci		unsigned long align = (1ULL << (VTD_PAGE_SHIFT + mask));
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci		if (WARN_ON_ONCE(!IS_ALIGNED(addr, align)))
157862306a36Sopenharmony_ci			addr = ALIGN_DOWN(addr, align);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci		desc.qw0 = QI_EIOTLB_PASID(pasid) |
158162306a36Sopenharmony_ci				QI_EIOTLB_DID(did) |
158262306a36Sopenharmony_ci				QI_EIOTLB_GRAN(QI_GRAN_PSI_PASID) |
158362306a36Sopenharmony_ci				QI_EIOTLB_TYPE;
158462306a36Sopenharmony_ci		desc.qw1 = QI_EIOTLB_ADDR(addr) |
158562306a36Sopenharmony_ci				QI_EIOTLB_IH(ih) |
158662306a36Sopenharmony_ci				QI_EIOTLB_AM(mask);
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
159062306a36Sopenharmony_ci}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci/* PASID-based device IOTLB Invalidate */
159362306a36Sopenharmony_civoid qi_flush_dev_iotlb_pasid(struct intel_iommu *iommu, u16 sid, u16 pfsid,
159462306a36Sopenharmony_ci			      u32 pasid,  u16 qdep, u64 addr, unsigned int size_order)
159562306a36Sopenharmony_ci{
159662306a36Sopenharmony_ci	unsigned long mask = 1UL << (VTD_PAGE_SHIFT + size_order - 1);
159762306a36Sopenharmony_ci	struct qi_desc desc = {.qw1 = 0, .qw2 = 0, .qw3 = 0};
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	/*
160062306a36Sopenharmony_ci	 * VT-d spec, section 4.3:
160162306a36Sopenharmony_ci	 *
160262306a36Sopenharmony_ci	 * Software is recommended to not submit any Device-TLB invalidation
160362306a36Sopenharmony_ci	 * requests while address remapping hardware is disabled.
160462306a36Sopenharmony_ci	 */
160562306a36Sopenharmony_ci	if (!(iommu->gcmd & DMA_GCMD_TE))
160662306a36Sopenharmony_ci		return;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	desc.qw0 = QI_DEV_EIOTLB_PASID(pasid) | QI_DEV_EIOTLB_SID(sid) |
160962306a36Sopenharmony_ci		QI_DEV_EIOTLB_QDEP(qdep) | QI_DEIOTLB_TYPE |
161062306a36Sopenharmony_ci		QI_DEV_IOTLB_PFSID(pfsid);
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	/*
161362306a36Sopenharmony_ci	 * If S bit is 0, we only flush a single page. If S bit is set,
161462306a36Sopenharmony_ci	 * The least significant zero bit indicates the invalidation address
161562306a36Sopenharmony_ci	 * range. VT-d spec 6.5.2.6.
161662306a36Sopenharmony_ci	 * e.g. address bit 12[0] indicates 8KB, 13[0] indicates 16KB.
161762306a36Sopenharmony_ci	 * size order = 0 is PAGE_SIZE 4KB
161862306a36Sopenharmony_ci	 * Max Invs Pending (MIP) is set to 0 for now until we have DIT in
161962306a36Sopenharmony_ci	 * ECAP.
162062306a36Sopenharmony_ci	 */
162162306a36Sopenharmony_ci	if (!IS_ALIGNED(addr, VTD_PAGE_SIZE << size_order))
162262306a36Sopenharmony_ci		pr_warn_ratelimited("Invalidate non-aligned address %llx, order %d\n",
162362306a36Sopenharmony_ci				    addr, size_order);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	/* Take page address */
162662306a36Sopenharmony_ci	desc.qw1 = QI_DEV_EIOTLB_ADDR(addr);
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	if (size_order) {
162962306a36Sopenharmony_ci		/*
163062306a36Sopenharmony_ci		 * Existing 0s in address below size_order may be the least
163162306a36Sopenharmony_ci		 * significant bit, we must set them to 1s to avoid having
163262306a36Sopenharmony_ci		 * smaller size than desired.
163362306a36Sopenharmony_ci		 */
163462306a36Sopenharmony_ci		desc.qw1 |= GENMASK_ULL(size_order + VTD_PAGE_SHIFT - 1,
163562306a36Sopenharmony_ci					VTD_PAGE_SHIFT);
163662306a36Sopenharmony_ci		/* Clear size_order bit to indicate size */
163762306a36Sopenharmony_ci		desc.qw1 &= ~mask;
163862306a36Sopenharmony_ci		/* Set the S bit to indicate flushing more than 1 page */
163962306a36Sopenharmony_ci		desc.qw1 |= QI_DEV_EIOTLB_SIZE;
164062306a36Sopenharmony_ci	}
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
164362306a36Sopenharmony_ci}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_civoid qi_flush_pasid_cache(struct intel_iommu *iommu, u16 did,
164662306a36Sopenharmony_ci			  u64 granu, u32 pasid)
164762306a36Sopenharmony_ci{
164862306a36Sopenharmony_ci	struct qi_desc desc = {.qw1 = 0, .qw2 = 0, .qw3 = 0};
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	desc.qw0 = QI_PC_PASID(pasid) | QI_PC_DID(did) |
165162306a36Sopenharmony_ci			QI_PC_GRAN(granu) | QI_PC_TYPE;
165262306a36Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
165362306a36Sopenharmony_ci}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci/*
165662306a36Sopenharmony_ci * Disable Queued Invalidation interface.
165762306a36Sopenharmony_ci */
165862306a36Sopenharmony_civoid dmar_disable_qi(struct intel_iommu *iommu)
165962306a36Sopenharmony_ci{
166062306a36Sopenharmony_ci	unsigned long flags;
166162306a36Sopenharmony_ci	u32 sts;
166262306a36Sopenharmony_ci	cycles_t start_time = get_cycles();
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	if (!ecap_qis(iommu->ecap))
166562306a36Sopenharmony_ci		return;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flags);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	sts =  readl(iommu->reg + DMAR_GSTS_REG);
167062306a36Sopenharmony_ci	if (!(sts & DMA_GSTS_QIES))
167162306a36Sopenharmony_ci		goto end;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	/*
167462306a36Sopenharmony_ci	 * Give a chance to HW to complete the pending invalidation requests.
167562306a36Sopenharmony_ci	 */
167662306a36Sopenharmony_ci	while ((readl(iommu->reg + DMAR_IQT_REG) !=
167762306a36Sopenharmony_ci		readl(iommu->reg + DMAR_IQH_REG)) &&
167862306a36Sopenharmony_ci		(DMAR_OPERATION_TIMEOUT > (get_cycles() - start_time)))
167962306a36Sopenharmony_ci		cpu_relax();
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	iommu->gcmd &= ~DMA_GCMD_QIE;
168262306a36Sopenharmony_ci	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl,
168562306a36Sopenharmony_ci		      !(sts & DMA_GSTS_QIES), sts);
168662306a36Sopenharmony_ciend:
168762306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
168862306a36Sopenharmony_ci}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci/*
169162306a36Sopenharmony_ci * Enable queued invalidation.
169262306a36Sopenharmony_ci */
169362306a36Sopenharmony_cistatic void __dmar_enable_qi(struct intel_iommu *iommu)
169462306a36Sopenharmony_ci{
169562306a36Sopenharmony_ci	u32 sts;
169662306a36Sopenharmony_ci	unsigned long flags;
169762306a36Sopenharmony_ci	struct q_inval *qi = iommu->qi;
169862306a36Sopenharmony_ci	u64 val = virt_to_phys(qi->desc);
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	qi->free_head = qi->free_tail = 0;
170162306a36Sopenharmony_ci	qi->free_cnt = QI_LENGTH;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	/*
170462306a36Sopenharmony_ci	 * Set DW=1 and QS=1 in IQA_REG when Scalable Mode capability
170562306a36Sopenharmony_ci	 * is present.
170662306a36Sopenharmony_ci	 */
170762306a36Sopenharmony_ci	if (ecap_smts(iommu->ecap))
170862306a36Sopenharmony_ci		val |= BIT_ULL(11) | BIT_ULL(0);
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flags);
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	/* write zero to the tail reg */
171362306a36Sopenharmony_ci	writel(0, iommu->reg + DMAR_IQT_REG);
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	dmar_writeq(iommu->reg + DMAR_IQA_REG, val);
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	iommu->gcmd |= DMA_GCMD_QIE;
171862306a36Sopenharmony_ci	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	/* Make sure hardware complete it */
172162306a36Sopenharmony_ci	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts);
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
172462306a36Sopenharmony_ci}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci/*
172762306a36Sopenharmony_ci * Enable Queued Invalidation interface. This is a must to support
172862306a36Sopenharmony_ci * interrupt-remapping. Also used by DMA-remapping, which replaces
172962306a36Sopenharmony_ci * register based IOTLB invalidation.
173062306a36Sopenharmony_ci */
173162306a36Sopenharmony_ciint dmar_enable_qi(struct intel_iommu *iommu)
173262306a36Sopenharmony_ci{
173362306a36Sopenharmony_ci	struct q_inval *qi;
173462306a36Sopenharmony_ci	struct page *desc_page;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	if (!ecap_qis(iommu->ecap))
173762306a36Sopenharmony_ci		return -ENOENT;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	/*
174062306a36Sopenharmony_ci	 * queued invalidation is already setup and enabled.
174162306a36Sopenharmony_ci	 */
174262306a36Sopenharmony_ci	if (iommu->qi)
174362306a36Sopenharmony_ci		return 0;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	iommu->qi = kmalloc(sizeof(*qi), GFP_ATOMIC);
174662306a36Sopenharmony_ci	if (!iommu->qi)
174762306a36Sopenharmony_ci		return -ENOMEM;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	qi = iommu->qi;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	/*
175262306a36Sopenharmony_ci	 * Need two pages to accommodate 256 descriptors of 256 bits each
175362306a36Sopenharmony_ci	 * if the remapping hardware supports scalable mode translation.
175462306a36Sopenharmony_ci	 */
175562306a36Sopenharmony_ci	desc_page = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO,
175662306a36Sopenharmony_ci				     !!ecap_smts(iommu->ecap));
175762306a36Sopenharmony_ci	if (!desc_page) {
175862306a36Sopenharmony_ci		kfree(qi);
175962306a36Sopenharmony_ci		iommu->qi = NULL;
176062306a36Sopenharmony_ci		return -ENOMEM;
176162306a36Sopenharmony_ci	}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	qi->desc = page_address(desc_page);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	qi->desc_status = kcalloc(QI_LENGTH, sizeof(int), GFP_ATOMIC);
176662306a36Sopenharmony_ci	if (!qi->desc_status) {
176762306a36Sopenharmony_ci		free_page((unsigned long) qi->desc);
176862306a36Sopenharmony_ci		kfree(qi);
176962306a36Sopenharmony_ci		iommu->qi = NULL;
177062306a36Sopenharmony_ci		return -ENOMEM;
177162306a36Sopenharmony_ci	}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	raw_spin_lock_init(&qi->q_lock);
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	__dmar_enable_qi(iommu);
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	return 0;
177862306a36Sopenharmony_ci}
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci/* iommu interrupt handling. Most stuff are MSI-like. */
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_cienum faulttype {
178362306a36Sopenharmony_ci	DMA_REMAP,
178462306a36Sopenharmony_ci	INTR_REMAP,
178562306a36Sopenharmony_ci	UNKNOWN,
178662306a36Sopenharmony_ci};
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_cistatic const char *dma_remap_fault_reasons[] =
178962306a36Sopenharmony_ci{
179062306a36Sopenharmony_ci	"Software",
179162306a36Sopenharmony_ci	"Present bit in root entry is clear",
179262306a36Sopenharmony_ci	"Present bit in context entry is clear",
179362306a36Sopenharmony_ci	"Invalid context entry",
179462306a36Sopenharmony_ci	"Access beyond MGAW",
179562306a36Sopenharmony_ci	"PTE Write access is not set",
179662306a36Sopenharmony_ci	"PTE Read access is not set",
179762306a36Sopenharmony_ci	"Next page table ptr is invalid",
179862306a36Sopenharmony_ci	"Root table address invalid",
179962306a36Sopenharmony_ci	"Context table ptr is invalid",
180062306a36Sopenharmony_ci	"non-zero reserved fields in RTP",
180162306a36Sopenharmony_ci	"non-zero reserved fields in CTP",
180262306a36Sopenharmony_ci	"non-zero reserved fields in PTE",
180362306a36Sopenharmony_ci	"PCE for translation request specifies blocking",
180462306a36Sopenharmony_ci};
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_cistatic const char * const dma_remap_sm_fault_reasons[] = {
180762306a36Sopenharmony_ci	"SM: Invalid Root Table Address",
180862306a36Sopenharmony_ci	"SM: TTM 0 for request with PASID",
180962306a36Sopenharmony_ci	"SM: TTM 0 for page group request",
181062306a36Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x33-0x37 */
181162306a36Sopenharmony_ci	"SM: Error attempting to access Root Entry",
181262306a36Sopenharmony_ci	"SM: Present bit in Root Entry is clear",
181362306a36Sopenharmony_ci	"SM: Non-zero reserved field set in Root Entry",
181462306a36Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x3B-0x3F */
181562306a36Sopenharmony_ci	"SM: Error attempting to access Context Entry",
181662306a36Sopenharmony_ci	"SM: Present bit in Context Entry is clear",
181762306a36Sopenharmony_ci	"SM: Non-zero reserved field set in the Context Entry",
181862306a36Sopenharmony_ci	"SM: Invalid Context Entry",
181962306a36Sopenharmony_ci	"SM: DTE field in Context Entry is clear",
182062306a36Sopenharmony_ci	"SM: PASID Enable field in Context Entry is clear",
182162306a36Sopenharmony_ci	"SM: PASID is larger than the max in Context Entry",
182262306a36Sopenharmony_ci	"SM: PRE field in Context-Entry is clear",
182362306a36Sopenharmony_ci	"SM: RID_PASID field error in Context-Entry",
182462306a36Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x49-0x4F */
182562306a36Sopenharmony_ci	"SM: Error attempting to access the PASID Directory Entry",
182662306a36Sopenharmony_ci	"SM: Present bit in Directory Entry is clear",
182762306a36Sopenharmony_ci	"SM: Non-zero reserved field set in PASID Directory Entry",
182862306a36Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x53-0x57 */
182962306a36Sopenharmony_ci	"SM: Error attempting to access PASID Table Entry",
183062306a36Sopenharmony_ci	"SM: Present bit in PASID Table Entry is clear",
183162306a36Sopenharmony_ci	"SM: Non-zero reserved field set in PASID Table Entry",
183262306a36Sopenharmony_ci	"SM: Invalid Scalable-Mode PASID Table Entry",
183362306a36Sopenharmony_ci	"SM: ERE field is clear in PASID Table Entry",
183462306a36Sopenharmony_ci	"SM: SRE field is clear in PASID Table Entry",
183562306a36Sopenharmony_ci	"Unknown", "Unknown",/* 0x5E-0x5F */
183662306a36Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x60-0x67 */
183762306a36Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x68-0x6F */
183862306a36Sopenharmony_ci	"SM: Error attempting to access first-level paging entry",
183962306a36Sopenharmony_ci	"SM: Present bit in first-level paging entry is clear",
184062306a36Sopenharmony_ci	"SM: Non-zero reserved field set in first-level paging entry",
184162306a36Sopenharmony_ci	"SM: Error attempting to access FL-PML4 entry",
184262306a36Sopenharmony_ci	"SM: First-level entry address beyond MGAW in Nested translation",
184362306a36Sopenharmony_ci	"SM: Read permission error in FL-PML4 entry in Nested translation",
184462306a36Sopenharmony_ci	"SM: Read permission error in first-level paging entry in Nested translation",
184562306a36Sopenharmony_ci	"SM: Write permission error in first-level paging entry in Nested translation",
184662306a36Sopenharmony_ci	"SM: Error attempting to access second-level paging entry",
184762306a36Sopenharmony_ci	"SM: Read/Write permission error in second-level paging entry",
184862306a36Sopenharmony_ci	"SM: Non-zero reserved field set in second-level paging entry",
184962306a36Sopenharmony_ci	"SM: Invalid second-level page table pointer",
185062306a36Sopenharmony_ci	"SM: A/D bit update needed in second-level entry when set up in no snoop",
185162306a36Sopenharmony_ci	"Unknown", "Unknown", "Unknown", /* 0x7D-0x7F */
185262306a36Sopenharmony_ci	"SM: Address in first-level translation is not canonical",
185362306a36Sopenharmony_ci	"SM: U/S set 0 for first-level translation with user privilege",
185462306a36Sopenharmony_ci	"SM: No execute permission for request with PASID and ER=1",
185562306a36Sopenharmony_ci	"SM: Address beyond the DMA hardware max",
185662306a36Sopenharmony_ci	"SM: Second-level entry address beyond the max",
185762306a36Sopenharmony_ci	"SM: No write permission for Write/AtomicOp request",
185862306a36Sopenharmony_ci	"SM: No read permission for Read/AtomicOp request",
185962306a36Sopenharmony_ci	"SM: Invalid address-interrupt address",
186062306a36Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x88-0x8F */
186162306a36Sopenharmony_ci	"SM: A/D bit update needed in first-level entry when set up in no snoop",
186262306a36Sopenharmony_ci};
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_cistatic const char *irq_remap_fault_reasons[] =
186562306a36Sopenharmony_ci{
186662306a36Sopenharmony_ci	"Detected reserved fields in the decoded interrupt-remapped request",
186762306a36Sopenharmony_ci	"Interrupt index exceeded the interrupt-remapping table size",
186862306a36Sopenharmony_ci	"Present field in the IRTE entry is clear",
186962306a36Sopenharmony_ci	"Error accessing interrupt-remapping table pointed by IRTA_REG",
187062306a36Sopenharmony_ci	"Detected reserved fields in the IRTE entry",
187162306a36Sopenharmony_ci	"Blocked a compatibility format interrupt request",
187262306a36Sopenharmony_ci	"Blocked an interrupt request due to source-id verification failure",
187362306a36Sopenharmony_ci};
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_cistatic const char *dmar_get_fault_reason(u8 fault_reason, int *fault_type)
187662306a36Sopenharmony_ci{
187762306a36Sopenharmony_ci	if (fault_reason >= 0x20 && (fault_reason - 0x20 <
187862306a36Sopenharmony_ci					ARRAY_SIZE(irq_remap_fault_reasons))) {
187962306a36Sopenharmony_ci		*fault_type = INTR_REMAP;
188062306a36Sopenharmony_ci		return irq_remap_fault_reasons[fault_reason - 0x20];
188162306a36Sopenharmony_ci	} else if (fault_reason >= 0x30 && (fault_reason - 0x30 <
188262306a36Sopenharmony_ci			ARRAY_SIZE(dma_remap_sm_fault_reasons))) {
188362306a36Sopenharmony_ci		*fault_type = DMA_REMAP;
188462306a36Sopenharmony_ci		return dma_remap_sm_fault_reasons[fault_reason - 0x30];
188562306a36Sopenharmony_ci	} else if (fault_reason < ARRAY_SIZE(dma_remap_fault_reasons)) {
188662306a36Sopenharmony_ci		*fault_type = DMA_REMAP;
188762306a36Sopenharmony_ci		return dma_remap_fault_reasons[fault_reason];
188862306a36Sopenharmony_ci	} else {
188962306a36Sopenharmony_ci		*fault_type = UNKNOWN;
189062306a36Sopenharmony_ci		return "Unknown";
189162306a36Sopenharmony_ci	}
189262306a36Sopenharmony_ci}
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_cistatic inline int dmar_msi_reg(struct intel_iommu *iommu, int irq)
189662306a36Sopenharmony_ci{
189762306a36Sopenharmony_ci	if (iommu->irq == irq)
189862306a36Sopenharmony_ci		return DMAR_FECTL_REG;
189962306a36Sopenharmony_ci	else if (iommu->pr_irq == irq)
190062306a36Sopenharmony_ci		return DMAR_PECTL_REG;
190162306a36Sopenharmony_ci	else if (iommu->perf_irq == irq)
190262306a36Sopenharmony_ci		return DMAR_PERFINTRCTL_REG;
190362306a36Sopenharmony_ci	else
190462306a36Sopenharmony_ci		BUG();
190562306a36Sopenharmony_ci}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_civoid dmar_msi_unmask(struct irq_data *data)
190862306a36Sopenharmony_ci{
190962306a36Sopenharmony_ci	struct intel_iommu *iommu = irq_data_get_irq_handler_data(data);
191062306a36Sopenharmony_ci	int reg = dmar_msi_reg(iommu, data->irq);
191162306a36Sopenharmony_ci	unsigned long flag;
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	/* unmask it */
191462306a36Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flag);
191562306a36Sopenharmony_ci	writel(0, iommu->reg + reg);
191662306a36Sopenharmony_ci	/* Read a reg to force flush the post write */
191762306a36Sopenharmony_ci	readl(iommu->reg + reg);
191862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
191962306a36Sopenharmony_ci}
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_civoid dmar_msi_mask(struct irq_data *data)
192262306a36Sopenharmony_ci{
192362306a36Sopenharmony_ci	struct intel_iommu *iommu = irq_data_get_irq_handler_data(data);
192462306a36Sopenharmony_ci	int reg = dmar_msi_reg(iommu, data->irq);
192562306a36Sopenharmony_ci	unsigned long flag;
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	/* mask it */
192862306a36Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flag);
192962306a36Sopenharmony_ci	writel(DMA_FECTL_IM, iommu->reg + reg);
193062306a36Sopenharmony_ci	/* Read a reg to force flush the post write */
193162306a36Sopenharmony_ci	readl(iommu->reg + reg);
193262306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
193362306a36Sopenharmony_ci}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_civoid dmar_msi_write(int irq, struct msi_msg *msg)
193662306a36Sopenharmony_ci{
193762306a36Sopenharmony_ci	struct intel_iommu *iommu = irq_get_handler_data(irq);
193862306a36Sopenharmony_ci	int reg = dmar_msi_reg(iommu, irq);
193962306a36Sopenharmony_ci	unsigned long flag;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flag);
194262306a36Sopenharmony_ci	writel(msg->data, iommu->reg + reg + 4);
194362306a36Sopenharmony_ci	writel(msg->address_lo, iommu->reg + reg + 8);
194462306a36Sopenharmony_ci	writel(msg->address_hi, iommu->reg + reg + 12);
194562306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
194662306a36Sopenharmony_ci}
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_civoid dmar_msi_read(int irq, struct msi_msg *msg)
194962306a36Sopenharmony_ci{
195062306a36Sopenharmony_ci	struct intel_iommu *iommu = irq_get_handler_data(irq);
195162306a36Sopenharmony_ci	int reg = dmar_msi_reg(iommu, irq);
195262306a36Sopenharmony_ci	unsigned long flag;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flag);
195562306a36Sopenharmony_ci	msg->data = readl(iommu->reg + reg + 4);
195662306a36Sopenharmony_ci	msg->address_lo = readl(iommu->reg + reg + 8);
195762306a36Sopenharmony_ci	msg->address_hi = readl(iommu->reg + reg + 12);
195862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
195962306a36Sopenharmony_ci}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_cistatic int dmar_fault_do_one(struct intel_iommu *iommu, int type,
196262306a36Sopenharmony_ci		u8 fault_reason, u32 pasid, u16 source_id,
196362306a36Sopenharmony_ci		unsigned long long addr)
196462306a36Sopenharmony_ci{
196562306a36Sopenharmony_ci	const char *reason;
196662306a36Sopenharmony_ci	int fault_type;
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	reason = dmar_get_fault_reason(fault_reason, &fault_type);
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	if (fault_type == INTR_REMAP) {
197162306a36Sopenharmony_ci		pr_err("[INTR-REMAP] Request device [%02x:%02x.%d] fault index 0x%llx [fault reason 0x%02x] %s\n",
197262306a36Sopenharmony_ci		       source_id >> 8, PCI_SLOT(source_id & 0xFF),
197362306a36Sopenharmony_ci		       PCI_FUNC(source_id & 0xFF), addr >> 48,
197462306a36Sopenharmony_ci		       fault_reason, reason);
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci		return 0;
197762306a36Sopenharmony_ci	}
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	if (pasid == IOMMU_PASID_INVALID)
198062306a36Sopenharmony_ci		pr_err("[%s NO_PASID] Request device [%02x:%02x.%d] fault addr 0x%llx [fault reason 0x%02x] %s\n",
198162306a36Sopenharmony_ci		       type ? "DMA Read" : "DMA Write",
198262306a36Sopenharmony_ci		       source_id >> 8, PCI_SLOT(source_id & 0xFF),
198362306a36Sopenharmony_ci		       PCI_FUNC(source_id & 0xFF), addr,
198462306a36Sopenharmony_ci		       fault_reason, reason);
198562306a36Sopenharmony_ci	else
198662306a36Sopenharmony_ci		pr_err("[%s PASID 0x%x] Request device [%02x:%02x.%d] fault addr 0x%llx [fault reason 0x%02x] %s\n",
198762306a36Sopenharmony_ci		       type ? "DMA Read" : "DMA Write", pasid,
198862306a36Sopenharmony_ci		       source_id >> 8, PCI_SLOT(source_id & 0xFF),
198962306a36Sopenharmony_ci		       PCI_FUNC(source_id & 0xFF), addr,
199062306a36Sopenharmony_ci		       fault_reason, reason);
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	dmar_fault_dump_ptes(iommu, source_id, addr, pasid);
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	return 0;
199562306a36Sopenharmony_ci}
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci#define PRIMARY_FAULT_REG_LEN (16)
199862306a36Sopenharmony_ciirqreturn_t dmar_fault(int irq, void *dev_id)
199962306a36Sopenharmony_ci{
200062306a36Sopenharmony_ci	struct intel_iommu *iommu = dev_id;
200162306a36Sopenharmony_ci	int reg, fault_index;
200262306a36Sopenharmony_ci	u32 fault_status;
200362306a36Sopenharmony_ci	unsigned long flag;
200462306a36Sopenharmony_ci	static DEFINE_RATELIMIT_STATE(rs,
200562306a36Sopenharmony_ci				      DEFAULT_RATELIMIT_INTERVAL,
200662306a36Sopenharmony_ci				      DEFAULT_RATELIMIT_BURST);
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flag);
200962306a36Sopenharmony_ci	fault_status = readl(iommu->reg + DMAR_FSTS_REG);
201062306a36Sopenharmony_ci	if (fault_status && __ratelimit(&rs))
201162306a36Sopenharmony_ci		pr_err("DRHD: handling fault status reg %x\n", fault_status);
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	/* TBD: ignore advanced fault log currently */
201462306a36Sopenharmony_ci	if (!(fault_status & DMA_FSTS_PPF))
201562306a36Sopenharmony_ci		goto unlock_exit;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	fault_index = dma_fsts_fault_record_index(fault_status);
201862306a36Sopenharmony_ci	reg = cap_fault_reg_offset(iommu->cap);
201962306a36Sopenharmony_ci	while (1) {
202062306a36Sopenharmony_ci		/* Disable printing, simply clear the fault when ratelimited */
202162306a36Sopenharmony_ci		bool ratelimited = !__ratelimit(&rs);
202262306a36Sopenharmony_ci		u8 fault_reason;
202362306a36Sopenharmony_ci		u16 source_id;
202462306a36Sopenharmony_ci		u64 guest_addr;
202562306a36Sopenharmony_ci		u32 pasid;
202662306a36Sopenharmony_ci		int type;
202762306a36Sopenharmony_ci		u32 data;
202862306a36Sopenharmony_ci		bool pasid_present;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci		/* highest 32 bits */
203162306a36Sopenharmony_ci		data = readl(iommu->reg + reg +
203262306a36Sopenharmony_ci				fault_index * PRIMARY_FAULT_REG_LEN + 12);
203362306a36Sopenharmony_ci		if (!(data & DMA_FRCD_F))
203462306a36Sopenharmony_ci			break;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci		if (!ratelimited) {
203762306a36Sopenharmony_ci			fault_reason = dma_frcd_fault_reason(data);
203862306a36Sopenharmony_ci			type = dma_frcd_type(data);
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci			pasid = dma_frcd_pasid_value(data);
204162306a36Sopenharmony_ci			data = readl(iommu->reg + reg +
204262306a36Sopenharmony_ci				     fault_index * PRIMARY_FAULT_REG_LEN + 8);
204362306a36Sopenharmony_ci			source_id = dma_frcd_source_id(data);
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci			pasid_present = dma_frcd_pasid_present(data);
204662306a36Sopenharmony_ci			guest_addr = dmar_readq(iommu->reg + reg +
204762306a36Sopenharmony_ci					fault_index * PRIMARY_FAULT_REG_LEN);
204862306a36Sopenharmony_ci			guest_addr = dma_frcd_page_addr(guest_addr);
204962306a36Sopenharmony_ci		}
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci		/* clear the fault */
205262306a36Sopenharmony_ci		writel(DMA_FRCD_F, iommu->reg + reg +
205362306a36Sopenharmony_ci			fault_index * PRIMARY_FAULT_REG_LEN + 12);
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci		if (!ratelimited)
205862306a36Sopenharmony_ci			/* Using pasid -1 if pasid is not present */
205962306a36Sopenharmony_ci			dmar_fault_do_one(iommu, type, fault_reason,
206062306a36Sopenharmony_ci					  pasid_present ? pasid : IOMMU_PASID_INVALID,
206162306a36Sopenharmony_ci					  source_id, guest_addr);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci		fault_index++;
206462306a36Sopenharmony_ci		if (fault_index >= cap_num_fault_regs(iommu->cap))
206562306a36Sopenharmony_ci			fault_index = 0;
206662306a36Sopenharmony_ci		raw_spin_lock_irqsave(&iommu->register_lock, flag);
206762306a36Sopenharmony_ci	}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	writel(DMA_FSTS_PFO | DMA_FSTS_PPF | DMA_FSTS_PRO,
207062306a36Sopenharmony_ci	       iommu->reg + DMAR_FSTS_REG);
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ciunlock_exit:
207362306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
207462306a36Sopenharmony_ci	return IRQ_HANDLED;
207562306a36Sopenharmony_ci}
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ciint dmar_set_interrupt(struct intel_iommu *iommu)
207862306a36Sopenharmony_ci{
207962306a36Sopenharmony_ci	int irq, ret;
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	/*
208262306a36Sopenharmony_ci	 * Check if the fault interrupt is already initialized.
208362306a36Sopenharmony_ci	 */
208462306a36Sopenharmony_ci	if (iommu->irq)
208562306a36Sopenharmony_ci		return 0;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	irq = dmar_alloc_hwirq(iommu->seq_id, iommu->node, iommu);
208862306a36Sopenharmony_ci	if (irq > 0) {
208962306a36Sopenharmony_ci		iommu->irq = irq;
209062306a36Sopenharmony_ci	} else {
209162306a36Sopenharmony_ci		pr_err("No free IRQ vectors\n");
209262306a36Sopenharmony_ci		return -EINVAL;
209362306a36Sopenharmony_ci	}
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	ret = request_irq(irq, dmar_fault, IRQF_NO_THREAD, iommu->name, iommu);
209662306a36Sopenharmony_ci	if (ret)
209762306a36Sopenharmony_ci		pr_err("Can't request irq\n");
209862306a36Sopenharmony_ci	return ret;
209962306a36Sopenharmony_ci}
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ciint __init enable_drhd_fault_handling(void)
210262306a36Sopenharmony_ci{
210362306a36Sopenharmony_ci	struct dmar_drhd_unit *drhd;
210462306a36Sopenharmony_ci	struct intel_iommu *iommu;
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	/*
210762306a36Sopenharmony_ci	 * Enable fault control interrupt.
210862306a36Sopenharmony_ci	 */
210962306a36Sopenharmony_ci	for_each_iommu(iommu, drhd) {
211062306a36Sopenharmony_ci		u32 fault_status;
211162306a36Sopenharmony_ci		int ret = dmar_set_interrupt(iommu);
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci		if (ret) {
211462306a36Sopenharmony_ci			pr_err("DRHD %Lx: failed to enable fault, interrupt, ret %d\n",
211562306a36Sopenharmony_ci			       (unsigned long long)drhd->reg_base_addr, ret);
211662306a36Sopenharmony_ci			return -1;
211762306a36Sopenharmony_ci		}
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci		/*
212062306a36Sopenharmony_ci		 * Clear any previous faults.
212162306a36Sopenharmony_ci		 */
212262306a36Sopenharmony_ci		dmar_fault(iommu->irq, iommu);
212362306a36Sopenharmony_ci		fault_status = readl(iommu->reg + DMAR_FSTS_REG);
212462306a36Sopenharmony_ci		writel(fault_status, iommu->reg + DMAR_FSTS_REG);
212562306a36Sopenharmony_ci	}
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	return 0;
212862306a36Sopenharmony_ci}
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci/*
213162306a36Sopenharmony_ci * Re-enable Queued Invalidation interface.
213262306a36Sopenharmony_ci */
213362306a36Sopenharmony_ciint dmar_reenable_qi(struct intel_iommu *iommu)
213462306a36Sopenharmony_ci{
213562306a36Sopenharmony_ci	if (!ecap_qis(iommu->ecap))
213662306a36Sopenharmony_ci		return -ENOENT;
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci	if (!iommu->qi)
213962306a36Sopenharmony_ci		return -ENOENT;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	/*
214262306a36Sopenharmony_ci	 * First disable queued invalidation.
214362306a36Sopenharmony_ci	 */
214462306a36Sopenharmony_ci	dmar_disable_qi(iommu);
214562306a36Sopenharmony_ci	/*
214662306a36Sopenharmony_ci	 * Then enable queued invalidation again. Since there is no pending
214762306a36Sopenharmony_ci	 * invalidation requests now, it's safe to re-enable queued
214862306a36Sopenharmony_ci	 * invalidation.
214962306a36Sopenharmony_ci	 */
215062306a36Sopenharmony_ci	__dmar_enable_qi(iommu);
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	return 0;
215362306a36Sopenharmony_ci}
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci/*
215662306a36Sopenharmony_ci * Check interrupt remapping support in DMAR table description.
215762306a36Sopenharmony_ci */
215862306a36Sopenharmony_ciint __init dmar_ir_support(void)
215962306a36Sopenharmony_ci{
216062306a36Sopenharmony_ci	struct acpi_table_dmar *dmar;
216162306a36Sopenharmony_ci	dmar = (struct acpi_table_dmar *)dmar_tbl;
216262306a36Sopenharmony_ci	if (!dmar)
216362306a36Sopenharmony_ci		return 0;
216462306a36Sopenharmony_ci	return dmar->flags & 0x1;
216562306a36Sopenharmony_ci}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci/* Check whether DMAR units are in use */
216862306a36Sopenharmony_cistatic inline bool dmar_in_use(void)
216962306a36Sopenharmony_ci{
217062306a36Sopenharmony_ci	return irq_remapping_enabled || intel_iommu_enabled;
217162306a36Sopenharmony_ci}
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_cistatic int __init dmar_free_unused_resources(void)
217462306a36Sopenharmony_ci{
217562306a36Sopenharmony_ci	struct dmar_drhd_unit *dmaru, *dmaru_n;
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	if (dmar_in_use())
217862306a36Sopenharmony_ci		return 0;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	if (dmar_dev_scope_status != 1 && !list_empty(&dmar_drhd_units))
218162306a36Sopenharmony_ci		bus_unregister_notifier(&pci_bus_type, &dmar_pci_bus_nb);
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	down_write(&dmar_global_lock);
218462306a36Sopenharmony_ci	list_for_each_entry_safe(dmaru, dmaru_n, &dmar_drhd_units, list) {
218562306a36Sopenharmony_ci		list_del(&dmaru->list);
218662306a36Sopenharmony_ci		dmar_free_drhd(dmaru);
218762306a36Sopenharmony_ci	}
218862306a36Sopenharmony_ci	up_write(&dmar_global_lock);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	return 0;
219162306a36Sopenharmony_ci}
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_cilate_initcall(dmar_free_unused_resources);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci/*
219662306a36Sopenharmony_ci * DMAR Hotplug Support
219762306a36Sopenharmony_ci * For more details, please refer to Intel(R) Virtualization Technology
219862306a36Sopenharmony_ci * for Directed-IO Architecture Specifiction, Rev 2.2, Section 8.8
219962306a36Sopenharmony_ci * "Remapping Hardware Unit Hot Plug".
220062306a36Sopenharmony_ci */
220162306a36Sopenharmony_cistatic guid_t dmar_hp_guid =
220262306a36Sopenharmony_ci	GUID_INIT(0xD8C1A3A6, 0xBE9B, 0x4C9B,
220362306a36Sopenharmony_ci		  0x91, 0xBF, 0xC3, 0xCB, 0x81, 0xFC, 0x5D, 0xAF);
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci/*
220662306a36Sopenharmony_ci * Currently there's only one revision and BIOS will not check the revision id,
220762306a36Sopenharmony_ci * so use 0 for safety.
220862306a36Sopenharmony_ci */
220962306a36Sopenharmony_ci#define	DMAR_DSM_REV_ID			0
221062306a36Sopenharmony_ci#define	DMAR_DSM_FUNC_DRHD		1
221162306a36Sopenharmony_ci#define	DMAR_DSM_FUNC_ATSR		2
221262306a36Sopenharmony_ci#define	DMAR_DSM_FUNC_RHSA		3
221362306a36Sopenharmony_ci#define	DMAR_DSM_FUNC_SATC		4
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_cistatic inline bool dmar_detect_dsm(acpi_handle handle, int func)
221662306a36Sopenharmony_ci{
221762306a36Sopenharmony_ci	return acpi_check_dsm(handle, &dmar_hp_guid, DMAR_DSM_REV_ID, 1 << func);
221862306a36Sopenharmony_ci}
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_cistatic int dmar_walk_dsm_resource(acpi_handle handle, int func,
222162306a36Sopenharmony_ci				  dmar_res_handler_t handler, void *arg)
222262306a36Sopenharmony_ci{
222362306a36Sopenharmony_ci	int ret = -ENODEV;
222462306a36Sopenharmony_ci	union acpi_object *obj;
222562306a36Sopenharmony_ci	struct acpi_dmar_header *start;
222662306a36Sopenharmony_ci	struct dmar_res_callback callback;
222762306a36Sopenharmony_ci	static int res_type[] = {
222862306a36Sopenharmony_ci		[DMAR_DSM_FUNC_DRHD] = ACPI_DMAR_TYPE_HARDWARE_UNIT,
222962306a36Sopenharmony_ci		[DMAR_DSM_FUNC_ATSR] = ACPI_DMAR_TYPE_ROOT_ATS,
223062306a36Sopenharmony_ci		[DMAR_DSM_FUNC_RHSA] = ACPI_DMAR_TYPE_HARDWARE_AFFINITY,
223162306a36Sopenharmony_ci		[DMAR_DSM_FUNC_SATC] = ACPI_DMAR_TYPE_SATC,
223262306a36Sopenharmony_ci	};
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci	if (!dmar_detect_dsm(handle, func))
223562306a36Sopenharmony_ci		return 0;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	obj = acpi_evaluate_dsm_typed(handle, &dmar_hp_guid, DMAR_DSM_REV_ID,
223862306a36Sopenharmony_ci				      func, NULL, ACPI_TYPE_BUFFER);
223962306a36Sopenharmony_ci	if (!obj)
224062306a36Sopenharmony_ci		return -ENODEV;
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	memset(&callback, 0, sizeof(callback));
224362306a36Sopenharmony_ci	callback.cb[res_type[func]] = handler;
224462306a36Sopenharmony_ci	callback.arg[res_type[func]] = arg;
224562306a36Sopenharmony_ci	start = (struct acpi_dmar_header *)obj->buffer.pointer;
224662306a36Sopenharmony_ci	ret = dmar_walk_remapping_entries(start, obj->buffer.length, &callback);
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	ACPI_FREE(obj);
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	return ret;
225162306a36Sopenharmony_ci}
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_cistatic int dmar_hp_add_drhd(struct acpi_dmar_header *header, void *arg)
225462306a36Sopenharmony_ci{
225562306a36Sopenharmony_ci	int ret;
225662306a36Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci	dmaru = dmar_find_dmaru((struct acpi_dmar_hardware_unit *)header);
225962306a36Sopenharmony_ci	if (!dmaru)
226062306a36Sopenharmony_ci		return -ENODEV;
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	ret = dmar_ir_hotplug(dmaru, true);
226362306a36Sopenharmony_ci	if (ret == 0)
226462306a36Sopenharmony_ci		ret = dmar_iommu_hotplug(dmaru, true);
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	return ret;
226762306a36Sopenharmony_ci}
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_cistatic int dmar_hp_remove_drhd(struct acpi_dmar_header *header, void *arg)
227062306a36Sopenharmony_ci{
227162306a36Sopenharmony_ci	int i, ret;
227262306a36Sopenharmony_ci	struct device *dev;
227362306a36Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	dmaru = dmar_find_dmaru((struct acpi_dmar_hardware_unit *)header);
227662306a36Sopenharmony_ci	if (!dmaru)
227762306a36Sopenharmony_ci		return 0;
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci	/*
228062306a36Sopenharmony_ci	 * All PCI devices managed by this unit should have been destroyed.
228162306a36Sopenharmony_ci	 */
228262306a36Sopenharmony_ci	if (!dmaru->include_all && dmaru->devices && dmaru->devices_cnt) {
228362306a36Sopenharmony_ci		for_each_active_dev_scope(dmaru->devices,
228462306a36Sopenharmony_ci					  dmaru->devices_cnt, i, dev)
228562306a36Sopenharmony_ci			return -EBUSY;
228662306a36Sopenharmony_ci	}
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	ret = dmar_ir_hotplug(dmaru, false);
228962306a36Sopenharmony_ci	if (ret == 0)
229062306a36Sopenharmony_ci		ret = dmar_iommu_hotplug(dmaru, false);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	return ret;
229362306a36Sopenharmony_ci}
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_cistatic int dmar_hp_release_drhd(struct acpi_dmar_header *header, void *arg)
229662306a36Sopenharmony_ci{
229762306a36Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	dmaru = dmar_find_dmaru((struct acpi_dmar_hardware_unit *)header);
230062306a36Sopenharmony_ci	if (dmaru) {
230162306a36Sopenharmony_ci		list_del_rcu(&dmaru->list);
230262306a36Sopenharmony_ci		synchronize_rcu();
230362306a36Sopenharmony_ci		dmar_free_drhd(dmaru);
230462306a36Sopenharmony_ci	}
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci	return 0;
230762306a36Sopenharmony_ci}
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_cistatic int dmar_hotplug_insert(acpi_handle handle)
231062306a36Sopenharmony_ci{
231162306a36Sopenharmony_ci	int ret;
231262306a36Sopenharmony_ci	int drhd_count = 0;
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
231562306a36Sopenharmony_ci				     &dmar_validate_one_drhd, (void *)1);
231662306a36Sopenharmony_ci	if (ret)
231762306a36Sopenharmony_ci		goto out;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
232062306a36Sopenharmony_ci				     &dmar_parse_one_drhd, (void *)&drhd_count);
232162306a36Sopenharmony_ci	if (ret == 0 && drhd_count == 0) {
232262306a36Sopenharmony_ci		pr_warn(FW_BUG "No DRHD structures in buffer returned by _DSM method\n");
232362306a36Sopenharmony_ci		goto out;
232462306a36Sopenharmony_ci	} else if (ret) {
232562306a36Sopenharmony_ci		goto release_drhd;
232662306a36Sopenharmony_ci	}
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_RHSA,
232962306a36Sopenharmony_ci				     &dmar_parse_one_rhsa, NULL);
233062306a36Sopenharmony_ci	if (ret)
233162306a36Sopenharmony_ci		goto release_drhd;
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR,
233462306a36Sopenharmony_ci				     &dmar_parse_one_atsr, NULL);
233562306a36Sopenharmony_ci	if (ret)
233662306a36Sopenharmony_ci		goto release_atsr;
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
233962306a36Sopenharmony_ci				     &dmar_hp_add_drhd, NULL);
234062306a36Sopenharmony_ci	if (!ret)
234162306a36Sopenharmony_ci		return 0;
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
234462306a36Sopenharmony_ci			       &dmar_hp_remove_drhd, NULL);
234562306a36Sopenharmony_cirelease_atsr:
234662306a36Sopenharmony_ci	dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR,
234762306a36Sopenharmony_ci			       &dmar_release_one_atsr, NULL);
234862306a36Sopenharmony_cirelease_drhd:
234962306a36Sopenharmony_ci	dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
235062306a36Sopenharmony_ci			       &dmar_hp_release_drhd, NULL);
235162306a36Sopenharmony_ciout:
235262306a36Sopenharmony_ci	return ret;
235362306a36Sopenharmony_ci}
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_cistatic int dmar_hotplug_remove(acpi_handle handle)
235662306a36Sopenharmony_ci{
235762306a36Sopenharmony_ci	int ret;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR,
236062306a36Sopenharmony_ci				     &dmar_check_one_atsr, NULL);
236162306a36Sopenharmony_ci	if (ret)
236262306a36Sopenharmony_ci		return ret;
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
236562306a36Sopenharmony_ci				     &dmar_hp_remove_drhd, NULL);
236662306a36Sopenharmony_ci	if (ret == 0) {
236762306a36Sopenharmony_ci		WARN_ON(dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR,
236862306a36Sopenharmony_ci					       &dmar_release_one_atsr, NULL));
236962306a36Sopenharmony_ci		WARN_ON(dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
237062306a36Sopenharmony_ci					       &dmar_hp_release_drhd, NULL));
237162306a36Sopenharmony_ci	} else {
237262306a36Sopenharmony_ci		dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
237362306a36Sopenharmony_ci				       &dmar_hp_add_drhd, NULL);
237462306a36Sopenharmony_ci	}
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	return ret;
237762306a36Sopenharmony_ci}
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_cistatic acpi_status dmar_get_dsm_handle(acpi_handle handle, u32 lvl,
238062306a36Sopenharmony_ci				       void *context, void **retval)
238162306a36Sopenharmony_ci{
238262306a36Sopenharmony_ci	acpi_handle *phdl = retval;
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci	if (dmar_detect_dsm(handle, DMAR_DSM_FUNC_DRHD)) {
238562306a36Sopenharmony_ci		*phdl = handle;
238662306a36Sopenharmony_ci		return AE_CTRL_TERMINATE;
238762306a36Sopenharmony_ci	}
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci	return AE_OK;
239062306a36Sopenharmony_ci}
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_cistatic int dmar_device_hotplug(acpi_handle handle, bool insert)
239362306a36Sopenharmony_ci{
239462306a36Sopenharmony_ci	int ret;
239562306a36Sopenharmony_ci	acpi_handle tmp = NULL;
239662306a36Sopenharmony_ci	acpi_status status;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	if (!dmar_in_use())
239962306a36Sopenharmony_ci		return 0;
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	if (dmar_detect_dsm(handle, DMAR_DSM_FUNC_DRHD)) {
240262306a36Sopenharmony_ci		tmp = handle;
240362306a36Sopenharmony_ci	} else {
240462306a36Sopenharmony_ci		status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
240562306a36Sopenharmony_ci					     ACPI_UINT32_MAX,
240662306a36Sopenharmony_ci					     dmar_get_dsm_handle,
240762306a36Sopenharmony_ci					     NULL, NULL, &tmp);
240862306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
240962306a36Sopenharmony_ci			pr_warn("Failed to locate _DSM method.\n");
241062306a36Sopenharmony_ci			return -ENXIO;
241162306a36Sopenharmony_ci		}
241262306a36Sopenharmony_ci	}
241362306a36Sopenharmony_ci	if (tmp == NULL)
241462306a36Sopenharmony_ci		return 0;
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	down_write(&dmar_global_lock);
241762306a36Sopenharmony_ci	if (insert)
241862306a36Sopenharmony_ci		ret = dmar_hotplug_insert(tmp);
241962306a36Sopenharmony_ci	else
242062306a36Sopenharmony_ci		ret = dmar_hotplug_remove(tmp);
242162306a36Sopenharmony_ci	up_write(&dmar_global_lock);
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	return ret;
242462306a36Sopenharmony_ci}
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ciint dmar_device_add(acpi_handle handle)
242762306a36Sopenharmony_ci{
242862306a36Sopenharmony_ci	return dmar_device_hotplug(handle, true);
242962306a36Sopenharmony_ci}
243062306a36Sopenharmony_ci
243162306a36Sopenharmony_ciint dmar_device_remove(acpi_handle handle)
243262306a36Sopenharmony_ci{
243362306a36Sopenharmony_ci	return dmar_device_hotplug(handle, false);
243462306a36Sopenharmony_ci}
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci/*
243762306a36Sopenharmony_ci * dmar_platform_optin - Is %DMA_CTRL_PLATFORM_OPT_IN_FLAG set in DMAR table
243862306a36Sopenharmony_ci *
243962306a36Sopenharmony_ci * Returns true if the platform has %DMA_CTRL_PLATFORM_OPT_IN_FLAG set in
244062306a36Sopenharmony_ci * the ACPI DMAR table. This means that the platform boot firmware has made
244162306a36Sopenharmony_ci * sure no device can issue DMA outside of RMRR regions.
244262306a36Sopenharmony_ci */
244362306a36Sopenharmony_cibool dmar_platform_optin(void)
244462306a36Sopenharmony_ci{
244562306a36Sopenharmony_ci	struct acpi_table_dmar *dmar;
244662306a36Sopenharmony_ci	acpi_status status;
244762306a36Sopenharmony_ci	bool ret;
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	status = acpi_get_table(ACPI_SIG_DMAR, 0,
245062306a36Sopenharmony_ci				(struct acpi_table_header **)&dmar);
245162306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
245262306a36Sopenharmony_ci		return false;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	ret = !!(dmar->flags & DMAR_PLATFORM_OPT_IN);
245562306a36Sopenharmony_ci	acpi_put_table((struct acpi_table_header *)dmar);
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci	return ret;
245862306a36Sopenharmony_ci}
245962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dmar_platform_optin);
2460