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 static mtx_t handle_lock
= _MTX_INITIALIZER_NP
;
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 drm_shim_driver_init();
86 static struct shim_fd
*
87 drm_shim_file_create(int fd
)
89 struct shim_fd
*shim_fd
= calloc(1, sizeof(*shim_fd
));
92 shim_fd
->handles
= _mesa_hash_table_create(NULL
,
100 * Called when the libc shims have interposed an open or dup of our simulated
103 void drm_shim_fd_register(int fd
, struct shim_fd
*shim_fd
)
106 shim_fd
= drm_shim_file_create(fd
);
108 _mesa_hash_table_insert(shim_device
.fd_map
, (void *)(uintptr_t)(fd
+ 1), shim_fd
);
112 drm_shim_fd_lookup(int fd
)
117 struct hash_entry
*entry
=
118 _mesa_hash_table_search(shim_device
.fd_map
, (void *)(uintptr_t)(fd
+ 1));
125 /* ioctl used by drmGetVersion() */
127 drm_shim_ioctl_version(int fd
, unsigned long request
, void *arg
)
129 struct drm_version
*args
= arg
;
130 const char *date
= "20190320";
131 const char *desc
= "shim";
134 strncpy(args
->name
, shim_device
.driver_name
, args
->name_len
);
136 strncpy(args
->date
, date
, args
->date_len
);
138 strncpy(args
->desc
, desc
, args
->desc_len
);
139 args
->name_len
= strlen(shim_device
.driver_name
);
140 args
->date_len
= strlen(date
);
141 args
->desc_len
= strlen(desc
);
147 drm_shim_ioctl_get_cap(int fd
, unsigned long request
, void *arg
)
149 struct drm_get_cap
*gc
= arg
;
151 switch (gc
->capability
) {
153 case DRM_CAP_SYNCOBJ
:
158 fprintf(stderr
, "DRM_IOCTL_GET_CAP: unhandled 0x%x\n",
159 (int)gc
->capability
);
165 drm_shim_ioctl_gem_close(int fd
, unsigned long request
, void *arg
)
167 struct shim_fd
*shim_fd
= drm_shim_fd_lookup(fd
);
168 struct drm_gem_close
*c
= arg
;
173 mtx_lock(&handle_lock
);
174 struct hash_entry
*entry
=
175 _mesa_hash_table_search(shim_fd
->handles
, (void *)(uintptr_t)c
->handle
);
177 mtx_unlock(&handle_lock
);
181 struct shim_bo
*bo
= entry
->data
;
182 _mesa_hash_table_remove(shim_fd
->handles
, entry
);
184 mtx_unlock(&handle_lock
);
189 drm_shim_ioctl_stub(int fd
, unsigned long request
, void *arg
)
194 ioctl_fn_t core_ioctls
[] = {
195 [_IOC_NR(DRM_IOCTL_VERSION
)] = drm_shim_ioctl_version
,
196 [_IOC_NR(DRM_IOCTL_GET_CAP
)] = drm_shim_ioctl_get_cap
,
197 [_IOC_NR(DRM_IOCTL_GEM_CLOSE
)] = drm_shim_ioctl_gem_close
,
198 [_IOC_NR(DRM_IOCTL_SYNCOBJ_CREATE
)] = drm_shim_ioctl_stub
,
199 [_IOC_NR(DRM_IOCTL_SYNCOBJ_DESTROY
)] = drm_shim_ioctl_stub
,
200 [_IOC_NR(DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
)] = drm_shim_ioctl_stub
,
201 [_IOC_NR(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE
)] = drm_shim_ioctl_stub
,
205 * Implements the GEM core ioctls, and calls into driver-specific ioctls.
208 drm_shim_ioctl(int fd
, unsigned long request
, void *arg
)
210 int type
= _IOC_TYPE(request
);
211 int nr
= _IOC_NR(request
);
213 assert(type
== DRM_IOCTL_BASE
);
215 if (nr
>= DRM_COMMAND_BASE
&& nr
< DRM_COMMAND_END
) {
216 int driver_nr
= nr
- DRM_COMMAND_BASE
;
218 if (driver_nr
< shim_device
.driver_ioctl_count
&&
219 shim_device
.driver_ioctls
[driver_nr
]) {
220 return shim_device
.driver_ioctls
[driver_nr
](fd
, request
, arg
);
223 if (nr
< ARRAY_SIZE(core_ioctls
) && core_ioctls
[nr
]) {
224 return core_ioctls
[nr
](fd
, request
, arg
);
228 if (nr
>= DRM_COMMAND_BASE
&& nr
< DRM_COMMAND_END
) {
230 "DRM_SHIM: unhandled driver DRM ioctl %d (0x%08lx)\n",
231 nr
- DRM_COMMAND_BASE
, request
);
234 "DRM_SHIM: unhandled core DRM ioctl 0x%X (0x%08lx)\n",
242 drm_shim_bo_init(struct shim_bo
*bo
, size_t size
)
245 bo
->fd
= memfd_create("shim bo", MFD_CLOEXEC
);
247 fprintf(stderr
, "Failed to create BO: %s\n", strerror(errno
));
251 if (ftruncate(bo
->fd
, size
) == -1) {
252 fprintf(stderr
, "Failed to size BO: %s\n", strerror(errno
));
258 drm_shim_bo_lookup(struct shim_fd
*shim_fd
, int handle
)
263 mtx_lock(&handle_lock
);
264 struct hash_entry
*entry
=
265 _mesa_hash_table_search(shim_fd
->handles
, (void *)(uintptr_t)handle
);
266 struct shim_bo
*bo
= entry
? entry
->data
: NULL
;
267 mtx_unlock(&handle_lock
);
270 p_atomic_inc(&bo
->refcount
);
276 drm_shim_bo_get(struct shim_bo
*bo
)
278 p_atomic_inc(&bo
->refcount
);
282 drm_shim_bo_put(struct shim_bo
*bo
)
284 if (p_atomic_dec_return(&bo
->refcount
) == 0)
287 if (shim_device
.driver_bo_free
)
288 shim_device
.driver_bo_free(bo
);
294 drm_shim_bo_get_handle(struct shim_fd
*shim_fd
, struct shim_bo
*bo
)
296 /* We should probably have some real datastructure for finding the free
299 mtx_lock(&handle_lock
);
300 for (int new_handle
= 1; ; new_handle
++) {
301 void *key
= (void *)(uintptr_t)new_handle
;
302 if (!_mesa_hash_table_search(shim_fd
->handles
, key
)) {
304 _mesa_hash_table_insert(shim_fd
->handles
, key
, bo
);
305 mtx_unlock(&handle_lock
);
309 mtx_unlock(&handle_lock
);
314 /* Creates an mmap offset for the BO in the DRM fd.
316 * XXX: We should be maintaining a u_mm allocator where the mmap offsets
317 * allocate the size of the BO and it can be used to look the BO back up.
318 * Instead, we just stuff the shim's pointer as the return value, and treat
319 * the incoming mmap offset on the DRM fd as a BO pointer. This doesn't work
320 * if someone tries to map a subset of the BO, but it's enough to get V3D
324 drm_shim_bo_get_mmap_offset(struct shim_fd
*shim_fd
, struct shim_bo
*bo
)
326 return (uintptr_t)bo
;
329 /* For mmap() on the DRM fd, look up the BO from the "offset" and map the BO's
333 drm_shim_mmap(struct shim_fd
*shim_fd
, size_t length
, int prot
, int flags
,
334 int fd
, off_t offset
)
336 struct shim_bo
*bo
= (void *)(uintptr_t)offset
;
338 return mmap(NULL
, length
, prot
, flags
, bo
->fd
, 0);