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
10a4d1f91dSJason Gunthorpe MODULE_IMPORT_NS(IOMMUFD);
11a4d1f91dSJason Gunthorpe 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
2831014aefSYi Liu return vdev->ops->bind_iommufd(vdev, ictx, &df->devid);
296f240ee6SYi Liu }
306f240ee6SYi Liu
vfio_iommufd_compat_attach_ioas(struct vfio_device * vdev,struct iommufd_ctx * ictx)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
vfio_df_iommufd_unbind(struct vfio_device_file * df)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
vfio_iommufd_device_ictx(struct vfio_device * vdev)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
vfio_iommufd_device_id(struct vfio_device * vdev)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 */
vfio_iommufd_get_dev_id(struct vfio_device * vdev,struct iommufd_ctx * ictx)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 */
vfio_iommufd_physical_bind(struct vfio_device * vdev,struct iommufd_ctx * ictx,u32 * out_device_id)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
vfio_iommufd_physical_unbind(struct vfio_device * vdev)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) {
131a4d1f91dSJason Gunthorpe iommufd_device_detach(vdev->iommufd_device);
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
vfio_iommufd_physical_attach_ioas(struct vfio_device * vdev,u32 * pt_id)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*c157fd88SNicolin Chen rc = iommufd_device_replace(vdev->iommufd_device, pt_id);
150*c157fd88SNicolin Chen else
151a4d1f91dSJason Gunthorpe rc = iommufd_device_attach(vdev->iommufd_device, pt_id);
152a4d1f91dSJason Gunthorpe if (rc)
153a4d1f91dSJason Gunthorpe return rc;
154a4d1f91dSJason Gunthorpe vdev->iommufd_attached = true;
155a4d1f91dSJason Gunthorpe return 0;
156a4d1f91dSJason Gunthorpe }
157a4d1f91dSJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_physical_attach_ioas);
1584741f2e9SJason Gunthorpe
vfio_iommufd_physical_detach_ioas(struct vfio_device * vdev)1599048c734SYi Liu void vfio_iommufd_physical_detach_ioas(struct vfio_device *vdev)
1609048c734SYi Liu {
1619048c734SYi Liu lockdep_assert_held(&vdev->dev_set->lock);
1629048c734SYi Liu
1639048c734SYi Liu if (WARN_ON(!vdev->iommufd_device) || !vdev->iommufd_attached)
1649048c734SYi Liu return;
1659048c734SYi Liu
1669048c734SYi Liu iommufd_device_detach(vdev->iommufd_device);
1679048c734SYi Liu vdev->iommufd_attached = false;
1689048c734SYi Liu }
1699048c734SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_physical_detach_ioas);
1709048c734SYi Liu
1714741f2e9SJason Gunthorpe /*
1724741f2e9SJason Gunthorpe * The emulated standard ops mean that vfio_device is going to use the
1734741f2e9SJason Gunthorpe * "mdev path" and will call vfio_pin_pages()/vfio_dma_rw(). Drivers using this
1740a782d15SYi Liu * ops set should call vfio_register_emulated_iommu_dev(). Drivers that do
1750a782d15SYi Liu * not call vfio_pin_pages()/vfio_dma_rw() have no need to provide dma_unmap.
1764741f2e9SJason Gunthorpe */
1774741f2e9SJason Gunthorpe
vfio_emulated_unmap(void * data,unsigned long iova,unsigned long length)1784741f2e9SJason Gunthorpe static void vfio_emulated_unmap(void *data, unsigned long iova,
1794741f2e9SJason Gunthorpe unsigned long length)
1804741f2e9SJason Gunthorpe {
1814741f2e9SJason Gunthorpe struct vfio_device *vdev = data;
1824741f2e9SJason Gunthorpe
1830a782d15SYi Liu if (vdev->ops->dma_unmap)
1844741f2e9SJason Gunthorpe vdev->ops->dma_unmap(vdev, iova, length);
1854741f2e9SJason Gunthorpe }
1864741f2e9SJason Gunthorpe
1874741f2e9SJason Gunthorpe static const struct iommufd_access_ops vfio_user_ops = {
1884741f2e9SJason Gunthorpe .needs_pin_pages = 1,
1894741f2e9SJason Gunthorpe .unmap = vfio_emulated_unmap,
1904741f2e9SJason Gunthorpe };
1914741f2e9SJason Gunthorpe
vfio_iommufd_emulated_bind(struct vfio_device * vdev,struct iommufd_ctx * ictx,u32 * out_device_id)1924741f2e9SJason Gunthorpe int vfio_iommufd_emulated_bind(struct vfio_device *vdev,
1934741f2e9SJason Gunthorpe struct iommufd_ctx *ictx, u32 *out_device_id)
1944741f2e9SJason Gunthorpe {
19554b47585SNicolin Chen struct iommufd_access *user;
19654b47585SNicolin Chen
1974741f2e9SJason Gunthorpe lockdep_assert_held(&vdev->dev_set->lock);
1984741f2e9SJason Gunthorpe
199632fda7fSYi Liu user = iommufd_access_create(ictx, &vfio_user_ops, vdev, out_device_id);
2004508a533SYi Liu if (IS_ERR(user))
20154b47585SNicolin Chen return PTR_ERR(user);
20254b47585SNicolin Chen vdev->iommufd_access = user;
2034741f2e9SJason Gunthorpe return 0;
2044741f2e9SJason Gunthorpe }
2054741f2e9SJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_bind);
2064741f2e9SJason Gunthorpe
vfio_iommufd_emulated_unbind(struct vfio_device * vdev)2074741f2e9SJason Gunthorpe void vfio_iommufd_emulated_unbind(struct vfio_device *vdev)
2084741f2e9SJason Gunthorpe {
2094741f2e9SJason Gunthorpe lockdep_assert_held(&vdev->dev_set->lock);
2104741f2e9SJason Gunthorpe
2114741f2e9SJason Gunthorpe if (vdev->iommufd_access) {
2124741f2e9SJason Gunthorpe iommufd_access_destroy(vdev->iommufd_access);
21354b47585SNicolin Chen vdev->iommufd_attached = false;
2144741f2e9SJason Gunthorpe vdev->iommufd_access = NULL;
2154741f2e9SJason Gunthorpe }
2164741f2e9SJason Gunthorpe }
2174741f2e9SJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_unbind);
2184741f2e9SJason Gunthorpe
vfio_iommufd_emulated_attach_ioas(struct vfio_device * vdev,u32 * pt_id)2194741f2e9SJason Gunthorpe int vfio_iommufd_emulated_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
2204741f2e9SJason Gunthorpe {
22154b47585SNicolin Chen int rc;
2224741f2e9SJason Gunthorpe
2234741f2e9SJason Gunthorpe lockdep_assert_held(&vdev->dev_set->lock);
2244741f2e9SJason Gunthorpe
22554b47585SNicolin Chen if (vdev->iommufd_attached)
226*c157fd88SNicolin Chen rc = iommufd_access_replace(vdev->iommufd_access, *pt_id);
227*c157fd88SNicolin Chen else
22854b47585SNicolin Chen rc = iommufd_access_attach(vdev->iommufd_access, *pt_id);
22954b47585SNicolin Chen if (rc)
23054b47585SNicolin Chen return rc;
23154b47585SNicolin Chen vdev->iommufd_attached = true;
2324741f2e9SJason Gunthorpe return 0;
2334741f2e9SJason Gunthorpe }
2344741f2e9SJason Gunthorpe EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_attach_ioas);
2358cfa7186SYi Liu
vfio_iommufd_emulated_detach_ioas(struct vfio_device * vdev)2368cfa7186SYi Liu void vfio_iommufd_emulated_detach_ioas(struct vfio_device *vdev)
2378cfa7186SYi Liu {
2388cfa7186SYi Liu lockdep_assert_held(&vdev->dev_set->lock);
2398cfa7186SYi Liu
2408cfa7186SYi Liu if (WARN_ON(!vdev->iommufd_access) ||
2418cfa7186SYi Liu !vdev->iommufd_attached)
2428cfa7186SYi Liu return;
2438cfa7186SYi Liu
2448cfa7186SYi Liu iommufd_access_detach(vdev->iommufd_access);
2458cfa7186SYi Liu vdev->iommufd_attached = false;
2468cfa7186SYi Liu }
2478cfa7186SYi Liu EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_detach_ioas);
248