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