xref: /aosp_15_r20/external/mesa3d/src/broadcom/simulator/v3d_simulator.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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