// SPDX-License-Identifier: GPL-2.0-only
/*
* VFIO core
*
* Copyright (C) 2012 Red Hat, Inc. All rights reserved.
* Author: Alex Williamson <alex.williamson@redhat.com>
*
* Derived from original vfio:
* Copyright 2010 Cisco Systems, Inc. All rights reserved.
* Author: Tom Lyon, pugs@cisco.com
*/
#include <linux/vfio.h>
#include <linux/iommufd.h>
#include <linux/anon_inodes.h>
#include "vfio.h"
static struct vfio {
struct class *class;
struct list_head group_list;
struct mutex group_lock; /* locks group_list */
struct ida group_ida;
dev_t group_devt;
} vfio;
static struct vfio_device *vfio_device_get_from_name(struct vfio_group *group,
char *buf)
{
struct vfio_device *it, *device = ERR_PTR(-ENODEV);
mutex_lock(&group->device_lock);
list_for_each_entry(it, &group->device_list, group_next) {
int ret;
if (it->ops->match) {
ret = it->ops->match(it, buf);
if (ret < 0) {
device = ERR_PTR(ret);
break;
}
} else {
ret = !strcmp(dev_name(it->dev), buf);
}
if (ret && vfio_device_try_get_registration(it)) {
device = it;
break;
}
}
mutex_unlock(&group->device_lock);
return device;
}
/*
* VFIO Group fd, /dev/vfio/$GROUP
*/
static bool vfio_group_has_iommu(struct vfio_group *group)
{
lockdep_assert_held(&group->group_lock);
/*
* There can only be users if there is a container, and if there is a
* container there must be users.
*/
WARN_ON(!group->container != !group->container_users);
return group->container || group->iommufd;
}
/*
* VFIO_GROUP_UNSET_CONTAINER should fail if there are other users or
* if there was no container to unset. Since the ioctl is called on
* the group, we know that still exists, therefore the only valid
* transition here is 1->0.
*/
static int vfio_group_ioctl_unset_container(struct vfio_group *group)
{
int ret = 0;
mutex_lock(&group->group_lock);
if (!vfio_group_has_iommu(group)) {
ret = -EINVAL;
goto out_unlock;
}
if (group->container) {
if (group->container_users != 1) {
ret = -EBUSY;
goto out_unlock;
}
vfio_group_detach_container(group);
}
if (group->iommufd) {
iommufd_ctx_put(group->iommufd);
group->iommufd = NULL;
}
out_unlock:
mutex_unlock(&group->group_lock);
return ret;
}
static int vfio_group_ioctl_set_container(struct vfio_group *group,
int __user *arg)
{
struct vfio_container *container;
struct iommufd_ctx *iommufd;
int ret;
int fd;
if (get_user(fd, arg))
return -EFAULT;
CLASS(fd, f)(fd);
if (fd_empty(f))
return -EBADF;
mutex_lock(&group->group_lock);
if (vfio_group_has_iommu(group)) {
ret = -EINVAL;
goto out_unlock;
}
if (!group->iommu_group) {
ret = -ENODEV;
goto out_unlock;
}
container = vfio_container_from_file(fd_file(f));
if (container) {
ret = vfio_container_attach_group(container, group);
goto out_unlock;
}
iommufd = iommufd_ctx_from_file(fd_file(f));
if (!IS_ERR(iommufd)) {
if (IS_ENABLED(CONFIG_VFIO_NOIOMMU) &&
group->type == VFIO_NO_IOMMU)
ret = iommufd_vfio_compat_set_no_iommu(iommufd);
else
ret = iommufd_vfio_compat_ioas_create(iommufd);
if (ret) {
iommufd_ctx_put(iommufd);
goto out_unlock;
}
group->iommufd = iommufd;
goto out_unlock;
}
/* The FD passed is not recognized. */
ret = -EBADFD;
out_unlock:
mutex_unlock(&group->group_lock);
return ret;
}
static void vfio_device_group_get_kvm_safe(struct vfio_device *device)
{
spin_lock(&device->group->kvm_ref_lock);
vfio_device_get_kvm_safe(device, device->group->kvm);
spin_unlock(&device->group->kvm_ref_lock);
}
static int vfio_df_group_open(struct vfio_device_file *df)
{
struct vfio_device *device = df->device;
int ret;
mutex_lock(&device->group->group_lock);
if (!vfio_group_has_iommu(device->group)) {
ret = -EINVAL;
goto out_unlock