Commit 942fd543 authored by Lu Baolu's avatar Lu Baolu Committed by Joerg Roedel

iommu: Remove SVM_FLAG_SUPERVISOR_MODE support

The current kernel DMA with PASID support is based on the SVA with a flag
SVM_FLAG_SUPERVISOR_MODE. The IOMMU driver binds the kernel memory address
space to a PASID of the device. The device driver programs the device with
kernel virtual address (KVA) for DMA access. There have been security and
functional issues with this approach:

- The lack of IOTLB synchronization upon kernel page table updates.
  (vmalloc, module/BPF loading, CONFIG_DEBUG_PAGEALLOC etc.)
- Other than slight more protection, using kernel virtual address (KVA)
  has little advantage over physical address. There are also no use
  cases yet where DMA engines need kernel virtual addresses for in-kernel
  DMA.

This removes SVM_FLAG_SUPERVISOR_MODE support from the IOMMU interface.
The device drivers are suggested to handle kernel DMA with PASID through
the kernel DMA APIs.

The drvdata parameter in iommu_sva_bind_device() and all callbacks is not
needed anymore. Cleanup them as well.

Link: https://lore.kernel.org/linux-iommu/20210511194726.GP1002214@nvidia.com/Signed-off-by: default avatarJacob Pan <jacob.jun.pan@linux.intel.com>
Signed-off-by: default avatarLu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Reviewed-by: default avatarJean-Philippe Brucker <jean-philippe@linaro.org>
Reviewed-by: default avatarKevin Tian <kevin.tian@intel.com>
Reviewed-by: default avatarFenghua Yu <fenghua.yu@intel.com>
Tested-by: default avatarZhangfei Gao <zhangfei.gao@linaro.org>
Tested-by: default avatarTony Zhu <tony.zhu@intel.com>
Link: https://lore.kernel.org/r/20221031005917.45690-4-baolu.lu@linux.intel.comSigned-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 22d2c7af
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/sched/task.h> #include <linux/sched/task.h>
#include <linux/intel-svm.h>
#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/fs.h> #include <linux/fs.h>
...@@ -100,7 +99,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) ...@@ -100,7 +99,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
filp->private_data = ctx; filp->private_data = ctx;
if (device_user_pasid_enabled(idxd)) { if (device_user_pasid_enabled(idxd)) {
sva = iommu_sva_bind_device(dev, current->mm, NULL); sva = iommu_sva_bind_device(dev, current->mm);
if (IS_ERR(sva)) { if (IS_ERR(sva)) {
rc = PTR_ERR(sva); rc = PTR_ERR(sva);
dev_err(dev, "pasid allocation failed: %d\n", rc); dev_err(dev, "pasid allocation failed: %d\n", rc);
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/intel-svm.h>
#include <linux/iommu.h> #include <linux/iommu.h>
#include <uapi/linux/idxd.h> #include <uapi/linux/idxd.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
...@@ -502,29 +501,7 @@ static struct idxd_device *idxd_alloc(struct pci_dev *pdev, struct idxd_driver_d ...@@ -502,29 +501,7 @@ static struct idxd_device *idxd_alloc(struct pci_dev *pdev, struct idxd_driver_d
static int idxd_enable_system_pasid(struct idxd_device *idxd) static int idxd_enable_system_pasid(struct idxd_device *idxd)
{ {
int flags; return -EOPNOTSUPP;
unsigned int pasid;
struct iommu_sva *sva;
flags = SVM_FLAG_SUPERVISOR_MODE;
sva = iommu_sva_bind_device(&idxd->pdev->dev, NULL, &flags);
if (IS_ERR(sva)) {
dev_warn(&idxd->pdev->dev,
"iommu sva bind failed: %ld\n", PTR_ERR(sva));
return PTR_ERR(sva);
}
pasid = iommu_sva_get_pasid(sva);
if (pasid == IOMMU_PASID_INVALID) {
iommu_sva_unbind_device(sva);
return -ENODEV;
}
idxd->sva = sva;
idxd->pasid = pasid;
dev_dbg(&idxd->pdev->dev, "system pasid: %u\n", pasid);
return 0;
} }
static void idxd_disable_system_pasid(struct idxd_device *idxd) static void idxd_disable_system_pasid(struct idxd_device *idxd)
......
...@@ -367,8 +367,7 @@ __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm) ...@@ -367,8 +367,7 @@ __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
return ERR_PTR(ret); return ERR_PTR(ret);
} }
struct iommu_sva * struct iommu_sva *arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm, void *drvdata)
{ {
struct iommu_sva *handle; struct iommu_sva *handle;
struct iommu_domain *domain = iommu_get_domain_for_dev(dev); struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
......
...@@ -754,8 +754,7 @@ bool arm_smmu_master_sva_enabled(struct arm_smmu_master *master); ...@@ -754,8 +754,7 @@ bool arm_smmu_master_sva_enabled(struct arm_smmu_master *master);
int arm_smmu_master_enable_sva(struct arm_smmu_master *master); int arm_smmu_master_enable_sva(struct arm_smmu_master *master);
int arm_smmu_master_disable_sva(struct arm_smmu_master *master); int arm_smmu_master_disable_sva(struct arm_smmu_master *master);
bool arm_smmu_master_iopf_supported(struct arm_smmu_master *master); bool arm_smmu_master_iopf_supported(struct arm_smmu_master *master);
struct iommu_sva *arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm, struct iommu_sva *arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm);
void *drvdata);
void arm_smmu_sva_unbind(struct iommu_sva *handle); void arm_smmu_sva_unbind(struct iommu_sva *handle);
u32 arm_smmu_sva_get_pasid(struct iommu_sva *handle); u32 arm_smmu_sva_get_pasid(struct iommu_sva *handle);
void arm_smmu_sva_notifier_synchronize(void); void arm_smmu_sva_notifier_synchronize(void);
...@@ -791,7 +790,7 @@ static inline bool arm_smmu_master_iopf_supported(struct arm_smmu_master *master ...@@ -791,7 +790,7 @@ static inline bool arm_smmu_master_iopf_supported(struct arm_smmu_master *master
} }
static inline struct iommu_sva * static inline struct iommu_sva *
arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm, void *drvdata) arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
{ {
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
} }
......
...@@ -748,8 +748,7 @@ struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn); ...@@ -748,8 +748,7 @@ struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn);
extern void intel_svm_check(struct intel_iommu *iommu); extern void intel_svm_check(struct intel_iommu *iommu);
extern int intel_svm_enable_prq(struct intel_iommu *iommu); extern int intel_svm_enable_prq(struct intel_iommu *iommu);
extern int intel_svm_finish_prq(struct intel_iommu *iommu); extern int intel_svm_finish_prq(struct intel_iommu *iommu);
struct iommu_sva *intel_svm_bind(struct device *dev, struct mm_struct *mm, struct iommu_sva *intel_svm_bind(struct device *dev, struct mm_struct *mm);
void *drvdata);
void intel_svm_unbind(struct iommu_sva *handle); void intel_svm_unbind(struct iommu_sva *handle);
u32 intel_svm_get_pasid(struct iommu_sva *handle); u32 intel_svm_get_pasid(struct iommu_sva *handle);
int intel_svm_page_response(struct device *dev, struct iommu_fault_event *evt, int intel_svm_page_response(struct device *dev, struct iommu_fault_event *evt,
......
...@@ -296,8 +296,7 @@ static int pasid_to_svm_sdev(struct device *dev, unsigned int pasid, ...@@ -296,8 +296,7 @@ static int pasid_to_svm_sdev(struct device *dev, unsigned int pasid,
return 0; return 0;
} }
static int intel_svm_alloc_pasid(struct device *dev, struct mm_struct *mm, static int intel_svm_alloc_pasid(struct device *dev, struct mm_struct *mm)
unsigned int flags)
{ {
ioasid_t max_pasid = dev_is_pci(dev) ? ioasid_t max_pasid = dev_is_pci(dev) ?
pci_max_pasids(to_pci_dev(dev)) : intel_pasid_max_id; pci_max_pasids(to_pci_dev(dev)) : intel_pasid_max_id;
...@@ -307,8 +306,7 @@ static int intel_svm_alloc_pasid(struct device *dev, struct mm_struct *mm, ...@@ -307,8 +306,7 @@ static int intel_svm_alloc_pasid(struct device *dev, struct mm_struct *mm,
static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu, static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu,
struct device *dev, struct device *dev,
struct mm_struct *mm, struct mm_struct *mm)
unsigned int flags)
{ {
struct device_domain_info *info = dev_iommu_priv_get(dev); struct device_domain_info *info = dev_iommu_priv_get(dev);
struct intel_svm_dev *sdev; struct intel_svm_dev *sdev;
...@@ -324,22 +322,18 @@ static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu, ...@@ -324,22 +322,18 @@ static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu,
svm->pasid = mm->pasid; svm->pasid = mm->pasid;
svm->mm = mm; svm->mm = mm;
svm->flags = flags;
INIT_LIST_HEAD_RCU(&svm->devs); INIT_LIST_HEAD_RCU(&svm->devs);
if (!(flags & SVM_FLAG_SUPERVISOR_MODE)) { svm->notifier.ops = &intel_mmuops;
svm->notifier.ops = &intel_mmuops; ret = mmu_notifier_register(&svm->notifier, mm);
ret = mmu_notifier_register(&svm->notifier, mm); if (ret) {
if (ret) { kfree(svm);
kfree(svm); return ERR_PTR(ret);
return ERR_PTR(ret);
}
} }
ret = pasid_private_add(svm->pasid, svm); ret = pasid_private_add(svm->pasid, svm);
if (ret) { if (ret) {
if (svm->notifier.ops) mmu_notifier_unregister(&svm->notifier, mm);
mmu_notifier_unregister(&svm->notifier, mm);
kfree(svm); kfree(svm);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
...@@ -374,9 +368,7 @@ static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu, ...@@ -374,9 +368,7 @@ static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu,
} }
/* Setup the pasid table: */ /* Setup the pasid table: */
sflags = (flags & SVM_FLAG_SUPERVISOR_MODE) ? sflags = cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0;
PASID_FLAG_SUPERVISOR_MODE : 0;
sflags |= cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0;
ret = intel_pasid_setup_first_level(iommu, dev, mm->pgd, mm->pasid, ret = intel_pasid_setup_first_level(iommu, dev, mm->pgd, mm->pasid,
FLPT_DEFAULT_DID, sflags); FLPT_DEFAULT_DID, sflags);
if (ret) if (ret)
...@@ -390,8 +382,7 @@ static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu, ...@@ -390,8 +382,7 @@ static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu,
kfree(sdev); kfree(sdev);
free_svm: free_svm:
if (list_empty(&svm->devs)) { if (list_empty(&svm->devs)) {
if (svm->notifier.ops) mmu_notifier_unregister(&svm->notifier, mm);
mmu_notifier_unregister(&svm->notifier, mm);
pasid_private_remove(mm->pasid); pasid_private_remove(mm->pasid);
kfree(svm); kfree(svm);
} }
...@@ -780,40 +771,20 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -780,40 +771,20 @@ static irqreturn_t prq_event_thread(int irq, void *d)
return IRQ_RETVAL(handled); return IRQ_RETVAL(handled);
} }
struct iommu_sva *intel_svm_bind(struct device *dev, struct mm_struct *mm, void *drvdata) struct iommu_sva *intel_svm_bind(struct device *dev, struct mm_struct *mm)
{ {
struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL); struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL);
unsigned int flags = 0;
struct iommu_sva *sva; struct iommu_sva *sva;
int ret; int ret;
if (drvdata)
flags = *(unsigned int *)drvdata;
if (flags & SVM_FLAG_SUPERVISOR_MODE) {
if (!ecap_srs(iommu->ecap)) {
dev_err(dev, "%s: Supervisor PASID not supported\n",
iommu->name);
return ERR_PTR(-EOPNOTSUPP);
}
if (mm) {
dev_err(dev, "%s: Supervisor PASID with user provided mm\n",
iommu->name);
return ERR_PTR(-EINVAL);
}
mm = &init_mm;
}
mutex_lock(&pasid_mutex); mutex_lock(&pasid_mutex);
ret = intel_svm_alloc_pasid(dev, mm, flags); ret = intel_svm_alloc_pasid(dev, mm);
if (ret) { if (ret) {
mutex_unlock(&pasid_mutex); mutex_unlock(&pasid_mutex);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
sva = intel_svm_bind_mm(iommu, dev, mm, flags); sva = intel_svm_bind_mm(iommu, dev, mm);
mutex_unlock(&pasid_mutex); mutex_unlock(&pasid_mutex);
return sva; return sva;
......
...@@ -2750,7 +2750,6 @@ EXPORT_SYMBOL_GPL(iommu_dev_disable_feature); ...@@ -2750,7 +2750,6 @@ EXPORT_SYMBOL_GPL(iommu_dev_disable_feature);
* iommu_sva_bind_device() - Bind a process address space to a device * iommu_sva_bind_device() - Bind a process address space to a device
* @dev: the device * @dev: the device
* @mm: the mm to bind, caller must hold a reference to it * @mm: the mm to bind, caller must hold a reference to it
* @drvdata: opaque data pointer to pass to bind callback
* *
* Create a bond between device and address space, allowing the device to access * Create a bond between device and address space, allowing the device to access
* the mm using the returned PASID. If a bond already exists between @device and * the mm using the returned PASID. If a bond already exists between @device and
...@@ -2763,7 +2762,7 @@ EXPORT_SYMBOL_GPL(iommu_dev_disable_feature); ...@@ -2763,7 +2762,7 @@ EXPORT_SYMBOL_GPL(iommu_dev_disable_feature);
* On error, returns an ERR_PTR value. * On error, returns an ERR_PTR value.
*/ */
struct iommu_sva * struct iommu_sva *
iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, void *drvdata) iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
{ {
struct iommu_group *group; struct iommu_group *group;
struct iommu_sva *handle = ERR_PTR(-EINVAL); struct iommu_sva *handle = ERR_PTR(-EINVAL);
...@@ -2788,7 +2787,7 @@ iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, void *drvdata) ...@@ -2788,7 +2787,7 @@ iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, void *drvdata)
if (iommu_group_device_count(group) != 1) if (iommu_group_device_count(group) != 1)
goto out_unlock; goto out_unlock;
handle = ops->sva_bind(dev, mm, drvdata); handle = ops->sva_bind(dev, mm);
out_unlock: out_unlock:
mutex_unlock(&group->mutex); mutex_unlock(&group->mutex);
......
...@@ -108,7 +108,7 @@ static int uacce_bind_queue(struct uacce_device *uacce, struct uacce_queue *q) ...@@ -108,7 +108,7 @@ static int uacce_bind_queue(struct uacce_device *uacce, struct uacce_queue *q)
if (!(uacce->flags & UACCE_DEV_SVA)) if (!(uacce->flags & UACCE_DEV_SVA))
return 0; return 0;
handle = iommu_sva_bind_device(uacce->parent, current->mm, NULL); handle = iommu_sva_bind_device(uacce->parent, current->mm);
if (IS_ERR(handle)) if (IS_ERR(handle))
return PTR_ERR(handle); return PTR_ERR(handle);
......
...@@ -13,17 +13,4 @@ ...@@ -13,17 +13,4 @@
#define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x20) #define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x20)
#define PRQ_DEPTH ((0x1000 << PRQ_ORDER) >> 5) #define PRQ_DEPTH ((0x1000 << PRQ_ORDER) >> 5)
/*
* The SVM_FLAG_SUPERVISOR_MODE flag requests a PASID which can be used only
* for access to kernel addresses. No IOTLB flushes are automatically done
* for kernel mappings; it is valid only for access to the kernel's static
* 1:1 mapping of physical memory — not to vmalloc or even module mappings.
* A future API addition may permit the use of such ranges, by means of an
* explicit IOTLB flush call (akin to the DMA API's unmap method).
*
* It is unlikely that we will ever hook into flush_tlb_kernel_range() to
* do such IOTLB flushes automatically.
*/
#define SVM_FLAG_SUPERVISOR_MODE BIT(0)
#endif /* __INTEL_SVM_H__ */ #endif /* __INTEL_SVM_H__ */
...@@ -247,8 +247,7 @@ struct iommu_ops { ...@@ -247,8 +247,7 @@ struct iommu_ops {
int (*dev_enable_feat)(struct device *dev, enum iommu_dev_features f); int (*dev_enable_feat)(struct device *dev, enum iommu_dev_features f);
int (*dev_disable_feat)(struct device *dev, enum iommu_dev_features f); int (*dev_disable_feat)(struct device *dev, enum iommu_dev_features f);
struct iommu_sva *(*sva_bind)(struct device *dev, struct mm_struct *mm, struct iommu_sva *(*sva_bind)(struct device *dev, struct mm_struct *mm);
void *drvdata);
void (*sva_unbind)(struct iommu_sva *handle); void (*sva_unbind)(struct iommu_sva *handle);
u32 (*sva_get_pasid)(struct iommu_sva *handle); u32 (*sva_get_pasid)(struct iommu_sva *handle);
...@@ -668,8 +667,7 @@ int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features f); ...@@ -668,8 +667,7 @@ int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features f);
int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features f); int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features f);
struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct iommu_sva *iommu_sva_bind_device(struct device *dev,
struct mm_struct *mm, struct mm_struct *mm);
void *drvdata);
void iommu_sva_unbind_device(struct iommu_sva *handle); void iommu_sva_unbind_device(struct iommu_sva *handle);
u32 iommu_sva_get_pasid(struct iommu_sva *handle); u32 iommu_sva_get_pasid(struct iommu_sva *handle);
...@@ -1000,7 +998,7 @@ iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features feat) ...@@ -1000,7 +998,7 @@ iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features feat)
} }
static inline struct iommu_sva * static inline struct iommu_sva *
iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, void *drvdata) iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
{ {
return NULL; return NULL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment