162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VFIO core 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All rights reserved. 662306a36Sopenharmony_ci * Author: Alex Williamson <alex.williamson@redhat.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Derived from original vfio: 962306a36Sopenharmony_ci * Copyright 2010 Cisco Systems, Inc. All rights reserved. 1062306a36Sopenharmony_ci * Author: Tom Lyon, pugs@cisco.com 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/vfio.h> 1462306a36Sopenharmony_ci#include <linux/iommufd.h> 1562306a36Sopenharmony_ci#include <linux/anon_inodes.h> 1662306a36Sopenharmony_ci#include "vfio.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic struct vfio { 1962306a36Sopenharmony_ci struct class *class; 2062306a36Sopenharmony_ci struct list_head group_list; 2162306a36Sopenharmony_ci struct mutex group_lock; /* locks group_list */ 2262306a36Sopenharmony_ci struct ida group_ida; 2362306a36Sopenharmony_ci dev_t group_devt; 2462306a36Sopenharmony_ci} vfio; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic struct vfio_device *vfio_device_get_from_name(struct vfio_group *group, 2762306a36Sopenharmony_ci char *buf) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct vfio_device *it, *device = ERR_PTR(-ENODEV); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci mutex_lock(&group->device_lock); 3262306a36Sopenharmony_ci list_for_each_entry(it, &group->device_list, group_next) { 3362306a36Sopenharmony_ci int ret; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (it->ops->match) { 3662306a36Sopenharmony_ci ret = it->ops->match(it, buf); 3762306a36Sopenharmony_ci if (ret < 0) { 3862306a36Sopenharmony_ci device = ERR_PTR(ret); 3962306a36Sopenharmony_ci break; 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci } else { 4262306a36Sopenharmony_ci ret = !strcmp(dev_name(it->dev), buf); 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (ret && vfio_device_try_get_registration(it)) { 4662306a36Sopenharmony_ci device = it; 4762306a36Sopenharmony_ci break; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci mutex_unlock(&group->device_lock); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return device; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * VFIO Group fd, /dev/vfio/$GROUP 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic bool vfio_group_has_iommu(struct vfio_group *group) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci lockdep_assert_held(&group->group_lock); 6162306a36Sopenharmony_ci /* 6262306a36Sopenharmony_ci * There can only be users if there is a container, and if there is a 6362306a36Sopenharmony_ci * container there must be users. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci WARN_ON(!group->container != !group->container_users); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return group->container || group->iommufd; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * VFIO_GROUP_UNSET_CONTAINER should fail if there are other users or 7262306a36Sopenharmony_ci * if there was no container to unset. Since the ioctl is called on 7362306a36Sopenharmony_ci * the group, we know that still exists, therefore the only valid 7462306a36Sopenharmony_ci * transition here is 1->0. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_cistatic int vfio_group_ioctl_unset_container(struct vfio_group *group) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci int ret = 0; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci mutex_lock(&group->group_lock); 8162306a36Sopenharmony_ci if (!vfio_group_has_iommu(group)) { 8262306a36Sopenharmony_ci ret = -EINVAL; 8362306a36Sopenharmony_ci goto out_unlock; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci if (group->container) { 8662306a36Sopenharmony_ci if (group->container_users != 1) { 8762306a36Sopenharmony_ci ret = -EBUSY; 8862306a36Sopenharmony_ci goto out_unlock; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci vfio_group_detach_container(group); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci if (group->iommufd) { 9362306a36Sopenharmony_ci iommufd_ctx_put(group->iommufd); 9462306a36Sopenharmony_ci group->iommufd = NULL; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciout_unlock: 9862306a36Sopenharmony_ci mutex_unlock(&group->group_lock); 9962306a36Sopenharmony_ci return ret; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int vfio_group_ioctl_set_container(struct vfio_group *group, 10362306a36Sopenharmony_ci int __user *arg) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct vfio_container *container; 10662306a36Sopenharmony_ci struct iommufd_ctx *iommufd; 10762306a36Sopenharmony_ci struct fd f; 10862306a36Sopenharmony_ci int ret; 10962306a36Sopenharmony_ci int fd; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (get_user(fd, arg)) 11262306a36Sopenharmony_ci return -EFAULT; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci f = fdget(fd); 11562306a36Sopenharmony_ci if (!f.file) 11662306a36Sopenharmony_ci return -EBADF; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci mutex_lock(&group->group_lock); 11962306a36Sopenharmony_ci if (vfio_group_has_iommu(group)) { 12062306a36Sopenharmony_ci ret = -EINVAL; 12162306a36Sopenharmony_ci goto out_unlock; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci if (!group->iommu_group) { 12462306a36Sopenharmony_ci ret = -ENODEV; 12562306a36Sopenharmony_ci goto out_unlock; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci container = vfio_container_from_file(f.file); 12962306a36Sopenharmony_ci if (container) { 13062306a36Sopenharmony_ci ret = vfio_container_attach_group(container, group); 13162306a36Sopenharmony_ci goto out_unlock; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci iommufd = iommufd_ctx_from_file(f.file); 13562306a36Sopenharmony_ci if (!IS_ERR(iommufd)) { 13662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_VFIO_NOIOMMU) && 13762306a36Sopenharmony_ci group->type == VFIO_NO_IOMMU) 13862306a36Sopenharmony_ci ret = iommufd_vfio_compat_set_no_iommu(iommufd); 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci ret = iommufd_vfio_compat_ioas_create(iommufd); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (ret) { 14362306a36Sopenharmony_ci iommufd_ctx_put(iommufd); 14462306a36Sopenharmony_ci goto out_unlock; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci group->iommufd = iommufd; 14862306a36Sopenharmony_ci goto out_unlock; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* The FD passed is not recognized. */ 15262306a36Sopenharmony_ci ret = -EBADFD; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciout_unlock: 15562306a36Sopenharmony_ci mutex_unlock(&group->group_lock); 15662306a36Sopenharmony_ci fdput(f); 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void vfio_device_group_get_kvm_safe(struct vfio_device *device) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci spin_lock(&device->group->kvm_ref_lock); 16362306a36Sopenharmony_ci vfio_device_get_kvm_safe(device, device->group->kvm); 16462306a36Sopenharmony_ci spin_unlock(&device->group->kvm_ref_lock); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int vfio_df_group_open(struct vfio_device_file *df) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct vfio_device *device = df->device; 17062306a36Sopenharmony_ci int ret; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci mutex_lock(&device->group->group_lock); 17362306a36Sopenharmony_ci if (!vfio_group_has_iommu(device->group)) { 17462306a36Sopenharmony_ci ret = -EINVAL; 17562306a36Sopenharmony_ci goto out_unlock; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci mutex_lock(&device->dev_set->lock); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* 18162306a36Sopenharmony_ci * Before the first device open, get the KVM pointer currently 18262306a36Sopenharmony_ci * associated with the group (if there is one) and obtain a reference 18362306a36Sopenharmony_ci * now that will be held until the open_count reaches 0 again. Save 18462306a36Sopenharmony_ci * the pointer in the device for use by drivers. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci if (device->open_count == 0) 18762306a36Sopenharmony_ci vfio_device_group_get_kvm_safe(device); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci df->iommufd = device->group->iommufd; 19062306a36Sopenharmony_ci if (df->iommufd && vfio_device_is_noiommu(device) && device->open_count == 0) { 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * Require no compat ioas to be assigned to proceed. The basic 19362306a36Sopenharmony_ci * statement is that the user cannot have done something that 19462306a36Sopenharmony_ci * implies they expected translation to exist 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci if (!capable(CAP_SYS_RAWIO) || 19762306a36Sopenharmony_ci vfio_iommufd_device_has_compat_ioas(device, df->iommufd)) 19862306a36Sopenharmony_ci ret = -EPERM; 19962306a36Sopenharmony_ci else 20062306a36Sopenharmony_ci ret = 0; 20162306a36Sopenharmony_ci goto out_put_kvm; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ret = vfio_df_open(df); 20562306a36Sopenharmony_ci if (ret) 20662306a36Sopenharmony_ci goto out_put_kvm; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (df->iommufd && device->open_count == 1) { 20962306a36Sopenharmony_ci ret = vfio_iommufd_compat_attach_ioas(device, df->iommufd); 21062306a36Sopenharmony_ci if (ret) 21162306a36Sopenharmony_ci goto out_close_device; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * Paired with smp_load_acquire() in vfio_device_fops::ioctl/ 21662306a36Sopenharmony_ci * read/write/mmap and vfio_file_has_device_access() 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci smp_store_release(&df->access_granted, true); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci mutex_unlock(&device->dev_set->lock); 22162306a36Sopenharmony_ci mutex_unlock(&device->group->group_lock); 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ciout_close_device: 22562306a36Sopenharmony_ci vfio_df_close(df); 22662306a36Sopenharmony_ciout_put_kvm: 22762306a36Sopenharmony_ci df->iommufd = NULL; 22862306a36Sopenharmony_ci if (device->open_count == 0) 22962306a36Sopenharmony_ci vfio_device_put_kvm(device); 23062306a36Sopenharmony_ci mutex_unlock(&device->dev_set->lock); 23162306a36Sopenharmony_ciout_unlock: 23262306a36Sopenharmony_ci mutex_unlock(&device->group->group_lock); 23362306a36Sopenharmony_ci return ret; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_civoid vfio_df_group_close(struct vfio_device_file *df) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct vfio_device *device = df->device; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci mutex_lock(&device->group->group_lock); 24162306a36Sopenharmony_ci mutex_lock(&device->dev_set->lock); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci vfio_df_close(df); 24462306a36Sopenharmony_ci df->iommufd = NULL; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (device->open_count == 0) 24762306a36Sopenharmony_ci vfio_device_put_kvm(device); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci mutex_unlock(&device->dev_set->lock); 25062306a36Sopenharmony_ci mutex_unlock(&device->group->group_lock); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic struct file *vfio_device_open_file(struct vfio_device *device) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct vfio_device_file *df; 25662306a36Sopenharmony_ci struct file *filep; 25762306a36Sopenharmony_ci int ret; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci df = vfio_allocate_device_file(device); 26062306a36Sopenharmony_ci if (IS_ERR(df)) { 26162306a36Sopenharmony_ci ret = PTR_ERR(df); 26262306a36Sopenharmony_ci goto err_out; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci df->group = device->group; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ret = vfio_df_group_open(df); 26862306a36Sopenharmony_ci if (ret) 26962306a36Sopenharmony_ci goto err_free; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* 27262306a36Sopenharmony_ci * We can't use anon_inode_getfd() because we need to modify 27362306a36Sopenharmony_ci * the f_mode flags directly to allow more than just ioctls 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops, 27662306a36Sopenharmony_ci df, O_RDWR); 27762306a36Sopenharmony_ci if (IS_ERR(filep)) { 27862306a36Sopenharmony_ci ret = PTR_ERR(filep); 27962306a36Sopenharmony_ci goto err_close_device; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * TODO: add an anon_inode interface to do this. 28462306a36Sopenharmony_ci * Appears to be missing by lack of need rather than 28562306a36Sopenharmony_ci * explicitly prevented. Now there's need. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci filep->f_mode |= (FMODE_PREAD | FMODE_PWRITE); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (device->group->type == VFIO_NO_IOMMU) 29062306a36Sopenharmony_ci dev_warn(device->dev, "vfio-noiommu device opened by user " 29162306a36Sopenharmony_ci "(%s:%d)\n", current->comm, task_pid_nr(current)); 29262306a36Sopenharmony_ci /* 29362306a36Sopenharmony_ci * On success the ref of device is moved to the file and 29462306a36Sopenharmony_ci * put in vfio_device_fops_release() 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_ci return filep; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cierr_close_device: 29962306a36Sopenharmony_ci vfio_df_group_close(df); 30062306a36Sopenharmony_cierr_free: 30162306a36Sopenharmony_ci kfree(df); 30262306a36Sopenharmony_cierr_out: 30362306a36Sopenharmony_ci return ERR_PTR(ret); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int vfio_group_ioctl_get_device_fd(struct vfio_group *group, 30762306a36Sopenharmony_ci char __user *arg) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct vfio_device *device; 31062306a36Sopenharmony_ci struct file *filep; 31162306a36Sopenharmony_ci char *buf; 31262306a36Sopenharmony_ci int fdno; 31362306a36Sopenharmony_ci int ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci buf = strndup_user(arg, PAGE_SIZE); 31662306a36Sopenharmony_ci if (IS_ERR(buf)) 31762306a36Sopenharmony_ci return PTR_ERR(buf); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci device = vfio_device_get_from_name(group, buf); 32062306a36Sopenharmony_ci kfree(buf); 32162306a36Sopenharmony_ci if (IS_ERR(device)) 32262306a36Sopenharmony_ci return PTR_ERR(device); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci fdno = get_unused_fd_flags(O_CLOEXEC); 32562306a36Sopenharmony_ci if (fdno < 0) { 32662306a36Sopenharmony_ci ret = fdno; 32762306a36Sopenharmony_ci goto err_put_device; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci filep = vfio_device_open_file(device); 33162306a36Sopenharmony_ci if (IS_ERR(filep)) { 33262306a36Sopenharmony_ci ret = PTR_ERR(filep); 33362306a36Sopenharmony_ci goto err_put_fdno; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci fd_install(fdno, filep); 33762306a36Sopenharmony_ci return fdno; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cierr_put_fdno: 34062306a36Sopenharmony_ci put_unused_fd(fdno); 34162306a36Sopenharmony_cierr_put_device: 34262306a36Sopenharmony_ci vfio_device_put_registration(device); 34362306a36Sopenharmony_ci return ret; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int vfio_group_ioctl_get_status(struct vfio_group *group, 34762306a36Sopenharmony_ci struct vfio_group_status __user *arg) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci unsigned long minsz = offsetofend(struct vfio_group_status, flags); 35062306a36Sopenharmony_ci struct vfio_group_status status; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (copy_from_user(&status, arg, minsz)) 35362306a36Sopenharmony_ci return -EFAULT; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (status.argsz < minsz) 35662306a36Sopenharmony_ci return -EINVAL; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci status.flags = 0; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci mutex_lock(&group->group_lock); 36162306a36Sopenharmony_ci if (!group->iommu_group) { 36262306a36Sopenharmony_ci mutex_unlock(&group->group_lock); 36362306a36Sopenharmony_ci return -ENODEV; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* 36762306a36Sopenharmony_ci * With the container FD the iommu_group_claim_dma_owner() is done 36862306a36Sopenharmony_ci * during SET_CONTAINER but for IOMMFD this is done during 36962306a36Sopenharmony_ci * VFIO_GROUP_GET_DEVICE_FD. Meaning that with iommufd 37062306a36Sopenharmony_ci * VFIO_GROUP_FLAGS_VIABLE could be set but GET_DEVICE_FD will fail due 37162306a36Sopenharmony_ci * to viability. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci if (vfio_group_has_iommu(group)) 37462306a36Sopenharmony_ci status.flags |= VFIO_GROUP_FLAGS_CONTAINER_SET | 37562306a36Sopenharmony_ci VFIO_GROUP_FLAGS_VIABLE; 37662306a36Sopenharmony_ci else if (!iommu_group_dma_owner_claimed(group->iommu_group)) 37762306a36Sopenharmony_ci status.flags |= VFIO_GROUP_FLAGS_VIABLE; 37862306a36Sopenharmony_ci mutex_unlock(&group->group_lock); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (copy_to_user(arg, &status, minsz)) 38162306a36Sopenharmony_ci return -EFAULT; 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic long vfio_group_fops_unl_ioctl(struct file *filep, 38662306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct vfio_group *group = filep->private_data; 38962306a36Sopenharmony_ci void __user *uarg = (void __user *)arg; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci switch (cmd) { 39262306a36Sopenharmony_ci case VFIO_GROUP_GET_DEVICE_FD: 39362306a36Sopenharmony_ci return vfio_group_ioctl_get_device_fd(group, uarg); 39462306a36Sopenharmony_ci case VFIO_GROUP_GET_STATUS: 39562306a36Sopenharmony_ci return vfio_group_ioctl_get_status(group, uarg); 39662306a36Sopenharmony_ci case VFIO_GROUP_SET_CONTAINER: 39762306a36Sopenharmony_ci return vfio_group_ioctl_set_container(group, uarg); 39862306a36Sopenharmony_ci case VFIO_GROUP_UNSET_CONTAINER: 39962306a36Sopenharmony_ci return vfio_group_ioctl_unset_container(group); 40062306a36Sopenharmony_ci default: 40162306a36Sopenharmony_ci return -ENOTTY; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ciint vfio_device_block_group(struct vfio_device *device) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct vfio_group *group = device->group; 40862306a36Sopenharmony_ci int ret = 0; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci mutex_lock(&group->group_lock); 41162306a36Sopenharmony_ci if (group->opened_file) { 41262306a36Sopenharmony_ci ret = -EBUSY; 41362306a36Sopenharmony_ci goto out_unlock; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci group->cdev_device_open_cnt++; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ciout_unlock: 41962306a36Sopenharmony_ci mutex_unlock(&group->group_lock); 42062306a36Sopenharmony_ci return ret; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_civoid vfio_device_unblock_group(struct vfio_device *device) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct vfio_group *group = device->group; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci mutex_lock(&group->group_lock); 42862306a36Sopenharmony_ci group->cdev_device_open_cnt--; 42962306a36Sopenharmony_ci mutex_unlock(&group->group_lock); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int vfio_group_fops_open(struct inode *inode, struct file *filep) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct vfio_group *group = 43562306a36Sopenharmony_ci container_of(inode->i_cdev, struct vfio_group, cdev); 43662306a36Sopenharmony_ci int ret; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci mutex_lock(&group->group_lock); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* 44162306a36Sopenharmony_ci * drivers can be zero if this races with vfio_device_remove_group(), it 44262306a36Sopenharmony_ci * will be stable at 0 under the group rwsem 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci if (refcount_read(&group->drivers) == 0) { 44562306a36Sopenharmony_ci ret = -ENODEV; 44662306a36Sopenharmony_ci goto out_unlock; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (group->type == VFIO_NO_IOMMU && !capable(CAP_SYS_RAWIO)) { 45062306a36Sopenharmony_ci ret = -EPERM; 45162306a36Sopenharmony_ci goto out_unlock; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (group->cdev_device_open_cnt) { 45562306a36Sopenharmony_ci ret = -EBUSY; 45662306a36Sopenharmony_ci goto out_unlock; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* 46062306a36Sopenharmony_ci * Do we need multiple instances of the group open? Seems not. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ci if (group->opened_file) { 46362306a36Sopenharmony_ci ret = -EBUSY; 46462306a36Sopenharmony_ci goto out_unlock; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci group->opened_file = filep; 46762306a36Sopenharmony_ci filep->private_data = group; 46862306a36Sopenharmony_ci ret = 0; 46962306a36Sopenharmony_ciout_unlock: 47062306a36Sopenharmony_ci mutex_unlock(&group->group_lock); 47162306a36Sopenharmony_ci return ret; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int vfio_group_fops_release(struct inode *inode, struct file *filep) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct vfio_group *group = filep->private_data; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci filep->private_data = NULL; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci mutex_lock(&group->group_lock); 48162306a36Sopenharmony_ci /* 48262306a36Sopenharmony_ci * Device FDs hold a group file reference, therefore the group release 48362306a36Sopenharmony_ci * is only called when there are no open devices. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_ci WARN_ON(group->notifier.head); 48662306a36Sopenharmony_ci if (group->container) 48762306a36Sopenharmony_ci vfio_group_detach_container(group); 48862306a36Sopenharmony_ci if (group->iommufd) { 48962306a36Sopenharmony_ci iommufd_ctx_put(group->iommufd); 49062306a36Sopenharmony_ci group->iommufd = NULL; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci group->opened_file = NULL; 49362306a36Sopenharmony_ci mutex_unlock(&group->group_lock); 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic const struct file_operations vfio_group_fops = { 49862306a36Sopenharmony_ci .owner = THIS_MODULE, 49962306a36Sopenharmony_ci .unlocked_ioctl = vfio_group_fops_unl_ioctl, 50062306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 50162306a36Sopenharmony_ci .open = vfio_group_fops_open, 50262306a36Sopenharmony_ci .release = vfio_group_fops_release, 50362306a36Sopenharmony_ci}; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/* 50662306a36Sopenharmony_ci * Group objects - create, release, get, put, search 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_cistatic struct vfio_group * 50962306a36Sopenharmony_civfio_group_find_from_iommu(struct iommu_group *iommu_group) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct vfio_group *group; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci lockdep_assert_held(&vfio.group_lock); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* 51662306a36Sopenharmony_ci * group->iommu_group from the vfio.group_list cannot be NULL 51762306a36Sopenharmony_ci * under the vfio.group_lock. 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_ci list_for_each_entry(group, &vfio.group_list, vfio_next) { 52062306a36Sopenharmony_ci if (group->iommu_group == iommu_group) 52162306a36Sopenharmony_ci return group; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci return NULL; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic void vfio_group_release(struct device *dev) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct vfio_group *group = container_of(dev, struct vfio_group, dev); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci mutex_destroy(&group->device_lock); 53162306a36Sopenharmony_ci mutex_destroy(&group->group_lock); 53262306a36Sopenharmony_ci WARN_ON(group->iommu_group); 53362306a36Sopenharmony_ci WARN_ON(group->cdev_device_open_cnt); 53462306a36Sopenharmony_ci ida_free(&vfio.group_ida, MINOR(group->dev.devt)); 53562306a36Sopenharmony_ci kfree(group); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic struct vfio_group *vfio_group_alloc(struct iommu_group *iommu_group, 53962306a36Sopenharmony_ci enum vfio_group_type type) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct vfio_group *group; 54262306a36Sopenharmony_ci int minor; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci group = kzalloc(sizeof(*group), GFP_KERNEL); 54562306a36Sopenharmony_ci if (!group) 54662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci minor = ida_alloc_max(&vfio.group_ida, MINORMASK, GFP_KERNEL); 54962306a36Sopenharmony_ci if (minor < 0) { 55062306a36Sopenharmony_ci kfree(group); 55162306a36Sopenharmony_ci return ERR_PTR(minor); 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci device_initialize(&group->dev); 55562306a36Sopenharmony_ci group->dev.devt = MKDEV(MAJOR(vfio.group_devt), minor); 55662306a36Sopenharmony_ci group->dev.class = vfio.class; 55762306a36Sopenharmony_ci group->dev.release = vfio_group_release; 55862306a36Sopenharmony_ci cdev_init(&group->cdev, &vfio_group_fops); 55962306a36Sopenharmony_ci group->cdev.owner = THIS_MODULE; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci refcount_set(&group->drivers, 1); 56262306a36Sopenharmony_ci mutex_init(&group->group_lock); 56362306a36Sopenharmony_ci spin_lock_init(&group->kvm_ref_lock); 56462306a36Sopenharmony_ci INIT_LIST_HEAD(&group->device_list); 56562306a36Sopenharmony_ci mutex_init(&group->device_lock); 56662306a36Sopenharmony_ci group->iommu_group = iommu_group; 56762306a36Sopenharmony_ci /* put in vfio_group_release() */ 56862306a36Sopenharmony_ci iommu_group_ref_get(iommu_group); 56962306a36Sopenharmony_ci group->type = type; 57062306a36Sopenharmony_ci BLOCKING_INIT_NOTIFIER_HEAD(&group->notifier); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return group; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic struct vfio_group *vfio_create_group(struct iommu_group *iommu_group, 57662306a36Sopenharmony_ci enum vfio_group_type type) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct vfio_group *group; 57962306a36Sopenharmony_ci struct vfio_group *ret; 58062306a36Sopenharmony_ci int err; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci lockdep_assert_held(&vfio.group_lock); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci group = vfio_group_alloc(iommu_group, type); 58562306a36Sopenharmony_ci if (IS_ERR(group)) 58662306a36Sopenharmony_ci return group; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci err = dev_set_name(&group->dev, "%s%d", 58962306a36Sopenharmony_ci group->type == VFIO_NO_IOMMU ? "noiommu-" : "", 59062306a36Sopenharmony_ci iommu_group_id(iommu_group)); 59162306a36Sopenharmony_ci if (err) { 59262306a36Sopenharmony_ci ret = ERR_PTR(err); 59362306a36Sopenharmony_ci goto err_put; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci err = cdev_device_add(&group->cdev, &group->dev); 59762306a36Sopenharmony_ci if (err) { 59862306a36Sopenharmony_ci ret = ERR_PTR(err); 59962306a36Sopenharmony_ci goto err_put; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci list_add(&group->vfio_next, &vfio.group_list); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return group; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cierr_put: 60762306a36Sopenharmony_ci put_device(&group->dev); 60862306a36Sopenharmony_ci return ret; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic struct vfio_group *vfio_noiommu_group_alloc(struct device *dev, 61262306a36Sopenharmony_ci enum vfio_group_type type) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct iommu_group *iommu_group; 61562306a36Sopenharmony_ci struct vfio_group *group; 61662306a36Sopenharmony_ci int ret; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci iommu_group = iommu_group_alloc(); 61962306a36Sopenharmony_ci if (IS_ERR(iommu_group)) 62062306a36Sopenharmony_ci return ERR_CAST(iommu_group); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci ret = iommu_group_set_name(iommu_group, "vfio-noiommu"); 62362306a36Sopenharmony_ci if (ret) 62462306a36Sopenharmony_ci goto out_put_group; 62562306a36Sopenharmony_ci ret = iommu_group_add_device(iommu_group, dev); 62662306a36Sopenharmony_ci if (ret) 62762306a36Sopenharmony_ci goto out_put_group; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci mutex_lock(&vfio.group_lock); 63062306a36Sopenharmony_ci group = vfio_create_group(iommu_group, type); 63162306a36Sopenharmony_ci mutex_unlock(&vfio.group_lock); 63262306a36Sopenharmony_ci if (IS_ERR(group)) { 63362306a36Sopenharmony_ci ret = PTR_ERR(group); 63462306a36Sopenharmony_ci goto out_remove_device; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci iommu_group_put(iommu_group); 63762306a36Sopenharmony_ci return group; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciout_remove_device: 64062306a36Sopenharmony_ci iommu_group_remove_device(dev); 64162306a36Sopenharmony_ciout_put_group: 64262306a36Sopenharmony_ci iommu_group_put(iommu_group); 64362306a36Sopenharmony_ci return ERR_PTR(ret); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic bool vfio_group_has_device(struct vfio_group *group, struct device *dev) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct vfio_device *device; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci mutex_lock(&group->device_lock); 65162306a36Sopenharmony_ci list_for_each_entry(device, &group->device_list, group_next) { 65262306a36Sopenharmony_ci if (device->dev == dev) { 65362306a36Sopenharmony_ci mutex_unlock(&group->device_lock); 65462306a36Sopenharmony_ci return true; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci mutex_unlock(&group->device_lock); 65862306a36Sopenharmony_ci return false; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic struct vfio_group *vfio_group_find_or_alloc(struct device *dev) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct iommu_group *iommu_group; 66462306a36Sopenharmony_ci struct vfio_group *group; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci iommu_group = iommu_group_get(dev); 66762306a36Sopenharmony_ci if (!iommu_group && vfio_noiommu) { 66862306a36Sopenharmony_ci /* 66962306a36Sopenharmony_ci * With noiommu enabled, create an IOMMU group for devices that 67062306a36Sopenharmony_ci * don't already have one, implying no IOMMU hardware/driver 67162306a36Sopenharmony_ci * exists. Taint the kernel because we're about to give a DMA 67262306a36Sopenharmony_ci * capable device to a user without IOMMU protection. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ci group = vfio_noiommu_group_alloc(dev, VFIO_NO_IOMMU); 67562306a36Sopenharmony_ci if (!IS_ERR(group)) { 67662306a36Sopenharmony_ci add_taint(TAINT_USER, LOCKDEP_STILL_OK); 67762306a36Sopenharmony_ci dev_warn(dev, "Adding kernel taint for vfio-noiommu group on device\n"); 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci return group; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (!iommu_group) 68362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci mutex_lock(&vfio.group_lock); 68662306a36Sopenharmony_ci group = vfio_group_find_from_iommu(iommu_group); 68762306a36Sopenharmony_ci if (group) { 68862306a36Sopenharmony_ci if (WARN_ON(vfio_group_has_device(group, dev))) 68962306a36Sopenharmony_ci group = ERR_PTR(-EINVAL); 69062306a36Sopenharmony_ci else 69162306a36Sopenharmony_ci refcount_inc(&group->drivers); 69262306a36Sopenharmony_ci } else { 69362306a36Sopenharmony_ci group = vfio_create_group(iommu_group, VFIO_IOMMU); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci mutex_unlock(&vfio.group_lock); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* The vfio_group holds a reference to the iommu_group */ 69862306a36Sopenharmony_ci iommu_group_put(iommu_group); 69962306a36Sopenharmony_ci return group; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ciint vfio_device_set_group(struct vfio_device *device, 70362306a36Sopenharmony_ci enum vfio_group_type type) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct vfio_group *group; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (type == VFIO_IOMMU) 70862306a36Sopenharmony_ci group = vfio_group_find_or_alloc(device->dev); 70962306a36Sopenharmony_ci else 71062306a36Sopenharmony_ci group = vfio_noiommu_group_alloc(device->dev, type); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (IS_ERR(group)) 71362306a36Sopenharmony_ci return PTR_ERR(group); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* Our reference on group is moved to the device */ 71662306a36Sopenharmony_ci device->group = group; 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_civoid vfio_device_remove_group(struct vfio_device *device) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct vfio_group *group = device->group; 72362306a36Sopenharmony_ci struct iommu_group *iommu_group; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (group->type == VFIO_NO_IOMMU || group->type == VFIO_EMULATED_IOMMU) 72662306a36Sopenharmony_ci iommu_group_remove_device(device->dev); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Pairs with vfio_create_group() / vfio_group_get_from_iommu() */ 72962306a36Sopenharmony_ci if (!refcount_dec_and_mutex_lock(&group->drivers, &vfio.group_lock)) 73062306a36Sopenharmony_ci return; 73162306a36Sopenharmony_ci list_del(&group->vfio_next); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* 73462306a36Sopenharmony_ci * We could concurrently probe another driver in the group that might 73562306a36Sopenharmony_ci * race vfio_device_remove_group() with vfio_get_group(), so we have to 73662306a36Sopenharmony_ci * ensure that the sysfs is all cleaned up under lock otherwise the 73762306a36Sopenharmony_ci * cdev_device_add() will fail due to the name aready existing. 73862306a36Sopenharmony_ci */ 73962306a36Sopenharmony_ci cdev_device_del(&group->cdev, &group->dev); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci mutex_lock(&group->group_lock); 74262306a36Sopenharmony_ci /* 74362306a36Sopenharmony_ci * These data structures all have paired operations that can only be 74462306a36Sopenharmony_ci * undone when the caller holds a live reference on the device. Since 74562306a36Sopenharmony_ci * all pairs must be undone these WARN_ON's indicate some caller did not 74662306a36Sopenharmony_ci * properly hold the group reference. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_ci WARN_ON(!list_empty(&group->device_list)); 74962306a36Sopenharmony_ci WARN_ON(group->notifier.head); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* 75262306a36Sopenharmony_ci * Revoke all users of group->iommu_group. At this point we know there 75362306a36Sopenharmony_ci * are no devices active because we are unplugging the last one. Setting 75462306a36Sopenharmony_ci * iommu_group to NULL blocks all new users. 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_ci if (group->container) 75762306a36Sopenharmony_ci vfio_group_detach_container(group); 75862306a36Sopenharmony_ci iommu_group = group->iommu_group; 75962306a36Sopenharmony_ci group->iommu_group = NULL; 76062306a36Sopenharmony_ci mutex_unlock(&group->group_lock); 76162306a36Sopenharmony_ci mutex_unlock(&vfio.group_lock); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci iommu_group_put(iommu_group); 76462306a36Sopenharmony_ci put_device(&group->dev); 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_civoid vfio_device_group_register(struct vfio_device *device) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci mutex_lock(&device->group->device_lock); 77062306a36Sopenharmony_ci list_add(&device->group_next, &device->group->device_list); 77162306a36Sopenharmony_ci mutex_unlock(&device->group->device_lock); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_civoid vfio_device_group_unregister(struct vfio_device *device) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci mutex_lock(&device->group->device_lock); 77762306a36Sopenharmony_ci list_del(&device->group_next); 77862306a36Sopenharmony_ci mutex_unlock(&device->group->device_lock); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ciint vfio_device_group_use_iommu(struct vfio_device *device) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct vfio_group *group = device->group; 78462306a36Sopenharmony_ci int ret = 0; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci lockdep_assert_held(&group->group_lock); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (WARN_ON(!group->container)) 78962306a36Sopenharmony_ci return -EINVAL; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci ret = vfio_group_use_container(group); 79262306a36Sopenharmony_ci if (ret) 79362306a36Sopenharmony_ci return ret; 79462306a36Sopenharmony_ci vfio_device_container_register(device); 79562306a36Sopenharmony_ci return 0; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_civoid vfio_device_group_unuse_iommu(struct vfio_device *device) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct vfio_group *group = device->group; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci lockdep_assert_held(&group->group_lock); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (WARN_ON(!group->container)) 80562306a36Sopenharmony_ci return; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci vfio_device_container_unregister(device); 80862306a36Sopenharmony_ci vfio_group_unuse_container(group); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cibool vfio_device_has_container(struct vfio_device *device) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci return device->group->container; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistruct vfio_group *vfio_group_from_file(struct file *file) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct vfio_group *group = file->private_data; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (file->f_op != &vfio_group_fops) 82162306a36Sopenharmony_ci return NULL; 82262306a36Sopenharmony_ci return group; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci/** 82662306a36Sopenharmony_ci * vfio_file_iommu_group - Return the struct iommu_group for the vfio group file 82762306a36Sopenharmony_ci * @file: VFIO group file 82862306a36Sopenharmony_ci * 82962306a36Sopenharmony_ci * The returned iommu_group is valid as long as a ref is held on the file. This 83062306a36Sopenharmony_ci * returns a reference on the group. This function is deprecated, only the SPAPR 83162306a36Sopenharmony_ci * path in kvm should call it. 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_cistruct iommu_group *vfio_file_iommu_group(struct file *file) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct vfio_group *group = vfio_group_from_file(file); 83662306a36Sopenharmony_ci struct iommu_group *iommu_group = NULL; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SPAPR_TCE_IOMMU)) 83962306a36Sopenharmony_ci return NULL; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (!group) 84262306a36Sopenharmony_ci return NULL; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci mutex_lock(&group->group_lock); 84562306a36Sopenharmony_ci if (group->iommu_group) { 84662306a36Sopenharmony_ci iommu_group = group->iommu_group; 84762306a36Sopenharmony_ci iommu_group_ref_get(iommu_group); 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci mutex_unlock(&group->group_lock); 85062306a36Sopenharmony_ci return iommu_group; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vfio_file_iommu_group); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/** 85562306a36Sopenharmony_ci * vfio_file_is_group - True if the file is a vfio group file 85662306a36Sopenharmony_ci * @file: VFIO group file 85762306a36Sopenharmony_ci */ 85862306a36Sopenharmony_cibool vfio_file_is_group(struct file *file) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci return vfio_group_from_file(file); 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vfio_file_is_group); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cibool vfio_group_enforced_coherent(struct vfio_group *group) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct vfio_device *device; 86762306a36Sopenharmony_ci bool ret = true; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci /* 87062306a36Sopenharmony_ci * If the device does not have IOMMU_CAP_ENFORCE_CACHE_COHERENCY then 87162306a36Sopenharmony_ci * any domain later attached to it will also not support it. If the cap 87262306a36Sopenharmony_ci * is set then the iommu_domain eventually attached to the device/group 87362306a36Sopenharmony_ci * must use a domain with enforce_cache_coherency(). 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_ci mutex_lock(&group->device_lock); 87662306a36Sopenharmony_ci list_for_each_entry(device, &group->device_list, group_next) { 87762306a36Sopenharmony_ci if (!device_iommu_capable(device->dev, 87862306a36Sopenharmony_ci IOMMU_CAP_ENFORCE_CACHE_COHERENCY)) { 87962306a36Sopenharmony_ci ret = false; 88062306a36Sopenharmony_ci break; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci mutex_unlock(&group->device_lock); 88462306a36Sopenharmony_ci return ret; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_civoid vfio_group_set_kvm(struct vfio_group *group, struct kvm *kvm) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci spin_lock(&group->kvm_ref_lock); 89062306a36Sopenharmony_ci group->kvm = kvm; 89162306a36Sopenharmony_ci spin_unlock(&group->kvm_ref_lock); 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci/** 89562306a36Sopenharmony_ci * vfio_file_has_dev - True if the VFIO file is a handle for device 89662306a36Sopenharmony_ci * @file: VFIO file to check 89762306a36Sopenharmony_ci * @device: Device that must be part of the file 89862306a36Sopenharmony_ci * 89962306a36Sopenharmony_ci * Returns true if given file has permission to manipulate the given device. 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_cibool vfio_file_has_dev(struct file *file, struct vfio_device *device) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci struct vfio_group *group = vfio_group_from_file(file); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (!group) 90662306a36Sopenharmony_ci return false; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci return group == device->group; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vfio_file_has_dev); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic char *vfio_devnode(const struct device *dev, umode_t *mode) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "vfio/%s", dev_name(dev)); 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ciint __init vfio_group_init(void) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci int ret; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci ida_init(&vfio.group_ida); 92262306a36Sopenharmony_ci mutex_init(&vfio.group_lock); 92362306a36Sopenharmony_ci INIT_LIST_HEAD(&vfio.group_list); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci ret = vfio_container_init(); 92662306a36Sopenharmony_ci if (ret) 92762306a36Sopenharmony_ci return ret; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* /dev/vfio/$GROUP */ 93062306a36Sopenharmony_ci vfio.class = class_create("vfio"); 93162306a36Sopenharmony_ci if (IS_ERR(vfio.class)) { 93262306a36Sopenharmony_ci ret = PTR_ERR(vfio.class); 93362306a36Sopenharmony_ci goto err_group_class; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci vfio.class->devnode = vfio_devnode; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci ret = alloc_chrdev_region(&vfio.group_devt, 0, MINORMASK + 1, "vfio"); 93962306a36Sopenharmony_ci if (ret) 94062306a36Sopenharmony_ci goto err_alloc_chrdev; 94162306a36Sopenharmony_ci return 0; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cierr_alloc_chrdev: 94462306a36Sopenharmony_ci class_destroy(vfio.class); 94562306a36Sopenharmony_ci vfio.class = NULL; 94662306a36Sopenharmony_cierr_group_class: 94762306a36Sopenharmony_ci vfio_container_cleanup(); 94862306a36Sopenharmony_ci return ret; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_civoid vfio_group_cleanup(void) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci WARN_ON(!list_empty(&vfio.group_list)); 95462306a36Sopenharmony_ci ida_destroy(&vfio.group_ida); 95562306a36Sopenharmony_ci unregister_chrdev_region(vfio.group_devt, MINORMASK + 1); 95662306a36Sopenharmony_ci class_destroy(vfio.class); 95762306a36Sopenharmony_ci vfio.class = NULL; 95862306a36Sopenharmony_ci vfio_container_cleanup(); 95962306a36Sopenharmony_ci} 960