xref: /linux/drivers/vfio/iommufd.c (revision 2fb69c602d57f77483b8dcdd12d17408a09f76fe)
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 
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 
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 
2831014aefSYi Liu 	return vdev->ops->bind_iommufd(vdev, ictx, &df->devid);
296f240ee6SYi Liu }
306f240ee6SYi Liu 
316f240ee6SYi Liu int vfio_iommufd_compat_attach_ioas(struct vfio_device *vdev,
326f240ee6SYi Liu 				    struct iommufd_ctx *ictx)
336f240ee6SYi Liu {
346f240ee6SYi Liu 	u32 ioas_id;
35a4d1f91dSJason Gunthorpe 	int ret;
36a4d1f91dSJason Gunthorpe 
37a4d1f91dSJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
38a4d1f91dSJason Gunthorpe 
396f240ee6SYi Liu 	/* compat noiommu does not need to do ioas attach */
406f240ee6SYi Liu 	if (vfio_device_is_noiommu(vdev))
416f240ee6SYi Liu 		return 0;
42a4d1f91dSJason Gunthorpe 
43c9a397ceSJason Gunthorpe 	ret = iommufd_vfio_compat_ioas_get_id(ictx, &ioas_id);
44a4d1f91dSJason Gunthorpe 	if (ret)
45a4d1f91dSJason Gunthorpe 		return ret;
466f240ee6SYi Liu 
476f240ee6SYi Liu 	/* The legacy path has no way to return the selected pt_id */
486f240ee6SYi Liu 	return vdev->ops->attach_ioas(vdev, &ioas_id);
49a4d1f91dSJason Gunthorpe }
50a4d1f91dSJason Gunthorpe 
5131014aefSYi Liu void vfio_df_iommufd_unbind(struct vfio_device_file *df)
52a4d1f91dSJason Gunthorpe {
5331014aefSYi Liu 	struct vfio_device *vdev = df->device;
5431014aefSYi Liu 
55a4d1f91dSJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
56a4d1f91dSJason Gunthorpe 
57c9a397ceSJason Gunthorpe 	if (vfio_device_is_noiommu(vdev))
58c9a397ceSJason Gunthorpe 		return;
59c9a397ceSJason Gunthorpe 
60a4d1f91dSJason Gunthorpe 	if (vdev->ops->unbind_iommufd)
61a4d1f91dSJason Gunthorpe 		vdev->ops->unbind_iommufd(vdev);
62a4d1f91dSJason Gunthorpe }
63a4d1f91dSJason Gunthorpe 
649062ff40SYi Liu struct iommufd_ctx *vfio_iommufd_device_ictx(struct vfio_device *vdev)
659062ff40SYi Liu {
669062ff40SYi Liu 	if (vdev->iommufd_device)
679062ff40SYi Liu 		return iommufd_device_to_ictx(vdev->iommufd_device);
689062ff40SYi Liu 	return NULL;
699062ff40SYi Liu }
709062ff40SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_device_ictx);
719062ff40SYi Liu 
729062ff40SYi Liu static int vfio_iommufd_device_id(struct vfio_device *vdev)
739062ff40SYi Liu {
749062ff40SYi Liu 	if (vdev->iommufd_device)
759062ff40SYi Liu 		return iommufd_device_to_id(vdev->iommufd_device);
769062ff40SYi Liu 	return -EINVAL;
779062ff40SYi Liu }
789062ff40SYi Liu 
799062ff40SYi Liu /*
809062ff40SYi Liu  * Return devid for a device.
819062ff40SYi Liu  *  valid ID for the device that is owned by the ictx
829062ff40SYi Liu  *  -ENOENT = device is owned but there is no ID
839062ff40SYi Liu  *  -ENODEV or other error = device is not owned
849062ff40SYi Liu  */
859062ff40SYi Liu int vfio_iommufd_get_dev_id(struct vfio_device *vdev, struct iommufd_ctx *ictx)
869062ff40SYi Liu {
879062ff40SYi Liu 	struct iommu_group *group;
889062ff40SYi Liu 	int devid;
899062ff40SYi Liu 
909062ff40SYi Liu 	if (vfio_iommufd_device_ictx(vdev) == ictx)
919062ff40SYi Liu 		return vfio_iommufd_device_id(vdev);
929062ff40SYi Liu 
939062ff40SYi Liu 	group = iommu_group_get(vdev->dev);
949062ff40SYi Liu 	if (!group)
959062ff40SYi Liu 		return -ENODEV;
969062ff40SYi Liu 
979062ff40SYi Liu 	if (iommufd_ctx_has_group(ictx, group))
989062ff40SYi Liu 		devid = -ENOENT;
999062ff40SYi Liu 	else
1009062ff40SYi Liu 		devid = -ENODEV;
1019062ff40SYi Liu 
1029062ff40SYi Liu 	iommu_group_put(group);
1039062ff40SYi Liu 
1049062ff40SYi Liu 	return devid;
1059062ff40SYi Liu }
1069062ff40SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_get_dev_id);
1079062ff40SYi Liu 
108a4d1f91dSJason Gunthorpe /*
109a4d1f91dSJason Gunthorpe  * The physical standard ops mean that the iommufd_device is bound to the
110a4d1f91dSJason Gunthorpe  * physical device vdev->dev that was provided to vfio_init_group_dev(). Drivers
111a4d1f91dSJason Gunthorpe  * using this ops set should call vfio_register_group_dev()
112a4d1f91dSJason Gunthorpe  */
113a4d1f91dSJason Gunthorpe int vfio_iommufd_physical_bind(struct vfio_device *vdev,
114a4d1f91dSJason Gunthorpe 			       struct iommufd_ctx *ictx, u32 *out_device_id)
115a4d1f91dSJason Gunthorpe {
116a4d1f91dSJason Gunthorpe 	struct iommufd_device *idev;
117a4d1f91dSJason Gunthorpe 
118a4d1f91dSJason Gunthorpe 	idev = iommufd_device_bind(ictx, vdev->dev, out_device_id);
119a4d1f91dSJason Gunthorpe 	if (IS_ERR(idev))
120a4d1f91dSJason Gunthorpe 		return PTR_ERR(idev);
121a4d1f91dSJason Gunthorpe 	vdev->iommufd_device = idev;
122a4d1f91dSJason Gunthorpe 	return 0;
123a4d1f91dSJason Gunthorpe }
124a4d1f91dSJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_physical_bind);
125a4d1f91dSJason Gunthorpe 
126a4d1f91dSJason Gunthorpe void vfio_iommufd_physical_unbind(struct vfio_device *vdev)
127a4d1f91dSJason Gunthorpe {
128a4d1f91dSJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
129a4d1f91dSJason Gunthorpe 
130a4d1f91dSJason Gunthorpe 	if (vdev->iommufd_attached) {
131*2fb69c60SYi Liu 		iommufd_device_detach(vdev->iommufd_device, IOMMU_NO_PASID);
132a4d1f91dSJason Gunthorpe 		vdev->iommufd_attached = false;
133a4d1f91dSJason Gunthorpe 	}
134a4d1f91dSJason Gunthorpe 	iommufd_device_unbind(vdev->iommufd_device);
135a4d1f91dSJason Gunthorpe 	vdev->iommufd_device = NULL;
136a4d1f91dSJason Gunthorpe }
137a4d1f91dSJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_physical_unbind);
138a4d1f91dSJason Gunthorpe 
139a4d1f91dSJason Gunthorpe int vfio_iommufd_physical_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
140a4d1f91dSJason Gunthorpe {
141a4d1f91dSJason Gunthorpe 	int rc;
142a4d1f91dSJason Gunthorpe 
1439048c734SYi Liu 	lockdep_assert_held(&vdev->dev_set->lock);
1449048c734SYi Liu 
1459048c734SYi Liu 	if (WARN_ON(!vdev->iommufd_device))
1469048c734SYi Liu 		return -EINVAL;
1479048c734SYi Liu 
1489048c734SYi Liu 	if (vdev->iommufd_attached)
149*2fb69c60SYi Liu 		rc = iommufd_device_replace(vdev->iommufd_device,
150*2fb69c60SYi Liu 					    IOMMU_NO_PASID, pt_id);
151c157fd88SNicolin Chen 	else
152*2fb69c60SYi Liu 		rc = iommufd_device_attach(vdev->iommufd_device,
153*2fb69c60SYi Liu 					   IOMMU_NO_PASID, pt_id);
154a4d1f91dSJason Gunthorpe 	if (rc)
155a4d1f91dSJason Gunthorpe 		return rc;
156a4d1f91dSJason Gunthorpe 	vdev->iommufd_attached = true;
157a4d1f91dSJason Gunthorpe 	return 0;
158a4d1f91dSJason Gunthorpe }
159a4d1f91dSJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_physical_attach_ioas);
1604741f2e9SJason Gunthorpe 
1619048c734SYi Liu void vfio_iommufd_physical_detach_ioas(struct vfio_device *vdev)
1629048c734SYi Liu {
1639048c734SYi Liu 	lockdep_assert_held(&vdev->dev_set->lock);
1649048c734SYi Liu 
1659048c734SYi Liu 	if (WARN_ON(!vdev->iommufd_device) || !vdev->iommufd_attached)
1669048c734SYi Liu 		return;
1679048c734SYi Liu 
168*2fb69c60SYi Liu 	iommufd_device_detach(vdev->iommufd_device, IOMMU_NO_PASID);
1699048c734SYi Liu 	vdev->iommufd_attached = false;
1709048c734SYi Liu }
1719048c734SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_physical_detach_ioas);
1729048c734SYi Liu 
1734741f2e9SJason Gunthorpe /*
1744741f2e9SJason Gunthorpe  * The emulated standard ops mean that vfio_device is going to use the
1754741f2e9SJason Gunthorpe  * "mdev path" and will call vfio_pin_pages()/vfio_dma_rw(). Drivers using this
1760a782d15SYi Liu  * ops set should call vfio_register_emulated_iommu_dev(). Drivers that do
1770a782d15SYi Liu  * not call vfio_pin_pages()/vfio_dma_rw() have no need to provide dma_unmap.
1784741f2e9SJason Gunthorpe  */
1794741f2e9SJason Gunthorpe 
1804741f2e9SJason Gunthorpe static void vfio_emulated_unmap(void *data, unsigned long iova,
1814741f2e9SJason Gunthorpe 				unsigned long length)
1824741f2e9SJason Gunthorpe {
1834741f2e9SJason Gunthorpe 	struct vfio_device *vdev = data;
1844741f2e9SJason Gunthorpe 
1850a782d15SYi Liu 	if (vdev->ops->dma_unmap)
1864741f2e9SJason Gunthorpe 		vdev->ops->dma_unmap(vdev, iova, length);
1874741f2e9SJason Gunthorpe }
1884741f2e9SJason Gunthorpe 
1894741f2e9SJason Gunthorpe static const struct iommufd_access_ops vfio_user_ops = {
1904741f2e9SJason Gunthorpe 	.needs_pin_pages = 1,
1914741f2e9SJason Gunthorpe 	.unmap = vfio_emulated_unmap,
1924741f2e9SJason Gunthorpe };
1934741f2e9SJason Gunthorpe 
1944741f2e9SJason Gunthorpe int vfio_iommufd_emulated_bind(struct vfio_device *vdev,
1954741f2e9SJason Gunthorpe 			       struct iommufd_ctx *ictx, u32 *out_device_id)
1964741f2e9SJason Gunthorpe {
19754b47585SNicolin Chen 	struct iommufd_access *user;
19854b47585SNicolin Chen 
1994741f2e9SJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
2004741f2e9SJason Gunthorpe 
201632fda7fSYi Liu 	user = iommufd_access_create(ictx, &vfio_user_ops, vdev, out_device_id);
2024508a533SYi Liu 	if (IS_ERR(user))
20354b47585SNicolin Chen 		return PTR_ERR(user);
20454b47585SNicolin Chen 	vdev->iommufd_access = user;
2054741f2e9SJason Gunthorpe 	return 0;
2064741f2e9SJason Gunthorpe }
2074741f2e9SJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_bind);
2084741f2e9SJason Gunthorpe 
2094741f2e9SJason Gunthorpe void vfio_iommufd_emulated_unbind(struct vfio_device *vdev)
2104741f2e9SJason Gunthorpe {
2114741f2e9SJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
2124741f2e9SJason Gunthorpe 
2134741f2e9SJason Gunthorpe 	if (vdev->iommufd_access) {
2144741f2e9SJason Gunthorpe 		iommufd_access_destroy(vdev->iommufd_access);
21554b47585SNicolin Chen 		vdev->iommufd_attached = false;
2164741f2e9SJason Gunthorpe 		vdev->iommufd_access = NULL;
2174741f2e9SJason Gunthorpe 	}
2184741f2e9SJason Gunthorpe }
2194741f2e9SJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_unbind);
2204741f2e9SJason Gunthorpe 
2214741f2e9SJason Gunthorpe int vfio_iommufd_emulated_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
2224741f2e9SJason Gunthorpe {
22354b47585SNicolin Chen 	int rc;
2244741f2e9SJason Gunthorpe 
2254741f2e9SJason Gunthorpe 	lockdep_assert_held(&vdev->dev_set->lock);
2264741f2e9SJason Gunthorpe 
22754b47585SNicolin Chen 	if (vdev->iommufd_attached)
228c157fd88SNicolin Chen 		rc = iommufd_access_replace(vdev->iommufd_access, *pt_id);
229c157fd88SNicolin Chen 	else
23054b47585SNicolin Chen 		rc = iommufd_access_attach(vdev->iommufd_access, *pt_id);
23154b47585SNicolin Chen 	if (rc)
23254b47585SNicolin Chen 		return rc;
23354b47585SNicolin Chen 	vdev->iommufd_attached = true;
2344741f2e9SJason Gunthorpe 	return 0;
2354741f2e9SJason Gunthorpe }
2364741f2e9SJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_attach_ioas);
2378cfa7186SYi Liu 
2388cfa7186SYi Liu void vfio_iommufd_emulated_detach_ioas(struct vfio_device *vdev)
2398cfa7186SYi Liu {
2408cfa7186SYi Liu 	lockdep_assert_held(&vdev->dev_set->lock);
2418cfa7186SYi Liu 
2428cfa7186SYi Liu 	if (WARN_ON(!vdev->iommufd_access) ||
2438cfa7186SYi Liu 	    !vdev->iommufd_attached)
2448cfa7186SYi Liu 		return;
2458cfa7186SYi Liu 
2468cfa7186SYi Liu 	iommufd_access_detach(vdev->iommufd_access);
2478cfa7186SYi Liu 	vdev->iommufd_attached = false;
2488cfa7186SYi Liu }
2498cfa7186SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_detach_ioas);
250