18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2006, Intel Corporation.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Intel Corporation
68c2ecf20Sopenharmony_ci * Author: Ashok Raj <ashok.raj@intel.com>
78c2ecf20Sopenharmony_ci * Author: Shaohua Li <shaohua.li@intel.com>
88c2ecf20Sopenharmony_ci * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This file implements early detection/parsing of Remapping Devices
118c2ecf20Sopenharmony_ci * reported to OS through BIOS via DMA remapping reporting (DMAR) ACPI
128c2ecf20Sopenharmony_ci * tables.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * These routines are used by both DMA-remapping and Interrupt-remapping
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define pr_fmt(fmt)     "DMAR: " fmt
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/pci.h>
208c2ecf20Sopenharmony_ci#include <linux/dmar.h>
218c2ecf20Sopenharmony_ci#include <linux/iova.h>
228c2ecf20Sopenharmony_ci#include <linux/intel-iommu.h>
238c2ecf20Sopenharmony_ci#include <linux/timer.h>
248c2ecf20Sopenharmony_ci#include <linux/irq.h>
258c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
268c2ecf20Sopenharmony_ci#include <linux/tboot.h>
278c2ecf20Sopenharmony_ci#include <linux/dmi.h>
288c2ecf20Sopenharmony_ci#include <linux/slab.h>
298c2ecf20Sopenharmony_ci#include <linux/iommu.h>
308c2ecf20Sopenharmony_ci#include <linux/numa.h>
318c2ecf20Sopenharmony_ci#include <linux/limits.h>
328c2ecf20Sopenharmony_ci#include <asm/irq_remapping.h>
338c2ecf20Sopenharmony_ci#include <asm/iommu_table.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include "../irq_remapping.h"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_citypedef int (*dmar_res_handler_t)(struct acpi_dmar_header *, void *);
388c2ecf20Sopenharmony_cistruct dmar_res_callback {
398c2ecf20Sopenharmony_ci	dmar_res_handler_t	cb[ACPI_DMAR_TYPE_RESERVED];
408c2ecf20Sopenharmony_ci	void			*arg[ACPI_DMAR_TYPE_RESERVED];
418c2ecf20Sopenharmony_ci	bool			ignore_unhandled;
428c2ecf20Sopenharmony_ci	bool			print_entry;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * Assumptions:
478c2ecf20Sopenharmony_ci * 1) The hotplug framework guarentees that DMAR unit will be hot-added
488c2ecf20Sopenharmony_ci *    before IO devices managed by that unit.
498c2ecf20Sopenharmony_ci * 2) The hotplug framework guarantees that DMAR unit will be hot-removed
508c2ecf20Sopenharmony_ci *    after IO devices managed by that unit.
518c2ecf20Sopenharmony_ci * 3) Hotplug events are rare.
528c2ecf20Sopenharmony_ci *
538c2ecf20Sopenharmony_ci * Locking rules for DMA and interrupt remapping related global data structures:
548c2ecf20Sopenharmony_ci * 1) Use dmar_global_lock in process context
558c2ecf20Sopenharmony_ci * 2) Use RCU in interrupt context
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_ciDECLARE_RWSEM(dmar_global_lock);
588c2ecf20Sopenharmony_ciLIST_HEAD(dmar_drhd_units);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct acpi_table_header * __initdata dmar_tbl;
618c2ecf20Sopenharmony_cistatic int dmar_dev_scope_status = 1;
628c2ecf20Sopenharmony_cistatic unsigned long dmar_seq_ids[BITS_TO_LONGS(DMAR_UNITS_SUPPORTED)];
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int alloc_iommu(struct dmar_drhd_unit *drhd);
658c2ecf20Sopenharmony_cistatic void free_iommu(struct intel_iommu *iommu);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ciextern const struct iommu_ops intel_iommu_ops;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	/*
728c2ecf20Sopenharmony_ci	 * add INCLUDE_ALL at the tail, so scan the list will find it at
738c2ecf20Sopenharmony_ci	 * the very end.
748c2ecf20Sopenharmony_ci	 */
758c2ecf20Sopenharmony_ci	if (drhd->include_all)
768c2ecf20Sopenharmony_ci		list_add_tail_rcu(&drhd->list, &dmar_drhd_units);
778c2ecf20Sopenharmony_ci	else
788c2ecf20Sopenharmony_ci		list_add_rcu(&drhd->list, &dmar_drhd_units);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_civoid *dmar_alloc_dev_scope(void *start, void *end, int *cnt)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct acpi_dmar_device_scope *scope;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	*cnt = 0;
868c2ecf20Sopenharmony_ci	while (start < end) {
878c2ecf20Sopenharmony_ci		scope = start;
888c2ecf20Sopenharmony_ci		if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_NAMESPACE ||
898c2ecf20Sopenharmony_ci		    scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
908c2ecf20Sopenharmony_ci		    scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE)
918c2ecf20Sopenharmony_ci			(*cnt)++;
928c2ecf20Sopenharmony_ci		else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC &&
938c2ecf20Sopenharmony_ci			scope->entry_type != ACPI_DMAR_SCOPE_TYPE_HPET) {
948c2ecf20Sopenharmony_ci			pr_warn("Unsupported device scope\n");
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci		start += scope->length;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci	if (*cnt == 0)
998c2ecf20Sopenharmony_ci		return NULL;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return kcalloc(*cnt, sizeof(struct dmar_dev_scope), GFP_KERNEL);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_civoid dmar_free_dev_scope(struct dmar_dev_scope **devices, int *cnt)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int i;
1078c2ecf20Sopenharmony_ci	struct device *tmp_dev;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (*devices && *cnt) {
1108c2ecf20Sopenharmony_ci		for_each_active_dev_scope(*devices, *cnt, i, tmp_dev)
1118c2ecf20Sopenharmony_ci			put_device(tmp_dev);
1128c2ecf20Sopenharmony_ci		kfree(*devices);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	*devices = NULL;
1168c2ecf20Sopenharmony_ci	*cnt = 0;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/* Optimize out kzalloc()/kfree() for normal cases */
1208c2ecf20Sopenharmony_cistatic char dmar_pci_notify_info_buf[64];
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic struct dmar_pci_notify_info *
1238c2ecf20Sopenharmony_cidmar_alloc_pci_notify_info(struct pci_dev *dev, unsigned long event)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	int level = 0;
1268c2ecf20Sopenharmony_ci	size_t size;
1278c2ecf20Sopenharmony_ci	struct pci_dev *tmp;
1288c2ecf20Sopenharmony_ci	struct dmar_pci_notify_info *info;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	BUG_ON(dev->is_virtfn);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/*
1338c2ecf20Sopenharmony_ci	 * Ignore devices that have a domain number higher than what can
1348c2ecf20Sopenharmony_ci	 * be looked up in DMAR, e.g. VMD subdevices with domain 0x10000
1358c2ecf20Sopenharmony_ci	 */
1368c2ecf20Sopenharmony_ci	if (pci_domain_nr(dev->bus) > U16_MAX)
1378c2ecf20Sopenharmony_ci		return NULL;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* Only generate path[] for device addition event */
1408c2ecf20Sopenharmony_ci	if (event == BUS_NOTIFY_ADD_DEVICE)
1418c2ecf20Sopenharmony_ci		for (tmp = dev; tmp; tmp = tmp->bus->self)
1428c2ecf20Sopenharmony_ci			level++;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	size = struct_size(info, path, level);
1458c2ecf20Sopenharmony_ci	if (size <= sizeof(dmar_pci_notify_info_buf)) {
1468c2ecf20Sopenharmony_ci		info = (struct dmar_pci_notify_info *)dmar_pci_notify_info_buf;
1478c2ecf20Sopenharmony_ci	} else {
1488c2ecf20Sopenharmony_ci		info = kzalloc(size, GFP_KERNEL);
1498c2ecf20Sopenharmony_ci		if (!info) {
1508c2ecf20Sopenharmony_ci			pr_warn("Out of memory when allocating notify_info "
1518c2ecf20Sopenharmony_ci				"for %s.\n", pci_name(dev));
1528c2ecf20Sopenharmony_ci			if (dmar_dev_scope_status == 0)
1538c2ecf20Sopenharmony_ci				dmar_dev_scope_status = -ENOMEM;
1548c2ecf20Sopenharmony_ci			return NULL;
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	info->event = event;
1598c2ecf20Sopenharmony_ci	info->dev = dev;
1608c2ecf20Sopenharmony_ci	info->seg = pci_domain_nr(dev->bus);
1618c2ecf20Sopenharmony_ci	info->level = level;
1628c2ecf20Sopenharmony_ci	if (event == BUS_NOTIFY_ADD_DEVICE) {
1638c2ecf20Sopenharmony_ci		for (tmp = dev; tmp; tmp = tmp->bus->self) {
1648c2ecf20Sopenharmony_ci			level--;
1658c2ecf20Sopenharmony_ci			info->path[level].bus = tmp->bus->number;
1668c2ecf20Sopenharmony_ci			info->path[level].device = PCI_SLOT(tmp->devfn);
1678c2ecf20Sopenharmony_ci			info->path[level].function = PCI_FUNC(tmp->devfn);
1688c2ecf20Sopenharmony_ci			if (pci_is_root_bus(tmp->bus))
1698c2ecf20Sopenharmony_ci				info->bus = tmp->bus->number;
1708c2ecf20Sopenharmony_ci		}
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return info;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic inline void dmar_free_pci_notify_info(struct dmar_pci_notify_info *info)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	if ((void *)info != dmar_pci_notify_info_buf)
1798c2ecf20Sopenharmony_ci		kfree(info);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic bool dmar_match_pci_path(struct dmar_pci_notify_info *info, int bus,
1838c2ecf20Sopenharmony_ci				struct acpi_dmar_pci_path *path, int count)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	int i;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (info->bus != bus)
1888c2ecf20Sopenharmony_ci		goto fallback;
1898c2ecf20Sopenharmony_ci	if (info->level != count)
1908c2ecf20Sopenharmony_ci		goto fallback;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
1938c2ecf20Sopenharmony_ci		if (path[i].device != info->path[i].device ||
1948c2ecf20Sopenharmony_ci		    path[i].function != info->path[i].function)
1958c2ecf20Sopenharmony_ci			goto fallback;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return true;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cifallback:
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (count != 1)
2038c2ecf20Sopenharmony_ci		return false;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	i = info->level - 1;
2068c2ecf20Sopenharmony_ci	if (bus              == info->path[i].bus &&
2078c2ecf20Sopenharmony_ci	    path[0].device   == info->path[i].device &&
2088c2ecf20Sopenharmony_ci	    path[0].function == info->path[i].function) {
2098c2ecf20Sopenharmony_ci		pr_info(FW_BUG "RMRR entry for device %02x:%02x.%x is broken - applying workaround\n",
2108c2ecf20Sopenharmony_ci			bus, path[0].device, path[0].function);
2118c2ecf20Sopenharmony_ci		return true;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return false;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/* Return: > 0 if match found, 0 if no match found, < 0 if error happens */
2188c2ecf20Sopenharmony_ciint dmar_insert_dev_scope(struct dmar_pci_notify_info *info,
2198c2ecf20Sopenharmony_ci			  void *start, void*end, u16 segment,
2208c2ecf20Sopenharmony_ci			  struct dmar_dev_scope *devices,
2218c2ecf20Sopenharmony_ci			  int devices_cnt)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	int i, level;
2248c2ecf20Sopenharmony_ci	struct device *tmp, *dev = &info->dev->dev;
2258c2ecf20Sopenharmony_ci	struct acpi_dmar_device_scope *scope;
2268c2ecf20Sopenharmony_ci	struct acpi_dmar_pci_path *path;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (segment != info->seg)
2298c2ecf20Sopenharmony_ci		return 0;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	for (; start < end; start += scope->length) {
2328c2ecf20Sopenharmony_ci		scope = start;
2338c2ecf20Sopenharmony_ci		if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_ENDPOINT &&
2348c2ecf20Sopenharmony_ci		    scope->entry_type != ACPI_DMAR_SCOPE_TYPE_BRIDGE)
2358c2ecf20Sopenharmony_ci			continue;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci		path = (struct acpi_dmar_pci_path *)(scope + 1);
2388c2ecf20Sopenharmony_ci		level = (scope->length - sizeof(*scope)) / sizeof(*path);
2398c2ecf20Sopenharmony_ci		if (!dmar_match_pci_path(info, scope->bus, path, level))
2408c2ecf20Sopenharmony_ci			continue;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci		/*
2438c2ecf20Sopenharmony_ci		 * We expect devices with endpoint scope to have normal PCI
2448c2ecf20Sopenharmony_ci		 * headers, and devices with bridge scope to have bridge PCI
2458c2ecf20Sopenharmony_ci		 * headers.  However PCI NTB devices may be listed in the
2468c2ecf20Sopenharmony_ci		 * DMAR table with bridge scope, even though they have a
2478c2ecf20Sopenharmony_ci		 * normal PCI header.  NTB devices are identified by class
2488c2ecf20Sopenharmony_ci		 * "BRIDGE_OTHER" (0680h) - we don't declare a socpe mismatch
2498c2ecf20Sopenharmony_ci		 * for this special case.
2508c2ecf20Sopenharmony_ci		 */
2518c2ecf20Sopenharmony_ci		if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT &&
2528c2ecf20Sopenharmony_ci		     info->dev->hdr_type != PCI_HEADER_TYPE_NORMAL) ||
2538c2ecf20Sopenharmony_ci		    (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE &&
2548c2ecf20Sopenharmony_ci		     (info->dev->hdr_type == PCI_HEADER_TYPE_NORMAL &&
2558c2ecf20Sopenharmony_ci		      info->dev->class >> 16 != PCI_BASE_CLASS_BRIDGE))) {
2568c2ecf20Sopenharmony_ci			pr_warn("Device scope type does not match for %s\n",
2578c2ecf20Sopenharmony_ci				pci_name(info->dev));
2588c2ecf20Sopenharmony_ci			return -EINVAL;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		for_each_dev_scope(devices, devices_cnt, i, tmp)
2628c2ecf20Sopenharmony_ci			if (tmp == NULL) {
2638c2ecf20Sopenharmony_ci				devices[i].bus = info->dev->bus->number;
2648c2ecf20Sopenharmony_ci				devices[i].devfn = info->dev->devfn;
2658c2ecf20Sopenharmony_ci				rcu_assign_pointer(devices[i].dev,
2668c2ecf20Sopenharmony_ci						   get_device(dev));
2678c2ecf20Sopenharmony_ci				return 1;
2688c2ecf20Sopenharmony_ci			}
2698c2ecf20Sopenharmony_ci		BUG_ON(i >= devices_cnt);
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return 0;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ciint dmar_remove_dev_scope(struct dmar_pci_notify_info *info, u16 segment,
2768c2ecf20Sopenharmony_ci			  struct dmar_dev_scope *devices, int count)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	int index;
2798c2ecf20Sopenharmony_ci	struct device *tmp;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (info->seg != segment)
2828c2ecf20Sopenharmony_ci		return 0;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	for_each_active_dev_scope(devices, count, index, tmp)
2858c2ecf20Sopenharmony_ci		if (tmp == &info->dev->dev) {
2868c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(devices[index].dev, NULL);
2878c2ecf20Sopenharmony_ci			synchronize_rcu();
2888c2ecf20Sopenharmony_ci			put_device(tmp);
2898c2ecf20Sopenharmony_ci			return 1;
2908c2ecf20Sopenharmony_ci		}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return 0;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int dmar_pci_bus_add_dev(struct dmar_pci_notify_info *info)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	int ret = 0;
2988c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
2998c2ecf20Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	for_each_drhd_unit(dmaru) {
3028c2ecf20Sopenharmony_ci		if (dmaru->include_all)
3038c2ecf20Sopenharmony_ci			continue;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		drhd = container_of(dmaru->hdr,
3068c2ecf20Sopenharmony_ci				    struct acpi_dmar_hardware_unit, header);
3078c2ecf20Sopenharmony_ci		ret = dmar_insert_dev_scope(info, (void *)(drhd + 1),
3088c2ecf20Sopenharmony_ci				((void *)drhd) + drhd->header.length,
3098c2ecf20Sopenharmony_ci				dmaru->segment,
3108c2ecf20Sopenharmony_ci				dmaru->devices, dmaru->devices_cnt);
3118c2ecf20Sopenharmony_ci		if (ret)
3128c2ecf20Sopenharmony_ci			break;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci	if (ret >= 0)
3158c2ecf20Sopenharmony_ci		ret = dmar_iommu_notify_scope_dev(info);
3168c2ecf20Sopenharmony_ci	if (ret < 0 && dmar_dev_scope_status == 0)
3178c2ecf20Sopenharmony_ci		dmar_dev_scope_status = ret;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (ret >= 0)
3208c2ecf20Sopenharmony_ci		intel_irq_remap_add_device(info);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return ret;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void  dmar_pci_bus_del_dev(struct dmar_pci_notify_info *info)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	for_each_drhd_unit(dmaru)
3308c2ecf20Sopenharmony_ci		if (dmar_remove_dev_scope(info, dmaru->segment,
3318c2ecf20Sopenharmony_ci			dmaru->devices, dmaru->devices_cnt))
3328c2ecf20Sopenharmony_ci			break;
3338c2ecf20Sopenharmony_ci	dmar_iommu_notify_scope_dev(info);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic inline void vf_inherit_msi_domain(struct pci_dev *pdev)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct pci_dev *physfn = pci_physfn(pdev);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	dev_set_msi_domain(&pdev->dev, dev_get_msi_domain(&physfn->dev));
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic int dmar_pci_bus_notifier(struct notifier_block *nb,
3448c2ecf20Sopenharmony_ci				 unsigned long action, void *data)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(data);
3478c2ecf20Sopenharmony_ci	struct dmar_pci_notify_info *info;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* Only care about add/remove events for physical functions.
3508c2ecf20Sopenharmony_ci	 * For VFs we actually do the lookup based on the corresponding
3518c2ecf20Sopenharmony_ci	 * PF in device_to_iommu() anyway. */
3528c2ecf20Sopenharmony_ci	if (pdev->is_virtfn) {
3538c2ecf20Sopenharmony_ci		/*
3548c2ecf20Sopenharmony_ci		 * Ensure that the VF device inherits the irq domain of the
3558c2ecf20Sopenharmony_ci		 * PF device. Ideally the device would inherit the domain
3568c2ecf20Sopenharmony_ci		 * from the bus, but DMAR can have multiple units per bus
3578c2ecf20Sopenharmony_ci		 * which makes this impossible. The VF 'bus' could inherit
3588c2ecf20Sopenharmony_ci		 * from the PF device, but that's yet another x86'sism to
3598c2ecf20Sopenharmony_ci		 * inflict on everybody else.
3608c2ecf20Sopenharmony_ci		 */
3618c2ecf20Sopenharmony_ci		if (action == BUS_NOTIFY_ADD_DEVICE)
3628c2ecf20Sopenharmony_ci			vf_inherit_msi_domain(pdev);
3638c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (action != BUS_NOTIFY_ADD_DEVICE &&
3678c2ecf20Sopenharmony_ci	    action != BUS_NOTIFY_REMOVED_DEVICE)
3688c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	info = dmar_alloc_pci_notify_info(pdev, action);
3718c2ecf20Sopenharmony_ci	if (!info)
3728c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	down_write(&dmar_global_lock);
3758c2ecf20Sopenharmony_ci	if (action == BUS_NOTIFY_ADD_DEVICE)
3768c2ecf20Sopenharmony_ci		dmar_pci_bus_add_dev(info);
3778c2ecf20Sopenharmony_ci	else if (action == BUS_NOTIFY_REMOVED_DEVICE)
3788c2ecf20Sopenharmony_ci		dmar_pci_bus_del_dev(info);
3798c2ecf20Sopenharmony_ci	up_write(&dmar_global_lock);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	dmar_free_pci_notify_info(info);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return NOTIFY_OK;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic struct notifier_block dmar_pci_bus_nb = {
3878c2ecf20Sopenharmony_ci	.notifier_call = dmar_pci_bus_notifier,
3888c2ecf20Sopenharmony_ci	.priority = 1,
3898c2ecf20Sopenharmony_ci};
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic struct dmar_drhd_unit *
3928c2ecf20Sopenharmony_cidmar_find_dmaru(struct acpi_dmar_hardware_unit *drhd)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(dmaru, &dmar_drhd_units, list,
3978c2ecf20Sopenharmony_ci				dmar_rcu_check())
3988c2ecf20Sopenharmony_ci		if (dmaru->segment == drhd->segment &&
3998c2ecf20Sopenharmony_ci		    dmaru->reg_base_addr == drhd->address)
4008c2ecf20Sopenharmony_ci			return dmaru;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return NULL;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci/*
4068c2ecf20Sopenharmony_ci * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition
4078c2ecf20Sopenharmony_ci * structure which uniquely represent one DMA remapping hardware unit
4088c2ecf20Sopenharmony_ci * present in the platform
4098c2ecf20Sopenharmony_ci */
4108c2ecf20Sopenharmony_cistatic int dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
4138c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
4148c2ecf20Sopenharmony_ci	int ret;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	drhd = (struct acpi_dmar_hardware_unit *)header;
4178c2ecf20Sopenharmony_ci	dmaru = dmar_find_dmaru(drhd);
4188c2ecf20Sopenharmony_ci	if (dmaru)
4198c2ecf20Sopenharmony_ci		goto out;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	dmaru = kzalloc(sizeof(*dmaru) + header->length, GFP_KERNEL);
4228c2ecf20Sopenharmony_ci	if (!dmaru)
4238c2ecf20Sopenharmony_ci		return -ENOMEM;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/*
4268c2ecf20Sopenharmony_ci	 * If header is allocated from slab by ACPI _DSM method, we need to
4278c2ecf20Sopenharmony_ci	 * copy the content because the memory buffer will be freed on return.
4288c2ecf20Sopenharmony_ci	 */
4298c2ecf20Sopenharmony_ci	dmaru->hdr = (void *)(dmaru + 1);
4308c2ecf20Sopenharmony_ci	memcpy(dmaru->hdr, header, header->length);
4318c2ecf20Sopenharmony_ci	dmaru->reg_base_addr = drhd->address;
4328c2ecf20Sopenharmony_ci	dmaru->segment = drhd->segment;
4338c2ecf20Sopenharmony_ci	dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
4348c2ecf20Sopenharmony_ci	dmaru->devices = dmar_alloc_dev_scope((void *)(drhd + 1),
4358c2ecf20Sopenharmony_ci					      ((void *)drhd) + drhd->header.length,
4368c2ecf20Sopenharmony_ci					      &dmaru->devices_cnt);
4378c2ecf20Sopenharmony_ci	if (dmaru->devices_cnt && dmaru->devices == NULL) {
4388c2ecf20Sopenharmony_ci		kfree(dmaru);
4398c2ecf20Sopenharmony_ci		return -ENOMEM;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	ret = alloc_iommu(dmaru);
4438c2ecf20Sopenharmony_ci	if (ret) {
4448c2ecf20Sopenharmony_ci		dmar_free_dev_scope(&dmaru->devices,
4458c2ecf20Sopenharmony_ci				    &dmaru->devices_cnt);
4468c2ecf20Sopenharmony_ci		kfree(dmaru);
4478c2ecf20Sopenharmony_ci		return ret;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci	dmar_register_drhd_unit(dmaru);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ciout:
4528c2ecf20Sopenharmony_ci	if (arg)
4538c2ecf20Sopenharmony_ci		(*(int *)arg)++;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	return 0;
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic void dmar_free_drhd(struct dmar_drhd_unit *dmaru)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	if (dmaru->devices && dmaru->devices_cnt)
4618c2ecf20Sopenharmony_ci		dmar_free_dev_scope(&dmaru->devices, &dmaru->devices_cnt);
4628c2ecf20Sopenharmony_ci	if (dmaru->iommu)
4638c2ecf20Sopenharmony_ci		free_iommu(dmaru->iommu);
4648c2ecf20Sopenharmony_ci	kfree(dmaru);
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic int __init dmar_parse_one_andd(struct acpi_dmar_header *header,
4688c2ecf20Sopenharmony_ci				      void *arg)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	struct acpi_dmar_andd *andd = (void *)header;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	/* Check for NUL termination within the designated length */
4738c2ecf20Sopenharmony_ci	if (strnlen(andd->device_name, header->length - 8) == header->length - 8) {
4748c2ecf20Sopenharmony_ci		pr_warn(FW_BUG
4758c2ecf20Sopenharmony_ci			   "Your BIOS is broken; ANDD object name is not NUL-terminated\n"
4768c2ecf20Sopenharmony_ci			   "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
4778c2ecf20Sopenharmony_ci			   dmi_get_system_info(DMI_BIOS_VENDOR),
4788c2ecf20Sopenharmony_ci			   dmi_get_system_info(DMI_BIOS_VERSION),
4798c2ecf20Sopenharmony_ci			   dmi_get_system_info(DMI_PRODUCT_VERSION));
4808c2ecf20Sopenharmony_ci		add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
4818c2ecf20Sopenharmony_ci		return -EINVAL;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci	pr_info("ANDD device: %x name: %s\n", andd->device_number,
4848c2ecf20Sopenharmony_ci		andd->device_name);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	return 0;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI_NUMA
4908c2ecf20Sopenharmony_cistatic int dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	struct acpi_dmar_rhsa *rhsa;
4938c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	rhsa = (struct acpi_dmar_rhsa *)header;
4968c2ecf20Sopenharmony_ci	for_each_drhd_unit(drhd) {
4978c2ecf20Sopenharmony_ci		if (drhd->reg_base_addr == rhsa->base_address) {
4988c2ecf20Sopenharmony_ci			int node = pxm_to_node(rhsa->proximity_domain);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci			if (node != NUMA_NO_NODE && !node_online(node))
5018c2ecf20Sopenharmony_ci				node = NUMA_NO_NODE;
5028c2ecf20Sopenharmony_ci			drhd->iommu->node = node;
5038c2ecf20Sopenharmony_ci			return 0;
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci	pr_warn(FW_BUG
5078c2ecf20Sopenharmony_ci		"Your BIOS is broken; RHSA refers to non-existent DMAR unit at %llx\n"
5088c2ecf20Sopenharmony_ci		"BIOS vendor: %s; Ver: %s; Product Version: %s\n",
5098c2ecf20Sopenharmony_ci		rhsa->base_address,
5108c2ecf20Sopenharmony_ci		dmi_get_system_info(DMI_BIOS_VENDOR),
5118c2ecf20Sopenharmony_ci		dmi_get_system_info(DMI_BIOS_VERSION),
5128c2ecf20Sopenharmony_ci		dmi_get_system_info(DMI_PRODUCT_VERSION));
5138c2ecf20Sopenharmony_ci	add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	return 0;
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci#else
5188c2ecf20Sopenharmony_ci#define	dmar_parse_one_rhsa		dmar_res_noop
5198c2ecf20Sopenharmony_ci#endif
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic void
5228c2ecf20Sopenharmony_cidmar_table_print_dmar_entry(struct acpi_dmar_header *header)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
5258c2ecf20Sopenharmony_ci	struct acpi_dmar_reserved_memory *rmrr;
5268c2ecf20Sopenharmony_ci	struct acpi_dmar_atsr *atsr;
5278c2ecf20Sopenharmony_ci	struct acpi_dmar_rhsa *rhsa;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	switch (header->type) {
5308c2ecf20Sopenharmony_ci	case ACPI_DMAR_TYPE_HARDWARE_UNIT:
5318c2ecf20Sopenharmony_ci		drhd = container_of(header, struct acpi_dmar_hardware_unit,
5328c2ecf20Sopenharmony_ci				    header);
5338c2ecf20Sopenharmony_ci		pr_info("DRHD base: %#016Lx flags: %#x\n",
5348c2ecf20Sopenharmony_ci			(unsigned long long)drhd->address, drhd->flags);
5358c2ecf20Sopenharmony_ci		break;
5368c2ecf20Sopenharmony_ci	case ACPI_DMAR_TYPE_RESERVED_MEMORY:
5378c2ecf20Sopenharmony_ci		rmrr = container_of(header, struct acpi_dmar_reserved_memory,
5388c2ecf20Sopenharmony_ci				    header);
5398c2ecf20Sopenharmony_ci		pr_info("RMRR base: %#016Lx end: %#016Lx\n",
5408c2ecf20Sopenharmony_ci			(unsigned long long)rmrr->base_address,
5418c2ecf20Sopenharmony_ci			(unsigned long long)rmrr->end_address);
5428c2ecf20Sopenharmony_ci		break;
5438c2ecf20Sopenharmony_ci	case ACPI_DMAR_TYPE_ROOT_ATS:
5448c2ecf20Sopenharmony_ci		atsr = container_of(header, struct acpi_dmar_atsr, header);
5458c2ecf20Sopenharmony_ci		pr_info("ATSR flags: %#x\n", atsr->flags);
5468c2ecf20Sopenharmony_ci		break;
5478c2ecf20Sopenharmony_ci	case ACPI_DMAR_TYPE_HARDWARE_AFFINITY:
5488c2ecf20Sopenharmony_ci		rhsa = container_of(header, struct acpi_dmar_rhsa, header);
5498c2ecf20Sopenharmony_ci		pr_info("RHSA base: %#016Lx proximity domain: %#x\n",
5508c2ecf20Sopenharmony_ci		       (unsigned long long)rhsa->base_address,
5518c2ecf20Sopenharmony_ci		       rhsa->proximity_domain);
5528c2ecf20Sopenharmony_ci		break;
5538c2ecf20Sopenharmony_ci	case ACPI_DMAR_TYPE_NAMESPACE:
5548c2ecf20Sopenharmony_ci		/* We don't print this here because we need to sanity-check
5558c2ecf20Sopenharmony_ci		   it first. So print it in dmar_parse_one_andd() instead. */
5568c2ecf20Sopenharmony_ci		break;
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci/**
5618c2ecf20Sopenharmony_ci * dmar_table_detect - checks to see if the platform supports DMAR devices
5628c2ecf20Sopenharmony_ci */
5638c2ecf20Sopenharmony_cistatic int __init dmar_table_detect(void)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	acpi_status status = AE_OK;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* if we could find DMAR table, then there are DMAR devices */
5688c2ecf20Sopenharmony_ci	status = acpi_get_table(ACPI_SIG_DMAR, 0, &dmar_tbl);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (ACPI_SUCCESS(status) && !dmar_tbl) {
5718c2ecf20Sopenharmony_ci		pr_warn("Unable to map DMAR\n");
5728c2ecf20Sopenharmony_ci		status = AE_NOT_FOUND;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return ACPI_SUCCESS(status) ? 0 : -ENOENT;
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic int dmar_walk_remapping_entries(struct acpi_dmar_header *start,
5798c2ecf20Sopenharmony_ci				       size_t len, struct dmar_res_callback *cb)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct acpi_dmar_header *iter, *next;
5828c2ecf20Sopenharmony_ci	struct acpi_dmar_header *end = ((void *)start) + len;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	for (iter = start; iter < end; iter = next) {
5858c2ecf20Sopenharmony_ci		next = (void *)iter + iter->length;
5868c2ecf20Sopenharmony_ci		if (iter->length == 0) {
5878c2ecf20Sopenharmony_ci			/* Avoid looping forever on bad ACPI tables */
5888c2ecf20Sopenharmony_ci			pr_debug(FW_BUG "Invalid 0-length structure\n");
5898c2ecf20Sopenharmony_ci			break;
5908c2ecf20Sopenharmony_ci		} else if (next > end) {
5918c2ecf20Sopenharmony_ci			/* Avoid passing table end */
5928c2ecf20Sopenharmony_ci			pr_warn(FW_BUG "Record passes table end\n");
5938c2ecf20Sopenharmony_ci			return -EINVAL;
5948c2ecf20Sopenharmony_ci		}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		if (cb->print_entry)
5978c2ecf20Sopenharmony_ci			dmar_table_print_dmar_entry(iter);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci		if (iter->type >= ACPI_DMAR_TYPE_RESERVED) {
6008c2ecf20Sopenharmony_ci			/* continue for forward compatibility */
6018c2ecf20Sopenharmony_ci			pr_debug("Unknown DMAR structure type %d\n",
6028c2ecf20Sopenharmony_ci				 iter->type);
6038c2ecf20Sopenharmony_ci		} else if (cb->cb[iter->type]) {
6048c2ecf20Sopenharmony_ci			int ret;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci			ret = cb->cb[iter->type](iter, cb->arg[iter->type]);
6078c2ecf20Sopenharmony_ci			if (ret)
6088c2ecf20Sopenharmony_ci				return ret;
6098c2ecf20Sopenharmony_ci		} else if (!cb->ignore_unhandled) {
6108c2ecf20Sopenharmony_ci			pr_warn("No handler for DMAR structure type %d\n",
6118c2ecf20Sopenharmony_ci				iter->type);
6128c2ecf20Sopenharmony_ci			return -EINVAL;
6138c2ecf20Sopenharmony_ci		}
6148c2ecf20Sopenharmony_ci	}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	return 0;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic inline int dmar_walk_dmar_table(struct acpi_table_dmar *dmar,
6208c2ecf20Sopenharmony_ci				       struct dmar_res_callback *cb)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	return dmar_walk_remapping_entries((void *)(dmar + 1),
6238c2ecf20Sopenharmony_ci			dmar->header.length - sizeof(*dmar), cb);
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci/**
6278c2ecf20Sopenharmony_ci * parse_dmar_table - parses the DMA reporting table
6288c2ecf20Sopenharmony_ci */
6298c2ecf20Sopenharmony_cistatic int __init
6308c2ecf20Sopenharmony_ciparse_dmar_table(void)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	struct acpi_table_dmar *dmar;
6338c2ecf20Sopenharmony_ci	int drhd_count = 0;
6348c2ecf20Sopenharmony_ci	int ret;
6358c2ecf20Sopenharmony_ci	struct dmar_res_callback cb = {
6368c2ecf20Sopenharmony_ci		.print_entry = true,
6378c2ecf20Sopenharmony_ci		.ignore_unhandled = true,
6388c2ecf20Sopenharmony_ci		.arg[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &drhd_count,
6398c2ecf20Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_parse_one_drhd,
6408c2ecf20Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_RESERVED_MEMORY] = &dmar_parse_one_rmrr,
6418c2ecf20Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_ROOT_ATS] = &dmar_parse_one_atsr,
6428c2ecf20Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_HARDWARE_AFFINITY] = &dmar_parse_one_rhsa,
6438c2ecf20Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_NAMESPACE] = &dmar_parse_one_andd,
6448c2ecf20Sopenharmony_ci	};
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/*
6478c2ecf20Sopenharmony_ci	 * Do it again, earlier dmar_tbl mapping could be mapped with
6488c2ecf20Sopenharmony_ci	 * fixed map.
6498c2ecf20Sopenharmony_ci	 */
6508c2ecf20Sopenharmony_ci	dmar_table_detect();
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	/*
6538c2ecf20Sopenharmony_ci	 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
6548c2ecf20Sopenharmony_ci	 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
6558c2ecf20Sopenharmony_ci	 */
6568c2ecf20Sopenharmony_ci	dmar_tbl = tboot_get_dmar_table(dmar_tbl);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	dmar = (struct acpi_table_dmar *)dmar_tbl;
6598c2ecf20Sopenharmony_ci	if (!dmar)
6608c2ecf20Sopenharmony_ci		return -ENODEV;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	if (dmar->width < PAGE_SHIFT - 1) {
6638c2ecf20Sopenharmony_ci		pr_warn("Invalid DMAR haw\n");
6648c2ecf20Sopenharmony_ci		return -EINVAL;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	pr_info("Host address width %d\n", dmar->width + 1);
6688c2ecf20Sopenharmony_ci	ret = dmar_walk_dmar_table(dmar, &cb);
6698c2ecf20Sopenharmony_ci	if (ret == 0 && drhd_count == 0)
6708c2ecf20Sopenharmony_ci		pr_warn(FW_BUG "No DRHD structure found in DMAR table\n");
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	return ret;
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_cistatic int dmar_pci_device_match(struct dmar_dev_scope devices[],
6768c2ecf20Sopenharmony_ci				 int cnt, struct pci_dev *dev)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	int index;
6798c2ecf20Sopenharmony_ci	struct device *tmp;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	while (dev) {
6828c2ecf20Sopenharmony_ci		for_each_active_dev_scope(devices, cnt, index, tmp)
6838c2ecf20Sopenharmony_ci			if (dev_is_pci(tmp) && dev == to_pci_dev(tmp))
6848c2ecf20Sopenharmony_ci				return 1;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		/* Check our parent */
6878c2ecf20Sopenharmony_ci		dev = dev->bus->self;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	return 0;
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistruct dmar_drhd_unit *
6948c2ecf20Sopenharmony_cidmar_find_matched_drhd_unit(struct pci_dev *dev)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
6978c2ecf20Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	dev = pci_physfn(dev);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	rcu_read_lock();
7028c2ecf20Sopenharmony_ci	for_each_drhd_unit(dmaru) {
7038c2ecf20Sopenharmony_ci		drhd = container_of(dmaru->hdr,
7048c2ecf20Sopenharmony_ci				    struct acpi_dmar_hardware_unit,
7058c2ecf20Sopenharmony_ci				    header);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci		if (dmaru->include_all &&
7088c2ecf20Sopenharmony_ci		    drhd->segment == pci_domain_nr(dev->bus))
7098c2ecf20Sopenharmony_ci			goto out;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci		if (dmar_pci_device_match(dmaru->devices,
7128c2ecf20Sopenharmony_ci					  dmaru->devices_cnt, dev))
7138c2ecf20Sopenharmony_ci			goto out;
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci	dmaru = NULL;
7168c2ecf20Sopenharmony_ciout:
7178c2ecf20Sopenharmony_ci	rcu_read_unlock();
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	return dmaru;
7208c2ecf20Sopenharmony_ci}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_cistatic void __init dmar_acpi_insert_dev_scope(u8 device_number,
7238c2ecf20Sopenharmony_ci					      struct acpi_device *adev)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
7268c2ecf20Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
7278c2ecf20Sopenharmony_ci	struct acpi_dmar_device_scope *scope;
7288c2ecf20Sopenharmony_ci	struct device *tmp;
7298c2ecf20Sopenharmony_ci	int i;
7308c2ecf20Sopenharmony_ci	struct acpi_dmar_pci_path *path;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	for_each_drhd_unit(dmaru) {
7338c2ecf20Sopenharmony_ci		drhd = container_of(dmaru->hdr,
7348c2ecf20Sopenharmony_ci				    struct acpi_dmar_hardware_unit,
7358c2ecf20Sopenharmony_ci				    header);
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci		for (scope = (void *)(drhd + 1);
7388c2ecf20Sopenharmony_ci		     (unsigned long)scope < ((unsigned long)drhd) + drhd->header.length;
7398c2ecf20Sopenharmony_ci		     scope = ((void *)scope) + scope->length) {
7408c2ecf20Sopenharmony_ci			if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_NAMESPACE)
7418c2ecf20Sopenharmony_ci				continue;
7428c2ecf20Sopenharmony_ci			if (scope->enumeration_id != device_number)
7438c2ecf20Sopenharmony_ci				continue;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci			path = (void *)(scope + 1);
7468c2ecf20Sopenharmony_ci			pr_info("ACPI device \"%s\" under DMAR at %llx as %02x:%02x.%d\n",
7478c2ecf20Sopenharmony_ci				dev_name(&adev->dev), dmaru->reg_base_addr,
7488c2ecf20Sopenharmony_ci				scope->bus, path->device, path->function);
7498c2ecf20Sopenharmony_ci			for_each_dev_scope(dmaru->devices, dmaru->devices_cnt, i, tmp)
7508c2ecf20Sopenharmony_ci				if (tmp == NULL) {
7518c2ecf20Sopenharmony_ci					dmaru->devices[i].bus = scope->bus;
7528c2ecf20Sopenharmony_ci					dmaru->devices[i].devfn = PCI_DEVFN(path->device,
7538c2ecf20Sopenharmony_ci									    path->function);
7548c2ecf20Sopenharmony_ci					rcu_assign_pointer(dmaru->devices[i].dev,
7558c2ecf20Sopenharmony_ci							   get_device(&adev->dev));
7568c2ecf20Sopenharmony_ci					return;
7578c2ecf20Sopenharmony_ci				}
7588c2ecf20Sopenharmony_ci			BUG_ON(i >= dmaru->devices_cnt);
7598c2ecf20Sopenharmony_ci		}
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci	pr_warn("No IOMMU scope found for ANDD enumeration ID %d (%s)\n",
7628c2ecf20Sopenharmony_ci		device_number, dev_name(&adev->dev));
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic int __init dmar_acpi_dev_scope_init(void)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	struct acpi_dmar_andd *andd;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	if (dmar_tbl == NULL)
7708c2ecf20Sopenharmony_ci		return -ENODEV;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	for (andd = (void *)dmar_tbl + sizeof(struct acpi_table_dmar);
7738c2ecf20Sopenharmony_ci	     ((unsigned long)andd) < ((unsigned long)dmar_tbl) + dmar_tbl->length;
7748c2ecf20Sopenharmony_ci	     andd = ((void *)andd) + andd->header.length) {
7758c2ecf20Sopenharmony_ci		if (andd->header.type == ACPI_DMAR_TYPE_NAMESPACE) {
7768c2ecf20Sopenharmony_ci			acpi_handle h;
7778c2ecf20Sopenharmony_ci			struct acpi_device *adev;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci			if (!ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT,
7808c2ecf20Sopenharmony_ci							  andd->device_name,
7818c2ecf20Sopenharmony_ci							  &h))) {
7828c2ecf20Sopenharmony_ci				pr_err("Failed to find handle for ACPI object %s\n",
7838c2ecf20Sopenharmony_ci				       andd->device_name);
7848c2ecf20Sopenharmony_ci				continue;
7858c2ecf20Sopenharmony_ci			}
7868c2ecf20Sopenharmony_ci			if (acpi_bus_get_device(h, &adev)) {
7878c2ecf20Sopenharmony_ci				pr_err("Failed to get device for ACPI object %s\n",
7888c2ecf20Sopenharmony_ci				       andd->device_name);
7898c2ecf20Sopenharmony_ci				continue;
7908c2ecf20Sopenharmony_ci			}
7918c2ecf20Sopenharmony_ci			dmar_acpi_insert_dev_scope(andd->device_number, adev);
7928c2ecf20Sopenharmony_ci		}
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci	return 0;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ciint __init dmar_dev_scope_init(void)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct pci_dev *dev = NULL;
8008c2ecf20Sopenharmony_ci	struct dmar_pci_notify_info *info;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (dmar_dev_scope_status != 1)
8038c2ecf20Sopenharmony_ci		return dmar_dev_scope_status;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	if (list_empty(&dmar_drhd_units)) {
8068c2ecf20Sopenharmony_ci		dmar_dev_scope_status = -ENODEV;
8078c2ecf20Sopenharmony_ci	} else {
8088c2ecf20Sopenharmony_ci		dmar_dev_scope_status = 0;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci		dmar_acpi_dev_scope_init();
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci		for_each_pci_dev(dev) {
8138c2ecf20Sopenharmony_ci			if (dev->is_virtfn)
8148c2ecf20Sopenharmony_ci				continue;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci			info = dmar_alloc_pci_notify_info(dev,
8178c2ecf20Sopenharmony_ci					BUS_NOTIFY_ADD_DEVICE);
8188c2ecf20Sopenharmony_ci			if (!info) {
8198c2ecf20Sopenharmony_ci				pci_dev_put(dev);
8208c2ecf20Sopenharmony_ci				return dmar_dev_scope_status;
8218c2ecf20Sopenharmony_ci			} else {
8228c2ecf20Sopenharmony_ci				dmar_pci_bus_add_dev(info);
8238c2ecf20Sopenharmony_ci				dmar_free_pci_notify_info(info);
8248c2ecf20Sopenharmony_ci			}
8258c2ecf20Sopenharmony_ci		}
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	return dmar_dev_scope_status;
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_civoid __init dmar_register_bus_notifier(void)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb);
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ciint __init dmar_table_init(void)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	static int dmar_table_initialized;
8408c2ecf20Sopenharmony_ci	int ret;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	if (dmar_table_initialized == 0) {
8438c2ecf20Sopenharmony_ci		ret = parse_dmar_table();
8448c2ecf20Sopenharmony_ci		if (ret < 0) {
8458c2ecf20Sopenharmony_ci			if (ret != -ENODEV)
8468c2ecf20Sopenharmony_ci				pr_info("Parse DMAR table failure.\n");
8478c2ecf20Sopenharmony_ci		} else  if (list_empty(&dmar_drhd_units)) {
8488c2ecf20Sopenharmony_ci			pr_info("No DMAR devices found\n");
8498c2ecf20Sopenharmony_ci			ret = -ENODEV;
8508c2ecf20Sopenharmony_ci		}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci		if (ret < 0)
8538c2ecf20Sopenharmony_ci			dmar_table_initialized = ret;
8548c2ecf20Sopenharmony_ci		else
8558c2ecf20Sopenharmony_ci			dmar_table_initialized = 1;
8568c2ecf20Sopenharmony_ci	}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	return dmar_table_initialized < 0 ? dmar_table_initialized : 0;
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_cistatic void warn_invalid_dmar(u64 addr, const char *message)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	pr_warn_once(FW_BUG
8648c2ecf20Sopenharmony_ci		"Your BIOS is broken; DMAR reported at address %llx%s!\n"
8658c2ecf20Sopenharmony_ci		"BIOS vendor: %s; Ver: %s; Product Version: %s\n",
8668c2ecf20Sopenharmony_ci		addr, message,
8678c2ecf20Sopenharmony_ci		dmi_get_system_info(DMI_BIOS_VENDOR),
8688c2ecf20Sopenharmony_ci		dmi_get_system_info(DMI_BIOS_VERSION),
8698c2ecf20Sopenharmony_ci		dmi_get_system_info(DMI_PRODUCT_VERSION));
8708c2ecf20Sopenharmony_ci	add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic int __ref
8748c2ecf20Sopenharmony_cidmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci	struct acpi_dmar_hardware_unit *drhd;
8778c2ecf20Sopenharmony_ci	void __iomem *addr;
8788c2ecf20Sopenharmony_ci	u64 cap, ecap;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	drhd = (void *)entry;
8818c2ecf20Sopenharmony_ci	if (!drhd->address) {
8828c2ecf20Sopenharmony_ci		warn_invalid_dmar(0, "");
8838c2ecf20Sopenharmony_ci		return -EINVAL;
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	if (arg)
8878c2ecf20Sopenharmony_ci		addr = ioremap(drhd->address, VTD_PAGE_SIZE);
8888c2ecf20Sopenharmony_ci	else
8898c2ecf20Sopenharmony_ci		addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
8908c2ecf20Sopenharmony_ci	if (!addr) {
8918c2ecf20Sopenharmony_ci		pr_warn("Can't validate DRHD address: %llx\n", drhd->address);
8928c2ecf20Sopenharmony_ci		return -EINVAL;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	cap = dmar_readq(addr + DMAR_CAP_REG);
8968c2ecf20Sopenharmony_ci	ecap = dmar_readq(addr + DMAR_ECAP_REG);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	if (arg)
8998c2ecf20Sopenharmony_ci		iounmap(addr);
9008c2ecf20Sopenharmony_ci	else
9018c2ecf20Sopenharmony_ci		early_iounmap(addr, VTD_PAGE_SIZE);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) {
9048c2ecf20Sopenharmony_ci		warn_invalid_dmar(drhd->address, " returns all ones");
9058c2ecf20Sopenharmony_ci		return -EINVAL;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	return 0;
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ciint __init detect_intel_iommu(void)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	int ret;
9148c2ecf20Sopenharmony_ci	struct dmar_res_callback validate_drhd_cb = {
9158c2ecf20Sopenharmony_ci		.cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_validate_one_drhd,
9168c2ecf20Sopenharmony_ci		.ignore_unhandled = true,
9178c2ecf20Sopenharmony_ci	};
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	down_write(&dmar_global_lock);
9208c2ecf20Sopenharmony_ci	ret = dmar_table_detect();
9218c2ecf20Sopenharmony_ci	if (!ret)
9228c2ecf20Sopenharmony_ci		ret = dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl,
9238c2ecf20Sopenharmony_ci					   &validate_drhd_cb);
9248c2ecf20Sopenharmony_ci	if (!ret && !no_iommu && !iommu_detected &&
9258c2ecf20Sopenharmony_ci	    (!dmar_disabled || dmar_platform_optin())) {
9268c2ecf20Sopenharmony_ci		iommu_detected = 1;
9278c2ecf20Sopenharmony_ci		/* Make sure ACS will be enabled */
9288c2ecf20Sopenharmony_ci		pci_request_acs();
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci#ifdef CONFIG_X86
9328c2ecf20Sopenharmony_ci	if (!ret) {
9338c2ecf20Sopenharmony_ci		x86_init.iommu.iommu_init = intel_iommu_init;
9348c2ecf20Sopenharmony_ci		x86_platform.iommu_shutdown = intel_iommu_shutdown;
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci#endif
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	if (dmar_tbl) {
9408c2ecf20Sopenharmony_ci		acpi_put_table(dmar_tbl);
9418c2ecf20Sopenharmony_ci		dmar_tbl = NULL;
9428c2ecf20Sopenharmony_ci	}
9438c2ecf20Sopenharmony_ci	up_write(&dmar_global_lock);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	return ret ? ret : 1;
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic void unmap_iommu(struct intel_iommu *iommu)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	iounmap(iommu->reg);
9518c2ecf20Sopenharmony_ci	release_mem_region(iommu->reg_phys, iommu->reg_size);
9528c2ecf20Sopenharmony_ci}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci/**
9558c2ecf20Sopenharmony_ci * map_iommu: map the iommu's registers
9568c2ecf20Sopenharmony_ci * @iommu: the iommu to map
9578c2ecf20Sopenharmony_ci * @phys_addr: the physical address of the base resgister
9588c2ecf20Sopenharmony_ci *
9598c2ecf20Sopenharmony_ci * Memory map the iommu's registers.  Start w/ a single page, and
9608c2ecf20Sopenharmony_ci * possibly expand if that turns out to be insufficent.
9618c2ecf20Sopenharmony_ci */
9628c2ecf20Sopenharmony_cistatic int map_iommu(struct intel_iommu *iommu, u64 phys_addr)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	int map_size, err=0;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	iommu->reg_phys = phys_addr;
9678c2ecf20Sopenharmony_ci	iommu->reg_size = VTD_PAGE_SIZE;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	if (!request_mem_region(iommu->reg_phys, iommu->reg_size, iommu->name)) {
9708c2ecf20Sopenharmony_ci		pr_err("Can't reserve memory\n");
9718c2ecf20Sopenharmony_ci		err = -EBUSY;
9728c2ecf20Sopenharmony_ci		goto out;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size);
9768c2ecf20Sopenharmony_ci	if (!iommu->reg) {
9778c2ecf20Sopenharmony_ci		pr_err("Can't map the region\n");
9788c2ecf20Sopenharmony_ci		err = -ENOMEM;
9798c2ecf20Sopenharmony_ci		goto release;
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG);
9838c2ecf20Sopenharmony_ci	iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) {
9868c2ecf20Sopenharmony_ci		err = -EINVAL;
9878c2ecf20Sopenharmony_ci		warn_invalid_dmar(phys_addr, " returns all ones");
9888c2ecf20Sopenharmony_ci		goto unmap;
9898c2ecf20Sopenharmony_ci	}
9908c2ecf20Sopenharmony_ci	if (ecap_vcs(iommu->ecap))
9918c2ecf20Sopenharmony_ci		iommu->vccap = dmar_readq(iommu->reg + DMAR_VCCAP_REG);
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	/* the registers might be more than one page */
9948c2ecf20Sopenharmony_ci	map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap),
9958c2ecf20Sopenharmony_ci			 cap_max_fault_reg_offset(iommu->cap));
9968c2ecf20Sopenharmony_ci	map_size = VTD_PAGE_ALIGN(map_size);
9978c2ecf20Sopenharmony_ci	if (map_size > iommu->reg_size) {
9988c2ecf20Sopenharmony_ci		iounmap(iommu->reg);
9998c2ecf20Sopenharmony_ci		release_mem_region(iommu->reg_phys, iommu->reg_size);
10008c2ecf20Sopenharmony_ci		iommu->reg_size = map_size;
10018c2ecf20Sopenharmony_ci		if (!request_mem_region(iommu->reg_phys, iommu->reg_size,
10028c2ecf20Sopenharmony_ci					iommu->name)) {
10038c2ecf20Sopenharmony_ci			pr_err("Can't reserve memory\n");
10048c2ecf20Sopenharmony_ci			err = -EBUSY;
10058c2ecf20Sopenharmony_ci			goto out;
10068c2ecf20Sopenharmony_ci		}
10078c2ecf20Sopenharmony_ci		iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size);
10088c2ecf20Sopenharmony_ci		if (!iommu->reg) {
10098c2ecf20Sopenharmony_ci			pr_err("Can't map the region\n");
10108c2ecf20Sopenharmony_ci			err = -ENOMEM;
10118c2ecf20Sopenharmony_ci			goto release;
10128c2ecf20Sopenharmony_ci		}
10138c2ecf20Sopenharmony_ci	}
10148c2ecf20Sopenharmony_ci	err = 0;
10158c2ecf20Sopenharmony_ci	goto out;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ciunmap:
10188c2ecf20Sopenharmony_ci	iounmap(iommu->reg);
10198c2ecf20Sopenharmony_cirelease:
10208c2ecf20Sopenharmony_ci	release_mem_region(iommu->reg_phys, iommu->reg_size);
10218c2ecf20Sopenharmony_ciout:
10228c2ecf20Sopenharmony_ci	return err;
10238c2ecf20Sopenharmony_ci}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_cistatic int dmar_alloc_seq_id(struct intel_iommu *iommu)
10268c2ecf20Sopenharmony_ci{
10278c2ecf20Sopenharmony_ci	iommu->seq_id = find_first_zero_bit(dmar_seq_ids,
10288c2ecf20Sopenharmony_ci					    DMAR_UNITS_SUPPORTED);
10298c2ecf20Sopenharmony_ci	if (iommu->seq_id >= DMAR_UNITS_SUPPORTED) {
10308c2ecf20Sopenharmony_ci		iommu->seq_id = -1;
10318c2ecf20Sopenharmony_ci	} else {
10328c2ecf20Sopenharmony_ci		set_bit(iommu->seq_id, dmar_seq_ids);
10338c2ecf20Sopenharmony_ci		sprintf(iommu->name, "dmar%d", iommu->seq_id);
10348c2ecf20Sopenharmony_ci	}
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	return iommu->seq_id;
10378c2ecf20Sopenharmony_ci}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_cistatic void dmar_free_seq_id(struct intel_iommu *iommu)
10408c2ecf20Sopenharmony_ci{
10418c2ecf20Sopenharmony_ci	if (iommu->seq_id >= 0) {
10428c2ecf20Sopenharmony_ci		clear_bit(iommu->seq_id, dmar_seq_ids);
10438c2ecf20Sopenharmony_ci		iommu->seq_id = -1;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic int alloc_iommu(struct dmar_drhd_unit *drhd)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
10508c2ecf20Sopenharmony_ci	u32 ver, sts;
10518c2ecf20Sopenharmony_ci	int agaw = -1;
10528c2ecf20Sopenharmony_ci	int msagaw = -1;
10538c2ecf20Sopenharmony_ci	int err;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	if (!drhd->reg_base_addr) {
10568c2ecf20Sopenharmony_ci		warn_invalid_dmar(0, "");
10578c2ecf20Sopenharmony_ci		return -EINVAL;
10588c2ecf20Sopenharmony_ci	}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
10618c2ecf20Sopenharmony_ci	if (!iommu)
10628c2ecf20Sopenharmony_ci		return -ENOMEM;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	if (dmar_alloc_seq_id(iommu) < 0) {
10658c2ecf20Sopenharmony_ci		pr_err("Failed to allocate seq_id\n");
10668c2ecf20Sopenharmony_ci		err = -ENOSPC;
10678c2ecf20Sopenharmony_ci		goto error;
10688c2ecf20Sopenharmony_ci	}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	err = map_iommu(iommu, drhd->reg_base_addr);
10718c2ecf20Sopenharmony_ci	if (err) {
10728c2ecf20Sopenharmony_ci		pr_err("Failed to map %s\n", iommu->name);
10738c2ecf20Sopenharmony_ci		goto error_free_seq_id;
10748c2ecf20Sopenharmony_ci	}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	err = -EINVAL;
10778c2ecf20Sopenharmony_ci	if (cap_sagaw(iommu->cap) == 0) {
10788c2ecf20Sopenharmony_ci		pr_info("%s: No supported address widths. Not attempting DMA translation.\n",
10798c2ecf20Sopenharmony_ci			iommu->name);
10808c2ecf20Sopenharmony_ci		drhd->ignored = 1;
10818c2ecf20Sopenharmony_ci	}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	if (!drhd->ignored) {
10848c2ecf20Sopenharmony_ci		agaw = iommu_calculate_agaw(iommu);
10858c2ecf20Sopenharmony_ci		if (agaw < 0) {
10868c2ecf20Sopenharmony_ci			pr_err("Cannot get a valid agaw for iommu (seq_id = %d)\n",
10878c2ecf20Sopenharmony_ci			       iommu->seq_id);
10888c2ecf20Sopenharmony_ci			drhd->ignored = 1;
10898c2ecf20Sopenharmony_ci		}
10908c2ecf20Sopenharmony_ci	}
10918c2ecf20Sopenharmony_ci	if (!drhd->ignored) {
10928c2ecf20Sopenharmony_ci		msagaw = iommu_calculate_max_sagaw(iommu);
10938c2ecf20Sopenharmony_ci		if (msagaw < 0) {
10948c2ecf20Sopenharmony_ci			pr_err("Cannot get a valid max agaw for iommu (seq_id = %d)\n",
10958c2ecf20Sopenharmony_ci			       iommu->seq_id);
10968c2ecf20Sopenharmony_ci			drhd->ignored = 1;
10978c2ecf20Sopenharmony_ci			agaw = -1;
10988c2ecf20Sopenharmony_ci		}
10998c2ecf20Sopenharmony_ci	}
11008c2ecf20Sopenharmony_ci	iommu->agaw = agaw;
11018c2ecf20Sopenharmony_ci	iommu->msagaw = msagaw;
11028c2ecf20Sopenharmony_ci	iommu->segment = drhd->segment;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	iommu->node = NUMA_NO_NODE;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	ver = readl(iommu->reg + DMAR_VER_REG);
11078c2ecf20Sopenharmony_ci	pr_info("%s: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n",
11088c2ecf20Sopenharmony_ci		iommu->name,
11098c2ecf20Sopenharmony_ci		(unsigned long long)drhd->reg_base_addr,
11108c2ecf20Sopenharmony_ci		DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
11118c2ecf20Sopenharmony_ci		(unsigned long long)iommu->cap,
11128c2ecf20Sopenharmony_ci		(unsigned long long)iommu->ecap);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	/* Reflect status in gcmd */
11158c2ecf20Sopenharmony_ci	sts = readl(iommu->reg + DMAR_GSTS_REG);
11168c2ecf20Sopenharmony_ci	if (sts & DMA_GSTS_IRES)
11178c2ecf20Sopenharmony_ci		iommu->gcmd |= DMA_GCMD_IRE;
11188c2ecf20Sopenharmony_ci	if (sts & DMA_GSTS_TES)
11198c2ecf20Sopenharmony_ci		iommu->gcmd |= DMA_GCMD_TE;
11208c2ecf20Sopenharmony_ci	if (sts & DMA_GSTS_QIES)
11218c2ecf20Sopenharmony_ci		iommu->gcmd |= DMA_GCMD_QIE;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	raw_spin_lock_init(&iommu->register_lock);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	/*
11268c2ecf20Sopenharmony_ci	 * This is only for hotplug; at boot time intel_iommu_enabled won't
11278c2ecf20Sopenharmony_ci	 * be set yet. When intel_iommu_init() runs, it registers the units
11288c2ecf20Sopenharmony_ci	 * present at boot time, then sets intel_iommu_enabled.
11298c2ecf20Sopenharmony_ci	 */
11308c2ecf20Sopenharmony_ci	if (intel_iommu_enabled && !drhd->ignored) {
11318c2ecf20Sopenharmony_ci		err = iommu_device_sysfs_add(&iommu->iommu, NULL,
11328c2ecf20Sopenharmony_ci					     intel_iommu_groups,
11338c2ecf20Sopenharmony_ci					     "%s", iommu->name);
11348c2ecf20Sopenharmony_ci		if (err)
11358c2ecf20Sopenharmony_ci			goto err_unmap;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci		iommu_device_set_ops(&iommu->iommu, &intel_iommu_ops);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci		err = iommu_device_register(&iommu->iommu);
11408c2ecf20Sopenharmony_ci		if (err)
11418c2ecf20Sopenharmony_ci			goto err_sysfs;
11428c2ecf20Sopenharmony_ci	}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	drhd->iommu = iommu;
11458c2ecf20Sopenharmony_ci	iommu->drhd = drhd;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	return 0;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_cierr_sysfs:
11508c2ecf20Sopenharmony_ci	iommu_device_sysfs_remove(&iommu->iommu);
11518c2ecf20Sopenharmony_cierr_unmap:
11528c2ecf20Sopenharmony_ci	unmap_iommu(iommu);
11538c2ecf20Sopenharmony_cierror_free_seq_id:
11548c2ecf20Sopenharmony_ci	dmar_free_seq_id(iommu);
11558c2ecf20Sopenharmony_cierror:
11568c2ecf20Sopenharmony_ci	kfree(iommu);
11578c2ecf20Sopenharmony_ci	return err;
11588c2ecf20Sopenharmony_ci}
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_cistatic void free_iommu(struct intel_iommu *iommu)
11618c2ecf20Sopenharmony_ci{
11628c2ecf20Sopenharmony_ci	if (intel_iommu_enabled && !iommu->drhd->ignored) {
11638c2ecf20Sopenharmony_ci		iommu_device_unregister(&iommu->iommu);
11648c2ecf20Sopenharmony_ci		iommu_device_sysfs_remove(&iommu->iommu);
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	if (iommu->irq) {
11688c2ecf20Sopenharmony_ci		if (iommu->pr_irq) {
11698c2ecf20Sopenharmony_ci			free_irq(iommu->pr_irq, iommu);
11708c2ecf20Sopenharmony_ci			dmar_free_hwirq(iommu->pr_irq);
11718c2ecf20Sopenharmony_ci			iommu->pr_irq = 0;
11728c2ecf20Sopenharmony_ci		}
11738c2ecf20Sopenharmony_ci		free_irq(iommu->irq, iommu);
11748c2ecf20Sopenharmony_ci		dmar_free_hwirq(iommu->irq);
11758c2ecf20Sopenharmony_ci		iommu->irq = 0;
11768c2ecf20Sopenharmony_ci	}
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	if (iommu->qi) {
11798c2ecf20Sopenharmony_ci		free_page((unsigned long)iommu->qi->desc);
11808c2ecf20Sopenharmony_ci		kfree(iommu->qi->desc_status);
11818c2ecf20Sopenharmony_ci		kfree(iommu->qi);
11828c2ecf20Sopenharmony_ci	}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	if (iommu->reg)
11858c2ecf20Sopenharmony_ci		unmap_iommu(iommu);
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	dmar_free_seq_id(iommu);
11888c2ecf20Sopenharmony_ci	kfree(iommu);
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci/*
11928c2ecf20Sopenharmony_ci * Reclaim all the submitted descriptors which have completed its work.
11938c2ecf20Sopenharmony_ci */
11948c2ecf20Sopenharmony_cistatic inline void reclaim_free_desc(struct q_inval *qi)
11958c2ecf20Sopenharmony_ci{
11968c2ecf20Sopenharmony_ci	while (qi->desc_status[qi->free_tail] == QI_DONE ||
11978c2ecf20Sopenharmony_ci	       qi->desc_status[qi->free_tail] == QI_ABORT) {
11988c2ecf20Sopenharmony_ci		qi->desc_status[qi->free_tail] = QI_FREE;
11998c2ecf20Sopenharmony_ci		qi->free_tail = (qi->free_tail + 1) % QI_LENGTH;
12008c2ecf20Sopenharmony_ci		qi->free_cnt++;
12018c2ecf20Sopenharmony_ci	}
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_cistatic int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
12058c2ecf20Sopenharmony_ci{
12068c2ecf20Sopenharmony_ci	u32 fault;
12078c2ecf20Sopenharmony_ci	int head, tail;
12088c2ecf20Sopenharmony_ci	struct q_inval *qi = iommu->qi;
12098c2ecf20Sopenharmony_ci	int shift = qi_shift(iommu);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	if (qi->desc_status[wait_index] == QI_ABORT)
12128c2ecf20Sopenharmony_ci		return -EAGAIN;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	fault = readl(iommu->reg + DMAR_FSTS_REG);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	/*
12178c2ecf20Sopenharmony_ci	 * If IQE happens, the head points to the descriptor associated
12188c2ecf20Sopenharmony_ci	 * with the error. No new descriptors are fetched until the IQE
12198c2ecf20Sopenharmony_ci	 * is cleared.
12208c2ecf20Sopenharmony_ci	 */
12218c2ecf20Sopenharmony_ci	if (fault & DMA_FSTS_IQE) {
12228c2ecf20Sopenharmony_ci		head = readl(iommu->reg + DMAR_IQH_REG);
12238c2ecf20Sopenharmony_ci		if ((head >> shift) == index) {
12248c2ecf20Sopenharmony_ci			struct qi_desc *desc = qi->desc + head;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci			/*
12278c2ecf20Sopenharmony_ci			 * desc->qw2 and desc->qw3 are either reserved or
12288c2ecf20Sopenharmony_ci			 * used by software as private data. We won't print
12298c2ecf20Sopenharmony_ci			 * out these two qw's for security consideration.
12308c2ecf20Sopenharmony_ci			 */
12318c2ecf20Sopenharmony_ci			pr_err("VT-d detected invalid descriptor: qw0 = %llx, qw1 = %llx\n",
12328c2ecf20Sopenharmony_ci			       (unsigned long long)desc->qw0,
12338c2ecf20Sopenharmony_ci			       (unsigned long long)desc->qw1);
12348c2ecf20Sopenharmony_ci			memcpy(desc, qi->desc + (wait_index << shift),
12358c2ecf20Sopenharmony_ci			       1 << shift);
12368c2ecf20Sopenharmony_ci			writel(DMA_FSTS_IQE, iommu->reg + DMAR_FSTS_REG);
12378c2ecf20Sopenharmony_ci			return -EINVAL;
12388c2ecf20Sopenharmony_ci		}
12398c2ecf20Sopenharmony_ci	}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	/*
12428c2ecf20Sopenharmony_ci	 * If ITE happens, all pending wait_desc commands are aborted.
12438c2ecf20Sopenharmony_ci	 * No new descriptors are fetched until the ITE is cleared.
12448c2ecf20Sopenharmony_ci	 */
12458c2ecf20Sopenharmony_ci	if (fault & DMA_FSTS_ITE) {
12468c2ecf20Sopenharmony_ci		head = readl(iommu->reg + DMAR_IQH_REG);
12478c2ecf20Sopenharmony_ci		head = ((head >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
12488c2ecf20Sopenharmony_ci		head |= 1;
12498c2ecf20Sopenharmony_ci		tail = readl(iommu->reg + DMAR_IQT_REG);
12508c2ecf20Sopenharmony_ci		tail = ((tail >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci		writel(DMA_FSTS_ITE, iommu->reg + DMAR_FSTS_REG);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci		do {
12558c2ecf20Sopenharmony_ci			if (qi->desc_status[head] == QI_IN_USE)
12568c2ecf20Sopenharmony_ci				qi->desc_status[head] = QI_ABORT;
12578c2ecf20Sopenharmony_ci			head = (head - 2 + QI_LENGTH) % QI_LENGTH;
12588c2ecf20Sopenharmony_ci		} while (head != tail);
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci		if (qi->desc_status[wait_index] == QI_ABORT)
12618c2ecf20Sopenharmony_ci			return -EAGAIN;
12628c2ecf20Sopenharmony_ci	}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	if (fault & DMA_FSTS_ICE)
12658c2ecf20Sopenharmony_ci		writel(DMA_FSTS_ICE, iommu->reg + DMAR_FSTS_REG);
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	return 0;
12688c2ecf20Sopenharmony_ci}
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci/*
12718c2ecf20Sopenharmony_ci * Function to submit invalidation descriptors of all types to the queued
12728c2ecf20Sopenharmony_ci * invalidation interface(QI). Multiple descriptors can be submitted at a
12738c2ecf20Sopenharmony_ci * time, a wait descriptor will be appended to each submission to ensure
12748c2ecf20Sopenharmony_ci * hardware has completed the invalidation before return. Wait descriptors
12758c2ecf20Sopenharmony_ci * can be part of the submission but it will not be polled for completion.
12768c2ecf20Sopenharmony_ci */
12778c2ecf20Sopenharmony_ciint qi_submit_sync(struct intel_iommu *iommu, struct qi_desc *desc,
12788c2ecf20Sopenharmony_ci		   unsigned int count, unsigned long options)
12798c2ecf20Sopenharmony_ci{
12808c2ecf20Sopenharmony_ci	struct q_inval *qi = iommu->qi;
12818c2ecf20Sopenharmony_ci	struct qi_desc wait_desc;
12828c2ecf20Sopenharmony_ci	int wait_index, index;
12838c2ecf20Sopenharmony_ci	unsigned long flags;
12848c2ecf20Sopenharmony_ci	int offset, shift;
12858c2ecf20Sopenharmony_ci	int rc, i;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	if (!qi)
12888c2ecf20Sopenharmony_ci		return 0;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_cirestart:
12918c2ecf20Sopenharmony_ci	rc = 0;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&qi->q_lock, flags);
12948c2ecf20Sopenharmony_ci	/*
12958c2ecf20Sopenharmony_ci	 * Check if we have enough empty slots in the queue to submit,
12968c2ecf20Sopenharmony_ci	 * the calculation is based on:
12978c2ecf20Sopenharmony_ci	 * # of desc + 1 wait desc + 1 space between head and tail
12988c2ecf20Sopenharmony_ci	 */
12998c2ecf20Sopenharmony_ci	while (qi->free_cnt < count + 2) {
13008c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&qi->q_lock, flags);
13018c2ecf20Sopenharmony_ci		cpu_relax();
13028c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&qi->q_lock, flags);
13038c2ecf20Sopenharmony_ci	}
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	index = qi->free_head;
13068c2ecf20Sopenharmony_ci	wait_index = (index + count) % QI_LENGTH;
13078c2ecf20Sopenharmony_ci	shift = qi_shift(iommu);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
13108c2ecf20Sopenharmony_ci		offset = ((index + i) % QI_LENGTH) << shift;
13118c2ecf20Sopenharmony_ci		memcpy(qi->desc + offset, &desc[i], 1 << shift);
13128c2ecf20Sopenharmony_ci		qi->desc_status[(index + i) % QI_LENGTH] = QI_IN_USE;
13138c2ecf20Sopenharmony_ci	}
13148c2ecf20Sopenharmony_ci	qi->desc_status[wait_index] = QI_IN_USE;
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	wait_desc.qw0 = QI_IWD_STATUS_DATA(QI_DONE) |
13178c2ecf20Sopenharmony_ci			QI_IWD_STATUS_WRITE | QI_IWD_TYPE;
13188c2ecf20Sopenharmony_ci	if (options & QI_OPT_WAIT_DRAIN)
13198c2ecf20Sopenharmony_ci		wait_desc.qw0 |= QI_IWD_PRQ_DRAIN;
13208c2ecf20Sopenharmony_ci	wait_desc.qw1 = virt_to_phys(&qi->desc_status[wait_index]);
13218c2ecf20Sopenharmony_ci	wait_desc.qw2 = 0;
13228c2ecf20Sopenharmony_ci	wait_desc.qw3 = 0;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	offset = wait_index << shift;
13258c2ecf20Sopenharmony_ci	memcpy(qi->desc + offset, &wait_desc, 1 << shift);
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	qi->free_head = (qi->free_head + count + 1) % QI_LENGTH;
13288c2ecf20Sopenharmony_ci	qi->free_cnt -= count + 1;
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	/*
13318c2ecf20Sopenharmony_ci	 * update the HW tail register indicating the presence of
13328c2ecf20Sopenharmony_ci	 * new descriptors.
13338c2ecf20Sopenharmony_ci	 */
13348c2ecf20Sopenharmony_ci	writel(qi->free_head << shift, iommu->reg + DMAR_IQT_REG);
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	while (qi->desc_status[wait_index] != QI_DONE) {
13378c2ecf20Sopenharmony_ci		/*
13388c2ecf20Sopenharmony_ci		 * We will leave the interrupts disabled, to prevent interrupt
13398c2ecf20Sopenharmony_ci		 * context to queue another cmd while a cmd is already submitted
13408c2ecf20Sopenharmony_ci		 * and waiting for completion on this cpu. This is to avoid
13418c2ecf20Sopenharmony_ci		 * a deadlock where the interrupt context can wait indefinitely
13428c2ecf20Sopenharmony_ci		 * for free slots in the queue.
13438c2ecf20Sopenharmony_ci		 */
13448c2ecf20Sopenharmony_ci		rc = qi_check_fault(iommu, index, wait_index);
13458c2ecf20Sopenharmony_ci		if (rc)
13468c2ecf20Sopenharmony_ci			break;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci		raw_spin_unlock(&qi->q_lock);
13498c2ecf20Sopenharmony_ci		cpu_relax();
13508c2ecf20Sopenharmony_ci		raw_spin_lock(&qi->q_lock);
13518c2ecf20Sopenharmony_ci	}
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++)
13548c2ecf20Sopenharmony_ci		qi->desc_status[(index + i) % QI_LENGTH] = QI_DONE;
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	reclaim_free_desc(qi);
13578c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&qi->q_lock, flags);
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	if (rc == -EAGAIN)
13608c2ecf20Sopenharmony_ci		goto restart;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	return rc;
13638c2ecf20Sopenharmony_ci}
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci/*
13668c2ecf20Sopenharmony_ci * Flush the global interrupt entry cache.
13678c2ecf20Sopenharmony_ci */
13688c2ecf20Sopenharmony_civoid qi_global_iec(struct intel_iommu *iommu)
13698c2ecf20Sopenharmony_ci{
13708c2ecf20Sopenharmony_ci	struct qi_desc desc;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	desc.qw0 = QI_IEC_TYPE;
13738c2ecf20Sopenharmony_ci	desc.qw1 = 0;
13748c2ecf20Sopenharmony_ci	desc.qw2 = 0;
13758c2ecf20Sopenharmony_ci	desc.qw3 = 0;
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	/* should never fail */
13788c2ecf20Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
13798c2ecf20Sopenharmony_ci}
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_civoid qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm,
13828c2ecf20Sopenharmony_ci		      u64 type)
13838c2ecf20Sopenharmony_ci{
13848c2ecf20Sopenharmony_ci	struct qi_desc desc;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	desc.qw0 = QI_CC_FM(fm) | QI_CC_SID(sid) | QI_CC_DID(did)
13878c2ecf20Sopenharmony_ci			| QI_CC_GRAN(type) | QI_CC_TYPE;
13888c2ecf20Sopenharmony_ci	desc.qw1 = 0;
13898c2ecf20Sopenharmony_ci	desc.qw2 = 0;
13908c2ecf20Sopenharmony_ci	desc.qw3 = 0;
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
13938c2ecf20Sopenharmony_ci}
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_civoid qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr,
13968c2ecf20Sopenharmony_ci		    unsigned int size_order, u64 type)
13978c2ecf20Sopenharmony_ci{
13988c2ecf20Sopenharmony_ci	u8 dw = 0, dr = 0;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	struct qi_desc desc;
14018c2ecf20Sopenharmony_ci	int ih = 0;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	if (cap_write_drain(iommu->cap))
14048c2ecf20Sopenharmony_ci		dw = 1;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	if (cap_read_drain(iommu->cap))
14078c2ecf20Sopenharmony_ci		dr = 1;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	desc.qw0 = QI_IOTLB_DID(did) | QI_IOTLB_DR(dr) | QI_IOTLB_DW(dw)
14108c2ecf20Sopenharmony_ci		| QI_IOTLB_GRAN(type) | QI_IOTLB_TYPE;
14118c2ecf20Sopenharmony_ci	desc.qw1 = QI_IOTLB_ADDR(addr) | QI_IOTLB_IH(ih)
14128c2ecf20Sopenharmony_ci		| QI_IOTLB_AM(size_order);
14138c2ecf20Sopenharmony_ci	desc.qw2 = 0;
14148c2ecf20Sopenharmony_ci	desc.qw3 = 0;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
14178c2ecf20Sopenharmony_ci}
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_civoid qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 pfsid,
14208c2ecf20Sopenharmony_ci			u16 qdep, u64 addr, unsigned mask)
14218c2ecf20Sopenharmony_ci{
14228c2ecf20Sopenharmony_ci	struct qi_desc desc;
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	if (mask) {
14258c2ecf20Sopenharmony_ci		addr |= (1ULL << (VTD_PAGE_SHIFT + mask - 1)) - 1;
14268c2ecf20Sopenharmony_ci		desc.qw1 = QI_DEV_IOTLB_ADDR(addr) | QI_DEV_IOTLB_SIZE;
14278c2ecf20Sopenharmony_ci	} else
14288c2ecf20Sopenharmony_ci		desc.qw1 = QI_DEV_IOTLB_ADDR(addr);
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	if (qdep >= QI_DEV_IOTLB_MAX_INVS)
14318c2ecf20Sopenharmony_ci		qdep = 0;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	desc.qw0 = QI_DEV_IOTLB_SID(sid) | QI_DEV_IOTLB_QDEP(qdep) |
14348c2ecf20Sopenharmony_ci		   QI_DIOTLB_TYPE | QI_DEV_IOTLB_PFSID(pfsid);
14358c2ecf20Sopenharmony_ci	desc.qw2 = 0;
14368c2ecf20Sopenharmony_ci	desc.qw3 = 0;
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
14398c2ecf20Sopenharmony_ci}
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci/* PASID-based IOTLB invalidation */
14428c2ecf20Sopenharmony_civoid qi_flush_piotlb(struct intel_iommu *iommu, u16 did, u32 pasid, u64 addr,
14438c2ecf20Sopenharmony_ci		     unsigned long npages, bool ih)
14448c2ecf20Sopenharmony_ci{
14458c2ecf20Sopenharmony_ci	struct qi_desc desc = {.qw2 = 0, .qw3 = 0};
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	/*
14488c2ecf20Sopenharmony_ci	 * npages == -1 means a PASID-selective invalidation, otherwise,
14498c2ecf20Sopenharmony_ci	 * a positive value for Page-selective-within-PASID invalidation.
14508c2ecf20Sopenharmony_ci	 * 0 is not a valid input.
14518c2ecf20Sopenharmony_ci	 */
14528c2ecf20Sopenharmony_ci	if (WARN_ON(!npages)) {
14538c2ecf20Sopenharmony_ci		pr_err("Invalid input npages = %ld\n", npages);
14548c2ecf20Sopenharmony_ci		return;
14558c2ecf20Sopenharmony_ci	}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	if (npages == -1) {
14588c2ecf20Sopenharmony_ci		desc.qw0 = QI_EIOTLB_PASID(pasid) |
14598c2ecf20Sopenharmony_ci				QI_EIOTLB_DID(did) |
14608c2ecf20Sopenharmony_ci				QI_EIOTLB_GRAN(QI_GRAN_NONG_PASID) |
14618c2ecf20Sopenharmony_ci				QI_EIOTLB_TYPE;
14628c2ecf20Sopenharmony_ci		desc.qw1 = 0;
14638c2ecf20Sopenharmony_ci	} else {
14648c2ecf20Sopenharmony_ci		int mask = ilog2(__roundup_pow_of_two(npages));
14658c2ecf20Sopenharmony_ci		unsigned long align = (1ULL << (VTD_PAGE_SHIFT + mask));
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!IS_ALIGNED(addr, align)))
14688c2ecf20Sopenharmony_ci			addr = ALIGN_DOWN(addr, align);
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci		desc.qw0 = QI_EIOTLB_PASID(pasid) |
14718c2ecf20Sopenharmony_ci				QI_EIOTLB_DID(did) |
14728c2ecf20Sopenharmony_ci				QI_EIOTLB_GRAN(QI_GRAN_PSI_PASID) |
14738c2ecf20Sopenharmony_ci				QI_EIOTLB_TYPE;
14748c2ecf20Sopenharmony_ci		desc.qw1 = QI_EIOTLB_ADDR(addr) |
14758c2ecf20Sopenharmony_ci				QI_EIOTLB_IH(ih) |
14768c2ecf20Sopenharmony_ci				QI_EIOTLB_AM(mask);
14778c2ecf20Sopenharmony_ci	}
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
14808c2ecf20Sopenharmony_ci}
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci/* PASID-based device IOTLB Invalidate */
14838c2ecf20Sopenharmony_civoid qi_flush_dev_iotlb_pasid(struct intel_iommu *iommu, u16 sid, u16 pfsid,
14848c2ecf20Sopenharmony_ci			      u32 pasid,  u16 qdep, u64 addr, unsigned int size_order)
14858c2ecf20Sopenharmony_ci{
14868c2ecf20Sopenharmony_ci	unsigned long mask = 1UL << (VTD_PAGE_SHIFT + size_order - 1);
14878c2ecf20Sopenharmony_ci	struct qi_desc desc = {.qw1 = 0, .qw2 = 0, .qw3 = 0};
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	desc.qw0 = QI_DEV_EIOTLB_PASID(pasid) | QI_DEV_EIOTLB_SID(sid) |
14908c2ecf20Sopenharmony_ci		QI_DEV_EIOTLB_QDEP(qdep) | QI_DEIOTLB_TYPE |
14918c2ecf20Sopenharmony_ci		QI_DEV_IOTLB_PFSID(pfsid);
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	/*
14948c2ecf20Sopenharmony_ci	 * If S bit is 0, we only flush a single page. If S bit is set,
14958c2ecf20Sopenharmony_ci	 * The least significant zero bit indicates the invalidation address
14968c2ecf20Sopenharmony_ci	 * range. VT-d spec 6.5.2.6.
14978c2ecf20Sopenharmony_ci	 * e.g. address bit 12[0] indicates 8KB, 13[0] indicates 16KB.
14988c2ecf20Sopenharmony_ci	 * size order = 0 is PAGE_SIZE 4KB
14998c2ecf20Sopenharmony_ci	 * Max Invs Pending (MIP) is set to 0 for now until we have DIT in
15008c2ecf20Sopenharmony_ci	 * ECAP.
15018c2ecf20Sopenharmony_ci	 */
15028c2ecf20Sopenharmony_ci	if (!IS_ALIGNED(addr, VTD_PAGE_SIZE << size_order))
15038c2ecf20Sopenharmony_ci		pr_warn_ratelimited("Invalidate non-aligned address %llx, order %d\n",
15048c2ecf20Sopenharmony_ci				    addr, size_order);
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	/* Take page address */
15078c2ecf20Sopenharmony_ci	desc.qw1 = QI_DEV_EIOTLB_ADDR(addr);
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	if (size_order) {
15108c2ecf20Sopenharmony_ci		/*
15118c2ecf20Sopenharmony_ci		 * Existing 0s in address below size_order may be the least
15128c2ecf20Sopenharmony_ci		 * significant bit, we must set them to 1s to avoid having
15138c2ecf20Sopenharmony_ci		 * smaller size than desired.
15148c2ecf20Sopenharmony_ci		 */
15158c2ecf20Sopenharmony_ci		desc.qw1 |= GENMASK_ULL(size_order + VTD_PAGE_SHIFT - 1,
15168c2ecf20Sopenharmony_ci					VTD_PAGE_SHIFT);
15178c2ecf20Sopenharmony_ci		/* Clear size_order bit to indicate size */
15188c2ecf20Sopenharmony_ci		desc.qw1 &= ~mask;
15198c2ecf20Sopenharmony_ci		/* Set the S bit to indicate flushing more than 1 page */
15208c2ecf20Sopenharmony_ci		desc.qw1 |= QI_DEV_EIOTLB_SIZE;
15218c2ecf20Sopenharmony_ci	}
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
15248c2ecf20Sopenharmony_ci}
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_civoid qi_flush_pasid_cache(struct intel_iommu *iommu, u16 did,
15278c2ecf20Sopenharmony_ci			  u64 granu, u32 pasid)
15288c2ecf20Sopenharmony_ci{
15298c2ecf20Sopenharmony_ci	struct qi_desc desc = {.qw1 = 0, .qw2 = 0, .qw3 = 0};
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	desc.qw0 = QI_PC_PASID(pasid) | QI_PC_DID(did) |
15328c2ecf20Sopenharmony_ci			QI_PC_GRAN(granu) | QI_PC_TYPE;
15338c2ecf20Sopenharmony_ci	qi_submit_sync(iommu, &desc, 1, 0);
15348c2ecf20Sopenharmony_ci}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci/*
15378c2ecf20Sopenharmony_ci * Disable Queued Invalidation interface.
15388c2ecf20Sopenharmony_ci */
15398c2ecf20Sopenharmony_civoid dmar_disable_qi(struct intel_iommu *iommu)
15408c2ecf20Sopenharmony_ci{
15418c2ecf20Sopenharmony_ci	unsigned long flags;
15428c2ecf20Sopenharmony_ci	u32 sts;
15438c2ecf20Sopenharmony_ci	cycles_t start_time = get_cycles();
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	if (!ecap_qis(iommu->ecap))
15468c2ecf20Sopenharmony_ci		return;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flags);
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	sts =  readl(iommu->reg + DMAR_GSTS_REG);
15518c2ecf20Sopenharmony_ci	if (!(sts & DMA_GSTS_QIES))
15528c2ecf20Sopenharmony_ci		goto end;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	/*
15558c2ecf20Sopenharmony_ci	 * Give a chance to HW to complete the pending invalidation requests.
15568c2ecf20Sopenharmony_ci	 */
15578c2ecf20Sopenharmony_ci	while ((readl(iommu->reg + DMAR_IQT_REG) !=
15588c2ecf20Sopenharmony_ci		readl(iommu->reg + DMAR_IQH_REG)) &&
15598c2ecf20Sopenharmony_ci		(DMAR_OPERATION_TIMEOUT > (get_cycles() - start_time)))
15608c2ecf20Sopenharmony_ci		cpu_relax();
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	iommu->gcmd &= ~DMA_GCMD_QIE;
15638c2ecf20Sopenharmony_ci	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl,
15668c2ecf20Sopenharmony_ci		      !(sts & DMA_GSTS_QIES), sts);
15678c2ecf20Sopenharmony_ciend:
15688c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
15698c2ecf20Sopenharmony_ci}
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci/*
15728c2ecf20Sopenharmony_ci * Enable queued invalidation.
15738c2ecf20Sopenharmony_ci */
15748c2ecf20Sopenharmony_cistatic void __dmar_enable_qi(struct intel_iommu *iommu)
15758c2ecf20Sopenharmony_ci{
15768c2ecf20Sopenharmony_ci	u32 sts;
15778c2ecf20Sopenharmony_ci	unsigned long flags;
15788c2ecf20Sopenharmony_ci	struct q_inval *qi = iommu->qi;
15798c2ecf20Sopenharmony_ci	u64 val = virt_to_phys(qi->desc);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	qi->free_head = qi->free_tail = 0;
15828c2ecf20Sopenharmony_ci	qi->free_cnt = QI_LENGTH;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	/*
15858c2ecf20Sopenharmony_ci	 * Set DW=1 and QS=1 in IQA_REG when Scalable Mode capability
15868c2ecf20Sopenharmony_ci	 * is present.
15878c2ecf20Sopenharmony_ci	 */
15888c2ecf20Sopenharmony_ci	if (ecap_smts(iommu->ecap))
15898c2ecf20Sopenharmony_ci		val |= (1 << 11) | 1;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flags);
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	/* write zero to the tail reg */
15948c2ecf20Sopenharmony_ci	writel(0, iommu->reg + DMAR_IQT_REG);
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	dmar_writeq(iommu->reg + DMAR_IQA_REG, val);
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	iommu->gcmd |= DMA_GCMD_QIE;
15998c2ecf20Sopenharmony_ci	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	/* Make sure hardware complete it */
16028c2ecf20Sopenharmony_ci	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts);
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
16058c2ecf20Sopenharmony_ci}
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci/*
16088c2ecf20Sopenharmony_ci * Enable Queued Invalidation interface. This is a must to support
16098c2ecf20Sopenharmony_ci * interrupt-remapping. Also used by DMA-remapping, which replaces
16108c2ecf20Sopenharmony_ci * register based IOTLB invalidation.
16118c2ecf20Sopenharmony_ci */
16128c2ecf20Sopenharmony_ciint dmar_enable_qi(struct intel_iommu *iommu)
16138c2ecf20Sopenharmony_ci{
16148c2ecf20Sopenharmony_ci	struct q_inval *qi;
16158c2ecf20Sopenharmony_ci	struct page *desc_page;
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	if (!ecap_qis(iommu->ecap))
16188c2ecf20Sopenharmony_ci		return -ENOENT;
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	/*
16218c2ecf20Sopenharmony_ci	 * queued invalidation is already setup and enabled.
16228c2ecf20Sopenharmony_ci	 */
16238c2ecf20Sopenharmony_ci	if (iommu->qi)
16248c2ecf20Sopenharmony_ci		return 0;
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	iommu->qi = kmalloc(sizeof(*qi), GFP_ATOMIC);
16278c2ecf20Sopenharmony_ci	if (!iommu->qi)
16288c2ecf20Sopenharmony_ci		return -ENOMEM;
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	qi = iommu->qi;
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	/*
16338c2ecf20Sopenharmony_ci	 * Need two pages to accommodate 256 descriptors of 256 bits each
16348c2ecf20Sopenharmony_ci	 * if the remapping hardware supports scalable mode translation.
16358c2ecf20Sopenharmony_ci	 */
16368c2ecf20Sopenharmony_ci	desc_page = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO,
16378c2ecf20Sopenharmony_ci				     !!ecap_smts(iommu->ecap));
16388c2ecf20Sopenharmony_ci	if (!desc_page) {
16398c2ecf20Sopenharmony_ci		kfree(qi);
16408c2ecf20Sopenharmony_ci		iommu->qi = NULL;
16418c2ecf20Sopenharmony_ci		return -ENOMEM;
16428c2ecf20Sopenharmony_ci	}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	qi->desc = page_address(desc_page);
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	qi->desc_status = kcalloc(QI_LENGTH, sizeof(int), GFP_ATOMIC);
16478c2ecf20Sopenharmony_ci	if (!qi->desc_status) {
16488c2ecf20Sopenharmony_ci		free_page((unsigned long) qi->desc);
16498c2ecf20Sopenharmony_ci		kfree(qi);
16508c2ecf20Sopenharmony_ci		iommu->qi = NULL;
16518c2ecf20Sopenharmony_ci		return -ENOMEM;
16528c2ecf20Sopenharmony_ci	}
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	raw_spin_lock_init(&qi->q_lock);
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci	__dmar_enable_qi(iommu);
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	return 0;
16598c2ecf20Sopenharmony_ci}
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci/* iommu interrupt handling. Most stuff are MSI-like. */
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_cienum faulttype {
16648c2ecf20Sopenharmony_ci	DMA_REMAP,
16658c2ecf20Sopenharmony_ci	INTR_REMAP,
16668c2ecf20Sopenharmony_ci	UNKNOWN,
16678c2ecf20Sopenharmony_ci};
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_cistatic const char *dma_remap_fault_reasons[] =
16708c2ecf20Sopenharmony_ci{
16718c2ecf20Sopenharmony_ci	"Software",
16728c2ecf20Sopenharmony_ci	"Present bit in root entry is clear",
16738c2ecf20Sopenharmony_ci	"Present bit in context entry is clear",
16748c2ecf20Sopenharmony_ci	"Invalid context entry",
16758c2ecf20Sopenharmony_ci	"Access beyond MGAW",
16768c2ecf20Sopenharmony_ci	"PTE Write access is not set",
16778c2ecf20Sopenharmony_ci	"PTE Read access is not set",
16788c2ecf20Sopenharmony_ci	"Next page table ptr is invalid",
16798c2ecf20Sopenharmony_ci	"Root table address invalid",
16808c2ecf20Sopenharmony_ci	"Context table ptr is invalid",
16818c2ecf20Sopenharmony_ci	"non-zero reserved fields in RTP",
16828c2ecf20Sopenharmony_ci	"non-zero reserved fields in CTP",
16838c2ecf20Sopenharmony_ci	"non-zero reserved fields in PTE",
16848c2ecf20Sopenharmony_ci	"PCE for translation request specifies blocking",
16858c2ecf20Sopenharmony_ci};
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_cistatic const char * const dma_remap_sm_fault_reasons[] = {
16888c2ecf20Sopenharmony_ci	"SM: Invalid Root Table Address",
16898c2ecf20Sopenharmony_ci	"SM: TTM 0 for request with PASID",
16908c2ecf20Sopenharmony_ci	"SM: TTM 0 for page group request",
16918c2ecf20Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x33-0x37 */
16928c2ecf20Sopenharmony_ci	"SM: Error attempting to access Root Entry",
16938c2ecf20Sopenharmony_ci	"SM: Present bit in Root Entry is clear",
16948c2ecf20Sopenharmony_ci	"SM: Non-zero reserved field set in Root Entry",
16958c2ecf20Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x3B-0x3F */
16968c2ecf20Sopenharmony_ci	"SM: Error attempting to access Context Entry",
16978c2ecf20Sopenharmony_ci	"SM: Present bit in Context Entry is clear",
16988c2ecf20Sopenharmony_ci	"SM: Non-zero reserved field set in the Context Entry",
16998c2ecf20Sopenharmony_ci	"SM: Invalid Context Entry",
17008c2ecf20Sopenharmony_ci	"SM: DTE field in Context Entry is clear",
17018c2ecf20Sopenharmony_ci	"SM: PASID Enable field in Context Entry is clear",
17028c2ecf20Sopenharmony_ci	"SM: PASID is larger than the max in Context Entry",
17038c2ecf20Sopenharmony_ci	"SM: PRE field in Context-Entry is clear",
17048c2ecf20Sopenharmony_ci	"SM: RID_PASID field error in Context-Entry",
17058c2ecf20Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x49-0x4F */
17068c2ecf20Sopenharmony_ci	"SM: Error attempting to access the PASID Directory Entry",
17078c2ecf20Sopenharmony_ci	"SM: Present bit in Directory Entry is clear",
17088c2ecf20Sopenharmony_ci	"SM: Non-zero reserved field set in PASID Directory Entry",
17098c2ecf20Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x53-0x57 */
17108c2ecf20Sopenharmony_ci	"SM: Error attempting to access PASID Table Entry",
17118c2ecf20Sopenharmony_ci	"SM: Present bit in PASID Table Entry is clear",
17128c2ecf20Sopenharmony_ci	"SM: Non-zero reserved field set in PASID Table Entry",
17138c2ecf20Sopenharmony_ci	"SM: Invalid Scalable-Mode PASID Table Entry",
17148c2ecf20Sopenharmony_ci	"SM: ERE field is clear in PASID Table Entry",
17158c2ecf20Sopenharmony_ci	"SM: SRE field is clear in PASID Table Entry",
17168c2ecf20Sopenharmony_ci	"Unknown", "Unknown",/* 0x5E-0x5F */
17178c2ecf20Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x60-0x67 */
17188c2ecf20Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x68-0x6F */
17198c2ecf20Sopenharmony_ci	"SM: Error attempting to access first-level paging entry",
17208c2ecf20Sopenharmony_ci	"SM: Present bit in first-level paging entry is clear",
17218c2ecf20Sopenharmony_ci	"SM: Non-zero reserved field set in first-level paging entry",
17228c2ecf20Sopenharmony_ci	"SM: Error attempting to access FL-PML4 entry",
17238c2ecf20Sopenharmony_ci	"SM: First-level entry address beyond MGAW in Nested translation",
17248c2ecf20Sopenharmony_ci	"SM: Read permission error in FL-PML4 entry in Nested translation",
17258c2ecf20Sopenharmony_ci	"SM: Read permission error in first-level paging entry in Nested translation",
17268c2ecf20Sopenharmony_ci	"SM: Write permission error in first-level paging entry in Nested translation",
17278c2ecf20Sopenharmony_ci	"SM: Error attempting to access second-level paging entry",
17288c2ecf20Sopenharmony_ci	"SM: Read/Write permission error in second-level paging entry",
17298c2ecf20Sopenharmony_ci	"SM: Non-zero reserved field set in second-level paging entry",
17308c2ecf20Sopenharmony_ci	"SM: Invalid second-level page table pointer",
17318c2ecf20Sopenharmony_ci	"SM: A/D bit update needed in second-level entry when set up in no snoop",
17328c2ecf20Sopenharmony_ci	"Unknown", "Unknown", "Unknown", /* 0x7D-0x7F */
17338c2ecf20Sopenharmony_ci	"SM: Address in first-level translation is not canonical",
17348c2ecf20Sopenharmony_ci	"SM: U/S set 0 for first-level translation with user privilege",
17358c2ecf20Sopenharmony_ci	"SM: No execute permission for request with PASID and ER=1",
17368c2ecf20Sopenharmony_ci	"SM: Address beyond the DMA hardware max",
17378c2ecf20Sopenharmony_ci	"SM: Second-level entry address beyond the max",
17388c2ecf20Sopenharmony_ci	"SM: No write permission for Write/AtomicOp request",
17398c2ecf20Sopenharmony_ci	"SM: No read permission for Read/AtomicOp request",
17408c2ecf20Sopenharmony_ci	"SM: Invalid address-interrupt address",
17418c2ecf20Sopenharmony_ci	"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x88-0x8F */
17428c2ecf20Sopenharmony_ci	"SM: A/D bit update needed in first-level entry when set up in no snoop",
17438c2ecf20Sopenharmony_ci};
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_cistatic const char *irq_remap_fault_reasons[] =
17468c2ecf20Sopenharmony_ci{
17478c2ecf20Sopenharmony_ci	"Detected reserved fields in the decoded interrupt-remapped request",
17488c2ecf20Sopenharmony_ci	"Interrupt index exceeded the interrupt-remapping table size",
17498c2ecf20Sopenharmony_ci	"Present field in the IRTE entry is clear",
17508c2ecf20Sopenharmony_ci	"Error accessing interrupt-remapping table pointed by IRTA_REG",
17518c2ecf20Sopenharmony_ci	"Detected reserved fields in the IRTE entry",
17528c2ecf20Sopenharmony_ci	"Blocked a compatibility format interrupt request",
17538c2ecf20Sopenharmony_ci	"Blocked an interrupt request due to source-id verification failure",
17548c2ecf20Sopenharmony_ci};
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_cistatic const char *dmar_get_fault_reason(u8 fault_reason, int *fault_type)
17578c2ecf20Sopenharmony_ci{
17588c2ecf20Sopenharmony_ci	if (fault_reason >= 0x20 && (fault_reason - 0x20 <
17598c2ecf20Sopenharmony_ci					ARRAY_SIZE(irq_remap_fault_reasons))) {
17608c2ecf20Sopenharmony_ci		*fault_type = INTR_REMAP;
17618c2ecf20Sopenharmony_ci		return irq_remap_fault_reasons[fault_reason - 0x20];
17628c2ecf20Sopenharmony_ci	} else if (fault_reason >= 0x30 && (fault_reason - 0x30 <
17638c2ecf20Sopenharmony_ci			ARRAY_SIZE(dma_remap_sm_fault_reasons))) {
17648c2ecf20Sopenharmony_ci		*fault_type = DMA_REMAP;
17658c2ecf20Sopenharmony_ci		return dma_remap_sm_fault_reasons[fault_reason - 0x30];
17668c2ecf20Sopenharmony_ci	} else if (fault_reason < ARRAY_SIZE(dma_remap_fault_reasons)) {
17678c2ecf20Sopenharmony_ci		*fault_type = DMA_REMAP;
17688c2ecf20Sopenharmony_ci		return dma_remap_fault_reasons[fault_reason];
17698c2ecf20Sopenharmony_ci	} else {
17708c2ecf20Sopenharmony_ci		*fault_type = UNKNOWN;
17718c2ecf20Sopenharmony_ci		return "Unknown";
17728c2ecf20Sopenharmony_ci	}
17738c2ecf20Sopenharmony_ci}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_cistatic inline int dmar_msi_reg(struct intel_iommu *iommu, int irq)
17778c2ecf20Sopenharmony_ci{
17788c2ecf20Sopenharmony_ci	if (iommu->irq == irq)
17798c2ecf20Sopenharmony_ci		return DMAR_FECTL_REG;
17808c2ecf20Sopenharmony_ci	else if (iommu->pr_irq == irq)
17818c2ecf20Sopenharmony_ci		return DMAR_PECTL_REG;
17828c2ecf20Sopenharmony_ci	else
17838c2ecf20Sopenharmony_ci		BUG();
17848c2ecf20Sopenharmony_ci}
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_civoid dmar_msi_unmask(struct irq_data *data)
17878c2ecf20Sopenharmony_ci{
17888c2ecf20Sopenharmony_ci	struct intel_iommu *iommu = irq_data_get_irq_handler_data(data);
17898c2ecf20Sopenharmony_ci	int reg = dmar_msi_reg(iommu, data->irq);
17908c2ecf20Sopenharmony_ci	unsigned long flag;
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	/* unmask it */
17938c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flag);
17948c2ecf20Sopenharmony_ci	writel(0, iommu->reg + reg);
17958c2ecf20Sopenharmony_ci	/* Read a reg to force flush the post write */
17968c2ecf20Sopenharmony_ci	readl(iommu->reg + reg);
17978c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
17988c2ecf20Sopenharmony_ci}
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_civoid dmar_msi_mask(struct irq_data *data)
18018c2ecf20Sopenharmony_ci{
18028c2ecf20Sopenharmony_ci	struct intel_iommu *iommu = irq_data_get_irq_handler_data(data);
18038c2ecf20Sopenharmony_ci	int reg = dmar_msi_reg(iommu, data->irq);
18048c2ecf20Sopenharmony_ci	unsigned long flag;
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	/* mask it */
18078c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flag);
18088c2ecf20Sopenharmony_ci	writel(DMA_FECTL_IM, iommu->reg + reg);
18098c2ecf20Sopenharmony_ci	/* Read a reg to force flush the post write */
18108c2ecf20Sopenharmony_ci	readl(iommu->reg + reg);
18118c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
18128c2ecf20Sopenharmony_ci}
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_civoid dmar_msi_write(int irq, struct msi_msg *msg)
18158c2ecf20Sopenharmony_ci{
18168c2ecf20Sopenharmony_ci	struct intel_iommu *iommu = irq_get_handler_data(irq);
18178c2ecf20Sopenharmony_ci	int reg = dmar_msi_reg(iommu, irq);
18188c2ecf20Sopenharmony_ci	unsigned long flag;
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flag);
18218c2ecf20Sopenharmony_ci	writel(msg->data, iommu->reg + reg + 4);
18228c2ecf20Sopenharmony_ci	writel(msg->address_lo, iommu->reg + reg + 8);
18238c2ecf20Sopenharmony_ci	writel(msg->address_hi, iommu->reg + reg + 12);
18248c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
18258c2ecf20Sopenharmony_ci}
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_civoid dmar_msi_read(int irq, struct msi_msg *msg)
18288c2ecf20Sopenharmony_ci{
18298c2ecf20Sopenharmony_ci	struct intel_iommu *iommu = irq_get_handler_data(irq);
18308c2ecf20Sopenharmony_ci	int reg = dmar_msi_reg(iommu, irq);
18318c2ecf20Sopenharmony_ci	unsigned long flag;
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flag);
18348c2ecf20Sopenharmony_ci	msg->data = readl(iommu->reg + reg + 4);
18358c2ecf20Sopenharmony_ci	msg->address_lo = readl(iommu->reg + reg + 8);
18368c2ecf20Sopenharmony_ci	msg->address_hi = readl(iommu->reg + reg + 12);
18378c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
18388c2ecf20Sopenharmony_ci}
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_cistatic int dmar_fault_do_one(struct intel_iommu *iommu, int type,
18418c2ecf20Sopenharmony_ci		u8 fault_reason, u32 pasid, u16 source_id,
18428c2ecf20Sopenharmony_ci		unsigned long long addr)
18438c2ecf20Sopenharmony_ci{
18448c2ecf20Sopenharmony_ci	const char *reason;
18458c2ecf20Sopenharmony_ci	int fault_type;
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	reason = dmar_get_fault_reason(fault_reason, &fault_type);
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	if (fault_type == INTR_REMAP)
18508c2ecf20Sopenharmony_ci		pr_err("[INTR-REMAP] Request device [%02x:%02x.%d] fault index %llx [fault reason %02d] %s\n",
18518c2ecf20Sopenharmony_ci			source_id >> 8, PCI_SLOT(source_id & 0xFF),
18528c2ecf20Sopenharmony_ci			PCI_FUNC(source_id & 0xFF), addr >> 48,
18538c2ecf20Sopenharmony_ci			fault_reason, reason);
18548c2ecf20Sopenharmony_ci	else
18558c2ecf20Sopenharmony_ci		pr_err("[%s] Request device [%02x:%02x.%d] PASID %x fault addr %llx [fault reason %02d] %s\n",
18568c2ecf20Sopenharmony_ci		       type ? "DMA Read" : "DMA Write",
18578c2ecf20Sopenharmony_ci		       source_id >> 8, PCI_SLOT(source_id & 0xFF),
18588c2ecf20Sopenharmony_ci		       PCI_FUNC(source_id & 0xFF), pasid, addr,
18598c2ecf20Sopenharmony_ci		       fault_reason, reason);
18608c2ecf20Sopenharmony_ci	return 0;
18618c2ecf20Sopenharmony_ci}
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci#define PRIMARY_FAULT_REG_LEN (16)
18648c2ecf20Sopenharmony_ciirqreturn_t dmar_fault(int irq, void *dev_id)
18658c2ecf20Sopenharmony_ci{
18668c2ecf20Sopenharmony_ci	struct intel_iommu *iommu = dev_id;
18678c2ecf20Sopenharmony_ci	int reg, fault_index;
18688c2ecf20Sopenharmony_ci	u32 fault_status;
18698c2ecf20Sopenharmony_ci	unsigned long flag;
18708c2ecf20Sopenharmony_ci	static DEFINE_RATELIMIT_STATE(rs,
18718c2ecf20Sopenharmony_ci				      DEFAULT_RATELIMIT_INTERVAL,
18728c2ecf20Sopenharmony_ci				      DEFAULT_RATELIMIT_BURST);
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&iommu->register_lock, flag);
18758c2ecf20Sopenharmony_ci	fault_status = readl(iommu->reg + DMAR_FSTS_REG);
18768c2ecf20Sopenharmony_ci	if (fault_status && __ratelimit(&rs))
18778c2ecf20Sopenharmony_ci		pr_err("DRHD: handling fault status reg %x\n", fault_status);
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	/* TBD: ignore advanced fault log currently */
18808c2ecf20Sopenharmony_ci	if (!(fault_status & DMA_FSTS_PPF))
18818c2ecf20Sopenharmony_ci		goto unlock_exit;
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	fault_index = dma_fsts_fault_record_index(fault_status);
18848c2ecf20Sopenharmony_ci	reg = cap_fault_reg_offset(iommu->cap);
18858c2ecf20Sopenharmony_ci	while (1) {
18868c2ecf20Sopenharmony_ci		/* Disable printing, simply clear the fault when ratelimited */
18878c2ecf20Sopenharmony_ci		bool ratelimited = !__ratelimit(&rs);
18888c2ecf20Sopenharmony_ci		u8 fault_reason;
18898c2ecf20Sopenharmony_ci		u16 source_id;
18908c2ecf20Sopenharmony_ci		u64 guest_addr;
18918c2ecf20Sopenharmony_ci		u32 pasid;
18928c2ecf20Sopenharmony_ci		int type;
18938c2ecf20Sopenharmony_ci		u32 data;
18948c2ecf20Sopenharmony_ci		bool pasid_present;
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ci		/* highest 32 bits */
18978c2ecf20Sopenharmony_ci		data = readl(iommu->reg + reg +
18988c2ecf20Sopenharmony_ci				fault_index * PRIMARY_FAULT_REG_LEN + 12);
18998c2ecf20Sopenharmony_ci		if (!(data & DMA_FRCD_F))
19008c2ecf20Sopenharmony_ci			break;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci		if (!ratelimited) {
19038c2ecf20Sopenharmony_ci			fault_reason = dma_frcd_fault_reason(data);
19048c2ecf20Sopenharmony_ci			type = dma_frcd_type(data);
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci			pasid = dma_frcd_pasid_value(data);
19078c2ecf20Sopenharmony_ci			data = readl(iommu->reg + reg +
19088c2ecf20Sopenharmony_ci				     fault_index * PRIMARY_FAULT_REG_LEN + 8);
19098c2ecf20Sopenharmony_ci			source_id = dma_frcd_source_id(data);
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci			pasid_present = dma_frcd_pasid_present(data);
19128c2ecf20Sopenharmony_ci			guest_addr = dmar_readq(iommu->reg + reg +
19138c2ecf20Sopenharmony_ci					fault_index * PRIMARY_FAULT_REG_LEN);
19148c2ecf20Sopenharmony_ci			guest_addr = dma_frcd_page_addr(guest_addr);
19158c2ecf20Sopenharmony_ci		}
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci		/* clear the fault */
19188c2ecf20Sopenharmony_ci		writel(DMA_FRCD_F, iommu->reg + reg +
19198c2ecf20Sopenharmony_ci			fault_index * PRIMARY_FAULT_REG_LEN + 12);
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci		if (!ratelimited)
19248c2ecf20Sopenharmony_ci			/* Using pasid -1 if pasid is not present */
19258c2ecf20Sopenharmony_ci			dmar_fault_do_one(iommu, type, fault_reason,
19268c2ecf20Sopenharmony_ci					  pasid_present ? pasid : -1,
19278c2ecf20Sopenharmony_ci					  source_id, guest_addr);
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci		fault_index++;
19308c2ecf20Sopenharmony_ci		if (fault_index >= cap_num_fault_regs(iommu->cap))
19318c2ecf20Sopenharmony_ci			fault_index = 0;
19328c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&iommu->register_lock, flag);
19338c2ecf20Sopenharmony_ci	}
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci	writel(DMA_FSTS_PFO | DMA_FSTS_PPF | DMA_FSTS_PRO,
19368c2ecf20Sopenharmony_ci	       iommu->reg + DMAR_FSTS_REG);
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ciunlock_exit:
19398c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
19408c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
19418c2ecf20Sopenharmony_ci}
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ciint dmar_set_interrupt(struct intel_iommu *iommu)
19448c2ecf20Sopenharmony_ci{
19458c2ecf20Sopenharmony_ci	int irq, ret;
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci	/*
19488c2ecf20Sopenharmony_ci	 * Check if the fault interrupt is already initialized.
19498c2ecf20Sopenharmony_ci	 */
19508c2ecf20Sopenharmony_ci	if (iommu->irq)
19518c2ecf20Sopenharmony_ci		return 0;
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci	irq = dmar_alloc_hwirq(iommu->seq_id, iommu->node, iommu);
19548c2ecf20Sopenharmony_ci	if (irq > 0) {
19558c2ecf20Sopenharmony_ci		iommu->irq = irq;
19568c2ecf20Sopenharmony_ci	} else {
19578c2ecf20Sopenharmony_ci		pr_err("No free IRQ vectors\n");
19588c2ecf20Sopenharmony_ci		return -EINVAL;
19598c2ecf20Sopenharmony_ci	}
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci	ret = request_irq(irq, dmar_fault, IRQF_NO_THREAD, iommu->name, iommu);
19628c2ecf20Sopenharmony_ci	if (ret)
19638c2ecf20Sopenharmony_ci		pr_err("Can't request irq\n");
19648c2ecf20Sopenharmony_ci	return ret;
19658c2ecf20Sopenharmony_ci}
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ciint __init enable_drhd_fault_handling(void)
19688c2ecf20Sopenharmony_ci{
19698c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *drhd;
19708c2ecf20Sopenharmony_ci	struct intel_iommu *iommu;
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ci	/*
19738c2ecf20Sopenharmony_ci	 * Enable fault control interrupt.
19748c2ecf20Sopenharmony_ci	 */
19758c2ecf20Sopenharmony_ci	for_each_iommu(iommu, drhd) {
19768c2ecf20Sopenharmony_ci		u32 fault_status;
19778c2ecf20Sopenharmony_ci		int ret = dmar_set_interrupt(iommu);
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci		if (ret) {
19808c2ecf20Sopenharmony_ci			pr_err("DRHD %Lx: failed to enable fault, interrupt, ret %d\n",
19818c2ecf20Sopenharmony_ci			       (unsigned long long)drhd->reg_base_addr, ret);
19828c2ecf20Sopenharmony_ci			return -1;
19838c2ecf20Sopenharmony_ci		}
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci		/*
19868c2ecf20Sopenharmony_ci		 * Clear any previous faults.
19878c2ecf20Sopenharmony_ci		 */
19888c2ecf20Sopenharmony_ci		dmar_fault(iommu->irq, iommu);
19898c2ecf20Sopenharmony_ci		fault_status = readl(iommu->reg + DMAR_FSTS_REG);
19908c2ecf20Sopenharmony_ci		writel(fault_status, iommu->reg + DMAR_FSTS_REG);
19918c2ecf20Sopenharmony_ci	}
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	return 0;
19948c2ecf20Sopenharmony_ci}
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci/*
19978c2ecf20Sopenharmony_ci * Re-enable Queued Invalidation interface.
19988c2ecf20Sopenharmony_ci */
19998c2ecf20Sopenharmony_ciint dmar_reenable_qi(struct intel_iommu *iommu)
20008c2ecf20Sopenharmony_ci{
20018c2ecf20Sopenharmony_ci	if (!ecap_qis(iommu->ecap))
20028c2ecf20Sopenharmony_ci		return -ENOENT;
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	if (!iommu->qi)
20058c2ecf20Sopenharmony_ci		return -ENOENT;
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	/*
20088c2ecf20Sopenharmony_ci	 * First disable queued invalidation.
20098c2ecf20Sopenharmony_ci	 */
20108c2ecf20Sopenharmony_ci	dmar_disable_qi(iommu);
20118c2ecf20Sopenharmony_ci	/*
20128c2ecf20Sopenharmony_ci	 * Then enable queued invalidation again. Since there is no pending
20138c2ecf20Sopenharmony_ci	 * invalidation requests now, it's safe to re-enable queued
20148c2ecf20Sopenharmony_ci	 * invalidation.
20158c2ecf20Sopenharmony_ci	 */
20168c2ecf20Sopenharmony_ci	__dmar_enable_qi(iommu);
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	return 0;
20198c2ecf20Sopenharmony_ci}
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci/*
20228c2ecf20Sopenharmony_ci * Check interrupt remapping support in DMAR table description.
20238c2ecf20Sopenharmony_ci */
20248c2ecf20Sopenharmony_ciint __init dmar_ir_support(void)
20258c2ecf20Sopenharmony_ci{
20268c2ecf20Sopenharmony_ci	struct acpi_table_dmar *dmar;
20278c2ecf20Sopenharmony_ci	dmar = (struct acpi_table_dmar *)dmar_tbl;
20288c2ecf20Sopenharmony_ci	if (!dmar)
20298c2ecf20Sopenharmony_ci		return 0;
20308c2ecf20Sopenharmony_ci	return dmar->flags & 0x1;
20318c2ecf20Sopenharmony_ci}
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci/* Check whether DMAR units are in use */
20348c2ecf20Sopenharmony_cistatic inline bool dmar_in_use(void)
20358c2ecf20Sopenharmony_ci{
20368c2ecf20Sopenharmony_ci	return irq_remapping_enabled || intel_iommu_enabled;
20378c2ecf20Sopenharmony_ci}
20388c2ecf20Sopenharmony_ci
20398c2ecf20Sopenharmony_cistatic int __init dmar_free_unused_resources(void)
20408c2ecf20Sopenharmony_ci{
20418c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *dmaru, *dmaru_n;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	if (dmar_in_use())
20448c2ecf20Sopenharmony_ci		return 0;
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci	if (dmar_dev_scope_status != 1 && !list_empty(&dmar_drhd_units))
20478c2ecf20Sopenharmony_ci		bus_unregister_notifier(&pci_bus_type, &dmar_pci_bus_nb);
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci	down_write(&dmar_global_lock);
20508c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dmaru, dmaru_n, &dmar_drhd_units, list) {
20518c2ecf20Sopenharmony_ci		list_del(&dmaru->list);
20528c2ecf20Sopenharmony_ci		dmar_free_drhd(dmaru);
20538c2ecf20Sopenharmony_ci	}
20548c2ecf20Sopenharmony_ci	up_write(&dmar_global_lock);
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ci	return 0;
20578c2ecf20Sopenharmony_ci}
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_cilate_initcall(dmar_free_unused_resources);
20608c2ecf20Sopenharmony_ciIOMMU_INIT_POST(detect_intel_iommu);
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci/*
20638c2ecf20Sopenharmony_ci * DMAR Hotplug Support
20648c2ecf20Sopenharmony_ci * For more details, please refer to Intel(R) Virtualization Technology
20658c2ecf20Sopenharmony_ci * for Directed-IO Architecture Specifiction, Rev 2.2, Section 8.8
20668c2ecf20Sopenharmony_ci * "Remapping Hardware Unit Hot Plug".
20678c2ecf20Sopenharmony_ci */
20688c2ecf20Sopenharmony_cistatic guid_t dmar_hp_guid =
20698c2ecf20Sopenharmony_ci	GUID_INIT(0xD8C1A3A6, 0xBE9B, 0x4C9B,
20708c2ecf20Sopenharmony_ci		  0x91, 0xBF, 0xC3, 0xCB, 0x81, 0xFC, 0x5D, 0xAF);
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci/*
20738c2ecf20Sopenharmony_ci * Currently there's only one revision and BIOS will not check the revision id,
20748c2ecf20Sopenharmony_ci * so use 0 for safety.
20758c2ecf20Sopenharmony_ci */
20768c2ecf20Sopenharmony_ci#define	DMAR_DSM_REV_ID			0
20778c2ecf20Sopenharmony_ci#define	DMAR_DSM_FUNC_DRHD		1
20788c2ecf20Sopenharmony_ci#define	DMAR_DSM_FUNC_ATSR		2
20798c2ecf20Sopenharmony_ci#define	DMAR_DSM_FUNC_RHSA		3
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_cistatic inline bool dmar_detect_dsm(acpi_handle handle, int func)
20828c2ecf20Sopenharmony_ci{
20838c2ecf20Sopenharmony_ci	return acpi_check_dsm(handle, &dmar_hp_guid, DMAR_DSM_REV_ID, 1 << func);
20848c2ecf20Sopenharmony_ci}
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_cistatic int dmar_walk_dsm_resource(acpi_handle handle, int func,
20878c2ecf20Sopenharmony_ci				  dmar_res_handler_t handler, void *arg)
20888c2ecf20Sopenharmony_ci{
20898c2ecf20Sopenharmony_ci	int ret = -ENODEV;
20908c2ecf20Sopenharmony_ci	union acpi_object *obj;
20918c2ecf20Sopenharmony_ci	struct acpi_dmar_header *start;
20928c2ecf20Sopenharmony_ci	struct dmar_res_callback callback;
20938c2ecf20Sopenharmony_ci	static int res_type[] = {
20948c2ecf20Sopenharmony_ci		[DMAR_DSM_FUNC_DRHD] = ACPI_DMAR_TYPE_HARDWARE_UNIT,
20958c2ecf20Sopenharmony_ci		[DMAR_DSM_FUNC_ATSR] = ACPI_DMAR_TYPE_ROOT_ATS,
20968c2ecf20Sopenharmony_ci		[DMAR_DSM_FUNC_RHSA] = ACPI_DMAR_TYPE_HARDWARE_AFFINITY,
20978c2ecf20Sopenharmony_ci	};
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci	if (!dmar_detect_dsm(handle, func))
21008c2ecf20Sopenharmony_ci		return 0;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	obj = acpi_evaluate_dsm_typed(handle, &dmar_hp_guid, DMAR_DSM_REV_ID,
21038c2ecf20Sopenharmony_ci				      func, NULL, ACPI_TYPE_BUFFER);
21048c2ecf20Sopenharmony_ci	if (!obj)
21058c2ecf20Sopenharmony_ci		return -ENODEV;
21068c2ecf20Sopenharmony_ci
21078c2ecf20Sopenharmony_ci	memset(&callback, 0, sizeof(callback));
21088c2ecf20Sopenharmony_ci	callback.cb[res_type[func]] = handler;
21098c2ecf20Sopenharmony_ci	callback.arg[res_type[func]] = arg;
21108c2ecf20Sopenharmony_ci	start = (struct acpi_dmar_header *)obj->buffer.pointer;
21118c2ecf20Sopenharmony_ci	ret = dmar_walk_remapping_entries(start, obj->buffer.length, &callback);
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	ACPI_FREE(obj);
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci	return ret;
21168c2ecf20Sopenharmony_ci}
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_cistatic int dmar_hp_add_drhd(struct acpi_dmar_header *header, void *arg)
21198c2ecf20Sopenharmony_ci{
21208c2ecf20Sopenharmony_ci	int ret;
21218c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci	dmaru = dmar_find_dmaru((struct acpi_dmar_hardware_unit *)header);
21248c2ecf20Sopenharmony_ci	if (!dmaru)
21258c2ecf20Sopenharmony_ci		return -ENODEV;
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	ret = dmar_ir_hotplug(dmaru, true);
21288c2ecf20Sopenharmony_ci	if (ret == 0)
21298c2ecf20Sopenharmony_ci		ret = dmar_iommu_hotplug(dmaru, true);
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci	return ret;
21328c2ecf20Sopenharmony_ci}
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_cistatic int dmar_hp_remove_drhd(struct acpi_dmar_header *header, void *arg)
21358c2ecf20Sopenharmony_ci{
21368c2ecf20Sopenharmony_ci	int i, ret;
21378c2ecf20Sopenharmony_ci	struct device *dev;
21388c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci	dmaru = dmar_find_dmaru((struct acpi_dmar_hardware_unit *)header);
21418c2ecf20Sopenharmony_ci	if (!dmaru)
21428c2ecf20Sopenharmony_ci		return 0;
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci	/*
21458c2ecf20Sopenharmony_ci	 * All PCI devices managed by this unit should have been destroyed.
21468c2ecf20Sopenharmony_ci	 */
21478c2ecf20Sopenharmony_ci	if (!dmaru->include_all && dmaru->devices && dmaru->devices_cnt) {
21488c2ecf20Sopenharmony_ci		for_each_active_dev_scope(dmaru->devices,
21498c2ecf20Sopenharmony_ci					  dmaru->devices_cnt, i, dev)
21508c2ecf20Sopenharmony_ci			return -EBUSY;
21518c2ecf20Sopenharmony_ci	}
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci	ret = dmar_ir_hotplug(dmaru, false);
21548c2ecf20Sopenharmony_ci	if (ret == 0)
21558c2ecf20Sopenharmony_ci		ret = dmar_iommu_hotplug(dmaru, false);
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci	return ret;
21588c2ecf20Sopenharmony_ci}
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_cistatic int dmar_hp_release_drhd(struct acpi_dmar_header *header, void *arg)
21618c2ecf20Sopenharmony_ci{
21628c2ecf20Sopenharmony_ci	struct dmar_drhd_unit *dmaru;
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci	dmaru = dmar_find_dmaru((struct acpi_dmar_hardware_unit *)header);
21658c2ecf20Sopenharmony_ci	if (dmaru) {
21668c2ecf20Sopenharmony_ci		list_del_rcu(&dmaru->list);
21678c2ecf20Sopenharmony_ci		synchronize_rcu();
21688c2ecf20Sopenharmony_ci		dmar_free_drhd(dmaru);
21698c2ecf20Sopenharmony_ci	}
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci	return 0;
21728c2ecf20Sopenharmony_ci}
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_cistatic int dmar_hotplug_insert(acpi_handle handle)
21758c2ecf20Sopenharmony_ci{
21768c2ecf20Sopenharmony_ci	int ret;
21778c2ecf20Sopenharmony_ci	int drhd_count = 0;
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
21808c2ecf20Sopenharmony_ci				     &dmar_validate_one_drhd, (void *)1);
21818c2ecf20Sopenharmony_ci	if (ret)
21828c2ecf20Sopenharmony_ci		goto out;
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
21858c2ecf20Sopenharmony_ci				     &dmar_parse_one_drhd, (void *)&drhd_count);
21868c2ecf20Sopenharmony_ci	if (ret == 0 && drhd_count == 0) {
21878c2ecf20Sopenharmony_ci		pr_warn(FW_BUG "No DRHD structures in buffer returned by _DSM method\n");
21888c2ecf20Sopenharmony_ci		goto out;
21898c2ecf20Sopenharmony_ci	} else if (ret) {
21908c2ecf20Sopenharmony_ci		goto release_drhd;
21918c2ecf20Sopenharmony_ci	}
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_RHSA,
21948c2ecf20Sopenharmony_ci				     &dmar_parse_one_rhsa, NULL);
21958c2ecf20Sopenharmony_ci	if (ret)
21968c2ecf20Sopenharmony_ci		goto release_drhd;
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR,
21998c2ecf20Sopenharmony_ci				     &dmar_parse_one_atsr, NULL);
22008c2ecf20Sopenharmony_ci	if (ret)
22018c2ecf20Sopenharmony_ci		goto release_atsr;
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
22048c2ecf20Sopenharmony_ci				     &dmar_hp_add_drhd, NULL);
22058c2ecf20Sopenharmony_ci	if (!ret)
22068c2ecf20Sopenharmony_ci		return 0;
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci	dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
22098c2ecf20Sopenharmony_ci			       &dmar_hp_remove_drhd, NULL);
22108c2ecf20Sopenharmony_cirelease_atsr:
22118c2ecf20Sopenharmony_ci	dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR,
22128c2ecf20Sopenharmony_ci			       &dmar_release_one_atsr, NULL);
22138c2ecf20Sopenharmony_cirelease_drhd:
22148c2ecf20Sopenharmony_ci	dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
22158c2ecf20Sopenharmony_ci			       &dmar_hp_release_drhd, NULL);
22168c2ecf20Sopenharmony_ciout:
22178c2ecf20Sopenharmony_ci	return ret;
22188c2ecf20Sopenharmony_ci}
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_cistatic int dmar_hotplug_remove(acpi_handle handle)
22218c2ecf20Sopenharmony_ci{
22228c2ecf20Sopenharmony_ci	int ret;
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR,
22258c2ecf20Sopenharmony_ci				     &dmar_check_one_atsr, NULL);
22268c2ecf20Sopenharmony_ci	if (ret)
22278c2ecf20Sopenharmony_ci		return ret;
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_ci	ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
22308c2ecf20Sopenharmony_ci				     &dmar_hp_remove_drhd, NULL);
22318c2ecf20Sopenharmony_ci	if (ret == 0) {
22328c2ecf20Sopenharmony_ci		WARN_ON(dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR,
22338c2ecf20Sopenharmony_ci					       &dmar_release_one_atsr, NULL));
22348c2ecf20Sopenharmony_ci		WARN_ON(dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
22358c2ecf20Sopenharmony_ci					       &dmar_hp_release_drhd, NULL));
22368c2ecf20Sopenharmony_ci	} else {
22378c2ecf20Sopenharmony_ci		dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD,
22388c2ecf20Sopenharmony_ci				       &dmar_hp_add_drhd, NULL);
22398c2ecf20Sopenharmony_ci	}
22408c2ecf20Sopenharmony_ci
22418c2ecf20Sopenharmony_ci	return ret;
22428c2ecf20Sopenharmony_ci}
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_cistatic acpi_status dmar_get_dsm_handle(acpi_handle handle, u32 lvl,
22458c2ecf20Sopenharmony_ci				       void *context, void **retval)
22468c2ecf20Sopenharmony_ci{
22478c2ecf20Sopenharmony_ci	acpi_handle *phdl = retval;
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci	if (dmar_detect_dsm(handle, DMAR_DSM_FUNC_DRHD)) {
22508c2ecf20Sopenharmony_ci		*phdl = handle;
22518c2ecf20Sopenharmony_ci		return AE_CTRL_TERMINATE;
22528c2ecf20Sopenharmony_ci	}
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	return AE_OK;
22558c2ecf20Sopenharmony_ci}
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_cistatic int dmar_device_hotplug(acpi_handle handle, bool insert)
22588c2ecf20Sopenharmony_ci{
22598c2ecf20Sopenharmony_ci	int ret;
22608c2ecf20Sopenharmony_ci	acpi_handle tmp = NULL;
22618c2ecf20Sopenharmony_ci	acpi_status status;
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci	if (!dmar_in_use())
22648c2ecf20Sopenharmony_ci		return 0;
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	if (dmar_detect_dsm(handle, DMAR_DSM_FUNC_DRHD)) {
22678c2ecf20Sopenharmony_ci		tmp = handle;
22688c2ecf20Sopenharmony_ci	} else {
22698c2ecf20Sopenharmony_ci		status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
22708c2ecf20Sopenharmony_ci					     ACPI_UINT32_MAX,
22718c2ecf20Sopenharmony_ci					     dmar_get_dsm_handle,
22728c2ecf20Sopenharmony_ci					     NULL, NULL, &tmp);
22738c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status)) {
22748c2ecf20Sopenharmony_ci			pr_warn("Failed to locate _DSM method.\n");
22758c2ecf20Sopenharmony_ci			return -ENXIO;
22768c2ecf20Sopenharmony_ci		}
22778c2ecf20Sopenharmony_ci	}
22788c2ecf20Sopenharmony_ci	if (tmp == NULL)
22798c2ecf20Sopenharmony_ci		return 0;
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_ci	down_write(&dmar_global_lock);
22828c2ecf20Sopenharmony_ci	if (insert)
22838c2ecf20Sopenharmony_ci		ret = dmar_hotplug_insert(tmp);
22848c2ecf20Sopenharmony_ci	else
22858c2ecf20Sopenharmony_ci		ret = dmar_hotplug_remove(tmp);
22868c2ecf20Sopenharmony_ci	up_write(&dmar_global_lock);
22878c2ecf20Sopenharmony_ci
22888c2ecf20Sopenharmony_ci	return ret;
22898c2ecf20Sopenharmony_ci}
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ciint dmar_device_add(acpi_handle handle)
22928c2ecf20Sopenharmony_ci{
22938c2ecf20Sopenharmony_ci	return dmar_device_hotplug(handle, true);
22948c2ecf20Sopenharmony_ci}
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ciint dmar_device_remove(acpi_handle handle)
22978c2ecf20Sopenharmony_ci{
22988c2ecf20Sopenharmony_ci	return dmar_device_hotplug(handle, false);
22998c2ecf20Sopenharmony_ci}
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci/*
23028c2ecf20Sopenharmony_ci * dmar_platform_optin - Is %DMA_CTRL_PLATFORM_OPT_IN_FLAG set in DMAR table
23038c2ecf20Sopenharmony_ci *
23048c2ecf20Sopenharmony_ci * Returns true if the platform has %DMA_CTRL_PLATFORM_OPT_IN_FLAG set in
23058c2ecf20Sopenharmony_ci * the ACPI DMAR table. This means that the platform boot firmware has made
23068c2ecf20Sopenharmony_ci * sure no device can issue DMA outside of RMRR regions.
23078c2ecf20Sopenharmony_ci */
23088c2ecf20Sopenharmony_cibool dmar_platform_optin(void)
23098c2ecf20Sopenharmony_ci{
23108c2ecf20Sopenharmony_ci	struct acpi_table_dmar *dmar;
23118c2ecf20Sopenharmony_ci	acpi_status status;
23128c2ecf20Sopenharmony_ci	bool ret;
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	status = acpi_get_table(ACPI_SIG_DMAR, 0,
23158c2ecf20Sopenharmony_ci				(struct acpi_table_header **)&dmar);
23168c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
23178c2ecf20Sopenharmony_ci		return false;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	ret = !!(dmar->flags & DMAR_PLATFORM_OPT_IN);
23208c2ecf20Sopenharmony_ci	acpi_put_table((struct acpi_table_header *)dmar);
23218c2ecf20Sopenharmony_ci
23228c2ecf20Sopenharmony_ci	return ret;
23238c2ecf20Sopenharmony_ci}
23248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dmar_platform_optin);
2325