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