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 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 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 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 #if defined(HAVE_NOATEXIT) 176 static void __attribute__((__destructor__)) 177 #else 178 static void 179 #endif 180 destroy_shim(void) 181 { 182 _mesa_set_destroy(opendir_set, NULL); 183 free(render_node_path); 184 free(render_node_dirent_name); 185 free(subsystem_path); 186 } 187 188 /* Initialization, which will be called from the first general library call 189 * that might need to be wrapped with the shim. 190 */ 191 static void 192 init_shim(void) 193 { 194 static bool inited = false; 195 drm_shim_debug = debug_get_bool_option("DRM_SHIM_DEBUG", false); 196 197 /* We can't lock this, because we recurse during initialization. */ 198 if (inited) 199 return; 200 201 /* This comes first (and we're locked), to make sure we don't recurse 202 * during initialization. 203 */ 204 inited = true; 205 206 opendir_set = _mesa_set_create(NULL, 207 _mesa_hash_string, 208 _mesa_key_string_equal); 209 210 GET_FUNCTION_POINTER(closedir); 211 GET_FUNCTION_POINTER(dup); 212 GET_FUNCTION_POINTER(fcntl); 213 GET_FUNCTION_POINTER(fopen); 214 GET_FUNCTION_POINTER(ioctl); 215 GET_FUNCTION_POINTER(mmap); 216 GET_FUNCTION_POINTER(open); 217 GET_FUNCTION_POINTER(opendir); 218 GET_FUNCTION_POINTER(readdir); 219 GET_FUNCTION_POINTER(readdir64); 220 GET_FUNCTION_POINTER(readlink); 221 GET_FUNCTION_POINTER(realpath); 222 223 #if HAS_XSTAT 224 GET_FUNCTION_POINTER(__xstat); 225 GET_FUNCTION_POINTER(__xstat64); 226 GET_FUNCTION_POINTER(__fxstat); 227 GET_FUNCTION_POINTER(__fxstat64); 228 #else 229 GET_FUNCTION_POINTER(stat); 230 GET_FUNCTION_POINTER(stat64); 231 GET_FUNCTION_POINTER(fstat); 232 GET_FUNCTION_POINTER(fstat64); 233 #endif 234 235 get_dri_render_node_minor(); 236 237 if (drm_shim_debug) { 238 fprintf(stderr, "Initializing DRM shim on %s\n", 239 render_node_path); 240 } 241 242 nfasprintf(&device_path, 243 "/sys/dev/char/%d:%d/device", 244 DRM_MAJOR, render_node_minor); 245 246 nfasprintf(&subsystem_path, 247 "/sys/dev/char/%d:%d/device/subsystem", 248 DRM_MAJOR, render_node_minor); 249 250 drm_shim_device_init(); 251 252 #if !defined(HAVE_NOATEXIT) 253 atexit(destroy_shim); 254 #endif 255 } 256 257 /* Override libdrm's reading of various sysfs files for device enumeration. */ 258 PUBLIC FILE *fopen(const char *path, const char *mode) 259 { 260 init_shim(); 261 262 for (int i = 0; i < file_overrides_count; i++) { 263 if (strcmp(file_overrides[i].path, path) == 0) { 264 int fds[2]; 265 pipe(fds); 266 write(fds[1], file_overrides[i].contents, 267 strlen(file_overrides[i].contents)); 268 close(fds[1]); 269 return fdopen(fds[0], "r"); 270 } 271 } 272 273 return real_fopen(path, mode); 274 } 275 PUBLIC FILE *fopen64(const char *path, const char *mode) 276 __attribute__((alias("fopen"))); 277 278 /* Intercepts open(render_node_path) to redirect it to the simulator. */ 279 PUBLIC int open(const char *path, int flags, ...) 280 { 281 init_shim(); 282 283 va_list ap; 284 va_start(ap, flags); 285 mode_t mode = va_arg(ap, mode_t); 286 va_end(ap); 287 288 if (strcmp(path, render_node_path) != 0) 289 return real_open(path, flags, mode); 290 291 int fd = real_open("/dev/null", O_RDWR, 0); 292 293 drm_shim_fd_register(fd, NULL); 294 295 return fd; 296 } 297 PUBLIC int open64(const char*, int, ...) __attribute__((alias("open"))); 298 299 #if HAS_XSTAT 300 /* Fakes stat to return character device stuff for our fake render node. */ 301 PUBLIC int __xstat(int ver, const char *path, struct stat *st) 302 { 303 init_shim(); 304 305 /* Note: call real stat if we're in the process of probing for a free 306 * render node! 307 */ 308 if (render_node_minor == -1) 309 return real___xstat(ver, path, st); 310 311 /* Fool libdrm's probe of whether the /sys dir for this char dev is 312 * there. 313 */ 314 char *sys_dev_drm_dir; 315 nfasprintf(&sys_dev_drm_dir, 316 "/sys/dev/char/%d:%d/device/drm", 317 DRM_MAJOR, render_node_minor); 318 if (strcmp(path, sys_dev_drm_dir) == 0) { 319 free(sys_dev_drm_dir); 320 return 0; 321 } 322 free(sys_dev_drm_dir); 323 324 if (strcmp(path, render_node_path) != 0) 325 return real___xstat(ver, path, st); 326 327 memset(st, 0, sizeof(*st)); 328 st->st_rdev = makedev(DRM_MAJOR, render_node_minor); 329 st->st_mode = S_IFCHR; 330 331 return 0; 332 } 333 334 /* Fakes stat to return character device stuff for our fake render node. */ 335 PUBLIC int __xstat64(int ver, const char *path, struct stat64 *st) 336 { 337 init_shim(); 338 339 /* Note: call real stat if we're in the process of probing for a free 340 * render node! 341 */ 342 if (render_node_minor == -1) 343 return real___xstat64(ver, path, st); 344 345 /* Fool libdrm's probe of whether the /sys dir for this char dev is 346 * there. 347 */ 348 char *sys_dev_drm_dir; 349 nfasprintf(&sys_dev_drm_dir, 350 "/sys/dev/char/%d:%d/device/drm", 351 DRM_MAJOR, render_node_minor); 352 if (strcmp(path, sys_dev_drm_dir) == 0) { 353 free(sys_dev_drm_dir); 354 return 0; 355 } 356 free(sys_dev_drm_dir); 357 358 if (strcmp(path, render_node_path) != 0) 359 return real___xstat64(ver, path, st); 360 361 memset(st, 0, sizeof(*st)); 362 st->st_rdev = makedev(DRM_MAJOR, render_node_minor); 363 st->st_mode = S_IFCHR; 364 365 return 0; 366 } 367 368 /* Fakes fstat to return character device stuff for our fake render node. */ 369 PUBLIC int __fxstat(int ver, int fd, struct stat *st) 370 { 371 init_shim(); 372 373 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 374 375 if (!shim_fd) 376 return real___fxstat(ver, fd, st); 377 378 memset(st, 0, sizeof(*st)); 379 st->st_rdev = makedev(DRM_MAJOR, render_node_minor); 380 st->st_mode = S_IFCHR; 381 382 return 0; 383 } 384 385 PUBLIC int __fxstat64(int ver, int fd, struct stat64 *st) 386 { 387 init_shim(); 388 389 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 390 391 if (!shim_fd) 392 return real___fxstat64(ver, fd, st); 393 394 memset(st, 0, sizeof(*st)); 395 st->st_rdev = makedev(DRM_MAJOR, render_node_minor); 396 st->st_mode = S_IFCHR; 397 398 return 0; 399 } 400 401 #else 402 403 PUBLIC int stat(const char* path, struct stat* stat_buf) 404 { 405 init_shim(); 406 407 /* Note: call real stat if we're in the process of probing for a free 408 * render node! 409 */ 410 if (render_node_minor == -1) 411 return real_stat(path, stat_buf); 412 413 /* Fool libdrm's probe of whether the /sys dir for this char dev is 414 * there. 415 */ 416 char *sys_dev_drm_dir; 417 nfasprintf(&sys_dev_drm_dir, 418 "/sys/dev/char/%d:%d/device/drm", 419 DRM_MAJOR, render_node_minor); 420 if (strcmp(path, sys_dev_drm_dir) == 0) { 421 free(sys_dev_drm_dir); 422 return 0; 423 } 424 free(sys_dev_drm_dir); 425 426 if (strcmp(path, render_node_path) != 0) 427 return real_stat(path, stat_buf); 428 429 memset(stat_buf, 0, sizeof(*stat_buf)); 430 stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor); 431 stat_buf->st_mode = S_IFCHR; 432 433 return 0; 434 } 435 436 PUBLIC int stat64(const char* path, struct stat64* stat_buf) 437 { 438 init_shim(); 439 440 /* Note: call real stat if we're in the process of probing for a free 441 * render node! 442 */ 443 if (render_node_minor == -1) 444 return real_stat64(path, stat_buf); 445 446 /* Fool libdrm's probe of whether the /sys dir for this char dev is 447 * there. 448 */ 449 char *sys_dev_drm_dir; 450 nfasprintf(&sys_dev_drm_dir, 451 "/sys/dev/char/%d:%d/device/drm", 452 DRM_MAJOR, render_node_minor); 453 if (strcmp(path, sys_dev_drm_dir) == 0) { 454 free(sys_dev_drm_dir); 455 return 0; 456 } 457 free(sys_dev_drm_dir); 458 459 if (strcmp(path, render_node_path) != 0) 460 return real_stat64(path, stat_buf); 461 462 memset(stat_buf, 0, sizeof(*stat_buf)); 463 stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor); 464 stat_buf->st_mode = S_IFCHR; 465 466 return 0; 467 } 468 469 PUBLIC int fstat(int fd, struct stat* stat_buf) 470 { 471 init_shim(); 472 473 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 474 475 if (!shim_fd) 476 return real_fstat(fd, stat_buf); 477 478 memset(stat_buf, 0, sizeof(*stat_buf)); 479 stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor); 480 stat_buf->st_mode = S_IFCHR; 481 482 return 0; 483 } 484 485 PUBLIC int fstat64(int fd, struct stat64* stat_buf) 486 { 487 init_shim(); 488 489 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 490 491 if (!shim_fd) 492 return real_fstat64(fd, stat_buf); 493 494 memset(stat_buf, 0, sizeof(*stat_buf)); 495 stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor); 496 stat_buf->st_mode = S_IFCHR; 497 498 return 0; 499 } 500 #endif 501 502 /* Tracks if the opendir was on /dev/dri. */ 503 PUBLIC DIR * 504 opendir(const char *name) 505 { 506 init_shim(); 507 508 DIR *dir = real_opendir(name); 509 if (strcmp(name, "/dev/dri") == 0) { 510 if (!dir) { 511 /* If /dev/dri didn't exist, we still want to be able to return our 512 * fake /dev/dri/render* even though we probably can't 513 * mkdir("/dev/dri"). Return a fake DIR pointer for that. 514 */ 515 dir = fake_dev_dri; 516 } 517 518 mtx_lock(&shim_lock); 519 _mesa_set_add(opendir_set, dir); 520 mtx_unlock(&shim_lock); 521 } 522 523 return dir; 524 } 525 526 /* If we've reached the end of the real directory list and we're 527 * looking at /dev/dri, add our render node to the list. 528 */ 529 PUBLIC struct dirent * 530 readdir(DIR *dir) 531 { 532 init_shim(); 533 534 struct dirent *ent = NULL; 535 536 if (dir != fake_dev_dri) 537 ent = real_readdir(dir); 538 static struct dirent render_node_dirent = { 0 }; 539 540 if (!ent) { 541 mtx_lock(&shim_lock); 542 if (_mesa_set_search(opendir_set, dir)) { 543 strcpy(render_node_dirent.d_name, 544 render_node_dirent_name); 545 ent = &render_node_dirent; 546 _mesa_set_remove_key(opendir_set, dir); 547 } 548 mtx_unlock(&shim_lock); 549 } 550 551 return ent; 552 } 553 554 /* If we've reached the end of the real directory list and we're 555 * looking at /dev/dri, add our render node to the list. 556 */ 557 PUBLIC struct dirent64 * 558 readdir64(DIR *dir) 559 { 560 init_shim(); 561 562 struct dirent64 *ent = NULL; 563 if (dir != fake_dev_dri) 564 ent = real_readdir64(dir); 565 static struct dirent64 render_node_dirent = { 0 }; 566 567 if (!ent) { 568 mtx_lock(&shim_lock); 569 if (_mesa_set_search(opendir_set, dir)) { 570 strcpy(render_node_dirent.d_name, 571 render_node_dirent_name); 572 ent = &render_node_dirent; 573 _mesa_set_remove_key(opendir_set, dir); 574 } 575 mtx_unlock(&shim_lock); 576 } 577 578 return ent; 579 } 580 581 /* Cleans up tracking of opendir("/dev/dri") */ 582 PUBLIC int 583 closedir(DIR *dir) 584 { 585 init_shim(); 586 587 mtx_lock(&shim_lock); 588 _mesa_set_remove_key(opendir_set, dir); 589 mtx_unlock(&shim_lock); 590 591 if (dir != fake_dev_dri) 592 return real_closedir(dir); 593 else 594 return 0; 595 } 596 597 /* Handles libdrm's readlink to figure out what kind of device we have. */ 598 PUBLIC ssize_t 599 readlink(const char *path, char *buf, size_t size) 600 { 601 init_shim(); 602 603 if (strcmp(path, subsystem_path) != 0) 604 return real_readlink(path, buf, size); 605 606 static const struct { 607 const char *name; 608 int bus_type; 609 } bus_types[] = { 610 { "/pci", DRM_BUS_PCI }, 611 { "/usb", DRM_BUS_USB }, 612 { "/platform", DRM_BUS_PLATFORM }, 613 { "/spi", DRM_BUS_PLATFORM }, 614 { "/host1x", DRM_BUS_HOST1X }, 615 }; 616 617 for (uint32_t i = 0; i < ARRAY_SIZE(bus_types); i++) { 618 if (bus_types[i].bus_type != shim_device.bus_type) 619 continue; 620 621 strncpy(buf, bus_types[i].name, size); 622 buf[size - 1] = 0; 623 break; 624 } 625 626 return strlen(buf) + 1; 627 } 628 629 /* Handles libdrm's realpath to figure out what kind of device we have. */ 630 PUBLIC char * 631 realpath(const char *path, char *resolved_path) 632 { 633 init_shim(); 634 635 if (strcmp(path, device_path) != 0) 636 return real_realpath(path, resolved_path); 637 638 strcpy(resolved_path, path); 639 640 return resolved_path; 641 } 642 643 /* Main entrypoint to DRM drivers: the ioctl syscall. We send all ioctls on 644 * our DRM fd to drm_shim_ioctl(). 645 */ 646 PUBLIC int 647 ioctl(int fd, unsigned long request, ...) 648 { 649 init_shim(); 650 651 va_list ap; 652 va_start(ap, request); 653 void *arg = va_arg(ap, void *); 654 va_end(ap); 655 656 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 657 if (!shim_fd) 658 return real_ioctl(fd, request, arg); 659 660 return drm_shim_ioctl(fd, request, arg); 661 } 662 663 /* Gallium uses this to dup the incoming fd on gbm screen creation */ 664 PUBLIC int 665 fcntl(int fd, int cmd, ...) 666 { 667 init_shim(); 668 669 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 670 671 va_list ap; 672 va_start(ap, cmd); 673 void *arg = va_arg(ap, void *); 674 va_end(ap); 675 676 int ret = real_fcntl(fd, cmd, arg); 677 678 if (shim_fd && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC)) 679 drm_shim_fd_register(ret, shim_fd); 680 681 return ret; 682 } 683 PUBLIC int fcntl64(int, int, ...) 684 __attribute__((alias("fcntl"))); 685 686 /* I wrote this when trying to fix gallium screen creation, leaving it around 687 * since it's probably good to have. 688 */ 689 PUBLIC int 690 dup(int fd) 691 { 692 init_shim(); 693 694 int ret = real_dup(fd); 695 696 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 697 if (shim_fd && ret >= 0) 698 drm_shim_fd_register(ret, shim_fd); 699 700 return ret; 701 } 702 703 PUBLIC void * 704 mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) 705 { 706 init_shim(); 707 708 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd); 709 if (shim_fd) 710 return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset); 711 712 return real_mmap(addr, length, prot, flags, fd, offset); 713 } 714 PUBLIC void *mmap64(void*, size_t, int, int, int, off_t) 715 __attribute__((alias("mmap"))); 716