linux/drivers/gpu/drm/nouveau/nvkm/core/ioctl.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#include <core/ioctl.h>
  25#include <core/client.h>
  26#include <core/engine.h>
  27
  28#include <nvif/unpack.h>
  29#include <nvif/ioctl.h>
  30
  31static int
  32nvkm_ioctl_nop(struct nvkm_client *client,
  33               struct nvkm_object *object, void *data, u32 size)
  34{
  35        union {
  36                struct nvif_ioctl_nop_v0 v0;
  37        } *args = data;
  38        int ret = -ENOSYS;
  39
  40        nvif_ioctl(object, "nop size %d\n", size);
  41        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
  42                nvif_ioctl(object, "nop vers %lld\n", args->v0.version);
  43                args->v0.version = NVIF_VERSION_LATEST;
  44        }
  45
  46        return ret;
  47}
  48
  49static int
  50nvkm_ioctl_sclass(struct nvkm_client *client,
  51                  struct nvkm_object *object, void *data, u32 size)
  52{
  53        union {
  54                struct nvif_ioctl_sclass_v0 v0;
  55        } *args = data;
  56        struct nvkm_oclass oclass = { .client = client };
  57        int ret = -ENOSYS, i = 0;
  58
  59        nvif_ioctl(object, "sclass size %d\n", size);
  60        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
  61                nvif_ioctl(object, "sclass vers %d count %d\n",
  62                           args->v0.version, args->v0.count);
  63                if (size != args->v0.count * sizeof(args->v0.oclass[0]))
  64                        return -EINVAL;
  65
  66                while (object->func->sclass &&
  67                       object->func->sclass(object, i, &oclass) >= 0) {
  68                        if (i < args->v0.count) {
  69                                args->v0.oclass[i].oclass = oclass.base.oclass;
  70                                args->v0.oclass[i].minver = oclass.base.minver;
  71                                args->v0.oclass[i].maxver = oclass.base.maxver;
  72                        }
  73                        i++;
  74                }
  75
  76                args->v0.count = i;
  77        }
  78
  79        return ret;
  80}
  81
  82static int
  83nvkm_ioctl_new(struct nvkm_client *client,
  84               struct nvkm_object *parent, void *data, u32 size)
  85{
  86        union {
  87                struct nvif_ioctl_new_v0 v0;
  88        } *args = data;
  89        struct nvkm_object *object = NULL;
  90        struct nvkm_oclass oclass;
  91        int ret = -ENOSYS, i = 0;
  92
  93        nvif_ioctl(parent, "new size %d\n", size);
  94        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
  95                nvif_ioctl(parent, "new vers %d handle %08x class %08x "
  96                                   "route %02x token %llx object %016llx\n",
  97                           args->v0.version, args->v0.handle, args->v0.oclass,
  98                           args->v0.route, args->v0.token, args->v0.object);
  99        } else
 100                return ret;
 101
 102        if (!parent->func->sclass) {
 103                nvif_ioctl(parent, "cannot have children\n");
 104                return -EINVAL;
 105        }
 106
 107        do {
 108                memset(&oclass, 0x00, sizeof(oclass));
 109                oclass.handle = args->v0.handle;
 110                oclass.route  = args->v0.route;
 111                oclass.token  = args->v0.token;
 112                oclass.object = args->v0.object;
 113                oclass.client = client;
 114                oclass.parent = parent;
 115                ret = parent->func->sclass(parent, i++, &oclass);
 116                if (ret)
 117                        return ret;
 118        } while (oclass.base.oclass != args->v0.oclass);
 119
 120        if (oclass.engine) {
 121                oclass.engine = nvkm_engine_ref(oclass.engine);
 122                if (IS_ERR(oclass.engine))
 123                        return PTR_ERR(oclass.engine);
 124        }
 125
 126        ret = oclass.ctor(&oclass, data, size, &object);
 127        nvkm_engine_unref(&oclass.engine);
 128        if (ret == 0) {
 129                ret = nvkm_object_init(object);
 130                if (ret == 0) {
 131                        list_add(&object->head, &parent->tree);
 132                        if (nvkm_object_insert(object)) {
 133                                client->data = object;
 134                                return 0;
 135                        }
 136                        ret = -EEXIST;
 137                }
 138                nvkm_object_fini(object, false);
 139        }
 140
 141        nvkm_object_del(&object);
 142        return ret;
 143}
 144
 145static int
 146nvkm_ioctl_del(struct nvkm_client *client,
 147               struct nvkm_object *object, void *data, u32 size)
 148{
 149        union {
 150                struct nvif_ioctl_del none;
 151        } *args = data;
 152        int ret = -ENOSYS;
 153
 154        nvif_ioctl(object, "delete size %d\n", size);
 155        if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
 156                nvif_ioctl(object, "delete\n");
 157                nvkm_object_fini(object, false);
 158                nvkm_object_del(&object);
 159        }
 160
 161        return ret ? ret : 1;
 162}
 163
 164static int
 165nvkm_ioctl_mthd(struct nvkm_client *client,
 166                struct nvkm_object *object, void *data, u32 size)
 167{
 168        union {
 169                struct nvif_ioctl_mthd_v0 v0;
 170        } *args = data;
 171        int ret = -ENOSYS;
 172
 173        nvif_ioctl(object, "mthd size %d\n", size);
 174        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 175                nvif_ioctl(object, "mthd vers %d mthd %02x\n",
 176                           args->v0.version, args->v0.method);
 177                ret = nvkm_object_mthd(object, args->v0.method, data, size);
 178        }
 179
 180        return ret;
 181}
 182
 183
 184static int
 185nvkm_ioctl_rd(struct nvkm_client *client,
 186              struct nvkm_object *object, void *data, u32 size)
 187{
 188        union {
 189                struct nvif_ioctl_rd_v0 v0;
 190        } *args = data;
 191        union {
 192                u8  b08;
 193                u16 b16;
 194                u32 b32;
 195        } v;
 196        int ret = -ENOSYS;
 197
 198        nvif_ioctl(object, "rd size %d\n", size);
 199        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 200                nvif_ioctl(object, "rd vers %d size %d addr %016llx\n",
 201                           args->v0.version, args->v0.size, args->v0.addr);
 202                switch (args->v0.size) {
 203                case 1:
 204                        ret = nvkm_object_rd08(object, args->v0.addr, &v.b08);
 205                        args->v0.data = v.b08;
 206                        break;
 207                case 2:
 208                        ret = nvkm_object_rd16(object, args->v0.addr, &v.b16);
 209                        args->v0.data = v.b16;
 210                        break;
 211                case 4:
 212                        ret = nvkm_object_rd32(object, args->v0.addr, &v.b32);
 213                        args->v0.data = v.b32;
 214                        break;
 215                default:
 216                        ret = -EINVAL;
 217                        break;
 218                }
 219        }
 220
 221        return ret;
 222}
 223
 224static int
 225nvkm_ioctl_wr(struct nvkm_client *client,
 226              struct nvkm_object *object, void *data, u32 size)
 227{
 228        union {
 229                struct nvif_ioctl_wr_v0 v0;
 230        } *args = data;
 231        int ret = -ENOSYS;
 232
 233        nvif_ioctl(object, "wr size %d\n", size);
 234        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 235                nvif_ioctl(object,
 236                           "wr vers %d size %d addr %016llx data %08x\n",
 237                           args->v0.version, args->v0.size, args->v0.addr,
 238                           args->v0.data);
 239        } else
 240                return ret;
 241
 242        switch (args->v0.size) {
 243        case 1: return nvkm_object_wr08(object, args->v0.addr, args->v0.data);
 244        case 2: return nvkm_object_wr16(object, args->v0.addr, args->v0.data);
 245        case 4: return nvkm_object_wr32(object, args->v0.addr, args->v0.data);
 246        default:
 247                break;
 248        }
 249
 250        return -EINVAL;
 251}
 252
 253static int
 254nvkm_ioctl_map(struct nvkm_client *client,
 255               struct nvkm_object *object, void *data, u32 size)
 256{
 257        union {
 258                struct nvif_ioctl_map_v0 v0;
 259        } *args = data;
 260        enum nvkm_object_map type;
 261        int ret = -ENOSYS;
 262
 263        nvif_ioctl(object, "map size %d\n", size);
 264        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 265                nvif_ioctl(object, "map vers %d\n", args->v0.version);
 266                ret = nvkm_object_map(object, data, size, &type,
 267                                      &args->v0.handle,
 268                                      &args->v0.length);
 269                if (type == NVKM_OBJECT_MAP_IO)
 270                        args->v0.type = NVIF_IOCTL_MAP_V0_IO;
 271                else
 272                        args->v0.type = NVIF_IOCTL_MAP_V0_VA;
 273        }
 274
 275        return ret;
 276}
 277
 278static int
 279nvkm_ioctl_unmap(struct nvkm_client *client,
 280                 struct nvkm_object *object, void *data, u32 size)
 281{
 282        union {
 283                struct nvif_ioctl_unmap none;
 284        } *args = data;
 285        int ret = -ENOSYS;
 286
 287        nvif_ioctl(object, "unmap size %d\n", size);
 288        if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
 289                nvif_ioctl(object, "unmap\n");
 290                ret = nvkm_object_unmap(object);
 291        }
 292
 293        return ret;
 294}
 295
 296static int
 297nvkm_ioctl_ntfy_new(struct nvkm_client *client,
 298                    struct nvkm_object *object, void *data, u32 size)
 299{
 300        union {
 301                struct nvif_ioctl_ntfy_new_v0 v0;
 302        } *args = data;
 303        struct nvkm_event *event;
 304        int ret = -ENOSYS;
 305
 306        nvif_ioctl(object, "ntfy new size %d\n", size);
 307        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 308                nvif_ioctl(object, "ntfy new vers %d event %02x\n",
 309                           args->v0.version, args->v0.event);
 310                ret = nvkm_object_ntfy(object, args->v0.event, &event);
 311                if (ret == 0) {
 312                        ret = nvkm_client_notify_new(object, event, data, size);
 313                        if (ret >= 0) {
 314                                args->v0.index = ret;
 315                                ret = 0;
 316                        }
 317                }
 318        }
 319
 320        return ret;
 321}
 322
 323static int
 324nvkm_ioctl_ntfy_del(struct nvkm_client *client,
 325                    struct nvkm_object *object, void *data, u32 size)
 326{
 327        union {
 328                struct nvif_ioctl_ntfy_del_v0 v0;
 329        } *args = data;
 330        int ret = -ENOSYS;
 331
 332        nvif_ioctl(object, "ntfy del size %d\n", size);
 333        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 334                nvif_ioctl(object, "ntfy del vers %d index %d\n",
 335                           args->v0.version, args->v0.index);
 336                ret = nvkm_client_notify_del(client, args->v0.index);
 337        }
 338
 339        return ret;
 340}
 341
 342static int
 343nvkm_ioctl_ntfy_get(struct nvkm_client *client,
 344                    struct nvkm_object *object, void *data, u32 size)
 345{
 346        union {
 347                struct nvif_ioctl_ntfy_get_v0 v0;
 348        } *args = data;
 349        int ret = -ENOSYS;
 350
 351        nvif_ioctl(object, "ntfy get size %d\n", size);
 352        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 353                nvif_ioctl(object, "ntfy get vers %d index %d\n",
 354                           args->v0.version, args->v0.index);
 355                ret = nvkm_client_notify_get(client, args->v0.index);
 356        }
 357
 358        return ret;
 359}
 360
 361static int
 362nvkm_ioctl_ntfy_put(struct nvkm_client *client,
 363                    struct nvkm_object *object, void *data, u32 size)
 364{
 365        union {
 366                struct nvif_ioctl_ntfy_put_v0 v0;
 367        } *args = data;
 368        int ret = -ENOSYS;
 369
 370        nvif_ioctl(object, "ntfy put size %d\n", size);
 371        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 372                nvif_ioctl(object, "ntfy put vers %d index %d\n",
 373                           args->v0.version, args->v0.index);
 374                ret = nvkm_client_notify_put(client, args->v0.index);
 375        }
 376
 377        return ret;
 378}
 379
 380static struct {
 381        int version;
 382        int (*func)(struct nvkm_client *, struct nvkm_object *, void *, u32);
 383}
 384nvkm_ioctl_v0[] = {
 385        { 0x00, nvkm_ioctl_nop },
 386        { 0x00, nvkm_ioctl_sclass },
 387        { 0x00, nvkm_ioctl_new },
 388        { 0x00, nvkm_ioctl_del },
 389        { 0x00, nvkm_ioctl_mthd },
 390        { 0x00, nvkm_ioctl_rd },
 391        { 0x00, nvkm_ioctl_wr },
 392        { 0x00, nvkm_ioctl_map },
 393        { 0x00, nvkm_ioctl_unmap },
 394        { 0x00, nvkm_ioctl_ntfy_new },
 395        { 0x00, nvkm_ioctl_ntfy_del },
 396        { 0x00, nvkm_ioctl_ntfy_get },
 397        { 0x00, nvkm_ioctl_ntfy_put },
 398};
 399
 400static int
 401nvkm_ioctl_path(struct nvkm_client *client, u64 handle, u32 type,
 402                void *data, u32 size, u8 owner, u8 *route, u64 *token)
 403{
 404        struct nvkm_object *object;
 405        int ret;
 406
 407        object = nvkm_object_search(client, handle, NULL);
 408        if (IS_ERR(object)) {
 409                nvif_ioctl(&client->object, "object not found\n");
 410                return PTR_ERR(object);
 411        }
 412
 413        if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != object->route) {
 414                nvif_ioctl(&client->object, "route != owner\n");
 415                return -EACCES;
 416        }
 417        *route = object->route;
 418        *token = object->token;
 419
 420        if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) {
 421                if (nvkm_ioctl_v0[type].version == 0)
 422                        ret = nvkm_ioctl_v0[type].func(client, object, data, size);
 423        }
 424
 425        return ret;
 426}
 427
 428int
 429nvkm_ioctl(struct nvkm_client *client, bool supervisor,
 430           void *data, u32 size, void **hack)
 431{
 432        struct nvkm_object *object = &client->object;
 433        union {
 434                struct nvif_ioctl_v0 v0;
 435        } *args = data;
 436        int ret = -ENOSYS;
 437
 438        client->super = supervisor;
 439        nvif_ioctl(object, "size %d\n", size);
 440
 441        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 442                nvif_ioctl(object,
 443                           "vers %d type %02x object %016llx owner %02x\n",
 444                           args->v0.version, args->v0.type, args->v0.object,
 445                           args->v0.owner);
 446                ret = nvkm_ioctl_path(client, args->v0.object, args->v0.type,
 447                                      data, size, args->v0.owner,
 448                                      &args->v0.route, &args->v0.token);
 449        }
 450
 451        if (ret != 1) {
 452                nvif_ioctl(object, "return %d\n", ret);
 453                if (hack) {
 454                        *hack = client->data;
 455                        client->data = NULL;
 456                }
 457        }
 458
 459        return ret;
 460}
 461