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