123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- #include <linux/mutex.h>
- #include <linux/sched/mm.h>
- #include <linux/iommu.h>
- #include "iommu-sva.h"
- static DEFINE_MUTEX(iommu_sva_lock);
- static DECLARE_IOASID_SET(iommu_sva_pasid);
- int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max)
- {
- int ret = 0;
- ioasid_t pasid;
- if (min == INVALID_IOASID || max == INVALID_IOASID ||
- min == 0 || max < min)
- return -EINVAL;
- mutex_lock(&iommu_sva_lock);
-
- if (pasid_valid(mm->pasid)) {
- if (mm->pasid < min || mm->pasid >= max)
- ret = -EOVERFLOW;
- goto out;
- }
- pasid = ioasid_alloc(&iommu_sva_pasid, min, max, mm);
- if (!pasid_valid(pasid))
- ret = -ENOMEM;
- else
- mm_pasid_set(mm, pasid);
- out:
- mutex_unlock(&iommu_sva_lock);
- return ret;
- }
- EXPORT_SYMBOL_GPL(iommu_sva_alloc_pasid);
- static bool __mmget_not_zero(void *mm)
- {
- return mmget_not_zero(mm);
- }
- struct mm_struct *iommu_sva_find(ioasid_t pasid)
- {
- return ioasid_find(&iommu_sva_pasid, pasid, __mmget_not_zero);
- }
- EXPORT_SYMBOL_GPL(iommu_sva_find);
- struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
- {
- struct iommu_domain *domain;
- struct iommu_sva *handle;
- ioasid_t max_pasids;
- int ret;
- max_pasids = dev->iommu->max_pasids;
- if (!max_pasids)
- return ERR_PTR(-EOPNOTSUPP);
-
- ret = iommu_sva_alloc_pasid(mm, 1, max_pasids - 1);
- if (ret)
- return ERR_PTR(ret);
- handle = kzalloc(sizeof(*handle), GFP_KERNEL);
- if (!handle)
- return ERR_PTR(-ENOMEM);
- mutex_lock(&iommu_sva_lock);
-
- domain = iommu_get_domain_for_dev_pasid(dev, mm->pasid,
- IOMMU_DOMAIN_SVA);
- if (IS_ERR(domain)) {
- ret = PTR_ERR(domain);
- goto out_unlock;
- }
- if (domain) {
- domain->users++;
- goto out;
- }
-
- domain = iommu_sva_domain_alloc(dev, mm);
- if (!domain) {
- ret = -ENOMEM;
- goto out_unlock;
- }
- ret = iommu_attach_device_pasid(domain, dev, mm->pasid);
- if (ret)
- goto out_free_domain;
- domain->users = 1;
- out:
- mutex_unlock(&iommu_sva_lock);
- handle->dev = dev;
- handle->domain = domain;
- return handle;
- out_free_domain:
- iommu_domain_free(domain);
- out_unlock:
- mutex_unlock(&iommu_sva_lock);
- kfree(handle);
- return ERR_PTR(ret);
- }
- EXPORT_SYMBOL_GPL(iommu_sva_bind_device);
- void iommu_sva_unbind_device(struct iommu_sva *handle)
- {
- struct iommu_domain *domain = handle->domain;
- ioasid_t pasid = domain->mm->pasid;
- struct device *dev = handle->dev;
- mutex_lock(&iommu_sva_lock);
- if (--domain->users == 0) {
- iommu_detach_device_pasid(domain, dev, pasid);
- iommu_domain_free(domain);
- }
- mutex_unlock(&iommu_sva_lock);
- kfree(handle);
- }
- EXPORT_SYMBOL_GPL(iommu_sva_unbind_device);
- u32 iommu_sva_get_pasid(struct iommu_sva *handle)
- {
- struct iommu_domain *domain = handle->domain;
- return domain->mm->pasid;
- }
- EXPORT_SYMBOL_GPL(iommu_sva_get_pasid);
- enum iommu_page_response_code
- iommu_sva_handle_iopf(struct iommu_fault *fault, void *data)
- {
- vm_fault_t ret;
- struct vm_area_struct *vma;
- struct mm_struct *mm = data;
- unsigned int access_flags = 0;
- unsigned int fault_flags = FAULT_FLAG_REMOTE;
- struct iommu_fault_page_request *prm = &fault->prm;
- enum iommu_page_response_code status = IOMMU_PAGE_RESP_INVALID;
- if (!(prm->flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID))
- return status;
- if (!mmget_not_zero(mm))
- return status;
- mmap_read_lock(mm);
- vma = vma_lookup(mm, prm->addr);
- if (!vma)
-
- goto out_put_mm;
- if (prm->perm & IOMMU_FAULT_PERM_READ)
- access_flags |= VM_READ;
- if (prm->perm & IOMMU_FAULT_PERM_WRITE) {
- access_flags |= VM_WRITE;
- fault_flags |= FAULT_FLAG_WRITE;
- }
- if (prm->perm & IOMMU_FAULT_PERM_EXEC) {
- access_flags |= VM_EXEC;
- fault_flags |= FAULT_FLAG_INSTRUCTION;
- }
- if (!(prm->perm & IOMMU_FAULT_PERM_PRIV))
- fault_flags |= FAULT_FLAG_USER;
- if (access_flags & ~vma->vm_flags)
-
- goto out_put_mm;
- ret = handle_mm_fault(vma, prm->addr, fault_flags, NULL);
- status = ret & VM_FAULT_ERROR ? IOMMU_PAGE_RESP_INVALID :
- IOMMU_PAGE_RESP_SUCCESS;
- out_put_mm:
- mmap_read_unlock(mm);
- mmput(mm);
- return status;
- }
|