• 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 vc5_simulator.c
26  *
27  * Implements VC5 simulation on top of a non-VC5 GEM fd.
28  *
29  * This file's goal is to emulate the VC5 ioctls' behavior in the kernel on
30  * top of the simpenrose software simulator.  Generally, VC5 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 VC5 is going to use, which of course
44  * doesn't work out in practice.  This means that for now, only DRI3 (VC5
45  * makes the winsys BOs) is supported, not DRI2 (window system makes the winys
46  * BOs).
47  */
48 
49 #ifdef USE_VC5_SIMULATOR
50 
51 #include <sys/mman.h>
52 #include "util/hash_table.h"
53 #include "util/ralloc.h"
54 #include "util/set.h"
55 #include "util/u_memory.h"
56 #include "util/u_mm.h"
57 #include "vc5_simulator_wrapper.h"
58 
59 #include "vc5_screen.h"
60 #include "vc5_context.h"
61 
62 /** Global (across GEM fds) state for the simulator */
63 static struct vc5_simulator_state {
64         mtx_t mutex;
65 
66         struct v3d_hw *v3d;
67         int ver;
68 
69         /* Base virtual address of the heap. */
70         void *mem;
71         /* Base hardware address of the heap. */
72         uint32_t mem_base;
73         /* Size of the heap. */
74         size_t mem_size;
75 
76         struct mem_block *heap;
77         struct mem_block *overflow;
78 
79         /** Mapping from GEM handle to struct vc5_simulator_bo * */
80         struct hash_table *fd_map;
81 
82         int refcount;
83 } sim_state = {
84         .mutex = _MTX_INITIALIZER_NP,
85 };
86 
87 /** Per-GEM-fd state for the simulator. */
88 struct vc5_simulator_file {
89         int fd;
90 
91         /** Mapping from GEM handle to struct vc5_simulator_bo * */
92         struct hash_table *bo_map;
93 
94         struct mem_block *gmp;
95         void *gmp_vaddr;
96 };
97 
98 /** Wrapper for drm_vc5_bo tracking the simulator-specific state. */
99 struct vc5_simulator_bo {
100         struct vc5_simulator_file *file;
101 
102         /** Area for this BO within sim_state->mem */
103         struct mem_block *block;
104         uint32_t size;
105         void *vaddr;
106 
107         void *winsys_map;
108         uint32_t winsys_stride;
109 
110         int handle;
111 };
112 
113 static void *
int_to_key(int key)114 int_to_key(int key)
115 {
116         return (void *)(uintptr_t)key;
117 }
118 
119 static struct vc5_simulator_file *
vc5_get_simulator_file_for_fd(int fd)120 vc5_get_simulator_file_for_fd(int fd)
121 {
122         struct hash_entry *entry = _mesa_hash_table_search(sim_state.fd_map,
123                                                            int_to_key(fd + 1));
124         return entry ? entry->data : NULL;
125 }
126 
127 /* A marker placed just after each BO, then checked after rendering to make
128  * sure it's still there.
129  */
130 #define BO_SENTINEL		0xfedcba98
131 
132 /* 128kb */
133 #define GMP_ALIGN2		17
134 
135 /**
136  * Sets the range of GPU virtual address space to have the given GMP
137  * permissions (bit 0 = read, bit 1 = write, write-only forbidden).
138  */
139 static void
set_gmp_flags(struct vc5_simulator_file * file,uint32_t offset,uint32_t size,uint32_t flag)140 set_gmp_flags(struct vc5_simulator_file *file,
141               uint32_t offset, uint32_t size, uint32_t flag)
142 {
143         assert((offset & ((1 << GMP_ALIGN2) - 1)) == 0);
144         int gmp_offset = offset >> GMP_ALIGN2;
145         int gmp_count = align(size, 1 << GMP_ALIGN2) >> GMP_ALIGN2;
146         uint32_t *gmp = file->gmp_vaddr;
147 
148         assert(flag <= 0x3);
149 
150         for (int i = gmp_offset; i < gmp_offset + gmp_count; i++) {
151                 int32_t bitshift = (i % 16) * 2;
152                 gmp[i / 16] &= ~(0x3 << bitshift);
153                 gmp[i / 16] |= flag << bitshift;
154         }
155 }
156 
157 /**
158  * Allocates space in simulator memory and returns a tracking struct for it
159  * that also contains the drm_gem_cma_object struct.
160  */
161 static struct vc5_simulator_bo *
vc5_create_simulator_bo(int fd,int handle,unsigned size)162 vc5_create_simulator_bo(int fd, int handle, unsigned size)
163 {
164         struct vc5_simulator_file *file = vc5_get_simulator_file_for_fd(fd);
165         struct vc5_simulator_bo *sim_bo = rzalloc(file,
166                                                   struct vc5_simulator_bo);
167         size = align(size, 4096);
168 
169         sim_bo->file = file;
170         sim_bo->handle = handle;
171 
172         mtx_lock(&sim_state.mutex);
173         sim_bo->block = u_mmAllocMem(sim_state.heap, size + 4, GMP_ALIGN2, 0);
174         mtx_unlock(&sim_state.mutex);
175         assert(sim_bo->block);
176 
177         set_gmp_flags(file, sim_bo->block->ofs, size, 0x3);
178 
179         sim_bo->size = size;
180         sim_bo->vaddr = sim_state.mem + sim_bo->block->ofs - sim_state.mem_base;
181         memset(sim_bo->vaddr, 0xd0, size);
182 
183         *(uint32_t *)(sim_bo->vaddr + sim_bo->size) = BO_SENTINEL;
184 
185         /* A handle of 0 is used for vc5_gem.c internal allocations that
186          * don't need to go in the lookup table.
187          */
188         if (handle != 0) {
189                 mtx_lock(&sim_state.mutex);
190                 _mesa_hash_table_insert(file->bo_map, int_to_key(handle),
191                                         sim_bo);
192                 mtx_unlock(&sim_state.mutex);
193         }
194 
195         return sim_bo;
196 }
197 
198 static void
vc5_free_simulator_bo(struct vc5_simulator_bo * sim_bo)199 vc5_free_simulator_bo(struct vc5_simulator_bo *sim_bo)
200 {
201         struct vc5_simulator_file *sim_file = sim_bo->file;
202 
203         if (sim_bo->winsys_map)
204                 munmap(sim_bo->winsys_map, sim_bo->size);
205 
206         set_gmp_flags(sim_file, sim_bo->block->ofs, sim_bo->size, 0x0);
207 
208         mtx_lock(&sim_state.mutex);
209         u_mmFreeMem(sim_bo->block);
210         if (sim_bo->handle) {
211                 struct hash_entry *entry =
212                         _mesa_hash_table_search(sim_file->bo_map,
213                                                 int_to_key(sim_bo->handle));
214                 _mesa_hash_table_remove(sim_file->bo_map, entry);
215         }
216         mtx_unlock(&sim_state.mutex);
217         ralloc_free(sim_bo);
218 }
219 
220 static struct vc5_simulator_bo *
vc5_get_simulator_bo(struct vc5_simulator_file * file,int gem_handle)221 vc5_get_simulator_bo(struct vc5_simulator_file *file, int gem_handle)
222 {
223         mtx_lock(&sim_state.mutex);
224         struct hash_entry *entry =
225                 _mesa_hash_table_search(file->bo_map, int_to_key(gem_handle));
226         mtx_unlock(&sim_state.mutex);
227 
228         return entry ? entry->data : NULL;
229 }
230 
231 static int
vc5_simulator_pin_bos(int fd,struct vc5_job * job)232 vc5_simulator_pin_bos(int fd, struct vc5_job *job)
233 {
234         struct vc5_simulator_file *file = vc5_get_simulator_file_for_fd(fd);
235         struct set_entry *entry;
236 
237         set_foreach(job->bos, entry) {
238                 struct vc5_bo *bo = (struct vc5_bo *)entry->key;
239                 struct vc5_simulator_bo *sim_bo =
240                         vc5_get_simulator_bo(file, bo->handle);
241 
242                 vc5_bo_map(bo);
243                 memcpy(sim_bo->vaddr, bo->map, bo->size);
244         }
245 
246         return 0;
247 }
248 
249 static int
vc5_simulator_unpin_bos(int fd,struct vc5_job * job)250 vc5_simulator_unpin_bos(int fd, struct vc5_job *job)
251 {
252         struct vc5_simulator_file *file = vc5_get_simulator_file_for_fd(fd);
253         struct set_entry *entry;
254 
255         set_foreach(job->bos, entry) {
256                 struct vc5_bo *bo = (struct vc5_bo *)entry->key;
257                 struct vc5_simulator_bo *sim_bo =
258                         vc5_get_simulator_bo(file, bo->handle);
259 
260                 if (*(uint32_t *)(sim_bo->vaddr +
261                                   sim_bo->size) != BO_SENTINEL) {
262                         fprintf(stderr, "Buffer overflow in %s\n", bo->name);
263                 }
264 
265                 vc5_bo_map(bo);
266                 memcpy(bo->map, sim_bo->vaddr, bo->size);
267         }
268 
269         return 0;
270 }
271 
272 #if 0
273 static void
274 vc5_dump_to_file(struct vc5_exec_info *exec)
275 {
276         static int dumpno = 0;
277         struct drm_vc5_get_hang_state *state;
278         struct drm_vc5_get_hang_state_bo *bo_state;
279         unsigned int dump_version = 0;
280 
281         if (!(vc5_debug & VC5_DEBUG_DUMP))
282                 return;
283 
284         state = calloc(1, sizeof(*state));
285 
286         int unref_count = 0;
287         list_for_each_entry_safe(struct drm_vc5_bo, bo, &exec->unref_list,
288                                  unref_head) {
289                 unref_count++;
290         }
291 
292         /* Add one more for the overflow area that isn't wrapped in a BO. */
293         state->bo_count = exec->bo_count + unref_count + 1;
294         bo_state = calloc(state->bo_count, sizeof(*bo_state));
295 
296         char *filename = NULL;
297         asprintf(&filename, "vc5-dri-%d.dump", dumpno++);
298         FILE *f = fopen(filename, "w+");
299         if (!f) {
300                 fprintf(stderr, "Couldn't open %s: %s", filename,
301                         strerror(errno));
302                 return;
303         }
304 
305         fwrite(&dump_version, sizeof(dump_version), 1, f);
306 
307         state->ct0ca = exec->ct0ca;
308         state->ct0ea = exec->ct0ea;
309         state->ct1ca = exec->ct1ca;
310         state->ct1ea = exec->ct1ea;
311         state->start_bin = exec->ct0ca;
312         state->start_render = exec->ct1ca;
313         fwrite(state, sizeof(*state), 1, f);
314 
315         int i;
316         for (i = 0; i < exec->bo_count; i++) {
317                 struct drm_gem_cma_object *cma_bo = exec->bo[i];
318                 bo_state[i].handle = i; /* Not used by the parser. */
319                 bo_state[i].paddr = cma_bo->paddr;
320                 bo_state[i].size = cma_bo->base.size;
321         }
322 
323         list_for_each_entry_safe(struct drm_vc5_bo, bo, &exec->unref_list,
324                                  unref_head) {
325                 struct drm_gem_cma_object *cma_bo = &bo->base;
326                 bo_state[i].handle = 0;
327                 bo_state[i].paddr = cma_bo->paddr;
328                 bo_state[i].size = cma_bo->base.size;
329                 i++;
330         }
331 
332         /* Add the static overflow memory area. */
333         bo_state[i].handle = exec->bo_count;
334         bo_state[i].paddr = sim_state.overflow->ofs;
335         bo_state[i].size = sim_state.overflow->size;
336         i++;
337 
338         fwrite(bo_state, sizeof(*bo_state), state->bo_count, f);
339 
340         for (int i = 0; i < exec->bo_count; i++) {
341                 struct drm_gem_cma_object *cma_bo = exec->bo[i];
342                 fwrite(cma_bo->vaddr, cma_bo->base.size, 1, f);
343         }
344 
345         list_for_each_entry_safe(struct drm_vc5_bo, bo, &exec->unref_list,
346                                  unref_head) {
347                 struct drm_gem_cma_object *cma_bo = &bo->base;
348                 fwrite(cma_bo->vaddr, cma_bo->base.size, 1, f);
349         }
350 
351         void *overflow = calloc(1, sim_state.overflow->size);
352         fwrite(overflow, 1, sim_state.overflow->size, f);
353         free(overflow);
354 
355         free(state);
356         free(bo_state);
357         fclose(f);
358 }
359 #endif
360 
361 int
vc5_simulator_flush(struct vc5_context * vc5,struct drm_vc5_submit_cl * submit,struct vc5_job * job)362 vc5_simulator_flush(struct vc5_context *vc5,
363                     struct drm_vc5_submit_cl *submit, struct vc5_job *job)
364 {
365         struct vc5_screen *screen = vc5->screen;
366         int fd = screen->fd;
367         struct vc5_simulator_file *file = vc5_get_simulator_file_for_fd(fd);
368         struct vc5_surface *csurf = vc5_surface(vc5->framebuffer.cbufs[0]);
369         struct vc5_resource *ctex = csurf ? vc5_resource(csurf->base.texture) : NULL;
370         struct vc5_simulator_bo *csim_bo = ctex ? vc5_get_simulator_bo(file, ctex->bo->handle) : NULL;
371         uint32_t winsys_stride = ctex ? csim_bo->winsys_stride : 0;
372         uint32_t sim_stride = ctex ? ctex->slices[0].stride : 0;
373         uint32_t row_len = MIN2(sim_stride, winsys_stride);
374         int ret;
375 
376         if (ctex && csim_bo->winsys_map) {
377 #if 0
378                 fprintf(stderr, "%dx%d %d %d %d\n",
379                         ctex->base.b.width0, ctex->base.b.height0,
380                         winsys_stride,
381                         sim_stride,
382                         ctex->bo->size);
383 #endif
384 
385                 for (int y = 0; y < ctex->base.height0; y++) {
386                         memcpy(ctex->bo->map + y * sim_stride,
387                                csim_bo->winsys_map + y * winsys_stride,
388                                row_len);
389                 }
390         }
391 
392         ret = vc5_simulator_pin_bos(fd, job);
393         if (ret)
394                 return ret;
395 
396         //vc5_dump_to_file(&exec);
397 
398         if (sim_state.ver >= 41)
399                 v3d41_simulator_flush(sim_state.v3d, submit, file->gmp->ofs);
400         else
401                 v3d33_simulator_flush(sim_state.v3d, submit, file->gmp->ofs);
402 
403         ret = vc5_simulator_unpin_bos(fd, job);
404         if (ret)
405                 return ret;
406 
407         if (ctex && csim_bo->winsys_map) {
408                 for (int y = 0; y < ctex->base.height0; y++) {
409                         memcpy(csim_bo->winsys_map + y * winsys_stride,
410                                ctex->bo->map + y * sim_stride,
411                                row_len);
412                 }
413         }
414 
415         return 0;
416 }
417 
418 /**
419  * Map the underlying GEM object from the real hardware GEM handle.
420  */
421 static void *
vc5_simulator_map_winsys_bo(int fd,struct vc5_simulator_bo * sim_bo)422 vc5_simulator_map_winsys_bo(int fd, struct vc5_simulator_bo *sim_bo)
423 {
424         int ret;
425         void *map;
426 
427         struct drm_mode_map_dumb map_dumb = {
428                 .handle = sim_bo->handle,
429         };
430         ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
431         if (ret != 0) {
432                 fprintf(stderr, "map ioctl failure\n");
433                 abort();
434         }
435 
436         map = mmap(NULL, sim_bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
437                    fd, map_dumb.offset);
438         if (map == MAP_FAILED) {
439                 fprintf(stderr,
440                         "mmap of bo %d (offset 0x%016llx, size %d) failed\n",
441                         sim_bo->handle, (long long)map_dumb.offset,
442                         (int)sim_bo->size);
443                 abort();
444         }
445 
446         return map;
447 }
448 
449 /**
450  * Do fixups after a BO has been opened from a handle.
451  *
452  * This could be done at DRM_IOCTL_GEM_OPEN/DRM_IOCTL_GEM_PRIME_FD_TO_HANDLE
453  * time, but we're still using drmPrimeFDToHandle() so we have this helper to
454  * be called afterward instead.
455  */
vc5_simulator_open_from_handle(int fd,uint32_t winsys_stride,int handle,uint32_t size)456 void vc5_simulator_open_from_handle(int fd, uint32_t winsys_stride,
457                                     int handle, uint32_t size)
458 {
459         struct vc5_simulator_bo *sim_bo =
460                 vc5_create_simulator_bo(fd, handle, size);
461 
462         sim_bo->winsys_stride = winsys_stride;
463         sim_bo->winsys_map = vc5_simulator_map_winsys_bo(fd, sim_bo);
464 }
465 
466 /**
467  * Simulated ioctl(fd, DRM_VC5_CREATE_BO) implementation.
468  *
469  * Making a VC5 BO is just a matter of making a corresponding BO on the host.
470  */
471 static int
vc5_simulator_create_bo_ioctl(int fd,struct drm_vc5_create_bo * args)472 vc5_simulator_create_bo_ioctl(int fd, struct drm_vc5_create_bo *args)
473 {
474         int ret;
475         struct drm_mode_create_dumb create = {
476                 .width = 128,
477                 .bpp = 8,
478                 .height = (args->size + 127) / 128,
479         };
480 
481         ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
482         assert(create.size >= args->size);
483 
484         args->handle = create.handle;
485 
486         struct vc5_simulator_bo *sim_bo =
487                 vc5_create_simulator_bo(fd, create.handle, args->size);
488 
489         args->offset = sim_bo->block->ofs;
490 
491         return ret;
492 }
493 
494 /**
495  * Simulated ioctl(fd, DRM_VC5_MMAP_BO) implementation.
496  *
497  * We just pass this straight through to dumb mmap.
498  */
499 static int
vc5_simulator_mmap_bo_ioctl(int fd,struct drm_vc5_mmap_bo * args)500 vc5_simulator_mmap_bo_ioctl(int fd, struct drm_vc5_mmap_bo *args)
501 {
502         int ret;
503         struct drm_mode_map_dumb map = {
504                 .handle = args->handle,
505         };
506 
507         ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
508         args->offset = map.offset;
509 
510         return ret;
511 }
512 
513 static int
vc5_simulator_gem_close_ioctl(int fd,struct drm_gem_close * args)514 vc5_simulator_gem_close_ioctl(int fd, struct drm_gem_close *args)
515 {
516         /* Free the simulator's internal tracking. */
517         struct vc5_simulator_file *file = vc5_get_simulator_file_for_fd(fd);
518         struct vc5_simulator_bo *sim_bo = vc5_get_simulator_bo(file,
519                                                                args->handle);
520 
521         vc5_free_simulator_bo(sim_bo);
522 
523         /* Pass the call on down. */
524         return drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, args);
525 }
526 
527 static int
vc5_simulator_get_param_ioctl(int fd,struct drm_vc5_get_param * args)528 vc5_simulator_get_param_ioctl(int fd, struct drm_vc5_get_param *args)
529 {
530         if (sim_state.ver >= 41)
531                 return v3d41_simulator_get_param_ioctl(sim_state.v3d, args);
532         else
533                 return v3d33_simulator_get_param_ioctl(sim_state.v3d, args);
534 }
535 
536 int
vc5_simulator_ioctl(int fd,unsigned long request,void * args)537 vc5_simulator_ioctl(int fd, unsigned long request, void *args)
538 {
539         switch (request) {
540         case DRM_IOCTL_VC5_CREATE_BO:
541                 return vc5_simulator_create_bo_ioctl(fd, args);
542         case DRM_IOCTL_VC5_MMAP_BO:
543                 return vc5_simulator_mmap_bo_ioctl(fd, args);
544 
545         case DRM_IOCTL_VC5_WAIT_BO:
546         case DRM_IOCTL_VC5_WAIT_SEQNO:
547                 /* We do all of the vc5 rendering synchronously, so we just
548                  * return immediately on the wait ioctls.  This ignores any
549                  * native rendering to the host BO, so it does mean we race on
550                  * front buffer rendering.
551                  */
552                 return 0;
553 
554         case DRM_IOCTL_VC5_GET_PARAM:
555                 return vc5_simulator_get_param_ioctl(fd, args);
556 
557         case DRM_IOCTL_GEM_CLOSE:
558                 return vc5_simulator_gem_close_ioctl(fd, args);
559 
560         case DRM_IOCTL_GEM_OPEN:
561         case DRM_IOCTL_GEM_FLINK:
562                 return drmIoctl(fd, request, args);
563         default:
564                 fprintf(stderr, "Unknown ioctl 0x%08x\n", (int)request);
565                 abort();
566         }
567 }
568 
569 static void
vc5_simulator_init_global(const struct v3d_device_info * devinfo)570 vc5_simulator_init_global(const struct v3d_device_info *devinfo)
571 {
572         mtx_lock(&sim_state.mutex);
573         if (sim_state.refcount++) {
574                 mtx_unlock(&sim_state.mutex);
575                 return;
576         }
577 
578         sim_state.v3d = v3d_hw_auto_new(NULL);
579         v3d_hw_alloc_mem(sim_state.v3d, 1024 * 1024 * 1024);
580         sim_state.mem_base =
581                 v3d_hw_get_mem(sim_state.v3d, &sim_state.mem_size,
582                                &sim_state.mem);
583 
584         sim_state.heap = u_mmInit(0, sim_state.mem_size);
585 
586         /* Make a block of 0xd0 at address 0 to make sure we don't screw up
587          * and land there.
588          */
589         struct mem_block *b = u_mmAllocMem(sim_state.heap, 4096, GMP_ALIGN2, 0);
590         memset(sim_state.mem + b->ofs - sim_state.mem_base, 0xd0, 4096);
591 
592         sim_state.ver = v3d_hw_get_version(sim_state.v3d);
593 
594         mtx_unlock(&sim_state.mutex);
595 
596         sim_state.fd_map =
597                 _mesa_hash_table_create(NULL,
598                                         _mesa_hash_pointer,
599                                         _mesa_key_pointer_equal);
600 
601         if (sim_state.ver >= 41)
602                 v3d41_simulator_init_regs(sim_state.v3d);
603         else
604                 v3d33_simulator_init_regs(sim_state.v3d);
605 }
606 
607 void
vc5_simulator_init(struct vc5_screen * screen)608 vc5_simulator_init(struct vc5_screen *screen)
609 {
610         vc5_simulator_init_global(&screen->devinfo);
611 
612         screen->sim_file = rzalloc(screen, struct vc5_simulator_file);
613         struct vc5_simulator_file *sim_file = screen->sim_file;
614 
615         screen->sim_file->bo_map =
616                 _mesa_hash_table_create(screen->sim_file,
617                                         _mesa_hash_pointer,
618                                         _mesa_key_pointer_equal);
619 
620         mtx_lock(&sim_state.mutex);
621         _mesa_hash_table_insert(sim_state.fd_map, int_to_key(screen->fd + 1),
622                                 screen->sim_file);
623         mtx_unlock(&sim_state.mutex);
624 
625         sim_file->gmp = u_mmAllocMem(sim_state.heap, 8096, GMP_ALIGN2, 0);
626         sim_file->gmp_vaddr = (sim_state.mem + sim_file->gmp->ofs -
627                                sim_state.mem_base);
628 }
629 
630 void
vc5_simulator_destroy(struct vc5_screen * screen)631 vc5_simulator_destroy(struct vc5_screen *screen)
632 {
633         mtx_lock(&sim_state.mutex);
634         if (!--sim_state.refcount) {
635                 _mesa_hash_table_destroy(sim_state.fd_map, NULL);
636                 u_mmDestroy(sim_state.heap);
637                 /* No memsetting the struct, because it contains the mutex. */
638                 sim_state.mem = NULL;
639         }
640         mtx_unlock(&sim_state.mutex);
641 }
642 
643 #endif /* USE_VC5_SIMULATOR */
644