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/simple_mtx.h"
58 #include "util/u_dynarray.h"
59 #include "util/u_memory.h"
60 #include "util/u_mm.h"
61 #include "util/u_math.h"
62
63 #include <xf86drm.h>
64 #include "drm-uapi/amdgpu_drm.h"
65 #include "drm-uapi/i915_drm.h"
66 #include "drm-uapi/v3d_drm.h"
67
68 #include "v3d_simulator.h"
69 #include "v3d_simulator_wrapper.h"
70
71 #include "broadcom/common/v3d_csd.h"
72
73 /** Global (across GEM fds) state for the simulator */
74 static struct v3d_simulator_state {
75 simple_mtx_t mutex;
76 mtx_t submit_lock;
77
78 struct v3d_hw *v3d;
79 int ver;
80
81 /* Base virtual address of the heap. */
82 void *mem;
83 /* Base hardware address of the heap. */
84 uint32_t mem_base;
85 /* Size of the heap. */
86 uint32_t mem_size;
87
88 struct mem_block *heap;
89 struct mem_block *overflow;
90
91 /** Mapping from GEM fd to struct v3d_simulator_file * */
92 struct hash_table *fd_map;
93
94 /** Last performance monitor ID. */
95 uint32_t last_perfid;
96
97 /** Total performance counters */
98 uint32_t perfcnt_total;
99
100 struct util_dynarray bin_oom;
101 int refcount;
102 } sim_state = {
103 .mutex = SIMPLE_MTX_INITIALIZER,
104 };
105
106 enum gem_type {
107 GEM_I915,
108 GEM_AMDGPU,
109 GEM_DUMB
110 };
111
112 /** Per-GEM-fd state for the simulator. */
113 struct v3d_simulator_file {
114 int fd;
115
116 /** Mapping from GEM handle to struct v3d_simulator_bo * */
117 struct hash_table *bo_map;
118
119 /** Dynamic array with performance monitors */
120 struct v3d_simulator_perfmon **perfmons;
121 uint32_t perfmons_size;
122 uint32_t active_perfid;
123
124 struct mem_block *gmp;
125 void *gmp_vaddr;
126
127 /** For specific gpus, use their create ioctl. Otherwise use dumb bo. */
128 enum gem_type gem_type;
129 };
130
131 /** Wrapper for drm_v3d_bo tracking the simulator-specific state. */
132 struct v3d_simulator_bo {
133 struct v3d_simulator_file *file;
134
135 /** Area for this BO within sim_state->mem */
136 struct mem_block *block;
137 uint32_t size;
138 uint64_t mmap_offset;
139 void *sim_vaddr;
140 void *gem_vaddr;
141
142 int handle;
143 };
144
145 struct v3d_simulator_perfmon {
146 uint32_t ncounters;
147 uint8_t counters[DRM_V3D_MAX_PERF_COUNTERS];
148 uint64_t values[DRM_V3D_MAX_PERF_COUNTERS];
149 };
150
151 static void *
int_to_key(int key)152 int_to_key(int key)
153 {
154 return (void *)(uintptr_t)key;
155 }
156
157 #define PERFMONS_ALLOC_SIZE 100
158
159 static uint32_t
perfmons_next_id(struct v3d_simulator_file * sim_file)160 perfmons_next_id(struct v3d_simulator_file *sim_file) {
161 sim_state.last_perfid++;
162 if (sim_state.last_perfid > sim_file->perfmons_size) {
163 sim_file->perfmons_size += PERFMONS_ALLOC_SIZE;
164 sim_file->perfmons = reralloc(sim_file,
165 sim_file->perfmons,
166 struct v3d_simulator_perfmon *,
167 sim_file->perfmons_size);
168 }
169
170 return sim_state.last_perfid;
171 }
172
173 static struct v3d_simulator_file *
v3d_get_simulator_file_for_fd(int fd)174 v3d_get_simulator_file_for_fd(int fd)
175 {
176 struct hash_entry *entry = _mesa_hash_table_search(sim_state.fd_map,
177 int_to_key(fd + 1));
178 return entry ? entry->data : NULL;
179 }
180
181 /* A marker placed just after each BO, then checked after rendering to make
182 * sure it's still there.
183 */
184 #define BO_SENTINEL 0xfedcba98
185
186 /* 128kb */
187 #define GMP_ALIGN2 17
188
189 /**
190 * Sets the range of GPU virtual address space to have the given GMP
191 * permissions (bit 0 = read, bit 1 = write, write-only forbidden).
192 */
193 static void
set_gmp_flags(struct v3d_simulator_file * file,uint32_t offset,uint32_t size,uint32_t flag)194 set_gmp_flags(struct v3d_simulator_file *file,
195 uint32_t offset, uint32_t size, uint32_t flag)
196 {
197 assert((offset & ((1 << GMP_ALIGN2) - 1)) == 0);
198 int gmp_offset = offset >> GMP_ALIGN2;
199 int gmp_count = align(size, 1 << GMP_ALIGN2) >> GMP_ALIGN2;
200 uint32_t *gmp = file->gmp_vaddr;
201
202 assert(flag <= 0x3);
203
204 for (int i = gmp_offset; i < gmp_offset + gmp_count; i++) {
205 int32_t bitshift = (i % 16) * 2;
206 gmp[i / 16] &= ~(0x3 << bitshift);
207 gmp[i / 16] |= flag << bitshift;
208 }
209 }
210
211 /**
212 * Allocates space in simulator memory and returns a tracking struct for it
213 * that also contains the drm_gem_cma_object struct.
214 */
215 static struct v3d_simulator_bo *
v3d_create_simulator_bo(int fd,unsigned size)216 v3d_create_simulator_bo(int fd, unsigned size)
217 {
218 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
219
220 simple_mtx_lock(&sim_state.mutex);
221 struct v3d_simulator_bo *sim_bo = rzalloc(file,
222 struct v3d_simulator_bo);
223 sim_bo->block = u_mmAllocMem(sim_state.heap, size + 4, GMP_ALIGN2, 0);
224 simple_mtx_unlock(&sim_state.mutex);
225 assert(sim_bo->block);
226 size = align(size, 4096);
227 sim_bo->file = file;
228 set_gmp_flags(file, sim_bo->block->ofs, size, 0x3);
229
230 sim_bo->size = size;
231
232 /* Allocate space for the buffer in simulator memory. */
233 sim_bo->sim_vaddr = sim_state.mem + sim_bo->block->ofs - sim_state.mem_base;
234 memset(sim_bo->sim_vaddr, 0xd0, size);
235
236 *(uint32_t *)(sim_bo->sim_vaddr + sim_bo->size) = BO_SENTINEL;
237
238 return sim_bo;
239 }
240
241 static struct v3d_simulator_bo *
v3d_create_simulator_bo_for_gem(int fd,int handle,unsigned size)242 v3d_create_simulator_bo_for_gem(int fd, int handle, unsigned size)
243 {
244 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
245 struct v3d_simulator_bo *sim_bo =
246 v3d_create_simulator_bo(fd, size);
247
248 sim_bo->handle = handle;
249
250 /* Map the GEM buffer for copy in/out to the simulator. i915 blocks
251 * dumb mmap on render nodes, so use their ioctl directly if we're on
252 * one.
253 */
254 int ret;
255 switch (file->gem_type) {
256 case GEM_I915:
257 {
258 struct drm_i915_gem_mmap_gtt map = {
259 .handle = handle,
260 };
261
262 /* We could potentially use non-gtt (cached) for LLC systems,
263 * but the copy-in/out won't be the limiting factor on
264 * simulation anyway.
265 */
266 ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &map);
267 sim_bo->mmap_offset = map.offset;
268 break;
269 }
270 case GEM_AMDGPU:
271 {
272 union drm_amdgpu_gem_mmap map = { 0 };
273 map.in.handle = handle;
274
275 ret = drmIoctl(fd, DRM_IOCTL_AMDGPU_GEM_MMAP, &map);
276 sim_bo->mmap_offset = map.out.addr_ptr;
277 break;
278 }
279 default:
280 {
281 struct drm_mode_map_dumb map = {
282 .handle = handle,
283 };
284 ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
285 sim_bo->mmap_offset = map.offset;
286 }
287 }
288 if (ret) {
289 fprintf(stderr, "Failed to get MMAP offset: %d\n", ret);
290 abort();
291 }
292
293 sim_bo->gem_vaddr = mmap(NULL, sim_bo->size,
294 PROT_READ | PROT_WRITE, MAP_SHARED,
295 fd, sim_bo->mmap_offset);
296 if (sim_bo->gem_vaddr == MAP_FAILED) {
297 fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n",
298 handle, (long long)sim_bo->mmap_offset, sim_bo->size);
299 abort();
300 }
301
302 /* A handle of 0 is used for v3d_gem.c internal allocations that
303 * don't need to go in the lookup table.
304 */
305 if (handle != 0) {
306 simple_mtx_lock(&sim_state.mutex);
307 _mesa_hash_table_insert(file->bo_map, int_to_key(handle),
308 sim_bo);
309 simple_mtx_unlock(&sim_state.mutex);
310 }
311
312 return sim_bo;
313 }
314
315 static int bin_fd;
316
317 uint32_t
v3d_simulator_get_spill(uint32_t spill_size)318 v3d_simulator_get_spill(uint32_t spill_size)
319 {
320 struct v3d_simulator_bo *sim_bo =
321 v3d_create_simulator_bo(bin_fd, spill_size);
322
323 util_dynarray_append(&sim_state.bin_oom, struct v3d_simulator_bo *,
324 sim_bo);
325
326 return sim_bo->block->ofs;
327 }
328
329 static void
v3d_free_simulator_bo(struct v3d_simulator_bo * sim_bo)330 v3d_free_simulator_bo(struct v3d_simulator_bo *sim_bo)
331 {
332 struct v3d_simulator_file *sim_file = sim_bo->file;
333
334 set_gmp_flags(sim_file, sim_bo->block->ofs, sim_bo->size, 0x0);
335
336 if (sim_bo->gem_vaddr)
337 munmap(sim_bo->gem_vaddr, sim_bo->size);
338
339 simple_mtx_lock(&sim_state.mutex);
340 u_mmFreeMem(sim_bo->block);
341 if (sim_bo->handle) {
342 _mesa_hash_table_remove_key(sim_file->bo_map,
343 int_to_key(sim_bo->handle));
344 }
345 ralloc_free(sim_bo);
346 simple_mtx_unlock(&sim_state.mutex);
347 }
348
349 static struct v3d_simulator_bo *
v3d_get_simulator_bo(struct v3d_simulator_file * file,int gem_handle)350 v3d_get_simulator_bo(struct v3d_simulator_file *file, int gem_handle)
351 {
352 if (gem_handle == 0)
353 return NULL;
354
355 simple_mtx_lock(&sim_state.mutex);
356 struct hash_entry *entry =
357 _mesa_hash_table_search(file->bo_map, int_to_key(gem_handle));
358 simple_mtx_unlock(&sim_state.mutex);
359
360 return entry ? entry->data : NULL;
361 }
362
363 static void
v3d_simulator_copy_in_handle(struct v3d_simulator_file * file,int handle)364 v3d_simulator_copy_in_handle(struct v3d_simulator_file *file, int handle)
365 {
366 struct v3d_simulator_bo *sim_bo = v3d_get_simulator_bo(file, handle);
367
368 if (!sim_bo)
369 return;
370
371 memcpy(sim_bo->sim_vaddr, sim_bo->gem_vaddr, sim_bo->size);
372 }
373
374 static void
v3d_simulator_copy_out_handle(struct v3d_simulator_file * file,int handle)375 v3d_simulator_copy_out_handle(struct v3d_simulator_file *file, int handle)
376 {
377 struct v3d_simulator_bo *sim_bo = v3d_get_simulator_bo(file, handle);
378
379 if (!sim_bo)
380 return;
381
382 memcpy(sim_bo->gem_vaddr, sim_bo->sim_vaddr, sim_bo->size);
383
384 if (*(uint32_t *)(sim_bo->sim_vaddr +
385 sim_bo->size) != BO_SENTINEL) {
386 fprintf(stderr, "Buffer overflow in handle %d\n",
387 handle);
388 }
389 }
390
391 static int
v3d_simulator_pin_bos(struct v3d_simulator_file * file,struct drm_v3d_submit_cl * submit)392 v3d_simulator_pin_bos(struct v3d_simulator_file *file,
393 struct drm_v3d_submit_cl *submit)
394 {
395 uint32_t *bo_handles = (uint32_t *)(uintptr_t)submit->bo_handles;
396
397 for (int i = 0; i < submit->bo_handle_count; i++)
398 v3d_simulator_copy_in_handle(file, bo_handles[i]);
399
400 return 0;
401 }
402
403 static int
v3d_simulator_unpin_bos(struct v3d_simulator_file * file,struct drm_v3d_submit_cl * submit)404 v3d_simulator_unpin_bos(struct v3d_simulator_file *file,
405 struct drm_v3d_submit_cl *submit)
406 {
407 uint32_t *bo_handles = (uint32_t *)(uintptr_t)submit->bo_handles;
408
409 for (int i = 0; i < submit->bo_handle_count; i++)
410 v3d_simulator_copy_out_handle(file, bo_handles[i]);
411
412 return 0;
413 }
414
415 static struct v3d_simulator_perfmon *
v3d_get_simulator_perfmon(int fd,uint32_t perfid)416 v3d_get_simulator_perfmon(int fd, uint32_t perfid)
417 {
418 if (!perfid || perfid > sim_state.last_perfid)
419 return NULL;
420
421 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
422
423 simple_mtx_lock(&sim_state.mutex);
424 assert(perfid <= file->perfmons_size);
425 struct v3d_simulator_perfmon *perfmon = file->perfmons[perfid - 1];
426 simple_mtx_unlock(&sim_state.mutex);
427
428 return perfmon;
429 }
430
431 static void
v3d_simulator_perfmon_switch(int fd,uint32_t perfid)432 v3d_simulator_perfmon_switch(int fd, uint32_t perfid)
433 {
434 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
435 struct v3d_simulator_perfmon *perfmon;
436
437 if (perfid == file->active_perfid)
438 return;
439
440 perfmon = v3d_get_simulator_perfmon(fd, file->active_perfid);
441 if (perfmon)
442 v3d_X_simulator(perfmon_stop)(sim_state.v3d,
443 perfmon->ncounters,
444 perfmon->values);
445
446 perfmon = v3d_get_simulator_perfmon(fd, perfid);
447 if (perfmon)
448 v3d_X_simulator(perfmon_start)(sim_state.v3d,
449 perfmon->ncounters,
450 perfmon->counters);
451
452 file->active_perfid = perfid;
453 }
454
455 static int
v3d_simulator_signal_syncobjs(int fd,struct drm_v3d_multi_sync * ms)456 v3d_simulator_signal_syncobjs(int fd, struct drm_v3d_multi_sync *ms)
457 {
458 struct drm_v3d_sem *out_syncs = (void *)(uintptr_t)ms->out_syncs;
459 int n_syncobjs = ms->out_sync_count;
460 uint32_t syncobjs[n_syncobjs];
461
462 for (int i = 0; i < n_syncobjs; i++)
463 syncobjs[i] = out_syncs[i].handle;
464 return drmSyncobjSignal(fd, (uint32_t *) &syncobjs, n_syncobjs);
465 }
466
467 static int
v3d_simulator_process_post_deps(int fd,struct drm_v3d_extension * ext)468 v3d_simulator_process_post_deps(int fd, struct drm_v3d_extension *ext)
469 {
470 int ret = 0;
471 while (ext && ext->id != DRM_V3D_EXT_ID_MULTI_SYNC)
472 ext = (void *)(uintptr_t) ext->next;
473
474 if (ext) {
475 struct drm_v3d_multi_sync *ms = (struct drm_v3d_multi_sync *) ext;
476 ret = v3d_simulator_signal_syncobjs(fd, ms);
477 }
478 return ret;
479 }
480
481 static int
v3d_simulator_submit_cl_ioctl(int fd,struct drm_v3d_submit_cl * submit)482 v3d_simulator_submit_cl_ioctl(int fd, struct drm_v3d_submit_cl *submit)
483 {
484 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
485 int ret;
486
487 ret = v3d_simulator_pin_bos(file, submit);
488 if (ret)
489 return ret;
490
491 mtx_lock(&sim_state.submit_lock);
492 bin_fd = fd;
493
494 v3d_simulator_perfmon_switch(fd, submit->perfmon_id);
495 v3d_X_simulator(submit_cl_ioctl)(sim_state.v3d, submit, file->gmp->ofs);
496
497 util_dynarray_foreach(&sim_state.bin_oom, struct v3d_simulator_bo *,
498 sim_bo) {
499 v3d_free_simulator_bo(*sim_bo);
500 }
501 util_dynarray_clear(&sim_state.bin_oom);
502
503 mtx_unlock(&sim_state.submit_lock);
504
505 ret = v3d_simulator_unpin_bos(file, submit);
506 if (ret)
507 return ret;
508
509 if (submit->flags & DRM_V3D_SUBMIT_EXTENSION) {
510 struct drm_v3d_extension *ext = (void *)(uintptr_t)submit->extensions;
511 ret = v3d_simulator_process_post_deps(fd, ext);
512 }
513
514 return ret;
515 }
516
517 /**
518 * Do fixups after a BO has been opened from a handle.
519 *
520 * This could be done at DRM_IOCTL_GEM_OPEN/DRM_IOCTL_GEM_PRIME_FD_TO_HANDLE
521 * time, but we're still using drmPrimeFDToHandle() so we have this helper to
522 * be called afterward instead.
523 */
v3d_simulator_open_from_handle(int fd,int handle,uint32_t size)524 void v3d_simulator_open_from_handle(int fd, int handle, uint32_t size)
525 {
526 v3d_create_simulator_bo_for_gem(fd, handle, size);
527 }
528
529 /**
530 * Simulated ioctl(fd, DRM_V3D_CREATE_BO) implementation.
531 *
532 * Making a V3D BO is just a matter of making a corresponding BO on the host.
533 */
534 static int
v3d_simulator_create_bo_ioctl(int fd,struct drm_v3d_create_bo * args)535 v3d_simulator_create_bo_ioctl(int fd, struct drm_v3d_create_bo *args)
536 {
537 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
538
539 /* i915 bans dumb create on render nodes, so we have to use their
540 * native ioctl in case we're on a render node.
541 */
542 int ret;
543 switch (file->gem_type) {
544 case GEM_I915:
545 {
546 struct drm_i915_gem_create create = {
547 .size = args->size,
548 };
549
550 ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create);
551
552 args->handle = create.handle;
553 break;
554 }
555 case GEM_AMDGPU:
556 {
557 union drm_amdgpu_gem_create create = { 0 };
558 create.in.bo_size = args->size;
559
560 ret = drmIoctl(fd, DRM_IOCTL_AMDGPU_GEM_CREATE, &create);
561
562 args->handle = create.out.handle;
563 break;
564 }
565 default:
566 {
567 struct drm_mode_create_dumb create = {
568 .width = 128,
569 .bpp = 8,
570 .height = (args->size + 127) / 128,
571 };
572
573 ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
574 assert(ret != 0 || create.size >= args->size);
575
576 args->handle = create.handle;
577 }
578 }
579 if (ret == 0) {
580 struct v3d_simulator_bo *sim_bo =
581 v3d_create_simulator_bo_for_gem(fd, args->handle,
582 args->size);
583
584 args->offset = sim_bo->block->ofs;
585 }
586
587 return ret;
588 }
589
590 /**
591 * Simulated ioctl(fd, DRM_V3D_MMAP_BO) implementation.
592 *
593 * We've already grabbed the mmap offset when we created the sim bo, so just
594 * return it.
595 */
596 static int
v3d_simulator_mmap_bo_ioctl(int fd,struct drm_v3d_mmap_bo * args)597 v3d_simulator_mmap_bo_ioctl(int fd, struct drm_v3d_mmap_bo *args)
598 {
599 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
600 struct v3d_simulator_bo *sim_bo = v3d_get_simulator_bo(file,
601 args->handle);
602
603 args->offset = sim_bo->mmap_offset;
604
605 return 0;
606 }
607
608 static int
v3d_simulator_get_bo_offset_ioctl(int fd,struct drm_v3d_get_bo_offset * args)609 v3d_simulator_get_bo_offset_ioctl(int fd, struct drm_v3d_get_bo_offset *args)
610 {
611 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
612 struct v3d_simulator_bo *sim_bo = v3d_get_simulator_bo(file,
613 args->handle);
614
615 args->offset = sim_bo->block->ofs;
616
617 return 0;
618 }
619
620 static int
v3d_simulator_gem_close_ioctl(int fd,struct drm_gem_close * args)621 v3d_simulator_gem_close_ioctl(int fd, struct drm_gem_close *args)
622 {
623 /* Free the simulator's internal tracking. */
624 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
625 struct v3d_simulator_bo *sim_bo = v3d_get_simulator_bo(file,
626 args->handle);
627
628 v3d_free_simulator_bo(sim_bo);
629
630 /* Pass the call on down. */
631 return drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, args);
632 }
633
634 static int
v3d_simulator_submit_tfu_ioctl(int fd,struct drm_v3d_submit_tfu * args)635 v3d_simulator_submit_tfu_ioctl(int fd, struct drm_v3d_submit_tfu *args)
636 {
637 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
638 int ret;
639
640 v3d_simulator_copy_in_handle(file, args->bo_handles[0]);
641 v3d_simulator_copy_in_handle(file, args->bo_handles[1]);
642 v3d_simulator_copy_in_handle(file, args->bo_handles[2]);
643 v3d_simulator_copy_in_handle(file, args->bo_handles[3]);
644
645 ret = v3d_X_simulator(submit_tfu_ioctl)(sim_state.v3d, args);
646
647 v3d_simulator_copy_out_handle(file, args->bo_handles[0]);
648
649 if (ret)
650 return ret;
651
652 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) {
653 struct drm_v3d_extension *ext = (void *)(uintptr_t)args->extensions;
654 ret = v3d_simulator_process_post_deps(fd, ext);
655 }
656
657 return ret;
658 }
659
660 static int
v3d_simulator_submit_csd_ioctl(int fd,struct drm_v3d_submit_csd * args)661 v3d_simulator_submit_csd_ioctl(int fd, struct drm_v3d_submit_csd *args)
662 {
663 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
664 uint32_t *bo_handles = (uint32_t *)(uintptr_t)args->bo_handles;
665 int ret;
666
667 for (int i = 0; i < args->bo_handle_count; i++)
668 v3d_simulator_copy_in_handle(file, bo_handles[i]);
669
670 v3d_simulator_perfmon_switch(fd, args->perfmon_id);
671
672 ret = v3d_X_simulator(submit_csd_ioctl)(sim_state.v3d, args,
673 file->gmp->ofs);
674
675 for (int i = 0; i < args->bo_handle_count; i++)
676 v3d_simulator_copy_out_handle(file, bo_handles[i]);
677
678 if (ret < 0)
679 return ret;
680
681 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) {
682 struct drm_v3d_extension *ext = (void *)(uintptr_t)args->extensions;
683 ret = v3d_simulator_process_post_deps(fd, ext);
684 }
685
686 return ret;
687 }
688
689 static void
v3d_rewrite_csd_job_wg_counts_from_indirect(int fd,struct drm_v3d_extension * ext,struct drm_v3d_submit_cpu * args)690 v3d_rewrite_csd_job_wg_counts_from_indirect(int fd,
691 struct drm_v3d_extension *ext,
692 struct drm_v3d_submit_cpu *args)
693 {
694 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
695 struct drm_v3d_indirect_csd *indirect_csd = (struct drm_v3d_indirect_csd *) ext;
696 uint32_t *bo_handles = (uint32_t *)(uintptr_t)args->bo_handles;
697
698 assert(args->bo_handle_count == 1);
699 struct v3d_simulator_bo *bo = v3d_get_simulator_bo(file, bo_handles[0]);
700 struct v3d_simulator_bo *indirect = v3d_get_simulator_bo(file, indirect_csd->indirect);
701 struct drm_v3d_submit_csd *submit = &indirect_csd->submit;
702
703 uint32_t *wg_counts = (uint32_t *) (bo->gem_vaddr + indirect_csd->offset);
704
705 if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0)
706 return;
707
708 submit->cfg[0] = wg_counts[0] << V3D_CSD_CFG012_WG_COUNT_SHIFT;
709 submit->cfg[1] = wg_counts[1] << V3D_CSD_CFG012_WG_COUNT_SHIFT;
710 submit->cfg[2] = wg_counts[2] << V3D_CSD_CFG012_WG_COUNT_SHIFT;
711 submit->cfg[4] = DIV_ROUND_UP(indirect_csd->wg_size, 16) *
712 (wg_counts[0] * wg_counts[1] * wg_counts[2]) - 1;
713
714 for (int i = 0; i < 3; i++) {
715 /* 0xffffffff indicates that the uniform rewrite is not needed */
716 if (indirect_csd->wg_uniform_offsets[i] != 0xffffffff) {
717 uint32_t uniform_idx = indirect_csd->wg_uniform_offsets[i];
718 ((uint32_t *) indirect->gem_vaddr)[uniform_idx] = wg_counts[i];
719 }
720 }
721
722 v3d_simulator_submit_csd_ioctl(fd, submit);
723 }
724
725 static void
v3d_timestamp_query(int fd,struct drm_v3d_extension * ext,struct drm_v3d_submit_cpu * args)726 v3d_timestamp_query(int fd,
727 struct drm_v3d_extension *ext,
728 struct drm_v3d_submit_cpu *args)
729 {
730 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
731 struct drm_v3d_timestamp_query *timestamp_query = (struct drm_v3d_timestamp_query *) ext;
732 uint32_t *bo_handles = (uint32_t *)(uintptr_t)args->bo_handles;
733 struct v3d_simulator_bo *bo = v3d_get_simulator_bo(file, bo_handles[0]);
734 uint32_t *offsets = (void *)(uintptr_t) timestamp_query->offsets;
735 uint32_t *syncs = (void *)(uintptr_t) timestamp_query->syncs;
736 uint8_t *value_addr;
737
738 struct timespec t;
739 clock_gettime(CLOCK_MONOTONIC, &t);
740
741 for (uint32_t i = 0; i < timestamp_query->count; i++) {
742 value_addr = ((uint8_t *) bo->sim_vaddr) + offsets[i];
743 *((uint64_t*)value_addr) = (i == 0) ? t.tv_sec * 1000000000ull + t.tv_nsec : 0ull;
744 }
745
746 drmSyncobjSignal(fd, syncs, timestamp_query->count);
747 }
748
749 static void
v3d_reset_timestamp_queries(int fd,struct drm_v3d_extension * ext,struct drm_v3d_submit_cpu * args)750 v3d_reset_timestamp_queries(int fd,
751 struct drm_v3d_extension *ext,
752 struct drm_v3d_submit_cpu *args)
753 {
754 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
755 struct drm_v3d_reset_timestamp_query *reset = (struct drm_v3d_reset_timestamp_query *) ext;
756 uint32_t *bo_handles = (uint32_t *)(uintptr_t)args->bo_handles;
757 struct v3d_simulator_bo *bo = v3d_get_simulator_bo(file, bo_handles[0]);
758 uint32_t *syncs = (void *)(uintptr_t) reset->syncs;
759 uint8_t *base_addr;
760
761 base_addr = ((uint8_t *) bo->sim_vaddr) + reset->offset;
762 memset(base_addr, 0, 8 * reset->count);
763
764 drmSyncobjReset(fd, syncs, reset->count);
765 }
766
767 static void
write_to_buffer(void * dst,uint32_t idx,bool do_64bit,uint64_t value)768 write_to_buffer(void *dst, uint32_t idx, bool do_64bit, uint64_t value)
769 {
770 if (do_64bit) {
771 uint64_t *dst64 = (uint64_t *) dst;
772 dst64[idx] = value;
773 } else {
774 uint32_t *dst32 = (uint32_t *) dst;
775 dst32[idx] = (uint32_t) value;
776 }
777 }
778
779 static void
v3d_copy_query_results(int fd,struct drm_v3d_extension * ext,struct drm_v3d_submit_cpu * args)780 v3d_copy_query_results(int fd,
781 struct drm_v3d_extension *ext,
782 struct drm_v3d_submit_cpu *args)
783 {
784 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
785 struct drm_v3d_copy_timestamp_query *copy = (struct drm_v3d_copy_timestamp_query *) ext;
786 uint32_t *bo_handles = (uint32_t *)(uintptr_t)args->bo_handles;
787 struct v3d_simulator_bo *bo = v3d_get_simulator_bo(file, bo_handles[0]);
788 struct v3d_simulator_bo *timestamp = v3d_get_simulator_bo(file, bo_handles[1]);
789 uint32_t *offsets = (void *)(uintptr_t) copy->offsets;
790 uint32_t *syncs = (void *)(uintptr_t) copy->syncs;
791 bool available, write_result;
792 uint8_t *query_addr;
793 uint8_t *data;
794
795 data = ((uint8_t *) bo->sim_vaddr) + copy->offset;
796
797 for (uint32_t i = 0; i < copy->count; i++) {
798 available = (drmSyncobjWait(fd, &syncs[i], 1, 0, 0, NULL) == 0);
799
800 write_result = available || copy->do_partial;
801 if (write_result) {
802 query_addr = ((uint8_t *) timestamp->sim_vaddr) + offsets[i];
803 write_to_buffer(data, 0, copy->do_64bit, *((uint64_t *) query_addr));
804 }
805
806 if (copy->availability_bit)
807 write_to_buffer(data, 1, copy->do_64bit, available ? 1u : 0u);
808
809 data += copy->stride;
810 }
811 }
812
813 static void
v3d_reset_performance_queries(int fd,struct drm_v3d_extension * ext,struct drm_v3d_submit_cpu * args)814 v3d_reset_performance_queries(int fd,
815 struct drm_v3d_extension *ext,
816 struct drm_v3d_submit_cpu *args)
817 {
818 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
819 struct drm_v3d_reset_performance_query *reset = (struct drm_v3d_reset_performance_query *) ext;
820 uint64_t *kperfmon_ids = (void *)(uintptr_t) reset->kperfmon_ids;
821 uint32_t *syncs = (void *)(uintptr_t) reset->syncs;
822 struct v3d_simulator_perfmon *perfmon;
823
824 for (uint32_t i = 0; i < reset->count; i++) {
825 uint32_t *ids = (void *)(uintptr_t) kperfmon_ids[i];
826
827 for (uint32_t j = 0; j < reset->nperfmons; j++) {
828 mtx_lock(&sim_state.submit_lock);
829
830 /* Stop the perfmon if it is still active */
831 if (ids[j] == file->active_perfid)
832 v3d_simulator_perfmon_switch(fd, 0);
833
834 mtx_unlock(&sim_state.submit_lock);
835
836 perfmon = v3d_get_simulator_perfmon(fd, ids[j]);
837
838 if (!perfmon)
839 return;
840
841 memset(perfmon->values, 0, perfmon->ncounters * sizeof(uint64_t));
842 }
843 }
844
845 drmSyncobjReset(fd, syncs, reset->count);
846 }
847
848 static void
v3d_write_performance_query_result(int fd,struct drm_v3d_copy_performance_query * copy,uint32_t * kperfmon_ids,void * data)849 v3d_write_performance_query_result(int fd,
850 struct drm_v3d_copy_performance_query *copy,
851 uint32_t *kperfmon_ids,
852 void *data)
853 {
854 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
855 struct v3d_simulator_perfmon *perfmon;
856 uint64_t counter_values[V3D_PERFCNT_NUM];
857
858 for (uint32_t i = 0; i < copy->nperfmons; i++) {
859 mtx_lock(&sim_state.submit_lock);
860
861 /* Stop the perfmon if it is still active */
862 if (kperfmon_ids[i] == file->active_perfid)
863 v3d_simulator_perfmon_switch(fd, 0);
864
865 mtx_unlock(&sim_state.submit_lock);
866
867 perfmon = v3d_get_simulator_perfmon(fd, kperfmon_ids[i]);
868
869 if (!perfmon)
870 return;
871
872 memcpy(&counter_values[i * DRM_V3D_MAX_PERF_COUNTERS], perfmon->values,
873 perfmon->ncounters * sizeof(uint64_t));
874 }
875
876 for (uint32_t i = 0; i < copy->ncounters; i++)
877 write_to_buffer(data, i, copy->do_64bit, counter_values[i]);
878 }
879
880 static void
v3d_copy_performance_query(int fd,struct drm_v3d_extension * ext,struct drm_v3d_submit_cpu * args)881 v3d_copy_performance_query(int fd,
882 struct drm_v3d_extension *ext,
883 struct drm_v3d_submit_cpu *args)
884 {
885 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
886 struct drm_v3d_copy_performance_query *copy = (struct drm_v3d_copy_performance_query *) ext;
887 uint32_t *bo_handles = (uint32_t *)(uintptr_t)args->bo_handles;
888 struct v3d_simulator_bo *bo = v3d_get_simulator_bo(file, bo_handles[0]);
889 uint64_t *kperfmon_ids = (void *)(uintptr_t) copy->kperfmon_ids;
890 uint32_t *syncs = (void *)(uintptr_t) copy->syncs;
891 bool available, write_result;
892 uint8_t *data;
893
894 data = ((uint8_t *) bo->sim_vaddr) + copy->offset;
895
896 for (uint32_t i = 0; i < copy->count; i++) {
897 /* Although we don't have in_syncs implemented in the simulator,
898 * we don't need to wait for the availability of the syncobjs,
899 * as they are signaled by CL and CSD jobs, which are serialized
900 * by the simulator.
901 */
902 available = (drmSyncobjWait(fd, &syncs[i], 1, 0, 0, NULL) == 0);
903
904 write_result = available || copy->do_partial;
905 if (write_result) {
906 v3d_write_performance_query_result(fd, copy,
907 (void *)(uintptr_t) kperfmon_ids[i],
908 data);
909 }
910
911 if (copy->availability_bit) {
912 write_to_buffer(data, copy->ncounters, copy->do_64bit,
913 available ? 1u : 0u);
914 }
915
916 data += copy->stride;
917 }
918 }
919
920 static int
v3d_simulator_submit_cpu_ioctl(int fd,struct drm_v3d_submit_cpu * args)921 v3d_simulator_submit_cpu_ioctl(int fd, struct drm_v3d_submit_cpu *args)
922 {
923 struct drm_v3d_extension *ext = (void *)(uintptr_t)args->extensions;
924 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
925 uint32_t *bo_handles = (uint32_t *)(uintptr_t)args->bo_handles;
926 int ret = 0;
927
928 for (int i = 0; i < args->bo_handle_count; i++)
929 v3d_simulator_copy_in_handle(file, bo_handles[i]);
930
931 while (ext) {
932 switch (ext->id) {
933 case DRM_V3D_EXT_ID_MULTI_SYNC:
934 /* As the simulator serializes the jobs, we don't need
935 * to handle the in_syncs here. The out_syncs are handled
936 * by the end of the ioctl in v3d_simulator_process_post_deps().
937 */
938 break;
939 case DRM_V3D_EXT_ID_CPU_INDIRECT_CSD:
940 v3d_rewrite_csd_job_wg_counts_from_indirect(fd, ext, args);
941 break;
942 case DRM_V3D_EXT_ID_CPU_TIMESTAMP_QUERY:
943 v3d_timestamp_query(fd, ext, args);
944 break;
945 case DRM_V3D_EXT_ID_CPU_RESET_TIMESTAMP_QUERY:
946 v3d_reset_timestamp_queries(fd, ext, args);
947 break;
948 case DRM_V3D_EXT_ID_CPU_COPY_TIMESTAMP_QUERY:
949 v3d_copy_query_results(fd, ext, args);
950 break;
951 case DRM_V3D_EXT_ID_CPU_RESET_PERFORMANCE_QUERY:
952 v3d_reset_performance_queries(fd, ext, args);
953 break;
954 case DRM_V3D_EXT_ID_CPU_COPY_PERFORMANCE_QUERY:
955 v3d_copy_performance_query(fd, ext, args);
956 break;
957 default:
958 fprintf(stderr, "Unknown CPU job 0x%08x\n", (int)ext->id);
959 break;
960 }
961
962 ext = (void *)(uintptr_t) ext->next;
963 }
964
965 for (int i = 0; i < args->bo_handle_count; i++)
966 v3d_simulator_copy_out_handle(file, bo_handles[i]);
967
968 if (ret < 0)
969 return ret;
970
971 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) {
972 ext = (void *)(uintptr_t)args->extensions;
973 ret = v3d_simulator_process_post_deps(fd, ext);
974 }
975
976 return ret;
977 }
978
979 static int
v3d_simulator_perfmon_create_ioctl(int fd,struct drm_v3d_perfmon_create * args)980 v3d_simulator_perfmon_create_ioctl(int fd, struct drm_v3d_perfmon_create *args)
981 {
982 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
983
984 if (args->ncounters == 0 ||
985 args->ncounters > DRM_V3D_MAX_PERF_COUNTERS)
986 return -EINVAL;
987
988 struct v3d_simulator_perfmon *perfmon = rzalloc(file,
989 struct v3d_simulator_perfmon);
990
991 perfmon->ncounters = args->ncounters;
992 for (int i = 0; i < args->ncounters; i++) {
993 if (args->counters[i] >= sim_state.perfcnt_total) {
994 ralloc_free(perfmon);
995 return -EINVAL;
996 } else {
997 perfmon->counters[i] = args->counters[i];
998 }
999 }
1000
1001 simple_mtx_lock(&sim_state.mutex);
1002 args->id = perfmons_next_id(file);
1003 file->perfmons[args->id - 1] = perfmon;
1004 simple_mtx_unlock(&sim_state.mutex);
1005
1006 return 0;
1007 }
1008
1009 static int
v3d_simulator_perfmon_destroy_ioctl(int fd,struct drm_v3d_perfmon_destroy * args)1010 v3d_simulator_perfmon_destroy_ioctl(int fd, struct drm_v3d_perfmon_destroy *args)
1011 {
1012 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
1013 struct v3d_simulator_perfmon *perfmon =
1014 v3d_get_simulator_perfmon(fd, args->id);
1015
1016 if (!perfmon)
1017 return -EINVAL;
1018
1019 simple_mtx_lock(&sim_state.mutex);
1020 file->perfmons[args->id - 1] = NULL;
1021 simple_mtx_unlock(&sim_state.mutex);
1022
1023 ralloc_free(perfmon);
1024
1025 return 0;
1026 }
1027
1028 static int
v3d_simulator_perfmon_get_values_ioctl(int fd,struct drm_v3d_perfmon_get_values * args)1029 v3d_simulator_perfmon_get_values_ioctl(int fd, struct drm_v3d_perfmon_get_values *args)
1030 {
1031 struct v3d_simulator_file *file = v3d_get_simulator_file_for_fd(fd);
1032
1033 mtx_lock(&sim_state.submit_lock);
1034
1035 /* Stop the perfmon if it is still active */
1036 if (args->id == file->active_perfid)
1037 v3d_simulator_perfmon_switch(fd, 0);
1038
1039 mtx_unlock(&sim_state.submit_lock);
1040
1041 struct v3d_simulator_perfmon *perfmon =
1042 v3d_get_simulator_perfmon(fd, args->id);
1043
1044 if (!perfmon)
1045 return -EINVAL;
1046
1047 memcpy((void *)args->values_ptr, perfmon->values, perfmon->ncounters * sizeof(uint64_t));
1048
1049 return 0;
1050 }
1051
1052 int
v3d_simulator_ioctl(int fd,unsigned long request,void * args)1053 v3d_simulator_ioctl(int fd, unsigned long request, void *args)
1054 {
1055 switch (request) {
1056 case DRM_IOCTL_V3D_SUBMIT_CL:
1057 return v3d_simulator_submit_cl_ioctl(fd, args);
1058 case DRM_IOCTL_V3D_CREATE_BO:
1059 return v3d_simulator_create_bo_ioctl(fd, args);
1060 case DRM_IOCTL_V3D_MMAP_BO:
1061 return v3d_simulator_mmap_bo_ioctl(fd, args);
1062 case DRM_IOCTL_V3D_GET_BO_OFFSET:
1063 return v3d_simulator_get_bo_offset_ioctl(fd, args);
1064
1065 case DRM_IOCTL_V3D_WAIT_BO:
1066 /* We do all of the v3d rendering synchronously, so we just
1067 * return immediately on the wait ioctls. This ignores any
1068 * native rendering to the host BO, so it does mean we race on
1069 * front buffer rendering.
1070 */
1071 return 0;
1072
1073 case DRM_IOCTL_V3D_GET_PARAM:
1074 return v3d_X_simulator(get_param_ioctl)(sim_state.v3d, args);
1075
1076 case DRM_IOCTL_GEM_CLOSE:
1077 return v3d_simulator_gem_close_ioctl(fd, args);
1078
1079 case DRM_IOCTL_V3D_SUBMIT_TFU:
1080 return v3d_simulator_submit_tfu_ioctl(fd, args);
1081
1082 case DRM_IOCTL_V3D_SUBMIT_CSD:
1083 return v3d_simulator_submit_csd_ioctl(fd, args);
1084
1085 case DRM_IOCTL_V3D_SUBMIT_CPU:
1086 return v3d_simulator_submit_cpu_ioctl(fd, args);
1087
1088 case DRM_IOCTL_V3D_PERFMON_CREATE:
1089 return v3d_simulator_perfmon_create_ioctl(fd, args);
1090
1091 case DRM_IOCTL_V3D_PERFMON_DESTROY:
1092 return v3d_simulator_perfmon_destroy_ioctl(fd, args);
1093
1094 case DRM_IOCTL_V3D_PERFMON_GET_VALUES:
1095 return v3d_simulator_perfmon_get_values_ioctl(fd, args);
1096
1097 case DRM_IOCTL_GEM_OPEN:
1098 case DRM_IOCTL_GEM_FLINK:
1099 return drmIoctl(fd, request, args);
1100 default:
1101 fprintf(stderr, "Unknown ioctl 0x%08x\n", (int)request);
1102 abort();
1103 }
1104 }
1105
1106 uint32_t
v3d_simulator_get_mem_size(void)1107 v3d_simulator_get_mem_size(void)
1108 {
1109 return sim_state.mem_size;
1110 }
1111
1112 uint32_t
v3d_simulator_get_mem_free(void)1113 v3d_simulator_get_mem_free(void)
1114 {
1115 uint32_t total_free = 0;
1116 struct mem_block *p;
1117 for (p = sim_state.heap->next_free; p != sim_state.heap; p = p->next_free)
1118 total_free += p->size;
1119 return total_free;
1120 }
1121
1122 static void
v3d_simulator_init_global()1123 v3d_simulator_init_global()
1124 {
1125 simple_mtx_lock(&sim_state.mutex);
1126 if (sim_state.refcount++) {
1127 simple_mtx_unlock(&sim_state.mutex);
1128 return;
1129 }
1130
1131 sim_state.v3d = v3d_hw_auto_new(NULL);
1132 v3d_hw_alloc_mem(sim_state.v3d, 1024 * 1024 * 1024);
1133 sim_state.mem_base =
1134 v3d_hw_get_mem(sim_state.v3d, &sim_state.mem_size,
1135 &sim_state.mem);
1136
1137 /* Allocate from anywhere from 4096 up. We don't allocate at 0,
1138 * because for OQs and some other addresses in the HW, 0 means
1139 * disabled.
1140 */
1141 sim_state.heap = u_mmInit(4096, sim_state.mem_size - 4096);
1142
1143 /* Make a block of 0xd0 at address 0 to make sure we don't screw up
1144 * and land there.
1145 */
1146 struct mem_block *b = u_mmAllocMem(sim_state.heap, 4096, GMP_ALIGN2, 0);
1147 memset(sim_state.mem + b->ofs - sim_state.mem_base, 0xd0, 4096);
1148
1149 sim_state.ver = v3d_hw_get_version(sim_state.v3d);
1150
1151 simple_mtx_unlock(&sim_state.mutex);
1152
1153 sim_state.fd_map =
1154 _mesa_hash_table_create(NULL,
1155 _mesa_hash_pointer,
1156 _mesa_key_pointer_equal);
1157
1158 util_dynarray_init(&sim_state.bin_oom, NULL);
1159
1160 v3d_X_simulator(init_regs)(sim_state.v3d);
1161 v3d_X_simulator(get_perfcnt_total)(&sim_state.perfcnt_total);
1162 }
1163
1164 struct v3d_simulator_file *
v3d_simulator_init(int fd)1165 v3d_simulator_init(int fd)
1166 {
1167 v3d_simulator_init_global();
1168
1169 struct v3d_simulator_file *sim_file = rzalloc(NULL, struct v3d_simulator_file);
1170
1171 drmVersionPtr version = drmGetVersion(fd);
1172 if (version && strncmp(version->name, "i915", version->name_len) == 0)
1173 sim_file->gem_type = GEM_I915;
1174 else if (version && strncmp(version->name, "amdgpu", version->name_len) == 0)
1175 sim_file->gem_type = GEM_AMDGPU;
1176 else
1177 sim_file->gem_type = GEM_DUMB;
1178 drmFreeVersion(version);
1179
1180 sim_file->bo_map =
1181 _mesa_hash_table_create(sim_file,
1182 _mesa_hash_pointer,
1183 _mesa_key_pointer_equal);
1184
1185 simple_mtx_lock(&sim_state.mutex);
1186 _mesa_hash_table_insert(sim_state.fd_map, int_to_key(fd + 1),
1187 sim_file);
1188 simple_mtx_unlock(&sim_state.mutex);
1189
1190 sim_file->gmp = u_mmAllocMem(sim_state.heap, 8096, GMP_ALIGN2, 0);
1191 sim_file->gmp_vaddr = (sim_state.mem + sim_file->gmp->ofs -
1192 sim_state.mem_base);
1193 memset(sim_file->gmp_vaddr, 0, 8096);
1194
1195 return sim_file;
1196 }
1197
1198 void
v3d_simulator_destroy(struct v3d_simulator_file * sim_file)1199 v3d_simulator_destroy(struct v3d_simulator_file *sim_file)
1200 {
1201 simple_mtx_lock(&sim_state.mutex);
1202 if (!--sim_state.refcount) {
1203 _mesa_hash_table_destroy(sim_state.fd_map, NULL);
1204 util_dynarray_fini(&sim_state.bin_oom);
1205 u_mmDestroy(sim_state.heap);
1206 /* No memsetting the struct, because it contains the mutex. */
1207 sim_state.mem = NULL;
1208 }
1209 ralloc_free(sim_file);
1210 simple_mtx_unlock(&sim_state.mutex);
1211 }
1212
1213 #endif /* USE_V3D_SIMULATOR */
1214