162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * VFIO container (/dev/vfio/vfio) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/file.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/capability.h> 1162306a36Sopenharmony_ci#include <linux/iommu.h> 1262306a36Sopenharmony_ci#include <linux/miscdevice.h> 1362306a36Sopenharmony_ci#include <linux/vfio.h> 1462306a36Sopenharmony_ci#include <uapi/linux/vfio.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "vfio.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct vfio_container { 1962306a36Sopenharmony_ci struct kref kref; 2062306a36Sopenharmony_ci struct list_head group_list; 2162306a36Sopenharmony_ci struct rw_semaphore group_lock; 2262306a36Sopenharmony_ci struct vfio_iommu_driver *iommu_driver; 2362306a36Sopenharmony_ci void *iommu_data; 2462306a36Sopenharmony_ci bool noiommu; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic struct vfio { 2862306a36Sopenharmony_ci struct list_head iommu_drivers_list; 2962306a36Sopenharmony_ci struct mutex iommu_drivers_lock; 3062306a36Sopenharmony_ci} vfio; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void *vfio_noiommu_open(unsigned long arg) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci if (arg != VFIO_NOIOMMU_IOMMU) 3562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 3662306a36Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 3762306a36Sopenharmony_ci return ERR_PTR(-EPERM); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci return NULL; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void vfio_noiommu_release(void *iommu_data) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic long vfio_noiommu_ioctl(void *iommu_data, 4762306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci if (cmd == VFIO_CHECK_EXTENSION) 5062306a36Sopenharmony_ci return vfio_noiommu && (arg == VFIO_NOIOMMU_IOMMU) ? 1 : 0; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return -ENOTTY; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int vfio_noiommu_attach_group(void *iommu_data, 5662306a36Sopenharmony_ci struct iommu_group *iommu_group, enum vfio_group_type type) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void vfio_noiommu_detach_group(void *iommu_data, 6262306a36Sopenharmony_ci struct iommu_group *iommu_group) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic const struct vfio_iommu_driver_ops vfio_noiommu_ops = { 6762306a36Sopenharmony_ci .name = "vfio-noiommu", 6862306a36Sopenharmony_ci .owner = THIS_MODULE, 6962306a36Sopenharmony_ci .open = vfio_noiommu_open, 7062306a36Sopenharmony_ci .release = vfio_noiommu_release, 7162306a36Sopenharmony_ci .ioctl = vfio_noiommu_ioctl, 7262306a36Sopenharmony_ci .attach_group = vfio_noiommu_attach_group, 7362306a36Sopenharmony_ci .detach_group = vfio_noiommu_detach_group, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * Only noiommu containers can use vfio-noiommu and noiommu containers can only 7862306a36Sopenharmony_ci * use vfio-noiommu. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic bool vfio_iommu_driver_allowed(struct vfio_container *container, 8162306a36Sopenharmony_ci const struct vfio_iommu_driver *driver) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_VFIO_NOIOMMU)) 8462306a36Sopenharmony_ci return true; 8562306a36Sopenharmony_ci return container->noiommu == (driver->ops == &vfio_noiommu_ops); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * IOMMU driver registration 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ciint vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct vfio_iommu_driver *driver, *tmp; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (WARN_ON(!ops->register_device != !ops->unregister_device)) 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci driver = kzalloc(sizeof(*driver), GFP_KERNEL); 9962306a36Sopenharmony_ci if (!driver) 10062306a36Sopenharmony_ci return -ENOMEM; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci driver->ops = ops; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci mutex_lock(&vfio.iommu_drivers_lock); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Check for duplicates */ 10762306a36Sopenharmony_ci list_for_each_entry(tmp, &vfio.iommu_drivers_list, vfio_next) { 10862306a36Sopenharmony_ci if (tmp->ops == ops) { 10962306a36Sopenharmony_ci mutex_unlock(&vfio.iommu_drivers_lock); 11062306a36Sopenharmony_ci kfree(driver); 11162306a36Sopenharmony_ci return -EINVAL; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci list_add(&driver->vfio_next, &vfio.iommu_drivers_list); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci mutex_unlock(&vfio.iommu_drivers_lock); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vfio_register_iommu_driver); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_civoid vfio_unregister_iommu_driver(const struct vfio_iommu_driver_ops *ops) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct vfio_iommu_driver *driver; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci mutex_lock(&vfio.iommu_drivers_lock); 12862306a36Sopenharmony_ci list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) { 12962306a36Sopenharmony_ci if (driver->ops == ops) { 13062306a36Sopenharmony_ci list_del(&driver->vfio_next); 13162306a36Sopenharmony_ci mutex_unlock(&vfio.iommu_drivers_lock); 13262306a36Sopenharmony_ci kfree(driver); 13362306a36Sopenharmony_ci return; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci mutex_unlock(&vfio.iommu_drivers_lock); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vfio_unregister_iommu_driver); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* 14162306a36Sopenharmony_ci * Container objects - containers are created when /dev/vfio/vfio is 14262306a36Sopenharmony_ci * opened, but their lifecycle extends until the last user is done, so 14362306a36Sopenharmony_ci * it's freed via kref. Must support container/group/device being 14462306a36Sopenharmony_ci * closed in any order. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistatic void vfio_container_release(struct kref *kref) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct vfio_container *container; 14962306a36Sopenharmony_ci container = container_of(kref, struct vfio_container, kref); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci kfree(container); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void vfio_container_get(struct vfio_container *container) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci kref_get(&container->kref); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void vfio_container_put(struct vfio_container *container) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci kref_put(&container->kref, vfio_container_release); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_civoid vfio_device_container_register(struct vfio_device *device) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct vfio_iommu_driver *iommu_driver = 16762306a36Sopenharmony_ci device->group->container->iommu_driver; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (iommu_driver && iommu_driver->ops->register_device) 17062306a36Sopenharmony_ci iommu_driver->ops->register_device( 17162306a36Sopenharmony_ci device->group->container->iommu_data, device); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_civoid vfio_device_container_unregister(struct vfio_device *device) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct vfio_iommu_driver *iommu_driver = 17762306a36Sopenharmony_ci device->group->container->iommu_driver; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (iommu_driver && iommu_driver->ops->unregister_device) 18062306a36Sopenharmony_ci iommu_driver->ops->unregister_device( 18162306a36Sopenharmony_ci device->group->container->iommu_data, device); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic long 18562306a36Sopenharmony_civfio_container_ioctl_check_extension(struct vfio_container *container, 18662306a36Sopenharmony_ci unsigned long arg) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct vfio_iommu_driver *driver; 18962306a36Sopenharmony_ci long ret = 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci down_read(&container->group_lock); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci driver = container->iommu_driver; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci switch (arg) { 19662306a36Sopenharmony_ci /* No base extensions yet */ 19762306a36Sopenharmony_ci default: 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * If no driver is set, poll all registered drivers for 20062306a36Sopenharmony_ci * extensions and return the first positive result. If 20162306a36Sopenharmony_ci * a driver is already set, further queries will be passed 20262306a36Sopenharmony_ci * only to that driver. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci if (!driver) { 20562306a36Sopenharmony_ci mutex_lock(&vfio.iommu_drivers_lock); 20662306a36Sopenharmony_ci list_for_each_entry(driver, &vfio.iommu_drivers_list, 20762306a36Sopenharmony_ci vfio_next) { 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!list_empty(&container->group_list) && 21062306a36Sopenharmony_ci !vfio_iommu_driver_allowed(container, 21162306a36Sopenharmony_ci driver)) 21262306a36Sopenharmony_ci continue; 21362306a36Sopenharmony_ci if (!try_module_get(driver->ops->owner)) 21462306a36Sopenharmony_ci continue; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = driver->ops->ioctl(NULL, 21762306a36Sopenharmony_ci VFIO_CHECK_EXTENSION, 21862306a36Sopenharmony_ci arg); 21962306a36Sopenharmony_ci module_put(driver->ops->owner); 22062306a36Sopenharmony_ci if (ret > 0) 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci mutex_unlock(&vfio.iommu_drivers_lock); 22462306a36Sopenharmony_ci } else 22562306a36Sopenharmony_ci ret = driver->ops->ioctl(container->iommu_data, 22662306a36Sopenharmony_ci VFIO_CHECK_EXTENSION, arg); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci up_read(&container->group_lock); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return ret; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* hold write lock on container->group_lock */ 23562306a36Sopenharmony_cistatic int __vfio_container_attach_groups(struct vfio_container *container, 23662306a36Sopenharmony_ci struct vfio_iommu_driver *driver, 23762306a36Sopenharmony_ci void *data) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct vfio_group *group; 24062306a36Sopenharmony_ci int ret = -ENODEV; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci list_for_each_entry(group, &container->group_list, container_next) { 24362306a36Sopenharmony_ci ret = driver->ops->attach_group(data, group->iommu_group, 24462306a36Sopenharmony_ci group->type); 24562306a36Sopenharmony_ci if (ret) 24662306a36Sopenharmony_ci goto unwind; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return ret; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ciunwind: 25262306a36Sopenharmony_ci list_for_each_entry_continue_reverse(group, &container->group_list, 25362306a36Sopenharmony_ci container_next) { 25462306a36Sopenharmony_ci driver->ops->detach_group(data, group->iommu_group); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return ret; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic long vfio_ioctl_set_iommu(struct vfio_container *container, 26162306a36Sopenharmony_ci unsigned long arg) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct vfio_iommu_driver *driver; 26462306a36Sopenharmony_ci long ret = -ENODEV; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci down_write(&container->group_lock); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * The container is designed to be an unprivileged interface while 27062306a36Sopenharmony_ci * the group can be assigned to specific users. Therefore, only by 27162306a36Sopenharmony_ci * adding a group to a container does the user get the privilege of 27262306a36Sopenharmony_ci * enabling the iommu, which may allocate finite resources. There 27362306a36Sopenharmony_ci * is no unset_iommu, but by removing all the groups from a container, 27462306a36Sopenharmony_ci * the container is deprivileged and returns to an unset state. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci if (list_empty(&container->group_list) || container->iommu_driver) { 27762306a36Sopenharmony_ci up_write(&container->group_lock); 27862306a36Sopenharmony_ci return -EINVAL; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci mutex_lock(&vfio.iommu_drivers_lock); 28262306a36Sopenharmony_ci list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) { 28362306a36Sopenharmony_ci void *data; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (!vfio_iommu_driver_allowed(container, driver)) 28662306a36Sopenharmony_ci continue; 28762306a36Sopenharmony_ci if (!try_module_get(driver->ops->owner)) 28862306a36Sopenharmony_ci continue; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * The arg magic for SET_IOMMU is the same as CHECK_EXTENSION, 29262306a36Sopenharmony_ci * so test which iommu driver reported support for this 29362306a36Sopenharmony_ci * extension and call open on them. We also pass them the 29462306a36Sopenharmony_ci * magic, allowing a single driver to support multiple 29562306a36Sopenharmony_ci * interfaces if they'd like. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci if (driver->ops->ioctl(NULL, VFIO_CHECK_EXTENSION, arg) <= 0) { 29862306a36Sopenharmony_ci module_put(driver->ops->owner); 29962306a36Sopenharmony_ci continue; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci data = driver->ops->open(arg); 30362306a36Sopenharmony_ci if (IS_ERR(data)) { 30462306a36Sopenharmony_ci ret = PTR_ERR(data); 30562306a36Sopenharmony_ci module_put(driver->ops->owner); 30662306a36Sopenharmony_ci continue; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ret = __vfio_container_attach_groups(container, driver, data); 31062306a36Sopenharmony_ci if (ret) { 31162306a36Sopenharmony_ci driver->ops->release(data); 31262306a36Sopenharmony_ci module_put(driver->ops->owner); 31362306a36Sopenharmony_ci continue; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci container->iommu_driver = driver; 31762306a36Sopenharmony_ci container->iommu_data = data; 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci mutex_unlock(&vfio.iommu_drivers_lock); 32262306a36Sopenharmony_ci up_write(&container->group_lock); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return ret; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic long vfio_fops_unl_ioctl(struct file *filep, 32862306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct vfio_container *container = filep->private_data; 33162306a36Sopenharmony_ci struct vfio_iommu_driver *driver; 33262306a36Sopenharmony_ci void *data; 33362306a36Sopenharmony_ci long ret = -EINVAL; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (!container) 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci switch (cmd) { 33962306a36Sopenharmony_ci case VFIO_GET_API_VERSION: 34062306a36Sopenharmony_ci ret = VFIO_API_VERSION; 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci case VFIO_CHECK_EXTENSION: 34362306a36Sopenharmony_ci ret = vfio_container_ioctl_check_extension(container, arg); 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci case VFIO_SET_IOMMU: 34662306a36Sopenharmony_ci ret = vfio_ioctl_set_iommu(container, arg); 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci default: 34962306a36Sopenharmony_ci driver = container->iommu_driver; 35062306a36Sopenharmony_ci data = container->iommu_data; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (driver) /* passthrough all unrecognized ioctls */ 35362306a36Sopenharmony_ci ret = driver->ops->ioctl(data, cmd, arg); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return ret; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int vfio_fops_open(struct inode *inode, struct file *filep) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct vfio_container *container; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci container = kzalloc(sizeof(*container), GFP_KERNEL_ACCOUNT); 36462306a36Sopenharmony_ci if (!container) 36562306a36Sopenharmony_ci return -ENOMEM; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci INIT_LIST_HEAD(&container->group_list); 36862306a36Sopenharmony_ci init_rwsem(&container->group_lock); 36962306a36Sopenharmony_ci kref_init(&container->kref); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci filep->private_data = container; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int vfio_fops_release(struct inode *inode, struct file *filep) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct vfio_container *container = filep->private_data; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci filep->private_data = NULL; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci vfio_container_put(container); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic const struct file_operations vfio_fops = { 38862306a36Sopenharmony_ci .owner = THIS_MODULE, 38962306a36Sopenharmony_ci .open = vfio_fops_open, 39062306a36Sopenharmony_ci .release = vfio_fops_release, 39162306a36Sopenharmony_ci .unlocked_ioctl = vfio_fops_unl_ioctl, 39262306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 39362306a36Sopenharmony_ci}; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistruct vfio_container *vfio_container_from_file(struct file *file) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct vfio_container *container; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Sanity check, is this really our fd? */ 40062306a36Sopenharmony_ci if (file->f_op != &vfio_fops) 40162306a36Sopenharmony_ci return NULL; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci container = file->private_data; 40462306a36Sopenharmony_ci WARN_ON(!container); /* fget ensures we don't race vfio_release */ 40562306a36Sopenharmony_ci return container; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic struct miscdevice vfio_dev = { 40962306a36Sopenharmony_ci .minor = VFIO_MINOR, 41062306a36Sopenharmony_ci .name = "vfio", 41162306a36Sopenharmony_ci .fops = &vfio_fops, 41262306a36Sopenharmony_ci .nodename = "vfio/vfio", 41362306a36Sopenharmony_ci .mode = S_IRUGO | S_IWUGO, 41462306a36Sopenharmony_ci}; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ciint vfio_container_attach_group(struct vfio_container *container, 41762306a36Sopenharmony_ci struct vfio_group *group) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct vfio_iommu_driver *driver; 42062306a36Sopenharmony_ci int ret = 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci lockdep_assert_held(&group->group_lock); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (group->type == VFIO_NO_IOMMU && !capable(CAP_SYS_RAWIO)) 42562306a36Sopenharmony_ci return -EPERM; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci down_write(&container->group_lock); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Real groups and fake groups cannot mix */ 43062306a36Sopenharmony_ci if (!list_empty(&container->group_list) && 43162306a36Sopenharmony_ci container->noiommu != (group->type == VFIO_NO_IOMMU)) { 43262306a36Sopenharmony_ci ret = -EPERM; 43362306a36Sopenharmony_ci goto out_unlock_container; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (group->type == VFIO_IOMMU) { 43762306a36Sopenharmony_ci ret = iommu_group_claim_dma_owner(group->iommu_group, group); 43862306a36Sopenharmony_ci if (ret) 43962306a36Sopenharmony_ci goto out_unlock_container; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci driver = container->iommu_driver; 44362306a36Sopenharmony_ci if (driver) { 44462306a36Sopenharmony_ci ret = driver->ops->attach_group(container->iommu_data, 44562306a36Sopenharmony_ci group->iommu_group, 44662306a36Sopenharmony_ci group->type); 44762306a36Sopenharmony_ci if (ret) { 44862306a36Sopenharmony_ci if (group->type == VFIO_IOMMU) 44962306a36Sopenharmony_ci iommu_group_release_dma_owner( 45062306a36Sopenharmony_ci group->iommu_group); 45162306a36Sopenharmony_ci goto out_unlock_container; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci group->container = container; 45662306a36Sopenharmony_ci group->container_users = 1; 45762306a36Sopenharmony_ci container->noiommu = (group->type == VFIO_NO_IOMMU); 45862306a36Sopenharmony_ci list_add(&group->container_next, &container->group_list); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Get a reference on the container and mark a user within the group */ 46162306a36Sopenharmony_ci vfio_container_get(container); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ciout_unlock_container: 46462306a36Sopenharmony_ci up_write(&container->group_lock); 46562306a36Sopenharmony_ci return ret; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_civoid vfio_group_detach_container(struct vfio_group *group) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct vfio_container *container = group->container; 47162306a36Sopenharmony_ci struct vfio_iommu_driver *driver; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci lockdep_assert_held(&group->group_lock); 47462306a36Sopenharmony_ci WARN_ON(group->container_users != 1); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci down_write(&container->group_lock); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci driver = container->iommu_driver; 47962306a36Sopenharmony_ci if (driver) 48062306a36Sopenharmony_ci driver->ops->detach_group(container->iommu_data, 48162306a36Sopenharmony_ci group->iommu_group); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (group->type == VFIO_IOMMU) 48462306a36Sopenharmony_ci iommu_group_release_dma_owner(group->iommu_group); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci group->container = NULL; 48762306a36Sopenharmony_ci group->container_users = 0; 48862306a36Sopenharmony_ci list_del(&group->container_next); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* Detaching the last group deprivileges a container, remove iommu */ 49162306a36Sopenharmony_ci if (driver && list_empty(&container->group_list)) { 49262306a36Sopenharmony_ci driver->ops->release(container->iommu_data); 49362306a36Sopenharmony_ci module_put(driver->ops->owner); 49462306a36Sopenharmony_ci container->iommu_driver = NULL; 49562306a36Sopenharmony_ci container->iommu_data = NULL; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci up_write(&container->group_lock); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci vfio_container_put(container); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ciint vfio_group_use_container(struct vfio_group *group) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci lockdep_assert_held(&group->group_lock); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* 50862306a36Sopenharmony_ci * The container fd has been assigned with VFIO_GROUP_SET_CONTAINER but 50962306a36Sopenharmony_ci * VFIO_SET_IOMMU hasn't been done yet. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ci if (!group->container->iommu_driver) 51262306a36Sopenharmony_ci return -EINVAL; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (group->type == VFIO_NO_IOMMU && !capable(CAP_SYS_RAWIO)) 51562306a36Sopenharmony_ci return -EPERM; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci get_file(group->opened_file); 51862306a36Sopenharmony_ci group->container_users++; 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_civoid vfio_group_unuse_container(struct vfio_group *group) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci lockdep_assert_held(&group->group_lock); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci WARN_ON(group->container_users <= 1); 52762306a36Sopenharmony_ci group->container_users--; 52862306a36Sopenharmony_ci fput(group->opened_file); 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ciint vfio_device_container_pin_pages(struct vfio_device *device, 53262306a36Sopenharmony_ci dma_addr_t iova, int npage, 53362306a36Sopenharmony_ci int prot, struct page **pages) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct vfio_container *container = device->group->container; 53662306a36Sopenharmony_ci struct iommu_group *iommu_group = device->group->iommu_group; 53762306a36Sopenharmony_ci struct vfio_iommu_driver *driver = container->iommu_driver; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (npage > VFIO_PIN_PAGES_MAX_ENTRIES) 54062306a36Sopenharmony_ci return -E2BIG; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (unlikely(!driver || !driver->ops->pin_pages)) 54362306a36Sopenharmony_ci return -ENOTTY; 54462306a36Sopenharmony_ci return driver->ops->pin_pages(container->iommu_data, iommu_group, iova, 54562306a36Sopenharmony_ci npage, prot, pages); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_civoid vfio_device_container_unpin_pages(struct vfio_device *device, 54962306a36Sopenharmony_ci dma_addr_t iova, int npage) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct vfio_container *container = device->group->container; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (WARN_ON(npage <= 0 || npage > VFIO_PIN_PAGES_MAX_ENTRIES)) 55462306a36Sopenharmony_ci return; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci container->iommu_driver->ops->unpin_pages(container->iommu_data, iova, 55762306a36Sopenharmony_ci npage); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ciint vfio_device_container_dma_rw(struct vfio_device *device, 56162306a36Sopenharmony_ci dma_addr_t iova, void *data, 56262306a36Sopenharmony_ci size_t len, bool write) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct vfio_container *container = device->group->container; 56562306a36Sopenharmony_ci struct vfio_iommu_driver *driver = container->iommu_driver; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (unlikely(!driver || !driver->ops->dma_rw)) 56862306a36Sopenharmony_ci return -ENOTTY; 56962306a36Sopenharmony_ci return driver->ops->dma_rw(container->iommu_data, iova, data, len, 57062306a36Sopenharmony_ci write); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ciint __init vfio_container_init(void) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci int ret; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci mutex_init(&vfio.iommu_drivers_lock); 57862306a36Sopenharmony_ci INIT_LIST_HEAD(&vfio.iommu_drivers_list); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci ret = misc_register(&vfio_dev); 58162306a36Sopenharmony_ci if (ret) { 58262306a36Sopenharmony_ci pr_err("vfio: misc device register failed\n"); 58362306a36Sopenharmony_ci return ret; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_VFIO_NOIOMMU)) { 58762306a36Sopenharmony_ci ret = vfio_register_iommu_driver(&vfio_noiommu_ops); 58862306a36Sopenharmony_ci if (ret) 58962306a36Sopenharmony_ci goto err_misc; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cierr_misc: 59462306a36Sopenharmony_ci misc_deregister(&vfio_dev); 59562306a36Sopenharmony_ci return ret; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_civoid vfio_container_cleanup(void) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_VFIO_NOIOMMU)) 60162306a36Sopenharmony_ci vfio_unregister_iommu_driver(&vfio_noiommu_ops); 60262306a36Sopenharmony_ci misc_deregister(&vfio_dev); 60362306a36Sopenharmony_ci mutex_destroy(&vfio.iommu_drivers_lock); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ciMODULE_ALIAS_MISCDEV(VFIO_MINOR); 60762306a36Sopenharmony_ciMODULE_ALIAS("devname:vfio/vfio"); 608