929 lines · 21.9 KB · C
RawDownload
// 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 char *vfio_devnode(const struct device *, umode_t *);static const struct class vfio_class = {	.name	= "vfio",	.devnode = vfio_devnode}; static struct vfio {	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;	} 	mutex_lock(&device->dev_set->lock); 	/*	 * Before the first device open, get the KVM pointer currently	 * associated with the group (if there is one) and obtain a reference	 * now that will be held until the open_count reaches 0 again.  Save	 * the pointer in the device for use by drivers.	 */	if (device->open_count == 0)		vfio_device_group_get_kvm_safe(device); 	df->iommufd = device->group->iommufd;	if (df->iommufd && vfio_device_is_noiommu(device) && device->open_count == 0) {		/*		 * Require no compat ioas to be assigned to proceed.  The basic		 * statement is that the user cannot have done something that		 * implies they expected translation to exist		 */		if (!capable(CAP_SYS_RAWIO) ||		    vfio_iommufd_device_has_compat_ioas(device, df->iommufd)) {			ret = -EPERM;			goto out_put_kvm;		}	} 	ret = vfio_df_open(df);	if (ret)		goto out_put_kvm; 	if (df->iommufd && device->open_count == 1) {		ret = vfio_iommufd_compat_attach_ioas(device, df->iommufd);		if (ret)			goto out_close_device;	} 	/*	 * Paired with smp_load_acquire() in vfio_device_fops::ioctl/	 * read/write/mmap and vfio_file_has_device_access()	 */	smp_store_release(&df->access_granted, true); 	mutex_unlock(&device->dev_set->lock);	mutex_unlock(&device->group->group_lock);	return 0; out_close_device:	vfio_df_close(df);out_put_kvm:	df->iommufd = NULL;	if (device->open_count == 0)		vfio_device_put_kvm(device);	mutex_unlock(&device->dev_set->lock);out_unlock:	mutex_unlock(&device->group->group_lock);	return ret;} void vfio_df_group_close(struct vfio_device_file *df){	struct vfio_device *device = df->device; 	mutex_lock(&device->group->group_lock);	mutex_lock(&device->dev_set->lock); 	vfio_df_close(df);	df->iommufd = NULL; 	if (device->open_count == 0)		vfio_device_put_kvm(device); 	mutex_unlock(&device->dev_set->lock);	mutex_unlock(&device->group->group_lock);} static struct file *vfio_device_open_file(struct vfio_device *device){	struct vfio_device_file *df;	struct file *filep;	int ret; 	df = vfio_allocate_device_file(device);	if (IS_ERR(df)) {		ret = PTR_ERR(df);		goto err_out;	} 	df->group = device->group; 	ret = vfio_df_group_open(df);	if (ret)		goto err_free; 	filep = anon_inode_getfile_fmode("[vfio-device]", &vfio_device_fops,				   df, O_RDWR, FMODE_PREAD | FMODE_PWRITE);	if (IS_ERR(filep)) {		ret = PTR_ERR(filep);		goto err_close_device;	}	/*	 * Use the pseudo fs inode on the device to link all mmaps	 * to the same address space, allowing us to unmap all vmas	 * associated to this device using unmap_mapping_range().	 */	filep->f_mapping = device->inode->i_mapping; 	if (device->group->type == VFIO_NO_IOMMU)		dev_warn(device->dev, "vfio-noiommu device opened by user "			 "(%s:%d)\n", current->comm, task_pid_nr(current));	/*	 * On success the ref of device is moved to the file and	 * put in vfio_device_fops_release()	 */	return filep; err_close_device:	vfio_df_group_close(df);err_free:	kfree(df);err_out:	return ERR_PTR(ret);} static int vfio_group_ioctl_get_device_fd(struct vfio_group *group,					  char __user *arg){	struct vfio_device *device;	char *buf;	int fd; 	buf = strndup_user(arg, PAGE_SIZE);	if (IS_ERR(buf))		return PTR_ERR(buf); 	device = vfio_device_get_from_name(group, buf);	kfree(buf);	if (IS_ERR(device))		return PTR_ERR(device); 	fd = FD_ADD(O_CLOEXEC, vfio_device_open_file(device));	if (fd < 0)		vfio_device_put_registration(device);	return fd;} static int vfio_group_ioctl_get_status(struct vfio_group *group,				       struct vfio_group_status __user *arg){	unsigned long minsz = offsetofend(struct vfio_group_status, flags);	struct vfio_group_status status; 	if (copy_from_user(&status, arg, minsz))		return -EFAULT; 	if (status.argsz < minsz)		return -EINVAL; 	status.flags = 0; 	mutex_lock(&group->group_lock);	if (!group->iommu_group) {		mutex_unlock(&group->group_lock);		return -ENODEV;	} 	/*	 * With the container FD the iommu_group_claim_dma_owner() is done	 * during SET_CONTAINER but for IOMMFD this is done during	 * VFIO_GROUP_GET_DEVICE_FD. Meaning that with iommufd	 * VFIO_GROUP_FLAGS_VIABLE could be set but GET_DEVICE_FD will fail due	 * to viability.	 */	if (vfio_group_has_iommu(group))		status.flags |= VFIO_GROUP_FLAGS_CONTAINER_SET |				VFIO_GROUP_FLAGS_VIABLE;	else if (!iommu_group_dma_owner_claimed(group->iommu_group))		status.flags |= VFIO_GROUP_FLAGS_VIABLE;	mutex_unlock(&group->group_lock); 	if (copy_to_user(arg, &status, minsz))		return -EFAULT;	return 0;} static long vfio_group_fops_unl_ioctl(struct file *filep,				      unsigned int cmd, unsigned long arg){	struct vfio_group *group = filep->private_data;	void __user *uarg = (void __user *)arg; 	switch (cmd) {	case VFIO_GROUP_GET_DEVICE_FD:		return vfio_group_ioctl_get_device_fd(group, uarg);	case VFIO_GROUP_GET_STATUS:		return vfio_group_ioctl_get_status(group, uarg);	case VFIO_GROUP_SET_CONTAINER:		return vfio_group_ioctl_set_container(group, uarg);	case VFIO_GROUP_UNSET_CONTAINER:		return vfio_group_ioctl_unset_container(group);	default:		return -ENOTTY;	}} int vfio_device_block_group(struct vfio_device *device){	struct vfio_group *group = device->group;	int ret = 0; 	mutex_lock(&group->group_lock);	if (group->opened_file) {		ret = -EBUSY;		goto out_unlock;	} 	group->cdev_device_open_cnt++; out_unlock:	mutex_unlock(&group->group_lock);	return ret;} void vfio_device_unblock_group(struct vfio_device *device){	struct vfio_group *group = device->group; 	mutex_lock(&group->group_lock);	group->cdev_device_open_cnt--;	mutex_unlock(&group->group_lock);} static int vfio_group_fops_open(struct inode *inode, struct file *filep){	struct vfio_group *group =		container_of(inode->i_cdev, struct vfio_group, cdev);	int ret; 	mutex_lock(&group->group_lock); 	/*	 * drivers can be zero if this races with vfio_device_remove_group(), it	 * will be stable at 0 under the group rwsem	 */	if (refcount_read(&group->drivers) == 0) {		ret = -ENODEV;		goto out_unlock;	} 	if (group->type == VFIO_NO_IOMMU && !capable(CAP_SYS_RAWIO)) {		ret = -EPERM;		goto out_unlock;	} 	if (group->cdev_device_open_cnt) {		ret = -EBUSY;		goto out_unlock;	} 	/*	 * Do we need multiple instances of the group open?  Seems not.	 */	if (group->opened_file) {		ret = -EBUSY;		goto out_unlock;	}	group->opened_file = filep;	filep->private_data = group;	ret = 0;out_unlock:	mutex_unlock(&group->group_lock);	return ret;} static int vfio_group_fops_release(struct inode *inode, struct file *filep){	struct vfio_group *group = filep->private_data; 	filep->private_data = NULL; 	mutex_lock(&group->group_lock);	/*	 * Device FDs hold a group file reference, therefore the group release	 * is only called when there are no open devices.	 */	if (group->container)		vfio_group_detach_container(group);	if (group->iommufd) {		iommufd_ctx_put(group->iommufd);		group->iommufd = NULL;	}	group->opened_file = NULL;	mutex_unlock(&group->group_lock);	return 0;} static const struct file_operations vfio_group_fops = {	.owner		= THIS_MODULE,	.unlocked_ioctl	= vfio_group_fops_unl_ioctl,	.compat_ioctl	= compat_ptr_ioctl,	.open		= vfio_group_fops_open,	.release	= vfio_group_fops_release,}; /* * Group objects - create, release, get, put, search */static struct vfio_group *vfio_group_find_from_iommu(struct iommu_group *iommu_group){	struct vfio_group *group; 	lockdep_assert_held(&vfio.group_lock); 	/*	 * group->iommu_group from the vfio.group_list cannot be NULL	 * under the vfio.group_lock.	 */	list_for_each_entry(group, &vfio.group_list, vfio_next) {		if (group->iommu_group == iommu_group)			return group;	}

Lines 1–500 of 929.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t