linux/drivers/gpu/drm/nouveau/nouveau_usif.c
<<
>>
Prefs
   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
  38struct usif_notify_p {
  39        struct drm_pending_event base;
  40        struct {
  41                struct drm_event base;
  42                u8 data[];
  43        } e;
  44};
  45
  46struct 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
  56static inline struct usif_notify *
  57usif_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
  68static inline void
  69usif_notify_dtor(struct usif_notify *ntfy)
  70{
  71        list_del(&ntfy->head);
  72        kfree(ntfy);
  73}
  74
  75int
  76usif_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
 124static int
 125usif_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
 169static int
 170usif_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
 192static int
 193usif_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);
 221done:
 222        if (ret) {
 223                atomic_set(&ntfy->enabled, 0);
 224                kfree(ntfy->p);
 225        }
 226        return ret;
 227}
 228
 229static int
 230usif_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
 252struct usif_object {
 253        struct list_head head;
 254        struct list_head ntfy;
 255        u8  route;
 256        u64 token;
 257};
 258
 259static void
 260usif_object_dtor(struct usif_object *object)
 261{
 262        list_del(&object->head);
 263        kfree(object);
 264}
 265
 266static int
 267usif_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
 321int
 322usif_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;
 404done:
 405        kfree(argv);
 406        return ret;
 407}
 408
 409void
 410usif_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
 424void
 425usif_client_init(struct nouveau_cli *cli)
 426{
 427        INIT_LIST_HEAD(&cli->objects);
 428        INIT_LIST_HEAD(&cli->notifys);
 429}
 430