61a1cb2ae50224cfeecb13542efb439ad9070529
[mesa.git] / src / drm-shim / drm_shim.c
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 REAL_FUNCTION_POINTER(__xstat);
80 REAL_FUNCTION_POINTER(__xstat64);
81 REAL_FUNCTION_POINTER(__fxstat);
82 REAL_FUNCTION_POINTER(__fxstat64);
83
84 /* Full path of /dev/dri/renderD* */
85 static char *render_node_path;
86 /* renderD* */
87 static char *render_node_dirent_name;
88 /* /sys/dev/char/major:minor/device */
89 static char *device_path;
90 /* /sys/dev/char/major:minor/device/subsystem */
91 static char *subsystem_path;
92 int render_node_minor = -1;
93
94 struct file_override {
95 const char *path;
96 char *contents;
97 };
98 static struct file_override file_overrides[10];
99 static int file_overrides_count;
100 extern bool drm_shim_driver_prefers_first_render_node;
101
102 /* Pick the minor and filename for our shimmed render node. This can be
103 * either a new one that didn't exist on the system, or if the driver wants,
104 * it can replace the first render node.
105 */
106 static void
107 get_dri_render_node_minor(void)
108 {
109 for (int i = 0; i < 10; i++) {
110 int minor = 128 + i;
111 asprintf(&render_node_dirent_name, "renderD%d", minor);
112 asprintf(&render_node_path, "/dev/dri/%s",
113 render_node_dirent_name);
114 struct stat st;
115 if (drm_shim_driver_prefers_first_render_node ||
116 stat(render_node_path, &st) == -1) {
117
118 render_node_minor = minor;
119 return;
120 }
121 }
122
123 fprintf(stderr, "Couldn't find a spare render node slot\n");
124 }
125
126 static void *get_function_pointer(const char *name)
127 {
128 void *func = dlsym(RTLD_NEXT, name);
129 if (!func) {
130 fprintf(stderr, "Failed to resolve %s\n", name);
131 abort();
132 }
133 return func;
134 }
135
136 #define GET_FUNCTION_POINTER(x) real_##x = get_function_pointer(#x)
137
138 void
139 drm_shim_override_file(const char *contents, const char *path_format, ...)
140 {
141 assert(file_overrides_count < ARRAY_SIZE(file_overrides));
142
143 char *path;
144 va_list ap;
145 va_start(ap, path_format);
146 vasprintf(&path, path_format, ap);
147 va_end(ap);
148
149 struct file_override *override = &file_overrides[file_overrides_count++];
150 override->path = path;
151 override->contents = strdup(contents);
152 }
153
154 static void
155 destroy_shim(void)
156 {
157 _mesa_set_destroy(opendir_set, NULL);
158 free(render_node_path);
159 free(render_node_dirent_name);
160 free(subsystem_path);
161 }
162
163 /* Initialization, which will be called from the first general library call
164 * that might need to be wrapped with the shim.
165 */
166 static void
167 init_shim(void)
168 {
169 static bool inited = false;
170 drm_shim_debug = debug_get_bool_option("DRM_SHIM_DEBUG", false);
171
172 /* We can't lock this, because we recurse during initialization. */
173 if (inited)
174 return;
175
176 /* This comes first (and we're locked), to make sure we don't recurse
177 * during initialization.
178 */
179 inited = true;
180
181 opendir_set = _mesa_set_create(NULL,
182 _mesa_hash_string,
183 _mesa_key_string_equal);
184
185 GET_FUNCTION_POINTER(closedir);
186 GET_FUNCTION_POINTER(dup);
187 GET_FUNCTION_POINTER(fcntl);
188 GET_FUNCTION_POINTER(fopen);
189 GET_FUNCTION_POINTER(ioctl);
190 GET_FUNCTION_POINTER(mmap);
191 GET_FUNCTION_POINTER(open);
192 GET_FUNCTION_POINTER(opendir);
193 GET_FUNCTION_POINTER(readdir);
194 GET_FUNCTION_POINTER(readdir64);
195 GET_FUNCTION_POINTER(readlink);
196 GET_FUNCTION_POINTER(realpath);
197 GET_FUNCTION_POINTER(__xstat);
198 GET_FUNCTION_POINTER(__xstat64);
199 GET_FUNCTION_POINTER(__fxstat);
200 GET_FUNCTION_POINTER(__fxstat64);
201
202 get_dri_render_node_minor();
203
204 if (drm_shim_debug) {
205 fprintf(stderr, "Initializing DRM shim on %s\n",
206 render_node_path);
207 }
208
209 asprintf(&device_path,
210 "/sys/dev/char/%d:%d/device",
211 DRM_MAJOR, render_node_minor);
212
213 asprintf(&subsystem_path,
214 "/sys/dev/char/%d:%d/device/subsystem",
215 DRM_MAJOR, render_node_minor);
216
217 drm_shim_device_init();
218
219 atexit(destroy_shim);
220 }
221
222 /* Override libdrm's reading of various sysfs files for device enumeration. */
223 PUBLIC FILE *fopen(const char *path, const char *mode)
224 {
225 init_shim();
226
227 for (int i = 0; i < file_overrides_count; i++) {
228 if (strcmp(file_overrides[i].path, path) == 0) {
229 int fds[2];
230 pipe(fds);
231 write(fds[1], file_overrides[i].contents,
232 strlen(file_overrides[i].contents));
233 close(fds[1]);
234 return fdopen(fds[0], "r");
235 }
236 }
237
238 return real_fopen(path, mode);
239 }
240 PUBLIC FILE *fopen64(const char *path, const char *mode)
241 __attribute__((alias("fopen")));
242
243 /* Intercepts open(render_node_path) to redirect it to the simulator. */
244 PUBLIC int open(const char *path, int flags, ...)
245 {
246 init_shim();
247
248 va_list ap;
249 va_start(ap, flags);
250 mode_t mode = va_arg(ap, mode_t);
251 va_end(ap);
252
253 if (strcmp(path, render_node_path) != 0)
254 return real_open(path, flags, mode);
255
256 int fd = real_open("/dev/null", O_RDWR, 0);
257
258 drm_shim_fd_register(fd, NULL);
259
260 return fd;
261 }
262 PUBLIC int open64(const char*, int, ...) __attribute__((alias("open")));
263
264 /* Fakes stat to return character device stuff for our fake render node. */
265 PUBLIC int __xstat(int ver, const char *path, struct stat *st)
266 {
267 init_shim();
268
269 /* Note: call real stat if we're in the process of probing for a free
270 * render node!
271 */
272 if (render_node_minor == -1)
273 return real___xstat(ver, path, st);
274
275 /* Fool libdrm's probe of whether the /sys dir for this char dev is
276 * there.
277 */
278 char *sys_dev_drm_dir;
279 asprintf(&sys_dev_drm_dir, "/sys/dev/char/%d:%d/device/drm",
280 DRM_MAJOR, render_node_minor);
281 if (strcmp(path, sys_dev_drm_dir) == 0) {
282 free(sys_dev_drm_dir);
283 return 0;
284 }
285 free(sys_dev_drm_dir);
286
287 if (strcmp(path, render_node_path) != 0)
288 return real___xstat(ver, path, st);
289
290 memset(st, 0, sizeof(*st));
291 st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
292 st->st_mode = S_IFCHR;
293
294 return 0;
295 }
296
297 /* Fakes stat to return character device stuff for our fake render node. */
298 PUBLIC int __xstat64(int ver, const char *path, struct stat64 *st)
299 {
300 init_shim();
301
302 /* Note: call real stat if we're in the process of probing for a free
303 * render node!
304 */
305 if (render_node_minor == -1)
306 return real___xstat64(ver, path, st);
307
308 /* Fool libdrm's probe of whether the /sys dir for this char dev is
309 * there.
310 */
311 char *sys_dev_drm_dir;
312 asprintf(&sys_dev_drm_dir, "/sys/dev/char/%d:%d/device/drm",
313 DRM_MAJOR, render_node_minor);
314 if (strcmp(path, sys_dev_drm_dir) == 0) {
315 free(sys_dev_drm_dir);
316 return 0;
317 }
318 free(sys_dev_drm_dir);
319
320 if (strcmp(path, render_node_path) != 0)
321 return real___xstat64(ver, path, st);
322
323 memset(st, 0, sizeof(*st));
324 st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
325 st->st_mode = S_IFCHR;
326
327 return 0;
328 }
329
330 /* Fakes fstat to return character device stuff for our fake render node. */
331 PUBLIC int __fxstat(int ver, int fd, struct stat *st)
332 {
333 init_shim();
334
335 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
336
337 if (!shim_fd)
338 return real___fxstat(ver, fd, st);
339
340 memset(st, 0, sizeof(*st));
341 st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
342 st->st_mode = S_IFCHR;
343
344 return 0;
345 }
346
347 PUBLIC int __fxstat64(int ver, int fd, struct stat64 *st)
348 {
349 init_shim();
350
351 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
352
353 if (!shim_fd)
354 return real___fxstat64(ver, fd, st);
355
356 memset(st, 0, sizeof(*st));
357 st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
358 st->st_mode = S_IFCHR;
359
360 return 0;
361 }
362
363 /* Tracks if the opendir was on /dev/dri. */
364 PUBLIC DIR *
365 opendir(const char *name)
366 {
367 init_shim();
368
369 DIR *dir = real_opendir(name);
370 if (strcmp(name, "/dev/dri") == 0) {
371 if (!dir) {
372 /* If /dev/dri didn't exist, we still want to be able to return our
373 * fake /dev/dri/render* even though we probably can't
374 * mkdir("/dev/dri"). Return a fake DIR pointer for that.
375 */
376 dir = fake_dev_dri;
377 }
378
379 mtx_lock(&shim_lock);
380 _mesa_set_add(opendir_set, dir);
381 mtx_unlock(&shim_lock);
382 }
383
384 return dir;
385 }
386
387 /* If we've reached the end of the real directory list and we're
388 * looking at /dev/dri, add our render node to the list.
389 */
390 PUBLIC struct dirent *
391 readdir(DIR *dir)
392 {
393 init_shim();
394
395 struct dirent *ent = NULL;
396
397 if (dir != fake_dev_dri)
398 ent = real_readdir(dir);
399 static struct dirent render_node_dirent = { 0 };
400
401 if (!ent) {
402 mtx_lock(&shim_lock);
403 if (_mesa_set_search(opendir_set, dir)) {
404 strcpy(render_node_dirent.d_name,
405 render_node_dirent_name);
406 ent = &render_node_dirent;
407 _mesa_set_remove_key(opendir_set, dir);
408 }
409 mtx_unlock(&shim_lock);
410 }
411
412 return ent;
413 }
414
415 /* If we've reached the end of the real directory list and we're
416 * looking at /dev/dri, add our render node to the list.
417 */
418 PUBLIC struct dirent64 *
419 readdir64(DIR *dir)
420 {
421 init_shim();
422
423 struct dirent64 *ent = NULL;
424 if (dir != fake_dev_dri)
425 ent = real_readdir64(dir);
426 static struct dirent64 render_node_dirent = { 0 };
427
428 if (!ent) {
429 mtx_lock(&shim_lock);
430 if (_mesa_set_search(opendir_set, dir)) {
431 strcpy(render_node_dirent.d_name,
432 render_node_dirent_name);
433 ent = &render_node_dirent;
434 _mesa_set_remove_key(opendir_set, dir);
435 }
436 mtx_unlock(&shim_lock);
437 }
438
439 return ent;
440 }
441
442 /* Cleans up tracking of opendir("/dev/dri") */
443 PUBLIC int
444 closedir(DIR *dir)
445 {
446 init_shim();
447
448 mtx_lock(&shim_lock);
449 _mesa_set_remove_key(opendir_set, dir);
450 mtx_unlock(&shim_lock);
451
452 if (dir != fake_dev_dri)
453 return real_closedir(dir);
454 else
455 return 0;
456 }
457
458 /* Handles libdrm's readlink to figure out what kind of device we have. */
459 PUBLIC ssize_t
460 readlink(const char *path, char *buf, size_t size)
461 {
462 init_shim();
463
464 if (strcmp(path, subsystem_path) != 0)
465 return real_readlink(path, buf, size);
466
467 static const struct {
468 const char *name;
469 int bus_type;
470 } bus_types[] = {
471 { "/pci", DRM_BUS_PCI },
472 { "/usb", DRM_BUS_USB },
473 { "/platform", DRM_BUS_PLATFORM },
474 { "/spi", DRM_BUS_PLATFORM },
475 { "/host1x", DRM_BUS_HOST1X },
476 };
477
478 for (uint32_t i = 0; i < ARRAY_SIZE(bus_types); i++) {
479 if (bus_types[i].bus_type != shim_device.bus_type)
480 continue;
481
482 strncpy(buf, bus_types[i].name, size);
483 buf[size - 1] = 0;
484 break;
485 }
486
487 return strlen(buf) + 1;
488 }
489
490 /* Handles libdrm's realpath to figure out what kind of device we have. */
491 PUBLIC char *
492 realpath(const char *path, char *resolved_path)
493 {
494 init_shim();
495
496 if (strcmp(path, device_path) != 0)
497 return real_realpath(path, resolved_path);
498
499 strcpy(resolved_path, path);
500
501 return resolved_path;
502 }
503
504 /* Main entrypoint to DRM drivers: the ioctl syscall. We send all ioctls on
505 * our DRM fd to drm_shim_ioctl().
506 */
507 PUBLIC int
508 ioctl(int fd, unsigned long request, ...)
509 {
510 init_shim();
511
512 va_list ap;
513 va_start(ap, request);
514 void *arg = va_arg(ap, void *);
515 va_end(ap);
516
517 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
518 if (!shim_fd)
519 return real_ioctl(fd, request, arg);
520
521 return drm_shim_ioctl(fd, request, arg);
522 }
523
524 /* Gallium uses this to dup the incoming fd on gbm screen creation */
525 PUBLIC int
526 fcntl(int fd, int cmd, ...)
527 {
528 init_shim();
529
530 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
531
532 va_list ap;
533 va_start(ap, cmd);
534 void *arg = va_arg(ap, void *);
535 va_end(ap);
536
537 int ret = real_fcntl(fd, cmd, arg);
538
539 if (shim_fd && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC))
540 drm_shim_fd_register(ret, shim_fd);
541
542 return ret;
543 }
544 PUBLIC int fcntl64(int, int, ...)
545 __attribute__((alias("fcntl")));
546
547 /* I wrote this when trying to fix gallium screen creation, leaving it around
548 * since it's probably good to have.
549 */
550 PUBLIC int
551 dup(int fd)
552 {
553 init_shim();
554
555 int ret = real_dup(fd);
556
557 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
558 if (shim_fd && ret >= 0)
559 drm_shim_fd_register(ret, shim_fd);
560
561 return ret;
562 }
563
564 PUBLIC void *
565 mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
566 {
567 init_shim();
568
569 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
570 if (shim_fd)
571 return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset);
572
573 return real_mmap(addr, length, prot, flags, fd, offset);
574 }
575 PUBLIC void *mmap64(void*, size_t, int, int, int, off_t)
576 __attribute__((alias("mmap")));