1/* 2 * Copyright © 2014-2017 Broadcom 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 (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24/** 25 * @file v3d_simulator.c 26 * 27 * Implements V3D simulation on top of a non-V3D GEM fd. 28 * 29 * This file's goal is to emulate the V3D ioctls' behavior in the kernel on 30 * top of the simpenrose software simulator. Generally, V3D driver BOs have a 31 * GEM-side copy of their contents and a simulator-side memory area that the 32 * GEM contents get copied into during simulation. Once simulation is done, 33 * the simulator's data is copied back out to the GEM BOs, so that rendering 34 * appears on the screen as if actual hardware rendering had been done. 35 * 36 * One of the limitations of this code is that we shouldn't really need a 37 * GEM-side BO for non-window-system BOs. However, do we need unique BO 38 * handles for each of our GEM bos so that this file can look up its state 39 * from the handle passed in at submit ioctl time (also, a couple of places 40 * outside of this file still call ioctls directly on the fd). 41 * 42 * Another limitation is that BO import doesn't work unless the underlying 43 * window system's BO size matches what V3D is going to use, which of course 44 * doesn't work out in practice. This means that for now, only DRI3 (V3D 45 * makes the winsys BOs) is supported, not DRI2 (window system makes the winys 46 * BOs). 47 */ 48 49#ifdef USE_V3D_SIMULATOR 50 51#include <stdio.h> 52#include <sys/mman.h> 53#include "c11/threads.h" 54#include "util/hash_table.h" 55#include "util/ralloc.h" 56#include "util/set.h" 57#include "util/u_dynarray.h" 58#include "util/u_memory.h" 59#include "util/u_mm.h" 60#include "util/u_math.h" 61 62#include <xf86drm.h> 63#include "drm-uapi/i915_drm.h" 64#include "drm-uapi/v3d_drm.h" 65 66#include "v3d_simulator.h" 67#include "v3d_simulator_wrapper.h" 68 69/** Global (across GEM fds) state for the simulator */ 70static struct v3d_simulator_state { 71 mtx_t mutex; 72 mtx_t submit_lock; 73 74 struct v3d_hw *v3d; 75 int ver; 76 77 /* Base virtual address of the heap. */ 78 void *mem; 79 /* Base hardware address of the heap. */ 80 uint32_t mem_base; 81 /* Size of the heap. */ 82 uint32_t mem_size; 83 84 struct mem_block *heap; 85 struct mem_block *overflow; 86 87 /** Mapping from GEM fd to struct v3d_simulator_file * */ 88 struct hash_table *fd_map; 89 90 /** Last performance monitor ID. */ 91 uint32_t last_perfid; 92 93 struct util_dynarray bin_oom; 94 int refcount; 95} sim_state = { 96 .mutex = _MTX_INITIALIZER_NP, 97}; 98 99/** Per-GEM-fd state for the simulator. */ 100struct v3d_simulator_file { 101 int fd; 102 103 /** Mapping from GEM handle to struct v3d_simulator_bo * */ 104 struct hash_table *bo_map; 105 106 /** Dynamic array with performance monitors */ 107 struct v3d_simulator_perfmon **perfmons; 108 uint32_t perfmons_size; 109 uint32_t active_perfid; 110 111 struct mem_block *gmp; 112 void *gmp_vaddr; 113 114 /** Actual GEM fd is i915, so we should use their create ioctl. */ 115 bool is_i915; 116}; 117 118/** Wrapper for drm_v3d_bo tracking the simulator-specific state. */ 119struct v3d_simulator_bo { 120 struct v3d_simulator_file *file; 121 122 /** Area for this BO within sim_state->mem */ 123 struct mem_block *block; 124 uint32_t size; 125 uint64_t mmap_offset; 126 void *sim_vaddr; 127 void *gem_vaddr; 128 129 int handle; 130}; 131 132struct v3d_simulator_perfmon { 133 uint32_t ncounters; 134 uint8_t counters[DRM_V3D_MAX_PERF_COUNTERS]; 135 uint64_t values[DRM_V3D_MAX_PERF_COUNTERS]; 136}; 137 138static void * 139int_to_key(int key) 140{ 141 return (void *)(uintptr_t)key; 142} 143 144#define PERFMONS_ALLOC_SIZE 100 145 146static uint32_t 147perfmons_next_id(struct v3d_simulator_file *sim_file) { 148 sim_state.last_perfid++; 149 if (sim_state.last_perfid > sim_file->perfmons_size) { 150 sim_file->perfmons_size += PERFMONS_ALLOC_SIZE; 151 sim_file->perfmons = reralloc(sim_file, 152 sim_file->perfmons, 153 struct v3d_simulator_perfmon *, 154 sim_file->perfmons_size); 155 } 156 157 return sim_state.last_perfid; 158} 159 160static struct v3d_simulator_file * 161v3d_get_simulator_file_for_fd(int fd) 162{ 163 struct hash_entry *entry = _mesa_hash_table_search(sim_state.fd_map, 164 int_to_key(fd + 1)); 165 return entry ? entry->data : NULL; 166} 167 168/* A marker placed just after each BO, then checked after rendering to make 169 * sure it's still there. 170 */ 171#define BO_SENTINEL 0xfedcba98 172 173/* 128kb */ 174#define GMP_ALIGN2 17 175 176/** 177 * Sets the range of GPU virtual address space to have the given GMP 178 * permissions (bit 0 = read, bit 1 = write, write-only forbidden). 179 */ 180static void 181set_gmp_flags(struct v3d_simulator_file *file, 182 uint32_t offset, uint32_t size, uint32_t flag) 183{ 184 assert((offset & ((1 << GMP_ALIGN2) - 1)) == 0); 185 int gmp_offset = offset >> GMP_ALIGN2; 186 int gmp_count = align(size, 1 << GMP_ALIGN2) >> GMP_ALIGN2; 187 uint32_t *gmp = file->gmp_vaddr; 188 189 assert(flag <= 0x3); 190 191 for (int i = gmp_offset; i < gmp_offset + gmp_count; i++) { 192 int32_t bitshift = (i % 16) * 2; 193 gmp[i / 16] &= ~(0x3 << bitshift); 194 gmp[i / 16] |= flag << bitshift; 195 } 196} 197 198/** 199 * Allocates space in simulator memory and returns a tracking struct for it 200 * that also contains the drm_gem_cma_object struct. 201 */ 202static struct v3d_simulator_bo * 203v3d_create_simulator_bo(int fd, unsigned size) 204{ 205 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 206 struct v3d_simulator_bo *sim_bo = rzalloc(file, 207 struct v3d_simulator_bo); 208 size = align(size, 4096); 209 210 sim_bo->file = file; 211 212 mtx_lock(&sim_state.mutex); 213 sim_bo->block = u_mmAllocMem(sim_state.heap, size + 4, GMP_ALIGN2, 0); 214 mtx_unlock(&sim_state.mutex); 215 assert(sim_bo->block); 216 217 set_gmp_flags(file, sim_bo->block->ofs, size, 0x3); 218 219 sim_bo->size = size; 220 221 /* Allocate space for the buffer in simulator memory. */ 222 sim_bo->sim_vaddr = sim_state.mem + sim_bo->block->ofs - sim_state.mem_base; 223 memset(sim_bo->sim_vaddr, 0xd0, size); 224 225 *(uint32_t *)(sim_bo->sim_vaddr + sim_bo->size) = BO_SENTINEL; 226 227 return sim_bo; 228} 229 230static struct v3d_simulator_bo * 231v3d_create_simulator_bo_for_gem(int fd, int handle, unsigned size) 232{ 233 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 234 struct v3d_simulator_bo *sim_bo = 235 v3d_create_simulator_bo(fd, size); 236 237 sim_bo->handle = handle; 238 239 /* Map the GEM buffer for copy in/out to the simulator. i915 blocks 240 * dumb mmap on render nodes, so use their ioctl directly if we're on 241 * one. 242 */ 243 int ret; 244 if (file->is_i915) { 245 struct drm_i915_gem_mmap_gtt map = { 246 .handle = handle, 247 }; 248 249 /* We could potentially use non-gtt (cached) for LLC systems, 250 * but the copy-in/out won't be the limiting factor on 251 * simulation anyway. 252 */ 253 ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &map); 254 sim_bo->mmap_offset = map.offset; 255 } else { 256 struct drm_mode_map_dumb map = { 257 .handle = handle, 258 }; 259 260 ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map); 261 sim_bo->mmap_offset = map.offset; 262 } 263 if (ret) { 264 fprintf(stderr, "Failed to get MMAP offset: %d\n", ret); 265 abort(); 266 } 267 268 sim_bo->gem_vaddr = mmap(NULL, sim_bo->size, 269 PROT_READ | PROT_WRITE, MAP_SHARED, 270 fd, sim_bo->mmap_offset); 271 if (sim_bo->gem_vaddr == MAP_FAILED) { 272 fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n", 273 handle, (long long)sim_bo->mmap_offset, sim_bo->size); 274 abort(); 275 } 276 277 /* A handle of 0 is used for v3d_gem.c internal allocations that 278 * don't need to go in the lookup table. 279 */ 280 if (handle != 0) { 281 mtx_lock(&sim_state.mutex); 282 _mesa_hash_table_insert(file->bo_map, int_to_key(handle), 283 sim_bo); 284 mtx_unlock(&sim_state.mutex); 285 } 286 287 return sim_bo; 288} 289 290static int bin_fd; 291 292uint32_t 293v3d_simulator_get_spill(uint32_t spill_size) 294{ 295 struct v3d_simulator_bo *sim_bo = 296 v3d_create_simulator_bo(bin_fd, spill_size); 297 298 util_dynarray_append(&sim_state.bin_oom, struct v3d_simulator_bo *, 299 sim_bo); 300 301 return sim_bo->block->ofs; 302} 303 304static void 305v3d_free_simulator_bo(struct v3d_simulator_bo *sim_bo) 306{ 307 struct v3d_simulator_file *sim_file = sim_bo->file; 308 309 set_gmp_flags(sim_file, sim_bo->block->ofs, sim_bo->size, 0x0); 310 311 if (sim_bo->gem_vaddr) 312 munmap(sim_bo->gem_vaddr, sim_bo->size); 313 314 mtx_lock(&sim_state.mutex); 315 u_mmFreeMem(sim_bo->block); 316 if (sim_bo->handle) { 317 _mesa_hash_table_remove_key(sim_file->bo_map, 318 int_to_key(sim_bo->handle)); 319 } 320 mtx_unlock(&sim_state.mutex); 321 ralloc_free(sim_bo); 322} 323 324static struct v3d_simulator_bo * 325v3d_get_simulator_bo(struct v3d_simulator_file *file, int gem_handle) 326{ 327 if (gem_handle == 0) 328 return NULL; 329 330 mtx_lock(&sim_state.mutex); 331 struct hash_entry *entry = 332 _mesa_hash_table_search(file->bo_map, int_to_key(gem_handle)); 333 mtx_unlock(&sim_state.mutex); 334 335 return entry ? entry->data : NULL; 336} 337 338static void 339v3d_simulator_copy_in_handle(struct v3d_simulator_file *file, int handle) 340{ 341 struct v3d_simulator_bo *sim_bo = v3d_get_simulator_bo(file, handle); 342 343 if (!sim_bo) 344 return; 345 346 memcpy(sim_bo->sim_vaddr, sim_bo->gem_vaddr, sim_bo->size); 347} 348 349static void 350v3d_simulator_copy_out_handle(struct v3d_simulator_file *file, int handle) 351{ 352 struct v3d_simulator_bo *sim_bo = v3d_get_simulator_bo(file, handle); 353 354 if (!sim_bo) 355 return; 356 357 memcpy(sim_bo->gem_vaddr, sim_bo->sim_vaddr, sim_bo->size); 358 359 if (*(uint32_t *)(sim_bo->sim_vaddr + 360 sim_bo->size) != BO_SENTINEL) { 361 fprintf(stderr, "Buffer overflow in handle %d\n", 362 handle); 363 } 364} 365 366static int 367v3d_simulator_pin_bos(struct v3d_simulator_file *file, 368 struct drm_v3d_submit_cl *submit) 369{ 370 uint32_t *bo_handles = (uint32_t *)(uintptr_t)submit->bo_handles; 371 372 for (int i = 0; i < submit->bo_handle_count; i++) 373 v3d_simulator_copy_in_handle(file, bo_handles[i]); 374 375 return 0; 376} 377 378static int 379v3d_simulator_unpin_bos(struct v3d_simulator_file *file, 380 struct drm_v3d_submit_cl *submit) 381{ 382 uint32_t *bo_handles = (uint32_t *)(uintptr_t)submit->bo_handles; 383 384 for (int i = 0; i < submit->bo_handle_count; i++) 385 v3d_simulator_copy_out_handle(file, bo_handles[i]); 386 387 return 0; 388} 389 390static struct v3d_simulator_perfmon * 391v3d_get_simulator_perfmon(int fd, uint32_t perfid) 392{ 393 if (!perfid || perfid > sim_state.last_perfid) 394 return NULL; 395 396 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 397 398 mtx_lock(&sim_state.mutex); 399 assert(perfid <= file->perfmons_size); 400 struct v3d_simulator_perfmon *perfmon = file->perfmons[perfid - 1]; 401 mtx_unlock(&sim_state.mutex); 402 403 return perfmon; 404} 405 406static void 407v3d_simulator_perfmon_switch(int fd, uint32_t perfid) 408{ 409 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 410 struct v3d_simulator_perfmon *perfmon; 411 412 if (perfid == file->active_perfid) 413 return; 414 415 perfmon = v3d_get_simulator_perfmon(fd, file->active_perfid); 416 if (perfmon) 417 v3d41_simulator_perfmon_stop(sim_state.v3d, 418 perfmon->ncounters, 419 perfmon->values); 420 421 perfmon = v3d_get_simulator_perfmon(fd, perfid); 422 if (perfmon) 423 v3d41_simulator_perfmon_start(sim_state.v3d, 424 perfmon->ncounters, 425 perfmon->counters); 426 427 file->active_perfid = perfid; 428} 429 430static int 431v3d_simulator_submit_cl_ioctl(int fd, struct drm_v3d_submit_cl *submit) 432{ 433 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 434 int ret; 435 436 ret = v3d_simulator_pin_bos(file, submit); 437 if (ret) 438 return ret; 439 440 mtx_lock(&sim_state.submit_lock); 441 bin_fd = fd; 442 443 v3d_simulator_perfmon_switch(fd, submit->perfmon_id); 444 445 if (sim_state.ver >= 41) 446 v3d41_simulator_submit_cl_ioctl(sim_state.v3d, submit, file->gmp->ofs); 447 else 448 v3d33_simulator_submit_cl_ioctl(sim_state.v3d, submit, file->gmp->ofs); 449 450 util_dynarray_foreach(&sim_state.bin_oom, struct v3d_simulator_bo *, 451 sim_bo) { 452 v3d_free_simulator_bo(*sim_bo); 453 } 454 util_dynarray_clear(&sim_state.bin_oom); 455 456 mtx_unlock(&sim_state.submit_lock); 457 458 ret = v3d_simulator_unpin_bos(file, submit); 459 if (ret) 460 return ret; 461 462 return 0; 463} 464 465/** 466 * Do fixups after a BO has been opened from a handle. 467 * 468 * This could be done at DRM_IOCTL_GEM_OPEN/DRM_IOCTL_GEM_PRIME_FD_TO_HANDLE 469 * time, but we're still using drmPrimeFDToHandle() so we have this helper to 470 * be called afterward instead. 471 */ 472void v3d_simulator_open_from_handle(int fd, int handle, uint32_t size) 473{ 474 v3d_create_simulator_bo_for_gem(fd, handle, size); 475} 476 477/** 478 * Simulated ioctl(fd, DRM_V3D_CREATE_BO) implementation. 479 * 480 * Making a V3D BO is just a matter of making a corresponding BO on the host. 481 */ 482static int 483v3d_simulator_create_bo_ioctl(int fd, struct drm_v3d_create_bo *args) 484{ 485 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 486 487 /* i915 bans dumb create on render nodes, so we have to use their 488 * native ioctl in case we're on a render node. 489 */ 490 int ret; 491 if (file->is_i915) { 492 struct drm_i915_gem_create create = { 493 .size = args->size, 494 }; 495 ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create); 496 497 args->handle = create.handle; 498 } else { 499 struct drm_mode_create_dumb create = { 500 .width = 128, 501 .bpp = 8, 502 .height = (args->size + 127) / 128, 503 }; 504 505 ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); 506 assert(ret != 0 || create.size >= args->size); 507 508 args->handle = create.handle; 509 } 510 511 if (ret == 0) { 512 struct v3d_simulator_bo *sim_bo = 513 v3d_create_simulator_bo_for_gem(fd, args->handle, 514 args->size); 515 516 args->offset = sim_bo->block->ofs; 517 } 518 519 return ret; 520} 521 522/** 523 * Simulated ioctl(fd, DRM_V3D_MMAP_BO) implementation. 524 * 525 * We've already grabbed the mmap offset when we created the sim bo, so just 526 * return it. 527 */ 528static int 529v3d_simulator_mmap_bo_ioctl(int fd, struct drm_v3d_mmap_bo *args) 530{ 531 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 532 struct v3d_simulator_bo *sim_bo = v3d_get_simulator_bo(file, 533 args->handle); 534 535 args->offset = sim_bo->mmap_offset; 536 537 return 0; 538} 539 540static int 541v3d_simulator_get_bo_offset_ioctl(int fd, struct drm_v3d_get_bo_offset *args) 542{ 543 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 544 struct v3d_simulator_bo *sim_bo = v3d_get_simulator_bo(file, 545 args->handle); 546 547 args->offset = sim_bo->block->ofs; 548 549 return 0; 550} 551 552static int 553v3d_simulator_gem_close_ioctl(int fd, struct drm_gem_close *args) 554{ 555 /* Free the simulator's internal tracking. */ 556 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 557 struct v3d_simulator_bo *sim_bo = v3d_get_simulator_bo(file, 558 args->handle); 559 560 v3d_free_simulator_bo(sim_bo); 561 562 /* Pass the call on down. */ 563 return drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, args); 564} 565 566static int 567v3d_simulator_get_param_ioctl(int fd, struct drm_v3d_get_param *args) 568{ 569 if (sim_state.ver >= 41) 570 return v3d41_simulator_get_param_ioctl(sim_state.v3d, args); 571 else 572 return v3d33_simulator_get_param_ioctl(sim_state.v3d, args); 573} 574 575static int 576v3d_simulator_submit_tfu_ioctl(int fd, struct drm_v3d_submit_tfu *args) 577{ 578 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 579 int ret; 580 581 v3d_simulator_copy_in_handle(file, args->bo_handles[0]); 582 v3d_simulator_copy_in_handle(file, args->bo_handles[1]); 583 v3d_simulator_copy_in_handle(file, args->bo_handles[2]); 584 v3d_simulator_copy_in_handle(file, args->bo_handles[3]); 585 586 if (sim_state.ver >= 41) 587 ret = v3d41_simulator_submit_tfu_ioctl(sim_state.v3d, args); 588 else 589 ret = v3d33_simulator_submit_tfu_ioctl(sim_state.v3d, args); 590 591 v3d_simulator_copy_out_handle(file, args->bo_handles[0]); 592 593 return ret; 594} 595 596static int 597v3d_simulator_submit_csd_ioctl(int fd, struct drm_v3d_submit_csd *args) 598{ 599 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 600 uint32_t *bo_handles = (uint32_t *)(uintptr_t)args->bo_handles; 601 int ret; 602 603 for (int i = 0; i < args->bo_handle_count; i++) 604 v3d_simulator_copy_in_handle(file, bo_handles[i]); 605 606 v3d_simulator_perfmon_switch(fd, args->perfmon_id); 607 608 if (sim_state.ver >= 41) 609 ret = v3d41_simulator_submit_csd_ioctl(sim_state.v3d, args, 610 file->gmp->ofs); 611 else 612 ret = -1; 613 614 for (int i = 0; i < args->bo_handle_count; i++) 615 v3d_simulator_copy_out_handle(file, bo_handles[i]); 616 617 return ret; 618} 619 620static int 621v3d_simulator_perfmon_create_ioctl(int fd, struct drm_v3d_perfmon_create *args) 622{ 623 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 624 625 if (args->ncounters == 0 || 626 args->ncounters > DRM_V3D_MAX_PERF_COUNTERS) 627 return -EINVAL; 628 629 struct v3d_simulator_perfmon *perfmon = rzalloc(file, 630 struct v3d_simulator_perfmon); 631 632 perfmon->ncounters = args->ncounters; 633 for (int i = 0; i < args->ncounters; i++) { 634 if (args->counters[i] >= V3D_PERFCNT_NUM) { 635 ralloc_free(perfmon); 636 return -EINVAL; 637 } else { 638 perfmon->counters[i] = args->counters[i]; 639 } 640 } 641 642 mtx_lock(&sim_state.mutex); 643 args->id = perfmons_next_id(file); 644 file->perfmons[args->id - 1] = perfmon; 645 mtx_unlock(&sim_state.mutex); 646 647 return 0; 648} 649 650static int 651v3d_simulator_perfmon_destroy_ioctl(int fd, struct drm_v3d_perfmon_destroy *args) 652{ 653 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 654 struct v3d_simulator_perfmon *perfmon = 655 v3d_get_simulator_perfmon(fd, args->id); 656 657 if (!perfmon) 658 return -EINVAL; 659 660 mtx_lock(&sim_state.mutex); 661 file->perfmons[args->id - 1] = NULL; 662 mtx_unlock(&sim_state.mutex); 663 664 ralloc_free(perfmon); 665 666 return 0; 667} 668 669static int 670v3d_simulator_perfmon_get_values_ioctl(int fd, struct drm_v3d_perfmon_get_values *args) 671{ 672 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd); 673 674 mtx_lock(&sim_state.submit_lock); 675 676 /* Stop the perfmon if it is still active */ 677 if (args->id == file->active_perfid) 678 v3d_simulator_perfmon_switch(fd, 0); 679 680 mtx_unlock(&sim_state.submit_lock); 681 682 struct v3d_simulator_perfmon *perfmon = 683 v3d_get_simulator_perfmon(fd, args->id); 684 685 if (!perfmon) 686 return -EINVAL; 687 688 memcpy((void *)args->values_ptr, perfmon->values, perfmon->ncounters * sizeof(uint64_t)); 689 690 return 0; 691} 692 693int 694v3d_simulator_ioctl(int fd, unsigned long request, void *args) 695{ 696 switch (request) { 697 case DRM_IOCTL_V3D_SUBMIT_CL: 698 return v3d_simulator_submit_cl_ioctl(fd, args); 699 case DRM_IOCTL_V3D_CREATE_BO: 700 return v3d_simulator_create_bo_ioctl(fd, args); 701 case DRM_IOCTL_V3D_MMAP_BO: 702 return v3d_simulator_mmap_bo_ioctl(fd, args); 703 case DRM_IOCTL_V3D_GET_BO_OFFSET: 704 return v3d_simulator_get_bo_offset_ioctl(fd, args); 705 706 case DRM_IOCTL_V3D_WAIT_BO: 707 /* We do all of the v3d rendering synchronously, so we just 708 * return immediately on the wait ioctls. This ignores any 709 * native rendering to the host BO, so it does mean we race on 710 * front buffer rendering. 711 */ 712 return 0; 713 714 case DRM_IOCTL_V3D_GET_PARAM: 715 return v3d_simulator_get_param_ioctl(fd, args); 716 717 case DRM_IOCTL_GEM_CLOSE: 718 return v3d_simulator_gem_close_ioctl(fd, args); 719 720 case DRM_IOCTL_V3D_SUBMIT_TFU: 721 return v3d_simulator_submit_tfu_ioctl(fd, args); 722 723 case DRM_IOCTL_V3D_SUBMIT_CSD: 724 return v3d_simulator_submit_csd_ioctl(fd, args); 725 726 case DRM_IOCTL_V3D_PERFMON_CREATE: 727 return v3d_simulator_perfmon_create_ioctl(fd, args); 728 729 case DRM_IOCTL_V3D_PERFMON_DESTROY: 730 return v3d_simulator_perfmon_destroy_ioctl(fd, args); 731 732 case DRM_IOCTL_V3D_PERFMON_GET_VALUES: 733 return v3d_simulator_perfmon_get_values_ioctl(fd, args); 734 735 case DRM_IOCTL_GEM_OPEN: 736 case DRM_IOCTL_GEM_FLINK: 737 return drmIoctl(fd, request, args); 738 default: 739 fprintf(stderr, "Unknown ioctl 0x%08x\n", (int)request); 740 abort(); 741 } 742} 743 744uint32_t 745v3d_simulator_get_mem_size(void) 746{ 747 return sim_state.mem_size; 748} 749 750static void 751v3d_simulator_init_global() 752{ 753 mtx_lock(&sim_state.mutex); 754 if (sim_state.refcount++) { 755 mtx_unlock(&sim_state.mutex); 756 return; 757 } 758 759 sim_state.v3d = v3d_hw_auto_new(NULL); 760 v3d_hw_alloc_mem(sim_state.v3d, 1024 * 1024 * 1024); 761 sim_state.mem_base = 762 v3d_hw_get_mem(sim_state.v3d, &sim_state.mem_size, 763 &sim_state.mem); 764 765 /* Allocate from anywhere from 4096 up. We don't allocate at 0, 766 * because for OQs and some other addresses in the HW, 0 means 767 * disabled. 768 */ 769 sim_state.heap = u_mmInit(4096, sim_state.mem_size - 4096); 770 771 /* Make a block of 0xd0 at address 0 to make sure we don't screw up 772 * and land there. 773 */ 774 struct mem_block *b = u_mmAllocMem(sim_state.heap, 4096, GMP_ALIGN2, 0); 775 memset(sim_state.mem + b->ofs - sim_state.mem_base, 0xd0, 4096); 776 777 sim_state.ver = v3d_hw_get_version(sim_state.v3d); 778 779 mtx_unlock(&sim_state.mutex); 780 781 sim_state.fd_map = 782 _mesa_hash_table_create(NULL, 783 _mesa_hash_pointer, 784 _mesa_key_pointer_equal); 785 786 util_dynarray_init(&sim_state.bin_oom, NULL); 787 788 if (sim_state.ver >= 41) 789 v3d41_simulator_init_regs(sim_state.v3d); 790 else 791 v3d33_simulator_init_regs(sim_state.v3d); 792} 793 794struct v3d_simulator_file * 795v3d_simulator_init(int fd) 796{ 797 v3d_simulator_init_global(); 798 799 struct v3d_simulator_file *sim_file = rzalloc(NULL, struct v3d_simulator_file); 800 801 drmVersionPtr version = drmGetVersion(fd); 802 if (version && strncmp(version->name, "i915", version->name_len) == 0) 803 sim_file->is_i915 = true; 804 drmFreeVersion(version); 805 806 sim_file->bo_map = 807 _mesa_hash_table_create(sim_file, 808 _mesa_hash_pointer, 809 _mesa_key_pointer_equal); 810 811 mtx_lock(&sim_state.mutex); 812 _mesa_hash_table_insert(sim_state.fd_map, int_to_key(fd + 1), 813 sim_file); 814 mtx_unlock(&sim_state.mutex); 815 816 sim_file->gmp = u_mmAllocMem(sim_state.heap, 8096, GMP_ALIGN2, 0); 817 sim_file->gmp_vaddr = (sim_state.mem + sim_file->gmp->ofs - 818 sim_state.mem_base); 819 memset(sim_file->gmp_vaddr, 0, 8096); 820 821 return sim_file; 822} 823 824void 825v3d_simulator_destroy(struct v3d_simulator_file *sim_file) 826{ 827 mtx_lock(&sim_state.mutex); 828 if (!--sim_state.refcount) { 829 _mesa_hash_table_destroy(sim_state.fd_map, NULL); 830 util_dynarray_fini(&sim_state.bin_oom); 831 u_mmDestroy(sim_state.heap); 832 /* No memsetting the struct, because it contains the mutex. */ 833 sim_state.mem = NULL; 834 } 835 mtx_unlock(&sim_state.mutex); 836 ralloc_free(sim_file); 837} 838 839#endif /* USE_V3D_SIMULATOR */ 840