61967ec66b184ae759b4cfe2b9d10fa402db133d
[platform/upstream/mesa.git] / src / drm-shim / drm_shim.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 /**
25  * @file
26  *
27  * Implements wrappers of libc functions to fake having a DRM device that
28  * isn't actually present in the kernel.
29  */
30
31 /* Prevent glibc from defining open64 when we want to alias it. */
32 #undef _FILE_OFFSET_BITS
33 #define _LARGEFILE64_SOURCE
34
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/ioctl.h>
41 #include <sys/mman.h>
42 #include <sys/stat.h>
43 #include <sys/sysmacros.h>
44 #include <stdarg.h>
45 #include <fcntl.h>
46 #include <dlfcn.h>
47 #include <dirent.h>
48 #include <c11/threads.h>
49 #include <drm-uapi/drm.h>
50
51 #include "util/set.h"
52 #include "util/u_debug.h"
53 #include "drm_shim.h"
54
55 #define REAL_FUNCTION_POINTER(x) typeof(x) *real_##x
56
57 static mtx_t shim_lock = _MTX_INITIALIZER_NP;
58 struct set *opendir_set;
59 bool drm_shim_debug;
60
61 /* If /dev/dri doesn't exist, we'll need an arbitrary pointer that wouldn't be
62  * returned by any other opendir() call so we can return just our fake node.
63  */
64 DIR *fake_dev_dri = (void *)&opendir_set;
65
66 /* XXX: implement REAL_FUNCTION_POINTER(close); */
67 REAL_FUNCTION_POINTER(closedir);
68 REAL_FUNCTION_POINTER(dup);
69 REAL_FUNCTION_POINTER(fcntl);
70 REAL_FUNCTION_POINTER(fopen);
71 REAL_FUNCTION_POINTER(ioctl);
72 REAL_FUNCTION_POINTER(mmap);
73 REAL_FUNCTION_POINTER(open);
74 REAL_FUNCTION_POINTER(opendir);
75 REAL_FUNCTION_POINTER(readdir);
76 REAL_FUNCTION_POINTER(readdir64);
77 REAL_FUNCTION_POINTER(readlink);
78 REAL_FUNCTION_POINTER(realpath);
79
80 #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 33
81 REAL_FUNCTION_POINTER(__xstat);
82 REAL_FUNCTION_POINTER(__xstat64);
83 REAL_FUNCTION_POINTER(__fxstat);
84 REAL_FUNCTION_POINTER(__fxstat64);
85 #endif
86
87 /* Full path of /dev/dri/renderD* */
88 static char *render_node_path;
89 /* renderD* */
90 static char *render_node_dirent_name;
91 /* /sys/dev/char/major:minor/device */
92 static char *device_path;
93 /* /sys/dev/char/major:minor/device/subsystem */
94 static char *subsystem_path;
95 int render_node_minor = -1;
96
97 struct file_override {
98    const char *path;
99    char *contents;
100 };
101 static struct file_override file_overrides[10];
102 static int file_overrides_count;
103 extern bool drm_shim_driver_prefers_first_render_node;
104
105 #define nfasprintf(...)                         \
106    {                                            \
107       UNUSED int __ret = asprintf(__VA_ARGS__); \
108       assert(__ret >= 0);                       \
109    }
110 #define nfvasprintf(...)                         \
111    {                                             \
112       UNUSED int __ret = vasprintf(__VA_ARGS__); \
113       assert(__ret >= 0);                        \
114    }
115
116 /* Pick the minor and filename for our shimmed render node.  This can be
117  * either a new one that didn't exist on the system, or if the driver wants,
118  * it can replace the first render node.
119  */
120 static void
121 get_dri_render_node_minor(void)
122 {
123    for (int i = 0; i < 10; i++) {
124       UNUSED int minor = 128 + i;
125       nfasprintf(&render_node_dirent_name, "renderD%d", minor);
126       nfasprintf(&render_node_path, "/dev/dri/%s",
127                  render_node_dirent_name);
128       struct stat st;
129       if (drm_shim_driver_prefers_first_render_node ||
130           stat(render_node_path, &st) == -1) {
131
132          render_node_minor = minor;
133          return;
134       }
135    }
136
137    fprintf(stderr, "Couldn't find a spare render node slot\n");
138 }
139
140 static void *get_function_pointer(const char *name)
141 {
142    void *func = dlsym(RTLD_NEXT, name);
143    if (!func) {
144       fprintf(stderr, "Failed to resolve %s\n", name);
145       abort();
146    }
147    return func;
148 }
149
150 #define GET_FUNCTION_POINTER(x) real_##x = get_function_pointer(#x)
151
152 void
153 drm_shim_override_file(const char *contents, const char *path_format, ...)
154 {
155    assert(file_overrides_count < ARRAY_SIZE(file_overrides));
156
157    char *path;
158    va_list ap;
159    va_start(ap, path_format);
160    nfvasprintf(&path, path_format, ap);
161    va_end(ap);
162
163    struct file_override *override = &file_overrides[file_overrides_count++];
164    override->path = path;
165    override->contents = strdup(contents);
166 }
167
168 static void
169 destroy_shim(void)
170 {
171    _mesa_set_destroy(opendir_set, NULL);
172    free(render_node_path);
173    free(render_node_dirent_name);
174    free(subsystem_path);
175 }
176
177 /* Initialization, which will be called from the first general library call
178  * that might need to be wrapped with the shim.
179  */
180 static void
181 init_shim(void)
182 {
183    static bool inited = false;
184    drm_shim_debug = debug_get_bool_option("DRM_SHIM_DEBUG", false);
185
186    /* We can't lock this, because we recurse during initialization. */
187    if (inited)
188       return;
189
190    /* This comes first (and we're locked), to make sure we don't recurse
191     * during initialization.
192     */
193    inited = true;
194
195    opendir_set = _mesa_set_create(NULL,
196                                   _mesa_hash_string,
197                                   _mesa_key_string_equal);
198
199    GET_FUNCTION_POINTER(closedir);
200    GET_FUNCTION_POINTER(dup);
201    GET_FUNCTION_POINTER(fcntl);
202    GET_FUNCTION_POINTER(fopen);
203    GET_FUNCTION_POINTER(ioctl);
204    GET_FUNCTION_POINTER(mmap);
205    GET_FUNCTION_POINTER(open);
206    GET_FUNCTION_POINTER(opendir);
207    GET_FUNCTION_POINTER(readdir);
208    GET_FUNCTION_POINTER(readdir64);
209    GET_FUNCTION_POINTER(readlink);
210    GET_FUNCTION_POINTER(realpath);
211
212 #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 33
213    GET_FUNCTION_POINTER(__xstat);
214    GET_FUNCTION_POINTER(__xstat64);
215    GET_FUNCTION_POINTER(__fxstat);
216    GET_FUNCTION_POINTER(__fxstat64);
217 #endif
218
219    get_dri_render_node_minor();
220
221    if (drm_shim_debug) {
222       fprintf(stderr, "Initializing DRM shim on %s\n",
223               render_node_path);
224    }
225
226    nfasprintf(&device_path,
227               "/sys/dev/char/%d:%d/device",
228               DRM_MAJOR, render_node_minor);
229
230    nfasprintf(&subsystem_path,
231               "/sys/dev/char/%d:%d/device/subsystem",
232               DRM_MAJOR, render_node_minor);
233
234    drm_shim_device_init();
235
236    atexit(destroy_shim);
237 }
238
239 /* Override libdrm's reading of various sysfs files for device enumeration. */
240 PUBLIC FILE *fopen(const char *path, const char *mode)
241 {
242    init_shim();
243
244    for (int i = 0; i < file_overrides_count; i++) {
245       if (strcmp(file_overrides[i].path, path) == 0) {
246          int fds[2];
247          pipe(fds);
248          write(fds[1], file_overrides[i].contents,
249                strlen(file_overrides[i].contents));
250          close(fds[1]);
251          return fdopen(fds[0], "r");
252       }
253    }
254
255    return real_fopen(path, mode);
256 }
257 PUBLIC FILE *fopen64(const char *path, const char *mode)
258    __attribute__((alias("fopen")));
259
260 /* Intercepts open(render_node_path) to redirect it to the simulator. */
261 PUBLIC int open(const char *path, int flags, ...)
262 {
263    init_shim();
264
265    va_list ap;
266    va_start(ap, flags);
267    mode_t mode = va_arg(ap, mode_t);
268    va_end(ap);
269
270    if (strcmp(path, render_node_path) != 0)
271       return real_open(path, flags, mode);
272
273    int fd = real_open("/dev/null", O_RDWR, 0);
274
275    drm_shim_fd_register(fd, NULL);
276
277    return fd;
278 }
279 PUBLIC int open64(const char*, int, ...) __attribute__((alias("open")));
280
281 #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 33
282 /* Fakes stat to return character device stuff for our fake render node. */
283 PUBLIC int __xstat(int ver, const char *path, struct stat *st)
284 {
285    init_shim();
286
287    /* Note: call real stat if we're in the process of probing for a free
288     * render node!
289     */
290    if (render_node_minor == -1)
291       return real___xstat(ver, path, st);
292
293    /* Fool libdrm's probe of whether the /sys dir for this char dev is
294     * there.
295     */
296    char *sys_dev_drm_dir;
297    nfasprintf(&sys_dev_drm_dir,
298               "/sys/dev/char/%d:%d/device/drm",
299               DRM_MAJOR, render_node_minor);
300    if (strcmp(path, sys_dev_drm_dir) == 0) {
301       free(sys_dev_drm_dir);
302       return 0;
303    }
304    free(sys_dev_drm_dir);
305
306    if (strcmp(path, render_node_path) != 0)
307       return real___xstat(ver, path, st);
308
309    memset(st, 0, sizeof(*st));
310    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
311    st->st_mode = S_IFCHR;
312
313    return 0;
314 }
315
316 /* Fakes stat to return character device stuff for our fake render node. */
317 PUBLIC int __xstat64(int ver, const char *path, struct stat64 *st)
318 {
319    init_shim();
320
321    /* Note: call real stat if we're in the process of probing for a free
322     * render node!
323     */
324    if (render_node_minor == -1)
325       return real___xstat64(ver, path, st);
326
327    /* Fool libdrm's probe of whether the /sys dir for this char dev is
328     * there.
329     */
330    char *sys_dev_drm_dir;
331    nfasprintf(&sys_dev_drm_dir,
332               "/sys/dev/char/%d:%d/device/drm",
333               DRM_MAJOR, render_node_minor);
334    if (strcmp(path, sys_dev_drm_dir) == 0) {
335       free(sys_dev_drm_dir);
336       return 0;
337    }
338    free(sys_dev_drm_dir);
339
340    if (strcmp(path, render_node_path) != 0)
341       return real___xstat64(ver, path, st);
342
343    memset(st, 0, sizeof(*st));
344    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
345    st->st_mode = S_IFCHR;
346
347    return 0;
348 }
349
350 /* Fakes fstat to return character device stuff for our fake render node. */
351 PUBLIC int __fxstat(int ver, int fd, struct stat *st)
352 {
353    init_shim();
354
355    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
356
357    if (!shim_fd)
358       return real___fxstat(ver, fd, st);
359
360    memset(st, 0, sizeof(*st));
361    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
362    st->st_mode = S_IFCHR;
363
364    return 0;
365 }
366
367 PUBLIC int __fxstat64(int ver, int fd, struct stat64 *st)
368 {
369    init_shim();
370
371    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
372
373    if (!shim_fd)
374       return real___fxstat64(ver, fd, st);
375
376    memset(st, 0, sizeof(*st));
377    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
378    st->st_mode = S_IFCHR;
379
380    return 0;
381 }
382 #endif
383
384 /* Tracks if the opendir was on /dev/dri. */
385 PUBLIC DIR *
386 opendir(const char *name)
387 {
388    init_shim();
389
390    DIR *dir = real_opendir(name);
391    if (strcmp(name, "/dev/dri") == 0) {
392       if (!dir) {
393          /* If /dev/dri didn't exist, we still want to be able to return our
394           * fake /dev/dri/render* even though we probably can't
395           * mkdir("/dev/dri").  Return a fake DIR pointer for that.
396           */
397          dir = fake_dev_dri;
398       }
399
400       mtx_lock(&shim_lock);
401       _mesa_set_add(opendir_set, dir);
402       mtx_unlock(&shim_lock);
403    }
404
405    return dir;
406 }
407
408 /* If we've reached the end of the real directory list and we're
409  * looking at /dev/dri, add our render node to the list.
410  */
411 PUBLIC struct dirent *
412 readdir(DIR *dir)
413 {
414    init_shim();
415
416    struct dirent *ent = NULL;
417
418    if (dir != fake_dev_dri)
419       ent = real_readdir(dir);
420    static struct dirent render_node_dirent = { 0 };
421
422    if (!ent) {
423       mtx_lock(&shim_lock);
424       if (_mesa_set_search(opendir_set, dir)) {
425          strcpy(render_node_dirent.d_name,
426                 render_node_dirent_name);
427          ent = &render_node_dirent;
428          _mesa_set_remove_key(opendir_set, dir);
429       }
430       mtx_unlock(&shim_lock);
431    }
432
433    return ent;
434 }
435
436 /* If we've reached the end of the real directory list and we're
437  * looking at /dev/dri, add our render node to the list.
438  */
439 PUBLIC struct dirent64 *
440 readdir64(DIR *dir)
441 {
442    init_shim();
443
444    struct dirent64 *ent = NULL;
445    if (dir != fake_dev_dri)
446       ent = real_readdir64(dir);
447    static struct dirent64 render_node_dirent = { 0 };
448
449    if (!ent) {
450       mtx_lock(&shim_lock);
451       if (_mesa_set_search(opendir_set, dir)) {
452          strcpy(render_node_dirent.d_name,
453                 render_node_dirent_name);
454          ent = &render_node_dirent;
455          _mesa_set_remove_key(opendir_set, dir);
456       }
457       mtx_unlock(&shim_lock);
458    }
459
460    return ent;
461 }
462
463 /* Cleans up tracking of opendir("/dev/dri") */
464 PUBLIC int
465 closedir(DIR *dir)
466 {
467    init_shim();
468
469    mtx_lock(&shim_lock);
470    _mesa_set_remove_key(opendir_set, dir);
471    mtx_unlock(&shim_lock);
472
473    if (dir != fake_dev_dri)
474       return real_closedir(dir);
475    else
476       return 0;
477 }
478
479 /* Handles libdrm's readlink to figure out what kind of device we have. */
480 PUBLIC ssize_t
481 readlink(const char *path, char *buf, size_t size)
482 {
483    init_shim();
484
485    if (strcmp(path, subsystem_path) != 0)
486       return real_readlink(path, buf, size);
487
488    static const struct {
489       const char *name;
490       int bus_type;
491    } bus_types[] = {
492       { "/pci", DRM_BUS_PCI },
493       { "/usb", DRM_BUS_USB },
494       { "/platform", DRM_BUS_PLATFORM },
495       { "/spi", DRM_BUS_PLATFORM },
496       { "/host1x", DRM_BUS_HOST1X },
497    };
498
499    for (uint32_t i = 0; i < ARRAY_SIZE(bus_types); i++) {
500       if (bus_types[i].bus_type != shim_device.bus_type)
501          continue;
502
503       strncpy(buf, bus_types[i].name, size);
504       buf[size - 1] = 0;
505       break;
506    }
507
508    return strlen(buf) + 1;
509 }
510
511 /* Handles libdrm's realpath to figure out what kind of device we have. */
512 PUBLIC char *
513 realpath(const char *path, char *resolved_path)
514 {
515    init_shim();
516
517    if (strcmp(path, device_path) != 0)
518       return real_realpath(path, resolved_path);
519
520    strcpy(resolved_path, path);
521
522    return resolved_path;
523 }
524
525 /* Main entrypoint to DRM drivers: the ioctl syscall.  We send all ioctls on
526  * our DRM fd to drm_shim_ioctl().
527  */
528 PUBLIC int
529 ioctl(int fd, unsigned long request, ...)
530 {
531    init_shim();
532
533    va_list ap;
534    va_start(ap, request);
535    void *arg = va_arg(ap, void *);
536    va_end(ap);
537
538    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
539    if (!shim_fd)
540       return real_ioctl(fd, request, arg);
541
542    return drm_shim_ioctl(fd, request, arg);
543 }
544
545 /* Gallium uses this to dup the incoming fd on gbm screen creation */
546 PUBLIC int
547 fcntl(int fd, int cmd, ...)
548 {
549    init_shim();
550
551    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
552
553    va_list ap;
554    va_start(ap, cmd);
555    void *arg = va_arg(ap, void *);
556    va_end(ap);
557
558    int ret = real_fcntl(fd, cmd, arg);
559
560    if (shim_fd && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC))
561       drm_shim_fd_register(ret, shim_fd);
562
563    return ret;
564 }
565 PUBLIC int fcntl64(int, int, ...)
566    __attribute__((alias("fcntl")));
567
568 /* I wrote this when trying to fix gallium screen creation, leaving it around
569  * since it's probably good to have.
570  */
571 PUBLIC int
572 dup(int fd)
573 {
574    init_shim();
575
576    int ret = real_dup(fd);
577
578    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
579    if (shim_fd && ret >= 0)
580       drm_shim_fd_register(ret, shim_fd);
581
582    return ret;
583 }
584
585 PUBLIC void *
586 mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
587 {
588    init_shim();
589
590    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
591    if (shim_fd)
592       return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset);
593
594    return real_mmap(addr, length, prot, flags, fd, offset);
595 }
596 PUBLIC void *mmap64(void*, size_t, int, int, int, off_t)
597    __attribute__((alias("mmap")));