1 /* $NetBSD: nouveau_nvkm_core_ioctl.c,v 1.7 2021/12/19 12:40:21 riastradh Exp $ */ 2 3 /* 4 * Copyright 2014 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Ben Skeggs <bskeggs (at) redhat.com> 25 */ 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_core_ioctl.c,v 1.7 2021/12/19 12:40:21 riastradh Exp $"); 28 29 #include <core/ioctl.h> 30 #include <core/client.h> 31 #include <core/engine.h> 32 33 #include <nvif/unpack.h> 34 #include <nvif/ioctl.h> 35 36 static int 37 nvkm_ioctl_nop(struct nvkm_client *client, 38 struct nvkm_object *object, void *data, u32 size) 39 { 40 union { 41 struct nvif_ioctl_nop_v0 v0; 42 } *args = data; 43 int ret = -ENOSYS; 44 45 nvif_ioctl(object, "nop size %d\n", size); 46 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 47 nvif_ioctl(object, "nop vers %"PRId64"\n", args->v0.version); 48 args->v0.version = NVIF_VERSION_LATEST; 49 } 50 51 return ret; 52 } 53 54 static int 55 nvkm_ioctl_sclass(struct nvkm_client *client, 56 struct nvkm_object *object, void *data, u32 size) 57 { 58 union { 59 struct nvif_ioctl_sclass_v0 v0; 60 } *args = data; 61 struct nvkm_oclass oclass = { .client = client }; 62 int ret = -ENOSYS, i = 0; 63 64 nvif_ioctl(object, "sclass size %d\n", size); 65 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 66 nvif_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 return -EINVAL; 70 71 while (object->func->sclass && 72 object->func->sclass(object, i, &oclass) >= 0) { 73 if (i < args->v0.count) { 74 args->v0.oclass[i].oclass = oclass.base.oclass; 75 args->v0.oclass[i].minver = oclass.base.minver; 76 args->v0.oclass[i].maxver = oclass.base.maxver; 77 } 78 i++; 79 } 80 81 args->v0.count = i; 82 } 83 84 return ret; 85 } 86 87 static int 88 nvkm_ioctl_new(struct nvkm_client *client, 89 struct nvkm_object *parent, void *data, u32 size) 90 { 91 union { 92 struct nvif_ioctl_new_v0 v0; 93 } *args = data; 94 struct nvkm_object *object = NULL; 95 struct nvkm_oclass oclass; 96 int ret = -ENOSYS, i = 0; 97 98 nvif_ioctl(parent, "new size %d\n", size); 99 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 100 nvif_ioctl(parent, "new vers %d handle %08x class %08x " 101 "route %02x token %"PRIx64" object %016"PRIx64"\n", 102 args->v0.version, args->v0.handle, args->v0.oclass, 103 args->v0.route, args->v0.token, args->v0.object); 104 } else 105 return ret; 106 107 if (!parent->func->sclass) { 108 nvif_ioctl(parent, "cannot have children\n"); 109 return -EINVAL; 110 } 111 112 do { 113 memset(&oclass, 0x00, sizeof(oclass)); 114 oclass.handle = args->v0.handle; 115 oclass.route = args->v0.route; 116 oclass.token = args->v0.token; 117 oclass.object = args->v0.object; 118 oclass.client = client; 119 oclass.parent = parent; 120 ret = parent->func->sclass(parent, i++, &oclass); 121 if (ret) 122 return ret; 123 } while (oclass.base.oclass != args->v0.oclass); 124 125 if (oclass.engine) { 126 oclass.engine = nvkm_engine_ref(oclass.engine); 127 if (IS_ERR(oclass.engine)) 128 return PTR_ERR(oclass.engine); 129 } 130 131 ret = oclass.ctor(&oclass, data, size, &object); 132 nvkm_engine_unref(&oclass.engine); 133 if (ret == 0) { 134 ret = nvkm_object_init(object); 135 if (ret == 0) { 136 list_add(&object->head, &parent->tree); 137 if (nvkm_object_insert(object)) { 138 client->data = object; 139 return 0; 140 } 141 ret = -EEXIST; 142 } 143 nvkm_object_fini(object, false); 144 } 145 146 nvkm_object_del(&object); 147 return ret; 148 } 149 150 static int 151 nvkm_ioctl_del(struct nvkm_client *client, 152 struct nvkm_object *object, void *data, u32 size) 153 { 154 union { 155 struct nvif_ioctl_del none; 156 } *args = data; 157 int ret = -ENOSYS; 158 159 nvif_ioctl(object, "delete size %d\n", size); 160 if (!(ret = nvif_unvers(ret, &data, &size, args->none))) { 161 nvif_ioctl(object, "delete\n"); 162 nvkm_object_fini(object, false); 163 nvkm_object_del(&object); 164 } 165 166 return ret ? ret : 1; 167 } 168 169 static int 170 nvkm_ioctl_mthd(struct nvkm_client *client, 171 struct nvkm_object *object, void *data, u32 size) 172 { 173 union { 174 struct nvif_ioctl_mthd_v0 v0; 175 } *args = data; 176 int ret = -ENOSYS; 177 178 nvif_ioctl(object, "mthd size %d\n", size); 179 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 180 nvif_ioctl(object, "mthd vers %d mthd %02x\n", 181 args->v0.version, args->v0.method); 182 ret = nvkm_object_mthd(object, args->v0.method, data, size); 183 } 184 185 return ret; 186 } 187 188 189 static int 190 nvkm_ioctl_rd(struct nvkm_client *client, 191 struct nvkm_object *object, void *data, u32 size) 192 { 193 union { 194 struct nvif_ioctl_rd_v0 v0; 195 } *args = data; 196 union { 197 u8 b08; 198 u16 b16; 199 u32 b32; 200 } v; 201 int ret = -ENOSYS; 202 203 nvif_ioctl(object, "rd size %d\n", size); 204 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 205 nvif_ioctl(object, "rd vers %d size %d addr %016"PRIx64"\n", 206 args->v0.version, args->v0.size, args->v0.addr); 207 switch (args->v0.size) { 208 case 1: 209 ret = nvkm_object_rd08(object, args->v0.addr, &v.b08); 210 args->v0.data = v.b08; 211 break; 212 case 2: 213 ret = nvkm_object_rd16(object, args->v0.addr, &v.b16); 214 args->v0.data = v.b16; 215 break; 216 case 4: 217 ret = nvkm_object_rd32(object, args->v0.addr, &v.b32); 218 args->v0.data = v.b32; 219 break; 220 default: 221 ret = -EINVAL; 222 break; 223 } 224 } 225 226 return ret; 227 } 228 229 static int 230 nvkm_ioctl_wr(struct nvkm_client *client, 231 struct nvkm_object *object, void *data, u32 size) 232 { 233 union { 234 struct nvif_ioctl_wr_v0 v0; 235 } *args = data; 236 int ret = -ENOSYS; 237 238 nvif_ioctl(object, "wr size %d\n", size); 239 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 240 nvif_ioctl(object, 241 "wr vers %d size %d addr %016"PRIx64" data %08x\n", 242 args->v0.version, args->v0.size, args->v0.addr, 243 args->v0.data); 244 } else 245 return ret; 246 247 switch (args->v0.size) { 248 case 1: return nvkm_object_wr08(object, args->v0.addr, args->v0.data); 249 case 2: return nvkm_object_wr16(object, args->v0.addr, args->v0.data); 250 case 4: return nvkm_object_wr32(object, args->v0.addr, args->v0.data); 251 default: 252 break; 253 } 254 255 return -EINVAL; 256 } 257 258 static int 259 nvkm_ioctl_map(struct nvkm_client *client, 260 struct nvkm_object *object, void *data, u32 size) 261 { 262 union { 263 struct nvif_ioctl_map_v0 v0; 264 } *args = data; 265 enum nvkm_object_map type; 266 int ret = -ENOSYS; 267 268 nvif_ioctl(object, "map size %d\n", size); 269 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 270 nvif_ioctl(object, "map vers %d\n", args->v0.version); 271 bus_space_tag_t dummy __unused; 272 ret = nvkm_object_map(object, data, size, &type, &dummy, 273 &args->v0.handle, 274 &args->v0.length); 275 if (type == NVKM_OBJECT_MAP_IO) 276 args->v0.type = NVIF_IOCTL_MAP_V0_IO; 277 else 278 args->v0.type = NVIF_IOCTL_MAP_V0_VA; 279 } 280 281 return ret; 282 } 283 284 static int 285 nvkm_ioctl_map_netbsd(struct nvkm_client *client, struct nvkm_object *object, 286 void *data, u32 size) 287 { 288 union { 289 struct nvif_ioctl_map_netbsd_v0 v0; 290 } *args = data; 291 enum nvkm_object_map type; 292 int ret = -ENOSYS; 293 294 nvif_ioctl(object, "map size %d\n", size); 295 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 296 nvif_ioctl(object, "map vers %d\n", args->v0.version); 297 ret = nvkm_object_map(object, data, size, &type, &args->v0.tag, 298 &args->v0.handle, 299 &args->v0.length); 300 if (type == NVKM_OBJECT_MAP_IO) 301 args->v0.type = NVIF_IOCTL_MAP_V0_IO; 302 else 303 args->v0.type = NVIF_IOCTL_MAP_V0_VA; 304 } 305 306 return ret; 307 } 308 309 static int 310 nvkm_ioctl_unmap(struct nvkm_client *client, 311 struct nvkm_object *object, void *data, u32 size) 312 { 313 union { 314 struct nvif_ioctl_unmap none; 315 } *args = data; 316 int ret = -ENOSYS; 317 318 nvif_ioctl(object, "unmap size %d\n", size); 319 if (!(ret = nvif_unvers(ret, &data, &size, args->none))) { 320 nvif_ioctl(object, "unmap\n"); 321 ret = nvkm_object_unmap(object); 322 } 323 324 return ret; 325 } 326 327 static int 328 nvkm_ioctl_ntfy_new(struct nvkm_client *client, 329 struct nvkm_object *object, void *data, u32 size) 330 { 331 union { 332 struct nvif_ioctl_ntfy_new_v0 v0; 333 } *args = data; 334 struct nvkm_event *event; 335 int ret = -ENOSYS; 336 337 nvif_ioctl(object, "ntfy new size %d\n", size); 338 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 339 nvif_ioctl(object, "ntfy new vers %d event %02x\n", 340 args->v0.version, args->v0.event); 341 ret = nvkm_object_ntfy(object, args->v0.event, &event); 342 if (ret == 0) { 343 ret = nvkm_client_notify_new(object, event, data, size); 344 if (ret >= 0) { 345 args->v0.index = ret; 346 ret = 0; 347 } 348 } 349 } 350 351 return ret; 352 } 353 354 static int 355 nvkm_ioctl_ntfy_del(struct nvkm_client *client, 356 struct nvkm_object *object, void *data, u32 size) 357 { 358 union { 359 struct nvif_ioctl_ntfy_del_v0 v0; 360 } *args = data; 361 int ret = -ENOSYS; 362 363 nvif_ioctl(object, "ntfy del size %d\n", size); 364 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 365 nvif_ioctl(object, "ntfy del vers %d index %d\n", 366 args->v0.version, args->v0.index); 367 ret = nvkm_client_notify_del(client, args->v0.index); 368 } 369 370 return ret; 371 } 372 373 static int 374 nvkm_ioctl_ntfy_get(struct nvkm_client *client, 375 struct nvkm_object *object, void *data, u32 size) 376 { 377 union { 378 struct nvif_ioctl_ntfy_get_v0 v0; 379 } *args = data; 380 int ret = -ENOSYS; 381 382 nvif_ioctl(object, "ntfy get size %d\n", size); 383 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 384 nvif_ioctl(object, "ntfy get vers %d index %d\n", 385 args->v0.version, args->v0.index); 386 ret = nvkm_client_notify_get(client, args->v0.index); 387 } 388 389 return ret; 390 } 391 392 static int 393 nvkm_ioctl_ntfy_put(struct nvkm_client *client, 394 struct nvkm_object *object, void *data, u32 size) 395 { 396 union { 397 struct nvif_ioctl_ntfy_put_v0 v0; 398 } *args = data; 399 int ret = -ENOSYS; 400 401 nvif_ioctl(object, "ntfy put size %d\n", size); 402 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 403 nvif_ioctl(object, "ntfy put vers %d index %d\n", 404 args->v0.version, args->v0.index); 405 ret = nvkm_client_notify_put(client, args->v0.index); 406 } 407 408 return ret; 409 } 410 411 static struct { 412 int version; 413 int (*func)(struct nvkm_client *, struct nvkm_object *, void *, u32); 414 } 415 nvkm_ioctl_v0[] = { 416 { 0x00, nvkm_ioctl_nop }, 417 { 0x00, nvkm_ioctl_sclass }, 418 { 0x00, nvkm_ioctl_new }, 419 { 0x00, nvkm_ioctl_del }, 420 { 0x00, nvkm_ioctl_mthd }, 421 { 0x00, nvkm_ioctl_rd }, 422 { 0x00, nvkm_ioctl_wr }, 423 { 0x00, nvkm_ioctl_map }, 424 { 0x00, nvkm_ioctl_unmap }, 425 { 0x00, nvkm_ioctl_ntfy_new }, 426 { 0x00, nvkm_ioctl_ntfy_del }, 427 { 0x00, nvkm_ioctl_ntfy_get }, 428 { 0x00, nvkm_ioctl_ntfy_put }, 429 { 0x00, nvkm_ioctl_map_netbsd }, 430 }; 431 432 static int 433 nvkm_ioctl_path(struct nvkm_client *client, u64 handle, u32 type, 434 void *data, u32 size, u8 owner, u8 *route, u64 *token) 435 { 436 struct nvkm_object *object; 437 int ret; 438 439 object = nvkm_object_search(client, handle, NULL); 440 if (IS_ERR(object)) { 441 nvif_ioctl(&client->object, "object not found\n"); 442 return PTR_ERR(object); 443 } 444 445 if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != object->route) { 446 nvif_ioctl(&client->object, "route != owner\n"); 447 return -EACCES; 448 } 449 *route = object->route; 450 *token = object->token; 451 452 if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) { 453 if (nvkm_ioctl_v0[type].version == 0) 454 ret = nvkm_ioctl_v0[type].func(client, object, data, size); 455 } 456 457 return ret; 458 } 459 460 int 461 nvkm_ioctl(struct nvkm_client *client, bool supervisor, 462 void *data, u32 size, void **hack) 463 { 464 struct nvkm_object *object = &client->object; 465 union { 466 struct nvif_ioctl_v0 v0; 467 } *args = data; 468 int ret = -ENOSYS; 469 470 client->super = supervisor; 471 nvif_ioctl(object, "size %d\n", size); 472 473 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 474 nvif_ioctl(object, 475 "vers %d type %02x object %016"PRIx64" owner %02x\n", 476 args->v0.version, args->v0.type, args->v0.object, 477 args->v0.owner); 478 ret = nvkm_ioctl_path(client, args->v0.object, args->v0.type, 479 data, size, args->v0.owner, 480 &args->v0.route, &args->v0.token); 481 } 482 483 if (ret != 1) { 484 nvif_ioctl(object, "return %d\n", ret); 485 if (hack) { 486 *hack = client->data; 487 client->data = NULL; 488 } 489 } 490 #ifdef __NetBSD__ 491 else 492 ret = 0; 493 #endif 494 495 return ret; 496 } 497