xref: /aosp_15_r20/external/mesa3d/src/drm-shim/device.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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 #define SHIM_MEM_SIZE (4ull * 1024 * 1024 * 1024)
46 
47 #ifndef HAVE_MEMFD_CREATE
48 #include <sys/syscall.h>
49 
50 static inline int
memfd_create(const char * name,unsigned int flags)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 long shim_page_size;
61 
62 static uint32_t
uint_key_hash(const void * key)63 uint_key_hash(const void *key)
64 {
65    return (uintptr_t)key;
66 }
67 
68 static bool
uint_key_compare(const void * a,const void * b)69 uint_key_compare(const void *a, const void *b)
70 {
71    return a == b;
72 }
73 
74 /**
75  * Called when the first libc shim is called, to initialize GEM simulation
76  * state (other than the shims themselves).
77  */
78 void
drm_shim_device_init(void)79 drm_shim_device_init(void)
80 {
81    shim_device.fd_map = _mesa_hash_table_create(NULL,
82                                                 uint_key_hash,
83                                                 uint_key_compare);
84 
85    shim_device.offset_map = _mesa_hash_table_u64_create(NULL);
86 
87    mtx_init(&shim_device.mem_lock, mtx_plain);
88 
89    shim_device.mem_fd = memfd_create("shim mem", MFD_CLOEXEC);
90    assert(shim_device.mem_fd != -1);
91 
92    ASSERTED int ret = ftruncate(shim_device.mem_fd, SHIM_MEM_SIZE);
93    assert(ret == 0);
94 
95    /* The man page for mmap() says
96     *
97     *    offset must be a multiple of the page size as returned by
98     *    sysconf(_SC_PAGE_SIZE).
99     *
100     * Depending on the configuration of the kernel, this may not be 4096. Get
101     * this page size once and use it as the page size throughout, ensuring that
102     * are offsets are page-size aligned as required. Otherwise, mmap will fail
103     * with EINVAL.
104     */
105 
106    shim_page_size = sysconf(_SC_PAGE_SIZE);
107 
108    util_vma_heap_init(&shim_device.mem_heap, shim_page_size,
109                       SHIM_MEM_SIZE - shim_page_size);
110 
111    drm_shim_driver_init();
112 }
113 
114 static struct shim_fd *
drm_shim_file_create(int fd)115 drm_shim_file_create(int fd)
116 {
117    struct shim_fd *shim_fd = calloc(1, sizeof(*shim_fd));
118 
119    shim_fd->fd = fd;
120    p_atomic_set(&shim_fd->refcount, 1);
121    mtx_init(&shim_fd->handle_lock, mtx_plain);
122    shim_fd->handles = _mesa_hash_table_create(NULL,
123                                               uint_key_hash,
124                                               uint_key_compare);
125 
126    return shim_fd;
127 }
128 
129 /**
130  * Called when the libc shims have interposed an open or dup of our simulated
131  * DRM device.
132  */
drm_shim_fd_register(int fd,struct shim_fd * shim_fd)133 void drm_shim_fd_register(int fd, struct shim_fd *shim_fd)
134 {
135    if (!shim_fd)
136       shim_fd = drm_shim_file_create(fd);
137    else
138       p_atomic_inc(&shim_fd->refcount);
139 
140    _mesa_hash_table_insert(shim_device.fd_map, (void *)(uintptr_t)(fd + 1), shim_fd);
141 }
142 
handle_delete_fxn(struct hash_entry * entry)143 static void handle_delete_fxn(struct hash_entry *entry)
144 {
145    drm_shim_bo_put(entry->data);
146 }
147 
drm_shim_fd_unregister(int fd)148 void drm_shim_fd_unregister(int fd)
149 {
150    if (fd == -1)
151       return;
152 
153    struct hash_entry *entry =
154          _mesa_hash_table_search(shim_device.fd_map, (void *)(uintptr_t)(fd + 1));
155    if (!entry)
156       return;
157    struct shim_fd *shim_fd = entry->data;
158    _mesa_hash_table_remove(shim_device.fd_map, entry);
159 
160    if (!p_atomic_dec_zero(&shim_fd->refcount))
161       return;
162 
163    _mesa_hash_table_destroy(shim_fd->handles, handle_delete_fxn);
164    free(shim_fd);
165 }
166 
167 struct shim_fd *
drm_shim_fd_lookup(int fd)168 drm_shim_fd_lookup(int fd)
169 {
170    if (fd == -1)
171       return NULL;
172 
173    struct hash_entry *entry =
174       _mesa_hash_table_search(shim_device.fd_map, (void *)(uintptr_t)(fd + 1));
175 
176    if (!entry)
177       return NULL;
178    return entry->data;
179 }
180 
181 /* ioctl used by drmGetVersion() */
182 static int
drm_shim_ioctl_version(int fd,unsigned long request,void * arg)183 drm_shim_ioctl_version(int fd, unsigned long request, void *arg)
184 {
185    struct drm_version *args = arg;
186    const char *date = "20190320";
187    const char *desc = "shim";
188 
189    args->version_major = shim_device.version_major;
190    args->version_minor = shim_device.version_minor;
191    args->version_patchlevel = shim_device.version_patchlevel;
192 
193    if (args->name)
194       strncpy(args->name, shim_device.driver_name, args->name_len);
195    if (args->date)
196       strncpy(args->date, date, args->date_len);
197    if (args->desc)
198       strncpy(args->desc, desc, args->desc_len);
199    args->name_len = strlen(shim_device.driver_name);
200    args->date_len = strlen(date);
201    args->desc_len = strlen(desc);
202 
203    return 0;
204 }
205 
206 static int
drm_shim_ioctl_get_unique(int fd,unsigned long request,void * arg)207 drm_shim_ioctl_get_unique(int fd, unsigned long request, void *arg)
208 {
209    struct drm_unique *gu = arg;
210 
211    if (gu->unique && shim_device.unique)
212       strncpy(gu->unique, shim_device.unique, gu->unique_len);
213    gu->unique_len = shim_device.unique ? strlen(shim_device.unique) : 0;
214 
215    return 0;
216 }
217 
218 static int
drm_shim_ioctl_get_cap(int fd,unsigned long request,void * arg)219 drm_shim_ioctl_get_cap(int fd, unsigned long request, void *arg)
220 {
221    struct drm_get_cap *gc = arg;
222 
223    switch (gc->capability) {
224    case DRM_CAP_PRIME:
225    case DRM_CAP_SYNCOBJ:
226    case DRM_CAP_SYNCOBJ_TIMELINE:
227       gc->value = 1;
228       return 0;
229 
230    default:
231       fprintf(stderr, "DRM_IOCTL_GET_CAP: unhandled 0x%x\n",
232               (int)gc->capability);
233       return -1;
234    }
235 }
236 
237 static int
drm_shim_ioctl_gem_close(int fd,unsigned long request,void * arg)238 drm_shim_ioctl_gem_close(int fd, unsigned long request, void *arg)
239 {
240    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
241    struct drm_gem_close *c = arg;
242 
243    if (!c->handle)
244       return 0;
245 
246    mtx_lock(&shim_fd->handle_lock);
247    struct hash_entry *entry =
248       _mesa_hash_table_search(shim_fd->handles, (void *)(uintptr_t)c->handle);
249    if (!entry) {
250       mtx_unlock(&shim_fd->handle_lock);
251       return -EINVAL;
252    }
253 
254    struct shim_bo *bo = entry->data;
255    _mesa_hash_table_remove(shim_fd->handles, entry);
256    drm_shim_bo_put(bo);
257    mtx_unlock(&shim_fd->handle_lock);
258    return 0;
259 }
260 
261 static int
drm_shim_ioctl_syncobj_create(int fd,unsigned long request,void * arg)262 drm_shim_ioctl_syncobj_create(int fd, unsigned long request, void *arg)
263 {
264    struct drm_syncobj_create *create = arg;
265 
266    create->handle = 1; /* 0 is invalid */
267 
268    return 0;
269 }
270 
271 static int
drm_shim_ioctl_stub(int fd,unsigned long request,void * arg)272 drm_shim_ioctl_stub(int fd, unsigned long request, void *arg)
273 {
274    return 0;
275 }
276 
277 ioctl_fn_t core_ioctls[] = {
278    [_IOC_NR(DRM_IOCTL_VERSION)] = drm_shim_ioctl_version,
279    [_IOC_NR(DRM_IOCTL_GET_UNIQUE)] = drm_shim_ioctl_get_unique,
280    [_IOC_NR(DRM_IOCTL_GET_CAP)] = drm_shim_ioctl_get_cap,
281    [_IOC_NR(DRM_IOCTL_GEM_CLOSE)] = drm_shim_ioctl_gem_close,
282    [_IOC_NR(DRM_IOCTL_SYNCOBJ_CREATE)] = drm_shim_ioctl_syncobj_create,
283    [_IOC_NR(DRM_IOCTL_SYNCOBJ_DESTROY)] = drm_shim_ioctl_stub,
284    [_IOC_NR(DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD)] = drm_shim_ioctl_stub,
285    [_IOC_NR(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE)] = drm_shim_ioctl_stub,
286    [_IOC_NR(DRM_IOCTL_SYNCOBJ_WAIT)] = drm_shim_ioctl_stub,
287    [_IOC_NR(DRM_IOCTL_SYNCOBJ_TRANSFER)] = drm_shim_ioctl_stub,
288    [_IOC_NR(DRM_IOCTL_SYNCOBJ_RESET)] = drm_shim_ioctl_stub,
289    [_IOC_NR(DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL)] = drm_shim_ioctl_stub,
290    [_IOC_NR(DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT)] = drm_shim_ioctl_stub,
291    [_IOC_NR(DRM_IOCTL_SYNCOBJ_QUERY)] = drm_shim_ioctl_stub,
292 };
293 
294 /**
295  * Implements the GEM core ioctls, and calls into driver-specific ioctls.
296  */
297 int
drm_shim_ioctl(int fd,unsigned long request,void * arg)298 drm_shim_ioctl(int fd, unsigned long request, void *arg)
299 {
300    ASSERTED int type = _IOC_TYPE(request);
301    int nr = _IOC_NR(request);
302 
303    assert(type == DRM_IOCTL_BASE);
304 
305    if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
306       int driver_nr = nr - DRM_COMMAND_BASE;
307 
308       if (driver_nr < shim_device.driver_ioctl_count &&
309           shim_device.driver_ioctls[driver_nr]) {
310          return shim_device.driver_ioctls[driver_nr](fd, request, arg);
311       }
312    } else {
313       if (nr < ARRAY_SIZE(core_ioctls) && core_ioctls[nr]) {
314          return core_ioctls[nr](fd, request, arg);
315       }
316    }
317 
318    if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
319       fprintf(stderr,
320               "DRM_SHIM: unhandled driver DRM ioctl %d (0x%08lx)\n",
321               nr - DRM_COMMAND_BASE, request);
322    } else {
323       fprintf(stderr,
324               "DRM_SHIM: unhandled core DRM ioctl 0x%X (0x%08lx)\n",
325               nr, request);
326    }
327 
328    return -EINVAL;
329 }
330 
331 int
drm_shim_bo_init(struct shim_bo * bo,size_t size)332 drm_shim_bo_init(struct shim_bo *bo, size_t size)
333 {
334 
335    mtx_lock(&shim_device.mem_lock);
336    bo->mem_addr = util_vma_heap_alloc(&shim_device.mem_heap, size, shim_page_size);
337    mtx_unlock(&shim_device.mem_lock);
338 
339    if (!bo->mem_addr)
340       return -ENOMEM;
341 
342    bo->size = size;
343 
344    return 0;
345 }
346 
347 struct shim_bo *
drm_shim_bo_lookup(struct shim_fd * shim_fd,int handle)348 drm_shim_bo_lookup(struct shim_fd *shim_fd, int handle)
349 {
350    if (!handle)
351       return NULL;
352 
353    mtx_lock(&shim_fd->handle_lock);
354    struct hash_entry *entry =
355       _mesa_hash_table_search(shim_fd->handles, (void *)(uintptr_t)handle);
356    struct shim_bo *bo = entry ? entry->data : NULL;
357    mtx_unlock(&shim_fd->handle_lock);
358 
359    if (bo)
360       p_atomic_inc(&bo->refcount);
361 
362    return bo;
363 }
364 
365 void
drm_shim_bo_get(struct shim_bo * bo)366 drm_shim_bo_get(struct shim_bo *bo)
367 {
368    p_atomic_inc(&bo->refcount);
369 }
370 
371 void
drm_shim_bo_put(struct shim_bo * bo)372 drm_shim_bo_put(struct shim_bo *bo)
373 {
374    if (p_atomic_dec_return(&bo->refcount) == 0)
375       return;
376 
377    if (shim_device.driver_bo_free)
378       shim_device.driver_bo_free(bo);
379 
380    mtx_lock(&shim_device.mem_lock);
381    util_vma_heap_free(&shim_device.mem_heap, bo->mem_addr, bo->size);
382    mtx_unlock(&shim_device.mem_lock);
383    free(bo);
384 }
385 
386 int
drm_shim_bo_get_handle(struct shim_fd * shim_fd,struct shim_bo * bo)387 drm_shim_bo_get_handle(struct shim_fd *shim_fd, struct shim_bo *bo)
388 {
389    /* We should probably have some real datastructure for finding the free
390     * number.
391     */
392    mtx_lock(&shim_fd->handle_lock);
393    for (int new_handle = 1; ; new_handle++) {
394       void *key = (void *)(uintptr_t)new_handle;
395       if (!_mesa_hash_table_search(shim_fd->handles, key)) {
396          drm_shim_bo_get(bo);
397          _mesa_hash_table_insert(shim_fd->handles, key, bo);
398          mtx_unlock(&shim_fd->handle_lock);
399          return new_handle;
400       }
401    }
402    mtx_unlock(&shim_fd->handle_lock);
403 
404    return 0;
405 }
406 
407 /* Creates an mmap offset for the BO in the DRM fd.
408  */
409 uint64_t
drm_shim_bo_get_mmap_offset(struct shim_fd * shim_fd,struct shim_bo * bo)410 drm_shim_bo_get_mmap_offset(struct shim_fd *shim_fd, struct shim_bo *bo)
411 {
412    mtx_lock(&shim_device.mem_lock);
413    _mesa_hash_table_u64_insert(shim_device.offset_map, bo->mem_addr, bo);
414    mtx_unlock(&shim_device.mem_lock);
415 
416    /* reuse the buffer address as the mmap offset: */
417    return bo->mem_addr;
418 }
419 
420 void
drm_shim_init_iomem_region(off64_t offset,size_t size,void * (* mmap_handler)(size_t,int,int,off64_t))421 drm_shim_init_iomem_region(off64_t offset, size_t size,
422                            void *(*mmap_handler)(size_t, int, int, off64_t))
423 {
424    shim_device.iomem_region.mmap = mmap_handler;
425    shim_device.iomem_region.start = offset;
426    shim_device.iomem_region.size = size;
427 }
428 
429 /* For mmap() on the DRM fd, look up the BO from the "offset" and map the BO's
430  * fd.
431  */
432 void *
drm_shim_mmap(struct shim_fd * shim_fd,size_t length,int prot,int flags,int fd,off64_t offset)433 drm_shim_mmap(struct shim_fd *shim_fd, size_t length, int prot, int flags,
434               int fd, off64_t offset)
435 {
436    if (shim_device.iomem_region.mmap &&
437        offset >= shim_device.iomem_region.start &&
438        offset + length <= shim_device.iomem_region.start + shim_device.iomem_region.size) {
439       return shim_device.iomem_region.mmap(length, prot, flags, offset);
440    }
441 
442    mtx_lock(&shim_device.mem_lock);
443    struct shim_bo *bo = _mesa_hash_table_u64_search(shim_device.offset_map, offset);
444    mtx_unlock(&shim_device.mem_lock);
445 
446    if (!bo)
447       return MAP_FAILED;
448 
449    if (length > bo->size)
450       return MAP_FAILED;
451 
452    /* The offset we pass to mmap must be aligned to the page size */
453    assert((bo->mem_addr & (shim_page_size - 1)) == 0);
454 
455    return mmap(NULL, length, prot, flags, shim_device.mem_fd, bo->mem_addr);
456 }
457