Skip to content

Commit

Permalink
iommu/iommufd: Add IOMMU_[UN]MAP_DMA on IOASID
Browse files Browse the repository at this point in the history
[HACK. will fix in v2]

This patch introduces vfio type1v2-equivalent interface to userspace. Due
to aforementioned hack, iommufd currently calls exported vfio symbols to
handle map/unmap requests from the user.

Signed-off-by: Liu Yi L <yi.l.liu@intel.com>
  • Loading branch information
yiliu1765 committed Sep 19, 2021
1 parent 918796d commit 681ef45
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
104 changes: 104 additions & 0 deletions drivers/iommu/iommufd/iommufd.c
Expand Up @@ -55,6 +55,7 @@ struct iommufd_ioas {
struct mutex lock;
struct list_head device_list;
struct iommu_domain *domain;
struct vfio_iommu *vfio_iommu; /* FIXME: added for reusing vfio_iommu_type1 code */
};

/*
Expand Down Expand Up @@ -158,6 +159,7 @@ static void ioas_put_locked(struct iommufd_ioas *ioas)
return;

WARN_ON(!list_empty(&ioas->device_list));
vfio_iommu_type1_release(ioas->vfio_iommu); /* FIXME: reused vfio code */
xa_erase(&ictx->ioasid_xa, ioasid);
iommufd_ctx_put(ictx);
kfree(ioas);
Expand Down Expand Up @@ -185,6 +187,7 @@ static int iommufd_ioasid_alloc(struct iommufd_ctx *ictx, unsigned long arg)
struct iommufd_ioas *ioas;
unsigned long minsz;
int ioasid, ret;
struct vfio_iommu *vfio_iommu;

minsz = offsetofend(struct iommu_ioasid_alloc, addr_width);

Expand All @@ -211,6 +214,18 @@ static int iommufd_ioasid_alloc(struct iommufd_ctx *ictx, unsigned long arg)
return ret;
}

/* FIXME: get a vfio_iommu object for dma map/unmap management */
vfio_iommu = vfio_iommu_type1_open(VFIO_TYPE1v2_IOMMU);
if (IS_ERR(vfio_iommu)) {
pr_err_ratelimited("Failed to get vfio_iommu object\n");
mutex_lock(&ictx->lock);
xa_erase(&ictx->ioasid_xa, ioasid);
mutex_unlock(&ictx->lock);
kfree(ioas);
return PTR_ERR(vfio_iommu);
}
ioas->vfio_iommu = vfio_iommu;

ioas->ioasid = ioasid;

/* only supports kernel managed I/O page table so far */
Expand Down Expand Up @@ -383,6 +398,49 @@ static int iommufd_get_device_info(struct iommufd_ctx *ictx,
return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
}

static int iommufd_process_dma_op(struct iommufd_ctx *ictx,
unsigned long arg, bool map)
{
struct iommu_ioasid_dma_op dma;
unsigned long minsz;
struct iommufd_ioas *ioas = NULL;
int ret;

minsz = offsetofend(struct iommu_ioasid_dma_op, padding);

if (copy_from_user(&dma, (void __user *)arg, minsz))
return -EFAULT;

if (dma.argsz < minsz || dma.flags || dma.ioasid < 0)
return -EINVAL;

ioas = ioasid_get_ioas(ictx, dma.ioasid);
if (!ioas) {
pr_err_ratelimited("unkonwn IOASID %u\n", dma.ioasid);
return -EINVAL;
}

mutex_lock(&ioas->lock);

/*
* Needs to block map/unmap request from userspace before IOASID
* is attached to any device.
*/
if (list_empty(&ioas->device_list)) {
ret = -EINVAL;
goto out;
}

if (map)
ret = vfio_iommu_type1_map_dma(ioas->vfio_iommu, arg + minsz);
else
ret = vfio_iommu_type1_unmap_dma(ioas->vfio_iommu, arg + minsz);
out:
mutex_unlock(&ioas->lock);
ioas_put(ioas);
return ret;
};

static long iommufd_fops_unl_ioctl(struct file *filep,
unsigned int cmd, unsigned long arg)
{
Expand All @@ -409,6 +467,12 @@ static long iommufd_fops_unl_ioctl(struct file *filep,
case IOMMU_IOASID_FREE:
ret = iommufd_ioasid_free(ictx, arg);
break;
case IOMMU_MAP_DMA:
ret = iommufd_process_dma_op(ictx, arg, true);
break;
case IOMMU_UNMAP_DMA:
ret = iommufd_process_dma_op(ictx, arg, false);
break;
default:
pr_err_ratelimited("unsupported cmd %u\n", cmd);
break;
Expand Down Expand Up @@ -478,6 +542,39 @@ static int ioas_check_device_compatibility(struct iommufd_ioas *ioas,
return 0;
}

/* HACK:
* vfio_iommu_add/remove_device() is hacky implementation for
* this version to add the device/group to vfio iommu type1.
*/
static int vfio_iommu_add_device(struct vfio_iommu *vfio_iommu,
struct device *dev,
struct iommu_domain *domain)
{
struct iommu_group *group;
int ret;

group = iommu_group_get(dev);
if (!group)
return -EINVAL;

ret = vfio_iommu_add_group(vfio_iommu, group, domain);
iommu_group_put(group);
return ret;
}

static void vfio_iommu_remove_device(struct vfio_iommu *vfio_iommu,
struct device *dev)
{
struct iommu_group *group;

group = iommu_group_get(dev);
if (!group)
return;

vfio_iommu_remove_group(vfio_iommu, group);
iommu_group_put(group);
}

/**
* iommufd_device_attach_ioasid - attach device to an ioasid
* @idev: [in] Pointer to struct iommufd_device.
Expand Down Expand Up @@ -539,11 +636,17 @@ int iommufd_device_attach_ioasid(struct iommufd_device *idev, int ioasid)
if (ret)
goto out_domain;

ret = vfio_iommu_add_device(ioas->vfio_iommu, idev->dev, domain);
if (ret)
goto out_detach;

ioas_dev->idev = idev;
list_add(&ioas_dev->next, &ioas->device_list);
mutex_unlock(&ioas->lock);

return 0;
out_detach:
iommu_detach_device(domain, idev->dev);
out_domain:
ioas_free_domain_if_empty(ioas);
out_free:
Expand Down Expand Up @@ -579,6 +682,7 @@ void iommufd_device_detach_ioasid(struct iommufd_device *idev, int ioasid)
}

list_del(&ioas_dev->next);
vfio_iommu_remove_device(ioas->vfio_iommu, idev->dev);
iommu_detach_device(ioas->domain, idev->dev);
ioas_free_domain_if_empty(ioas);
kfree(ioas_dev);
Expand Down
29 changes: 29 additions & 0 deletions include/uapi/linux/iommu.h
Expand Up @@ -141,6 +141,35 @@ struct iommu_ioasid_alloc {

#define IOMMU_IOASID_FREE _IO(IOMMU_TYPE, IOMMU_BASE + 3)

/*
* Map/unmap process virtual addresses to I/O virtual addresses.
*
* Provide VFIO type1 equivalent semantics. Start with the same
* restriction e.g. the unmap size should match those used in the
* original mapping call.
*
* @argsz: user filled size of this data.
* @flags: reserved for future extension.
* @ioasid: the handle of target I/O address space.
* @data: the operation payload, refer to vfio_iommu_type1_dma_{un}map.
*
* FIXME:
* userspace needs to include uapi/vfio.h as well as interface reuses
* the map/unmap logic from vfio iommu type1.
*
* Return: 0 on success, -errno on failure.
*/
struct iommu_ioasid_dma_op {
__u32 argsz;
__u32 flags;
__s32 ioasid;
__u32 padding;
__u8 data[];
};

#define IOMMU_MAP_DMA _IO(IOMMU_TYPE, IOMMU_BASE + 4)
#define IOMMU_UNMAP_DMA _IO(IOMMU_TYPE, IOMMU_BASE + 5)

#define IOMMU_FAULT_PERM_READ (1 << 0) /* read */
#define IOMMU_FAULT_PERM_WRITE (1 << 1) /* write */
#define IOMMU_FAULT_PERM_EXEC (1 << 2) /* exec */
Expand Down

0 comments on commit 681ef45

Please sign in to comment.