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