| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES
- */
- #include <linux/vfio.h>
- #include <linux/iommufd.h>
- #include "vfio.h"
- MODULE_IMPORT_NS(IOMMUFD);
- MODULE_IMPORT_NS(IOMMUFD_VFIO);
- bool vfio_iommufd_device_has_compat_ioas(struct vfio_device *vdev,
- struct iommufd_ctx *ictx)
- {
- u32 ioas_id;
- return !iommufd_vfio_compat_ioas_get_id(ictx, &ioas_id);
- }
- int vfio_df_iommufd_bind(struct vfio_device_file *df)
- {
- struct vfio_device *vdev = df->device;
- struct iommufd_ctx *ictx = df->iommufd;
- lockdep_assert_held(&vdev->dev_set->lock);
- return vdev->ops->bind_iommufd(vdev, ictx, &df->devid);
- }
- int vfio_iommufd_compat_attach_ioas(struct vfio_device *vdev,
- struct iommufd_ctx *ictx)
- {
- u32 ioas_id;
- int ret;
- lockdep_assert_held(&vdev->dev_set->lock);
- /* compat noiommu does not need to do ioas attach */
- if (vfio_device_is_noiommu(vdev))
- return 0;
- ret = iommufd_vfio_compat_ioas_get_id(ictx, &ioas_id);
- if (ret)
- return ret;
- /* The legacy path has no way to return the selected pt_id */
- return vdev->ops->attach_ioas(vdev, &ioas_id);
- }
- void vfio_df_iommufd_unbind(struct vfio_device_file *df)
- {
- struct vfio_device *vdev = df->device;
- lockdep_assert_held(&vdev->dev_set->lock);
- if (vfio_device_is_noiommu(vdev))
- return;
- if (vdev->ops->unbind_iommufd)
- vdev->ops->unbind_iommufd(vdev);
- }
- struct iommufd_ctx *vfio_iommufd_device_ictx(struct vfio_device *vdev)
- {
- if (vdev->iommufd_device)
- return iommufd_device_to_ictx(vdev->iommufd_device);
- return NULL;
- }
- EXPORT_SYMBOL_GPL(vfio_iommufd_device_ictx);
- static int vfio_iommufd_device_id(struct vfio_device *vdev)
- {
- if (vdev->iommufd_device)
- return iommufd_device_to_id(vdev->iommufd_device);
- return -EINVAL;
- }
- /*
- * Return devid for a device.
- * valid ID for the device that is owned by the ictx
- * -ENOENT = device is owned but there is no ID
- * -ENODEV or other error = device is not owned
- */
- int vfio_iommufd_get_dev_id(struct vfio_device *vdev, struct iommufd_ctx *ictx)
- {
- struct iommu_group *group;
- int devid;
- if (vfio_iommufd_device_ictx(vdev) == ictx)
- return vfio_iommufd_device_id(vdev);
- group = iommu_group_get(vdev->dev);
- if (!group)
- return -ENODEV;
- if (iommufd_ctx_has_group(ictx, group))
- devid = -ENOENT;
- else
- devid = -ENODEV;
- iommu_group_put(group);
- return devid;
- }
- EXPORT_SYMBOL_GPL(vfio_iommufd_get_dev_id);
- /*
- * The physical standard ops mean that the iommufd_device is bound to the
- * physical device vdev->dev that was provided to vfio_init_group_dev(). Drivers
- * using this ops set should call vfio_register_group_dev()
- */
- int vfio_iommufd_physical_bind(struct vfio_device *vdev,
- struct iommufd_ctx *ictx, u32 *out_device_id)
- {
- struct iommufd_device *idev;
- idev = iommufd_device_bind(ictx, vdev->dev, out_device_id);
- if (IS_ERR(idev))
- return PTR_ERR(idev);
- vdev->iommufd_device = idev;
- return 0;
- }
- EXPORT_SYMBOL_GPL(vfio_iommufd_physical_bind);
- void vfio_iommufd_physical_unbind(struct vfio_device *vdev)
- {
- lockdep_assert_held(&vdev->dev_set->lock);
- if (vdev->iommufd_attached) {
- iommufd_device_detach(vdev->iommufd_device);
- vdev->iommufd_attached = false;
- }
- iommufd_device_unbind(vdev->iommufd_device);
- vdev->iommufd_device = NULL;
- }
- EXPORT_SYMBOL_GPL(vfio_iommufd_physical_unbind);
- int vfio_iommufd_physical_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
- {
- int rc;
- lockdep_assert_held(&vdev->dev_set->lock);
- if (WARN_ON(!vdev->iommufd_device))
- return -EINVAL;
- if (vdev->iommufd_attached)
- rc = iommufd_device_replace(vdev->iommufd_device, pt_id);
- else
- rc = iommufd_device_attach(vdev->iommufd_device, pt_id);
- if (rc)
- return rc;
- vdev->iommufd_attached = true;
- return 0;
- }
- EXPORT_SYMBOL_GPL(vfio_iommufd_physical_attach_ioas);
- void vfio_iommufd_physical_detach_ioas(struct vfio_device *vdev)
- {
- lockdep_assert_held(&vdev->dev_set->lock);
- if (WARN_ON(!vdev->iommufd_device) || !vdev->iommufd_attached)
- return;
- iommufd_device_detach(vdev->iommufd_device);
- vdev->iommufd_attached = false;
- }
- EXPORT_SYMBOL_GPL(vfio_iommufd_physical_detach_ioas);
- /*
- * The emulated standard ops mean that vfio_device is going to use the
- * "mdev path" and will call vfio_pin_pages()/vfio_dma_rw(). Drivers using this
- * ops set should call vfio_register_emulated_iommu_dev(). Drivers that do
- * not call vfio_pin_pages()/vfio_dma_rw() have no need to provide dma_unmap.
- */
- static void vfio_emulated_unmap(void *data, unsigned long iova,
- unsigned long length)
- {
- struct vfio_device *vdev = data;
- if (vdev->ops->dma_unmap)
- vdev->ops->dma_unmap(vdev, iova, length);
- }
- static const struct iommufd_access_ops vfio_user_ops = {
- .needs_pin_pages = 1,
- .unmap = vfio_emulated_unmap,
- };
- int vfio_iommufd_emulated_bind(struct vfio_device *vdev,
- struct iommufd_ctx *ictx, u32 *out_device_id)
- {
- struct iommufd_access *user;
- lockdep_assert_held(&vdev->dev_set->lock);
- user = iommufd_access_create(ictx, &vfio_user_ops, vdev, out_device_id);
- if (IS_ERR(user))
- return PTR_ERR(user);
- vdev->iommufd_access = user;
- return 0;
- }
- EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_bind);
- void vfio_iommufd_emulated_unbind(struct vfio_device *vdev)
- {
- lockdep_assert_held(&vdev->dev_set->lock);
- if (vdev->iommufd_access) {
- iommufd_access_destroy(vdev->iommufd_access);
- vdev->iommufd_attached = false;
- vdev->iommufd_access = NULL;
- }
- }
- EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_unbind);
- int vfio_iommufd_emulated_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
- {
- int rc;
- lockdep_assert_held(&vdev->dev_set->lock);
- if (vdev->iommufd_attached)
- rc = iommufd_access_replace(vdev->iommufd_access, *pt_id);
- else
- rc = iommufd_access_attach(vdev->iommufd_access, *pt_id);
- if (rc)
- return rc;
- vdev->iommufd_attached = true;
- return 0;
- }
- EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_attach_ioas);
- void vfio_iommufd_emulated_detach_ioas(struct vfio_device *vdev)
- {
- lockdep_assert_held(&vdev->dev_set->lock);
- if (WARN_ON(!vdev->iommufd_access) ||
- !vdev->iommufd_attached)
- return;
- iommufd_access_detach(vdev->iommufd_access);
- vdev->iommufd_attached = false;
- }
- EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_detach_ioas);
|