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";
133 args
->version_major
= shim_device
.version_major
;
134 args
->version_minor
= shim_device
.version_minor
;
135 args
->version_patchlevel
= shim_device
.version_patchlevel
;
138 strncpy(args
->name
, shim_device
.driver_name
, args
->name_len
);
140 strncpy(args
->date
, date
, args
->date_len
);
142 strncpy(args
->desc
, desc
, args
->desc_len
);
143 args
->name_len
= strlen(shim_device
.driver_name
);
144 args
->date_len
= strlen(date
);
145 args
->desc_len
= strlen(desc
);
151 drm_shim_ioctl_get_cap(int fd
, unsigned long request
, void *arg
)
153 struct drm_get_cap
*gc
= arg
;
155 switch (gc
->capability
) {
157 case DRM_CAP_SYNCOBJ
:
162 fprintf(stderr
, "DRM_IOCTL_GET_CAP: unhandled 0x%x\n",
163 (int)gc
->capability
);
169 drm_shim_ioctl_gem_close(int fd
, unsigned long request
, void *arg
)
171 struct shim_fd
*shim_fd
= drm_shim_fd_lookup(fd
);
172 struct drm_gem_close
*c
= arg
;
177 mtx_lock(&handle_lock
);
178 struct hash_entry
*entry
=
179 _mesa_hash_table_search(shim_fd
->handles
, (void *)(uintptr_t)c
->handle
);
181 mtx_unlock(&handle_lock
);
185 struct shim_bo
*bo
= entry
->data
;
186 _mesa_hash_table_remove(shim_fd
->handles
, entry
);
188 mtx_unlock(&handle_lock
);
193 drm_shim_ioctl_stub(int fd
, unsigned long request
, void *arg
)
198 ioctl_fn_t core_ioctls
[] = {
199 [_IOC_NR(DRM_IOCTL_VERSION
)] = drm_shim_ioctl_version
,
200 [_IOC_NR(DRM_IOCTL_GET_CAP
)] = drm_shim_ioctl_get_cap
,
201 [_IOC_NR(DRM_IOCTL_GEM_CLOSE
)] = drm_shim_ioctl_gem_close
,
202 [_IOC_NR(DRM_IOCTL_SYNCOBJ_CREATE
)] = drm_shim_ioctl_stub
,
203 [_IOC_NR(DRM_IOCTL_SYNCOBJ_DESTROY
)] = drm_shim_ioctl_stub
,
204 [_IOC_NR(DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
)] = drm_shim_ioctl_stub
,
205 [_IOC_NR(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE
)] = drm_shim_ioctl_stub
,
209 * Implements the GEM core ioctls, and calls into driver-specific ioctls.
212 drm_shim_ioctl(int fd
, unsigned long request
, void *arg
)
214 int type
= _IOC_TYPE(request
);
215 int nr
= _IOC_NR(request
);
217 assert(type
== DRM_IOCTL_BASE
);
219 if (nr
>= DRM_COMMAND_BASE
&& nr
< DRM_COMMAND_END
) {
220 int driver_nr
= nr
- DRM_COMMAND_BASE
;
222 if (driver_nr
< shim_device
.driver_ioctl_count
&&
223 shim_device
.driver_ioctls
[driver_nr
]) {
224 return shim_device
.driver_ioctls
[driver_nr
](fd
, request
, arg
);
227 if (nr
< ARRAY_SIZE(core_ioctls
) && core_ioctls
[nr
]) {
228 return core_ioctls
[nr
](fd
, request
, arg
);
232 if (nr
>= DRM_COMMAND_BASE
&& nr
< DRM_COMMAND_END
) {
234 "DRM_SHIM: unhandled driver DRM ioctl %d (0x%08lx)\n",
235 nr
- DRM_COMMAND_BASE
, request
);
238 "DRM_SHIM: unhandled core DRM ioctl 0x%X (0x%08lx)\n",
246 drm_shim_bo_init(struct shim_bo
*bo
, size_t size
)
249 bo
->fd
= memfd_create("shim bo", MFD_CLOEXEC
);
251 fprintf(stderr
, "Failed to create BO: %s\n", strerror(errno
));
255 if (ftruncate(bo
->fd
, size
) == -1) {
256 fprintf(stderr
, "Failed to size BO: %s\n", strerror(errno
));
262 drm_shim_bo_lookup(struct shim_fd
*shim_fd
, int handle
)
267 mtx_lock(&handle_lock
);
268 struct hash_entry
*entry
=
269 _mesa_hash_table_search(shim_fd
->handles
, (void *)(uintptr_t)handle
);
270 struct shim_bo
*bo
= entry
? entry
->data
: NULL
;
271 mtx_unlock(&handle_lock
);
274 p_atomic_inc(&bo
->refcount
);
280 drm_shim_bo_get(struct shim_bo
*bo
)
282 p_atomic_inc(&bo
->refcount
);
286 drm_shim_bo_put(struct shim_bo
*bo
)
288 if (p_atomic_dec_return(&bo
->refcount
) == 0)
291 if (shim_device
.driver_bo_free
)
292 shim_device
.driver_bo_free(bo
);
298 drm_shim_bo_get_handle(struct shim_fd
*shim_fd
, struct shim_bo
*bo
)
300 /* We should probably have some real datastructure for finding the free
303 mtx_lock(&handle_lock
);
304 for (int new_handle
= 1; ; new_handle
++) {
305 void *key
= (void *)(uintptr_t)new_handle
;
306 if (!_mesa_hash_table_search(shim_fd
->handles
, key
)) {
308 _mesa_hash_table_insert(shim_fd
->handles
, key
, bo
);
309 mtx_unlock(&handle_lock
);
313 mtx_unlock(&handle_lock
);
318 /* Creates an mmap offset for the BO in the DRM fd.
320 * XXX: We should be maintaining a u_mm allocator where the mmap offsets
321 * allocate the size of the BO and it can be used to look the BO back up.
322 * Instead, we just stuff the shim's pointer as the return value, and treat
323 * the incoming mmap offset on the DRM fd as a BO pointer. This doesn't work
324 * if someone tries to map a subset of the BO, but it's enough to get V3D
328 drm_shim_bo_get_mmap_offset(struct shim_fd
*shim_fd
, struct shim_bo
*bo
)
330 return (uintptr_t)bo
;
333 /* For mmap() on the DRM fd, look up the BO from the "offset" and map the BO's
337 drm_shim_mmap(struct shim_fd
*shim_fd
, size_t length
, int prot
, int flags
,
338 int fd
, off_t offset
)
340 struct shim_bo
*bo
= (void *)(uintptr_t)offset
;
342 return mmap(NULL
, length
, prot
, flags
, bo
->fd
, 0);