xref: /aosp_15_r20/external/mesa3d/src/drm-shim/drm_shim.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 /**
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 #undef _TIME_BITS
34 #define _LARGEFILE64_SOURCE
35 
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <sys/ioctl.h>
42 #include <sys/mman.h>
43 #include <sys/stat.h>
44 #include <sys/sysmacros.h>
45 #include <stdarg.h>
46 #include <fcntl.h>
47 #include <dlfcn.h>
48 #include <dirent.h>
49 #include <c11/threads.h>
50 #include <drm-uapi/drm.h>
51 
52 #include "util/anon_file.h"
53 #include "util/set.h"
54 #include "util/simple_mtx.h"
55 #include "util/u_debug.h"
56 #include "drm_shim.h"
57 
58 #define REAL_FUNCTION_POINTER(x) __typeof__(x) *real_##x
59 
60 static simple_mtx_t shim_lock = SIMPLE_MTX_INITIALIZER;
61 struct set *opendir_set;
62 bool drm_shim_debug;
63 
64 /* If /dev/dri doesn't exist, we'll need an arbitrary pointer that wouldn't be
65  * returned by any other opendir() call so we can return just our fake node.
66  */
67 DIR *fake_dev_dri = (void *)&opendir_set;
68 
69 REAL_FUNCTION_POINTER(access);
70 REAL_FUNCTION_POINTER(close);
71 REAL_FUNCTION_POINTER(closedir);
72 REAL_FUNCTION_POINTER(dup);
73 REAL_FUNCTION_POINTER(fcntl);
74 REAL_FUNCTION_POINTER(fopen);
75 REAL_FUNCTION_POINTER(ioctl);
76 REAL_FUNCTION_POINTER(mmap);
77 REAL_FUNCTION_POINTER(mmap64);
78 REAL_FUNCTION_POINTER(open);
79 REAL_FUNCTION_POINTER(opendir);
80 REAL_FUNCTION_POINTER(readdir);
81 REAL_FUNCTION_POINTER(readdir64);
82 REAL_FUNCTION_POINTER(readlink);
83 REAL_FUNCTION_POINTER(realpath);
84 
85 #define HAS_XSTAT __GLIBC__ == 2 && __GLIBC_MINOR__ < 33
86 
87 #if HAS_XSTAT
88 REAL_FUNCTION_POINTER(__xstat);
89 REAL_FUNCTION_POINTER(__xstat64);
90 REAL_FUNCTION_POINTER(__fxstat);
91 REAL_FUNCTION_POINTER(__fxstat64);
92 #else
93 REAL_FUNCTION_POINTER(stat);
94 REAL_FUNCTION_POINTER(stat64);
95 REAL_FUNCTION_POINTER(fstat);
96 REAL_FUNCTION_POINTER(fstat64);
97 #endif
98 
99 static char render_node_dir[] = "/dev/dri/";
100 /* Full path of /dev/dri/renderD* */
101 static char *render_node_path;
102 /* renderD* */
103 static char *render_node_dirent_name;
104 /* /sys/dev/char/major: */
105 static int drm_device_path_len;
106 static char *drm_device_path;
107 /* /sys/dev/char/major:minor/device */
108 static int device_path_len;
109 static char *device_path;
110 /* /sys/dev/char/major:minor/device/subsystem */
111 static char *subsystem_path;
112 int render_node_minor = -1;
113 
114 struct file_override {
115    const char *path;
116    char *contents;
117 };
118 static struct file_override file_overrides[10];
119 static int file_overrides_count;
120 extern bool drm_shim_driver_prefers_first_render_node;
121 
122 static int
nfvasprintf(char ** restrict strp,const char * restrict fmt,va_list ap)123 nfvasprintf(char **restrict strp, const char *restrict fmt, va_list ap)
124 {
125    int ret = vasprintf(strp, fmt, ap);
126    assert(ret >= 0);
127    return ret;
128 }
129 
130 static int
nfasprintf(char ** restrict strp,const char * restrict fmt,...)131 nfasprintf(char **restrict strp, const char *restrict fmt, ...)
132 {
133    va_list ap;
134    va_start(ap, fmt);
135    int ret = nfvasprintf(strp, fmt, ap);
136    va_end(ap);
137    return ret;
138 }
139 
140 /* Pick the minor and filename for our shimmed render node.  This can be
141  * either a new one that didn't exist on the system, or if the driver wants,
142  * it can replace the first render node.
143  */
144 static void
get_dri_render_node_minor(void)145 get_dri_render_node_minor(void)
146 {
147    for (int i = 0; i < 10; i++) {
148       UNUSED int minor = 128 + i;
149       nfasprintf(&render_node_dirent_name, "renderD%d", minor);
150       nfasprintf(&render_node_path, "/dev/dri/%s",
151                  render_node_dirent_name);
152       struct stat st;
153       if (drm_shim_driver_prefers_first_render_node ||
154           stat(render_node_path, &st) == -1) {
155 
156          render_node_minor = minor;
157          return;
158       }
159    }
160 
161    fprintf(stderr, "Couldn't find a spare render node slot\n");
162 }
163 
get_function_pointer(const char * name)164 static void *get_function_pointer(const char *name)
165 {
166    void *func = dlsym(RTLD_NEXT, name);
167    if (!func) {
168       fprintf(stderr, "Failed to resolve %s\n", name);
169       abort();
170    }
171    return func;
172 }
173 
174 #define GET_FUNCTION_POINTER(x) real_##x = get_function_pointer(#x)
175 
176 void
drm_shim_override_file(const char * contents,const char * path_format,...)177 drm_shim_override_file(const char *contents, const char *path_format, ...)
178 {
179    assert(file_overrides_count < ARRAY_SIZE(file_overrides));
180 
181    char *path;
182    va_list ap;
183    va_start(ap, path_format);
184    nfvasprintf(&path, path_format, ap);
185    va_end(ap);
186 
187    struct file_override *override = &file_overrides[file_overrides_count++];
188    override->path = path;
189    override->contents = strdup(contents);
190 }
191 
192 static void
destroy_shim(void)193 destroy_shim(void)
194 {
195    _mesa_set_destroy(opendir_set, NULL);
196    free(render_node_path);
197    free(render_node_dirent_name);
198    free(subsystem_path);
199 }
200 
201 /* Initialization, which will be called from the first general library call
202  * that might need to be wrapped with the shim.
203  */
204 static void
init_shim(void)205 init_shim(void)
206 {
207    static bool inited = false;
208    drm_shim_debug = debug_get_bool_option("DRM_SHIM_DEBUG", false);
209 
210    /* We can't lock this, because we recurse during initialization. */
211    if (inited)
212       return;
213 
214    /* This comes first (and we're locked), to make sure we don't recurse
215     * during initialization.
216     */
217    inited = true;
218 
219    opendir_set = _mesa_set_create(NULL,
220                                   _mesa_hash_string,
221                                   _mesa_key_string_equal);
222 
223    GET_FUNCTION_POINTER(access);
224    GET_FUNCTION_POINTER(close);
225    GET_FUNCTION_POINTER(closedir);
226    GET_FUNCTION_POINTER(dup);
227    GET_FUNCTION_POINTER(fcntl);
228    GET_FUNCTION_POINTER(fopen);
229    GET_FUNCTION_POINTER(ioctl);
230    GET_FUNCTION_POINTER(mmap);
231    GET_FUNCTION_POINTER(mmap64);
232    GET_FUNCTION_POINTER(open);
233    GET_FUNCTION_POINTER(opendir);
234    GET_FUNCTION_POINTER(readdir);
235    GET_FUNCTION_POINTER(readdir64);
236    GET_FUNCTION_POINTER(readlink);
237    GET_FUNCTION_POINTER(realpath);
238 
239 #if HAS_XSTAT
240    GET_FUNCTION_POINTER(__xstat);
241    GET_FUNCTION_POINTER(__xstat64);
242    GET_FUNCTION_POINTER(__fxstat);
243    GET_FUNCTION_POINTER(__fxstat64);
244 #else
245    GET_FUNCTION_POINTER(stat);
246    GET_FUNCTION_POINTER(stat64);
247    GET_FUNCTION_POINTER(fstat);
248    GET_FUNCTION_POINTER(fstat64);
249 #endif
250 
251    get_dri_render_node_minor();
252 
253    if (drm_shim_debug) {
254       fprintf(stderr, "Initializing DRM shim on %s\n",
255               render_node_path);
256    }
257 
258    drm_device_path_len =
259       nfasprintf(&drm_device_path, "/sys/dev/char/%d:", DRM_MAJOR);
260 
261    device_path_len =
262       nfasprintf(&device_path,
263                  "/sys/dev/char/%d:%d/device",
264                  DRM_MAJOR, render_node_minor);
265 
266    nfasprintf(&subsystem_path,
267               "/sys/dev/char/%d:%d/device/subsystem",
268               DRM_MAJOR, render_node_minor);
269 
270    drm_shim_device_init();
271 
272    atexit(destroy_shim);
273 }
274 
hide_drm_device_path(const char * path)275 static bool hide_drm_device_path(const char *path)
276 {
277    if (render_node_minor == -1)
278       return false;
279 
280    /* If the path looks like our fake render node device, then don't hide it.
281     */
282    if (strncmp(path, device_path, device_path_len) == 0 ||
283        strcmp(path, render_node_path) == 0)
284       return false;
285 
286    /* String starts with /sys/dev/char/226: but is not the fake render node.
287     * We want to hide all other drm devices for the shim.
288     */
289    if (strncmp(path, drm_device_path, drm_device_path_len) == 0)
290       return true;
291 
292    /* String starts with /dev/dri/ but is not the fake render node. We want to
293     * hide all other drm devices for the shim.
294     */
295    if (strncmp(path, render_node_dir, sizeof(render_node_dir) - 1) == 0)
296       return true;
297 
298    return false;
299 }
300 
file_override_open(const char * path)301 static int file_override_open(const char *path)
302 {
303    for (int i = 0; i < file_overrides_count; i++) {
304       if (strcmp(file_overrides[i].path, path) == 0) {
305          int fd = os_create_anonymous_file(0, "shim file");
306          write(fd, file_overrides[i].contents,
307                strlen(file_overrides[i].contents));
308          lseek(fd, 0, SEEK_SET);
309          return fd;
310       }
311    }
312 
313    return -1;
314 }
315 
316 /* Override libdrm's reading of various sysfs files for device enumeration. */
fopen(const char * path,const char * mode)317 PUBLIC FILE *fopen(const char *path, const char *mode)
318 {
319    init_shim();
320 
321    int fd = file_override_open(path);
322    if (fd >= 0)
323       return fdopen(fd, "r");
324 
325    return real_fopen(path, mode);
326 }
327 PUBLIC FILE *fopen64(const char *path, const char *mode)
328    __attribute__((alias("fopen")));
329 
330 /* Intercepts access(render_node_path) to trick drmGetMinorType */
access(const char * path,int mode)331 PUBLIC int access(const char *path, int mode)
332 {
333    init_shim();
334 
335    if (hide_drm_device_path(path)) {
336       errno = ENOENT;
337       return -1;
338    }
339 
340    if (strcmp(path, render_node_path) != 0)
341       return real_access(path, mode);
342 
343    return 0;
344 }
345 
346 /* Intercepts open(render_node_path) to redirect it to the simulator. */
open(const char * path,int flags,...)347 PUBLIC int open(const char *path, int flags, ...)
348 {
349    init_shim();
350 
351    va_list ap;
352    va_start(ap, flags);
353    mode_t mode = va_arg(ap, mode_t);
354    va_end(ap);
355 
356    int fd = file_override_open(path);
357    if (fd >= 0)
358       return fd;
359 
360    if (hide_drm_device_path(path)) {
361       errno = ENOENT;
362       return -1;
363    }
364 
365    if (strcmp(path, render_node_path) != 0)
366       return real_open(path, flags, mode);
367 
368    fd = real_open("/dev/null", O_RDWR, 0);
369 
370    drm_shim_fd_register(fd, NULL);
371 
372    return fd;
373 }
374 PUBLIC int open64(const char*, int, ...) __attribute__((alias("open")));
375 
376 /* __open64_2 isn't declared unless _FORTIFY_SOURCE is defined. */
377 PUBLIC int __open64_2(const char *path, int flags);
__open64_2(const char * path,int flags)378 PUBLIC int __open64_2(const char *path, int flags)
379 {
380    return open(path, flags, 0);
381 }
382 
close(int fd)383 PUBLIC int close(int fd)
384 {
385    init_shim();
386 
387    drm_shim_fd_unregister(fd);
388 
389    return real_close(fd);
390 }
391 
392 #if HAS_XSTAT
393 /* Fakes stat to return character device stuff for our fake render node. */
__xstat(int ver,const char * path,struct stat * st)394 PUBLIC int __xstat(int ver, const char *path, struct stat *st)
395 {
396    init_shim();
397 
398    /* Note: call real stat if we're in the process of probing for a free
399     * render node!
400     */
401    if (render_node_minor == -1)
402       return real___xstat(ver, path, st);
403 
404    if (hide_drm_device_path(path)) {
405       errno = ENOENT;
406       return -1;
407    }
408 
409    /* Fool libdrm's probe of whether the /sys dir for this char dev is
410     * there.
411     */
412    char *sys_dev_drm_dir;
413    nfasprintf(&sys_dev_drm_dir,
414               "/sys/dev/char/%d:%d/device/drm",
415               DRM_MAJOR, render_node_minor);
416    if (strcmp(path, sys_dev_drm_dir) == 0) {
417       free(sys_dev_drm_dir);
418       return 0;
419    }
420    free(sys_dev_drm_dir);
421 
422    if (strcmp(path, render_node_path) != 0)
423       return real___xstat(ver, path, st);
424 
425    memset(st, 0, sizeof(*st));
426    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
427    st->st_mode = S_IFCHR;
428 
429    return 0;
430 }
431 
432 /* Fakes stat to return character device stuff for our fake render node. */
__xstat64(int ver,const char * path,struct stat64 * st)433 PUBLIC int __xstat64(int ver, const char *path, struct stat64 *st)
434 {
435    init_shim();
436 
437    /* Note: call real stat if we're in the process of probing for a free
438     * render node!
439     */
440    if (render_node_minor == -1)
441       return real___xstat64(ver, path, st);
442 
443    if (hide_drm_device_path(path)) {
444       errno = ENOENT;
445       return -1;
446    }
447 
448    /* Fool libdrm's probe of whether the /sys dir for this char dev is
449     * there.
450     */
451    char *sys_dev_drm_dir;
452    nfasprintf(&sys_dev_drm_dir,
453               "/sys/dev/char/%d:%d/device/drm",
454               DRM_MAJOR, render_node_minor);
455    if (strcmp(path, sys_dev_drm_dir) == 0) {
456       free(sys_dev_drm_dir);
457       return 0;
458    }
459    free(sys_dev_drm_dir);
460 
461    if (strcmp(path, render_node_path) != 0)
462       return real___xstat64(ver, path, st);
463 
464    memset(st, 0, sizeof(*st));
465    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
466    st->st_mode = S_IFCHR;
467 
468    return 0;
469 }
470 
471 /* Fakes fstat to return character device stuff for our fake render node. */
__fxstat(int ver,int fd,struct stat * st)472 PUBLIC int __fxstat(int ver, int fd, struct stat *st)
473 {
474    init_shim();
475 
476    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
477 
478    if (!shim_fd)
479       return real___fxstat(ver, fd, st);
480 
481    memset(st, 0, sizeof(*st));
482    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
483    st->st_mode = S_IFCHR;
484 
485    return 0;
486 }
487 
__fxstat64(int ver,int fd,struct stat64 * st)488 PUBLIC int __fxstat64(int ver, int fd, struct stat64 *st)
489 {
490    init_shim();
491 
492    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
493 
494    if (!shim_fd)
495       return real___fxstat64(ver, fd, st);
496 
497    memset(st, 0, sizeof(*st));
498    st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
499    st->st_mode = S_IFCHR;
500 
501    return 0;
502 }
503 
504 #else
505 
stat(const char * path,struct stat * stat_buf)506 PUBLIC int stat(const char* path, struct stat* stat_buf)
507 {
508    init_shim();
509 
510    /* Note: call real stat if we're in the process of probing for a free
511     * render node!
512     */
513    if (render_node_minor == -1)
514       return real_stat(path, stat_buf);
515 
516    if (hide_drm_device_path(path)) {
517       errno = ENOENT;
518       return -1;
519    }
520 
521    /* Fool libdrm's probe of whether the /sys dir for this char dev is
522     * there.
523     */
524    char *sys_dev_drm_dir;
525    nfasprintf(&sys_dev_drm_dir,
526               "/sys/dev/char/%d:%d/device/drm",
527               DRM_MAJOR, render_node_minor);
528    if (strcmp(path, sys_dev_drm_dir) == 0) {
529       free(sys_dev_drm_dir);
530       return 0;
531    }
532    free(sys_dev_drm_dir);
533 
534    if (strcmp(path, render_node_path) != 0)
535       return real_stat(path, stat_buf);
536 
537    memset(stat_buf, 0, sizeof(*stat_buf));
538    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
539    stat_buf->st_mode = S_IFCHR;
540 
541    return 0;
542 }
543 
stat64(const char * path,struct stat64 * stat_buf)544 PUBLIC int stat64(const char* path, struct stat64* stat_buf)
545 {
546    init_shim();
547 
548    /* Note: call real stat if we're in the process of probing for a free
549     * render node!
550     */
551    if (render_node_minor == -1)
552       return real_stat64(path, stat_buf);
553 
554    if (hide_drm_device_path(path)) {
555       errno = ENOENT;
556       return -1;
557    }
558 
559    /* Fool libdrm's probe of whether the /sys dir for this char dev is
560     * there.
561     */
562    char *sys_dev_drm_dir;
563    nfasprintf(&sys_dev_drm_dir,
564               "/sys/dev/char/%d:%d/device/drm",
565               DRM_MAJOR, render_node_minor);
566    if (strcmp(path, sys_dev_drm_dir) == 0) {
567       free(sys_dev_drm_dir);
568       return 0;
569    }
570    free(sys_dev_drm_dir);
571 
572    if (strcmp(path, render_node_path) != 0)
573       return real_stat64(path, stat_buf);
574 
575    memset(stat_buf, 0, sizeof(*stat_buf));
576    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
577    stat_buf->st_mode = S_IFCHR;
578 
579    return 0;
580 }
581 
fstat(int fd,struct stat * stat_buf)582 PUBLIC int fstat(int fd, struct stat* stat_buf)
583 {
584    init_shim();
585 
586    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
587 
588    if (!shim_fd)
589       return real_fstat(fd, stat_buf);
590 
591    memset(stat_buf, 0, sizeof(*stat_buf));
592    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
593    stat_buf->st_mode = S_IFCHR;
594 
595    return 0;
596 }
597 
fstat64(int fd,struct stat64 * stat_buf)598 PUBLIC int fstat64(int fd, struct stat64* stat_buf)
599 {
600    init_shim();
601 
602    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
603 
604    if (!shim_fd)
605       return real_fstat64(fd, stat_buf);
606 
607    memset(stat_buf, 0, sizeof(*stat_buf));
608    stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
609    stat_buf->st_mode = S_IFCHR;
610 
611    return 0;
612 }
613 #endif
614 
615 /* Tracks if the opendir was on /dev/dri. */
616 PUBLIC DIR *
opendir(const char * name)617 opendir(const char *name)
618 {
619    init_shim();
620 
621    DIR *dir = real_opendir(name);
622    if (strcmp(name, "/dev/dri") == 0) {
623       if (!dir) {
624          /* If /dev/dri didn't exist, we still want to be able to return our
625           * fake /dev/dri/render* even though we probably can't
626           * mkdir("/dev/dri").  Return a fake DIR pointer for that.
627           */
628          dir = fake_dev_dri;
629       }
630 
631       simple_mtx_lock(&shim_lock);
632       _mesa_set_add(opendir_set, dir);
633       simple_mtx_unlock(&shim_lock);
634    }
635 
636    return dir;
637 }
638 
639 /* If we're looking at /dev/dri, add our render node to the list
640  * before the real entries in the directory.
641  */
642 PUBLIC struct dirent *
readdir(DIR * dir)643 readdir(DIR *dir)
644 {
645    init_shim();
646 
647    struct dirent *ent = NULL;
648 
649    static struct dirent render_node_dirent = { 0 };
650 
651    simple_mtx_lock(&shim_lock);
652    if (_mesa_set_search(opendir_set, dir)) {
653       strcpy(render_node_dirent.d_name,
654              render_node_dirent_name);
655       render_node_dirent.d_type = DT_CHR;
656       ent = &render_node_dirent;
657       _mesa_set_remove_key(opendir_set, dir);
658    }
659    simple_mtx_unlock(&shim_lock);
660 
661    if (!ent && dir != fake_dev_dri)
662       ent = real_readdir(dir);
663 
664    return ent;
665 }
666 
667 /* If we're looking at /dev/dri, add our render node to the list
668  * before the real entries in the directory.
669  */
670 PUBLIC struct dirent64 *
readdir64(DIR * dir)671 readdir64(DIR *dir)
672 {
673    init_shim();
674 
675    struct dirent64 *ent = NULL;
676 
677    static struct dirent64 render_node_dirent = { 0 };
678 
679    simple_mtx_lock(&shim_lock);
680    if (_mesa_set_search(opendir_set, dir)) {
681       strcpy(render_node_dirent.d_name,
682              render_node_dirent_name);
683       render_node_dirent.d_type = DT_CHR;
684       ent = &render_node_dirent;
685       _mesa_set_remove_key(opendir_set, dir);
686    }
687    simple_mtx_unlock(&shim_lock);
688 
689    if (!ent && dir != fake_dev_dri)
690       ent = real_readdir64(dir);
691 
692    return ent;
693 }
694 
695 /* Cleans up tracking of opendir("/dev/dri") */
696 PUBLIC int
closedir(DIR * dir)697 closedir(DIR *dir)
698 {
699    init_shim();
700 
701    simple_mtx_lock(&shim_lock);
702    _mesa_set_remove_key(opendir_set, dir);
703    simple_mtx_unlock(&shim_lock);
704 
705    if (dir != fake_dev_dri)
706       return real_closedir(dir);
707    else
708       return 0;
709 }
710 
711 /* Handles libdrm's readlink to figure out what kind of device we have. */
712 PUBLIC ssize_t
readlink(const char * path,char * buf,size_t size)713 readlink(const char *path, char *buf, size_t size)
714 {
715    init_shim();
716 
717    if (hide_drm_device_path(path)) {
718       errno = ENOENT;
719       return -1;
720    }
721 
722    if (strcmp(path, subsystem_path) != 0)
723       return real_readlink(path, buf, size);
724 
725    static const struct {
726       const char *name;
727       int bus_type;
728    } bus_types[] = {
729       { "/pci", DRM_BUS_PCI },
730       { "/usb", DRM_BUS_USB },
731       { "/platform", DRM_BUS_PLATFORM },
732       { "/spi", DRM_BUS_PLATFORM },
733       { "/host1x", DRM_BUS_HOST1X },
734    };
735 
736    for (uint32_t i = 0; i < ARRAY_SIZE(bus_types); i++) {
737       if (bus_types[i].bus_type != shim_device.bus_type)
738          continue;
739 
740       strncpy(buf, bus_types[i].name, size);
741       buf[size - 1] = 0;
742       break;
743    }
744 
745    return strlen(buf) + 1;
746 }
747 
748 #if __USE_FORTIFY_LEVEL > 0 && !defined _CLANG_FORTIFY_DISABLE
749 /* Identical to readlink, but with buffer overflow check */
750 PUBLIC ssize_t
__readlink_chk(const char * path,char * buf,size_t size,size_t buflen)751 __readlink_chk(const char *path, char *buf, size_t size, size_t buflen)
752 {
753    if (size > buflen)
754       abort();
755    return readlink(path, buf, size);
756 }
757 #endif
758 
759 /* Handles libdrm's realpath to figure out what kind of device we have. */
760 PUBLIC char *
realpath(const char * path,char * resolved_path)761 realpath(const char *path, char *resolved_path)
762 {
763    init_shim();
764 
765    if (strcmp(path, device_path) != 0)
766       return real_realpath(path, resolved_path);
767 
768    strcpy(resolved_path, path);
769 
770    return resolved_path;
771 }
772 
773 /* Main entrypoint to DRM drivers: the ioctl syscall.  We send all ioctls on
774  * our DRM fd to drm_shim_ioctl().
775  */
776 PUBLIC int
ioctl(int fd,unsigned long request,...)777 ioctl(int fd, unsigned long request, ...)
778 {
779    init_shim();
780 
781    va_list ap;
782    va_start(ap, request);
783    void *arg = va_arg(ap, void *);
784    va_end(ap);
785 
786    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
787    if (!shim_fd)
788       return real_ioctl(fd, request, arg);
789 
790    return drm_shim_ioctl(fd, request, arg);
791 }
792 
793 /* Gallium uses this to dup the incoming fd on gbm screen creation */
794 PUBLIC int
fcntl(int fd,int cmd,...)795 fcntl(int fd, int cmd, ...)
796 {
797    init_shim();
798 
799    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
800 
801    va_list ap;
802    va_start(ap, cmd);
803    void *arg = va_arg(ap, void *);
804    va_end(ap);
805 
806    int ret = real_fcntl(fd, cmd, arg);
807 
808    if (shim_fd && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC))
809       drm_shim_fd_register(ret, shim_fd);
810 
811    return ret;
812 }
813 PUBLIC int fcntl64(int, int, ...)
814    __attribute__((alias("fcntl")));
815 
816 /* I wrote this when trying to fix gallium screen creation, leaving it around
817  * since it's probably good to have.
818  */
819 PUBLIC int
dup(int fd)820 dup(int fd)
821 {
822    init_shim();
823 
824    int ret = real_dup(fd);
825 
826    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
827    if (shim_fd && ret >= 0)
828       drm_shim_fd_register(ret, shim_fd);
829 
830    return ret;
831 }
832 
833 PUBLIC void *
mmap(void * addr,size_t length,int prot,int flags,int fd,off_t offset)834 mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
835 {
836    init_shim();
837 
838    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
839    if (shim_fd)
840       return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset);
841 
842    return real_mmap(addr, length, prot, flags, fd, offset);
843 }
844 
845 PUBLIC void *
mmap64(void * addr,size_t length,int prot,int flags,int fd,off64_t offset)846 mmap64(void* addr, size_t length, int prot, int flags, int fd, off64_t offset)
847 {
848    init_shim();
849 
850    struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
851    if (shim_fd)
852       return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset);
853 
854    return real_mmap64(addr, length, prot, flags, fd, offset);
855 }
856