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 static uint32_t
uint_key_hash(const void * key)61 uint_key_hash(const void *key)
62 {
63    return (uintptr_t)key;
64 }
65 
66 static bool
uint_key_compare(const void * a,const void * b)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
drm_shim_device_init(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    mtx_init(&shim_device.mem_lock, mtx_plain);
84 
85    shim_device.mem_fd = memfd_create("shim mem", MFD_CLOEXEC);
86    assert(shim_device.mem_fd != -1);
87 
88    ASSERTED int ret = ftruncate(shim_device.mem_fd, SHIM_MEM_SIZE);
89    assert(ret == 0);
90 
91    util_vma_heap_init(&shim_device.mem_heap, 4096, SHIM_MEM_SIZE - 4096);
92 
93    drm_shim_driver_init();
94 }
95 
96 static struct shim_fd *
drm_shim_file_create(int fd)97 drm_shim_file_create(int fd)
98 {
99    struct shim_fd *shim_fd = calloc(1, sizeof(*shim_fd));
100 
101    shim_fd->fd = fd;
102    mtx_init(&shim_fd->handle_lock, mtx_plain);
103    shim_fd->handles = _mesa_hash_table_create(NULL,
104                                               uint_key_hash,
105                                               uint_key_compare);
106 
107    return shim_fd;
108 }
109 
110 /**
111  * Called when the libc shims have interposed an open or dup of our simulated
112  * DRM device.
113  */
drm_shim_fd_register(int fd,struct shim_fd * shim_fd)114 void drm_shim_fd_register(int fd, struct shim_fd *shim_fd)
115 {
116    if (!shim_fd)
117       shim_fd = drm_shim_file_create(fd);
118 
119    _mesa_hash_table_insert(shim_device.fd_map, (void *)(uintptr_t)(fd + 1), shim_fd);
120 }
121 
122 struct shim_fd *
drm_shim_fd_lookup(int fd)123 drm_shim_fd_lookup(int fd)
124 {
125    if (fd == -1)
126       return NULL;
127 
128    struct hash_entry *entry =
129       _mesa_hash_table_search(shim_device.fd_map, (void *)(uintptr_t)(fd + 1));
130 
131    if (!entry)
132       return NULL;
133    return entry->data;
134 }
135 
136 /* ioctl used by drmGetVersion() */
137 static int
drm_shim_ioctl_version(int fd,unsigned long request,void * arg)138 drm_shim_ioctl_version(int fd, unsigned long request, void *arg)
139 {
140    struct drm_version *args = arg;
141    const char *date = "20190320";
142    const char *desc = "shim";
143 
144    args->version_major = shim_device.version_major;
145    args->version_minor = shim_device.version_minor;
146    args->version_patchlevel = shim_device.version_patchlevel;
147 
148    if (args->name)
149       strncpy(args->name, shim_device.driver_name, args->name_len);
150    if (args->date)
151       strncpy(args->date, date, args->date_len);
152    if (args->desc)
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);
157 
158    return 0;
159 }
160 
161 static int
drm_shim_ioctl_get_cap(int fd,unsigned long request,void * arg)162 drm_shim_ioctl_get_cap(int fd, unsigned long request, void *arg)
163 {
164    struct drm_get_cap *gc = arg;
165 
166    switch (gc->capability) {
167    case DRM_CAP_PRIME:
168    case DRM_CAP_SYNCOBJ:
169       gc->value = 1;
170       return 0;
171 
172    default:
173       fprintf(stderr, "DRM_IOCTL_GET_CAP: unhandled 0x%x\n",
174               (int)gc->capability);
175       return -1;
176    }
177 }
178 
179 static int
drm_shim_ioctl_gem_close(int fd,unsigned long request,void * arg)180 drm_shim_ioctl_gem_close(int fd, unsigned long request, void *arg)
181 {
182    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
183    struct drm_gem_close *c = arg;
184 
185    if (!c->handle)
186       return 0;
187 
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);
191    if (!entry) {
192       mtx_unlock(&shim_fd->handle_lock);
193       return -EINVAL;
194    }
195 
196    struct shim_bo *bo = entry->data;
197    _mesa_hash_table_remove(shim_fd->handles, entry);
198    drm_shim_bo_put(bo);
199    mtx_unlock(&shim_fd->handle_lock);
200    return 0;
201 }
202 
203 static int
drm_shim_ioctl_syncobj_create(int fd,unsigned long request,void * arg)204 drm_shim_ioctl_syncobj_create(int fd, unsigned long request, void *arg)
205 {
206    struct drm_syncobj_create *create = arg;
207 
208    create->handle = 1; /* 0 is invalid */
209 
210    return 0;
211 }
212 
213 static int
drm_shim_ioctl_stub(int fd,unsigned long request,void * arg)214 drm_shim_ioctl_stub(int fd, unsigned long request, void *arg)
215 {
216    return 0;
217 }
218 
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,
228 };
229 
230 /**
231  * Implements the GEM core ioctls, and calls into driver-specific ioctls.
232  */
233 int
drm_shim_ioctl(int fd,unsigned long request,void * arg)234 drm_shim_ioctl(int fd, unsigned long request, void *arg)
235 {
236    ASSERTED int type = _IOC_TYPE(request);
237    int nr = _IOC_NR(request);
238 
239    assert(type == DRM_IOCTL_BASE);
240 
241    if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
242       int driver_nr = nr - DRM_COMMAND_BASE;
243 
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);
247       }
248    } else {
249       if (nr < ARRAY_SIZE(core_ioctls) && core_ioctls[nr]) {
250          return core_ioctls[nr](fd, request, arg);
251       }
252    }
253 
254    if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
255       fprintf(stderr,
256               "DRM_SHIM: unhandled driver DRM ioctl %d (0x%08lx)\n",
257               nr - DRM_COMMAND_BASE, request);
258    } else {
259       fprintf(stderr,
260               "DRM_SHIM: unhandled core DRM ioctl 0x%X (0x%08lx)\n",
261               nr, request);
262    }
263 
264    return -EINVAL;
265 }
266 
267 void
drm_shim_bo_init(struct shim_bo * bo,size_t size)268 drm_shim_bo_init(struct shim_bo *bo, size_t size)
269 {
270 
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);
275 
276    bo->size = size;
277 }
278 
279 struct shim_bo *
drm_shim_bo_lookup(struct shim_fd * shim_fd,int handle)280 drm_shim_bo_lookup(struct shim_fd *shim_fd, int handle)
281 {
282    if (!handle)
283       return NULL;
284 
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);
290 
291    if (bo)
292       p_atomic_inc(&bo->refcount);
293 
294    return bo;
295 }
296 
297 void
drm_shim_bo_get(struct shim_bo * bo)298 drm_shim_bo_get(struct shim_bo *bo)
299 {
300    p_atomic_inc(&bo->refcount);
301 }
302 
303 void
drm_shim_bo_put(struct shim_bo * bo)304 drm_shim_bo_put(struct shim_bo *bo)
305 {
306    if (p_atomic_dec_return(&bo->refcount) == 0)
307       return;
308 
309    if (shim_device.driver_bo_free)
310       shim_device.driver_bo_free(bo);
311 
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);
315    free(bo);
316 }
317 
318 int
drm_shim_bo_get_handle(struct shim_fd * shim_fd,struct shim_bo * bo)319 drm_shim_bo_get_handle(struct shim_fd *shim_fd, struct shim_bo *bo)
320 {
321    /* We should probably have some real datastructure for finding the free
322     * number.
323     */
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)) {
328          drm_shim_bo_get(bo);
329          _mesa_hash_table_insert(shim_fd->handles, key, bo);
330          mtx_unlock(&shim_fd->handle_lock);
331          return new_handle;
332       }
333    }
334    mtx_unlock(&shim_fd->handle_lock);
335 
336    return 0;
337 }
338 
339 /* Creates an mmap offset for the BO in the DRM fd.
340  *
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
346  * working for now.
347  */
348 uint64_t
drm_shim_bo_get_mmap_offset(struct shim_fd * shim_fd,struct shim_bo * bo)349 drm_shim_bo_get_mmap_offset(struct shim_fd *shim_fd, struct shim_bo *bo)
350 {
351    return (uintptr_t)bo;
352 }
353 
354 /* For mmap() on the DRM fd, look up the BO from the "offset" and map the BO's
355  * fd.
356  */
357 void *
drm_shim_mmap(struct shim_fd * shim_fd,size_t length,int prot,int flags,int fd,off_t offset)358 drm_shim_mmap(struct shim_fd *shim_fd, size_t length, int prot, int flags,
359               int fd, off_t offset)
360 {
361    struct shim_bo *bo = (void *)(uintptr_t)offset;
362 
363    return mmap(NULL, length, prot, flags, shim_device.mem_fd, bo->mem_addr);
364 }
365