162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2015 Intel Corporation. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: David Woodhouse <dwmw2@infradead.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/mmu_notifier.h> 962306a36Sopenharmony_ci#include <linux/sched.h> 1062306a36Sopenharmony_ci#include <linux/sched/mm.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/rculist.h> 1362306a36Sopenharmony_ci#include <linux/pci.h> 1462306a36Sopenharmony_ci#include <linux/pci-ats.h> 1562306a36Sopenharmony_ci#include <linux/dmar.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/mm_types.h> 1862306a36Sopenharmony_ci#include <linux/xarray.h> 1962306a36Sopenharmony_ci#include <asm/page.h> 2062306a36Sopenharmony_ci#include <asm/fpu/api.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "iommu.h" 2362306a36Sopenharmony_ci#include "pasid.h" 2462306a36Sopenharmony_ci#include "perf.h" 2562306a36Sopenharmony_ci#include "../iommu-sva.h" 2662306a36Sopenharmony_ci#include "trace.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic irqreturn_t prq_event_thread(int irq, void *d); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic DEFINE_XARRAY_ALLOC(pasid_private_array); 3162306a36Sopenharmony_cistatic int pasid_private_add(ioasid_t pasid, void *priv) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci return xa_alloc(&pasid_private_array, &pasid, priv, 3462306a36Sopenharmony_ci XA_LIMIT(pasid, pasid), GFP_ATOMIC); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void pasid_private_remove(ioasid_t pasid) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci xa_erase(&pasid_private_array, pasid); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void *pasid_private_find(ioasid_t pasid) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci return xa_load(&pasid_private_array, pasid); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct intel_svm_dev * 4862306a36Sopenharmony_cisvm_lookup_device_by_dev(struct intel_svm *svm, struct device *dev) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct intel_svm_dev *sdev = NULL, *t; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci rcu_read_lock(); 5362306a36Sopenharmony_ci list_for_each_entry_rcu(t, &svm->devs, list) { 5462306a36Sopenharmony_ci if (t->dev == dev) { 5562306a36Sopenharmony_ci sdev = t; 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci rcu_read_unlock(); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return sdev; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciint intel_svm_enable_prq(struct intel_iommu *iommu) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct iopf_queue *iopfq; 6762306a36Sopenharmony_ci struct page *pages; 6862306a36Sopenharmony_ci int irq, ret; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, PRQ_ORDER); 7162306a36Sopenharmony_ci if (!pages) { 7262306a36Sopenharmony_ci pr_warn("IOMMU: %s: Failed to allocate page request queue\n", 7362306a36Sopenharmony_ci iommu->name); 7462306a36Sopenharmony_ci return -ENOMEM; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci iommu->prq = page_address(pages); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci irq = dmar_alloc_hwirq(IOMMU_IRQ_ID_OFFSET_PRQ + iommu->seq_id, iommu->node, iommu); 7962306a36Sopenharmony_ci if (irq <= 0) { 8062306a36Sopenharmony_ci pr_err("IOMMU: %s: Failed to create IRQ vector for page request queue\n", 8162306a36Sopenharmony_ci iommu->name); 8262306a36Sopenharmony_ci ret = -EINVAL; 8362306a36Sopenharmony_ci goto free_prq; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci iommu->pr_irq = irq; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci snprintf(iommu->iopfq_name, sizeof(iommu->iopfq_name), 8862306a36Sopenharmony_ci "dmar%d-iopfq", iommu->seq_id); 8962306a36Sopenharmony_ci iopfq = iopf_queue_alloc(iommu->iopfq_name); 9062306a36Sopenharmony_ci if (!iopfq) { 9162306a36Sopenharmony_ci pr_err("IOMMU: %s: Failed to allocate iopf queue\n", iommu->name); 9262306a36Sopenharmony_ci ret = -ENOMEM; 9362306a36Sopenharmony_ci goto free_hwirq; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci iommu->iopf_queue = iopfq; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci snprintf(iommu->prq_name, sizeof(iommu->prq_name), "dmar%d-prq", iommu->seq_id); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci ret = request_threaded_irq(irq, NULL, prq_event_thread, IRQF_ONESHOT, 10062306a36Sopenharmony_ci iommu->prq_name, iommu); 10162306a36Sopenharmony_ci if (ret) { 10262306a36Sopenharmony_ci pr_err("IOMMU: %s: Failed to request IRQ for page request queue\n", 10362306a36Sopenharmony_ci iommu->name); 10462306a36Sopenharmony_ci goto free_iopfq; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQH_REG, 0ULL); 10762306a36Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQT_REG, 0ULL); 10862306a36Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQA_REG, virt_to_phys(iommu->prq) | PRQ_ORDER); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci init_completion(&iommu->prq_complete); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cifree_iopfq: 11562306a36Sopenharmony_ci iopf_queue_free(iommu->iopf_queue); 11662306a36Sopenharmony_ci iommu->iopf_queue = NULL; 11762306a36Sopenharmony_cifree_hwirq: 11862306a36Sopenharmony_ci dmar_free_hwirq(irq); 11962306a36Sopenharmony_ci iommu->pr_irq = 0; 12062306a36Sopenharmony_cifree_prq: 12162306a36Sopenharmony_ci free_pages((unsigned long)iommu->prq, PRQ_ORDER); 12262306a36Sopenharmony_ci iommu->prq = NULL; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return ret; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciint intel_svm_finish_prq(struct intel_iommu *iommu) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQH_REG, 0ULL); 13062306a36Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQT_REG, 0ULL); 13162306a36Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQA_REG, 0ULL); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (iommu->pr_irq) { 13462306a36Sopenharmony_ci free_irq(iommu->pr_irq, iommu); 13562306a36Sopenharmony_ci dmar_free_hwirq(iommu->pr_irq); 13662306a36Sopenharmony_ci iommu->pr_irq = 0; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (iommu->iopf_queue) { 14062306a36Sopenharmony_ci iopf_queue_free(iommu->iopf_queue); 14162306a36Sopenharmony_ci iommu->iopf_queue = NULL; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci free_pages((unsigned long)iommu->prq, PRQ_ORDER); 14562306a36Sopenharmony_ci iommu->prq = NULL; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_civoid intel_svm_check(struct intel_iommu *iommu) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (!pasid_supported(iommu)) 15362306a36Sopenharmony_ci return; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (cpu_feature_enabled(X86_FEATURE_GBPAGES) && 15662306a36Sopenharmony_ci !cap_fl1gp_support(iommu->cap)) { 15762306a36Sopenharmony_ci pr_err("%s SVM disabled, incompatible 1GB page capability\n", 15862306a36Sopenharmony_ci iommu->name); 15962306a36Sopenharmony_ci return; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (cpu_feature_enabled(X86_FEATURE_LA57) && 16362306a36Sopenharmony_ci !cap_fl5lp_support(iommu->cap)) { 16462306a36Sopenharmony_ci pr_err("%s SVM disabled, incompatible paging mode\n", 16562306a36Sopenharmony_ci iommu->name); 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci iommu->flags |= VTD_FLAG_SVM_CAPABLE; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void __flush_svm_range_dev(struct intel_svm *svm, 17362306a36Sopenharmony_ci struct intel_svm_dev *sdev, 17462306a36Sopenharmony_ci unsigned long address, 17562306a36Sopenharmony_ci unsigned long pages, int ih) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct device_domain_info *info = dev_iommu_priv_get(sdev->dev); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (WARN_ON(!pages)) 18062306a36Sopenharmony_ci return; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci qi_flush_piotlb(sdev->iommu, sdev->did, svm->pasid, address, pages, ih); 18362306a36Sopenharmony_ci if (info->ats_enabled) { 18462306a36Sopenharmony_ci qi_flush_dev_iotlb_pasid(sdev->iommu, sdev->sid, info->pfsid, 18562306a36Sopenharmony_ci svm->pasid, sdev->qdep, address, 18662306a36Sopenharmony_ci order_base_2(pages)); 18762306a36Sopenharmony_ci quirk_extra_dev_tlb_flush(info, address, order_base_2(pages), 18862306a36Sopenharmony_ci svm->pasid, sdev->qdep); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void intel_flush_svm_range_dev(struct intel_svm *svm, 19362306a36Sopenharmony_ci struct intel_svm_dev *sdev, 19462306a36Sopenharmony_ci unsigned long address, 19562306a36Sopenharmony_ci unsigned long pages, int ih) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci unsigned long shift = ilog2(__roundup_pow_of_two(pages)); 19862306a36Sopenharmony_ci unsigned long align = (1ULL << (VTD_PAGE_SHIFT + shift)); 19962306a36Sopenharmony_ci unsigned long start = ALIGN_DOWN(address, align); 20062306a36Sopenharmony_ci unsigned long end = ALIGN(address + (pages << VTD_PAGE_SHIFT), align); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci while (start < end) { 20362306a36Sopenharmony_ci __flush_svm_range_dev(svm, sdev, start, align >> VTD_PAGE_SHIFT, ih); 20462306a36Sopenharmony_ci start += align; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void intel_flush_svm_range(struct intel_svm *svm, unsigned long address, 20962306a36Sopenharmony_ci unsigned long pages, int ih) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct intel_svm_dev *sdev; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci rcu_read_lock(); 21462306a36Sopenharmony_ci list_for_each_entry_rcu(sdev, &svm->devs, list) 21562306a36Sopenharmony_ci intel_flush_svm_range_dev(svm, sdev, address, pages, ih); 21662306a36Sopenharmony_ci rcu_read_unlock(); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void intel_flush_svm_all(struct intel_svm *svm) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct device_domain_info *info; 22262306a36Sopenharmony_ci struct intel_svm_dev *sdev; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci rcu_read_lock(); 22562306a36Sopenharmony_ci list_for_each_entry_rcu(sdev, &svm->devs, list) { 22662306a36Sopenharmony_ci info = dev_iommu_priv_get(sdev->dev); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci qi_flush_piotlb(sdev->iommu, sdev->did, svm->pasid, 0, -1UL, 0); 22962306a36Sopenharmony_ci if (info->ats_enabled) { 23062306a36Sopenharmony_ci qi_flush_dev_iotlb_pasid(sdev->iommu, sdev->sid, info->pfsid, 23162306a36Sopenharmony_ci svm->pasid, sdev->qdep, 23262306a36Sopenharmony_ci 0, 64 - VTD_PAGE_SHIFT); 23362306a36Sopenharmony_ci quirk_extra_dev_tlb_flush(info, 0, 64 - VTD_PAGE_SHIFT, 23462306a36Sopenharmony_ci svm->pasid, sdev->qdep); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci rcu_read_unlock(); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* Pages have been freed at this point */ 24162306a36Sopenharmony_cistatic void intel_arch_invalidate_secondary_tlbs(struct mmu_notifier *mn, 24262306a36Sopenharmony_ci struct mm_struct *mm, 24362306a36Sopenharmony_ci unsigned long start, unsigned long end) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct intel_svm *svm = container_of(mn, struct intel_svm, notifier); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (start == 0 && end == -1UL) { 24862306a36Sopenharmony_ci intel_flush_svm_all(svm); 24962306a36Sopenharmony_ci return; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci intel_flush_svm_range(svm, start, 25362306a36Sopenharmony_ci (end - start + PAGE_SIZE - 1) >> VTD_PAGE_SHIFT, 0); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct intel_svm *svm = container_of(mn, struct intel_svm, notifier); 25962306a36Sopenharmony_ci struct intel_svm_dev *sdev; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* This might end up being called from exit_mmap(), *before* the page 26262306a36Sopenharmony_ci * tables are cleared. And __mmu_notifier_release() will delete us from 26362306a36Sopenharmony_ci * the list of notifiers so that our invalidate_range() callback doesn't 26462306a36Sopenharmony_ci * get called when the page tables are cleared. So we need to protect 26562306a36Sopenharmony_ci * against hardware accessing those page tables. 26662306a36Sopenharmony_ci * 26762306a36Sopenharmony_ci * We do it by clearing the entry in the PASID table and then flushing 26862306a36Sopenharmony_ci * the IOTLB and the PASID table caches. This might upset hardware; 26962306a36Sopenharmony_ci * perhaps we'll want to point the PASID to a dummy PGD (like the zero 27062306a36Sopenharmony_ci * page) so that we end up taking a fault that the hardware really 27162306a36Sopenharmony_ci * *has* to handle gracefully without affecting other processes. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci rcu_read_lock(); 27462306a36Sopenharmony_ci list_for_each_entry_rcu(sdev, &svm->devs, list) 27562306a36Sopenharmony_ci intel_pasid_tear_down_entry(sdev->iommu, sdev->dev, 27662306a36Sopenharmony_ci svm->pasid, true); 27762306a36Sopenharmony_ci rcu_read_unlock(); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic const struct mmu_notifier_ops intel_mmuops = { 28262306a36Sopenharmony_ci .release = intel_mm_release, 28362306a36Sopenharmony_ci .arch_invalidate_secondary_tlbs = intel_arch_invalidate_secondary_tlbs, 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int pasid_to_svm_sdev(struct device *dev, unsigned int pasid, 28762306a36Sopenharmony_ci struct intel_svm **rsvm, 28862306a36Sopenharmony_ci struct intel_svm_dev **rsdev) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct intel_svm_dev *sdev = NULL; 29162306a36Sopenharmony_ci struct intel_svm *svm; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (pasid == IOMMU_PASID_INVALID || pasid >= PASID_MAX) 29462306a36Sopenharmony_ci return -EINVAL; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci svm = pasid_private_find(pasid); 29762306a36Sopenharmony_ci if (IS_ERR(svm)) 29862306a36Sopenharmony_ci return PTR_ERR(svm); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (!svm) 30162306a36Sopenharmony_ci goto out; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* 30462306a36Sopenharmony_ci * If we found svm for the PASID, there must be at least one device 30562306a36Sopenharmony_ci * bond. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ci if (WARN_ON(list_empty(&svm->devs))) 30862306a36Sopenharmony_ci return -EINVAL; 30962306a36Sopenharmony_ci sdev = svm_lookup_device_by_dev(svm, dev); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciout: 31262306a36Sopenharmony_ci *rsvm = svm; 31362306a36Sopenharmony_ci *rsdev = sdev; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int intel_svm_bind_mm(struct intel_iommu *iommu, struct device *dev, 31962306a36Sopenharmony_ci struct mm_struct *mm) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct device_domain_info *info = dev_iommu_priv_get(dev); 32262306a36Sopenharmony_ci struct intel_svm_dev *sdev; 32362306a36Sopenharmony_ci struct intel_svm *svm; 32462306a36Sopenharmony_ci unsigned long sflags; 32562306a36Sopenharmony_ci int ret = 0; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci svm = pasid_private_find(mm->pasid); 32862306a36Sopenharmony_ci if (!svm) { 32962306a36Sopenharmony_ci svm = kzalloc(sizeof(*svm), GFP_KERNEL); 33062306a36Sopenharmony_ci if (!svm) 33162306a36Sopenharmony_ci return -ENOMEM; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci svm->pasid = mm->pasid; 33462306a36Sopenharmony_ci svm->mm = mm; 33562306a36Sopenharmony_ci INIT_LIST_HEAD_RCU(&svm->devs); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci svm->notifier.ops = &intel_mmuops; 33862306a36Sopenharmony_ci ret = mmu_notifier_register(&svm->notifier, mm); 33962306a36Sopenharmony_ci if (ret) { 34062306a36Sopenharmony_ci kfree(svm); 34162306a36Sopenharmony_ci return ret; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci ret = pasid_private_add(svm->pasid, svm); 34562306a36Sopenharmony_ci if (ret) { 34662306a36Sopenharmony_ci mmu_notifier_unregister(&svm->notifier, mm); 34762306a36Sopenharmony_ci kfree(svm); 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); 35362306a36Sopenharmony_ci if (!sdev) { 35462306a36Sopenharmony_ci ret = -ENOMEM; 35562306a36Sopenharmony_ci goto free_svm; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci sdev->dev = dev; 35962306a36Sopenharmony_ci sdev->iommu = iommu; 36062306a36Sopenharmony_ci sdev->did = FLPT_DEFAULT_DID; 36162306a36Sopenharmony_ci sdev->sid = PCI_DEVID(info->bus, info->devfn); 36262306a36Sopenharmony_ci init_rcu_head(&sdev->rcu); 36362306a36Sopenharmony_ci if (info->ats_enabled) { 36462306a36Sopenharmony_ci sdev->qdep = info->ats_qdep; 36562306a36Sopenharmony_ci if (sdev->qdep >= QI_DEV_EIOTLB_MAX_INVS) 36662306a36Sopenharmony_ci sdev->qdep = 0; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Setup the pasid table: */ 37062306a36Sopenharmony_ci sflags = cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0; 37162306a36Sopenharmony_ci ret = intel_pasid_setup_first_level(iommu, dev, mm->pgd, mm->pasid, 37262306a36Sopenharmony_ci FLPT_DEFAULT_DID, sflags); 37362306a36Sopenharmony_ci if (ret) 37462306a36Sopenharmony_ci goto free_sdev; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci list_add_rcu(&sdev->list, &svm->devs); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cifree_sdev: 38162306a36Sopenharmony_ci kfree(sdev); 38262306a36Sopenharmony_cifree_svm: 38362306a36Sopenharmony_ci if (list_empty(&svm->devs)) { 38462306a36Sopenharmony_ci mmu_notifier_unregister(&svm->notifier, mm); 38562306a36Sopenharmony_ci pasid_private_remove(mm->pasid); 38662306a36Sopenharmony_ci kfree(svm); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return ret; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_civoid intel_svm_remove_dev_pasid(struct device *dev, u32 pasid) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct intel_svm_dev *sdev; 39562306a36Sopenharmony_ci struct intel_iommu *iommu; 39662306a36Sopenharmony_ci struct intel_svm *svm; 39762306a36Sopenharmony_ci struct mm_struct *mm; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci iommu = device_to_iommu(dev, NULL, NULL); 40062306a36Sopenharmony_ci if (!iommu) 40162306a36Sopenharmony_ci return; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (pasid_to_svm_sdev(dev, pasid, &svm, &sdev)) 40462306a36Sopenharmony_ci return; 40562306a36Sopenharmony_ci mm = svm->mm; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (sdev) { 40862306a36Sopenharmony_ci list_del_rcu(&sdev->list); 40962306a36Sopenharmony_ci kfree_rcu(sdev, rcu); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (list_empty(&svm->devs)) { 41262306a36Sopenharmony_ci if (svm->notifier.ops) 41362306a36Sopenharmony_ci mmu_notifier_unregister(&svm->notifier, mm); 41462306a36Sopenharmony_ci pasid_private_remove(svm->pasid); 41562306a36Sopenharmony_ci /* 41662306a36Sopenharmony_ci * We mandate that no page faults may be outstanding 41762306a36Sopenharmony_ci * for the PASID when intel_svm_unbind_mm() is called. 41862306a36Sopenharmony_ci * If that is not obeyed, subtle errors will happen. 41962306a36Sopenharmony_ci * Let's make them less subtle... 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci memset(svm, 0x6b, sizeof(*svm)); 42262306a36Sopenharmony_ci kfree(svm); 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/* Page request queue descriptor */ 42862306a36Sopenharmony_cistruct page_req_dsc { 42962306a36Sopenharmony_ci union { 43062306a36Sopenharmony_ci struct { 43162306a36Sopenharmony_ci u64 type:8; 43262306a36Sopenharmony_ci u64 pasid_present:1; 43362306a36Sopenharmony_ci u64 priv_data_present:1; 43462306a36Sopenharmony_ci u64 rsvd:6; 43562306a36Sopenharmony_ci u64 rid:16; 43662306a36Sopenharmony_ci u64 pasid:20; 43762306a36Sopenharmony_ci u64 exe_req:1; 43862306a36Sopenharmony_ci u64 pm_req:1; 43962306a36Sopenharmony_ci u64 rsvd2:10; 44062306a36Sopenharmony_ci }; 44162306a36Sopenharmony_ci u64 qw_0; 44262306a36Sopenharmony_ci }; 44362306a36Sopenharmony_ci union { 44462306a36Sopenharmony_ci struct { 44562306a36Sopenharmony_ci u64 rd_req:1; 44662306a36Sopenharmony_ci u64 wr_req:1; 44762306a36Sopenharmony_ci u64 lpig:1; 44862306a36Sopenharmony_ci u64 prg_index:9; 44962306a36Sopenharmony_ci u64 addr:52; 45062306a36Sopenharmony_ci }; 45162306a36Sopenharmony_ci u64 qw_1; 45262306a36Sopenharmony_ci }; 45362306a36Sopenharmony_ci u64 priv_data[2]; 45462306a36Sopenharmony_ci}; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic bool is_canonical_address(u64 addr) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci int shift = 64 - (__VIRTUAL_MASK_SHIFT + 1); 45962306a36Sopenharmony_ci long saddr = (long) addr; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return (((saddr << shift) >> shift) == saddr); 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci/** 46562306a36Sopenharmony_ci * intel_drain_pasid_prq - Drain page requests and responses for a pasid 46662306a36Sopenharmony_ci * @dev: target device 46762306a36Sopenharmony_ci * @pasid: pasid for draining 46862306a36Sopenharmony_ci * 46962306a36Sopenharmony_ci * Drain all pending page requests and responses related to @pasid in both 47062306a36Sopenharmony_ci * software and hardware. This is supposed to be called after the device 47162306a36Sopenharmony_ci * driver has stopped DMA, the pasid entry has been cleared, and both IOTLB 47262306a36Sopenharmony_ci * and DevTLB have been invalidated. 47362306a36Sopenharmony_ci * 47462306a36Sopenharmony_ci * It waits until all pending page requests for @pasid in the page fault 47562306a36Sopenharmony_ci * queue are completed by the prq handling thread. Then follow the steps 47662306a36Sopenharmony_ci * described in VT-d spec CH7.10 to drain all page requests and page 47762306a36Sopenharmony_ci * responses pending in the hardware. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_civoid intel_drain_pasid_prq(struct device *dev, u32 pasid) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct device_domain_info *info; 48262306a36Sopenharmony_ci struct dmar_domain *domain; 48362306a36Sopenharmony_ci struct intel_iommu *iommu; 48462306a36Sopenharmony_ci struct qi_desc desc[3]; 48562306a36Sopenharmony_ci struct pci_dev *pdev; 48662306a36Sopenharmony_ci int head, tail; 48762306a36Sopenharmony_ci u16 sid, did; 48862306a36Sopenharmony_ci int qdep; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci info = dev_iommu_priv_get(dev); 49162306a36Sopenharmony_ci if (WARN_ON(!info || !dev_is_pci(dev))) 49262306a36Sopenharmony_ci return; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (!info->pri_enabled) 49562306a36Sopenharmony_ci return; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci iommu = info->iommu; 49862306a36Sopenharmony_ci domain = info->domain; 49962306a36Sopenharmony_ci pdev = to_pci_dev(dev); 50062306a36Sopenharmony_ci sid = PCI_DEVID(info->bus, info->devfn); 50162306a36Sopenharmony_ci did = domain_id_iommu(domain, iommu); 50262306a36Sopenharmony_ci qdep = pci_ats_queue_depth(pdev); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* 50562306a36Sopenharmony_ci * Check and wait until all pending page requests in the queue are 50662306a36Sopenharmony_ci * handled by the prq handling thread. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_ciprq_retry: 50962306a36Sopenharmony_ci reinit_completion(&iommu->prq_complete); 51062306a36Sopenharmony_ci tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK; 51162306a36Sopenharmony_ci head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK; 51262306a36Sopenharmony_ci while (head != tail) { 51362306a36Sopenharmony_ci struct page_req_dsc *req; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci req = &iommu->prq[head / sizeof(*req)]; 51662306a36Sopenharmony_ci if (!req->pasid_present || req->pasid != pasid) { 51762306a36Sopenharmony_ci head = (head + sizeof(*req)) & PRQ_RING_MASK; 51862306a36Sopenharmony_ci continue; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci wait_for_completion(&iommu->prq_complete); 52262306a36Sopenharmony_ci goto prq_retry; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci iopf_queue_flush_dev(dev); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* 52862306a36Sopenharmony_ci * Perform steps described in VT-d spec CH7.10 to drain page 52962306a36Sopenharmony_ci * requests and responses in hardware. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci memset(desc, 0, sizeof(desc)); 53262306a36Sopenharmony_ci desc[0].qw0 = QI_IWD_STATUS_DATA(QI_DONE) | 53362306a36Sopenharmony_ci QI_IWD_FENCE | 53462306a36Sopenharmony_ci QI_IWD_TYPE; 53562306a36Sopenharmony_ci desc[1].qw0 = QI_EIOTLB_PASID(pasid) | 53662306a36Sopenharmony_ci QI_EIOTLB_DID(did) | 53762306a36Sopenharmony_ci QI_EIOTLB_GRAN(QI_GRAN_NONG_PASID) | 53862306a36Sopenharmony_ci QI_EIOTLB_TYPE; 53962306a36Sopenharmony_ci desc[2].qw0 = QI_DEV_EIOTLB_PASID(pasid) | 54062306a36Sopenharmony_ci QI_DEV_EIOTLB_SID(sid) | 54162306a36Sopenharmony_ci QI_DEV_EIOTLB_QDEP(qdep) | 54262306a36Sopenharmony_ci QI_DEIOTLB_TYPE | 54362306a36Sopenharmony_ci QI_DEV_IOTLB_PFSID(info->pfsid); 54462306a36Sopenharmony_ciqi_retry: 54562306a36Sopenharmony_ci reinit_completion(&iommu->prq_complete); 54662306a36Sopenharmony_ci qi_submit_sync(iommu, desc, 3, QI_OPT_WAIT_DRAIN); 54762306a36Sopenharmony_ci if (readl(iommu->reg + DMAR_PRS_REG) & DMA_PRS_PRO) { 54862306a36Sopenharmony_ci wait_for_completion(&iommu->prq_complete); 54962306a36Sopenharmony_ci goto qi_retry; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int prq_to_iommu_prot(struct page_req_dsc *req) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci int prot = 0; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (req->rd_req) 55862306a36Sopenharmony_ci prot |= IOMMU_FAULT_PERM_READ; 55962306a36Sopenharmony_ci if (req->wr_req) 56062306a36Sopenharmony_ci prot |= IOMMU_FAULT_PERM_WRITE; 56162306a36Sopenharmony_ci if (req->exe_req) 56262306a36Sopenharmony_ci prot |= IOMMU_FAULT_PERM_EXEC; 56362306a36Sopenharmony_ci if (req->pm_req) 56462306a36Sopenharmony_ci prot |= IOMMU_FAULT_PERM_PRIV; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return prot; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int intel_svm_prq_report(struct intel_iommu *iommu, struct device *dev, 57062306a36Sopenharmony_ci struct page_req_dsc *desc) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct iommu_fault_event event; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (!dev || !dev_is_pci(dev)) 57562306a36Sopenharmony_ci return -ENODEV; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* Fill in event data for device specific processing */ 57862306a36Sopenharmony_ci memset(&event, 0, sizeof(struct iommu_fault_event)); 57962306a36Sopenharmony_ci event.fault.type = IOMMU_FAULT_PAGE_REQ; 58062306a36Sopenharmony_ci event.fault.prm.addr = (u64)desc->addr << VTD_PAGE_SHIFT; 58162306a36Sopenharmony_ci event.fault.prm.pasid = desc->pasid; 58262306a36Sopenharmony_ci event.fault.prm.grpid = desc->prg_index; 58362306a36Sopenharmony_ci event.fault.prm.perm = prq_to_iommu_prot(desc); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (desc->lpig) 58662306a36Sopenharmony_ci event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE; 58762306a36Sopenharmony_ci if (desc->pasid_present) { 58862306a36Sopenharmony_ci event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID; 58962306a36Sopenharmony_ci event.fault.prm.flags |= IOMMU_FAULT_PAGE_RESPONSE_NEEDS_PASID; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci if (desc->priv_data_present) { 59262306a36Sopenharmony_ci /* 59362306a36Sopenharmony_ci * Set last page in group bit if private data is present, 59462306a36Sopenharmony_ci * page response is required as it does for LPIG. 59562306a36Sopenharmony_ci * iommu_report_device_fault() doesn't understand this vendor 59662306a36Sopenharmony_ci * specific requirement thus we set last_page as a workaround. 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ci event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE; 59962306a36Sopenharmony_ci event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PRIV_DATA; 60062306a36Sopenharmony_ci event.fault.prm.private_data[0] = desc->priv_data[0]; 60162306a36Sopenharmony_ci event.fault.prm.private_data[1] = desc->priv_data[1]; 60262306a36Sopenharmony_ci } else if (dmar_latency_enabled(iommu, DMAR_LATENCY_PRQ)) { 60362306a36Sopenharmony_ci /* 60462306a36Sopenharmony_ci * If the private data fields are not used by hardware, use it 60562306a36Sopenharmony_ci * to monitor the prq handle latency. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci event.fault.prm.private_data[0] = ktime_to_ns(ktime_get()); 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return iommu_report_device_fault(dev, &event); 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void handle_bad_prq_event(struct intel_iommu *iommu, 61462306a36Sopenharmony_ci struct page_req_dsc *req, int result) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct qi_desc desc; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci pr_err("%s: Invalid page request: %08llx %08llx\n", 61962306a36Sopenharmony_ci iommu->name, ((unsigned long long *)req)[0], 62062306a36Sopenharmony_ci ((unsigned long long *)req)[1]); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* 62362306a36Sopenharmony_ci * Per VT-d spec. v3.0 ch7.7, system software must 62462306a36Sopenharmony_ci * respond with page group response if private data 62562306a36Sopenharmony_ci * is present (PDP) or last page in group (LPIG) bit 62662306a36Sopenharmony_ci * is set. This is an additional VT-d feature beyond 62762306a36Sopenharmony_ci * PCI ATS spec. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci if (!req->lpig && !req->priv_data_present) 63062306a36Sopenharmony_ci return; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci desc.qw0 = QI_PGRP_PASID(req->pasid) | 63362306a36Sopenharmony_ci QI_PGRP_DID(req->rid) | 63462306a36Sopenharmony_ci QI_PGRP_PASID_P(req->pasid_present) | 63562306a36Sopenharmony_ci QI_PGRP_PDP(req->priv_data_present) | 63662306a36Sopenharmony_ci QI_PGRP_RESP_CODE(result) | 63762306a36Sopenharmony_ci QI_PGRP_RESP_TYPE; 63862306a36Sopenharmony_ci desc.qw1 = QI_PGRP_IDX(req->prg_index) | 63962306a36Sopenharmony_ci QI_PGRP_LPIG(req->lpig); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (req->priv_data_present) { 64262306a36Sopenharmony_ci desc.qw2 = req->priv_data[0]; 64362306a36Sopenharmony_ci desc.qw3 = req->priv_data[1]; 64462306a36Sopenharmony_ci } else { 64562306a36Sopenharmony_ci desc.qw2 = 0; 64662306a36Sopenharmony_ci desc.qw3 = 0; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci qi_submit_sync(iommu, &desc, 1, 0); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic irqreturn_t prq_event_thread(int irq, void *d) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct intel_iommu *iommu = d; 65562306a36Sopenharmony_ci struct page_req_dsc *req; 65662306a36Sopenharmony_ci int head, tail, handled; 65762306a36Sopenharmony_ci struct pci_dev *pdev; 65862306a36Sopenharmony_ci u64 address; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* 66162306a36Sopenharmony_ci * Clear PPR bit before reading head/tail registers, to ensure that 66262306a36Sopenharmony_ci * we get a new interrupt if needed. 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci writel(DMA_PRS_PPR, iommu->reg + DMAR_PRS_REG); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK; 66762306a36Sopenharmony_ci head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK; 66862306a36Sopenharmony_ci handled = (head != tail); 66962306a36Sopenharmony_ci while (head != tail) { 67062306a36Sopenharmony_ci req = &iommu->prq[head / sizeof(*req)]; 67162306a36Sopenharmony_ci address = (u64)req->addr << VTD_PAGE_SHIFT; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (unlikely(!req->pasid_present)) { 67462306a36Sopenharmony_ci pr_err("IOMMU: %s: Page request without PASID\n", 67562306a36Sopenharmony_ci iommu->name); 67662306a36Sopenharmony_cibad_req: 67762306a36Sopenharmony_ci handle_bad_prq_event(iommu, req, QI_RESP_INVALID); 67862306a36Sopenharmony_ci goto prq_advance; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (unlikely(!is_canonical_address(address))) { 68262306a36Sopenharmony_ci pr_err("IOMMU: %s: Address is not canonical\n", 68362306a36Sopenharmony_ci iommu->name); 68462306a36Sopenharmony_ci goto bad_req; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (unlikely(req->pm_req && (req->rd_req | req->wr_req))) { 68862306a36Sopenharmony_ci pr_err("IOMMU: %s: Page request in Privilege Mode\n", 68962306a36Sopenharmony_ci iommu->name); 69062306a36Sopenharmony_ci goto bad_req; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (unlikely(req->exe_req && req->rd_req)) { 69462306a36Sopenharmony_ci pr_err("IOMMU: %s: Execution request not supported\n", 69562306a36Sopenharmony_ci iommu->name); 69662306a36Sopenharmony_ci goto bad_req; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* Drop Stop Marker message. No need for a response. */ 70062306a36Sopenharmony_ci if (unlikely(req->lpig && !req->rd_req && !req->wr_req)) 70162306a36Sopenharmony_ci goto prq_advance; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci pdev = pci_get_domain_bus_and_slot(iommu->segment, 70462306a36Sopenharmony_ci PCI_BUS_NUM(req->rid), 70562306a36Sopenharmony_ci req->rid & 0xff); 70662306a36Sopenharmony_ci /* 70762306a36Sopenharmony_ci * If prq is to be handled outside iommu driver via receiver of 70862306a36Sopenharmony_ci * the fault notifiers, we skip the page response here. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_ci if (!pdev) 71162306a36Sopenharmony_ci goto bad_req; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (intel_svm_prq_report(iommu, &pdev->dev, req)) 71462306a36Sopenharmony_ci handle_bad_prq_event(iommu, req, QI_RESP_INVALID); 71562306a36Sopenharmony_ci else 71662306a36Sopenharmony_ci trace_prq_report(iommu, &pdev->dev, req->qw_0, req->qw_1, 71762306a36Sopenharmony_ci req->priv_data[0], req->priv_data[1], 71862306a36Sopenharmony_ci iommu->prq_seq_number++); 71962306a36Sopenharmony_ci pci_dev_put(pdev); 72062306a36Sopenharmony_ciprq_advance: 72162306a36Sopenharmony_ci head = (head + sizeof(*req)) & PRQ_RING_MASK; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQH_REG, tail); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* 72762306a36Sopenharmony_ci * Clear the page request overflow bit and wake up all threads that 72862306a36Sopenharmony_ci * are waiting for the completion of this handling. 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_ci if (readl(iommu->reg + DMAR_PRS_REG) & DMA_PRS_PRO) { 73162306a36Sopenharmony_ci pr_info_ratelimited("IOMMU: %s: PRQ overflow detected\n", 73262306a36Sopenharmony_ci iommu->name); 73362306a36Sopenharmony_ci head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK; 73462306a36Sopenharmony_ci tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK; 73562306a36Sopenharmony_ci if (head == tail) { 73662306a36Sopenharmony_ci iopf_queue_discard_partial(iommu->iopf_queue); 73762306a36Sopenharmony_ci writel(DMA_PRS_PRO, iommu->reg + DMAR_PRS_REG); 73862306a36Sopenharmony_ci pr_info_ratelimited("IOMMU: %s: PRQ overflow cleared", 73962306a36Sopenharmony_ci iommu->name); 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!completion_done(&iommu->prq_complete)) 74462306a36Sopenharmony_ci complete(&iommu->prq_complete); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return IRQ_RETVAL(handled); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ciint intel_svm_page_response(struct device *dev, 75062306a36Sopenharmony_ci struct iommu_fault_event *evt, 75162306a36Sopenharmony_ci struct iommu_page_response *msg) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci struct iommu_fault_page_request *prm; 75462306a36Sopenharmony_ci struct intel_iommu *iommu; 75562306a36Sopenharmony_ci bool private_present; 75662306a36Sopenharmony_ci bool pasid_present; 75762306a36Sopenharmony_ci bool last_page; 75862306a36Sopenharmony_ci u8 bus, devfn; 75962306a36Sopenharmony_ci int ret = 0; 76062306a36Sopenharmony_ci u16 sid; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (!dev || !dev_is_pci(dev)) 76362306a36Sopenharmony_ci return -ENODEV; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci iommu = device_to_iommu(dev, &bus, &devfn); 76662306a36Sopenharmony_ci if (!iommu) 76762306a36Sopenharmony_ci return -ENODEV; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (!msg || !evt) 77062306a36Sopenharmony_ci return -EINVAL; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci prm = &evt->fault.prm; 77362306a36Sopenharmony_ci sid = PCI_DEVID(bus, devfn); 77462306a36Sopenharmony_ci pasid_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID; 77562306a36Sopenharmony_ci private_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PRIV_DATA; 77662306a36Sopenharmony_ci last_page = prm->flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (!pasid_present) { 77962306a36Sopenharmony_ci ret = -EINVAL; 78062306a36Sopenharmony_ci goto out; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (prm->pasid == 0 || prm->pasid >= PASID_MAX) { 78462306a36Sopenharmony_ci ret = -EINVAL; 78562306a36Sopenharmony_ci goto out; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* 78962306a36Sopenharmony_ci * Per VT-d spec. v3.0 ch7.7, system software must respond 79062306a36Sopenharmony_ci * with page group response if private data is present (PDP) 79162306a36Sopenharmony_ci * or last page in group (LPIG) bit is set. This is an 79262306a36Sopenharmony_ci * additional VT-d requirement beyond PCI ATS spec. 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_ci if (last_page || private_present) { 79562306a36Sopenharmony_ci struct qi_desc desc; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci desc.qw0 = QI_PGRP_PASID(prm->pasid) | QI_PGRP_DID(sid) | 79862306a36Sopenharmony_ci QI_PGRP_PASID_P(pasid_present) | 79962306a36Sopenharmony_ci QI_PGRP_PDP(private_present) | 80062306a36Sopenharmony_ci QI_PGRP_RESP_CODE(msg->code) | 80162306a36Sopenharmony_ci QI_PGRP_RESP_TYPE; 80262306a36Sopenharmony_ci desc.qw1 = QI_PGRP_IDX(prm->grpid) | QI_PGRP_LPIG(last_page); 80362306a36Sopenharmony_ci desc.qw2 = 0; 80462306a36Sopenharmony_ci desc.qw3 = 0; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (private_present) { 80762306a36Sopenharmony_ci desc.qw2 = prm->private_data[0]; 80862306a36Sopenharmony_ci desc.qw3 = prm->private_data[1]; 80962306a36Sopenharmony_ci } else if (prm->private_data[0]) { 81062306a36Sopenharmony_ci dmar_latency_update(iommu, DMAR_LATENCY_PRQ, 81162306a36Sopenharmony_ci ktime_to_ns(ktime_get()) - prm->private_data[0]); 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci qi_submit_sync(iommu, &desc, 1, 0); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ciout: 81762306a36Sopenharmony_ci return ret; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic int intel_svm_set_dev_pasid(struct iommu_domain *domain, 82162306a36Sopenharmony_ci struct device *dev, ioasid_t pasid) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct device_domain_info *info = dev_iommu_priv_get(dev); 82462306a36Sopenharmony_ci struct intel_iommu *iommu = info->iommu; 82562306a36Sopenharmony_ci struct mm_struct *mm = domain->mm; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci return intel_svm_bind_mm(iommu, dev, mm); 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic void intel_svm_domain_free(struct iommu_domain *domain) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci kfree(to_dmar_domain(domain)); 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic const struct iommu_domain_ops intel_svm_domain_ops = { 83662306a36Sopenharmony_ci .set_dev_pasid = intel_svm_set_dev_pasid, 83762306a36Sopenharmony_ci .free = intel_svm_domain_free 83862306a36Sopenharmony_ci}; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistruct iommu_domain *intel_svm_domain_alloc(void) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct dmar_domain *domain; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci domain = kzalloc(sizeof(*domain), GFP_KERNEL); 84562306a36Sopenharmony_ci if (!domain) 84662306a36Sopenharmony_ci return NULL; 84762306a36Sopenharmony_ci domain->domain.ops = &intel_svm_domain_ops; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return &domain->domain; 85062306a36Sopenharmony_ci} 851