• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2014 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 vc4_simulator.c
26  *
27  * Implements VC4 simulation on top of a non-VC4 GEM fd.
28  *
29  * This file's goal is to emulate the VC4 ioctls' behavior in the kernel on
30  * top of the simpenrose software simulator.  Generally, VC4 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 VC4 is going to use, which of course
44  * doesn't work out in practice.  This means that for now, only DRI3 (VC4
45  * makes the winsys BOs) is supported, not DRI2 (window system makes the winys
46  * BOs).
47  */
48 
49 #ifdef USE_VC4_SIMULATOR
50 
51 #include <sys/mman.h>
52 #include "xf86drm.h"
53 #include "util/u_memory.h"
54 #include "util/u_mm.h"
55 #include "util/ralloc.h"
56 
57 #include "vc4_screen.h"
58 #include "vc4_cl_dump.h"
59 #include "vc4_context.h"
60 #include "kernel/vc4_drv.h"
61 #include "vc4_simulator_validate.h"
62 #include "simpenrose/simpenrose.h"
63 
64 /** Global (across GEM fds) state for the simulator */
65 static struct vc4_simulator_state {
66         mtx_t mutex;
67 
68         void *mem;
69         ssize_t mem_size;
70         struct mem_block *heap;
71         struct mem_block *overflow;
72 
73         /** Mapping from GEM handle to struct vc4_simulator_bo * */
74         struct hash_table *fd_map;
75 
76         int refcount;
77 } sim_state = {
78         .mutex = _MTX_INITIALIZER_NP,
79 };
80 
81 /** Per-GEM-fd state for the simulator. */
82 struct vc4_simulator_file {
83         int fd;
84 
85         /* This is weird -- we make a "vc4_device" per file, even though on
86          * the kernel side this is a global.  We do this so that kernel code
87          * calling us for BO allocation can get to our screen.
88          */
89         struct drm_device dev;
90 
91         /** Mapping from GEM handle to struct vc4_simulator_bo * */
92         struct hash_table *bo_map;
93 };
94 
95 /** Wrapper for drm_vc4_bo tracking the simulator-specific state. */
96 struct vc4_simulator_bo {
97         struct drm_vc4_bo base;
98         struct vc4_simulator_file *file;
99 
100         /** Area for this BO within sim_state->mem */
101         struct mem_block *block;
102         void *winsys_map;
103         uint32_t winsys_stride;
104 
105         int handle;
106 };
107 
108 static void *
int_to_key(int key)109 int_to_key(int key)
110 {
111         return (void *)(uintptr_t)key;
112 }
113 
114 static struct vc4_simulator_file *
vc4_get_simulator_file_for_fd(int fd)115 vc4_get_simulator_file_for_fd(int fd)
116 {
117         struct hash_entry *entry = _mesa_hash_table_search(sim_state.fd_map,
118                                                            int_to_key(fd + 1));
119         return entry ? entry->data : NULL;
120 }
121 
122 /* A marker placed just after each BO, then checked after rendering to make
123  * sure it's still there.
124  */
125 #define BO_SENTINEL		0xfedcba98
126 
127 #define PAGE_ALIGN2		12
128 
129 /**
130  * Allocates space in simulator memory and returns a tracking struct for it
131  * that also contains the drm_gem_cma_object struct.
132  */
133 static struct vc4_simulator_bo *
vc4_create_simulator_bo(int fd,int handle,unsigned size)134 vc4_create_simulator_bo(int fd, int handle, unsigned size)
135 {
136         struct vc4_simulator_file *file = vc4_get_simulator_file_for_fd(fd);
137         struct vc4_simulator_bo *sim_bo = rzalloc(file,
138                                                   struct vc4_simulator_bo);
139         struct drm_vc4_bo *bo = &sim_bo->base;
140         struct drm_gem_cma_object *obj = &bo->base;
141         size = align(size, 4096);
142 
143         sim_bo->file = file;
144         sim_bo->handle = handle;
145 
146         mtx_lock(&sim_state.mutex);
147         sim_bo->block = u_mmAllocMem(sim_state.heap, size + 4, PAGE_ALIGN2, 0);
148         mtx_unlock(&sim_state.mutex);
149         assert(sim_bo->block);
150 
151         obj->base.size = size;
152         obj->base.dev = &file->dev;
153         obj->vaddr = sim_state.mem + sim_bo->block->ofs;
154         obj->paddr = simpenrose_hw_addr(obj->vaddr);
155 
156         *(uint32_t *)(obj->vaddr + size) = BO_SENTINEL;
157 
158         /* A handle of 0 is used for vc4_gem.c internal allocations that
159          * don't need to go in the lookup table.
160          */
161         if (handle != 0) {
162                 mtx_lock(&sim_state.mutex);
163                 _mesa_hash_table_insert(file->bo_map, int_to_key(handle), bo);
164                 mtx_unlock(&sim_state.mutex);
165         }
166 
167         return sim_bo;
168 }
169 
170 static void
vc4_free_simulator_bo(struct vc4_simulator_bo * sim_bo)171 vc4_free_simulator_bo(struct vc4_simulator_bo *sim_bo)
172 {
173         struct vc4_simulator_file *sim_file = sim_bo->file;
174         struct drm_vc4_bo *bo = &sim_bo->base;
175         struct drm_gem_cma_object *obj = &bo->base;
176 
177         if (sim_bo->winsys_map)
178                 munmap(sim_bo->winsys_map, obj->base.size);
179 
180         mtx_lock(&sim_state.mutex);
181         u_mmFreeMem(sim_bo->block);
182         if (sim_bo->handle) {
183                 struct hash_entry *entry =
184                         _mesa_hash_table_search(sim_file->bo_map,
185                                                 int_to_key(sim_bo->handle));
186                 _mesa_hash_table_remove(sim_file->bo_map, entry);
187         }
188         mtx_unlock(&sim_state.mutex);
189         ralloc_free(sim_bo);
190 }
191 
192 static struct vc4_simulator_bo *
vc4_get_simulator_bo(struct vc4_simulator_file * file,int gem_handle)193 vc4_get_simulator_bo(struct vc4_simulator_file *file, int gem_handle)
194 {
195         mtx_lock(&sim_state.mutex);
196         struct hash_entry *entry =
197                 _mesa_hash_table_search(file->bo_map, int_to_key(gem_handle));
198         mtx_unlock(&sim_state.mutex);
199 
200         return entry ? entry->data : NULL;
201 }
202 
203 struct drm_gem_cma_object *
drm_gem_cma_create(struct drm_device * dev,size_t size)204 drm_gem_cma_create(struct drm_device *dev, size_t size)
205 {
206         struct vc4_screen *screen = dev->screen;
207         struct vc4_simulator_bo *sim_bo = vc4_create_simulator_bo(screen->fd,
208                                                                   0, size);
209         return &sim_bo->base.base;
210 }
211 
212 static int
vc4_simulator_pin_bos(struct drm_device * dev,struct vc4_job * job,struct vc4_exec_info * exec)213 vc4_simulator_pin_bos(struct drm_device *dev, struct vc4_job *job,
214                       struct vc4_exec_info *exec)
215 {
216         int fd = dev->screen->fd;
217         struct vc4_simulator_file *file = vc4_get_simulator_file_for_fd(fd);
218         struct drm_vc4_submit_cl *args = exec->args;
219         struct vc4_bo **bos = job->bo_pointers.base;
220 
221         exec->bo_count = args->bo_handle_count;
222         exec->bo = calloc(exec->bo_count, sizeof(void *));
223         for (int i = 0; i < exec->bo_count; i++) {
224                 struct vc4_bo *bo = bos[i];
225                 struct vc4_simulator_bo *sim_bo =
226                         vc4_get_simulator_bo(file, bo->handle);
227                 struct drm_vc4_bo *drm_bo = &sim_bo->base;
228                 struct drm_gem_cma_object *obj = &drm_bo->base;
229 
230                 drm_bo->bo = bo;
231 #if 0
232                 fprintf(stderr, "bo hindex %d: %s\n", i, bo->name);
233 #endif
234 
235                 vc4_bo_map(bo);
236                 memcpy(obj->vaddr, bo->map, bo->size);
237 
238                 exec->bo[i] = obj;
239 
240                 /* The kernel does this validation at shader create ioctl
241                  * time.
242                  */
243                 if (strcmp(bo->name, "code") == 0) {
244                         drm_bo->validated_shader = vc4_validate_shader(obj);
245                         if (!drm_bo->validated_shader)
246                                 abort();
247                 }
248         }
249         return 0;
250 }
251 
252 static int
vc4_simulator_unpin_bos(struct vc4_exec_info * exec)253 vc4_simulator_unpin_bos(struct vc4_exec_info *exec)
254 {
255         for (int i = 0; i < exec->bo_count; i++) {
256                 struct drm_gem_cma_object *obj = exec->bo[i];
257                 struct drm_vc4_bo *drm_bo = to_vc4_bo(&obj->base);
258                 struct vc4_bo *bo = drm_bo->bo;
259 
260                 assert(*(uint32_t *)(obj->vaddr +
261                                      obj->base.size) == BO_SENTINEL);
262                 memcpy(bo->map, obj->vaddr, bo->size);
263 
264                 if (drm_bo->validated_shader) {
265                         free(drm_bo->validated_shader->texture_samples);
266                         free(drm_bo->validated_shader);
267                 }
268         }
269 
270         free(exec->bo);
271 
272         return 0;
273 }
274 
275 static void
vc4_dump_to_file(struct vc4_exec_info * exec)276 vc4_dump_to_file(struct vc4_exec_info *exec)
277 {
278         static int dumpno = 0;
279         struct drm_vc4_get_hang_state *state;
280         struct drm_vc4_get_hang_state_bo *bo_state;
281         unsigned int dump_version = 0;
282 
283         if (!(vc4_debug & VC4_DEBUG_DUMP))
284                 return;
285 
286         state = calloc(1, sizeof(*state));
287 
288         int unref_count = 0;
289         list_for_each_entry_safe(struct drm_vc4_bo, bo, &exec->unref_list,
290                                  unref_head) {
291                 unref_count++;
292         }
293 
294         /* Add one more for the overflow area that isn't wrapped in a BO. */
295         state->bo_count = exec->bo_count + unref_count + 1;
296         bo_state = calloc(state->bo_count, sizeof(*bo_state));
297 
298         char *filename = NULL;
299         asprintf(&filename, "vc4-dri-%d.dump", dumpno++);
300         FILE *f = fopen(filename, "w+");
301         if (!f) {
302                 fprintf(stderr, "Couldn't open %s: %s", filename,
303                         strerror(errno));
304                 return;
305         }
306 
307         fwrite(&dump_version, sizeof(dump_version), 1, f);
308 
309         state->ct0ca = exec->ct0ca;
310         state->ct0ea = exec->ct0ea;
311         state->ct1ca = exec->ct1ca;
312         state->ct1ea = exec->ct1ea;
313         state->start_bin = exec->ct0ca;
314         state->start_render = exec->ct1ca;
315         fwrite(state, sizeof(*state), 1, f);
316 
317         int i;
318         for (i = 0; i < exec->bo_count; i++) {
319                 struct drm_gem_cma_object *cma_bo = exec->bo[i];
320                 bo_state[i].handle = i; /* Not used by the parser. */
321                 bo_state[i].paddr = cma_bo->paddr;
322                 bo_state[i].size = cma_bo->base.size;
323         }
324 
325         list_for_each_entry_safe(struct drm_vc4_bo, bo, &exec->unref_list,
326                                  unref_head) {
327                 struct drm_gem_cma_object *cma_bo = &bo->base;
328                 bo_state[i].handle = 0;
329                 bo_state[i].paddr = cma_bo->paddr;
330                 bo_state[i].size = cma_bo->base.size;
331                 i++;
332         }
333 
334         /* Add the static overflow memory area. */
335         bo_state[i].handle = exec->bo_count;
336         bo_state[i].paddr = sim_state.overflow->ofs;
337         bo_state[i].size = sim_state.overflow->size;
338         i++;
339 
340         fwrite(bo_state, sizeof(*bo_state), state->bo_count, f);
341 
342         for (int i = 0; i < exec->bo_count; i++) {
343                 struct drm_gem_cma_object *cma_bo = exec->bo[i];
344                 fwrite(cma_bo->vaddr, cma_bo->base.size, 1, f);
345         }
346 
347         list_for_each_entry_safe(struct drm_vc4_bo, bo, &exec->unref_list,
348                                  unref_head) {
349                 struct drm_gem_cma_object *cma_bo = &bo->base;
350                 fwrite(cma_bo->vaddr, cma_bo->base.size, 1, f);
351         }
352 
353         void *overflow = calloc(1, sim_state.overflow->size);
354         fwrite(overflow, 1, sim_state.overflow->size, f);
355         free(overflow);
356 
357         free(state);
358         free(bo_state);
359         fclose(f);
360 }
361 
362 int
vc4_simulator_flush(struct vc4_context * vc4,struct drm_vc4_submit_cl * args,struct vc4_job * job)363 vc4_simulator_flush(struct vc4_context *vc4,
364                     struct drm_vc4_submit_cl *args, struct vc4_job *job)
365 {
366         struct vc4_screen *screen = vc4->screen;
367         int fd = screen->fd;
368         struct vc4_simulator_file *file = vc4_get_simulator_file_for_fd(fd);
369         struct vc4_surface *csurf = vc4_surface(vc4->framebuffer.cbufs[0]);
370         struct vc4_resource *ctex = csurf ? vc4_resource(csurf->base.texture) : NULL;
371         struct vc4_simulator_bo *csim_bo = ctex ? vc4_get_simulator_bo(file, ctex->bo->handle) : NULL;
372         uint32_t winsys_stride = ctex ? csim_bo->winsys_stride : 0;
373         uint32_t sim_stride = ctex ? ctex->slices[0].stride : 0;
374         uint32_t row_len = MIN2(sim_stride, winsys_stride);
375         struct vc4_exec_info exec;
376         struct drm_device *dev = &file->dev;
377         int ret;
378 
379         memset(&exec, 0, sizeof(exec));
380         list_inithead(&exec.unref_list);
381 
382         if (ctex && csim_bo->winsys_map) {
383 #if 0
384                 fprintf(stderr, "%dx%d %d %d %d\n",
385                         ctex->base.b.width0, ctex->base.b.height0,
386                         winsys_stride,
387                         sim_stride,
388                         ctex->bo->size);
389 #endif
390 
391                 for (int y = 0; y < ctex->base.height0; y++) {
392                         memcpy(ctex->bo->map + y * sim_stride,
393                                csim_bo->winsys_map + y * winsys_stride,
394                                row_len);
395                 }
396         }
397 
398         exec.args = args;
399 
400         ret = vc4_simulator_pin_bos(dev, job, &exec);
401         if (ret)
402                 return ret;
403 
404         ret = vc4_cl_validate(dev, &exec);
405         if (ret)
406                 return ret;
407 
408         if (vc4_debug & VC4_DEBUG_CL) {
409                 fprintf(stderr, "RCL:\n");
410                 vc4_dump_cl(sim_state.mem + exec.ct1ca,
411                             exec.ct1ea - exec.ct1ca, true);
412         }
413 
414         vc4_dump_to_file(&exec);
415 
416         if (exec.ct0ca != exec.ct0ea) {
417                 int bfc = simpenrose_do_binning(exec.ct0ca, exec.ct0ea);
418                 if (bfc != 1) {
419                         fprintf(stderr, "Binning returned %d flushes, should be 1.\n",
420                                 bfc);
421                         fprintf(stderr, "Relocated binning command list:\n");
422                         vc4_dump_cl(sim_state.mem + exec.ct0ca,
423                                     exec.ct0ea - exec.ct0ca, false);
424                         abort();
425                 }
426         }
427         int rfc = simpenrose_do_rendering(exec.ct1ca, exec.ct1ea);
428         if (rfc != 1) {
429                 fprintf(stderr, "Rendering returned %d frames, should be 1.\n",
430                         rfc);
431                 fprintf(stderr, "Relocated render command list:\n");
432                 vc4_dump_cl(sim_state.mem + exec.ct1ca,
433                             exec.ct1ea - exec.ct1ca, true);
434                 abort();
435         }
436 
437         ret = vc4_simulator_unpin_bos(&exec);
438         if (ret)
439                 return ret;
440 
441         list_for_each_entry_safe(struct drm_vc4_bo, bo, &exec.unref_list,
442                                  unref_head) {
443                 struct vc4_simulator_bo *sim_bo = (struct vc4_simulator_bo *)bo;
444                 struct drm_gem_cma_object *obj = &sim_bo->base.base;
445 		list_del(&bo->unref_head);
446                 assert(*(uint32_t *)(obj->vaddr + obj->base.size) ==
447                        BO_SENTINEL);
448                 vc4_free_simulator_bo(sim_bo);
449         }
450 
451         if (ctex && csim_bo->winsys_map) {
452                 for (int y = 0; y < ctex->base.height0; y++) {
453                         memcpy(csim_bo->winsys_map + y * winsys_stride,
454                                ctex->bo->map + y * sim_stride,
455                                row_len);
456                 }
457         }
458 
459         return 0;
460 }
461 
462 /**
463  * Map the underlying GEM object from the real hardware GEM handle.
464  */
465 static void *
vc4_simulator_map_winsys_bo(int fd,struct vc4_simulator_bo * sim_bo)466 vc4_simulator_map_winsys_bo(int fd, struct vc4_simulator_bo *sim_bo)
467 {
468         struct drm_vc4_bo *bo = &sim_bo->base;
469         struct drm_gem_cma_object *obj = &bo->base;
470         int ret;
471         void *map;
472 
473         struct drm_mode_map_dumb map_dumb = {
474                 .handle = sim_bo->handle,
475         };
476         ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
477         if (ret != 0) {
478                 fprintf(stderr, "map ioctl failure\n");
479                 abort();
480         }
481 
482         map = mmap(NULL, obj->base.size, PROT_READ | PROT_WRITE, MAP_SHARED,
483                    fd, map_dumb.offset);
484         if (map == MAP_FAILED) {
485                 fprintf(stderr,
486                         "mmap of bo %d (offset 0x%016llx, size %d) failed\n",
487                         sim_bo->handle, (long long)map_dumb.offset,
488                         (int)obj->base.size);
489                 abort();
490         }
491 
492         return map;
493 }
494 
495 /**
496  * Do fixups after a BO has been opened from a handle.
497  *
498  * This could be done at DRM_IOCTL_GEM_OPEN/DRM_IOCTL_GEM_PRIME_FD_TO_HANDLE
499  * time, but we're still using drmPrimeFDToHandle() so we have this helper to
500  * be called afterward instead.
501  */
vc4_simulator_open_from_handle(int fd,uint32_t winsys_stride,int handle,uint32_t size)502 void vc4_simulator_open_from_handle(int fd, uint32_t winsys_stride,
503                                     int handle, uint32_t size)
504 {
505         struct vc4_simulator_bo *sim_bo =
506                 vc4_create_simulator_bo(fd, handle, size);
507 
508         sim_bo->winsys_stride = winsys_stride;
509         sim_bo->winsys_map = vc4_simulator_map_winsys_bo(fd, sim_bo);
510 }
511 
512 /**
513  * Simulated ioctl(fd, DRM_VC4_CREATE_BO) implementation.
514  *
515  * Making a VC4 BO is just a matter of making a corresponding BO on the host.
516  */
517 static int
vc4_simulator_create_bo_ioctl(int fd,struct drm_vc4_create_bo * args)518 vc4_simulator_create_bo_ioctl(int fd, struct drm_vc4_create_bo *args)
519 {
520         int ret;
521         struct drm_mode_create_dumb create = {
522                 .width = 128,
523                 .bpp = 8,
524                 .height = (args->size + 127) / 128,
525         };
526 
527         ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
528         assert(create.size >= args->size);
529 
530         args->handle = create.handle;
531 
532         vc4_create_simulator_bo(fd, create.handle, args->size);
533 
534         return ret;
535 }
536 
537 /**
538  * Simulated ioctl(fd, DRM_VC4_CREATE_SHADER_BO) implementation.
539  *
540  * In simulation we defer shader validation until exec time.  Just make a host
541  * BO and memcpy the contents in.
542  */
543 static int
vc4_simulator_create_shader_bo_ioctl(int fd,struct drm_vc4_create_shader_bo * args)544 vc4_simulator_create_shader_bo_ioctl(int fd,
545                                      struct drm_vc4_create_shader_bo *args)
546 {
547         int ret;
548         struct drm_mode_create_dumb create = {
549                 .width = 128,
550                 .bpp = 8,
551                 .height = (args->size + 127) / 128,
552         };
553 
554         ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
555         if (ret)
556                 return ret;
557         assert(create.size >= args->size);
558 
559         args->handle = create.handle;
560 
561         vc4_create_simulator_bo(fd, create.handle, args->size);
562 
563         struct drm_mode_map_dumb map = {
564                 .handle = create.handle
565         };
566         ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
567         if (ret)
568                 return ret;
569 
570         void *shader = mmap(NULL, args->size, PROT_READ | PROT_WRITE, MAP_SHARED,
571                             fd, map.offset);
572         memcpy(shader, (void *)(uintptr_t)args->data, args->size);
573         munmap(shader, args->size);
574 
575         return 0;
576 }
577 
578 /**
579  * Simulated ioctl(fd, DRM_VC4_MMAP_BO) implementation.
580  *
581  * We just pass this straight through to dumb mmap.
582  */
583 static int
vc4_simulator_mmap_bo_ioctl(int fd,struct drm_vc4_mmap_bo * args)584 vc4_simulator_mmap_bo_ioctl(int fd, struct drm_vc4_mmap_bo *args)
585 {
586         int ret;
587         struct drm_mode_map_dumb map = {
588                 .handle = args->handle,
589         };
590 
591         ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
592         args->offset = map.offset;
593 
594         return ret;
595 }
596 
597 static int
vc4_simulator_gem_close_ioctl(int fd,struct drm_gem_close * args)598 vc4_simulator_gem_close_ioctl(int fd, struct drm_gem_close *args)
599 {
600         /* Free the simulator's internal tracking. */
601         struct vc4_simulator_file *file = vc4_get_simulator_file_for_fd(fd);
602         struct vc4_simulator_bo *sim_bo = vc4_get_simulator_bo(file,
603                                                                args->handle);
604 
605         vc4_free_simulator_bo(sim_bo);
606 
607         /* Pass the call on down. */
608         return drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, args);
609 }
610 
611 static int
vc4_simulator_get_param_ioctl(int fd,struct drm_vc4_get_param * args)612 vc4_simulator_get_param_ioctl(int fd, struct drm_vc4_get_param *args)
613 {
614         switch (args->param) {
615         case DRM_VC4_PARAM_SUPPORTS_BRANCHES:
616         case DRM_VC4_PARAM_SUPPORTS_ETC1:
617         case DRM_VC4_PARAM_SUPPORTS_THREADED_FS:
618         case DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER:
619                 args->value = true;
620                 return 0;
621 
622         case DRM_VC4_PARAM_SUPPORTS_MADVISE:
623                 errno = -EINVAL;
624                 return -1;
625 
626         case DRM_VC4_PARAM_V3D_IDENT0:
627                 args->value = 0x02000000;
628                 return 0;
629 
630         case DRM_VC4_PARAM_V3D_IDENT1:
631                 args->value = 0x00000001;
632                 return 0;
633 
634         default:
635                 fprintf(stderr, "Unknown DRM_IOCTL_VC4_GET_PARAM(%lld)\n",
636                         (long long)args->param);
637                 abort();
638         };
639 }
640 
641 int
vc4_simulator_ioctl(int fd,unsigned long request,void * args)642 vc4_simulator_ioctl(int fd, unsigned long request, void *args)
643 {
644         switch (request) {
645         case DRM_IOCTL_VC4_CREATE_BO:
646                 return vc4_simulator_create_bo_ioctl(fd, args);
647         case DRM_IOCTL_VC4_CREATE_SHADER_BO:
648                 return vc4_simulator_create_shader_bo_ioctl(fd, args);
649         case DRM_IOCTL_VC4_MMAP_BO:
650                 return vc4_simulator_mmap_bo_ioctl(fd, args);
651 
652         case DRM_IOCTL_VC4_WAIT_BO:
653         case DRM_IOCTL_VC4_WAIT_SEQNO:
654                 /* We do all of the vc4 rendering synchronously, so we just
655                  * return immediately on the wait ioctls.  This ignores any
656                  * native rendering to the host BO, so it does mean we race on
657                  * front buffer rendering.
658                  */
659                 return 0;
660 
661         case DRM_IOCTL_VC4_LABEL_BO:
662                 /* This is just debug information, nothing to do. */
663                 return 0;
664 
665         case DRM_IOCTL_VC4_GET_TILING:
666         case DRM_IOCTL_VC4_SET_TILING:
667                 /* Disable these for now, since the sharing with i965 requires
668                  * linear buffers.
669                  */
670                 errno = -EINVAL;
671                 return -1;
672 
673         case DRM_IOCTL_VC4_GET_PARAM:
674                 return vc4_simulator_get_param_ioctl(fd, args);
675 
676         case DRM_IOCTL_GEM_CLOSE:
677                 return vc4_simulator_gem_close_ioctl(fd, args);
678 
679         case DRM_IOCTL_GEM_OPEN:
680         case DRM_IOCTL_GEM_FLINK:
681                 return drmIoctl(fd, request, args);
682         default:
683                 fprintf(stderr, "Unknown ioctl 0x%08x\n", (int)request);
684                 abort();
685         }
686 }
687 
688 static void
vc4_simulator_init_global(void)689 vc4_simulator_init_global(void)
690 {
691         mtx_lock(&sim_state.mutex);
692         if (sim_state.refcount++) {
693                 mtx_unlock(&sim_state.mutex);
694                 return;
695         }
696 
697         sim_state.mem_size = 256 * 1024 * 1024;
698         sim_state.mem = calloc(sim_state.mem_size, 1);
699         if (!sim_state.mem)
700                 abort();
701         sim_state.heap = u_mmInit(0, sim_state.mem_size);
702 
703         /* We supply our own memory so that we can have more aperture
704          * available (256MB instead of simpenrose's default 64MB).
705          */
706         simpenrose_init_hardware_supply_mem(sim_state.mem, sim_state.mem_size);
707 
708         /* Carve out low memory for tile allocation overflow.  The kernel
709          * should be automatically handling overflow memory setup on real
710          * hardware, but for simulation we just get one shot to set up enough
711          * overflow memory before execution.  This overflow mem will be used
712          * up over the whole lifetime of simpenrose (not reused on each
713          * flush), so it had better be big.
714          */
715         sim_state.overflow = u_mmAllocMem(sim_state.heap, 32 * 1024 * 1024,
716                                           PAGE_ALIGN2, 0);
717         simpenrose_supply_overflow_mem(sim_state.overflow->ofs,
718                                        sim_state.overflow->size);
719 
720         mtx_unlock(&sim_state.mutex);
721 
722         sim_state.fd_map =
723                 _mesa_hash_table_create(NULL,
724                                         _mesa_hash_pointer,
725                                         _mesa_key_pointer_equal);
726 }
727 
728 void
vc4_simulator_init(struct vc4_screen * screen)729 vc4_simulator_init(struct vc4_screen *screen)
730 {
731         vc4_simulator_init_global();
732 
733         screen->sim_file = rzalloc(screen, struct vc4_simulator_file);
734 
735         screen->sim_file->bo_map =
736                 _mesa_hash_table_create(screen->sim_file,
737                                         _mesa_hash_pointer,
738                                         _mesa_key_pointer_equal);
739 
740         mtx_lock(&sim_state.mutex);
741         _mesa_hash_table_insert(sim_state.fd_map, int_to_key(screen->fd + 1),
742                                 screen->sim_file);
743         mtx_unlock(&sim_state.mutex);
744 
745         screen->sim_file->dev.screen = screen;
746 }
747 
748 void
vc4_simulator_destroy(struct vc4_screen * screen)749 vc4_simulator_destroy(struct vc4_screen *screen)
750 {
751         mtx_lock(&sim_state.mutex);
752         if (!--sim_state.refcount) {
753                 _mesa_hash_table_destroy(sim_state.fd_map, NULL);
754                 u_mmDestroy(sim_state.heap);
755                 free(sim_state.mem);
756                 /* No memsetting it, because it contains the mutex. */
757         }
758         mtx_unlock(&sim_state.mutex);
759 }
760 
761 #endif /* USE_VC4_SIMULATOR */
762