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