1 /* $NetBSD: nouveau_abi16.c,v 1.6 2021/12/18 23:45:32 riastradh Exp $ */ 2 3 /* 4 * Copyright 2012 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 */ 25 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: nouveau_abi16.c,v 1.6 2021/12/18 23:45:32 riastradh Exp $"); 28 29 #include <nvif/client.h> 30 #include <nvif/driver.h> 31 #include <nvif/fifo.h> 32 #include <nvif/ioctl.h> 33 #include <nvif/class.h> 34 #include <nvif/cl0002.h> 35 #include <nvif/cla06f.h> 36 #include <nvif/unpack.h> 37 38 #include "nouveau_drv.h" 39 #include "nouveau_dma.h" 40 #include "nouveau_gem.h" 41 #include "nouveau_chan.h" 42 #include "nouveau_abi16.h" 43 #include "nouveau_vmm.h" 44 45 static struct nouveau_abi16 * 46 nouveau_abi16(struct drm_file *file_priv) 47 { 48 struct nouveau_cli *cli = nouveau_cli(file_priv); 49 if (!cli->abi16) { 50 struct nouveau_abi16 *abi16; 51 cli->abi16 = abi16 = kzalloc(sizeof(*abi16), GFP_KERNEL); 52 if (cli->abi16) { 53 struct nv_device_v0 args = { 54 .device = ~0ULL, 55 }; 56 57 INIT_LIST_HEAD(&abi16->channels); 58 59 /* allocate device object targeting client's default 60 * device (ie. the one that belongs to the fd it 61 * opened) 62 */ 63 if (nvif_device_init(&cli->base.object, 0, NV_DEVICE, 64 &args, sizeof(args), 65 &abi16->device) == 0) 66 return cli->abi16; 67 68 kfree(cli->abi16); 69 cli->abi16 = NULL; 70 } 71 } 72 return cli->abi16; 73 } 74 75 struct nouveau_abi16 * 76 nouveau_abi16_get(struct drm_file *file_priv) 77 { 78 struct nouveau_cli *cli = nouveau_cli(file_priv); 79 mutex_lock(&cli->mutex); 80 if (nouveau_abi16(file_priv)) 81 return cli->abi16; 82 mutex_unlock(&cli->mutex); 83 return NULL; 84 } 85 86 int 87 nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret) 88 { 89 struct nouveau_cli *cli = (void *)abi16->device.object.client; 90 mutex_unlock(&cli->mutex); 91 return ret; 92 } 93 94 s32 95 nouveau_abi16_swclass(struct nouveau_drm *drm) 96 { 97 switch (drm->client.device.info.family) { 98 case NV_DEVICE_INFO_V0_TNT: 99 return NVIF_CLASS_SW_NV04; 100 case NV_DEVICE_INFO_V0_CELSIUS: 101 case NV_DEVICE_INFO_V0_KELVIN: 102 case NV_DEVICE_INFO_V0_RANKINE: 103 case NV_DEVICE_INFO_V0_CURIE: 104 return NVIF_CLASS_SW_NV10; 105 case NV_DEVICE_INFO_V0_TESLA: 106 return NVIF_CLASS_SW_NV50; 107 case NV_DEVICE_INFO_V0_FERMI: 108 case NV_DEVICE_INFO_V0_KEPLER: 109 case NV_DEVICE_INFO_V0_MAXWELL: 110 case NV_DEVICE_INFO_V0_PASCAL: 111 case NV_DEVICE_INFO_V0_VOLTA: 112 return NVIF_CLASS_SW_GF100; 113 } 114 115 return 0x0000; 116 } 117 118 static void 119 nouveau_abi16_ntfy_fini(struct nouveau_abi16_chan *chan, 120 struct nouveau_abi16_ntfy *ntfy) 121 { 122 nvif_object_fini(&ntfy->object); 123 nvkm_mm_free(&chan->heap, &ntfy->node); 124 list_del(&ntfy->head); 125 kfree(ntfy); 126 } 127 128 static void 129 nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16, 130 struct nouveau_abi16_chan *chan) 131 { 132 struct nouveau_abi16_ntfy *ntfy, *temp; 133 134 /* wait for all activity to stop before releasing notify object, which 135 * may be still in use */ 136 if (chan->chan && chan->ntfy) 137 nouveau_channel_idle(chan->chan); 138 139 /* cleanup notifier state */ 140 list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) { 141 nouveau_abi16_ntfy_fini(chan, ntfy); 142 } 143 144 if (chan->ntfy) { 145 nouveau_vma_del(&chan->ntfy_vma); 146 nouveau_bo_unpin(chan->ntfy); 147 drm_gem_object_put_unlocked(&chan->ntfy->bo.base); 148 } 149 150 if (chan->heap.block_size) 151 nvkm_mm_fini(&chan->heap); 152 153 /* destroy channel object, all children will be killed too */ 154 if (chan->chan) { 155 nouveau_channel_idle(chan->chan); 156 nouveau_channel_del(&chan->chan); 157 } 158 159 list_del(&chan->head); 160 kfree(chan); 161 } 162 163 void 164 nouveau_abi16_fini(struct nouveau_abi16 *abi16) 165 { 166 struct nouveau_cli *cli = (void *)abi16->device.object.client; 167 struct nouveau_abi16_chan *chan, *temp; 168 169 /* cleanup channels */ 170 list_for_each_entry_safe(chan, temp, &abi16->channels, head) { 171 nouveau_abi16_chan_fini(abi16, chan); 172 } 173 174 /* destroy the device object */ 175 nvif_device_fini(&abi16->device); 176 177 kfree(cli->abi16); 178 cli->abi16 = NULL; 179 } 180 181 int 182 nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS) 183 { 184 struct nouveau_cli *cli = nouveau_cli(file_priv); 185 struct nouveau_drm *drm = nouveau_drm(dev); 186 struct nvif_device *device = &drm->client.device; 187 struct nvkm_gr *gr = nvxx_gr(device); 188 struct drm_nouveau_getparam *getparam = data; 189 190 switch (getparam->param) { 191 case NOUVEAU_GETPARAM_CHIPSET_ID: 192 getparam->value = device->info.chipset; 193 break; 194 case NOUVEAU_GETPARAM_PCI_VENDOR: 195 if (device->info.platform != NV_DEVICE_INFO_V0_SOC) 196 getparam->value = dev->pdev->vendor; 197 else 198 getparam->value = 0; 199 break; 200 case NOUVEAU_GETPARAM_PCI_DEVICE: 201 if (device->info.platform != NV_DEVICE_INFO_V0_SOC) 202 getparam->value = dev->pdev->device; 203 else 204 getparam->value = 0; 205 break; 206 case NOUVEAU_GETPARAM_BUS_TYPE: 207 switch (device->info.platform) { 208 case NV_DEVICE_INFO_V0_AGP : getparam->value = 0; break; 209 case NV_DEVICE_INFO_V0_PCI : getparam->value = 1; break; 210 case NV_DEVICE_INFO_V0_PCIE: getparam->value = 2; break; 211 case NV_DEVICE_INFO_V0_SOC : getparam->value = 3; break; 212 case NV_DEVICE_INFO_V0_IGP : 213 if (!pci_is_pcie(dev->pdev)) 214 getparam->value = 1; 215 else 216 getparam->value = 2; 217 break; 218 default: 219 WARN_ON(1); 220 break; 221 } 222 break; 223 case NOUVEAU_GETPARAM_FB_SIZE: 224 getparam->value = drm->gem.vram_available; 225 break; 226 case NOUVEAU_GETPARAM_AGP_SIZE: 227 getparam->value = drm->gem.gart_available; 228 break; 229 case NOUVEAU_GETPARAM_VM_VRAM_BASE: 230 getparam->value = 0; /* deprecated */ 231 break; 232 case NOUVEAU_GETPARAM_PTIMER_TIME: 233 getparam->value = nvif_device_time(device); 234 break; 235 case NOUVEAU_GETPARAM_HAS_BO_USAGE: 236 getparam->value = 1; 237 break; 238 case NOUVEAU_GETPARAM_HAS_PAGEFLIP: 239 getparam->value = 1; 240 break; 241 case NOUVEAU_GETPARAM_GRAPH_UNITS: 242 getparam->value = nvkm_gr_units(gr); 243 break; 244 default: 245 NV_PRINTK(dbg, cli, "unknown parameter %"PRId64"\n", getparam->param); 246 return -EINVAL; 247 } 248 249 return 0; 250 } 251 252 int 253 nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS) 254 { 255 struct drm_nouveau_channel_alloc *init = data; 256 struct nouveau_cli *cli = nouveau_cli(file_priv); 257 struct nouveau_drm *drm = nouveau_drm(dev); 258 struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv); 259 struct nouveau_abi16_chan *chan; 260 struct nvif_device *device; 261 u64 engine; 262 int ret; 263 264 if (unlikely(!abi16)) 265 return -ENOMEM; 266 267 if (!drm->channel) 268 return nouveau_abi16_put(abi16, -ENODEV); 269 270 device = &abi16->device; 271 272 /* hack to allow channel engine type specification on kepler */ 273 if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) { 274 if (init->fb_ctxdma_handle == ~0) { 275 switch (init->tt_ctxdma_handle) { 276 case 0x01: engine = NV_DEVICE_INFO_ENGINE_GR ; break; 277 case 0x02: engine = NV_DEVICE_INFO_ENGINE_MSPDEC; break; 278 case 0x04: engine = NV_DEVICE_INFO_ENGINE_MSPPP ; break; 279 case 0x08: engine = NV_DEVICE_INFO_ENGINE_MSVLD ; break; 280 case 0x30: engine = NV_DEVICE_INFO_ENGINE_CE ; break; 281 default: 282 return nouveau_abi16_put(abi16, -ENOSYS); 283 } 284 } else { 285 engine = NV_DEVICE_INFO_ENGINE_GR; 286 } 287 288 if (engine != NV_DEVICE_INFO_ENGINE_CE) 289 engine = nvif_fifo_runlist(device, engine); 290 else 291 engine = nvif_fifo_runlist_ce(device); 292 init->fb_ctxdma_handle = engine; 293 init->tt_ctxdma_handle = 0; 294 } 295 296 if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0) 297 return nouveau_abi16_put(abi16, -EINVAL); 298 299 /* allocate "abi16 channel" data and make up a handle for it */ 300 chan = kzalloc(sizeof(*chan), GFP_KERNEL); 301 if (!chan) 302 return nouveau_abi16_put(abi16, -ENOMEM); 303 304 INIT_LIST_HEAD(&chan->notifiers); 305 list_add(&chan->head, &abi16->channels); 306 307 /* create channel object and initialise dma and fence management */ 308 ret = nouveau_channel_new(drm, device, init->fb_ctxdma_handle, 309 init->tt_ctxdma_handle, false, &chan->chan); 310 if (ret) 311 goto done; 312 313 init->channel = chan->chan->chid; 314 315 if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) 316 init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM | 317 NOUVEAU_GEM_DOMAIN_GART; 318 else 319 if (chan->chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM) 320 init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM; 321 else 322 init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART; 323 324 if (device->info.family < NV_DEVICE_INFO_V0_CELSIUS) { 325 init->subchan[0].handle = 0x00000000; 326 init->subchan[0].grclass = 0x0000; 327 init->subchan[1].handle = chan->chan->nvsw.handle; 328 init->subchan[1].grclass = 0x506e; 329 init->nr_subchan = 2; 330 } 331 332 /* Named memory object area */ 333 ret = nouveau_gem_new(cli, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART, 334 0, 0, &chan->ntfy); 335 if (ret == 0) 336 ret = nouveau_bo_pin(chan->ntfy, TTM_PL_FLAG_TT, false); 337 if (ret) 338 goto done; 339 340 if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) { 341 ret = nouveau_vma_new(chan->ntfy, chan->chan->vmm, 342 &chan->ntfy_vma); 343 if (ret) 344 goto done; 345 } 346 347 ret = drm_gem_handle_create(file_priv, &chan->ntfy->bo.base, 348 &init->notifier_handle); 349 if (ret) 350 goto done; 351 352 ret = nvkm_mm_init(&chan->heap, 0, 0, PAGE_SIZE, 1); 353 done: 354 if (ret) 355 nouveau_abi16_chan_fini(abi16, chan); 356 return nouveau_abi16_put(abi16, ret); 357 } 358 359 static struct nouveau_abi16_chan * 360 nouveau_abi16_chan(struct nouveau_abi16 *abi16, int channel) 361 { 362 struct nouveau_abi16_chan *chan; 363 364 list_for_each_entry(chan, &abi16->channels, head) { 365 if (chan->chan->chid == channel) 366 return chan; 367 } 368 369 return NULL; 370 } 371 372 int 373 nouveau_abi16_usif(struct drm_file *file_priv, void *data, u32 size) 374 { 375 union { 376 struct nvif_ioctl_v0 v0; 377 } *args = data; 378 struct nouveau_abi16_chan *chan; 379 struct nouveau_abi16 *abi16; 380 int ret = -ENOSYS; 381 382 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 383 switch (args->v0.type) { 384 case NVIF_IOCTL_V0_NEW: 385 case NVIF_IOCTL_V0_MTHD: 386 case NVIF_IOCTL_V0_SCLASS: 387 break; 388 default: 389 return -EACCES; 390 } 391 } else 392 return ret; 393 394 if (!(abi16 = nouveau_abi16(file_priv))) 395 return -ENOMEM; 396 397 if (args->v0.token != ~0ULL) { 398 if (!(chan = nouveau_abi16_chan(abi16, args->v0.token))) 399 return -EINVAL; 400 args->v0.object = nvif_handle(&chan->chan->user); 401 args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY; 402 return 0; 403 } 404 405 args->v0.object = nvif_handle(&abi16->device.object); 406 args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY; 407 return 0; 408 } 409 410 int 411 nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS) 412 { 413 struct drm_nouveau_channel_free *req = data; 414 struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv); 415 struct nouveau_abi16_chan *chan; 416 417 if (unlikely(!abi16)) 418 return -ENOMEM; 419 420 chan = nouveau_abi16_chan(abi16, req->channel); 421 if (!chan) 422 return nouveau_abi16_put(abi16, -ENOENT); 423 nouveau_abi16_chan_fini(abi16, chan); 424 return nouveau_abi16_put(abi16, 0); 425 } 426 427 int 428 nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS) 429 { 430 struct drm_nouveau_grobj_alloc *init = data; 431 struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv); 432 struct nouveau_abi16_chan *chan; 433 struct nouveau_abi16_ntfy *ntfy; 434 struct nvif_client *client; 435 struct nvif_sclass *sclass; 436 s32 oclass = 0; 437 int ret, i; 438 439 if (unlikely(!abi16)) 440 return -ENOMEM; 441 442 if (init->handle == ~0) 443 return nouveau_abi16_put(abi16, -EINVAL); 444 client = abi16->device.object.client; 445 446 chan = nouveau_abi16_chan(abi16, init->channel); 447 if (!chan) 448 return nouveau_abi16_put(abi16, -ENOENT); 449 450 ret = nvif_object_sclass_get(&chan->chan->user, &sclass); 451 if (ret < 0) 452 return nouveau_abi16_put(abi16, ret); 453 454 if ((init->class & 0x00ff) == 0x006e) { 455 /* nvsw: compatibility with older 0x*6e class identifier */ 456 for (i = 0; !oclass && i < ret; i++) { 457 switch (sclass[i].oclass) { 458 case NVIF_CLASS_SW_NV04: 459 case NVIF_CLASS_SW_NV10: 460 case NVIF_CLASS_SW_NV50: 461 case NVIF_CLASS_SW_GF100: 462 oclass = sclass[i].oclass; 463 break; 464 default: 465 break; 466 } 467 } 468 } else 469 if ((init->class & 0x00ff) == 0x00b1) { 470 /* msvld: compatibility with incorrect version exposure */ 471 for (i = 0; i < ret; i++) { 472 if ((sclass[i].oclass & 0x00ff) == 0x00b1) { 473 oclass = sclass[i].oclass; 474 break; 475 } 476 } 477 } else 478 if ((init->class & 0x00ff) == 0x00b2) { /* mspdec */ 479 /* mspdec: compatibility with incorrect version exposure */ 480 for (i = 0; i < ret; i++) { 481 if ((sclass[i].oclass & 0x00ff) == 0x00b2) { 482 oclass = sclass[i].oclass; 483 break; 484 } 485 } 486 } else 487 if ((init->class & 0x00ff) == 0x00b3) { /* msppp */ 488 /* msppp: compatibility with incorrect version exposure */ 489 for (i = 0; i < ret; i++) { 490 if ((sclass[i].oclass & 0x00ff) == 0x00b3) { 491 oclass = sclass[i].oclass; 492 break; 493 } 494 } 495 } else { 496 oclass = init->class; 497 } 498 499 nvif_object_sclass_put(&sclass); 500 if (!oclass) 501 return nouveau_abi16_put(abi16, -EINVAL); 502 503 ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL); 504 if (!ntfy) 505 return nouveau_abi16_put(abi16, -ENOMEM); 506 507 list_add(&ntfy->head, &chan->notifiers); 508 509 client->route = NVDRM_OBJECT_ABI16; 510 ret = nvif_object_init(&chan->chan->user, init->handle, oclass, 511 NULL, 0, &ntfy->object); 512 client->route = NVDRM_OBJECT_NVIF; 513 514 if (ret) 515 nouveau_abi16_ntfy_fini(chan, ntfy); 516 return nouveau_abi16_put(abi16, ret); 517 } 518 519 int 520 nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS) 521 { 522 struct drm_nouveau_notifierobj_alloc *info = data; 523 struct nouveau_drm *drm = nouveau_drm(dev); 524 struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv); 525 struct nouveau_abi16_chan *chan; 526 struct nouveau_abi16_ntfy *ntfy; 527 struct nvif_device *device = &abi16->device; 528 struct nvif_client *client; 529 struct nv_dma_v0 args = {}; 530 int ret; 531 532 if (unlikely(!abi16)) 533 return -ENOMEM; 534 535 /* completely unnecessary for these chipsets... */ 536 if (unlikely(device->info.family >= NV_DEVICE_INFO_V0_FERMI)) 537 return nouveau_abi16_put(abi16, -EINVAL); 538 client = abi16->device.object.client; 539 540 chan = nouveau_abi16_chan(abi16, info->channel); 541 if (!chan) 542 return nouveau_abi16_put(abi16, -ENOENT); 543 544 ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL); 545 if (!ntfy) 546 return nouveau_abi16_put(abi16, -ENOMEM); 547 548 list_add(&ntfy->head, &chan->notifiers); 549 550 ret = nvkm_mm_head(&chan->heap, 0, 1, info->size, info->size, 1, 551 &ntfy->node); 552 if (ret) 553 goto done; 554 555 args.start = ntfy->node->offset; 556 args.limit = ntfy->node->offset + ntfy->node->length - 1; 557 if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) { 558 args.target = NV_DMA_V0_TARGET_VM; 559 args.access = NV_DMA_V0_ACCESS_VM; 560 args.start += chan->ntfy_vma->addr; 561 args.limit += chan->ntfy_vma->addr; 562 } else 563 if (drm->agp.bridge) { 564 args.target = NV_DMA_V0_TARGET_AGP; 565 args.access = NV_DMA_V0_ACCESS_RDWR; 566 args.start += drm->agp.base + chan->ntfy->bo.offset; 567 args.limit += drm->agp.base + chan->ntfy->bo.offset; 568 } else { 569 args.target = NV_DMA_V0_TARGET_VM; 570 args.access = NV_DMA_V0_ACCESS_RDWR; 571 args.start += chan->ntfy->bo.offset; 572 args.limit += chan->ntfy->bo.offset; 573 } 574 575 client->route = NVDRM_OBJECT_ABI16; 576 client->super = true; 577 ret = nvif_object_init(&chan->chan->user, info->handle, 578 NV_DMA_IN_MEMORY, &args, sizeof(args), 579 &ntfy->object); 580 client->super = false; 581 client->route = NVDRM_OBJECT_NVIF; 582 if (ret) 583 goto done; 584 585 info->offset = ntfy->node->offset; 586 done: 587 if (ret) 588 nouveau_abi16_ntfy_fini(chan, ntfy); 589 return nouveau_abi16_put(abi16, ret); 590 } 591 592 int 593 nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS) 594 { 595 struct drm_nouveau_gpuobj_free *fini = data; 596 struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv); 597 struct nouveau_abi16_chan *chan; 598 struct nouveau_abi16_ntfy *ntfy; 599 int ret = -ENOENT; 600 601 if (unlikely(!abi16)) 602 return -ENOMEM; 603 604 chan = nouveau_abi16_chan(abi16, fini->channel); 605 if (!chan) 606 return nouveau_abi16_put(abi16, -EINVAL); 607 608 /* synchronize with the user channel and destroy the gpu object */ 609 nouveau_channel_idle(chan->chan); 610 611 list_for_each_entry(ntfy, &chan->notifiers, head) { 612 if (ntfy->object.handle == fini->handle) { 613 nouveau_abi16_ntfy_fini(chan, ntfy); 614 ret = 0; 615 break; 616 } 617 } 618 619 return nouveau_abi16_put(abi16, ret); 620 } 621