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