1/* 2 * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org> 3 * Copyright (C) 2014-2016 Emil Velikov <emil.l.velikov@gmail.com> 4 * Copyright (C) 2016 Intel Corporation 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 * 25 * Authors: 26 * Rob Clark <robclark@freedesktop.org> 27 */ 28 29#include <dlfcn.h> 30#include <errno.h> 31#include <fcntl.h> 32#include <sys/stat.h> 33#include <stdarg.h> 34#include <stdio.h> 35#include <stdbool.h> 36#include <string.h> 37#include <unistd.h> 38#include <stdlib.h> 39#include <limits.h> 40#include <sys/param.h> 41#ifdef MAJOR_IN_MKDEV 42#include <sys/mkdev.h> 43#endif 44#ifdef MAJOR_IN_SYSMACROS 45#include <sys/sysmacros.h> 46#endif 47#include <GL/gl.h> 48#include <GL/internal/dri_interface.h> 49#include "loader.h" 50#include "util/os_file.h" 51 52#ifdef HAVE_LIBDRM 53#include <xf86drm.h> 54#define MAX_DRM_DEVICES 64 55#ifdef USE_DRICONF 56#include "util/xmlconfig.h" 57#include "util/driconf.h" 58#endif 59#endif 60 61#include "util/macros.h" 62 63#define __IS_LOADER 64#include "pci_id_driver_map.h" 65 66/* For systems like Hurd */ 67#ifndef PATH_MAX 68#define PATH_MAX 4096 69#endif 70 71#include "ohos_log.h" 72 73static void default_logger(int level, const char *fmt, ...) 74{ 75 const int MAX_BUFFER_LEN = 1024; 76 char log_string[MAX_BUFFER_LEN]; 77 if (level <= _LOADER_WARNING) { 78 va_list args; 79 va_start(args, fmt); 80 vfprintf(stderr, fmt, args); 81 va_end(args); 82 } 83 DISPLAY_LOGI(); 84} 85 86static void ohos_logger(int level, const char *fmt, ...) 87{ 88 const int MAX_BUFFER_LEN = 1024; 89 char log_string[MAX_BUFFER_LEN]; 90 va_list args; 91 va_start(args, fmt); 92 (void)snprintf(log_string, MAX_BUFFER_LEN, fmt, args); 93 va_end(args); 94 switch (level) { 95 case _LOADER_WARNING: 96 DISPLAY_LOGW("%{public}s", log_string); 97 break; 98 case _LOADER_DEBUG: 99 DISPLAY_LOGD("%{public}s", log_string); 100 break; 101 case _LOADER_FATAL: 102 DISPLAY_LOGE("%{public}s", log_string); 103 break; 104 case _LOADER_INFO: 105 DISPLAY_LOGI("%{public}s", log_string); 106 break; 107 default: 108 break; 109 } 110 DISPLAY_LOGI(); 111} 112 113static loader_logger *log_ = ohos_logger; 114 115int 116loader_open_device(const char *device_name) 117{ 118 log_(_LOADER_WARNING, "loader_open_device %s", device_name); 119 int fd; 120#ifdef O_CLOEXEC 121 fd = open(device_name, O_RDWR | O_CLOEXEC); 122#endif 123 if (fd == -1 && errno == EINVAL) 124 { 125 fd = open(device_name, O_RDWR); 126 if (fd != -1) 127 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 128 } 129 if (fd == -1 && errno == EACCES) { 130 log_(_LOADER_WARNING, "failed to open %s: %s\n", 131 device_name, strerror(errno)); 132 } 133 return fd; 134} 135 136static char *loader_get_kernel_driver_name(int fd) 137{ 138#if HAVE_LIBDRM 139 char *driver; 140 drmVersionPtr version = drmGetVersion(fd); 141 142 if (!version) { 143 log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd); 144 return NULL; 145 } 146 147 driver = strndup(version->name, version->name_len); 148 log_(driver ? _LOADER_DEBUG : _LOADER_WARNING, "using driver %s for %d\n", 149 driver, fd); 150 151 drmFreeVersion(version); 152 return driver; 153#else 154 return NULL; 155#endif 156} 157 158bool 159is_kernel_i915(int fd) 160{ 161 char *kernel_driver = loader_get_kernel_driver_name(fd); 162 bool is_i915 = kernel_driver && strcmp(kernel_driver, "i915") == 0; 163 164 free(kernel_driver); 165 return is_i915; 166} 167 168#if defined(HAVE_LIBDRM) 169int 170loader_open_render_node(const char *name) 171{ 172 drmDevicePtr devices[MAX_DRM_DEVICES], device; 173 int i, num_devices, fd = -1; 174 175 num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); 176 if (num_devices <= 0) 177 return -ENOENT; 178 179 for (i = 0; i < num_devices; i++) { 180 device = devices[i]; 181 182 if ((device->available_nodes & (1 << DRM_NODE_RENDER)) && 183 (device->bustype == DRM_BUS_PLATFORM)) { 184 drmVersionPtr version; 185 186 fd = loader_open_device(device->nodes[DRM_NODE_RENDER]); 187 if (fd < 0) 188 continue; 189 190 version = drmGetVersion(fd); 191 if (!version) { 192 close(fd); 193 continue; 194 } 195 196 if (strcmp(version->name, name) != 0) { 197 drmFreeVersion(version); 198 close(fd); 199 continue; 200 } 201 202 drmFreeVersion(version); 203 break; 204 } 205 } 206 drmFreeDevices(devices, num_devices); 207 208 if (i == num_devices) 209 return -ENOENT; 210 211 return fd; 212} 213 214char * 215loader_get_render_node(dev_t device) 216{ 217 char *render_node = NULL; 218 drmDevicePtr dev_ptr; 219 220 if (drmGetDeviceFromDevId(device, 0, &dev_ptr) < 0) 221 return NULL; 222 223 if (dev_ptr->available_nodes & (1 << DRM_NODE_RENDER)) { 224 render_node = strdup(dev_ptr->nodes[DRM_NODE_RENDER]); 225 if (!render_node) 226 log_(_LOADER_DEBUG, "MESA-LOADER: failed to allocate memory for render node\n"); 227 } 228 229 drmFreeDevice(&dev_ptr); 230 231 return render_node; 232} 233 234#ifdef USE_DRICONF 235static const driOptionDescription __driConfigOptionsLoader[] = { 236 DRI_CONF_SECTION_INITIALIZATION 237 DRI_CONF_DEVICE_ID_PATH_TAG() 238 DRI_CONF_DRI_DRIVER() 239 DRI_CONF_SECTION_END 240}; 241 242static char *loader_get_dri_config_driver(int fd) 243{ 244 driOptionCache defaultInitOptions; 245 driOptionCache userInitOptions; 246 char *dri_driver = NULL; 247 char *kernel_driver = loader_get_kernel_driver_name(fd); 248 249 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader, 250 ARRAY_SIZE(__driConfigOptionsLoader)); 251 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, 252 "loader", kernel_driver, NULL, NULL, 0, NULL, 0); 253 if (driCheckOption(&userInitOptions, "dri_driver", DRI_STRING)) { 254 char *opt = driQueryOptionstr(&userInitOptions, "dri_driver"); 255 /* not an empty string */ 256 if (*opt) 257 dri_driver = strdup(opt); 258 } 259 driDestroyOptionCache(&userInitOptions); 260 driDestroyOptionInfo(&defaultInitOptions); 261 262 free(kernel_driver); 263 return dri_driver; 264} 265 266static char *loader_get_dri_config_device_id(void) 267{ 268 driOptionCache defaultInitOptions; 269 driOptionCache userInitOptions; 270 char *prime = NULL; 271 272 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader, 273 ARRAY_SIZE(__driConfigOptionsLoader)); 274 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, 275 "loader", NULL, NULL, NULL, 0, NULL, 0); 276 if (driCheckOption(&userInitOptions, "device_id", DRI_STRING)) 277 prime = strdup(driQueryOptionstr(&userInitOptions, "device_id")); 278 driDestroyOptionCache(&userInitOptions); 279 driDestroyOptionInfo(&defaultInitOptions); 280 281 return prime; 282} 283#endif 284 285static char *drm_construct_id_path_tag(drmDevicePtr device) 286{ 287 char *tag = NULL; 288 289 if (device->bustype == DRM_BUS_PCI) { 290 if (asprintf(&tag, "pci-%04x_%02x_%02x_%1u", 291 device->businfo.pci->domain, 292 device->businfo.pci->bus, 293 device->businfo.pci->dev, 294 device->businfo.pci->func) < 0) { 295 return NULL; 296 } 297 } else if (device->bustype == DRM_BUS_PLATFORM || 298 device->bustype == DRM_BUS_HOST1X) { 299 char *fullname, *name, *address; 300 301 if (device->bustype == DRM_BUS_PLATFORM) 302 fullname = device->businfo.platform->fullname; 303 else 304 fullname = device->businfo.host1x->fullname; 305 306 name = strrchr(fullname, '/'); 307 if (!name) 308 name = strdup(fullname); 309 else 310 name = strdup(name + 1); 311 312 address = strchr(name, '@'); 313 if (address) { 314 *address++ = '\0'; 315 316 if (asprintf(&tag, "platform-%s_%s", address, name) < 0) 317 tag = NULL; 318 } else { 319 if (asprintf(&tag, "platform-%s", name) < 0) 320 tag = NULL; 321 } 322 323 free(name); 324 } 325 return tag; 326} 327 328static bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag) 329{ 330 char *tag = drm_construct_id_path_tag(device); 331 int ret; 332 333 if (tag == NULL) 334 return false; 335 336 ret = strcmp(tag, prime_tag); 337 338 free(tag); 339 return ret == 0; 340} 341 342static char *drm_get_id_path_tag_for_fd(int fd) 343{ 344 drmDevicePtr device; 345 char *tag; 346 347 if (drmGetDevice2(fd, 0, &device) != 0) 348 return NULL; 349 350 tag = drm_construct_id_path_tag(device); 351 drmFreeDevice(&device); 352 return tag; 353} 354 355int loader_get_user_preferred_fd(int default_fd, bool *different_device) 356{ 357 const char *dri_prime = getenv("DRI_PRIME"); 358 char *default_tag, *prime = NULL; 359 drmDevicePtr devices[MAX_DRM_DEVICES]; 360 int i, num_devices, fd = -1; 361 362#ifdef USE_DRICONF 363 if (dri_prime) 364 prime = strdup(dri_prime); 365 else 366 prime = loader_get_dri_config_device_id(); 367#else 368 if (dri_prime) 369 prime = strdup(dri_prime); 370#endif 371 372 if (prime == NULL) { 373 *different_device = false; 374 return default_fd; 375 } 376 377 default_tag = drm_get_id_path_tag_for_fd(default_fd); 378 if (default_tag == NULL) 379 goto err; 380 381 num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); 382 if (num_devices <= 0) 383 goto err; 384 385 for (i = 0; i < num_devices; i++) { 386 if (!(devices[i]->available_nodes & 1 << DRM_NODE_RENDER)) 387 continue; 388 389 /* two formats of DRI_PRIME are supported: 390 * "1": choose any other card than the card used by default. 391 * id_path_tag: (for example "pci-0000_02_00_0") choose the card 392 * with this id_path_tag. 393 */ 394 if (!strcmp(prime,"1")) { 395 if (drm_device_matches_tag(devices[i], default_tag)) 396 continue; 397 } else { 398 if (!drm_device_matches_tag(devices[i], prime)) 399 continue; 400 } 401 402 fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]); 403 break; 404 } 405 drmFreeDevices(devices, num_devices); 406 407 if (i == num_devices) 408 goto err; 409 410 if (fd < 0) 411 goto err; 412 413 close(default_fd); 414 415 *different_device = !!strcmp(default_tag, prime); 416 417 free(default_tag); 418 free(prime); 419 return fd; 420 421 err: 422 *different_device = false; 423 424 free(default_tag); 425 free(prime); 426 return default_fd; 427} 428#else 429int 430loader_open_render_node(const char *name) 431{ 432 return -1; 433} 434 435char * 436loader_get_render_node(dev_t device) 437{ 438 return NULL; 439} 440 441int loader_get_user_preferred_fd(int default_fd, bool *different_device) 442{ 443 *different_device = false; 444 return default_fd; 445} 446#endif 447 448#if defined(HAVE_LIBDRM) 449 450static bool 451drm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) 452{ 453 drmDevicePtr device; 454 455 if (drmGetDevice2(fd, 0, &device) != 0) { 456 log_(_LOADER_WARNING, "MESA-LOADER: failed to retrieve device information\n"); 457 return false; 458 } 459 460 if (device->bustype != DRM_BUS_PCI) { 461 drmFreeDevice(&device); 462 log_(_LOADER_DEBUG, "MESA-LOADER: device is not located on the PCI bus\n"); 463 return false; 464 } 465 466 *vendor_id = device->deviceinfo.pci->vendor_id; 467 *chip_id = device->deviceinfo.pci->device_id; 468 drmFreeDevice(&device); 469 return true; 470} 471#endif 472 473#ifdef __linux__ 474static int loader_get_linux_pci_field(int maj, int min, const char *field) 475{ 476 char path[PATH_MAX + 1]; 477 snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device/%s", maj, min, field); 478 479 char *field_str = os_read_file(path, NULL); 480 if (!field_str) { 481 /* Probably non-PCI device. */ 482 return 0; 483 } 484 485 int value = (int)strtoll(field_str, NULL, 16); 486 free(field_str); 487 488 return value; 489} 490 491static bool 492loader_get_linux_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) 493{ 494 struct stat sbuf; 495 if (fstat(fd, &sbuf) != 0) { 496 log_(_LOADER_DEBUG, "MESA-LOADER: failed to fstat fd\n"); 497 return false; 498 } 499 500 int maj = major(sbuf.st_rdev); 501 int min = minor(sbuf.st_rdev); 502 503 *vendor_id = loader_get_linux_pci_field(maj, min, "vendor"); 504 *chip_id = loader_get_linux_pci_field(maj, min, "device"); 505 506 return *vendor_id && *chip_id; 507} 508#endif /* __linux__ */ 509 510bool 511loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) 512{ 513#ifdef __linux__ 514 /* Implementation without causing full enumeration of DRM devices. */ 515 if (loader_get_linux_pci_id_for_fd(fd, vendor_id, chip_id)) 516 return true; 517#endif 518 519#if HAVE_LIBDRM 520 return drm_get_pci_id_for_fd(fd, vendor_id, chip_id); 521#endif 522 return false; 523} 524 525char * 526loader_get_device_name_for_fd(int fd) 527{ 528 char *result = NULL; 529 530#if HAVE_LIBDRM 531 result = drmGetDeviceNameFromFd2(fd); 532#endif 533 534 return result; 535} 536 537static char * 538loader_get_pci_driver(int fd) 539{ 540 int vendor_id, chip_id, i, j; 541 char *driver = NULL; 542 543 if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) 544 return NULL; 545 546 for (i = 0; i < ARRAY_SIZE(driver_map); i++) { 547 if (vendor_id != driver_map[i].vendor_id) 548 continue; 549 550 if (driver_map[i].predicate && !driver_map[i].predicate(fd)) 551 continue; 552 553 if (driver_map[i].num_chips_ids == -1) { 554 driver = strdup(driver_map[i].driver); 555 goto out; 556 } 557 558 for (j = 0; j < driver_map[i].num_chips_ids; j++) 559 if (driver_map[i].chip_ids[j] == chip_id) { 560 driver = strdup(driver_map[i].driver); 561 goto out; 562 } 563 } 564 565out: 566 log_(driver ? _LOADER_DEBUG : _LOADER_WARNING, 567 "pci id for fd %d: %04x:%04x, driver %s\n", 568 fd, vendor_id, chip_id, driver); 569 return driver; 570} 571 572char * 573loader_get_driver_for_fd(int fd) 574{ 575 char *driver; 576 577 /* Allow an environment variable to force choosing a different driver 578 * binary. If that driver binary can't survive on this FD, that's the 579 * user's problem, but this allows vc4 simulator to run on an i965 host, 580 * and may be useful for some touch testing of i915 on an i965 host. 581 */ 582 if (geteuid() == getuid()) { 583 driver = getenv("MESA_LOADER_DRIVER_OVERRIDE"); 584 if (driver) 585 return strdup(driver); 586 } 587 588#if defined(HAVE_LIBDRM) && defined(USE_DRICONF) 589 driver = loader_get_dri_config_driver(fd); 590 if (driver) 591 return driver; 592#endif 593 594 driver = loader_get_pci_driver(fd); 595 if (!driver) 596 driver = loader_get_kernel_driver_name(fd); 597 598 return driver; 599} 600 601void 602loader_set_logger(loader_logger *logger) 603{ 604 log_ = logger; 605} 606 607char * 608loader_get_extensions_name(const char *driver_name) 609{ 610 char *name = NULL; 611 612 if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0) 613 return NULL; 614 615 const size_t len = strlen(name); 616 for (size_t i = 0; i < len; i++) { 617 if (name[i] == '-') 618 name[i] = '_'; 619 } 620 621 return name; 622} 623 624/** 625 * Opens a driver or backend using its name, returning the library handle. 626 * 627 * \param driverName - a name like "i965", "radeon", "nouveau", etc. 628 * \param lib_suffix - a suffix to append to the driver name to generate the 629 * full library name. 630 * \param search_path_vars - NULL-terminated list of env vars that can be used 631 * \param default_search_path - a colon-separted list of directories used if 632 * search_path_vars is NULL or none of the vars are set in the environment. 633 * \param warn_on_fail - Log a warning if the driver is not found. 634 */ 635void * 636loader_open_driver_lib(const char *driver_name, 637 const char *lib_suffix, 638 const char **search_path_vars, 639 const char *default_search_path, 640 bool warn_on_fail) 641{ 642 char path[PATH_MAX]; 643 const char *search_paths, *next, *end; 644 645 search_paths = NULL; 646 if (geteuid() == getuid() && search_path_vars) { 647 for (int i = 0; search_path_vars[i] != NULL; i++) { 648 search_paths = getenv(search_path_vars[i]); 649 if (search_paths) 650 break; 651 } 652 } 653 if (search_paths == NULL) 654 search_paths = default_search_path; 655 656 void *driver = NULL; 657 const char *dl_error = NULL; 658 end = search_paths + strlen(search_paths); 659 for (const char *p = search_paths; p < end; p = next + 1) { 660 int len; 661 next = strchr(p, ':'); 662 if (next == NULL) 663 next = end; 664 665 len = next - p; 666 snprintf(path, sizeof(path), "%.*s/tls/%s%s.so", len, 667 p, driver_name, lib_suffix); 668 driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL); 669 if (driver == NULL) { 670 snprintf(path, sizeof(path), "%.*s/%s%s.so", len, 671 p, driver_name, lib_suffix); 672 driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL); 673 if (driver == NULL) { 674 dl_error = dlerror(); 675 log_(_LOADER_DEBUG, "MESA-LOADER: failed to open %s: %s\n", 676 path, dl_error); 677 } 678 } 679 /* not need continue to loop all paths once the driver is found */ 680 if (driver != NULL) 681 break; 682 } 683 684 if (driver == NULL) { 685 if (warn_on_fail) { 686 log_(_LOADER_WARNING, 687 "MESA-LOADER: failed to open %s: %s (search paths %s, suffix %s)\n", 688 driver_name, dl_error, search_paths, lib_suffix); 689 } 690 return NULL; 691 } 692 693 log_(_LOADER_DEBUG, "MESA-LOADER: dlopen(%s)\n", path); 694 695 return driver; 696} 697 698/** 699 * Opens a DRI driver using its driver name, returning the __DRIextension 700 * entrypoints. 701 * 702 * \param driverName - a name like "i965", "radeon", "nouveau", etc. 703 * \param out_driver - Address where the dlopen() return value will be stored. 704 * \param search_path_vars - NULL-terminated list of env vars that can be used 705 * to override the DEFAULT_DRIVER_DIR search path. 706 */ 707const struct __DRIextensionRec ** 708loader_open_driver(const char *driver_name, 709 void **out_driver_handle, 710 const char **search_path_vars) 711{ 712 char *get_extensions_name; 713 const struct __DRIextensionRec **extensions = NULL; 714 const struct __DRIextensionRec **(*get_extensions)(void); 715 void *driver = loader_open_driver_lib(driver_name, "_dri", search_path_vars, 716 DEFAULT_DRIVER_DIR, true); 717 718 if (!driver) 719 goto failed; 720 721 get_extensions_name = loader_get_extensions_name(driver_name); 722 if (get_extensions_name) { 723 get_extensions = dlsym(driver, get_extensions_name); 724 if (get_extensions) { 725 extensions = get_extensions(); 726 } else { 727 log_(_LOADER_DEBUG, "MESA-LOADER: driver does not expose %s(): %s\n", 728 get_extensions_name, dlerror()); 729 } 730 free(get_extensions_name); 731 } 732 733 if (!extensions) 734 extensions = dlsym(driver, __DRI_DRIVER_EXTENSIONS); 735 if (extensions == NULL) { 736 log_(_LOADER_WARNING, 737 "MESA-LOADER: driver exports no extensions (%s)\n", dlerror()); 738 dlclose(driver); 739 driver = NULL; 740 } 741 742failed: 743 *out_driver_handle = driver; 744 return extensions; 745} 746