f3f0e53347b012614b279067f295b86c22c22256
[mesa.git] / src / drm-shim / device.c
1 /*
2 * Copyright © 2018 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
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 /** @file
25 *
26 * Implements core GEM support (particularly ioctls) underneath the libc ioctl
27 * wrappers, and calls into the driver-specific code as necessary.
28 */
29
30 #include <c11/threads.h>
31 #include <errno.h>
32 #include <linux/memfd.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/ioctl.h>
38 #include <sys/mman.h>
39 #include <unistd.h>
40 #include "drm-uapi/drm.h"
41 #include "drm_shim.h"
42 #include "util/hash_table.h"
43 #include "util/u_atomic.h"
44
45 static mtx_t handle_lock = _MTX_INITIALIZER_NP;
46
47 #ifndef HAVE_MEMFD_CREATE
48 #include <sys/syscall.h>
49
50 static inline int
51 memfd_create(const char *name, unsigned int flags)
52 {
53 return syscall(SYS_memfd_create, name, flags);
54 }
55 #endif
56
57 /* Global state for the shim shared between libc, core, and driver. */
58 struct shim_device shim_device;
59
60 static uint32_t
61 uint_key_hash(const void *key)
62 {
63 return (uintptr_t)key;
64 }
65
66 static bool
67 uint_key_compare(const void *a, const void *b)
68 {
69 return a == b;
70 }
71
72 /**
73 * Called when the first libc shim is called, to initialize GEM simulation
74 * state (other than the shims themselves).
75 */
76 void
77 drm_shim_device_init(void)
78 {
79 shim_device.fd_map = _mesa_hash_table_create(NULL,
80 uint_key_hash,
81 uint_key_compare);
82
83 drm_shim_driver_init();
84 }
85
86 static struct shim_fd *
87 drm_shim_file_create(int fd)
88 {
89 struct shim_fd *shim_fd = calloc(1, sizeof(*shim_fd));
90
91 shim_fd->fd = fd;
92 shim_fd->handles = _mesa_hash_table_create(NULL,
93 uint_key_hash,
94 uint_key_compare);
95
96 return shim_fd;
97 }
98
99 /**
100 * Called when the libc shims have interposed an open or dup of our simulated
101 * DRM device.
102 */
103 void drm_shim_fd_register(int fd, struct shim_fd *shim_fd)
104 {
105 if (!shim_fd)
106 shim_fd = drm_shim_file_create(fd);
107
108 _mesa_hash_table_insert(shim_device.fd_map, (void *)(uintptr_t)(fd + 1), shim_fd);
109 }
110
111 struct shim_fd *
112 drm_shim_fd_lookup(int fd)
113 {
114 if (fd == -1)
115 return NULL;
116
117 struct hash_entry *entry =
118 _mesa_hash_table_search(shim_device.fd_map, (void *)(uintptr_t)(fd + 1));
119
120 if (!entry)
121 return NULL;
122 return entry->data;
123 }
124
125 /* ioctl used by drmGetVersion() */
126 static int
127 drm_shim_ioctl_version(int fd, unsigned long request, void *arg)
128 {
129 struct drm_version *args = arg;
130 const char *date = "20190320";
131 const char *desc = "shim";
132
133 args->version_major = shim_device.version_major;
134 args->version_minor = shim_device.version_minor;
135 args->version_patchlevel = shim_device.version_patchlevel;
136
137 if (args->name)
138 strncpy(args->name, shim_device.driver_name, args->name_len);
139 if (args->date)
140 strncpy(args->date, date, args->date_len);
141 if (args->desc)
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);
146
147 return 0;
148 }
149
150 static int
151 drm_shim_ioctl_get_cap(int fd, unsigned long request, void *arg)
152 {
153 struct drm_get_cap *gc = arg;
154
155 switch (gc->capability) {
156 case DRM_CAP_PRIME:
157 case DRM_CAP_SYNCOBJ:
158 gc->value = 1;
159 return 0;
160
161 default:
162 fprintf(stderr, "DRM_IOCTL_GET_CAP: unhandled 0x%x\n",
163 (int)gc->capability);
164 return -1;
165 }
166 }
167
168 static int
169 drm_shim_ioctl_gem_close(int fd, unsigned long request, void *arg)
170 {
171 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
172 struct drm_gem_close *c = arg;
173
174 if (!c->handle)
175 return 0;
176
177 mtx_lock(&handle_lock);
178 struct hash_entry *entry =
179 _mesa_hash_table_search(shim_fd->handles, (void *)(uintptr_t)c->handle);
180 if (!entry) {
181 mtx_unlock(&handle_lock);
182 return -EINVAL;
183 }
184
185 struct shim_bo *bo = entry->data;
186 _mesa_hash_table_remove(shim_fd->handles, entry);
187 drm_shim_bo_put(bo);
188 mtx_unlock(&handle_lock);
189 return 0;
190 }
191
192 static int
193 drm_shim_ioctl_syncobj_create(int fd, unsigned long request, void *arg)
194 {
195 struct drm_syncobj_create *create = arg;
196
197 create->handle = 1; /* 0 is invalid */
198
199 return 0;
200 }
201
202 static int
203 drm_shim_ioctl_stub(int fd, unsigned long request, void *arg)
204 {
205 return 0;
206 }
207
208 ioctl_fn_t core_ioctls[] = {
209 [_IOC_NR(DRM_IOCTL_VERSION)] = drm_shim_ioctl_version,
210 [_IOC_NR(DRM_IOCTL_GET_CAP)] = drm_shim_ioctl_get_cap,
211 [_IOC_NR(DRM_IOCTL_GEM_CLOSE)] = drm_shim_ioctl_gem_close,
212 [_IOC_NR(DRM_IOCTL_SYNCOBJ_CREATE)] = drm_shim_ioctl_syncobj_create,
213 [_IOC_NR(DRM_IOCTL_SYNCOBJ_DESTROY)] = drm_shim_ioctl_stub,
214 [_IOC_NR(DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD)] = drm_shim_ioctl_stub,
215 [_IOC_NR(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE)] = drm_shim_ioctl_stub,
216 [_IOC_NR(DRM_IOCTL_SYNCOBJ_WAIT)] = drm_shim_ioctl_stub,
217 };
218
219 /**
220 * Implements the GEM core ioctls, and calls into driver-specific ioctls.
221 */
222 int
223 drm_shim_ioctl(int fd, unsigned long request, void *arg)
224 {
225 int type = _IOC_TYPE(request);
226 int nr = _IOC_NR(request);
227
228 assert(type == DRM_IOCTL_BASE);
229
230 if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
231 int driver_nr = nr - DRM_COMMAND_BASE;
232
233 if (driver_nr < shim_device.driver_ioctl_count &&
234 shim_device.driver_ioctls[driver_nr]) {
235 return shim_device.driver_ioctls[driver_nr](fd, request, arg);
236 }
237 } else {
238 if (nr < ARRAY_SIZE(core_ioctls) && core_ioctls[nr]) {
239 return core_ioctls[nr](fd, request, arg);
240 }
241 }
242
243 if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
244 fprintf(stderr,
245 "DRM_SHIM: unhandled driver DRM ioctl %d (0x%08lx)\n",
246 nr - DRM_COMMAND_BASE, request);
247 } else {
248 fprintf(stderr,
249 "DRM_SHIM: unhandled core DRM ioctl 0x%X (0x%08lx)\n",
250 nr, request);
251 }
252
253 abort();
254 }
255
256 void
257 drm_shim_bo_init(struct shim_bo *bo, size_t size)
258 {
259 bo->size = size;
260 bo->fd = memfd_create("shim bo", MFD_CLOEXEC);
261 if (bo->fd == -1) {
262 fprintf(stderr, "Failed to create BO: %s\n", strerror(errno));
263 abort();
264 }
265
266 if (ftruncate(bo->fd, size) == -1) {
267 fprintf(stderr, "Failed to size BO: %s\n", strerror(errno));
268 abort();
269 }
270 }
271
272 struct shim_bo *
273 drm_shim_bo_lookup(struct shim_fd *shim_fd, int handle)
274 {
275 if (!handle)
276 return NULL;
277
278 mtx_lock(&handle_lock);
279 struct hash_entry *entry =
280 _mesa_hash_table_search(shim_fd->handles, (void *)(uintptr_t)handle);
281 struct shim_bo *bo = entry ? entry->data : NULL;
282 mtx_unlock(&handle_lock);
283
284 if (bo)
285 p_atomic_inc(&bo->refcount);
286
287 return bo;
288 }
289
290 void
291 drm_shim_bo_get(struct shim_bo *bo)
292 {
293 p_atomic_inc(&bo->refcount);
294 }
295
296 void
297 drm_shim_bo_put(struct shim_bo *bo)
298 {
299 if (p_atomic_dec_return(&bo->refcount) == 0)
300 return;
301
302 if (shim_device.driver_bo_free)
303 shim_device.driver_bo_free(bo);
304 close(bo->fd);
305 free(bo);
306 }
307
308 int
309 drm_shim_bo_get_handle(struct shim_fd *shim_fd, struct shim_bo *bo)
310 {
311 /* We should probably have some real datastructure for finding the free
312 * number.
313 */
314 mtx_lock(&handle_lock);
315 for (int new_handle = 1; ; new_handle++) {
316 void *key = (void *)(uintptr_t)new_handle;
317 if (!_mesa_hash_table_search(shim_fd->handles, key)) {
318 drm_shim_bo_get(bo);
319 _mesa_hash_table_insert(shim_fd->handles, key, bo);
320 mtx_unlock(&handle_lock);
321 return new_handle;
322 }
323 }
324 mtx_unlock(&handle_lock);
325
326 return 0;
327 }
328
329 /* Creates an mmap offset for the BO in the DRM fd.
330 *
331 * XXX: We should be maintaining a u_mm allocator where the mmap offsets
332 * allocate the size of the BO and it can be used to look the BO back up.
333 * Instead, we just stuff the shim's pointer as the return value, and treat
334 * the incoming mmap offset on the DRM fd as a BO pointer. This doesn't work
335 * if someone tries to map a subset of the BO, but it's enough to get V3D
336 * working for now.
337 */
338 uint64_t
339 drm_shim_bo_get_mmap_offset(struct shim_fd *shim_fd, struct shim_bo *bo)
340 {
341 return (uintptr_t)bo;
342 }
343
344 /* For mmap() on the DRM fd, look up the BO from the "offset" and map the BO's
345 * fd.
346 */
347 void *
348 drm_shim_mmap(struct shim_fd *shim_fd, size_t length, int prot, int flags,
349 int fd, off_t offset)
350 {
351 struct shim_bo *bo = (void *)(uintptr_t)offset;
352
353 return mmap(NULL, length, prot, flags, bo->fd, 0);
354 }