18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2015 Intel Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: David Woodhouse <dwmw2@infradead.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/intel-iommu.h> 98c2ecf20Sopenharmony_ci#include <linux/mmu_notifier.h> 108c2ecf20Sopenharmony_ci#include <linux/sched.h> 118c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/intel-svm.h> 148c2ecf20Sopenharmony_ci#include <linux/rculist.h> 158c2ecf20Sopenharmony_ci#include <linux/pci.h> 168c2ecf20Sopenharmony_ci#include <linux/pci-ats.h> 178c2ecf20Sopenharmony_ci#include <linux/dmar.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/mm_types.h> 208c2ecf20Sopenharmony_ci#include <linux/ioasid.h> 218c2ecf20Sopenharmony_ci#include <asm/page.h> 228c2ecf20Sopenharmony_ci#include <asm/fpu/api.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "pasid.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic irqreturn_t prq_event_thread(int irq, void *d); 278c2ecf20Sopenharmony_cistatic void intel_svm_drain_prq(struct device *dev, u32 pasid); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define PRQ_ORDER 0 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciint intel_svm_enable_prq(struct intel_iommu *iommu) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct page *pages; 348c2ecf20Sopenharmony_ci int irq, ret; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, PRQ_ORDER); 378c2ecf20Sopenharmony_ci if (!pages) { 388c2ecf20Sopenharmony_ci pr_warn("IOMMU: %s: Failed to allocate page request queue\n", 398c2ecf20Sopenharmony_ci iommu->name); 408c2ecf20Sopenharmony_ci return -ENOMEM; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci iommu->prq = page_address(pages); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci irq = dmar_alloc_hwirq(DMAR_UNITS_SUPPORTED + iommu->seq_id, iommu->node, iommu); 458c2ecf20Sopenharmony_ci if (irq <= 0) { 468c2ecf20Sopenharmony_ci pr_err("IOMMU: %s: Failed to create IRQ vector for page request queue\n", 478c2ecf20Sopenharmony_ci iommu->name); 488c2ecf20Sopenharmony_ci ret = -EINVAL; 498c2ecf20Sopenharmony_ci err: 508c2ecf20Sopenharmony_ci free_pages((unsigned long)iommu->prq, PRQ_ORDER); 518c2ecf20Sopenharmony_ci iommu->prq = NULL; 528c2ecf20Sopenharmony_ci return ret; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci iommu->pr_irq = irq; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci snprintf(iommu->prq_name, sizeof(iommu->prq_name), "dmar%d-prq", iommu->seq_id); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci ret = request_threaded_irq(irq, NULL, prq_event_thread, IRQF_ONESHOT, 598c2ecf20Sopenharmony_ci iommu->prq_name, iommu); 608c2ecf20Sopenharmony_ci if (ret) { 618c2ecf20Sopenharmony_ci pr_err("IOMMU: %s: Failed to request IRQ for page request queue\n", 628c2ecf20Sopenharmony_ci iommu->name); 638c2ecf20Sopenharmony_ci dmar_free_hwirq(irq); 648c2ecf20Sopenharmony_ci iommu->pr_irq = 0; 658c2ecf20Sopenharmony_ci goto err; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQH_REG, 0ULL); 688c2ecf20Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQT_REG, 0ULL); 698c2ecf20Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQA_REG, virt_to_phys(iommu->prq) | PRQ_ORDER); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci init_completion(&iommu->prq_complete); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciint intel_svm_finish_prq(struct intel_iommu *iommu) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQH_REG, 0ULL); 798c2ecf20Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQT_REG, 0ULL); 808c2ecf20Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQA_REG, 0ULL); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (iommu->pr_irq) { 838c2ecf20Sopenharmony_ci free_irq(iommu->pr_irq, iommu); 848c2ecf20Sopenharmony_ci dmar_free_hwirq(iommu->pr_irq); 858c2ecf20Sopenharmony_ci iommu->pr_irq = 0; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci free_pages((unsigned long)iommu->prq, PRQ_ORDER); 898c2ecf20Sopenharmony_ci iommu->prq = NULL; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline bool intel_svm_capable(struct intel_iommu *iommu) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci return iommu->flags & VTD_FLAG_SVM_CAPABLE; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_civoid intel_svm_check(struct intel_iommu *iommu) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci if (!pasid_supported(iommu)) 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (cpu_feature_enabled(X86_FEATURE_GBPAGES) && 1058c2ecf20Sopenharmony_ci !cap_fl1gp_support(iommu->cap)) { 1068c2ecf20Sopenharmony_ci pr_err("%s SVM disabled, incompatible 1GB page capability\n", 1078c2ecf20Sopenharmony_ci iommu->name); 1088c2ecf20Sopenharmony_ci return; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (cpu_feature_enabled(X86_FEATURE_LA57) && 1128c2ecf20Sopenharmony_ci !cap_5lp_support(iommu->cap)) { 1138c2ecf20Sopenharmony_ci pr_err("%s SVM disabled, incompatible paging mode\n", 1148c2ecf20Sopenharmony_ci iommu->name); 1158c2ecf20Sopenharmony_ci return; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci iommu->flags |= VTD_FLAG_SVM_CAPABLE; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void __flush_svm_range_dev(struct intel_svm *svm, 1228c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev, 1238c2ecf20Sopenharmony_ci unsigned long address, 1248c2ecf20Sopenharmony_ci unsigned long pages, int ih) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct device_domain_info *info = get_domain_info(sdev->dev); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (WARN_ON(!pages)) 1298c2ecf20Sopenharmony_ci return; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci qi_flush_piotlb(sdev->iommu, sdev->did, svm->pasid, address, pages, ih); 1328c2ecf20Sopenharmony_ci if (info->ats_enabled) 1338c2ecf20Sopenharmony_ci qi_flush_dev_iotlb_pasid(sdev->iommu, sdev->sid, info->pfsid, 1348c2ecf20Sopenharmony_ci svm->pasid, sdev->qdep, address, 1358c2ecf20Sopenharmony_ci order_base_2(pages)); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void intel_flush_svm_range_dev(struct intel_svm *svm, 1398c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev, 1408c2ecf20Sopenharmony_ci unsigned long address, 1418c2ecf20Sopenharmony_ci unsigned long pages, int ih) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci unsigned long shift = ilog2(__roundup_pow_of_two(pages)); 1448c2ecf20Sopenharmony_ci unsigned long align = (1ULL << (VTD_PAGE_SHIFT + shift)); 1458c2ecf20Sopenharmony_ci unsigned long start = ALIGN_DOWN(address, align); 1468c2ecf20Sopenharmony_ci unsigned long end = ALIGN(address + (pages << VTD_PAGE_SHIFT), align); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci while (start < end) { 1498c2ecf20Sopenharmony_ci __flush_svm_range_dev(svm, sdev, start, align >> VTD_PAGE_SHIFT, ih); 1508c2ecf20Sopenharmony_ci start += align; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void intel_flush_svm_range(struct intel_svm *svm, unsigned long address, 1558c2ecf20Sopenharmony_ci unsigned long pages, int ih) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci rcu_read_lock(); 1608c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sdev, &svm->devs, list) 1618c2ecf20Sopenharmony_ci intel_flush_svm_range_dev(svm, sdev, address, pages, ih); 1628c2ecf20Sopenharmony_ci rcu_read_unlock(); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* Pages have been freed at this point */ 1668c2ecf20Sopenharmony_cistatic void intel_invalidate_range(struct mmu_notifier *mn, 1678c2ecf20Sopenharmony_ci struct mm_struct *mm, 1688c2ecf20Sopenharmony_ci unsigned long start, unsigned long end) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct intel_svm *svm = container_of(mn, struct intel_svm, notifier); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci intel_flush_svm_range(svm, start, 1738c2ecf20Sopenharmony_ci (end - start + PAGE_SIZE - 1) >> VTD_PAGE_SHIFT, 0); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct intel_svm *svm = container_of(mn, struct intel_svm, notifier); 1798c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* This might end up being called from exit_mmap(), *before* the page 1828c2ecf20Sopenharmony_ci * tables are cleared. And __mmu_notifier_release() will delete us from 1838c2ecf20Sopenharmony_ci * the list of notifiers so that our invalidate_range() callback doesn't 1848c2ecf20Sopenharmony_ci * get called when the page tables are cleared. So we need to protect 1858c2ecf20Sopenharmony_ci * against hardware accessing those page tables. 1868c2ecf20Sopenharmony_ci * 1878c2ecf20Sopenharmony_ci * We do it by clearing the entry in the PASID table and then flushing 1888c2ecf20Sopenharmony_ci * the IOTLB and the PASID table caches. This might upset hardware; 1898c2ecf20Sopenharmony_ci * perhaps we'll want to point the PASID to a dummy PGD (like the zero 1908c2ecf20Sopenharmony_ci * page) so that we end up taking a fault that the hardware really 1918c2ecf20Sopenharmony_ci * *has* to handle gracefully without affecting other processes. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci rcu_read_lock(); 1948c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sdev, &svm->devs, list) 1958c2ecf20Sopenharmony_ci intel_pasid_tear_down_entry(sdev->iommu, sdev->dev, 1968c2ecf20Sopenharmony_ci svm->pasid, true); 1978c2ecf20Sopenharmony_ci rcu_read_unlock(); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic const struct mmu_notifier_ops intel_mmuops = { 2028c2ecf20Sopenharmony_ci .release = intel_mm_release, 2038c2ecf20Sopenharmony_ci .invalidate_range = intel_invalidate_range, 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pasid_mutex); 2078c2ecf20Sopenharmony_cistatic LIST_HEAD(global_svm_list); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#define for_each_svm_dev(sdev, svm, d) \ 2108c2ecf20Sopenharmony_ci list_for_each_entry((sdev), &(svm)->devs, list) \ 2118c2ecf20Sopenharmony_ci if ((d) != (sdev)->dev) {} else 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int pasid_to_svm_sdev(struct device *dev, unsigned int pasid, 2148c2ecf20Sopenharmony_ci struct intel_svm **rsvm, 2158c2ecf20Sopenharmony_ci struct intel_svm_dev **rsdev) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct intel_svm_dev *d, *sdev = NULL; 2188c2ecf20Sopenharmony_ci struct intel_svm *svm; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* The caller should hold the pasid_mutex lock */ 2218c2ecf20Sopenharmony_ci if (WARN_ON(!mutex_is_locked(&pasid_mutex))) 2228c2ecf20Sopenharmony_ci return -EINVAL; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (pasid == INVALID_IOASID || pasid >= PASID_MAX) 2258c2ecf20Sopenharmony_ci return -EINVAL; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci svm = ioasid_find(NULL, pasid, NULL); 2288c2ecf20Sopenharmony_ci if (IS_ERR(svm)) 2298c2ecf20Sopenharmony_ci return PTR_ERR(svm); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!svm) 2328c2ecf20Sopenharmony_ci goto out; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* 2358c2ecf20Sopenharmony_ci * If we found svm for the PASID, there must be at least one device 2368c2ecf20Sopenharmony_ci * bond. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci if (WARN_ON(list_empty(&svm->devs))) 2398c2ecf20Sopenharmony_ci return -EINVAL; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci rcu_read_lock(); 2428c2ecf20Sopenharmony_ci list_for_each_entry_rcu(d, &svm->devs, list) { 2438c2ecf20Sopenharmony_ci if (d->dev == dev) { 2448c2ecf20Sopenharmony_ci sdev = d; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci rcu_read_unlock(); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ciout: 2518c2ecf20Sopenharmony_ci *rsvm = svm; 2528c2ecf20Sopenharmony_ci *rsdev = sdev; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ciint intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev, 2588c2ecf20Sopenharmony_ci struct iommu_gpasid_bind_data *data) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL); 2618c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev = NULL; 2628c2ecf20Sopenharmony_ci struct dmar_domain *dmar_domain; 2638c2ecf20Sopenharmony_ci struct device_domain_info *info; 2648c2ecf20Sopenharmony_ci struct intel_svm *svm = NULL; 2658c2ecf20Sopenharmony_ci unsigned long iflags; 2668c2ecf20Sopenharmony_ci int ret = 0; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (WARN_ON(!iommu) || !data) 2698c2ecf20Sopenharmony_ci return -EINVAL; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (data->format != IOMMU_PASID_FORMAT_INTEL_VTD) 2728c2ecf20Sopenharmony_ci return -EINVAL; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* IOMMU core ensures argsz is more than the start of the union */ 2758c2ecf20Sopenharmony_ci if (data->argsz < offsetofend(struct iommu_gpasid_bind_data, vendor.vtd)) 2768c2ecf20Sopenharmony_ci return -EINVAL; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Make sure no undefined flags are used in vendor data */ 2798c2ecf20Sopenharmony_ci if (data->vendor.vtd.flags & ~(IOMMU_SVA_VTD_GPASID_LAST - 1)) 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (!dev_is_pci(dev)) 2838c2ecf20Sopenharmony_ci return -ENOTSUPP; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* VT-d supports devices with full 20 bit PASIDs only */ 2868c2ecf20Sopenharmony_ci if (pci_max_pasids(to_pci_dev(dev)) != PASID_MAX) 2878c2ecf20Sopenharmony_ci return -EINVAL; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * We only check host PASID range, we have no knowledge to check 2918c2ecf20Sopenharmony_ci * guest PASID range. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci if (data->hpasid <= 0 || data->hpasid >= PASID_MAX) 2948c2ecf20Sopenharmony_ci return -EINVAL; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci info = get_domain_info(dev); 2978c2ecf20Sopenharmony_ci if (!info) 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci dmar_domain = to_dmar_domain(domain); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci mutex_lock(&pasid_mutex); 3038c2ecf20Sopenharmony_ci ret = pasid_to_svm_sdev(dev, data->hpasid, &svm, &sdev); 3048c2ecf20Sopenharmony_ci if (ret) 3058c2ecf20Sopenharmony_ci goto out; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (sdev) { 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * Do not allow multiple bindings of the same device-PASID since 3108c2ecf20Sopenharmony_ci * there is only one SL page tables per PASID. We may revisit 3118c2ecf20Sopenharmony_ci * once sharing PGD across domains are supported. 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci dev_warn_ratelimited(dev, "Already bound with PASID %u\n", 3148c2ecf20Sopenharmony_ci svm->pasid); 3158c2ecf20Sopenharmony_ci ret = -EBUSY; 3168c2ecf20Sopenharmony_ci goto out; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (!svm) { 3208c2ecf20Sopenharmony_ci /* We come here when PASID has never been bond to a device. */ 3218c2ecf20Sopenharmony_ci svm = kzalloc(sizeof(*svm), GFP_KERNEL); 3228c2ecf20Sopenharmony_ci if (!svm) { 3238c2ecf20Sopenharmony_ci ret = -ENOMEM; 3248c2ecf20Sopenharmony_ci goto out; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci /* REVISIT: upper layer/VFIO can track host process that bind 3278c2ecf20Sopenharmony_ci * the PASID. ioasid_set = mm might be sufficient for vfio to 3288c2ecf20Sopenharmony_ci * check pasid VMM ownership. We can drop the following line 3298c2ecf20Sopenharmony_ci * once VFIO and IOASID set check is in place. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci svm->mm = get_task_mm(current); 3328c2ecf20Sopenharmony_ci svm->pasid = data->hpasid; 3338c2ecf20Sopenharmony_ci if (data->flags & IOMMU_SVA_GPASID_VAL) { 3348c2ecf20Sopenharmony_ci svm->gpasid = data->gpasid; 3358c2ecf20Sopenharmony_ci svm->flags |= SVM_FLAG_GUEST_PASID; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci ioasid_set_data(data->hpasid, svm); 3388c2ecf20Sopenharmony_ci INIT_LIST_HEAD_RCU(&svm->devs); 3398c2ecf20Sopenharmony_ci mmput(svm->mm); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); 3428c2ecf20Sopenharmony_ci if (!sdev) { 3438c2ecf20Sopenharmony_ci ret = -ENOMEM; 3448c2ecf20Sopenharmony_ci goto out; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci sdev->dev = dev; 3478c2ecf20Sopenharmony_ci sdev->sid = PCI_DEVID(info->bus, info->devfn); 3488c2ecf20Sopenharmony_ci sdev->iommu = iommu; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Only count users if device has aux domains */ 3518c2ecf20Sopenharmony_ci if (iommu_dev_feature_enabled(dev, IOMMU_DEV_FEAT_AUX)) 3528c2ecf20Sopenharmony_ci sdev->users = 1; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Set up device context entry for PASID if not enabled already */ 3558c2ecf20Sopenharmony_ci ret = intel_iommu_enable_pasid(iommu, sdev->dev); 3568c2ecf20Sopenharmony_ci if (ret) { 3578c2ecf20Sopenharmony_ci dev_err_ratelimited(dev, "Failed to enable PASID capability\n"); 3588c2ecf20Sopenharmony_ci kfree(sdev); 3598c2ecf20Sopenharmony_ci goto out; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* 3638c2ecf20Sopenharmony_ci * PASID table is per device for better security. Therefore, for 3648c2ecf20Sopenharmony_ci * each bind of a new device even with an existing PASID, we need to 3658c2ecf20Sopenharmony_ci * call the nested mode setup function here. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci spin_lock_irqsave(&iommu->lock, iflags); 3688c2ecf20Sopenharmony_ci ret = intel_pasid_setup_nested(iommu, dev, 3698c2ecf20Sopenharmony_ci (pgd_t *)(uintptr_t)data->gpgd, 3708c2ecf20Sopenharmony_ci data->hpasid, &data->vendor.vtd, dmar_domain, 3718c2ecf20Sopenharmony_ci data->addr_width); 3728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&iommu->lock, iflags); 3738c2ecf20Sopenharmony_ci if (ret) { 3748c2ecf20Sopenharmony_ci dev_err_ratelimited(dev, "Failed to set up PASID %llu in nested mode, Err %d\n", 3758c2ecf20Sopenharmony_ci data->hpasid, ret); 3768c2ecf20Sopenharmony_ci /* 3778c2ecf20Sopenharmony_ci * PASID entry should be in cleared state if nested mode 3788c2ecf20Sopenharmony_ci * set up failed. So we only need to clear IOASID tracking 3798c2ecf20Sopenharmony_ci * data such that free call will succeed. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_ci kfree(sdev); 3828c2ecf20Sopenharmony_ci goto out; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci svm->flags |= SVM_FLAG_GUEST_MODE; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci init_rcu_head(&sdev->rcu); 3888c2ecf20Sopenharmony_ci list_add_rcu(&sdev->list, &svm->devs); 3898c2ecf20Sopenharmony_ci out: 3908c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(svm) && list_empty(&svm->devs)) { 3918c2ecf20Sopenharmony_ci ioasid_set_data(data->hpasid, NULL); 3928c2ecf20Sopenharmony_ci kfree(svm); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci mutex_unlock(&pasid_mutex); 3968c2ecf20Sopenharmony_ci return ret; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ciint intel_svm_unbind_gpasid(struct device *dev, u32 pasid) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL); 4028c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev; 4038c2ecf20Sopenharmony_ci struct intel_svm *svm; 4048c2ecf20Sopenharmony_ci int ret; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (WARN_ON(!iommu)) 4078c2ecf20Sopenharmony_ci return -EINVAL; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci mutex_lock(&pasid_mutex); 4108c2ecf20Sopenharmony_ci ret = pasid_to_svm_sdev(dev, pasid, &svm, &sdev); 4118c2ecf20Sopenharmony_ci if (ret) 4128c2ecf20Sopenharmony_ci goto out; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (sdev) { 4158c2ecf20Sopenharmony_ci if (iommu_dev_feature_enabled(dev, IOMMU_DEV_FEAT_AUX)) 4168c2ecf20Sopenharmony_ci sdev->users--; 4178c2ecf20Sopenharmony_ci if (!sdev->users) { 4188c2ecf20Sopenharmony_ci list_del_rcu(&sdev->list); 4198c2ecf20Sopenharmony_ci intel_pasid_tear_down_entry(iommu, dev, 4208c2ecf20Sopenharmony_ci svm->pasid, false); 4218c2ecf20Sopenharmony_ci intel_svm_drain_prq(dev, svm->pasid); 4228c2ecf20Sopenharmony_ci kfree_rcu(sdev, rcu); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (list_empty(&svm->devs)) { 4258c2ecf20Sopenharmony_ci /* 4268c2ecf20Sopenharmony_ci * We do not free the IOASID here in that 4278c2ecf20Sopenharmony_ci * IOMMU driver did not allocate it. 4288c2ecf20Sopenharmony_ci * Unlike native SVM, IOASID for guest use was 4298c2ecf20Sopenharmony_ci * allocated prior to the bind call. 4308c2ecf20Sopenharmony_ci * In any case, if the free call comes before 4318c2ecf20Sopenharmony_ci * the unbind, IOMMU driver will get notified 4328c2ecf20Sopenharmony_ci * and perform cleanup. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_ci ioasid_set_data(pasid, NULL); 4358c2ecf20Sopenharmony_ci kfree(svm); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ciout: 4408c2ecf20Sopenharmony_ci mutex_unlock(&pasid_mutex); 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic void _load_pasid(void *unused) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci update_pasid(); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic void load_pasid(struct mm_struct *mm, u32 pasid) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci mutex_lock(&mm->context.lock); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* Synchronize with READ_ONCE in update_pasid(). */ 4548c2ecf20Sopenharmony_ci smp_store_release(&mm->pasid, pasid); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Update PASID MSR on all CPUs running the mm's tasks. */ 4578c2ecf20Sopenharmony_ci on_each_cpu_mask(mm_cpumask(mm), _load_pasid, NULL, true); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci mutex_unlock(&mm->context.lock); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/* Caller must hold pasid_mutex, mm reference */ 4638c2ecf20Sopenharmony_cistatic int 4648c2ecf20Sopenharmony_ciintel_svm_bind_mm(struct device *dev, unsigned int flags, 4658c2ecf20Sopenharmony_ci struct svm_dev_ops *ops, 4668c2ecf20Sopenharmony_ci struct mm_struct *mm, struct intel_svm_dev **sd) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL); 4698c2ecf20Sopenharmony_ci struct device_domain_info *info; 4708c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev; 4718c2ecf20Sopenharmony_ci struct intel_svm *svm = NULL; 4728c2ecf20Sopenharmony_ci unsigned long iflags; 4738c2ecf20Sopenharmony_ci int pasid_max; 4748c2ecf20Sopenharmony_ci int ret; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (!iommu || dmar_disabled) 4778c2ecf20Sopenharmony_ci return -EINVAL; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (!intel_svm_capable(iommu)) 4808c2ecf20Sopenharmony_ci return -ENOTSUPP; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (dev_is_pci(dev)) { 4838c2ecf20Sopenharmony_ci pasid_max = pci_max_pasids(to_pci_dev(dev)); 4848c2ecf20Sopenharmony_ci if (pasid_max < 0) 4858c2ecf20Sopenharmony_ci return -EINVAL; 4868c2ecf20Sopenharmony_ci } else 4878c2ecf20Sopenharmony_ci pasid_max = 1 << 20; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Bind supervisor PASID shuld have mm = NULL */ 4908c2ecf20Sopenharmony_ci if (flags & SVM_FLAG_SUPERVISOR_MODE) { 4918c2ecf20Sopenharmony_ci if (!ecap_srs(iommu->ecap) || mm) { 4928c2ecf20Sopenharmony_ci pr_err("Supervisor PASID with user provided mm.\n"); 4938c2ecf20Sopenharmony_ci return -EINVAL; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (!(flags & SVM_FLAG_PRIVATE_PASID)) { 4988c2ecf20Sopenharmony_ci struct intel_svm *t; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci list_for_each_entry(t, &global_svm_list, list) { 5018c2ecf20Sopenharmony_ci if (t->mm != mm || (t->flags & SVM_FLAG_PRIVATE_PASID)) 5028c2ecf20Sopenharmony_ci continue; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci svm = t; 5058c2ecf20Sopenharmony_ci if (svm->pasid >= pasid_max) { 5068c2ecf20Sopenharmony_ci dev_warn(dev, 5078c2ecf20Sopenharmony_ci "Limited PASID width. Cannot use existing PASID %d\n", 5088c2ecf20Sopenharmony_ci svm->pasid); 5098c2ecf20Sopenharmony_ci ret = -ENOSPC; 5108c2ecf20Sopenharmony_ci goto out; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* Find the matching device in svm list */ 5148c2ecf20Sopenharmony_ci for_each_svm_dev(sdev, svm, dev) { 5158c2ecf20Sopenharmony_ci if (sdev->ops != ops) { 5168c2ecf20Sopenharmony_ci ret = -EBUSY; 5178c2ecf20Sopenharmony_ci goto out; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci sdev->users++; 5208c2ecf20Sopenharmony_ci goto success; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); 5288c2ecf20Sopenharmony_ci if (!sdev) { 5298c2ecf20Sopenharmony_ci ret = -ENOMEM; 5308c2ecf20Sopenharmony_ci goto out; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci sdev->dev = dev; 5338c2ecf20Sopenharmony_ci sdev->iommu = iommu; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ret = intel_iommu_enable_pasid(iommu, dev); 5368c2ecf20Sopenharmony_ci if (ret) { 5378c2ecf20Sopenharmony_ci kfree(sdev); 5388c2ecf20Sopenharmony_ci goto out; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci info = get_domain_info(dev); 5428c2ecf20Sopenharmony_ci sdev->did = FLPT_DEFAULT_DID; 5438c2ecf20Sopenharmony_ci sdev->sid = PCI_DEVID(info->bus, info->devfn); 5448c2ecf20Sopenharmony_ci if (info->ats_enabled) { 5458c2ecf20Sopenharmony_ci sdev->dev_iotlb = 1; 5468c2ecf20Sopenharmony_ci sdev->qdep = info->ats_qdep; 5478c2ecf20Sopenharmony_ci if (sdev->qdep >= QI_DEV_EIOTLB_MAX_INVS) 5488c2ecf20Sopenharmony_ci sdev->qdep = 0; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Finish the setup now we know we're keeping it */ 5528c2ecf20Sopenharmony_ci sdev->users = 1; 5538c2ecf20Sopenharmony_ci sdev->ops = ops; 5548c2ecf20Sopenharmony_ci init_rcu_head(&sdev->rcu); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (!svm) { 5578c2ecf20Sopenharmony_ci svm = kzalloc(sizeof(*svm), GFP_KERNEL); 5588c2ecf20Sopenharmony_ci if (!svm) { 5598c2ecf20Sopenharmony_ci ret = -ENOMEM; 5608c2ecf20Sopenharmony_ci kfree(sdev); 5618c2ecf20Sopenharmony_ci goto out; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (pasid_max > intel_pasid_max_id) 5658c2ecf20Sopenharmony_ci pasid_max = intel_pasid_max_id; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Do not use PASID 0, reserved for RID to PASID */ 5688c2ecf20Sopenharmony_ci svm->pasid = ioasid_alloc(NULL, PASID_MIN, 5698c2ecf20Sopenharmony_ci pasid_max - 1, svm); 5708c2ecf20Sopenharmony_ci if (svm->pasid == INVALID_IOASID) { 5718c2ecf20Sopenharmony_ci kfree(svm); 5728c2ecf20Sopenharmony_ci kfree(sdev); 5738c2ecf20Sopenharmony_ci ret = -ENOSPC; 5748c2ecf20Sopenharmony_ci goto out; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci svm->notifier.ops = &intel_mmuops; 5778c2ecf20Sopenharmony_ci svm->mm = mm; 5788c2ecf20Sopenharmony_ci svm->flags = flags; 5798c2ecf20Sopenharmony_ci INIT_LIST_HEAD_RCU(&svm->devs); 5808c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&svm->list); 5818c2ecf20Sopenharmony_ci ret = -ENOMEM; 5828c2ecf20Sopenharmony_ci if (mm) { 5838c2ecf20Sopenharmony_ci ret = mmu_notifier_register(&svm->notifier, mm); 5848c2ecf20Sopenharmony_ci if (ret) { 5858c2ecf20Sopenharmony_ci ioasid_free(svm->pasid); 5868c2ecf20Sopenharmony_ci kfree(svm); 5878c2ecf20Sopenharmony_ci kfree(sdev); 5888c2ecf20Sopenharmony_ci goto out; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci spin_lock_irqsave(&iommu->lock, iflags); 5938c2ecf20Sopenharmony_ci ret = intel_pasid_setup_first_level(iommu, dev, 5948c2ecf20Sopenharmony_ci mm ? mm->pgd : init_mm.pgd, 5958c2ecf20Sopenharmony_ci svm->pasid, FLPT_DEFAULT_DID, 5968c2ecf20Sopenharmony_ci (mm ? 0 : PASID_FLAG_SUPERVISOR_MODE) | 5978c2ecf20Sopenharmony_ci (cpu_feature_enabled(X86_FEATURE_LA57) ? 5988c2ecf20Sopenharmony_ci PASID_FLAG_FL5LP : 0)); 5998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&iommu->lock, iflags); 6008c2ecf20Sopenharmony_ci if (ret) { 6018c2ecf20Sopenharmony_ci if (mm) 6028c2ecf20Sopenharmony_ci mmu_notifier_unregister(&svm->notifier, mm); 6038c2ecf20Sopenharmony_ci ioasid_free(svm->pasid); 6048c2ecf20Sopenharmony_ci kfree(svm); 6058c2ecf20Sopenharmony_ci kfree(sdev); 6068c2ecf20Sopenharmony_ci goto out; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci list_add_tail(&svm->list, &global_svm_list); 6108c2ecf20Sopenharmony_ci if (mm) { 6118c2ecf20Sopenharmony_ci /* The newly allocated pasid is loaded to the mm. */ 6128c2ecf20Sopenharmony_ci load_pasid(mm, svm->pasid); 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci } else { 6158c2ecf20Sopenharmony_ci /* 6168c2ecf20Sopenharmony_ci * Binding a new device with existing PASID, need to setup 6178c2ecf20Sopenharmony_ci * the PASID entry. 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ci spin_lock_irqsave(&iommu->lock, iflags); 6208c2ecf20Sopenharmony_ci ret = intel_pasid_setup_first_level(iommu, dev, 6218c2ecf20Sopenharmony_ci mm ? mm->pgd : init_mm.pgd, 6228c2ecf20Sopenharmony_ci svm->pasid, FLPT_DEFAULT_DID, 6238c2ecf20Sopenharmony_ci (mm ? 0 : PASID_FLAG_SUPERVISOR_MODE) | 6248c2ecf20Sopenharmony_ci (cpu_feature_enabled(X86_FEATURE_LA57) ? 6258c2ecf20Sopenharmony_ci PASID_FLAG_FL5LP : 0)); 6268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&iommu->lock, iflags); 6278c2ecf20Sopenharmony_ci if (ret) { 6288c2ecf20Sopenharmony_ci kfree(sdev); 6298c2ecf20Sopenharmony_ci goto out; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci list_add_rcu(&sdev->list, &svm->devs); 6338c2ecf20Sopenharmony_cisuccess: 6348c2ecf20Sopenharmony_ci sdev->pasid = svm->pasid; 6358c2ecf20Sopenharmony_ci sdev->sva.dev = dev; 6368c2ecf20Sopenharmony_ci if (sd) 6378c2ecf20Sopenharmony_ci *sd = sdev; 6388c2ecf20Sopenharmony_ci ret = 0; 6398c2ecf20Sopenharmony_ciout: 6408c2ecf20Sopenharmony_ci return ret; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci/* Caller must hold pasid_mutex */ 6448c2ecf20Sopenharmony_cistatic int intel_svm_unbind_mm(struct device *dev, u32 pasid) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev; 6478c2ecf20Sopenharmony_ci struct intel_iommu *iommu; 6488c2ecf20Sopenharmony_ci struct intel_svm *svm; 6498c2ecf20Sopenharmony_ci int ret = -EINVAL; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci iommu = device_to_iommu(dev, NULL, NULL); 6528c2ecf20Sopenharmony_ci if (!iommu) 6538c2ecf20Sopenharmony_ci goto out; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci ret = pasid_to_svm_sdev(dev, pasid, &svm, &sdev); 6568c2ecf20Sopenharmony_ci if (ret) 6578c2ecf20Sopenharmony_ci goto out; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (sdev) { 6608c2ecf20Sopenharmony_ci sdev->users--; 6618c2ecf20Sopenharmony_ci if (!sdev->users) { 6628c2ecf20Sopenharmony_ci list_del_rcu(&sdev->list); 6638c2ecf20Sopenharmony_ci /* Flush the PASID cache and IOTLB for this device. 6648c2ecf20Sopenharmony_ci * Note that we do depend on the hardware *not* using 6658c2ecf20Sopenharmony_ci * the PASID any more. Just as we depend on other 6668c2ecf20Sopenharmony_ci * devices never using PASIDs that they have no right 6678c2ecf20Sopenharmony_ci * to use. We have a *shared* PASID table, because it's 6688c2ecf20Sopenharmony_ci * large and has to be physically contiguous. So it's 6698c2ecf20Sopenharmony_ci * hard to be as defensive as we might like. */ 6708c2ecf20Sopenharmony_ci intel_pasid_tear_down_entry(iommu, dev, 6718c2ecf20Sopenharmony_ci svm->pasid, false); 6728c2ecf20Sopenharmony_ci intel_svm_drain_prq(dev, svm->pasid); 6738c2ecf20Sopenharmony_ci kfree_rcu(sdev, rcu); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (list_empty(&svm->devs)) { 6768c2ecf20Sopenharmony_ci ioasid_free(svm->pasid); 6778c2ecf20Sopenharmony_ci if (svm->mm) { 6788c2ecf20Sopenharmony_ci mmu_notifier_unregister(&svm->notifier, svm->mm); 6798c2ecf20Sopenharmony_ci /* Clear mm's pasid. */ 6808c2ecf20Sopenharmony_ci load_pasid(svm->mm, PASID_DISABLED); 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci list_del(&svm->list); 6838c2ecf20Sopenharmony_ci /* We mandate that no page faults may be outstanding 6848c2ecf20Sopenharmony_ci * for the PASID when intel_svm_unbind_mm() is called. 6858c2ecf20Sopenharmony_ci * If that is not obeyed, subtle errors will happen. 6868c2ecf20Sopenharmony_ci * Let's make them less subtle... */ 6878c2ecf20Sopenharmony_ci memset(svm, 0x6b, sizeof(*svm)); 6888c2ecf20Sopenharmony_ci kfree(svm); 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ciout: 6938c2ecf20Sopenharmony_ci return ret; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci/* Page request queue descriptor */ 6978c2ecf20Sopenharmony_cistruct page_req_dsc { 6988c2ecf20Sopenharmony_ci union { 6998c2ecf20Sopenharmony_ci struct { 7008c2ecf20Sopenharmony_ci u64 type:8; 7018c2ecf20Sopenharmony_ci u64 pasid_present:1; 7028c2ecf20Sopenharmony_ci u64 priv_data_present:1; 7038c2ecf20Sopenharmony_ci u64 rsvd:6; 7048c2ecf20Sopenharmony_ci u64 rid:16; 7058c2ecf20Sopenharmony_ci u64 pasid:20; 7068c2ecf20Sopenharmony_ci u64 exe_req:1; 7078c2ecf20Sopenharmony_ci u64 pm_req:1; 7088c2ecf20Sopenharmony_ci u64 rsvd2:10; 7098c2ecf20Sopenharmony_ci }; 7108c2ecf20Sopenharmony_ci u64 qw_0; 7118c2ecf20Sopenharmony_ci }; 7128c2ecf20Sopenharmony_ci union { 7138c2ecf20Sopenharmony_ci struct { 7148c2ecf20Sopenharmony_ci u64 rd_req:1; 7158c2ecf20Sopenharmony_ci u64 wr_req:1; 7168c2ecf20Sopenharmony_ci u64 lpig:1; 7178c2ecf20Sopenharmony_ci u64 prg_index:9; 7188c2ecf20Sopenharmony_ci u64 addr:52; 7198c2ecf20Sopenharmony_ci }; 7208c2ecf20Sopenharmony_ci u64 qw_1; 7218c2ecf20Sopenharmony_ci }; 7228c2ecf20Sopenharmony_ci u64 priv_data[2]; 7238c2ecf20Sopenharmony_ci}; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci#define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x20) 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci unsigned long requested = 0; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (req->exe_req) 7328c2ecf20Sopenharmony_ci requested |= VM_EXEC; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (req->rd_req) 7358c2ecf20Sopenharmony_ci requested |= VM_READ; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (req->wr_req) 7388c2ecf20Sopenharmony_ci requested |= VM_WRITE; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci return (requested & ~vma->vm_flags) != 0; 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic bool is_canonical_address(u64 addr) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci int shift = 64 - (__VIRTUAL_MASK_SHIFT + 1); 7468c2ecf20Sopenharmony_ci long saddr = (long) addr; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return (((saddr << shift) >> shift) == saddr); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci/** 7528c2ecf20Sopenharmony_ci * intel_svm_drain_prq - Drain page requests and responses for a pasid 7538c2ecf20Sopenharmony_ci * @dev: target device 7548c2ecf20Sopenharmony_ci * @pasid: pasid for draining 7558c2ecf20Sopenharmony_ci * 7568c2ecf20Sopenharmony_ci * Drain all pending page requests and responses related to @pasid in both 7578c2ecf20Sopenharmony_ci * software and hardware. This is supposed to be called after the device 7588c2ecf20Sopenharmony_ci * driver has stopped DMA, the pasid entry has been cleared, and both IOTLB 7598c2ecf20Sopenharmony_ci * and DevTLB have been invalidated. 7608c2ecf20Sopenharmony_ci * 7618c2ecf20Sopenharmony_ci * It waits until all pending page requests for @pasid in the page fault 7628c2ecf20Sopenharmony_ci * queue are completed by the prq handling thread. Then follow the steps 7638c2ecf20Sopenharmony_ci * described in VT-d spec CH7.10 to drain all page requests and page 7648c2ecf20Sopenharmony_ci * responses pending in the hardware. 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_cistatic void intel_svm_drain_prq(struct device *dev, u32 pasid) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct device_domain_info *info; 7698c2ecf20Sopenharmony_ci struct dmar_domain *domain; 7708c2ecf20Sopenharmony_ci struct intel_iommu *iommu; 7718c2ecf20Sopenharmony_ci struct qi_desc desc[3]; 7728c2ecf20Sopenharmony_ci struct pci_dev *pdev; 7738c2ecf20Sopenharmony_ci int head, tail; 7748c2ecf20Sopenharmony_ci u16 sid, did; 7758c2ecf20Sopenharmony_ci int qdep; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci info = get_domain_info(dev); 7788c2ecf20Sopenharmony_ci if (WARN_ON(!info || !dev_is_pci(dev))) 7798c2ecf20Sopenharmony_ci return; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (!info->pri_enabled) 7828c2ecf20Sopenharmony_ci return; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci iommu = info->iommu; 7858c2ecf20Sopenharmony_ci domain = info->domain; 7868c2ecf20Sopenharmony_ci pdev = to_pci_dev(dev); 7878c2ecf20Sopenharmony_ci sid = PCI_DEVID(info->bus, info->devfn); 7888c2ecf20Sopenharmony_ci did = domain->iommu_did[iommu->seq_id]; 7898c2ecf20Sopenharmony_ci qdep = pci_ats_queue_depth(pdev); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* 7928c2ecf20Sopenharmony_ci * Check and wait until all pending page requests in the queue are 7938c2ecf20Sopenharmony_ci * handled by the prq handling thread. 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_ciprq_retry: 7968c2ecf20Sopenharmony_ci reinit_completion(&iommu->prq_complete); 7978c2ecf20Sopenharmony_ci tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK; 7988c2ecf20Sopenharmony_ci head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK; 7998c2ecf20Sopenharmony_ci while (head != tail) { 8008c2ecf20Sopenharmony_ci struct page_req_dsc *req; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci req = &iommu->prq[head / sizeof(*req)]; 8038c2ecf20Sopenharmony_ci if (!req->pasid_present || req->pasid != pasid) { 8048c2ecf20Sopenharmony_ci head = (head + sizeof(*req)) & PRQ_RING_MASK; 8058c2ecf20Sopenharmony_ci continue; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci wait_for_completion(&iommu->prq_complete); 8098c2ecf20Sopenharmony_ci goto prq_retry; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* 8138c2ecf20Sopenharmony_ci * Perform steps described in VT-d spec CH7.10 to drain page 8148c2ecf20Sopenharmony_ci * requests and responses in hardware. 8158c2ecf20Sopenharmony_ci */ 8168c2ecf20Sopenharmony_ci memset(desc, 0, sizeof(desc)); 8178c2ecf20Sopenharmony_ci desc[0].qw0 = QI_IWD_STATUS_DATA(QI_DONE) | 8188c2ecf20Sopenharmony_ci QI_IWD_FENCE | 8198c2ecf20Sopenharmony_ci QI_IWD_TYPE; 8208c2ecf20Sopenharmony_ci desc[1].qw0 = QI_EIOTLB_PASID(pasid) | 8218c2ecf20Sopenharmony_ci QI_EIOTLB_DID(did) | 8228c2ecf20Sopenharmony_ci QI_EIOTLB_GRAN(QI_GRAN_NONG_PASID) | 8238c2ecf20Sopenharmony_ci QI_EIOTLB_TYPE; 8248c2ecf20Sopenharmony_ci desc[2].qw0 = QI_DEV_EIOTLB_PASID(pasid) | 8258c2ecf20Sopenharmony_ci QI_DEV_EIOTLB_SID(sid) | 8268c2ecf20Sopenharmony_ci QI_DEV_EIOTLB_QDEP(qdep) | 8278c2ecf20Sopenharmony_ci QI_DEIOTLB_TYPE | 8288c2ecf20Sopenharmony_ci QI_DEV_IOTLB_PFSID(info->pfsid); 8298c2ecf20Sopenharmony_ciqi_retry: 8308c2ecf20Sopenharmony_ci reinit_completion(&iommu->prq_complete); 8318c2ecf20Sopenharmony_ci qi_submit_sync(iommu, desc, 3, QI_OPT_WAIT_DRAIN); 8328c2ecf20Sopenharmony_ci if (readl(iommu->reg + DMAR_PRS_REG) & DMA_PRS_PRO) { 8338c2ecf20Sopenharmony_ci wait_for_completion(&iommu->prq_complete); 8348c2ecf20Sopenharmony_ci goto qi_retry; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic int prq_to_iommu_prot(struct page_req_dsc *req) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci int prot = 0; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (req->rd_req) 8438c2ecf20Sopenharmony_ci prot |= IOMMU_FAULT_PERM_READ; 8448c2ecf20Sopenharmony_ci if (req->wr_req) 8458c2ecf20Sopenharmony_ci prot |= IOMMU_FAULT_PERM_WRITE; 8468c2ecf20Sopenharmony_ci if (req->exe_req) 8478c2ecf20Sopenharmony_ci prot |= IOMMU_FAULT_PERM_EXEC; 8488c2ecf20Sopenharmony_ci if (req->pm_req) 8498c2ecf20Sopenharmony_ci prot |= IOMMU_FAULT_PERM_PRIV; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci return prot; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic int 8558c2ecf20Sopenharmony_ciintel_svm_prq_report(struct device *dev, struct page_req_dsc *desc) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci struct iommu_fault_event event; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (!dev || !dev_is_pci(dev)) 8608c2ecf20Sopenharmony_ci return -ENODEV; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* Fill in event data for device specific processing */ 8638c2ecf20Sopenharmony_ci memset(&event, 0, sizeof(struct iommu_fault_event)); 8648c2ecf20Sopenharmony_ci event.fault.type = IOMMU_FAULT_PAGE_REQ; 8658c2ecf20Sopenharmony_ci event.fault.prm.addr = (u64)desc->addr << VTD_PAGE_SHIFT; 8668c2ecf20Sopenharmony_ci event.fault.prm.pasid = desc->pasid; 8678c2ecf20Sopenharmony_ci event.fault.prm.grpid = desc->prg_index; 8688c2ecf20Sopenharmony_ci event.fault.prm.perm = prq_to_iommu_prot(desc); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (desc->lpig) 8718c2ecf20Sopenharmony_ci event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE; 8728c2ecf20Sopenharmony_ci if (desc->pasid_present) { 8738c2ecf20Sopenharmony_ci event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID; 8748c2ecf20Sopenharmony_ci event.fault.prm.flags |= IOMMU_FAULT_PAGE_RESPONSE_NEEDS_PASID; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci if (desc->priv_data_present) { 8778c2ecf20Sopenharmony_ci /* 8788c2ecf20Sopenharmony_ci * Set last page in group bit if private data is present, 8798c2ecf20Sopenharmony_ci * page response is required as it does for LPIG. 8808c2ecf20Sopenharmony_ci * iommu_report_device_fault() doesn't understand this vendor 8818c2ecf20Sopenharmony_ci * specific requirement thus we set last_page as a workaround. 8828c2ecf20Sopenharmony_ci */ 8838c2ecf20Sopenharmony_ci event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE; 8848c2ecf20Sopenharmony_ci event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PRIV_DATA; 8858c2ecf20Sopenharmony_ci memcpy(event.fault.prm.private_data, desc->priv_data, 8868c2ecf20Sopenharmony_ci sizeof(desc->priv_data)); 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci return iommu_report_device_fault(dev, &event); 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistatic irqreturn_t prq_event_thread(int irq, void *d) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev = NULL; 8958c2ecf20Sopenharmony_ci struct intel_iommu *iommu = d; 8968c2ecf20Sopenharmony_ci struct intel_svm *svm = NULL; 8978c2ecf20Sopenharmony_ci int head, tail, handled = 0; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci /* Clear PPR bit before reading head/tail registers, to 9008c2ecf20Sopenharmony_ci * ensure that we get a new interrupt if needed. */ 9018c2ecf20Sopenharmony_ci writel(DMA_PRS_PPR, iommu->reg + DMAR_PRS_REG); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK; 9048c2ecf20Sopenharmony_ci head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK; 9058c2ecf20Sopenharmony_ci while (head != tail) { 9068c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 9078c2ecf20Sopenharmony_ci struct page_req_dsc *req; 9088c2ecf20Sopenharmony_ci struct qi_desc resp; 9098c2ecf20Sopenharmony_ci int result; 9108c2ecf20Sopenharmony_ci vm_fault_t ret; 9118c2ecf20Sopenharmony_ci u64 address; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci handled = 1; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci req = &iommu->prq[head / sizeof(*req)]; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci result = QI_RESP_FAILURE; 9188c2ecf20Sopenharmony_ci address = (u64)req->addr << VTD_PAGE_SHIFT; 9198c2ecf20Sopenharmony_ci if (!req->pasid_present) { 9208c2ecf20Sopenharmony_ci pr_err("%s: Page request without PASID: %08llx %08llx\n", 9218c2ecf20Sopenharmony_ci iommu->name, ((unsigned long long *)req)[0], 9228c2ecf20Sopenharmony_ci ((unsigned long long *)req)[1]); 9238c2ecf20Sopenharmony_ci goto no_pasid; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci /* We shall not receive page request for supervisor SVM */ 9268c2ecf20Sopenharmony_ci if (req->pm_req && (req->rd_req | req->wr_req)) { 9278c2ecf20Sopenharmony_ci pr_err("Unexpected page request in Privilege Mode"); 9288c2ecf20Sopenharmony_ci /* No need to find the matching sdev as for bad_req */ 9298c2ecf20Sopenharmony_ci goto no_pasid; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci /* DMA read with exec requeset is not supported. */ 9328c2ecf20Sopenharmony_ci if (req->exe_req && req->rd_req) { 9338c2ecf20Sopenharmony_ci pr_err("Execution request not supported\n"); 9348c2ecf20Sopenharmony_ci goto no_pasid; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci if (!svm || svm->pasid != req->pasid) { 9378c2ecf20Sopenharmony_ci rcu_read_lock(); 9388c2ecf20Sopenharmony_ci svm = ioasid_find(NULL, req->pasid, NULL); 9398c2ecf20Sopenharmony_ci /* It *can't* go away, because the driver is not permitted 9408c2ecf20Sopenharmony_ci * to unbind the mm while any page faults are outstanding. 9418c2ecf20Sopenharmony_ci * So we only need RCU to protect the internal idr code. */ 9428c2ecf20Sopenharmony_ci rcu_read_unlock(); 9438c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(svm)) { 9448c2ecf20Sopenharmony_ci pr_err("%s: Page request for invalid PASID %d: %08llx %08llx\n", 9458c2ecf20Sopenharmony_ci iommu->name, req->pasid, ((unsigned long long *)req)[0], 9468c2ecf20Sopenharmony_ci ((unsigned long long *)req)[1]); 9478c2ecf20Sopenharmony_ci goto no_pasid; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (!sdev || sdev->sid != req->rid) { 9528c2ecf20Sopenharmony_ci struct intel_svm_dev *t; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci sdev = NULL; 9558c2ecf20Sopenharmony_ci rcu_read_lock(); 9568c2ecf20Sopenharmony_ci list_for_each_entry_rcu(t, &svm->devs, list) { 9578c2ecf20Sopenharmony_ci if (t->sid == req->rid) { 9588c2ecf20Sopenharmony_ci sdev = t; 9598c2ecf20Sopenharmony_ci break; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci rcu_read_unlock(); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci result = QI_RESP_INVALID; 9668c2ecf20Sopenharmony_ci /* Since we're using init_mm.pgd directly, we should never take 9678c2ecf20Sopenharmony_ci * any faults on kernel addresses. */ 9688c2ecf20Sopenharmony_ci if (!svm->mm) 9698c2ecf20Sopenharmony_ci goto bad_req; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* If address is not canonical, return invalid response */ 9728c2ecf20Sopenharmony_ci if (!is_canonical_address(address)) 9738c2ecf20Sopenharmony_ci goto bad_req; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* 9768c2ecf20Sopenharmony_ci * If prq is to be handled outside iommu driver via receiver of 9778c2ecf20Sopenharmony_ci * the fault notifiers, we skip the page response here. 9788c2ecf20Sopenharmony_ci */ 9798c2ecf20Sopenharmony_ci if (svm->flags & SVM_FLAG_GUEST_MODE) { 9808c2ecf20Sopenharmony_ci if (sdev && !intel_svm_prq_report(sdev->dev, req)) 9818c2ecf20Sopenharmony_ci goto prq_advance; 9828c2ecf20Sopenharmony_ci else 9838c2ecf20Sopenharmony_ci goto bad_req; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci /* If the mm is already defunct, don't handle faults. */ 9878c2ecf20Sopenharmony_ci if (!mmget_not_zero(svm->mm)) 9888c2ecf20Sopenharmony_ci goto bad_req; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci mmap_read_lock(svm->mm); 9918c2ecf20Sopenharmony_ci vma = find_extend_vma(svm->mm, address); 9928c2ecf20Sopenharmony_ci if (!vma || address < vma->vm_start) 9938c2ecf20Sopenharmony_ci goto invalid; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (access_error(vma, req)) 9968c2ecf20Sopenharmony_ci goto invalid; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci ret = handle_mm_fault(vma, address, 9998c2ecf20Sopenharmony_ci req->wr_req ? FAULT_FLAG_WRITE : 0, 10008c2ecf20Sopenharmony_ci NULL); 10018c2ecf20Sopenharmony_ci if (ret & VM_FAULT_ERROR) 10028c2ecf20Sopenharmony_ci goto invalid; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci result = QI_RESP_SUCCESS; 10058c2ecf20Sopenharmony_ciinvalid: 10068c2ecf20Sopenharmony_ci mmap_read_unlock(svm->mm); 10078c2ecf20Sopenharmony_ci mmput(svm->mm); 10088c2ecf20Sopenharmony_cibad_req: 10098c2ecf20Sopenharmony_ci WARN_ON(!sdev); 10108c2ecf20Sopenharmony_ci if (sdev && sdev->ops && sdev->ops->fault_cb) { 10118c2ecf20Sopenharmony_ci int rwxp = (req->rd_req << 3) | (req->wr_req << 2) | 10128c2ecf20Sopenharmony_ci (req->exe_req << 1) | (req->pm_req); 10138c2ecf20Sopenharmony_ci sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr, 10148c2ecf20Sopenharmony_ci req->priv_data, rwxp, result); 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci /* We get here in the error case where the PASID lookup failed, 10178c2ecf20Sopenharmony_ci and these can be NULL. Do not use them below this point! */ 10188c2ecf20Sopenharmony_ci sdev = NULL; 10198c2ecf20Sopenharmony_ci svm = NULL; 10208c2ecf20Sopenharmony_cino_pasid: 10218c2ecf20Sopenharmony_ci if (req->lpig || req->priv_data_present) { 10228c2ecf20Sopenharmony_ci /* 10238c2ecf20Sopenharmony_ci * Per VT-d spec. v3.0 ch7.7, system software must 10248c2ecf20Sopenharmony_ci * respond with page group response if private data 10258c2ecf20Sopenharmony_ci * is present (PDP) or last page in group (LPIG) bit 10268c2ecf20Sopenharmony_ci * is set. This is an additional VT-d feature beyond 10278c2ecf20Sopenharmony_ci * PCI ATS spec. 10288c2ecf20Sopenharmony_ci */ 10298c2ecf20Sopenharmony_ci resp.qw0 = QI_PGRP_PASID(req->pasid) | 10308c2ecf20Sopenharmony_ci QI_PGRP_DID(req->rid) | 10318c2ecf20Sopenharmony_ci QI_PGRP_PASID_P(req->pasid_present) | 10328c2ecf20Sopenharmony_ci QI_PGRP_PDP(req->priv_data_present) | 10338c2ecf20Sopenharmony_ci QI_PGRP_RESP_CODE(result) | 10348c2ecf20Sopenharmony_ci QI_PGRP_RESP_TYPE; 10358c2ecf20Sopenharmony_ci resp.qw1 = QI_PGRP_IDX(req->prg_index) | 10368c2ecf20Sopenharmony_ci QI_PGRP_LPIG(req->lpig); 10378c2ecf20Sopenharmony_ci resp.qw2 = 0; 10388c2ecf20Sopenharmony_ci resp.qw3 = 0; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (req->priv_data_present) 10418c2ecf20Sopenharmony_ci memcpy(&resp.qw2, req->priv_data, 10428c2ecf20Sopenharmony_ci sizeof(req->priv_data)); 10438c2ecf20Sopenharmony_ci qi_submit_sync(iommu, &resp, 1, 0); 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ciprq_advance: 10468c2ecf20Sopenharmony_ci head = (head + sizeof(*req)) & PRQ_RING_MASK; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci dmar_writeq(iommu->reg + DMAR_PQH_REG, tail); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* 10528c2ecf20Sopenharmony_ci * Clear the page request overflow bit and wake up all threads that 10538c2ecf20Sopenharmony_ci * are waiting for the completion of this handling. 10548c2ecf20Sopenharmony_ci */ 10558c2ecf20Sopenharmony_ci if (readl(iommu->reg + DMAR_PRS_REG) & DMA_PRS_PRO) { 10568c2ecf20Sopenharmony_ci pr_info_ratelimited("IOMMU: %s: PRQ overflow detected\n", 10578c2ecf20Sopenharmony_ci iommu->name); 10588c2ecf20Sopenharmony_ci head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK; 10598c2ecf20Sopenharmony_ci tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK; 10608c2ecf20Sopenharmony_ci if (head == tail) { 10618c2ecf20Sopenharmony_ci writel(DMA_PRS_PRO, iommu->reg + DMAR_PRS_REG); 10628c2ecf20Sopenharmony_ci pr_info_ratelimited("IOMMU: %s: PRQ overflow cleared", 10638c2ecf20Sopenharmony_ci iommu->name); 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (!completion_done(&iommu->prq_complete)) 10688c2ecf20Sopenharmony_ci complete(&iommu->prq_complete); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci#define to_intel_svm_dev(handle) container_of(handle, struct intel_svm_dev, sva) 10748c2ecf20Sopenharmony_cistruct iommu_sva * 10758c2ecf20Sopenharmony_ciintel_svm_bind(struct device *dev, struct mm_struct *mm, void *drvdata) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci struct iommu_sva *sva = ERR_PTR(-EINVAL); 10788c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev = NULL; 10798c2ecf20Sopenharmony_ci unsigned int flags = 0; 10808c2ecf20Sopenharmony_ci int ret; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* 10838c2ecf20Sopenharmony_ci * TODO: Consolidate with generic iommu-sva bind after it is merged. 10848c2ecf20Sopenharmony_ci * It will require shared SVM data structures, i.e. combine io_mm 10858c2ecf20Sopenharmony_ci * and intel_svm etc. 10868c2ecf20Sopenharmony_ci */ 10878c2ecf20Sopenharmony_ci if (drvdata) 10888c2ecf20Sopenharmony_ci flags = *(unsigned int *)drvdata; 10898c2ecf20Sopenharmony_ci mutex_lock(&pasid_mutex); 10908c2ecf20Sopenharmony_ci ret = intel_svm_bind_mm(dev, flags, NULL, mm, &sdev); 10918c2ecf20Sopenharmony_ci if (ret) 10928c2ecf20Sopenharmony_ci sva = ERR_PTR(ret); 10938c2ecf20Sopenharmony_ci else if (sdev) 10948c2ecf20Sopenharmony_ci sva = &sdev->sva; 10958c2ecf20Sopenharmony_ci else 10968c2ecf20Sopenharmony_ci WARN(!sdev, "SVM bind succeeded with no sdev!\n"); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci mutex_unlock(&pasid_mutex); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci return sva; 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_civoid intel_svm_unbind(struct iommu_sva *sva) 11048c2ecf20Sopenharmony_ci{ 11058c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci mutex_lock(&pasid_mutex); 11088c2ecf20Sopenharmony_ci sdev = to_intel_svm_dev(sva); 11098c2ecf20Sopenharmony_ci intel_svm_unbind_mm(sdev->dev, sdev->pasid); 11108c2ecf20Sopenharmony_ci mutex_unlock(&pasid_mutex); 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ciu32 intel_svm_get_pasid(struct iommu_sva *sva) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev; 11168c2ecf20Sopenharmony_ci u32 pasid; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci mutex_lock(&pasid_mutex); 11198c2ecf20Sopenharmony_ci sdev = to_intel_svm_dev(sva); 11208c2ecf20Sopenharmony_ci pasid = sdev->pasid; 11218c2ecf20Sopenharmony_ci mutex_unlock(&pasid_mutex); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci return pasid; 11248c2ecf20Sopenharmony_ci} 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ciint intel_svm_page_response(struct device *dev, 11278c2ecf20Sopenharmony_ci struct iommu_fault_event *evt, 11288c2ecf20Sopenharmony_ci struct iommu_page_response *msg) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci struct iommu_fault_page_request *prm; 11318c2ecf20Sopenharmony_ci struct intel_svm_dev *sdev = NULL; 11328c2ecf20Sopenharmony_ci struct intel_svm *svm = NULL; 11338c2ecf20Sopenharmony_ci struct intel_iommu *iommu; 11348c2ecf20Sopenharmony_ci bool private_present; 11358c2ecf20Sopenharmony_ci bool pasid_present; 11368c2ecf20Sopenharmony_ci bool last_page; 11378c2ecf20Sopenharmony_ci u8 bus, devfn; 11388c2ecf20Sopenharmony_ci int ret = 0; 11398c2ecf20Sopenharmony_ci u16 sid; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (!dev || !dev_is_pci(dev)) 11428c2ecf20Sopenharmony_ci return -ENODEV; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci iommu = device_to_iommu(dev, &bus, &devfn); 11458c2ecf20Sopenharmony_ci if (!iommu) 11468c2ecf20Sopenharmony_ci return -ENODEV; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (!msg || !evt) 11498c2ecf20Sopenharmony_ci return -EINVAL; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci mutex_lock(&pasid_mutex); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci prm = &evt->fault.prm; 11548c2ecf20Sopenharmony_ci sid = PCI_DEVID(bus, devfn); 11558c2ecf20Sopenharmony_ci pasid_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID; 11568c2ecf20Sopenharmony_ci private_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PRIV_DATA; 11578c2ecf20Sopenharmony_ci last_page = prm->flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci if (!pasid_present) { 11608c2ecf20Sopenharmony_ci ret = -EINVAL; 11618c2ecf20Sopenharmony_ci goto out; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci if (prm->pasid == 0 || prm->pasid >= PASID_MAX) { 11658c2ecf20Sopenharmony_ci ret = -EINVAL; 11668c2ecf20Sopenharmony_ci goto out; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci ret = pasid_to_svm_sdev(dev, prm->pasid, &svm, &sdev); 11708c2ecf20Sopenharmony_ci if (ret || !sdev) { 11718c2ecf20Sopenharmony_ci ret = -ENODEV; 11728c2ecf20Sopenharmony_ci goto out; 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci /* 11768c2ecf20Sopenharmony_ci * For responses from userspace, need to make sure that the 11778c2ecf20Sopenharmony_ci * pasid has been bound to its mm. 11788c2ecf20Sopenharmony_ci */ 11798c2ecf20Sopenharmony_ci if (svm->flags & SVM_FLAG_GUEST_MODE) { 11808c2ecf20Sopenharmony_ci struct mm_struct *mm; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci mm = get_task_mm(current); 11838c2ecf20Sopenharmony_ci if (!mm) { 11848c2ecf20Sopenharmony_ci ret = -EINVAL; 11858c2ecf20Sopenharmony_ci goto out; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (mm != svm->mm) { 11898c2ecf20Sopenharmony_ci ret = -ENODEV; 11908c2ecf20Sopenharmony_ci mmput(mm); 11918c2ecf20Sopenharmony_ci goto out; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci mmput(mm); 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* 11988c2ecf20Sopenharmony_ci * Per VT-d spec. v3.0 ch7.7, system software must respond 11998c2ecf20Sopenharmony_ci * with page group response if private data is present (PDP) 12008c2ecf20Sopenharmony_ci * or last page in group (LPIG) bit is set. This is an 12018c2ecf20Sopenharmony_ci * additional VT-d requirement beyond PCI ATS spec. 12028c2ecf20Sopenharmony_ci */ 12038c2ecf20Sopenharmony_ci if (last_page || private_present) { 12048c2ecf20Sopenharmony_ci struct qi_desc desc; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci desc.qw0 = QI_PGRP_PASID(prm->pasid) | QI_PGRP_DID(sid) | 12078c2ecf20Sopenharmony_ci QI_PGRP_PASID_P(pasid_present) | 12088c2ecf20Sopenharmony_ci QI_PGRP_PDP(private_present) | 12098c2ecf20Sopenharmony_ci QI_PGRP_RESP_CODE(msg->code) | 12108c2ecf20Sopenharmony_ci QI_PGRP_RESP_TYPE; 12118c2ecf20Sopenharmony_ci desc.qw1 = QI_PGRP_IDX(prm->grpid) | QI_PGRP_LPIG(last_page); 12128c2ecf20Sopenharmony_ci desc.qw2 = 0; 12138c2ecf20Sopenharmony_ci desc.qw3 = 0; 12148c2ecf20Sopenharmony_ci if (private_present) 12158c2ecf20Sopenharmony_ci memcpy(&desc.qw2, prm->private_data, 12168c2ecf20Sopenharmony_ci sizeof(prm->private_data)); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci qi_submit_sync(iommu, &desc, 1, 0); 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ciout: 12218c2ecf20Sopenharmony_ci mutex_unlock(&pasid_mutex); 12228c2ecf20Sopenharmony_ci return ret; 12238c2ecf20Sopenharmony_ci} 1224