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#include <core/handle.h> 28#include <core/namedb.h> 29 30#include <nvif/unpack.h> 31#include <nvif/ioctl.h> 32 33static int 34nvkm_ioctl_nop(struct nvkm_handle *handle, void *data, u32 size) 35{ 36 struct nvkm_object *object = handle->object; 37 union { 38 struct nvif_ioctl_nop none; 39 } *args = data; 40 int ret; 41 42 nv_ioctl(object, "nop size %d\n", size); 43 if (nvif_unvers(args->none)) { 44 nv_ioctl(object, "nop\n"); 45 } 46 47 return ret; 48} 49 50static int 51nvkm_ioctl_sclass(struct nvkm_handle *handle, void *data, u32 size) 52{ 53 struct nvkm_object *object = handle->object; 54 union { 55 struct nvif_ioctl_sclass_v0 v0; 56 } *args = data; 57 int ret; 58 59 if (!nv_iclass(object, NV_PARENT_CLASS)) { 60 nv_debug(object, "cannot have children (sclass)\n"); 61 return -ENODEV; 62 } 63 64 nv_ioctl(object, "sclass size %d\n", size); 65 if (nvif_unpack(args->v0, 0, 0, true)) { 66 nv_ioctl(object, "sclass vers %d count %d\n", 67 args->v0.version, args->v0.count); 68 if (size == args->v0.count * sizeof(args->v0.oclass[0])) { 69 ret = nvkm_parent_lclass(object, args->v0.oclass, 70 args->v0.count); 71 if (ret >= 0) { 72 args->v0.count = ret; 73 ret = 0; 74 } 75 } else { 76 ret = -EINVAL; 77 } 78 } 79 80 return ret; 81} 82 83static int 84nvkm_ioctl_new(struct nvkm_handle *handle, void *data, u32 size) 85{ 86 union { 87 struct nvif_ioctl_new_v0 v0; 88 } *args = data; 89 struct nvkm_client *client = nvkm_client(handle->object); 90 struct nvkm_object *engctx = NULL; 91 struct nvkm_object *object = NULL; 92 struct nvkm_parent *parent; 93 struct nvkm_object *engine; 94 struct nvkm_oclass *oclass; 95 u32 _handle, _oclass; 96 int ret; 97 98 nv_ioctl(client, "new size %d\n", size); 99 if (nvif_unpack(args->v0, 0, 0, true)) { 100 _handle = args->v0.handle; 101 _oclass = args->v0.oclass; 102 } else 103 return ret; 104 105 nv_ioctl(client, "new vers %d handle %08x class %08x " 106 "route %02x token %llx\n", 107 args->v0.version, _handle, _oclass, 108 args->v0.route, args->v0.token); 109 110 if (!nv_iclass(handle->object, NV_PARENT_CLASS)) { 111 nv_debug(handle->object, "cannot have children (ctor)\n"); 112 ret = -ENODEV; 113 goto fail_class; 114 } 115 116 parent = nv_parent(handle->object); 117 118 /* check that parent supports the requested subclass */ 119 ret = nvkm_parent_sclass(&parent->object, _oclass, &engine, &oclass); 120 if (ret) { 121 nv_debug(parent, "illegal class 0x%04x\n", _oclass); 122 goto fail_class; 123 } 124 125 /* make sure engine init has been completed *before* any objects 126 * it controls are created - the constructors may depend on 127 * state calculated at init (ie. default context construction) 128 */ 129 if (engine) { 130 ret = nvkm_object_inc(engine); 131 if (ret) 132 goto fail_class; 133 } 134 135 /* if engine requires it, create a context object to insert 136 * between the parent and its children (eg. PGRAPH context) 137 */ 138 if (engine && nv_engine(engine)->cclass) { 139 ret = nvkm_object_ctor(&parent->object, engine, 140 nv_engine(engine)->cclass, 141 data, size, &engctx); 142 if (ret) 143 goto fail_engctx; 144 } else { 145 nvkm_object_ref(&parent->object, &engctx); 146 } 147 148 /* finally, create new object and bind it to its handle */ 149 ret = nvkm_object_ctor(engctx, engine, oclass, data, size, &object); 150 client->data = object; 151 if (ret) 152 goto fail_ctor; 153 154 ret = nvkm_object_inc(object); 155 if (ret) 156 goto fail_init; 157 158 ret = nvkm_handle_create(&parent->object, handle->name, 159 _handle, object, &handle); 160 if (ret) 161 goto fail_handle; 162 163 ret = nvkm_handle_init(handle); 164 handle->route = args->v0.route; 165 handle->token = args->v0.token; 166 if (ret) 167 nvkm_handle_destroy(handle); 168 169fail_handle: 170 nvkm_object_dec(object, false); 171fail_init: 172 nvkm_object_ref(NULL, &object); 173fail_ctor: 174 nvkm_object_ref(NULL, &engctx); 175fail_engctx: 176 if (engine) 177 nvkm_object_dec(engine, false); 178fail_class: 179 return ret; 180} 181 182static int 183nvkm_ioctl_del(struct nvkm_handle *handle, void *data, u32 size) 184{ 185 struct nvkm_object *object = handle->object; 186 union { 187 struct nvif_ioctl_del none; 188 } *args = data; 189 int ret; 190 191 nv_ioctl(object, "delete size %d\n", size); 192 if (nvif_unvers(args->none)) { 193 nv_ioctl(object, "delete\n"); 194 nvkm_handle_fini(handle, false); 195 nvkm_handle_destroy(handle); 196 } 197 198 return ret; 199} 200 201static int 202nvkm_ioctl_mthd(struct nvkm_handle *handle, void *data, u32 size) 203{ 204 struct nvkm_object *object = handle->object; 205 struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; 206 union { 207 struct nvif_ioctl_mthd_v0 v0; 208 } *args = data; 209 int ret; 210 211 nv_ioctl(object, "mthd size %d\n", size); 212 if (nvif_unpack(args->v0, 0, 0, true)) { 213 nv_ioctl(object, "mthd vers %d mthd %02x\n", 214 args->v0.version, args->v0.method); 215 if (ret = -ENODEV, ofuncs->mthd) 216 ret = ofuncs->mthd(object, args->v0.method, data, size); 217 } 218 219 return ret; 220} 221 222 223static int 224nvkm_ioctl_rd(struct nvkm_handle *handle, void *data, u32 size) 225{ 226 struct nvkm_object *object = handle->object; 227 struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; 228 union { 229 struct nvif_ioctl_rd_v0 v0; 230 } *args = data; 231 int ret; 232 233 nv_ioctl(object, "rd size %d\n", size); 234 if (nvif_unpack(args->v0, 0, 0, false)) { 235 nv_ioctl(object, "rd vers %d size %d addr %016llx\n", 236 args->v0.version, args->v0.size, args->v0.addr); 237 switch (args->v0.size) { 238 case 1: 239 if (ret = -ENODEV, ofuncs->rd08) { 240 args->v0.data = nv_ro08(object, args->v0.addr); 241 ret = 0; 242 } 243 break; 244 case 2: 245 if (ret = -ENODEV, ofuncs->rd16) { 246 args->v0.data = nv_ro16(object, args->v0.addr); 247 ret = 0; 248 } 249 break; 250 case 4: 251 if (ret = -ENODEV, ofuncs->rd32) { 252 args->v0.data = nv_ro32(object, args->v0.addr); 253 ret = 0; 254 } 255 break; 256 default: 257 ret = -EINVAL; 258 break; 259 } 260 } 261 262 return ret; 263} 264 265static int 266nvkm_ioctl_wr(struct nvkm_handle *handle, void *data, u32 size) 267{ 268 struct nvkm_object *object = handle->object; 269 struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; 270 union { 271 struct nvif_ioctl_wr_v0 v0; 272 } *args = data; 273 int ret; 274 275 nv_ioctl(object, "wr size %d\n", size); 276 if (nvif_unpack(args->v0, 0, 0, false)) { 277 nv_ioctl(object, "wr vers %d size %d addr %016llx data %08x\n", 278 args->v0.version, args->v0.size, args->v0.addr, 279 args->v0.data); 280 switch (args->v0.size) { 281 case 1: 282 if (ret = -ENODEV, ofuncs->wr08) { 283 nv_wo08(object, args->v0.addr, args->v0.data); 284 ret = 0; 285 } 286 break; 287 case 2: 288 if (ret = -ENODEV, ofuncs->wr16) { 289 nv_wo16(object, args->v0.addr, args->v0.data); 290 ret = 0; 291 } 292 break; 293 case 4: 294 if (ret = -ENODEV, ofuncs->wr32) { 295 nv_wo32(object, args->v0.addr, args->v0.data); 296 ret = 0; 297 } 298 break; 299 default: 300 ret = -EINVAL; 301 break; 302 } 303 } 304 305 return ret; 306} 307 308static int 309nvkm_ioctl_map(struct nvkm_handle *handle, void *data, u32 size) 310{ 311 struct nvkm_object *object = handle->object; 312 struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; 313 union { 314 struct nvif_ioctl_map_v0 v0; 315 } *args = data; 316 int ret; 317 318 nv_ioctl(object, "map size %d\n", size); 319 if (nvif_unpack(args->v0, 0, 0, false)) { 320 nv_ioctl(object, "map vers %d\n", args->v0.version); 321 if (ret = -ENODEV, ofuncs->map) { 322 ret = ofuncs->map(object, &args->v0.handle, 323 &args->v0.length); 324 } 325 } 326 327 return ret; 328} 329 330static int 331nvkm_ioctl_unmap(struct nvkm_handle *handle, void *data, u32 size) 332{ 333 struct nvkm_object *object = handle->object; 334 union { 335 struct nvif_ioctl_unmap none; 336 } *args = data; 337 int ret; 338 339 nv_ioctl(object, "unmap size %d\n", size); 340 if (nvif_unvers(args->none)) { 341 nv_ioctl(object, "unmap\n"); 342 } 343 344 return ret; 345} 346 347static int 348nvkm_ioctl_ntfy_new(struct nvkm_handle *handle, void *data, u32 size) 349{ 350 struct nvkm_object *object = handle->object; 351 struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; 352 union { 353 struct nvif_ioctl_ntfy_new_v0 v0; 354 } *args = data; 355 struct nvkm_event *event; 356 int ret; 357 358 nv_ioctl(object, "ntfy new size %d\n", size); 359 if (nvif_unpack(args->v0, 0, 0, true)) { 360 nv_ioctl(object, "ntfy new vers %d event %02x\n", 361 args->v0.version, args->v0.event); 362 if (ret = -ENODEV, ofuncs->ntfy) 363 ret = ofuncs->ntfy(object, args->v0.event, &event); 364 if (ret == 0) { 365 ret = nvkm_client_notify_new(object, event, data, size); 366 if (ret >= 0) { 367 args->v0.index = ret; 368 ret = 0; 369 } 370 } 371 } 372 373 return ret; 374} 375 376static int 377nvkm_ioctl_ntfy_del(struct nvkm_handle *handle, void *data, u32 size) 378{ 379 struct nvkm_client *client = nvkm_client(handle->object); 380 struct nvkm_object *object = handle->object; 381 union { 382 struct nvif_ioctl_ntfy_del_v0 v0; 383 } *args = data; 384 int ret; 385 386 nv_ioctl(object, "ntfy del size %d\n", size); 387 if (nvif_unpack(args->v0, 0, 0, false)) { 388 nv_ioctl(object, "ntfy del vers %d index %d\n", 389 args->v0.version, args->v0.index); 390 ret = nvkm_client_notify_del(client, args->v0.index); 391 } 392 393 return ret; 394} 395 396static int 397nvkm_ioctl_ntfy_get(struct nvkm_handle *handle, void *data, u32 size) 398{ 399 struct nvkm_client *client = nvkm_client(handle->object); 400 struct nvkm_object *object = handle->object; 401 union { 402 struct nvif_ioctl_ntfy_get_v0 v0; 403 } *args = data; 404 int ret; 405 406 nv_ioctl(object, "ntfy get size %d\n", size); 407 if (nvif_unpack(args->v0, 0, 0, false)) { 408 nv_ioctl(object, "ntfy get vers %d index %d\n", 409 args->v0.version, args->v0.index); 410 ret = nvkm_client_notify_get(client, args->v0.index); 411 } 412 413 return ret; 414} 415 416static int 417nvkm_ioctl_ntfy_put(struct nvkm_handle *handle, void *data, u32 size) 418{ 419 struct nvkm_client *client = nvkm_client(handle->object); 420 struct nvkm_object *object = handle->object; 421 union { 422 struct nvif_ioctl_ntfy_put_v0 v0; 423 } *args = data; 424 int ret; 425 426 nv_ioctl(object, "ntfy put size %d\n", size); 427 if (nvif_unpack(args->v0, 0, 0, false)) { 428 nv_ioctl(object, "ntfy put vers %d index %d\n", 429 args->v0.version, args->v0.index); 430 ret = nvkm_client_notify_put(client, args->v0.index); 431 } 432 433 return ret; 434} 435 436static struct { 437 int version; 438 int (*func)(struct nvkm_handle *, void *, u32); 439} 440nvkm_ioctl_v0[] = { 441 { 0x00, nvkm_ioctl_nop }, 442 { 0x00, nvkm_ioctl_sclass }, 443 { 0x00, nvkm_ioctl_new }, 444 { 0x00, nvkm_ioctl_del }, 445 { 0x00, nvkm_ioctl_mthd }, 446 { 0x00, nvkm_ioctl_rd }, 447 { 0x00, nvkm_ioctl_wr }, 448 { 0x00, nvkm_ioctl_map }, 449 { 0x00, nvkm_ioctl_unmap }, 450 { 0x00, nvkm_ioctl_ntfy_new }, 451 { 0x00, nvkm_ioctl_ntfy_del }, 452 { 0x00, nvkm_ioctl_ntfy_get }, 453 { 0x00, nvkm_ioctl_ntfy_put }, 454}; 455 456static int 457nvkm_ioctl_path(struct nvkm_handle *parent, u32 type, u32 nr, u32 *path, 458 void *data, u32 size, u8 owner, u8 *route, u64 *token) 459{ 460 struct nvkm_handle *handle = parent; 461 struct nvkm_namedb *namedb; 462 struct nvkm_object *object; 463 int ret; 464 465 while ((object = parent->object), nr--) { 466 nv_ioctl(object, "path 0x%08x\n", path[nr]); 467 if (!nv_iclass(object, NV_PARENT_CLASS)) { 468 nv_debug(object, "cannot have children (path)\n"); 469 return -EINVAL; 470 } 471 472 if (!(namedb = (void *)nv_pclass(object, NV_NAMEDB_CLASS)) || 473 !(handle = nvkm_namedb_get(namedb, path[nr]))) { 474 nv_debug(object, "handle 0x%08x not found\n", path[nr]); 475 return -ENOENT; 476 } 477 nvkm_namedb_put(handle); 478 parent = handle; 479 } 480 481 if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != handle->route) { 482 nv_ioctl(object, "object route != owner\n"); 483 return -EACCES; 484 } 485 *route = handle->route; 486 *token = handle->token; 487 488 if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) { 489 if (nvkm_ioctl_v0[type].version == 0) 490 ret = nvkm_ioctl_v0[type].func(handle, data, size); 491 } 492 493 return ret; 494} 495 496int 497nvkm_ioctl(struct nvkm_client *client, bool supervisor, 498 void *data, u32 size, void **hack) 499{ 500 union { 501 struct nvif_ioctl_v0 v0; 502 } *args = data; 503 int ret; 504 505 client->super = supervisor; 506 nv_ioctl(client, "size %d\n", size); 507 508 if (nvif_unpack(args->v0, 0, 0, true)) { 509 nv_ioctl(client, "vers %d type %02x path %d owner %02x\n", 510 args->v0.version, args->v0.type, args->v0.path_nr, 511 args->v0.owner); 512 ret = nvkm_ioctl_path(client->root, args->v0.type, 513 args->v0.path_nr, args->v0.path, 514 data, size, args->v0.owner, 515 &args->v0.route, &args->v0.token); 516 } 517 518 nv_ioctl(client, "return %d\n", ret); 519 if (hack) { 520 *hack = client->data; 521 client->data = NULL; 522 } 523 524 client->super = false; 525 return ret; 526} 527