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