17ec681f3Smrg/* 27ec681f3Smrg * Copyright © 2018 Broadcom 37ec681f3Smrg * 47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 57ec681f3Smrg * copy of this software and associated documentation files (the "Software"), 67ec681f3Smrg * to deal in the Software without restriction, including without limitation 77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the 97ec681f3Smrg * Software is furnished to do so, subject to the following conditions: 107ec681f3Smrg * 117ec681f3Smrg * The above copyright notice and this permission notice (including the next 127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the 137ec681f3Smrg * Software. 147ec681f3Smrg * 157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 207ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 217ec681f3Smrg * DEALINGS IN THE SOFTWARE. 227ec681f3Smrg */ 237ec681f3Smrg 247ec681f3Smrg/** 257ec681f3Smrg * @file 267ec681f3Smrg * 277ec681f3Smrg * Implements wrappers of libc functions to fake having a DRM device that 287ec681f3Smrg * isn't actually present in the kernel. 297ec681f3Smrg */ 307ec681f3Smrg 317ec681f3Smrg/* Prevent glibc from defining open64 when we want to alias it. */ 327ec681f3Smrg#undef _FILE_OFFSET_BITS 337ec681f3Smrg#define _LARGEFILE64_SOURCE 347ec681f3Smrg 357ec681f3Smrg#include <stdbool.h> 367ec681f3Smrg#include <stdio.h> 377ec681f3Smrg#include <stdlib.h> 387ec681f3Smrg#include <string.h> 397ec681f3Smrg#include <unistd.h> 407ec681f3Smrg#include <sys/ioctl.h> 417ec681f3Smrg#include <sys/mman.h> 427ec681f3Smrg#include <sys/stat.h> 437ec681f3Smrg#include <sys/sysmacros.h> 447ec681f3Smrg#include <stdarg.h> 457ec681f3Smrg#include <fcntl.h> 467ec681f3Smrg#include <dlfcn.h> 477ec681f3Smrg#include <dirent.h> 487ec681f3Smrg#include <c11/threads.h> 497ec681f3Smrg#include <drm-uapi/drm.h> 507ec681f3Smrg 517ec681f3Smrg#include "util/set.h" 527ec681f3Smrg#include "util/u_debug.h" 537ec681f3Smrg#include "drm_shim.h" 547ec681f3Smrg 557ec681f3Smrg#define REAL_FUNCTION_POINTER(x) typeof(x) *real_##x 567ec681f3Smrg 577ec681f3Smrgstatic mtx_t shim_lock = _MTX_INITIALIZER_NP; 587ec681f3Smrgstruct set *opendir_set; 597ec681f3Smrgbool drm_shim_debug; 607ec681f3Smrg 617ec681f3Smrg/* If /dev/dri doesn't exist, we'll need an arbitrary pointer that wouldn't be 627ec681f3Smrg * returned by any other opendir() call so we can return just our fake node. 637ec681f3Smrg */ 647ec681f3SmrgDIR *fake_dev_dri = (void *)&opendir_set; 657ec681f3Smrg 667ec681f3Smrg/* XXX: implement REAL_FUNCTION_POINTER(close); */ 677ec681f3SmrgREAL_FUNCTION_POINTER(closedir); 687ec681f3SmrgREAL_FUNCTION_POINTER(dup); 697ec681f3SmrgREAL_FUNCTION_POINTER(fcntl); 707ec681f3SmrgREAL_FUNCTION_POINTER(fopen); 717ec681f3SmrgREAL_FUNCTION_POINTER(ioctl); 727ec681f3SmrgREAL_FUNCTION_POINTER(mmap); 737ec681f3SmrgREAL_FUNCTION_POINTER(open); 747ec681f3SmrgREAL_FUNCTION_POINTER(opendir); 757ec681f3SmrgREAL_FUNCTION_POINTER(readdir); 767ec681f3SmrgREAL_FUNCTION_POINTER(readdir64); 777ec681f3SmrgREAL_FUNCTION_POINTER(readlink); 787ec681f3SmrgREAL_FUNCTION_POINTER(realpath); 797ec681f3Smrg 807ec681f3Smrg#define HAS_XSTAT __GLIBC__ == 2 && __GLIBC_MINOR__ < 33 817ec681f3Smrg 827ec681f3Smrg#if HAS_XSTAT 837ec681f3SmrgREAL_FUNCTION_POINTER(__xstat); 847ec681f3SmrgREAL_FUNCTION_POINTER(__xstat64); 857ec681f3SmrgREAL_FUNCTION_POINTER(__fxstat); 867ec681f3SmrgREAL_FUNCTION_POINTER(__fxstat64); 877ec681f3Smrg#else 887ec681f3SmrgREAL_FUNCTION_POINTER(stat); 897ec681f3SmrgREAL_FUNCTION_POINTER(stat64); 907ec681f3SmrgREAL_FUNCTION_POINTER(fstat); 917ec681f3SmrgREAL_FUNCTION_POINTER(fstat64); 927ec681f3Smrg#endif 937ec681f3Smrg 947ec681f3Smrg/* Full path of /dev/dri/renderD* */ 957ec681f3Smrgstatic char *render_node_path; 967ec681f3Smrg/* renderD* */ 977ec681f3Smrgstatic char *render_node_dirent_name; 987ec681f3Smrg/* /sys/dev/char/major:minor/device */ 997ec681f3Smrgstatic char *device_path; 1007ec681f3Smrg/* /sys/dev/char/major:minor/device/subsystem */ 1017ec681f3Smrgstatic char *subsystem_path; 1027ec681f3Smrgint render_node_minor = -1; 1037ec681f3Smrg 1047ec681f3Smrgstruct file_override { 1057ec681f3Smrg const char *path; 1067ec681f3Smrg char *contents; 1077ec681f3Smrg}; 1087ec681f3Smrgstatic struct file_override file_overrides[10]; 1097ec681f3Smrgstatic int file_overrides_count; 1107ec681f3Smrgextern bool drm_shim_driver_prefers_first_render_node; 1117ec681f3Smrg 1127ec681f3Smrg#define nfasprintf(...) \ 1137ec681f3Smrg { \ 1147ec681f3Smrg UNUSED int __ret = asprintf(__VA_ARGS__); \ 1157ec681f3Smrg assert(__ret >= 0); \ 1167ec681f3Smrg } 1177ec681f3Smrg#define nfvasprintf(...) \ 1187ec681f3Smrg { \ 1197ec681f3Smrg UNUSED int __ret = vasprintf(__VA_ARGS__); \ 1207ec681f3Smrg assert(__ret >= 0); \ 1217ec681f3Smrg } 1227ec681f3Smrg 1237ec681f3Smrg/* Pick the minor and filename for our shimmed render node. This can be 1247ec681f3Smrg * either a new one that didn't exist on the system, or if the driver wants, 1257ec681f3Smrg * it can replace the first render node. 1267ec681f3Smrg */ 1277ec681f3Smrgstatic void 1287ec681f3Smrgget_dri_render_node_minor(void) 1297ec681f3Smrg{ 1307ec681f3Smrg for (int i = 0; i < 10; i++) { 1317ec681f3Smrg UNUSED int minor = 128 + i; 1327ec681f3Smrg nfasprintf(&render_node_dirent_name, "renderD%d", minor); 1337ec681f3Smrg nfasprintf(&render_node_path, "/dev/dri/%s", 1347ec681f3Smrg render_node_dirent_name); 1357ec681f3Smrg struct stat st; 1367ec681f3Smrg if (drm_shim_driver_prefers_first_render_node || 1377ec681f3Smrg stat(render_node_path, &st) == -1) { 1387ec681f3Smrg 1397ec681f3Smrg render_node_minor = minor; 1407ec681f3Smrg return; 1417ec681f3Smrg } 1427ec681f3Smrg } 1437ec681f3Smrg 1447ec681f3Smrg fprintf(stderr, "Couldn't find a spare render node slot\n"); 1457ec681f3Smrg} 1467ec681f3Smrg 1477ec681f3Smrgstatic void *get_function_pointer(const char *name) 1487ec681f3Smrg{ 1497ec681f3Smrg void *func = dlsym(RTLD_NEXT, name); 1507ec681f3Smrg if (!func) { 1517ec681f3Smrg fprintf(stderr, "Failed to resolve %s\n", name); 1527ec681f3Smrg abort(); 1537ec681f3Smrg } 1547ec681f3Smrg return func; 1557ec681f3Smrg} 1567ec681f3Smrg 1577ec681f3Smrg#define GET_FUNCTION_POINTER(x) real_##x = get_function_pointer(#x) 1587ec681f3Smrg 1597ec681f3Smrgvoid 1607ec681f3Smrgdrm_shim_override_file(const char *contents, const char *path_format, ...) 1617ec681f3Smrg{ 1627ec681f3Smrg assert(file_overrides_count < ARRAY_SIZE(file_overrides)); 1637ec681f3Smrg 1647ec681f3Smrg char *path; 1657ec681f3Smrg va_list ap; 1667ec681f3Smrg va_start(ap, path_format); 1677ec681f3Smrg nfvasprintf(&path, path_format, ap); 1687ec681f3Smrg va_end(ap); 1697ec681f3Smrg 1707ec681f3Smrg struct file_override *override = &file_overrides[file_overrides_count++]; 1717ec681f3Smrg override->path = path; 1727ec681f3Smrg override->contents = strdup(contents); 1737ec681f3Smrg} 1747ec681f3Smrg 1757ec681f3Smrgstatic void 1767ec681f3Smrgdestroy_shim(void) 1777ec681f3Smrg{ 1787ec681f3Smrg _mesa_set_destroy(opendir_set, NULL); 1797ec681f3Smrg free(render_node_path); 1807ec681f3Smrg free(render_node_dirent_name); 1817ec681f3Smrg free(subsystem_path); 1827ec681f3Smrg} 1837ec681f3Smrg 1847ec681f3Smrg/* Initialization, which will be called from the first general library call 1857ec681f3Smrg * that might need to be wrapped with the shim. 1867ec681f3Smrg */ 1877ec681f3Smrgstatic void 1887ec681f3Smrginit_shim(void) 1897ec681f3Smrg{ 1907ec681f3Smrg static bool inited = false; 1917ec681f3Smrg drm_shim_debug = debug_get_bool_option("DRM_SHIM_DEBUG", false); 1927ec681f3Smrg 1937ec681f3Smrg /* We can't lock this, because we recurse during initialization. */ 1947ec681f3Smrg if (inited) 1957ec681f3Smrg return; 1967ec681f3Smrg 1977ec681f3Smrg /* This comes first (and we're locked), to make sure we don't recurse 1987ec681f3Smrg * during initialization. 1997ec681f3Smrg */ 2007ec681f3Smrg inited = true; 2017ec681f3Smrg 2027ec681f3Smrg opendir_set = _mesa_set_create(NULL, 2037ec681f3Smrg _mesa_hash_string, 2047ec681f3Smrg _mesa_key_string_equal); 2057ec681f3Smrg 2067ec681f3Smrg GET_FUNCTION_POINTER(closedir); 2077ec681f3Smrg GET_FUNCTION_POINTER(dup); 2087ec681f3Smrg GET_FUNCTION_POINTER(fcntl); 2097ec681f3Smrg GET_FUNCTION_POINTER(fopen); 2107ec681f3Smrg GET_FUNCTION_POINTER(ioctl); 2117ec681f3Smrg GET_FUNCTION_POINTER(mmap); 2127ec681f3Smrg GET_FUNCTION_POINTER(open); 2137ec681f3Smrg GET_FUNCTION_POINTER(opendir); 2147ec681f3Smrg GET_FUNCTION_POINTER(readdir); 2157ec681f3Smrg GET_FUNCTION_POINTER(readdir64); 2167ec681f3Smrg GET_FUNCTION_POINTER(readlink); 2177ec681f3Smrg GET_FUNCTION_POINTER(realpath); 2187ec681f3Smrg 2197ec681f3Smrg#if HAS_XSTAT 2207ec681f3Smrg GET_FUNCTION_POINTER(__xstat); 2217ec681f3Smrg GET_FUNCTION_POINTER(__xstat64); 2227ec681f3Smrg GET_FUNCTION_POINTER(__fxstat); 2237ec681f3Smrg GET_FUNCTION_POINTER(__fxstat64); 2247ec681f3Smrg#else 2257ec681f3Smrg GET_FUNCTION_POINTER(stat); 2267ec681f3Smrg GET_FUNCTION_POINTER(stat64); 2277ec681f3Smrg GET_FUNCTION_POINTER(fstat); 2287ec681f3Smrg GET_FUNCTION_POINTER(fstat64); 2297ec681f3Smrg#endif 2307ec681f3Smrg 2317ec681f3Smrg get_dri_render_node_minor(); 2327ec681f3Smrg 2337ec681f3Smrg if (drm_shim_debug) { 2347ec681f3Smrg fprintf(stderr, "Initializing DRM shim on %s\n", 2357ec681f3Smrg render_node_path); 2367ec681f3Smrg } 2377ec681f3Smrg 2387ec681f3Smrg nfasprintf(&device_path, 2397ec681f3Smrg "/sys/dev/char/%d:%d/device", 2407ec681f3Smrg DRM_MAJOR, render_node_minor); 2417ec681f3Smrg 2427ec681f3Smrg nfasprintf(&subsystem_path, 2437ec681f3Smrg "/sys/dev/char/%d:%d/device/subsystem", 2447ec681f3Smrg DRM_MAJOR, render_node_minor); 2457ec681f3Smrg 2467ec681f3Smrg drm_shim_device_init(); 2477ec681f3Smrg 2487ec681f3Smrg atexit(destroy_shim); 2497ec681f3Smrg} 2507ec681f3Smrg 2517ec681f3Smrg/* Override libdrm's reading of various sysfs files for device enumeration. */ 2527ec681f3SmrgPUBLIC FILE *fopen(const char *path, const char *mode) 2537ec681f3Smrg{ 2547ec681f3Smrg init_shim(); 2557ec681f3Smrg 2567ec681f3Smrg for (int i = 0; i < file_overrides_count; i++) { 2577ec681f3Smrg if (strcmp(file_overrides[i].path, path) == 0) { 2587ec681f3Smrg int fds[2]; 2597ec681f3Smrg pipe(fds); 2607ec681f3Smrg write(fds[1], file_overrides[i].contents, 2617ec681f3Smrg strlen(file_overrides[i].contents)); 2627ec681f3Smrg close(fds[1]); 2637ec681f3Smrg return fdopen(fds[0], "r"); 2647ec681f3Smrg } 2657ec681f3Smrg } 2667ec681f3Smrg 2677ec681f3Smrg return real_fopen(path, mode); 2687ec681f3Smrg} 2697ec681f3SmrgPUBLIC FILE *fopen64(const char *path, const char *mode) 2707ec681f3Smrg __attribute__((alias("fopen"))); 2717ec681f3Smrg 2727ec681f3Smrg/* Intercepts open(render_node_path) to redirect it to the simulator. */ 2737ec681f3SmrgPUBLIC int open(const char *path, int flags, ...) 2747ec681f3Smrg{ 2757ec681f3Smrg init_shim(); 2767ec681f3Smrg 2777ec681f3Smrg va_list ap; 2787ec681f3Smrg va_start(ap, flags); 2797ec681f3Smrg mode_t mode = va_arg(ap, mode_t); 2807ec681f3Smrg va_end(ap); 2817ec681f3Smrg 2827ec681f3Smrg if (strcmp(path, render_node_path) != 0) 2837ec681f3Smrg return real_open(path, flags, mode); 2847ec681f3Smrg 2857ec681f3Smrg int fd = real_open("/dev/null", O_RDWR, 0); 2867ec681f3Smrg 2877ec681f3Smrg drm_shim_fd_register(fd, NULL); 2887ec681f3Smrg 2897ec681f3Smrg return fd; 2907ec681f3Smrg} 2917ec681f3SmrgPUBLIC int open64(const char*, int, ...) __attribute__((alias("open"))); 2927ec681f3Smrg 2937ec681f3Smrg#if HAS_XSTAT 2947ec681f3Smrg/* Fakes stat to return character device stuff for our fake render node. */ 2957ec681f3SmrgPUBLIC int __xstat(int ver, const char *path, struct stat *st) 2967ec681f3Smrg{ 2977ec681f3Smrg init_shim(); 2987ec681f3Smrg 2997ec681f3Smrg /* Note: call real stat if we're in the process of probing for a free 3007ec681f3Smrg * render node! 3017ec681f3Smrg */ 3027ec681f3Smrg if (render_node_minor == -1) 3037ec681f3Smrg return real___xstat(ver, path, st); 3047ec681f3Smrg 3057ec681f3Smrg /* Fool libdrm's probe of whether the /sys dir for this char dev is 3067ec681f3Smrg * there. 3077ec681f3Smrg */ 3087ec681f3Smrg char *sys_dev_drm_dir; 3097ec681f3Smrg nfasprintf(&sys_dev_drm_dir, 3107ec681f3Smrg "/sys/dev/char/%d:%d/device/drm", 3117ec681f3Smrg DRM_MAJOR, render_node_minor); 3127ec681f3Smrg if (strcmp(path, sys_dev_drm_dir) == 0) { 3137ec681f3Smrg free(sys_dev_drm_dir); 3147ec681f3Smrg return 0; 3157ec681f3Smrg } 3167ec681f3Smrg free(sys_dev_drm_dir); 3177ec681f3Smrg 3187ec681f3Smrg if (strcmp(path, render_node_path) != 0) 3197ec681f3Smrg return real___xstat(ver, path, st); 3207ec681f3Smrg 3217ec681f3Smrg memset(st, 0, sizeof(*st)); 3227ec681f3Smrg st->st_rdev = makedev(DRM_MAJOR, render_node_minor); 3237ec681f3Smrg st->st_mode = S_IFCHR; 3247ec681f3Smrg 3257ec681f3Smrg return 0; 3267ec681f3Smrg} 3277ec681f3Smrg 3287ec681f3Smrg/* Fakes stat to return character device stuff for our fake render node. */ 3297ec681f3SmrgPUBLIC int __xstat64(int ver, const char *path, struct stat64 *st) 3307ec681f3Smrg{ 3317ec681f3Smrg init_shim(); 3327ec681f3Smrg 3337ec681f3Smrg /* Note: call real stat if we're in the process of probing for a free 3347ec681f3Smrg * render node! 3357ec681f3Smrg */ 3367ec681f3Smrg if (render_node_minor == -1) 3377ec681f3Smrg return real___xstat64(ver, path, st); 3387ec681f3Smrg 3397ec681f3Smrg /* Fool libdrm's probe of whether the /sys dir for this char dev is 3407ec681f3Smrg * there. 3417ec681f3Smrg */ 3427ec681f3Smrg char *sys_dev_drm_dir; 3437ec681f3Smrg nfasprintf(&sys_dev_drm_dir, 3447ec681f3Smrg "/sys/dev/char/%d:%d/device/drm", 3457ec681f3Smrg DRM_MAJOR, render_node_minor); 3467ec681f3Smrg if (strcmp(path, sys_dev_drm_dir) == 0) { 3477ec681f3Smrg free(sys_dev_drm_dir); 3487ec681f3Smrg return 0; 3497ec681f3Smrg } 3507ec681f3Smrg free(sys_dev_drm_dir); 3517ec681f3Smrg 3527ec681f3Smrg if (strcmp(path, render_node_path) != 0) 3537ec681f3Smrg return real___xstat64(ver, path, st); 3547ec681f3Smrg 3557ec681f3Smrg memset(st, 0, sizeof(*st)); 3567ec681f3Smrg st->st_rdev = makedev(DRM_MAJOR, render_node_minor); 3577ec681f3Smrg st->st_mode = S_IFCHR; 3587ec681f3Smrg 3597ec681f3Smrg return 0; 3607ec681f3Smrg} 3617ec681f3Smrg 3627ec681f3Smrg/* Fakes fstat to return character device stuff for our fake render node. */ 3637ec681f3SmrgPUBLIC int __fxstat(int ver, int fd, struct stat *st) 3647ec681f3Smrg{ 3657ec681f3Smrg init_shim(); 3667ec681f3Smrg 3677ec681f3Smrg struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 3687ec681f3Smrg 3697ec681f3Smrg if (!shim_fd) 3707ec681f3Smrg return real___fxstat(ver, fd, st); 3717ec681f3Smrg 3727ec681f3Smrg memset(st, 0, sizeof(*st)); 3737ec681f3Smrg st->st_rdev = makedev(DRM_MAJOR, render_node_minor); 3747ec681f3Smrg st->st_mode = S_IFCHR; 3757ec681f3Smrg 3767ec681f3Smrg return 0; 3777ec681f3Smrg} 3787ec681f3Smrg 3797ec681f3SmrgPUBLIC int __fxstat64(int ver, int fd, struct stat64 *st) 3807ec681f3Smrg{ 3817ec681f3Smrg init_shim(); 3827ec681f3Smrg 3837ec681f3Smrg struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 3847ec681f3Smrg 3857ec681f3Smrg if (!shim_fd) 3867ec681f3Smrg return real___fxstat64(ver, fd, st); 3877ec681f3Smrg 3887ec681f3Smrg memset(st, 0, sizeof(*st)); 3897ec681f3Smrg st->st_rdev = makedev(DRM_MAJOR, render_node_minor); 3907ec681f3Smrg st->st_mode = S_IFCHR; 3917ec681f3Smrg 3927ec681f3Smrg return 0; 3937ec681f3Smrg} 3947ec681f3Smrg 3957ec681f3Smrg#else 3967ec681f3Smrg 3977ec681f3SmrgPUBLIC int stat(const char* path, struct stat* stat_buf) 3987ec681f3Smrg{ 3997ec681f3Smrg init_shim(); 4007ec681f3Smrg 4017ec681f3Smrg /* Note: call real stat if we're in the process of probing for a free 4027ec681f3Smrg * render node! 4037ec681f3Smrg */ 4047ec681f3Smrg if (render_node_minor == -1) 4057ec681f3Smrg return real_stat(path, stat_buf); 4067ec681f3Smrg 4077ec681f3Smrg /* Fool libdrm's probe of whether the /sys dir for this char dev is 4087ec681f3Smrg * there. 4097ec681f3Smrg */ 4107ec681f3Smrg char *sys_dev_drm_dir; 4117ec681f3Smrg nfasprintf(&sys_dev_drm_dir, 4127ec681f3Smrg "/sys/dev/char/%d:%d/device/drm", 4137ec681f3Smrg DRM_MAJOR, render_node_minor); 4147ec681f3Smrg if (strcmp(path, sys_dev_drm_dir) == 0) { 4157ec681f3Smrg free(sys_dev_drm_dir); 4167ec681f3Smrg return 0; 4177ec681f3Smrg } 4187ec681f3Smrg free(sys_dev_drm_dir); 4197ec681f3Smrg 4207ec681f3Smrg if (strcmp(path, render_node_path) != 0) 4217ec681f3Smrg return real_stat(path, stat_buf); 4227ec681f3Smrg 4237ec681f3Smrg memset(stat_buf, 0, sizeof(*stat_buf)); 4247ec681f3Smrg stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor); 4257ec681f3Smrg stat_buf->st_mode = S_IFCHR; 4267ec681f3Smrg 4277ec681f3Smrg return 0; 4287ec681f3Smrg} 4297ec681f3Smrg 4307ec681f3SmrgPUBLIC int stat64(const char* path, struct stat64* stat_buf) 4317ec681f3Smrg{ 4327ec681f3Smrg init_shim(); 4337ec681f3Smrg 4347ec681f3Smrg /* Note: call real stat if we're in the process of probing for a free 4357ec681f3Smrg * render node! 4367ec681f3Smrg */ 4377ec681f3Smrg if (render_node_minor == -1) 4387ec681f3Smrg return real_stat64(path, stat_buf); 4397ec681f3Smrg 4407ec681f3Smrg /* Fool libdrm's probe of whether the /sys dir for this char dev is 4417ec681f3Smrg * there. 4427ec681f3Smrg */ 4437ec681f3Smrg char *sys_dev_drm_dir; 4447ec681f3Smrg nfasprintf(&sys_dev_drm_dir, 4457ec681f3Smrg "/sys/dev/char/%d:%d/device/drm", 4467ec681f3Smrg DRM_MAJOR, render_node_minor); 4477ec681f3Smrg if (strcmp(path, sys_dev_drm_dir) == 0) { 4487ec681f3Smrg free(sys_dev_drm_dir); 4497ec681f3Smrg return 0; 4507ec681f3Smrg } 4517ec681f3Smrg free(sys_dev_drm_dir); 4527ec681f3Smrg 4537ec681f3Smrg if (strcmp(path, render_node_path) != 0) 4547ec681f3Smrg return real_stat64(path, stat_buf); 4557ec681f3Smrg 4567ec681f3Smrg memset(stat_buf, 0, sizeof(*stat_buf)); 4577ec681f3Smrg stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor); 4587ec681f3Smrg stat_buf->st_mode = S_IFCHR; 4597ec681f3Smrg 4607ec681f3Smrg return 0; 4617ec681f3Smrg} 4627ec681f3Smrg 4637ec681f3SmrgPUBLIC int fstat(int fd, struct stat* stat_buf) 4647ec681f3Smrg{ 4657ec681f3Smrg init_shim(); 4667ec681f3Smrg 4677ec681f3Smrg struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 4687ec681f3Smrg 4697ec681f3Smrg if (!shim_fd) 4707ec681f3Smrg return real_fstat(fd, stat_buf); 4717ec681f3Smrg 4727ec681f3Smrg memset(stat_buf, 0, sizeof(*stat_buf)); 4737ec681f3Smrg stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor); 4747ec681f3Smrg stat_buf->st_mode = S_IFCHR; 4757ec681f3Smrg 4767ec681f3Smrg return 0; 4777ec681f3Smrg} 4787ec681f3Smrg 4797ec681f3SmrgPUBLIC int fstat64(int fd, struct stat64* stat_buf) 4807ec681f3Smrg{ 4817ec681f3Smrg init_shim(); 4827ec681f3Smrg 4837ec681f3Smrg struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 4847ec681f3Smrg 4857ec681f3Smrg if (!shim_fd) 4867ec681f3Smrg return real_fstat64(fd, stat_buf); 4877ec681f3Smrg 4887ec681f3Smrg memset(stat_buf, 0, sizeof(*stat_buf)); 4897ec681f3Smrg stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor); 4907ec681f3Smrg stat_buf->st_mode = S_IFCHR; 4917ec681f3Smrg 4927ec681f3Smrg return 0; 4937ec681f3Smrg} 4947ec681f3Smrg#endif 4957ec681f3Smrg 4967ec681f3Smrg/* Tracks if the opendir was on /dev/dri. */ 4977ec681f3SmrgPUBLIC DIR * 4987ec681f3Smrgopendir(const char *name) 4997ec681f3Smrg{ 5007ec681f3Smrg init_shim(); 5017ec681f3Smrg 5027ec681f3Smrg DIR *dir = real_opendir(name); 5037ec681f3Smrg if (strcmp(name, "/dev/dri") == 0) { 5047ec681f3Smrg if (!dir) { 5057ec681f3Smrg /* If /dev/dri didn't exist, we still want to be able to return our 5067ec681f3Smrg * fake /dev/dri/render* even though we probably can't 5077ec681f3Smrg * mkdir("/dev/dri"). Return a fake DIR pointer for that. 5087ec681f3Smrg */ 5097ec681f3Smrg dir = fake_dev_dri; 5107ec681f3Smrg } 5117ec681f3Smrg 5127ec681f3Smrg mtx_lock(&shim_lock); 5137ec681f3Smrg _mesa_set_add(opendir_set, dir); 5147ec681f3Smrg mtx_unlock(&shim_lock); 5157ec681f3Smrg } 5167ec681f3Smrg 5177ec681f3Smrg return dir; 5187ec681f3Smrg} 5197ec681f3Smrg 5207ec681f3Smrg/* If we've reached the end of the real directory list and we're 5217ec681f3Smrg * looking at /dev/dri, add our render node to the list. 5227ec681f3Smrg */ 5237ec681f3SmrgPUBLIC struct dirent * 5247ec681f3Smrgreaddir(DIR *dir) 5257ec681f3Smrg{ 5267ec681f3Smrg init_shim(); 5277ec681f3Smrg 5287ec681f3Smrg struct dirent *ent = NULL; 5297ec681f3Smrg 5307ec681f3Smrg if (dir != fake_dev_dri) 5317ec681f3Smrg ent = real_readdir(dir); 5327ec681f3Smrg static struct dirent render_node_dirent = { 0 }; 5337ec681f3Smrg 5347ec681f3Smrg if (!ent) { 5357ec681f3Smrg mtx_lock(&shim_lock); 5367ec681f3Smrg if (_mesa_set_search(opendir_set, dir)) { 5377ec681f3Smrg strcpy(render_node_dirent.d_name, 5387ec681f3Smrg render_node_dirent_name); 5397ec681f3Smrg ent = &render_node_dirent; 5407ec681f3Smrg _mesa_set_remove_key(opendir_set, dir); 5417ec681f3Smrg } 5427ec681f3Smrg mtx_unlock(&shim_lock); 5437ec681f3Smrg } 5447ec681f3Smrg 5457ec681f3Smrg return ent; 5467ec681f3Smrg} 5477ec681f3Smrg 5487ec681f3Smrg/* If we've reached the end of the real directory list and we're 5497ec681f3Smrg * looking at /dev/dri, add our render node to the list. 5507ec681f3Smrg */ 5517ec681f3SmrgPUBLIC struct dirent64 * 5527ec681f3Smrgreaddir64(DIR *dir) 5537ec681f3Smrg{ 5547ec681f3Smrg init_shim(); 5557ec681f3Smrg 5567ec681f3Smrg struct dirent64 *ent = NULL; 5577ec681f3Smrg if (dir != fake_dev_dri) 5587ec681f3Smrg ent = real_readdir64(dir); 5597ec681f3Smrg static struct dirent64 render_node_dirent = { 0 }; 5607ec681f3Smrg 5617ec681f3Smrg if (!ent) { 5627ec681f3Smrg mtx_lock(&shim_lock); 5637ec681f3Smrg if (_mesa_set_search(opendir_set, dir)) { 5647ec681f3Smrg strcpy(render_node_dirent.d_name, 5657ec681f3Smrg render_node_dirent_name); 5667ec681f3Smrg ent = &render_node_dirent; 5677ec681f3Smrg _mesa_set_remove_key(opendir_set, dir); 5687ec681f3Smrg } 5697ec681f3Smrg mtx_unlock(&shim_lock); 5707ec681f3Smrg } 5717ec681f3Smrg 5727ec681f3Smrg return ent; 5737ec681f3Smrg} 5747ec681f3Smrg 5757ec681f3Smrg/* Cleans up tracking of opendir("/dev/dri") */ 5767ec681f3SmrgPUBLIC int 5777ec681f3Smrgclosedir(DIR *dir) 5787ec681f3Smrg{ 5797ec681f3Smrg init_shim(); 5807ec681f3Smrg 5817ec681f3Smrg mtx_lock(&shim_lock); 5827ec681f3Smrg _mesa_set_remove_key(opendir_set, dir); 5837ec681f3Smrg mtx_unlock(&shim_lock); 5847ec681f3Smrg 5857ec681f3Smrg if (dir != fake_dev_dri) 5867ec681f3Smrg return real_closedir(dir); 5877ec681f3Smrg else 5887ec681f3Smrg return 0; 5897ec681f3Smrg} 5907ec681f3Smrg 5917ec681f3Smrg/* Handles libdrm's readlink to figure out what kind of device we have. */ 5927ec681f3SmrgPUBLIC ssize_t 5937ec681f3Smrgreadlink(const char *path, char *buf, size_t size) 5947ec681f3Smrg{ 5957ec681f3Smrg init_shim(); 5967ec681f3Smrg 5977ec681f3Smrg if (strcmp(path, subsystem_path) != 0) 5987ec681f3Smrg return real_readlink(path, buf, size); 5997ec681f3Smrg 6007ec681f3Smrg static const struct { 6017ec681f3Smrg const char *name; 6027ec681f3Smrg int bus_type; 6037ec681f3Smrg } bus_types[] = { 6047ec681f3Smrg { "/pci", DRM_BUS_PCI }, 6057ec681f3Smrg { "/usb", DRM_BUS_USB }, 6067ec681f3Smrg { "/platform", DRM_BUS_PLATFORM }, 6077ec681f3Smrg { "/spi", DRM_BUS_PLATFORM }, 6087ec681f3Smrg { "/host1x", DRM_BUS_HOST1X }, 6097ec681f3Smrg }; 6107ec681f3Smrg 6117ec681f3Smrg for (uint32_t i = 0; i < ARRAY_SIZE(bus_types); i++) { 6127ec681f3Smrg if (bus_types[i].bus_type != shim_device.bus_type) 6137ec681f3Smrg continue; 6147ec681f3Smrg 6157ec681f3Smrg strncpy(buf, bus_types[i].name, size); 6167ec681f3Smrg buf[size - 1] = 0; 6177ec681f3Smrg break; 6187ec681f3Smrg } 6197ec681f3Smrg 6207ec681f3Smrg return strlen(buf) + 1; 6217ec681f3Smrg} 6227ec681f3Smrg 6237ec681f3Smrg/* Handles libdrm's realpath to figure out what kind of device we have. */ 6247ec681f3SmrgPUBLIC char * 6257ec681f3Smrgrealpath(const char *path, char *resolved_path) 6267ec681f3Smrg{ 6277ec681f3Smrg init_shim(); 6287ec681f3Smrg 6297ec681f3Smrg if (strcmp(path, device_path) != 0) 6307ec681f3Smrg return real_realpath(path, resolved_path); 6317ec681f3Smrg 6327ec681f3Smrg strcpy(resolved_path, path); 6337ec681f3Smrg 6347ec681f3Smrg return resolved_path; 6357ec681f3Smrg} 6367ec681f3Smrg 6377ec681f3Smrg/* Main entrypoint to DRM drivers: the ioctl syscall. We send all ioctls on 6387ec681f3Smrg * our DRM fd to drm_shim_ioctl(). 6397ec681f3Smrg */ 6407ec681f3SmrgPUBLIC int 6417ec681f3Smrgioctl(int fd, unsigned long request, ...) 6427ec681f3Smrg{ 6437ec681f3Smrg init_shim(); 6447ec681f3Smrg 6457ec681f3Smrg va_list ap; 6467ec681f3Smrg va_start(ap, request); 6477ec681f3Smrg void *arg = va_arg(ap, void *); 6487ec681f3Smrg va_end(ap); 6497ec681f3Smrg 6507ec681f3Smrg struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 6517ec681f3Smrg if (!shim_fd) 6527ec681f3Smrg return real_ioctl(fd, request, arg); 6537ec681f3Smrg 6547ec681f3Smrg return drm_shim_ioctl(fd, request, arg); 6557ec681f3Smrg} 6567ec681f3Smrg 6577ec681f3Smrg/* Gallium uses this to dup the incoming fd on gbm screen creation */ 6587ec681f3SmrgPUBLIC int 6597ec681f3Smrgfcntl(int fd, int cmd, ...) 6607ec681f3Smrg{ 6617ec681f3Smrg init_shim(); 6627ec681f3Smrg 6637ec681f3Smrg struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 6647ec681f3Smrg 6657ec681f3Smrg va_list ap; 6667ec681f3Smrg va_start(ap, cmd); 6677ec681f3Smrg void *arg = va_arg(ap, void *); 6687ec681f3Smrg va_end(ap); 6697ec681f3Smrg 6707ec681f3Smrg int ret = real_fcntl(fd, cmd, arg); 6717ec681f3Smrg 6727ec681f3Smrg if (shim_fd && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC)) 6737ec681f3Smrg drm_shim_fd_register(ret, shim_fd); 6747ec681f3Smrg 6757ec681f3Smrg return ret; 6767ec681f3Smrg} 6777ec681f3SmrgPUBLIC int fcntl64(int, int, ...) 6787ec681f3Smrg __attribute__((alias("fcntl"))); 6797ec681f3Smrg 6807ec681f3Smrg/* I wrote this when trying to fix gallium screen creation, leaving it around 6817ec681f3Smrg * since it's probably good to have. 6827ec681f3Smrg */ 6837ec681f3SmrgPUBLIC int 6847ec681f3Smrgdup(int fd) 6857ec681f3Smrg{ 6867ec681f3Smrg init_shim(); 6877ec681f3Smrg 6887ec681f3Smrg int ret = real_dup(fd); 6897ec681f3Smrg 6907ec681f3Smrg struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 6917ec681f3Smrg if (shim_fd && ret >= 0) 6927ec681f3Smrg drm_shim_fd_register(ret, shim_fd); 6937ec681f3Smrg 6947ec681f3Smrg return ret; 6957ec681f3Smrg} 6967ec681f3Smrg 6977ec681f3SmrgPUBLIC void * 6987ec681f3Smrgmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) 6997ec681f3Smrg{ 7007ec681f3Smrg init_shim(); 7017ec681f3Smrg 7027ec681f3Smrg struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 7037ec681f3Smrg if (shim_fd) 7047ec681f3Smrg return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset); 7057ec681f3Smrg 7067ec681f3Smrg return real_mmap(addr, length, prot, flags, fd, offset); 7077ec681f3Smrg} 7087ec681f3SmrgPUBLIC void *mmap64(void*, size_t, int, int, int, off_t) 7097ec681f3Smrg __attribute__((alias("mmap"))); 710