2 * Copyright © 2018 Broadcom
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:
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
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
21 * DEALINGS IN THE SOFTWARE.
26 * Implements core GEM support (particularly ioctls) underneath the libc ioctl
27 * wrappers, and calls into the driver-specific code as necessary.
30 #include <c11/threads.h>
32 #include <linux/memfd.h>
37 #include <sys/ioctl.h>
40 #include "drm-uapi/drm.h"
42 #include "util/hash_table.h"
43 #include "util/u_atomic.h"
45 #define SHIM_MEM_SIZE (4ull * 1024 * 1024 * 1024)
47 #ifndef HAVE_MEMFD_CREATE
48 #include <sys/syscall.h>
51 memfd_create(const char *name
, unsigned int flags
)
53 return syscall(SYS_memfd_create
, name
, flags
);
57 /* Global state for the shim shared between libc, core, and driver. */
58 struct shim_device shim_device
;
61 uint_key_hash(const void *key
)
63 return (uintptr_t)key
;
67 uint_key_compare(const void *a
, const void *b
)
73 * Called when the first libc shim is called, to initialize GEM simulation
74 * state (other than the shims themselves).
77 drm_shim_device_init(void)
79 shim_device
.fd_map
= _mesa_hash_table_create(NULL
,
83 mtx_init(&shim_device
.mem_lock
, mtx_plain
);
85 shim_device
.mem_fd
= memfd_create("shim mem", MFD_CLOEXEC
);
86 assert(shim_device
.mem_fd
!= -1);
88 int ret
= ftruncate(shim_device
.mem_fd
, SHIM_MEM_SIZE
);
91 util_vma_heap_init(&shim_device
.mem_heap
, 4096, SHIM_MEM_SIZE
- 4096);
93 drm_shim_driver_init();
96 static struct shim_fd
*
97 drm_shim_file_create(int fd
)
99 struct shim_fd
*shim_fd
= calloc(1, sizeof(*shim_fd
));
102 mtx_init(&shim_fd
->handle_lock
, mtx_plain
);
103 shim_fd
->handles
= _mesa_hash_table_create(NULL
,
111 * Called when the libc shims have interposed an open or dup of our simulated
114 void drm_shim_fd_register(int fd
, struct shim_fd
*shim_fd
)
117 shim_fd
= drm_shim_file_create(fd
);
119 _mesa_hash_table_insert(shim_device
.fd_map
, (void *)(uintptr_t)(fd
+ 1), shim_fd
);
123 drm_shim_fd_lookup(int fd
)
128 struct hash_entry
*entry
=
129 _mesa_hash_table_search(shim_device
.fd_map
, (void *)(uintptr_t)(fd
+ 1));
136 /* ioctl used by drmGetVersion() */
138 drm_shim_ioctl_version(int fd
, unsigned long request
, void *arg
)
140 struct drm_version
*args
= arg
;
141 const char *date
= "20190320";
142 const char *desc
= "shim";
144 args
->version_major
= shim_device
.version_major
;
145 args
->version_minor
= shim_device
.version_minor
;
146 args
->version_patchlevel
= shim_device
.version_patchlevel
;
149 strncpy(args
->name
, shim_device
.driver_name
, args
->name_len
);
151 strncpy(args
->date
, date
, args
->date_len
);
153 strncpy(args
->desc
, desc
, args
->desc_len
);
154 args
->name_len
= strlen(shim_device
.driver_name
);
155 args
->date_len
= strlen(date
);
156 args
->desc_len
= strlen(desc
);
162 drm_shim_ioctl_get_cap(int fd
, unsigned long request
, void *arg
)
164 struct drm_get_cap
*gc
= arg
;
166 switch (gc
->capability
) {
168 case DRM_CAP_SYNCOBJ
:
173 fprintf(stderr
, "DRM_IOCTL_GET_CAP: unhandled 0x%x\n",
174 (int)gc
->capability
);
180 drm_shim_ioctl_gem_close(int fd
, unsigned long request
, void *arg
)
182 struct shim_fd
*shim_fd
= drm_shim_fd_lookup(fd
);
183 struct drm_gem_close
*c
= arg
;
188 mtx_lock(&shim_fd
->handle_lock
);
189 struct hash_entry
*entry
=
190 _mesa_hash_table_search(shim_fd
->handles
, (void *)(uintptr_t)c
->handle
);
192 mtx_unlock(&shim_fd
->handle_lock
);
196 struct shim_bo
*bo
= entry
->data
;
197 _mesa_hash_table_remove(shim_fd
->handles
, entry
);
199 mtx_unlock(&shim_fd
->handle_lock
);
204 drm_shim_ioctl_syncobj_create(int fd
, unsigned long request
, void *arg
)
206 struct drm_syncobj_create
*create
= arg
;
208 create
->handle
= 1; /* 0 is invalid */
214 drm_shim_ioctl_stub(int fd
, unsigned long request
, void *arg
)
219 ioctl_fn_t core_ioctls
[] = {
220 [_IOC_NR(DRM_IOCTL_VERSION
)] = drm_shim_ioctl_version
,
221 [_IOC_NR(DRM_IOCTL_GET_CAP
)] = drm_shim_ioctl_get_cap
,
222 [_IOC_NR(DRM_IOCTL_GEM_CLOSE
)] = drm_shim_ioctl_gem_close
,
223 [_IOC_NR(DRM_IOCTL_SYNCOBJ_CREATE
)] = drm_shim_ioctl_syncobj_create
,
224 [_IOC_NR(DRM_IOCTL_SYNCOBJ_DESTROY
)] = drm_shim_ioctl_stub
,
225 [_IOC_NR(DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
)] = drm_shim_ioctl_stub
,
226 [_IOC_NR(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE
)] = drm_shim_ioctl_stub
,
227 [_IOC_NR(DRM_IOCTL_SYNCOBJ_WAIT
)] = drm_shim_ioctl_stub
,
231 * Implements the GEM core ioctls, and calls into driver-specific ioctls.
234 drm_shim_ioctl(int fd
, unsigned long request
, void *arg
)
236 int type
= _IOC_TYPE(request
);
237 int nr
= _IOC_NR(request
);
239 assert(type
== DRM_IOCTL_BASE
);
241 if (nr
>= DRM_COMMAND_BASE
&& nr
< DRM_COMMAND_END
) {
242 int driver_nr
= nr
- DRM_COMMAND_BASE
;
244 if (driver_nr
< shim_device
.driver_ioctl_count
&&
245 shim_device
.driver_ioctls
[driver_nr
]) {
246 return shim_device
.driver_ioctls
[driver_nr
](fd
, request
, arg
);
249 if (nr
< ARRAY_SIZE(core_ioctls
) && core_ioctls
[nr
]) {
250 return core_ioctls
[nr
](fd
, request
, arg
);
254 if (nr
>= DRM_COMMAND_BASE
&& nr
< DRM_COMMAND_END
) {
256 "DRM_SHIM: unhandled driver DRM ioctl %d (0x%08lx)\n",
257 nr
- DRM_COMMAND_BASE
, request
);
260 "DRM_SHIM: unhandled core DRM ioctl 0x%X (0x%08lx)\n",
268 drm_shim_bo_init(struct shim_bo
*bo
, size_t size
)
271 mtx_lock(&shim_device
.mem_lock
);
272 bo
->mem_addr
= util_vma_heap_alloc(&shim_device
.mem_heap
, size
, 4096);
273 mtx_unlock(&shim_device
.mem_lock
);
274 assert(bo
->mem_addr
);
280 drm_shim_bo_lookup(struct shim_fd
*shim_fd
, int handle
)
285 mtx_lock(&shim_fd
->handle_lock
);
286 struct hash_entry
*entry
=
287 _mesa_hash_table_search(shim_fd
->handles
, (void *)(uintptr_t)handle
);
288 struct shim_bo
*bo
= entry
? entry
->data
: NULL
;
289 mtx_unlock(&shim_fd
->handle_lock
);
292 p_atomic_inc(&bo
->refcount
);
298 drm_shim_bo_get(struct shim_bo
*bo
)
300 p_atomic_inc(&bo
->refcount
);
304 drm_shim_bo_put(struct shim_bo
*bo
)
306 if (p_atomic_dec_return(&bo
->refcount
) == 0)
309 if (shim_device
.driver_bo_free
)
310 shim_device
.driver_bo_free(bo
);
312 mtx_lock(&shim_device
.mem_lock
);
313 util_vma_heap_free(&shim_device
.mem_heap
, bo
->mem_addr
, bo
->size
);
314 mtx_unlock(&shim_device
.mem_lock
);
319 drm_shim_bo_get_handle(struct shim_fd
*shim_fd
, struct shim_bo
*bo
)
321 /* We should probably have some real datastructure for finding the free
324 mtx_lock(&shim_fd
->handle_lock
);
325 for (int new_handle
= 1; ; new_handle
++) {
326 void *key
= (void *)(uintptr_t)new_handle
;
327 if (!_mesa_hash_table_search(shim_fd
->handles
, key
)) {
329 _mesa_hash_table_insert(shim_fd
->handles
, key
, bo
);
330 mtx_unlock(&shim_fd
->handle_lock
);
334 mtx_unlock(&shim_fd
->handle_lock
);
339 /* Creates an mmap offset for the BO in the DRM fd.
341 * XXX: We should be maintaining a u_mm allocator where the mmap offsets
342 * allocate the size of the BO and it can be used to look the BO back up.
343 * Instead, we just stuff the shim's pointer as the return value, and treat
344 * the incoming mmap offset on the DRM fd as a BO pointer. This doesn't work
345 * if someone tries to map a subset of the BO, but it's enough to get V3D
349 drm_shim_bo_get_mmap_offset(struct shim_fd
*shim_fd
, struct shim_bo
*bo
)
351 return (uintptr_t)bo
;
354 /* For mmap() on the DRM fd, look up the BO from the "offset" and map the BO's
358 drm_shim_mmap(struct shim_fd
*shim_fd
, size_t length
, int prot
, int flags
,
359 int fd
, off_t offset
)
361 struct shim_bo
*bo
= (void *)(uintptr_t)offset
;
363 return mmap(NULL
, length
, prot
, flags
, shim_device
.mem_fd
, bo
->mem_addr
);