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