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