xref: /linux/drivers/vfio/iommufd.c (revision 0074281bb6316108e0cff094bd4db78ab3eee236)
1a4d1f91dSJason Gunthorpe // SPDX-License-Identifier: GPL-2.0-only
2a4d1f91dSJason Gunthorpe /*
3a4d1f91dSJason Gunthorpe  * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES
4a4d1f91dSJason Gunthorpe  */
5a4d1f91dSJason Gunthorpe #include <linux/vfio.h>
6a4d1f91dSJason Gunthorpe #include <linux/iommufd.h>
7a4d1f91dSJason Gunthorpe 
8a4d1f91dSJason Gunthorpe #include "vfio.h"
9a4d1f91dSJason Gunthorpe 
10cdd30ebbSPeter Zijlstra MODULE_IMPORT_NS("IOMMUFD");
11cdd30ebbSPeter Zijlstra MODULE_IMPORT_NS("IOMMUFD_VFIO");
12a4d1f91dSJason Gunthorpe 
vfio_iommufd_device_has_compat_ioas(struct vfio_device * vdev,struct iommufd_ctx * ictx)136086efe7SYi Liu bool vfio_iommufd_device_has_compat_ioas(struct vfio_device *vdev,
146086efe7SYi Liu 					 struct iommufd_ctx *ictx)
156086efe7SYi Liu {
166086efe7SYi Liu 	u32 ioas_id;
176086efe7SYi Liu 
186086efe7SYi Liu 	return !iommufd_vfio_compat_ioas_get_id(ictx, &ioas_id);
196086efe7SYi Liu }
206086efe7SYi Liu 
vfio_df_iommufd_bind(struct vfio_device_file * df)2131014aefSYi Liu int vfio_df_iommufd_bind(struct vfio_device_file *df)
22a4d1f91dSJason Gunthorpe {
2331014aefSYi Liu 	struct vfio_device *vdev = df->device;
2431014aefSYi Liu 	struct iommufd_ctx *ictx = df->iommufd;
256f240ee6SYi Liu 
266f240ee6SYi Liu 	lockdep_assert_held(&vdev->dev_set->lock);
276f240ee6SYi Liu 
28*b25e271bSJacob Pan 	/* Returns 0 to permit device opening under noiommu mode */
29*b25e271bSJacob Pan 	if (vfio_device_is_noiommu(vdev))
30*b25e271bSJacob Pan 		return 0;
31*b25e271bSJacob Pan 
3231014aefSYi Liu 	return vdev->ops->bind_iommufd(vdev, ictx, &df->devid);
336f240ee6SYi Liu }
346f240ee6SYi Liu 
vfio_iommufd_compat_attach_ioas(struct vfio_device * vdev,struct iommufd_ctx * ictx)356f240ee6SYi Liu int vfio_iommufd_compat_attach_ioas(struct vfio_device *vdev,
366f240ee6SYi Liu 				    struct iommufd_ctx *ictx)
376f240ee6SYi Liu {
386f240ee6SYi Liu 	u32 ioas_id;
39a4d1f91dSJason Gunthorpe 	int ret;
40a4d1f91dSJason Gunthorpe 
41a4d1f91dSJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
42a4d1f91dSJason Gunthorpe 
436f240ee6SYi Liu 	/* compat noiommu does not need to do ioas attach */
446f240ee6SYi Liu 	if (vfio_device_is_noiommu(vdev))
456f240ee6SYi Liu 		return 0;
46a4d1f91dSJason Gunthorpe 
47c9a397ceSJason Gunthorpe 	ret = iommufd_vfio_compat_ioas_get_id(ictx, &ioas_id);
48a4d1f91dSJason Gunthorpe 	if (ret)
49a4d1f91dSJason Gunthorpe 		return ret;
506f240ee6SYi Liu 
516f240ee6SYi Liu 	/* The legacy path has no way to return the selected pt_id */
526f240ee6SYi Liu 	return vdev->ops->attach_ioas(vdev, &ioas_id);
53a4d1f91dSJason Gunthorpe }
54a4d1f91dSJason Gunthorpe 
vfio_df_iommufd_unbind(struct vfio_device_file * df)5531014aefSYi Liu void vfio_df_iommufd_unbind(struct vfio_device_file *df)
56a4d1f91dSJason Gunthorpe {
5731014aefSYi Liu 	struct vfio_device *vdev = df->device;
5831014aefSYi Liu 
59a4d1f91dSJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
60a4d1f91dSJason Gunthorpe 
61c9a397ceSJason Gunthorpe 	if (vfio_device_is_noiommu(vdev))
62c9a397ceSJason Gunthorpe 		return;
63c9a397ceSJason Gunthorpe 
64a4d1f91dSJason Gunthorpe 	if (vdev->ops->unbind_iommufd)
65a4d1f91dSJason Gunthorpe 		vdev->ops->unbind_iommufd(vdev);
66a4d1f91dSJason Gunthorpe }
67a4d1f91dSJason Gunthorpe 
vfio_iommufd_device_ictx(struct vfio_device * vdev)689062ff40SYi Liu struct iommufd_ctx *vfio_iommufd_device_ictx(struct vfio_device *vdev)
699062ff40SYi Liu {
709062ff40SYi Liu 	if (vdev->iommufd_device)
719062ff40SYi Liu 		return iommufd_device_to_ictx(vdev->iommufd_device);
729062ff40SYi Liu 	return NULL;
739062ff40SYi Liu }
749062ff40SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_device_ictx);
759062ff40SYi Liu 
vfio_iommufd_device_id(struct vfio_device * vdev)769062ff40SYi Liu static int vfio_iommufd_device_id(struct vfio_device *vdev)
779062ff40SYi Liu {
789062ff40SYi Liu 	if (vdev->iommufd_device)
799062ff40SYi Liu 		return iommufd_device_to_id(vdev->iommufd_device);
809062ff40SYi Liu 	return -EINVAL;
819062ff40SYi Liu }
829062ff40SYi Liu 
839062ff40SYi Liu /*
849062ff40SYi Liu  * Return devid for a device.
859062ff40SYi Liu  *  valid ID for the device that is owned by the ictx
869062ff40SYi Liu  *  -ENOENT = device is owned but there is no ID
879062ff40SYi Liu  *  -ENODEV or other error = device is not owned
889062ff40SYi Liu  */
vfio_iommufd_get_dev_id(struct vfio_device * vdev,struct iommufd_ctx * ictx)899062ff40SYi Liu int vfio_iommufd_get_dev_id(struct vfio_device *vdev, struct iommufd_ctx *ictx)
909062ff40SYi Liu {
919062ff40SYi Liu 	struct iommu_group *group;
929062ff40SYi Liu 	int devid;
939062ff40SYi Liu 
949062ff40SYi Liu 	if (vfio_iommufd_device_ictx(vdev) == ictx)
959062ff40SYi Liu 		return vfio_iommufd_device_id(vdev);
969062ff40SYi Liu 
979062ff40SYi Liu 	group = iommu_group_get(vdev->dev);
989062ff40SYi Liu 	if (!group)
999062ff40SYi Liu 		return -ENODEV;
1009062ff40SYi Liu 
1019062ff40SYi Liu 	if (iommufd_ctx_has_group(ictx, group))
1029062ff40SYi Liu 		devid = -ENOENT;
1039062ff40SYi Liu 	else
1049062ff40SYi Liu 		devid = -ENODEV;
1059062ff40SYi Liu 
1069062ff40SYi Liu 	iommu_group_put(group);
1079062ff40SYi Liu 
1089062ff40SYi Liu 	return devid;
1099062ff40SYi Liu }
1109062ff40SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_get_dev_id);
1119062ff40SYi Liu 
112a4d1f91dSJason Gunthorpe /*
113a4d1f91dSJason Gunthorpe  * The physical standard ops mean that the iommufd_device is bound to the
114a4d1f91dSJason Gunthorpe  * physical device vdev->dev that was provided to vfio_init_group_dev(). Drivers
115a4d1f91dSJason Gunthorpe  * using this ops set should call vfio_register_group_dev()
116a4d1f91dSJason Gunthorpe  */
vfio_iommufd_physical_bind(struct vfio_device * vdev,struct iommufd_ctx * ictx,u32 * out_device_id)117a4d1f91dSJason Gunthorpe int vfio_iommufd_physical_bind(struct vfio_device *vdev,
118a4d1f91dSJason Gunthorpe 			       struct iommufd_ctx *ictx, u32 *out_device_id)
119a4d1f91dSJason Gunthorpe {
120a4d1f91dSJason Gunthorpe 	struct iommufd_device *idev;
121a4d1f91dSJason Gunthorpe 
122a4d1f91dSJason Gunthorpe 	idev = iommufd_device_bind(ictx, vdev->dev, out_device_id);
123a4d1f91dSJason Gunthorpe 	if (IS_ERR(idev))
124a4d1f91dSJason Gunthorpe 		return PTR_ERR(idev);
125a4d1f91dSJason Gunthorpe 	vdev->iommufd_device = idev;
12629064134SYi Liu 	ida_init(&vdev->pasids);
127a4d1f91dSJason Gunthorpe 	return 0;
128a4d1f91dSJason Gunthorpe }
129a4d1f91dSJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_physical_bind);
130a4d1f91dSJason Gunthorpe 
vfio_iommufd_physical_unbind(struct vfio_device * vdev)131a4d1f91dSJason Gunthorpe void vfio_iommufd_physical_unbind(struct vfio_device *vdev)
132a4d1f91dSJason Gunthorpe {
13329064134SYi Liu 	int pasid;
13429064134SYi Liu 
135a4d1f91dSJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
136a4d1f91dSJason Gunthorpe 
13729064134SYi Liu 	while ((pasid = ida_find_first(&vdev->pasids)) >= 0) {
13829064134SYi Liu 		iommufd_device_detach(vdev->iommufd_device, pasid);
13929064134SYi Liu 		ida_free(&vdev->pasids, pasid);
14029064134SYi Liu 	}
14129064134SYi Liu 
142a4d1f91dSJason Gunthorpe 	if (vdev->iommufd_attached) {
1432fb69c60SYi Liu 		iommufd_device_detach(vdev->iommufd_device, IOMMU_NO_PASID);
144a4d1f91dSJason Gunthorpe 		vdev->iommufd_attached = false;
145a4d1f91dSJason Gunthorpe 	}
146a4d1f91dSJason Gunthorpe 	iommufd_device_unbind(vdev->iommufd_device);
147a4d1f91dSJason Gunthorpe 	vdev->iommufd_device = NULL;
148a4d1f91dSJason Gunthorpe }
149a4d1f91dSJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_physical_unbind);
150a4d1f91dSJason Gunthorpe 
vfio_iommufd_physical_attach_ioas(struct vfio_device * vdev,u32 * pt_id)151a4d1f91dSJason Gunthorpe int vfio_iommufd_physical_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
152a4d1f91dSJason Gunthorpe {
153a4d1f91dSJason Gunthorpe 	int rc;
154a4d1f91dSJason Gunthorpe 
1559048c734SYi Liu 	lockdep_assert_held(&vdev->dev_set->lock);
1569048c734SYi Liu 
1579048c734SYi Liu 	if (WARN_ON(!vdev->iommufd_device))
1589048c734SYi Liu 		return -EINVAL;
1599048c734SYi Liu 
1609048c734SYi Liu 	if (vdev->iommufd_attached)
1612fb69c60SYi Liu 		rc = iommufd_device_replace(vdev->iommufd_device,
1622fb69c60SYi Liu 					    IOMMU_NO_PASID, pt_id);
163c157fd88SNicolin Chen 	else
1642fb69c60SYi Liu 		rc = iommufd_device_attach(vdev->iommufd_device,
1652fb69c60SYi Liu 					   IOMMU_NO_PASID, pt_id);
166a4d1f91dSJason Gunthorpe 	if (rc)
167a4d1f91dSJason Gunthorpe 		return rc;
168a4d1f91dSJason Gunthorpe 	vdev->iommufd_attached = true;
169a4d1f91dSJason Gunthorpe 	return 0;
170a4d1f91dSJason Gunthorpe }
171a4d1f91dSJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_physical_attach_ioas);
1724741f2e9SJason Gunthorpe 
vfio_iommufd_physical_detach_ioas(struct vfio_device * vdev)1739048c734SYi Liu void vfio_iommufd_physical_detach_ioas(struct vfio_device *vdev)
1749048c734SYi Liu {
1759048c734SYi Liu 	lockdep_assert_held(&vdev->dev_set->lock);
1769048c734SYi Liu 
1779048c734SYi Liu 	if (WARN_ON(!vdev->iommufd_device) || !vdev->iommufd_attached)
1789048c734SYi Liu 		return;
1799048c734SYi Liu 
1802fb69c60SYi Liu 	iommufd_device_detach(vdev->iommufd_device, IOMMU_NO_PASID);
1819048c734SYi Liu 	vdev->iommufd_attached = false;
1829048c734SYi Liu }
1839048c734SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_physical_detach_ioas);
1849048c734SYi Liu 
vfio_iommufd_physical_pasid_attach_ioas(struct vfio_device * vdev,u32 pasid,u32 * pt_id)18529064134SYi Liu int vfio_iommufd_physical_pasid_attach_ioas(struct vfio_device *vdev,
18629064134SYi Liu 					    u32 pasid, u32 *pt_id)
18729064134SYi Liu {
18829064134SYi Liu 	int rc;
18929064134SYi Liu 
19029064134SYi Liu 	lockdep_assert_held(&vdev->dev_set->lock);
19129064134SYi Liu 
19229064134SYi Liu 	if (WARN_ON(!vdev->iommufd_device))
19329064134SYi Liu 		return -EINVAL;
19429064134SYi Liu 
19529064134SYi Liu 	if (ida_exists(&vdev->pasids, pasid))
19629064134SYi Liu 		return iommufd_device_replace(vdev->iommufd_device,
19729064134SYi Liu 					      pasid, pt_id);
19829064134SYi Liu 
19929064134SYi Liu 	rc = ida_alloc_range(&vdev->pasids, pasid, pasid, GFP_KERNEL);
20029064134SYi Liu 	if (rc < 0)
20129064134SYi Liu 		return rc;
20229064134SYi Liu 
20329064134SYi Liu 	rc = iommufd_device_attach(vdev->iommufd_device, pasid, pt_id);
20429064134SYi Liu 	if (rc)
20529064134SYi Liu 		ida_free(&vdev->pasids, pasid);
20629064134SYi Liu 
20729064134SYi Liu 	return rc;
20829064134SYi Liu }
20929064134SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_physical_pasid_attach_ioas);
21029064134SYi Liu 
vfio_iommufd_physical_pasid_detach_ioas(struct vfio_device * vdev,u32 pasid)21129064134SYi Liu void vfio_iommufd_physical_pasid_detach_ioas(struct vfio_device *vdev,
21229064134SYi Liu 					     u32 pasid)
21329064134SYi Liu {
21429064134SYi Liu 	lockdep_assert_held(&vdev->dev_set->lock);
21529064134SYi Liu 
21629064134SYi Liu 	if (WARN_ON(!vdev->iommufd_device))
21729064134SYi Liu 		return;
21829064134SYi Liu 
21929064134SYi Liu 	if (!ida_exists(&vdev->pasids, pasid))
22029064134SYi Liu 		return;
22129064134SYi Liu 
22229064134SYi Liu 	iommufd_device_detach(vdev->iommufd_device, pasid);
22329064134SYi Liu 	ida_free(&vdev->pasids, pasid);
22429064134SYi Liu }
22529064134SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_physical_pasid_detach_ioas);
22629064134SYi Liu 
2274741f2e9SJason Gunthorpe /*
2284741f2e9SJason Gunthorpe  * The emulated standard ops mean that vfio_device is going to use the
2294741f2e9SJason Gunthorpe  * "mdev path" and will call vfio_pin_pages()/vfio_dma_rw(). Drivers using this
2300a782d15SYi Liu  * ops set should call vfio_register_emulated_iommu_dev(). Drivers that do
2310a782d15SYi Liu  * not call vfio_pin_pages()/vfio_dma_rw() have no need to provide dma_unmap.
2324741f2e9SJason Gunthorpe  */
2334741f2e9SJason Gunthorpe 
vfio_emulated_unmap(void * data,unsigned long iova,unsigned long length)2344741f2e9SJason Gunthorpe static void vfio_emulated_unmap(void *data, unsigned long iova,
2354741f2e9SJason Gunthorpe 				unsigned long length)
2364741f2e9SJason Gunthorpe {
2374741f2e9SJason Gunthorpe 	struct vfio_device *vdev = data;
2384741f2e9SJason Gunthorpe 
2390a782d15SYi Liu 	if (vdev->ops->dma_unmap)
2404741f2e9SJason Gunthorpe 		vdev->ops->dma_unmap(vdev, iova, length);
2414741f2e9SJason Gunthorpe }
2424741f2e9SJason Gunthorpe 
2434741f2e9SJason Gunthorpe static const struct iommufd_access_ops vfio_user_ops = {
2444741f2e9SJason Gunthorpe 	.needs_pin_pages = 1,
2454741f2e9SJason Gunthorpe 	.unmap = vfio_emulated_unmap,
2464741f2e9SJason Gunthorpe };
2474741f2e9SJason Gunthorpe 
vfio_iommufd_emulated_bind(struct vfio_device * vdev,struct iommufd_ctx * ictx,u32 * out_device_id)2484741f2e9SJason Gunthorpe int vfio_iommufd_emulated_bind(struct vfio_device *vdev,
2494741f2e9SJason Gunthorpe 			       struct iommufd_ctx *ictx, u32 *out_device_id)
2504741f2e9SJason Gunthorpe {
25154b47585SNicolin Chen 	struct iommufd_access *user;
25254b47585SNicolin Chen 
2534741f2e9SJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
2544741f2e9SJason Gunthorpe 
255632fda7fSYi Liu 	user = iommufd_access_create(ictx, &vfio_user_ops, vdev, out_device_id);
2564508a533SYi Liu 	if (IS_ERR(user))
25754b47585SNicolin Chen 		return PTR_ERR(user);
25854b47585SNicolin Chen 	vdev->iommufd_access = user;
2594741f2e9SJason Gunthorpe 	return 0;
2604741f2e9SJason Gunthorpe }
2614741f2e9SJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_bind);
2624741f2e9SJason Gunthorpe 
vfio_iommufd_emulated_unbind(struct vfio_device * vdev)2634741f2e9SJason Gunthorpe void vfio_iommufd_emulated_unbind(struct vfio_device *vdev)
2644741f2e9SJason Gunthorpe {
2654741f2e9SJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
2664741f2e9SJason Gunthorpe 
2674741f2e9SJason Gunthorpe 	if (vdev->iommufd_access) {
2684741f2e9SJason Gunthorpe 		iommufd_access_destroy(vdev->iommufd_access);
26954b47585SNicolin Chen 		vdev->iommufd_attached = false;
2704741f2e9SJason Gunthorpe 		vdev->iommufd_access = NULL;
2714741f2e9SJason Gunthorpe 	}
2724741f2e9SJason Gunthorpe }
2734741f2e9SJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_unbind);
2744741f2e9SJason Gunthorpe 
vfio_iommufd_emulated_attach_ioas(struct vfio_device * vdev,u32 * pt_id)2754741f2e9SJason Gunthorpe int vfio_iommufd_emulated_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
2764741f2e9SJason Gunthorpe {
27754b47585SNicolin Chen 	int rc;
2784741f2e9SJason Gunthorpe 
2794741f2e9SJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
2804741f2e9SJason Gunthorpe 
28154b47585SNicolin Chen 	if (vdev->iommufd_attached)
282c157fd88SNicolin Chen 		rc = iommufd_access_replace(vdev->iommufd_access, *pt_id);
283c157fd88SNicolin Chen 	else
28454b47585SNicolin Chen 		rc = iommufd_access_attach(vdev->iommufd_access, *pt_id);
28554b47585SNicolin Chen 	if (rc)
28654b47585SNicolin Chen 		return rc;
28754b47585SNicolin Chen 	vdev->iommufd_attached = true;
2884741f2e9SJason Gunthorpe 	return 0;
2894741f2e9SJason Gunthorpe }
2904741f2e9SJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_attach_ioas);
2918cfa7186SYi Liu 
vfio_iommufd_emulated_detach_ioas(struct vfio_device * vdev)2928cfa7186SYi Liu void vfio_iommufd_emulated_detach_ioas(struct vfio_device *vdev)
2938cfa7186SYi Liu {
2948cfa7186SYi Liu 	lockdep_assert_held(&vdev->dev_set->lock);
2958cfa7186SYi Liu 
2968cfa7186SYi Liu 	if (WARN_ON(!vdev->iommufd_access) ||
2978cfa7186SYi Liu 	    !vdev->iommufd_attached)
2988cfa7186SYi Liu 		return;
2998cfa7186SYi Liu 
3008cfa7186SYi Liu 	iommufd_access_detach(vdev->iommufd_access);
3018cfa7186SYi Liu 	vdev->iommufd_attached = false;
3028cfa7186SYi Liu }
3038cfa7186SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_detach_ioas);
304