This is the basic infrastructure of a new miscdevice to hold the iommufd IOCTL API. It provides: - A miscdevice to create file descriptors to run the IOCTL interface over - A table based ioctl dispatch and centralized extendable pre-validation step - An xarray mapping userspace ID's to kernel objects. The design has multiple inter-related objects held within in a single IOMMUFD fd - A simple usage count to build a graph of object relations and protect against hostile userspace racing ioctls The only IOCTL provided in this patch is the generic 'destroy any object by handle' operation.
Unified · Split
| @@ -105,6 +105,7 @@ Code Seq# Include File Comments · 104 unmodified lines | |||
| 105 | '8' all SNP8023 advanced NIC card | 105 | '8' all SNP8023 advanced NIC card |
| 106 | <mailto:mcr@solidum.com> | 106 | <mailto:mcr@solidum.com> |
| 107 | ';' 64-7F linux/vfio.h | 107 | ';' 64-7F linux/vfio.h |
| 108 | ';' 80-FF linux/iommufd.h | ||
| 108 | '=' 00-3f uapi/linux/ptp_clock.h <mailto:richardcochran@gmail.com> | 109 | '=' 00-3f uapi/linux/ptp_clock.h <mailto:richardcochran@gmail.com> |
| 109 | '@' 00-0F linux/radeonfb.h conflict! | 110 | '@' 00-0F linux/radeonfb.h conflict! |
| 110 | '@' 00-0F drivers/video/aty/aty128fb.c conflict! | 111 | '@' 00-0F drivers/video/aty/aty128fb.c conflict! |
| @@ -10717,6 +10717,18 @@ F: drivers/iommu/dma-iommu.h · 10716 unmodified lines | |||
| 10717 | F: drivers/iommu/iova.c | 10717 | F: drivers/iommu/iova.c |
| 10718 | F: include/linux/iova.h | 10718 | F: include/linux/iova.h |
| 10719 | 10719 | ||
| 10720 | IOMMUFD | ||
| 10721 | M: Jason Gunthorpe <jgg@nvidia.com> | ||
| 10722 | M: Kevin Tian <kevin.tian@intel.com> | ||
| 10723 | L: iommu@lists.linux.dev | ||
| 10724 | S: Maintained | ||
| 10725 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd.git | ||
| 10726 | F: Documentation/userspace-api/iommufd.rst | ||
| 10727 | F: drivers/iommu/iommufd/ | ||
| 10728 | F: include/linux/iommufd.h | ||
| 10729 | F: include/uapi/linux/iommufd.h | ||
| 10730 | F: tools/testing/selftests/iommu/ | ||
| 10731 | |||
| 10720 | IOMMU SUBSYSTEM | 10732 | IOMMU SUBSYSTEM |
| 10721 | M: Joerg Roedel <joro@8bytes.org> | 10733 | M: Joerg Roedel <joro@8bytes.org> |
| 10722 | M: Will Deacon <will@kernel.org> | 10734 | M: Will Deacon <will@kernel.org> |
| @@ -188,6 +188,7 @@ config MSM_IOMMU · 187 unmodified lines | |||
| 188 | 188 | ||
| 189 | source "drivers/iommu/amd/Kconfig" | 189 | source "drivers/iommu/amd/Kconfig" |
| 190 | source "drivers/iommu/intel/Kconfig" | 190 | source "drivers/iommu/intel/Kconfig" |
| 191 | source "drivers/iommu/iommufd/Kconfig" | ||
| 191 | 192 | ||
| 192 | config IRQ_REMAP | 193 | config IRQ_REMAP |
| 193 | bool "Support for Interrupt Remapping" | 194 | bool "Support for Interrupt Remapping" |
| @@ -1,5 +1,5 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | obj-y += amd/ intel/ arm/ | 2 | obj-y += amd/ intel/ arm/ iommufd/ |
| 3 | obj-$(CONFIG_IOMMU_API) += iommu.o | 3 | obj-$(CONFIG_IOMMU_API) += iommu.o |
| 4 | obj-$(CONFIG_IOMMU_API) += iommu-traces.o | 4 | obj-$(CONFIG_IOMMU_API) += iommu-traces.o |
| 5 | obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o | 5 | obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o |
| @@ -0,0 +1,12 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | config IOMMUFD | ||
| 3 | tristate "IOMMU Userspace API" | ||
| 4 | select INTERVAL_TREE | ||
| 5 | select INTERVAL_TREE_SPAN_ITER | ||
| 6 | select IOMMU_API | ||
| 7 | default n | ||
| 8 | help | ||
| 9 | Provides /dev/iommu, the user API to control the IOMMU subsystem as | ||
| 10 | it relates to managing IO page tables that point at user space memory. | ||
| 11 | |||
| 12 | If you don't know what to do here, say N. | ||
| @@ -0,0 +1,5 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | iommufd-y := \ | ||
| 3 | main.o | ||
| 4 | |||
| 5 | obj-$(CONFIG_IOMMUFD) += iommufd.o | ||
| @@ -0,0 +1,109 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ | ||
| 2 | /* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES | ||
| 3 | */ | ||
| 4 | #ifndef __IOMMUFD_PRIVATE_H | ||
| 5 | #define __IOMMUFD_PRIVATE_H | ||
| 6 | |||
| 7 | #include <linux/rwsem.h> | ||
| 8 | #include <linux/xarray.h> | ||
| 9 | #include <linux/refcount.h> | ||
| 10 | #include <linux/uaccess.h> | ||
| 11 | |||
| 12 | struct iommufd_ctx { | ||
| 13 | struct file *file; | ||
| 14 | struct xarray objects; | ||
| 15 | }; | ||
| 16 | |||
| 17 | struct iommufd_ucmd { | ||
| 18 | struct iommufd_ctx *ictx; | ||
| 19 | void __user *ubuffer; | ||
| 20 | u32 user_size; | ||
| 21 | void *cmd; | ||
| 22 | }; | ||
| 23 | |||
| 24 | /* Copy the response in ucmd->cmd back to userspace. */ | ||
| 25 | static inline int iommufd_ucmd_respond(struct iommufd_ucmd *ucmd, | ||
| 26 | size_t cmd_len) | ||
| 27 | { | ||
| 28 | if (copy_to_user(ucmd->ubuffer, ucmd->cmd, | ||
| 29 | min_t(size_t, ucmd->user_size, cmd_len))) | ||
| 30 | return -EFAULT; | ||
| 31 | return 0; | ||
| 32 | } | ||
| 33 | |||
| 34 | enum iommufd_object_type { | ||
| 35 | IOMMUFD_OBJ_NONE, | ||
| 36 | IOMMUFD_OBJ_ANY = IOMMUFD_OBJ_NONE, | ||
| 37 | }; | ||
| 38 | |||
| 39 | /* Base struct for all objects with a userspace ID handle. */ | ||
| 40 | struct iommufd_object { | ||
| 41 | struct rw_semaphore destroy_rwsem; | ||
| 42 | refcount_t users; | ||
| 43 | enum iommufd_object_type type; | ||
| 44 | unsigned int id; | ||
| 45 | }; | ||
| 46 | |||
| 47 | static inline bool iommufd_lock_obj(struct iommufd_object *obj) | ||
| 48 | { | ||
| 49 | if (!down_read_trylock(&obj->destroy_rwsem)) | ||
| 50 | return false; | ||
| 51 | if (!refcount_inc_not_zero(&obj->users)) { | ||
| 52 | up_read(&obj->destroy_rwsem); | ||
| 53 | return false; | ||
| 54 | } | ||
| 55 | return true; | ||
| 56 | } | ||
| 57 | |||
| 58 | struct iommufd_object *iommufd_get_object(struct iommufd_ctx *ictx, u32 id, | ||
| 59 | enum iommufd_object_type type); | ||
| 60 | static inline void iommufd_put_object(struct iommufd_object *obj) | ||
| 61 | { | ||
| 62 | refcount_dec(&obj->users); | ||
| 63 | up_read(&obj->destroy_rwsem); | ||
| 64 | } | ||
| 65 | |||
| 66 | /** | ||
| 67 | * iommufd_ref_to_users() - Switch from destroy_rwsem to users refcount | ||
| 68 | * protection | ||
| 69 | * @obj - Object to release | ||
| 70 | * | ||
| 71 | * Objects have two refcount protections (destroy_rwsem and the refcount_t | ||
| 72 | * users). Holding either of these will prevent the object from being destroyed. | ||
| 73 | * | ||
| 74 | * Depending on the use case, one protection or the other is appropriate. In | ||
| 75 | * most cases references are being protected by the destroy_rwsem. This allows | ||
| 76 | * orderly destruction of the object because iommufd_object_destroy_user() will | ||
| 77 | * wait for it to become unlocked. However, as a rwsem, it cannot be held across | ||
| 78 | * a system call return. So cases that have longer term needs must switch | ||
| 79 | * to the weaker users refcount_t. | ||
| 80 | * | ||
| 81 | * With users protection iommufd_object_destroy_user() will return false, | ||
| 82 | * refusing to destroy the object, causing -EBUSY to userspace. | ||
| 83 | */ | ||
| 84 | static inline void iommufd_ref_to_users(struct iommufd_object *obj) | ||
| 85 | { | ||
| 86 | up_read(&obj->destroy_rwsem); | ||
| 87 | /* iommufd_lock_obj() obtains users as well */ | ||
| 88 | } | ||
| 89 | void iommufd_object_abort(struct iommufd_ctx *ictx, struct iommufd_object *obj); | ||
| 90 | void iommufd_object_abort_and_destroy(struct iommufd_ctx *ictx, | ||
| 91 | struct iommufd_object *obj); | ||
| 92 | void iommufd_object_finalize(struct iommufd_ctx *ictx, | ||
| 93 | struct iommufd_object *obj); | ||
| 94 | bool iommufd_object_destroy_user(struct iommufd_ctx *ictx, | ||
| 95 | struct iommufd_object *obj); | ||
| 96 | struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx, | ||
| 97 | size_t size, | ||
| 98 | enum iommufd_object_type type); | ||
| 99 | |||
| 100 | #define iommufd_object_alloc(ictx, ptr, type) \ | ||
| 101 | container_of(_iommufd_object_alloc( \ | ||
| 102 | ictx, \ | ||
| 103 | sizeof(*(ptr)) + BUILD_BUG_ON_ZERO( \ | ||
| 104 | offsetof(typeof(*(ptr)), \ | ||
| 105 | obj) != 0), \ | ||
| 106 | type), \ | ||
| 107 | typeof(*(ptr)), obj) | ||
| 108 | |||
| 109 | #endif | ||
| @@ -0,0 +1,344 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* Copyright (C) 2021 Intel Corporation | ||
| 3 | * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES | ||
| 4 | * | ||
| 5 | * iommufd provides control over the IOMMU HW objects created by IOMMU kernel | ||
| 6 | * drivers. IOMMU HW objects revolve around IO page tables that map incoming DMA | ||
| 7 | * addresses (IOVA) to CPU addresses. | ||
| 8 | */ | ||
| 9 | #define pr_fmt(fmt) "iommufd: " fmt | ||
| 10 | |||
| 11 | #include <linux/file.h> | ||
| 12 | #include <linux/fs.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/slab.h> | ||
| 15 | #include <linux/miscdevice.h> | ||
| 16 | #include <linux/mutex.h> | ||
| 17 | #include <linux/bug.h> | ||
| 18 | #include <uapi/linux/iommufd.h> | ||
| 19 | #include <linux/iommufd.h> | ||
| 20 | |||
| 21 | #include "iommufd_private.h" | ||
| 22 | |||
| 23 | struct iommufd_object_ops { | ||
| 24 | void (*destroy)(struct iommufd_object *obj); | ||
| 25 | }; | ||
| 26 | static const struct iommufd_object_ops iommufd_object_ops[]; | ||
| 27 | |||
| 28 | struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx, | ||
| 29 | size_t size, | ||
| 30 | enum iommufd_object_type type) | ||
| 31 | { | ||
| 32 | struct iommufd_object *obj; | ||
| 33 | int rc; | ||
| 34 | |||
| 35 | obj = kzalloc(size, GFP_KERNEL_ACCOUNT); | ||
| 36 | if (!obj) | ||
| 37 | return ERR_PTR(-ENOMEM); | ||
| 38 | obj->type = type; | ||
| 39 | init_rwsem(&obj->destroy_rwsem); | ||
| 40 | refcount_set(&obj->users, 1); | ||
| 41 | |||
| 42 | /* | ||
| 43 | * Reserve an ID in the xarray but do not publish the pointer yet since | ||
| 44 | * the caller hasn't initialized it yet. Once the pointer is published | ||
| 45 | * in the xarray and visible to other threads we can't reliably destroy | ||
| 46 | * it anymore, so the caller must complete all errorable operations | ||
| 47 | * before calling iommufd_object_finalize(). | ||
| 48 | */ | ||
| 49 | rc = xa_alloc(&ictx->objects, &obj->id, XA_ZERO_ENTRY, | ||
| 50 | xa_limit_32b, GFP_KERNEL_ACCOUNT); | ||
| 51 | if (rc) | ||
| 52 | goto out_free; | ||
| 53 | return obj; | ||
| 54 | out_free: | ||
| 55 | kfree(obj); | ||
| 56 | return ERR_PTR(rc); | ||
| 57 | } | ||
| 58 | |||
| 59 | /* | ||
| 60 | * Allow concurrent access to the object. | ||
| 61 | * | ||
| 62 | * Once another thread can see the object pointer it can prevent object | ||
| 63 | * destruction. Expect for special kernel-only objects there is no in-kernel way | ||
| 64 | * to reliably destroy a single object. Thus all APIs that are creating objects | ||
| 65 | * must use iommufd_object_abort() to handle their errors and only call | ||
| 66 | * iommufd_object_finalize() once object creation cannot fail. | ||
| 67 | */ | ||
| 68 | void iommufd_object_finalize(struct iommufd_ctx *ictx, | ||
| 69 | struct iommufd_object *obj) | ||
| 70 | { | ||
| 71 | void *old; | ||
| 72 | |||
| 73 | old = xa_store(&ictx->objects, obj->id, obj, GFP_KERNEL); | ||
| 74 | /* obj->id was returned from xa_alloc() so the xa_store() cannot fail */ | ||
| 75 | WARN_ON(old); | ||
| 76 | } | ||
| 77 | |||
| 78 | /* Undo _iommufd_object_alloc() if iommufd_object_finalize() was not called */ | ||
| 79 | void iommufd_object_abort(struct iommufd_ctx *ictx, struct iommufd_object *obj) | ||
| 80 | { | ||
| 81 | void *old; | ||
| 82 | |||
| 83 | old = xa_erase(&ictx->objects, obj->id); | ||
| 84 | WARN_ON(old); | ||
| 85 | kfree(obj); | ||
| 86 | } | ||
| 87 | |||
| 88 | /* | ||
| 89 | * Abort an object that has been fully initialized and needs destroy, but has | ||
| 90 | * not been finalized. | ||
| 91 | */ | ||
| 92 | void iommufd_object_abort_and_destroy(struct iommufd_ctx *ictx, | ||
| 93 | struct iommufd_object *obj) | ||
| 94 | { | ||
| 95 | iommufd_object_ops[obj->type].destroy(obj); | ||
| 96 | iommufd_object_abort(ictx, obj); | ||
| 97 | } | ||
| 98 | |||
| 99 | struct iommufd_object *iommufd_get_object(struct iommufd_ctx *ictx, u32 id, | ||
| 100 | enum iommufd_object_type type) | ||
| 101 | { | ||
| 102 | struct iommufd_object *obj; | ||
| 103 | |||
| 104 | xa_lock(&ictx->objects); | ||
| 105 | obj = xa_load(&ictx->objects, id); | ||
| 106 | if (!obj || (type != IOMMUFD_OBJ_ANY && obj->type != type) || | ||
| 107 | !iommufd_lock_obj(obj)) | ||
| 108 | obj = ERR_PTR(-ENOENT); | ||
| 109 | xa_unlock(&ictx->objects); | ||
| 110 | return obj; | ||
| 111 | } | ||
| 112 | |||
| 113 | /* | ||
| 114 | * The caller holds a users refcount and wants to destroy the object. Returns | ||
| 115 | * true if the object was destroyed. In all cases the caller no longer has a | ||
| 116 | * reference on obj. | ||
| 117 | */ | ||
| 118 | bool iommufd_object_destroy_user(struct iommufd_ctx *ictx, | ||
| 119 | struct iommufd_object *obj) | ||
| 120 | { | ||
| 121 | /* | ||
| 122 | * The purpose of the destroy_rwsem is to ensure deterministic | ||
| 123 | * destruction of objects used by external drivers and destroyed by this | ||
| 124 | * function. Any temporary increment of the refcount must hold the read | ||
| 125 | * side of this, such as during ioctl execution. | ||
| 126 | */ | ||
| 127 | down_write(&obj->destroy_rwsem); | ||
| 128 | xa_lock(&ictx->objects); | ||
| 129 | refcount_dec(&obj->users); | ||
| 130 | if (!refcount_dec_if_one(&obj->users)) { | ||
| 131 | xa_unlock(&ictx->objects); | ||
| 132 | up_write(&obj->destroy_rwsem); | ||
| 133 | return false; | ||
| 134 | } | ||
| 135 | __xa_erase(&ictx->objects, obj->id); | ||
| 136 | xa_unlock(&ictx->objects); | ||
| 137 | up_write(&obj->destroy_rwsem); | ||
| 138 | |||
| 139 | iommufd_object_ops[obj->type].destroy(obj); | ||
| 140 | kfree(obj); | ||
| 141 | return true; | ||
| 142 | } | ||
| 143 | |||
| 144 | static int iommufd_destroy(struct iommufd_ucmd *ucmd) | ||
| 145 | { | ||
| 146 | struct iommu_destroy *cmd = ucmd->cmd; | ||
| 147 | struct iommufd_object *obj; | ||
| 148 | |||
| 149 | obj = iommufd_get_object(ucmd->ictx, cmd->id, IOMMUFD_OBJ_ANY); | ||
| 150 | if (IS_ERR(obj)) | ||
| 151 | return PTR_ERR(obj); | ||
| 152 | iommufd_ref_to_users(obj); | ||
| 153 | /* See iommufd_ref_to_users() */ | ||
| 154 | if (!iommufd_object_destroy_user(ucmd->ictx, obj)) | ||
| 155 | return -EBUSY; | ||
| 156 | return 0; | ||
| 157 | } | ||
| 158 | |||
| 159 | static int iommufd_fops_open(struct inode *inode, struct file *filp) | ||
| 160 | { | ||
| 161 | struct iommufd_ctx *ictx; | ||
| 162 | |||
| 163 | ictx = kzalloc(sizeof(*ictx), GFP_KERNEL_ACCOUNT); | ||
| 164 | if (!ictx) | ||
| 165 | return -ENOMEM; | ||
| 166 | |||
| 167 | xa_init_flags(&ictx->objects, XA_FLAGS_ALLOC1 | XA_FLAGS_ACCOUNT); | ||
| 168 | ictx->file = filp; | ||
| 169 | filp->private_data = ictx; | ||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | static int iommufd_fops_release(struct inode *inode, struct file *filp) | ||
| 174 | { | ||
| 175 | struct iommufd_ctx *ictx = filp->private_data; | ||
| 176 | struct iommufd_object *obj; | ||
| 177 | |||
| 178 | /* | ||
| 179 | * The objects in the xarray form a graph of "users" counts, and we have | ||
| 180 | * to destroy them in a depth first manner. Leaf objects will reduce the | ||
| 181 | * users count of interior objects when they are destroyed. | ||
| 182 | * | ||
| 183 | * Repeatedly destroying all the "1 users" leaf objects will progress | ||
| 184 | * until the entire list is destroyed. If this can't progress then there | ||
| 185 | * is some bug related to object refcounting. | ||
| 186 | */ | ||
| 187 | while (!xa_empty(&ictx->objects)) { | ||
| 188 | unsigned int destroyed = 0; | ||
| 189 | unsigned long index; | ||
| 190 | |||
| 191 | xa_for_each(&ictx->objects, index, obj) { | ||
| 192 | if (!refcount_dec_if_one(&obj->users)) | ||
| 193 | continue; | ||
| 194 | destroyed++; | ||
| 195 | xa_erase(&ictx->objects, index); | ||
| 196 | iommufd_object_ops[obj->type].destroy(obj); | ||
| 197 | kfree(obj); | ||
| 198 | } | ||
| 199 | /* Bug related to users refcount */ | ||
| 200 | if (WARN_ON(!destroyed)) | ||
| 201 | break; | ||
| 202 | } | ||
| 203 | kfree(ictx); | ||
| 204 | return 0; | ||
| 205 | } | ||
| 206 | |||
| 207 | union ucmd_buffer { | ||
| 208 | struct iommu_destroy destroy; | ||
| 209 | }; | ||
| 210 | |||
| 211 | struct iommufd_ioctl_op { | ||
| 212 | unsigned int size; | ||
| 213 | unsigned int min_size; | ||
| 214 | unsigned int ioctl_num; | ||
| 215 | int (*execute)(struct iommufd_ucmd *ucmd); | ||
| 216 | }; | ||
| 217 | |||
| 218 | #define IOCTL_OP(_ioctl, _fn, _struct, _last) \ | ||
| 219 | [_IOC_NR(_ioctl) - IOMMUFD_CMD_BASE] = { \ | ||
| 220 | .size = sizeof(_struct) + \ | ||
| 221 | BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \ | ||
| 222 | sizeof(_struct)), \ | ||
| 223 | .min_size = offsetofend(_struct, _last), \ | ||
| 224 | .ioctl_num = _ioctl, \ | ||
| 225 | .execute = _fn, \ | ||
| 226 | } | ||
| 227 | static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { | ||
| 228 | IOCTL_OP(IOMMU_DESTROY, iommufd_destroy, struct iommu_destroy, id), | ||
| 229 | }; | ||
| 230 | |||
| 231 | static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd, | ||
| 232 | unsigned long arg) | ||
| 233 | { | ||
| 234 | const struct iommufd_ioctl_op *op; | ||
| 235 | struct iommufd_ucmd ucmd = {}; | ||
| 236 | union ucmd_buffer buf; | ||
| 237 | unsigned int nr; | ||
| 238 | int ret; | ||
| 239 | |||
| 240 | ucmd.ictx = filp->private_data; | ||
| 241 | ucmd.ubuffer = (void __user *)arg; | ||
| 242 | ret = get_user(ucmd.user_size, (u32 __user *)ucmd.ubuffer); | ||
| 243 | if (ret) | ||
| 244 | return ret; | ||
| 245 | |||
| 246 | nr = _IOC_NR(cmd); | ||
| 247 | if (nr < IOMMUFD_CMD_BASE || | ||
| 248 | (nr - IOMMUFD_CMD_BASE) >= ARRAY_SIZE(iommufd_ioctl_ops)) | ||
| 249 | return -ENOIOCTLCMD; | ||
| 250 | op = &iommufd_ioctl_ops[nr - IOMMUFD_CMD_BASE]; | ||
| 251 | if (op->ioctl_num != cmd) | ||
| 252 | return -ENOIOCTLCMD; | ||
| 253 | if (ucmd.user_size < op->min_size) | ||
| 254 | return -EINVAL; | ||
| 255 | |||
| 256 | ucmd.cmd = &buf; | ||
| 257 | ret = copy_struct_from_user(ucmd.cmd, op->size, ucmd.ubuffer, | ||
| 258 | ucmd.user_size); | ||
| 259 | if (ret) | ||
| 260 | return ret; | ||
| 261 | ret = op->execute(&ucmd); | ||
| 262 | return ret; | ||
| 263 | } | ||
| 264 | |||
| 265 | static const struct file_operations iommufd_fops = { | ||
| 266 | .owner = THIS_MODULE, | ||
| 267 | .open = iommufd_fops_open, | ||
| 268 | .release = iommufd_fops_release, | ||
| 269 | .unlocked_ioctl = iommufd_fops_ioctl, | ||
| 270 | }; | ||
| 271 | |||
| 272 | /** | ||
| 273 | * iommufd_ctx_get - Get a context reference | ||
| 274 | * @ictx: Context to get | ||
| 275 | * | ||
| 276 | * The caller must already hold a valid reference to ictx. | ||
| 277 | */ | ||
| 278 | void iommufd_ctx_get(struct iommufd_ctx *ictx) | ||
| 279 | { | ||
| 280 | get_file(ictx->file); | ||
| 281 | } | ||
| 282 | EXPORT_SYMBOL_NS_GPL(iommufd_ctx_get, IOMMUFD); | ||
| 283 | |||
| 284 | /** | ||
| 285 | * iommufd_ctx_from_file - Acquires a reference to the iommufd context | ||
| 286 | * @file: File to obtain the reference from | ||
| 287 | * | ||
| 288 | * Returns a pointer to the iommufd_ctx, otherwise ERR_PTR. The struct file | ||
| 289 | * remains owned by the caller and the caller must still do fput. On success | ||
| 290 | * the caller is responsible to call iommufd_ctx_put(). | ||
| 291 | */ | ||
| 292 | struct iommufd_ctx *iommufd_ctx_from_file(struct file *file) | ||
| 293 | { | ||
| 294 | struct iommufd_ctx *ictx; | ||
| 295 | |||
| 296 | if (file->f_op != &iommufd_fops) | ||
| 297 | return ERR_PTR(-EBADFD); | ||
| 298 | ictx = file->private_data; | ||
| 299 | iommufd_ctx_get(ictx); | ||
| 300 | return ictx; | ||
| 301 | } | ||
| 302 | EXPORT_SYMBOL_NS_GPL(iommufd_ctx_from_file, IOMMUFD); | ||
| 303 | |||
| 304 | /** | ||
| 305 | * iommufd_ctx_put - Put back a reference | ||
| 306 | * @ictx: Context to put back | ||
| 307 | */ | ||
| 308 | void iommufd_ctx_put(struct iommufd_ctx *ictx) | ||
| 309 | { | ||
| 310 | fput(ictx->file); | ||
| 311 | } | ||
| 312 | EXPORT_SYMBOL_NS_GPL(iommufd_ctx_put, IOMMUFD); | ||
| 313 | |||
| 314 | static const struct iommufd_object_ops iommufd_object_ops[] = { | ||
| 315 | }; | ||
| 316 | |||
| 317 | static struct miscdevice iommu_misc_dev = { | ||
| 318 | .minor = MISC_DYNAMIC_MINOR, | ||
| 319 | .name = "iommu", | ||
| 320 | .fops = &iommufd_fops, | ||
| 321 | .nodename = "iommu", | ||
| 322 | .mode = 0660, | ||
| 323 | }; | ||
| 324 | |||
| 325 | static int __init iommufd_init(void) | ||
| 326 | { | ||
| 327 | int ret; | ||
| 328 | |||
| 329 | ret = misc_register(&iommu_misc_dev); | ||
| 330 | if (ret) | ||
| 331 | return ret; | ||
| 332 | return 0; | ||
| 333 | } | ||
| 334 | |||
| 335 | static void __exit iommufd_exit(void) | ||
| 336 | { | ||
| 337 | misc_deregister(&iommu_misc_dev); | ||
| 338 | } | ||
| 339 | |||
| 340 | module_init(iommufd_init); | ||
| 341 | module_exit(iommufd_exit); | ||
| 342 | |||
| 343 | MODULE_DESCRIPTION("I/O Address Space Management for passthrough devices"); | ||
| 344 | MODULE_LICENSE("GPL"); | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2021 Intel Corporation | ||
| 4 | * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES | ||
| 5 | */ | ||
| 6 | #ifndef __LINUX_IOMMUFD_H | ||
| 7 | #define __LINUX_IOMMUFD_H | ||
| 8 | |||
| 9 | #include <linux/types.h> | ||
| 10 | #include <linux/errno.h> | ||
| 11 | #include <linux/err.h> | ||
| 12 | |||
| 13 | struct iommufd_ctx; | ||
| 14 | struct file; | ||
| 15 | |||
| 16 | void iommufd_ctx_get(struct iommufd_ctx *ictx); | ||
| 17 | |||
| 18 | #if IS_ENABLED(CONFIG_IOMMUFD) | ||
| 19 | struct iommufd_ctx *iommufd_ctx_from_file(struct file *file); | ||
| 20 | void iommufd_ctx_put(struct iommufd_ctx *ictx); | ||
| 21 | #else /* !CONFIG_IOMMUFD */ | ||
| 22 | static inline struct iommufd_ctx *iommufd_ctx_from_file(struct file *file) | ||
| 23 | { | ||
| 24 | return ERR_PTR(-EOPNOTSUPP); | ||
| 25 | } | ||
| 26 | |||
| 27 | static inline void iommufd_ctx_put(struct iommufd_ctx *ictx) | ||
| 28 | { | ||
| 29 | } | ||
| 30 | #endif /* CONFIG_IOMMUFD */ | ||
| 31 | #endif | ||
| @@ -0,0 +1,55 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||
| 2 | /* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES. | ||
| 3 | */ | ||
| 4 | #ifndef _UAPI_IOMMUFD_H | ||
| 5 | #define _UAPI_IOMMUFD_H | ||
| 6 | |||
| 7 | #include <linux/types.h> | ||
| 8 | #include <linux/ioctl.h> | ||
| 9 | |||
| 10 | #define IOMMUFD_TYPE (';') | ||
| 11 | |||
| 12 | /** | ||
| 13 | * DOC: General ioctl format | ||
| 14 | * | ||
| 15 | * The ioctl interface follows a general format to allow for extensibility. Each | ||
| 16 | * ioctl is passed in a structure pointer as the argument providing the size of | ||
| 17 | * the structure in the first u32. The kernel checks that any structure space | ||
| 18 | * beyond what it understands is 0. This allows userspace to use the backward | ||
| 19 | * compatible portion while consistently using the newer, larger, structures. | ||
| 20 | * | ||
| 21 | * ioctls use a standard meaning for common errnos: | ||
| 22 | * | ||
| 23 | * - ENOTTY: The IOCTL number itself is not supported at all | ||
| 24 | * - E2BIG: The IOCTL number is supported, but the provided structure has | ||
| 25 | * non-zero in a part the kernel does not understand. | ||
| 26 | * - EOPNOTSUPP: The IOCTL number is supported, and the structure is | ||
| 27 | * understood, however a known field has a value the kernel does not | ||
| 28 | * understand or support. | ||
| 29 | * - EINVAL: Everything about the IOCTL was understood, but a field is not | ||
| 30 | * correct. | ||
| 31 | * - ENOENT: An ID or IOVA provided does not exist. | ||
| 32 | * - ENOMEM: Out of memory. | ||
| 33 | * - EOVERFLOW: Mathematics overflowed. | ||
| 34 | * | ||
| 35 | * As well as additional errnos, within specific ioctls. | ||
| 36 | */ | ||
| 37 | enum { | ||
| 38 | IOMMUFD_CMD_BASE = 0x80, | ||
| 39 | IOMMUFD_CMD_DESTROY = IOMMUFD_CMD_BASE, | ||
| 40 | }; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * struct iommu_destroy - ioctl(IOMMU_DESTROY) | ||
| 44 | * @size: sizeof(struct iommu_destroy) | ||
| 45 | * @id: iommufd object ID to destroy. Can by any destroyable object type. | ||
| 46 | * | ||
| 47 | * Destroy any object held within iommufd. | ||
| 48 | */ | ||
| 49 | struct iommu_destroy { | ||
| 50 | __u32 size; | ||
| 51 | __u32 id; | ||
| 52 | }; | ||
| 53 | #define IOMMU_DESTROY _IO(IOMMUFD_TYPE, IOMMUFD_CMD_DESTROY) | ||
| 54 | |||
| 55 | #endif | ||