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