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
57static mtx_t shim_lock = _MTX_INITIALIZER_NP;
58struct set *opendir_set;
59bool 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 */
64DIR *fake_dev_dri = (void *)&opendir_set;
65
66/* XXX: implement REAL_FUNCTION_POINTER(close); */
67REAL_FUNCTION_POINTER(closedir);
68REAL_FUNCTION_POINTER(dup);
69REAL_FUNCTION_POINTER(fcntl);
70REAL_FUNCTION_POINTER(fopen);
71REAL_FUNCTION_POINTER(ioctl);
72REAL_FUNCTION_POINTER(mmap);
73REAL_FUNCTION_POINTER(open);
74REAL_FUNCTION_POINTER(opendir);
75REAL_FUNCTION_POINTER(readdir);
76REAL_FUNCTION_POINTER(readdir64);
77REAL_FUNCTION_POINTER(readlink);
78REAL_FUNCTION_POINTER(realpath);
79
80#define HAS_XSTAT __GLIBC__ == 2 && __GLIBC_MINOR__ < 33
81
82#if HAS_XSTAT
83REAL_FUNCTION_POINTER(__xstat);
84REAL_FUNCTION_POINTER(__xstat64);
85REAL_FUNCTION_POINTER(__fxstat);
86REAL_FUNCTION_POINTER(__fxstat64);
87#else
88REAL_FUNCTION_POINTER(stat);
89REAL_FUNCTION_POINTER(stat64);
90REAL_FUNCTION_POINTER(fstat);
91REAL_FUNCTION_POINTER(fstat64);
92#endif
93
94/* Full path of /dev/dri/renderD* */
95static char *render_node_path;
96/* renderD* */
97static char *render_node_dirent_name;
98/* /sys/dev/char/major:minor/device */
99static char *device_path;
100/* /sys/dev/char/major:minor/device/subsystem */
101static char *subsystem_path;
102int render_node_minor = -1;
103
104struct file_override {
105   const char *path;
106   char *contents;
107};
108static struct file_override file_overrides[10];
109static int file_overrides_count;
110extern 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 */
127static void
128get_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
147static 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
159void
160drm_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
175static void
176destroy_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 */
187static void
188init_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. */
252PUBLIC 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}
269PUBLIC 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. */
273PUBLIC 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}
291PUBLIC 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. */
295PUBLIC 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. */
329PUBLIC 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. */
363PUBLIC 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
379PUBLIC 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
397PUBLIC 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
430PUBLIC 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
463PUBLIC 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
479PUBLIC 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. */
497PUBLIC DIR *
498opendir(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 */
523PUBLIC struct dirent *
524readdir(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 */
551PUBLIC struct dirent64 *
552readdir64(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") */
576PUBLIC int
577closedir(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. */
592PUBLIC ssize_t
593readlink(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. */
624PUBLIC char *
625realpath(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 */
640PUBLIC int
641ioctl(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 */
658PUBLIC int
659fcntl(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}
677PUBLIC 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 */
683PUBLIC int
684dup(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
697PUBLIC void *
698mmap(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}
708PUBLIC void *mmap64(void*, size_t, int, int, int, off_t)
709   __attribute__((alias("mmap")));
710