• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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