1 /* 2 * Copyright 2014 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Ben Skeggs <bskeggs@redhat.com> 23 */ 24 25 #include "nouveau_drv.h" 26 #include "nouveau_usif.h" 27 #include "nouveau_abi16.h" 28 29 #include <nvif/notify.h> 30 #include <nvif/unpack.h> 31 #include <nvif/client.h> 32 #include <nvif/event.h> 33 #include <nvif/ioctl.h> 34 35 #include <nvif/class.h> 36 #include <nvif/cl0080.h> 37 38 struct usif_notify_p { 39 struct drm_pending_event base; 40 struct { 41 struct drm_event base; 42 u8 data[]; 43 } e; 44 }; 45 46 struct usif_notify { 47 struct list_head head; 48 atomic_t enabled; 49 u32 handle; 50 u16 reply; 51 u8 route; 52 u64 token; 53 struct usif_notify_p *p; 54 }; 55 56 static inline struct usif_notify * 57 usif_notify_find(struct drm_file *filp, u32 handle) 58 { 59 struct nouveau_cli *cli = nouveau_cli(filp); 60 struct usif_notify *ntfy; 61 list_for_each_entry(ntfy, &cli->notifys, head) { 62 if (ntfy->handle == handle) 63 return ntfy; 64 } 65 return NULL; 66 } 67 68 static inline void 69 usif_notify_dtor(struct usif_notify *ntfy) 70 { 71 list_del(&ntfy->head); 72 kfree(ntfy); 73 } 74 75 int 76 usif_notify(const void *header, u32 length, const void *data, u32 size) 77 { 78 struct usif_notify *ntfy = NULL; 79 const union { 80 struct nvif_notify_rep_v0 v0; 81 } *rep = header; 82 struct drm_device *dev; 83 struct drm_file *filp; 84 unsigned long flags; 85 86 if (length == sizeof(rep->v0) && rep->v0.version == 0) { 87 if (WARN_ON(!(ntfy = (void *)(unsigned long)rep->v0.token))) 88 return NVIF_NOTIFY_DROP; 89 BUG_ON(rep->v0.route != NVDRM_NOTIFY_USIF); 90 } else 91 if (WARN_ON(1)) 92 return NVIF_NOTIFY_DROP; 93 94 if (WARN_ON(!ntfy->p || ntfy->reply != (length + size))) 95 return NVIF_NOTIFY_DROP; 96 filp = ntfy->p->base.file_priv; 97 dev = filp->minor->dev; 98 99 memcpy(&ntfy->p->e.data[0], header, length); 100 memcpy(&ntfy->p->e.data[length], data, size); 101 switch (rep->v0.version) { 102 case 0: { 103 struct nvif_notify_rep_v0 *rep = (void *)ntfy->p->e.data; 104 rep->route = ntfy->route; 105 rep->token = ntfy->token; 106 } 107 break; 108 default: 109 BUG(); 110 break; 111 } 112 113 spin_lock_irqsave(&dev->event_lock, flags); 114 if (!WARN_ON(filp->event_space < ntfy->p->e.base.length)) { 115 list_add_tail(&ntfy->p->base.link, &filp->event_list); 116 filp->event_space -= ntfy->p->e.base.length; 117 } 118 wake_up_interruptible(&filp->event_wait); 119 spin_unlock_irqrestore(&dev->event_lock, flags); 120 atomic_set(&ntfy->enabled, 0); 121 return NVIF_NOTIFY_DROP; 122 } 123 124 static int 125 usif_notify_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) 126 { 127 struct nouveau_cli *cli = nouveau_cli(f); 128 struct nvif_client *client = &cli->base; 129 union { 130 struct nvif_ioctl_ntfy_new_v0 v0; 131 } *args = data; 132 union { 133 struct nvif_notify_req_v0 v0; 134 } *req; 135 struct usif_notify *ntfy; 136 int ret = -ENOSYS; 137 138 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 139 if (usif_notify_find(f, args->v0.index)) 140 return -EEXIST; 141 } else 142 return ret; 143 req = data; 144 ret = -ENOSYS; 145 146 if (!(ntfy = kmalloc(sizeof(*ntfy), GFP_KERNEL))) 147 return -ENOMEM; 148 atomic_set(&ntfy->enabled, 0); 149 150 if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, true))) { 151 ntfy->reply = sizeof(struct nvif_notify_rep_v0) + req->v0.reply; 152 ntfy->route = req->v0.route; 153 ntfy->token = req->v0.token; 154 req->v0.route = NVDRM_NOTIFY_USIF; 155 req->v0.token = (unsigned long)(void *)ntfy; 156 ret = nvif_client_ioctl(client, argv, argc); 157 req->v0.token = ntfy->token; 158 req->v0.route = ntfy->route; 159 ntfy->handle = args->v0.index; 160 } 161 162 if (ret == 0) 163 list_add(&ntfy->head, &cli->notifys); 164 if (ret) 165 kfree(ntfy); 166 return ret; 167 } 168 169 static int 170 usif_notify_del(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) 171 { 172 struct nouveau_cli *cli = nouveau_cli(f); 173 struct nvif_client *client = &cli->base; 174 union { 175 struct nvif_ioctl_ntfy_del_v0 v0; 176 } *args = data; 177 struct usif_notify *ntfy; 178 int ret = -ENOSYS; 179 180 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 181 if (!(ntfy = usif_notify_find(f, args->v0.index))) 182 return -ENOENT; 183 } else 184 return ret; 185 186 ret = nvif_client_ioctl(client, argv, argc); 187 if (ret == 0) 188 usif_notify_dtor(ntfy); 189 return ret; 190 } 191 192 static int 193 usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) 194 { 195 struct nouveau_cli *cli = nouveau_cli(f); 196 struct nvif_client *client = &cli->base; 197 union { 198 struct nvif_ioctl_ntfy_del_v0 v0; 199 } *args = data; 200 struct usif_notify *ntfy; 201 int ret = -ENOSYS; 202 203 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 204 if (!(ntfy = usif_notify_find(f, args->v0.index))) 205 return -ENOENT; 206 } else 207 return ret; 208 209 if (atomic_xchg(&ntfy->enabled, 1)) 210 return 0; 211 212 ntfy->p = kmalloc(sizeof(*ntfy->p) + ntfy->reply, GFP_KERNEL); 213 if (ret = -ENOMEM, !ntfy->p) 214 goto done; 215 ntfy->p->base.event = &ntfy->p->e.base; 216 ntfy->p->base.file_priv = f; 217 ntfy->p->e.base.type = DRM_NOUVEAU_EVENT_NVIF; 218 ntfy->p->e.base.length = sizeof(ntfy->p->e.base) + ntfy->reply; 219 220 ret = nvif_client_ioctl(client, argv, argc); 221 done: 222 if (ret) { 223 atomic_set(&ntfy->enabled, 0); 224 kfree(ntfy->p); 225 } 226 return ret; 227 } 228 229 static int 230 usif_notify_put(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) 231 { 232 struct nouveau_cli *cli = nouveau_cli(f); 233 struct nvif_client *client = &cli->base; 234 union { 235 struct nvif_ioctl_ntfy_put_v0 v0; 236 } *args = data; 237 struct usif_notify *ntfy; 238 int ret = -ENOSYS; 239 240 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 241 if (!(ntfy = usif_notify_find(f, args->v0.index))) 242 return -ENOENT; 243 } else 244 return ret; 245 246 ret = nvif_client_ioctl(client, argv, argc); 247 if (ret == 0 && atomic_xchg(&ntfy->enabled, 0)) 248 kfree(ntfy->p); 249 return ret; 250 } 251 252 struct usif_object { 253 struct list_head head; 254 struct list_head ntfy; 255 u8 route; 256 u64 token; 257 }; 258 259 static void 260 usif_object_dtor(struct usif_object *object) 261 { 262 list_del(&object->head); 263 kfree(object); 264 } 265 266 static int 267 usif_object_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc, bool parent_abi16) 268 { 269 struct nouveau_cli *cli = nouveau_cli(f); 270 struct nvif_client *client = &cli->base; 271 union { 272 struct nvif_ioctl_new_v0 v0; 273 } *args = data; 274 struct usif_object *object; 275 int ret = -ENOSYS; 276 277 if ((ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) 278 return ret; 279 280 switch (args->v0.oclass) { 281 case NV_DMA_FROM_MEMORY: 282 case NV_DMA_TO_MEMORY: 283 case NV_DMA_IN_MEMORY: 284 return -EINVAL; 285 case NV_DEVICE: { 286 union { 287 struct nv_device_v0 v0; 288 } *args = data; 289 290 if ((ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) 291 return ret; 292 293 args->v0.priv = false; 294 break; 295 } 296 default: 297 if (!parent_abi16) 298 return -EINVAL; 299 break; 300 } 301 302 if (!(object = kmalloc(sizeof(*object), GFP_KERNEL))) 303 return -ENOMEM; 304 list_add(&object->head, &cli->objects); 305 306 object->route = args->v0.route; 307 object->token = args->v0.token; 308 args->v0.route = NVDRM_OBJECT_USIF; 309 args->v0.token = (unsigned long)(void *)object; 310 ret = nvif_client_ioctl(client, argv, argc); 311 if (ret) { 312 usif_object_dtor(object); 313 return ret; 314 } 315 316 args->v0.token = object->token; 317 args->v0.route = object->route; 318 return 0; 319 } 320 321 int 322 usif_ioctl(struct drm_file *filp, void __user *user, u32 argc) 323 { 324 struct nouveau_cli *cli = nouveau_cli(filp); 325 struct nvif_client *client = &cli->base; 326 void *data = kmalloc(argc, GFP_KERNEL); 327 u32 size = argc; 328 union { 329 struct nvif_ioctl_v0 v0; 330 } *argv = data; 331 struct usif_object *object; 332 bool abi16 = false; 333 u8 owner; 334 int ret; 335 336 if (ret = -ENOMEM, !argv) 337 goto done; 338 if (ret = -EFAULT, copy_from_user(argv, user, size)) 339 goto done; 340 341 if (!(ret = nvif_unpack(-ENOSYS, &data, &size, argv->v0, 0, 0, true))) { 342 /* block access to objects not created via this interface */ 343 owner = argv->v0.owner; 344 if (argv->v0.object == 0ULL && 345 argv->v0.type != NVIF_IOCTL_V0_DEL) 346 argv->v0.owner = NVDRM_OBJECT_ANY; /* except client */ 347 else 348 argv->v0.owner = NVDRM_OBJECT_USIF; 349 } else 350 goto done; 351 352 /* USIF slightly abuses some return-only ioctl members in order 353 * to provide interoperability with the older ABI16 objects 354 */ 355 mutex_lock(&cli->mutex); 356 if (argv->v0.route) { 357 if (ret = -EINVAL, argv->v0.route == 0xff) 358 ret = nouveau_abi16_usif(filp, argv, argc); 359 if (ret) { 360 mutex_unlock(&cli->mutex); 361 goto done; 362 } 363 364 abi16 = true; 365 } 366 367 switch (argv->v0.type) { 368 case NVIF_IOCTL_V0_NEW: 369 ret = usif_object_new(filp, data, size, argv, argc, abi16); 370 break; 371 case NVIF_IOCTL_V0_NTFY_NEW: 372 ret = usif_notify_new(filp, data, size, argv, argc); 373 break; 374 case NVIF_IOCTL_V0_NTFY_DEL: 375 ret = usif_notify_del(filp, data, size, argv, argc); 376 break; 377 case NVIF_IOCTL_V0_NTFY_GET: 378 ret = usif_notify_get(filp, data, size, argv, argc); 379 break; 380 case NVIF_IOCTL_V0_NTFY_PUT: 381 ret = usif_notify_put(filp, data, size, argv, argc); 382 break; 383 default: 384 ret = nvif_client_ioctl(client, argv, argc); 385 break; 386 } 387 if (argv->v0.route == NVDRM_OBJECT_USIF) { 388 object = (void *)(unsigned long)argv->v0.token; 389 argv->v0.route = object->route; 390 argv->v0.token = object->token; 391 if (ret == 0 && argv->v0.type == NVIF_IOCTL_V0_DEL) { 392 list_del(&object->head); 393 kfree(object); 394 } 395 } else { 396 argv->v0.route = NVIF_IOCTL_V0_ROUTE_HIDDEN; 397 argv->v0.token = 0; 398 } 399 argv->v0.owner = owner; 400 mutex_unlock(&cli->mutex); 401 402 if (copy_to_user(user, argv, argc)) 403 ret = -EFAULT; 404 done: 405 kfree(argv); 406 return ret; 407 } 408 409 void 410 usif_client_fini(struct nouveau_cli *cli) 411 { 412 struct usif_object *object, *otemp; 413 struct usif_notify *notify, *ntemp; 414 415 list_for_each_entry_safe(notify, ntemp, &cli->notifys, head) { 416 usif_notify_dtor(notify); 417 } 418 419 list_for_each_entry_safe(object, otemp, &cli->objects, head) { 420 usif_object_dtor(object); 421 } 422 } 423 424 void 425 usif_client_init(struct nouveau_cli *cli) 426 { 427 INIT_LIST_HEAD(&cli->objects); 428 INIT_LIST_HEAD(&cli->notifys); 429 } 430