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