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