loader.c revision 1463c08d
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 51#ifdef HAVE_LIBDRM 52#include <xf86drm.h> 53#define MAX_DRM_DEVICES 64 54#ifdef USE_DRICONF 55#include "util/xmlconfig.h" 56#include "util/driconf.h" 57#endif 58#endif 59 60#include "util/macros.h" 61 62#define __IS_LOADER 63#include "pci_id_driver_map.h" 64 65/* For systems like Hurd */ 66#ifndef PATH_MAX 67#define PATH_MAX 4096 68#endif 69 70static void default_logger(int level, const char *fmt, ...) 71{ 72 if (level <= _LOADER_WARNING) { 73 va_list args; 74 va_start(args, fmt); 75 vfprintf(stderr, fmt, args); 76 va_end(args); 77 } 78} 79 80static loader_logger *log_ = default_logger; 81 82int 83loader_open_device(const char *device_name) 84{ 85 int fd; 86#ifdef O_CLOEXEC 87 fd = open(device_name, O_RDWR | O_CLOEXEC); 88 if (fd == -1 && errno == EINVAL) 89#endif 90 { 91 fd = open(device_name, O_RDWR); 92 if (fd != -1) 93 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 94 } 95 if (fd == -1 && errno == EACCES) { 96 log_(_LOADER_WARNING, "failed to open %s: %s\n", 97 device_name, strerror(errno)); 98 } 99 return fd; 100} 101 102static char *loader_get_kernel_driver_name(int fd) 103{ 104#if HAVE_LIBDRM 105 char *driver; 106 drmVersionPtr version = drmGetVersion(fd); 107 108 if (!version) { 109 log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd); 110 return NULL; 111 } 112 113 driver = strndup(version->name, version->name_len); 114 log_(driver ? _LOADER_DEBUG : _LOADER_WARNING, "using driver %s for %d\n", 115 driver, fd); 116 117 drmFreeVersion(version); 118 return driver; 119#else 120 return NULL; 121#endif 122} 123 124bool 125is_kernel_i915(int fd) 126{ 127 char *kernel_driver = loader_get_kernel_driver_name(fd); 128 bool is_i915 = kernel_driver && strcmp(kernel_driver, "i915") == 0; 129 130 free(kernel_driver); 131 return is_i915; 132} 133 134#if defined(HAVE_LIBDRM) 135int 136loader_open_render_node(const char *name) 137{ 138 drmDevicePtr devices[MAX_DRM_DEVICES], device; 139 int i, num_devices, fd = -1; 140 141 num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); 142 if (num_devices <= 0) 143 return -ENOENT; 144 145 for (i = 0; i < num_devices; i++) { 146 device = devices[i]; 147 148 if ((device->available_nodes & (1 << DRM_NODE_RENDER)) && 149 (device->bustype == DRM_BUS_PLATFORM)) { 150 drmVersionPtr version; 151 152 fd = loader_open_device(device->nodes[DRM_NODE_RENDER]); 153 if (fd < 0) 154 continue; 155 156 version = drmGetVersion(fd); 157 if (!version) { 158 close(fd); 159 continue; 160 } 161 162 if (strcmp(version->name, name) != 0) { 163 drmFreeVersion(version); 164 close(fd); 165 continue; 166 } 167 168 drmFreeVersion(version); 169 break; 170 } 171 } 172 drmFreeDevices(devices, num_devices); 173 174 if (i == num_devices) 175 return -ENOENT; 176 177 return fd; 178} 179 180#ifdef USE_DRICONF 181static const driOptionDescription __driConfigOptionsLoader[] = { 182 DRI_CONF_SECTION_INITIALIZATION 183 DRI_CONF_DEVICE_ID_PATH_TAG() 184 DRI_CONF_DRI_DRIVER() 185 DRI_CONF_SECTION_END 186}; 187 188static char *loader_get_dri_config_driver(int fd) 189{ 190 driOptionCache defaultInitOptions; 191 driOptionCache userInitOptions; 192 char *dri_driver = NULL; 193 char *kernel_driver = loader_get_kernel_driver_name(fd); 194 195 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader, 196 ARRAY_SIZE(__driConfigOptionsLoader)); 197 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, 198 "loader", kernel_driver, NULL, NULL, 0, NULL, 0); 199 if (driCheckOption(&userInitOptions, "dri_driver", DRI_STRING)) { 200 char *opt = driQueryOptionstr(&userInitOptions, "dri_driver"); 201 /* not an empty string */ 202 if (*opt) 203 dri_driver = strdup(opt); 204 } 205 driDestroyOptionCache(&userInitOptions); 206 driDestroyOptionInfo(&defaultInitOptions); 207 208 free(kernel_driver); 209 return dri_driver; 210} 211 212static char *loader_get_dri_config_device_id(void) 213{ 214 driOptionCache defaultInitOptions; 215 driOptionCache userInitOptions; 216 char *prime = NULL; 217 218 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader, 219 ARRAY_SIZE(__driConfigOptionsLoader)); 220 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, 221 "loader", NULL, NULL, NULL, 0, NULL, 0); 222 if (driCheckOption(&userInitOptions, "device_id", DRI_STRING)) 223 prime = strdup(driQueryOptionstr(&userInitOptions, "device_id")); 224 driDestroyOptionCache(&userInitOptions); 225 driDestroyOptionInfo(&defaultInitOptions); 226 227 return prime; 228} 229#endif 230 231static char *drm_construct_id_path_tag(drmDevicePtr device) 232{ 233 char *tag = NULL; 234 235 if (device->bustype == DRM_BUS_PCI) { 236 if (asprintf(&tag, "pci-%04x_%02x_%02x_%1u", 237 device->businfo.pci->domain, 238 device->businfo.pci->bus, 239 device->businfo.pci->dev, 240 device->businfo.pci->func) < 0) { 241 return NULL; 242 } 243 } else if (device->bustype == DRM_BUS_PLATFORM || 244 device->bustype == DRM_BUS_HOST1X) { 245 char *fullname, *name, *address; 246 247 if (device->bustype == DRM_BUS_PLATFORM) 248 fullname = device->businfo.platform->fullname; 249 else 250 fullname = device->businfo.host1x->fullname; 251 252 name = strrchr(fullname, '/'); 253 if (!name) 254 name = strdup(fullname); 255 else 256 name = strdup(name + 1); 257 258 address = strchr(name, '@'); 259 if (address) { 260 *address++ = '\0'; 261 262 if (asprintf(&tag, "platform-%s_%s", address, name) < 0) 263 tag = NULL; 264 } else { 265 if (asprintf(&tag, "platform-%s", name) < 0) 266 tag = NULL; 267 } 268 269 free(name); 270 } 271 return tag; 272} 273 274static bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag) 275{ 276 char *tag = drm_construct_id_path_tag(device); 277 int ret; 278 279 if (tag == NULL) 280 return false; 281 282 ret = strcmp(tag, prime_tag); 283 284 free(tag); 285 return ret == 0; 286} 287 288static char *drm_get_id_path_tag_for_fd(int fd) 289{ 290 drmDevicePtr device; 291 char *tag; 292 293 if (drmGetDevice2(fd, 0, &device) != 0) 294 return NULL; 295 296 tag = drm_construct_id_path_tag(device); 297 drmFreeDevice(&device); 298 return tag; 299} 300 301int loader_get_user_preferred_fd(int default_fd, bool *different_device) 302{ 303 const char *dri_prime = getenv("DRI_PRIME"); 304 char *default_tag, *prime = NULL; 305 drmDevicePtr devices[MAX_DRM_DEVICES]; 306 int i, num_devices, fd = -1; 307 308 if (dri_prime) 309 prime = strdup(dri_prime); 310#ifdef USE_DRICONF 311 else 312 prime = loader_get_dri_config_device_id(); 313#endif 314 315 if (prime == NULL) { 316 *different_device = false; 317 return default_fd; 318 } 319 320 default_tag = drm_get_id_path_tag_for_fd(default_fd); 321 if (default_tag == NULL) 322 goto err; 323 324 num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); 325 if (num_devices <= 0) 326 goto err; 327 328 for (i = 0; i < num_devices; i++) { 329 if (!(devices[i]->available_nodes & 1 << DRM_NODE_RENDER)) 330 continue; 331 332 /* two formats of DRI_PRIME are supported: 333 * "1": choose any other card than the card used by default. 334 * id_path_tag: (for example "pci-0000_02_00_0") choose the card 335 * with this id_path_tag. 336 */ 337 if (!strcmp(prime,"1")) { 338 if (drm_device_matches_tag(devices[i], default_tag)) 339 continue; 340 } else { 341 if (!drm_device_matches_tag(devices[i], prime)) 342 continue; 343 } 344 345 fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]); 346 break; 347 } 348 drmFreeDevices(devices, num_devices); 349 350 if (i == num_devices) 351 goto err; 352 353 if (fd < 0) 354 goto err; 355 356 close(default_fd); 357 358 *different_device = !!strcmp(default_tag, prime); 359 360 free(default_tag); 361 free(prime); 362 return fd; 363 364 err: 365 *different_device = false; 366 367 free(default_tag); 368 free(prime); 369 return default_fd; 370} 371#else 372int 373loader_open_render_node(const char *name) 374{ 375 return -1; 376} 377 378int loader_get_user_preferred_fd(int default_fd, bool *different_device) 379{ 380 *different_device = false; 381 return default_fd; 382} 383#endif 384 385#if defined(HAVE_LIBDRM) 386 387static bool 388drm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) 389{ 390 drmDevicePtr device; 391 392 if (drmGetDevice2(fd, 0, &device) != 0) { 393 log_(_LOADER_WARNING, "MESA-LOADER: failed to retrieve device information\n"); 394 return false; 395 } 396 397 if (device->bustype != DRM_BUS_PCI) { 398 drmFreeDevice(&device); 399 log_(_LOADER_DEBUG, "MESA-LOADER: device is not located on the PCI bus\n"); 400 return false; 401 } 402 403 *vendor_id = device->deviceinfo.pci->vendor_id; 404 *chip_id = device->deviceinfo.pci->device_id; 405 drmFreeDevice(&device); 406 return true; 407} 408#endif 409 410 411bool 412loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) 413{ 414#if HAVE_LIBDRM 415 return drm_get_pci_id_for_fd(fd, vendor_id, chip_id); 416#endif 417 return false; 418} 419 420char * 421loader_get_device_name_for_fd(int fd) 422{ 423 char *result = NULL; 424 425#if HAVE_LIBDRM 426 result = drmGetDeviceNameFromFd2(fd); 427#endif 428 429 return result; 430} 431 432static char * 433loader_get_pci_driver(int fd) 434{ 435 int vendor_id, chip_id, i, j; 436 char *driver = NULL; 437 438 if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) 439 return NULL; 440 441 for (i = 0; i < ARRAY_SIZE(driver_map); i++) { 442 if (vendor_id != driver_map[i].vendor_id) 443 continue; 444 445 if (driver_map[i].predicate && !driver_map[i].predicate(fd)) 446 continue; 447 448 if (driver_map[i].num_chips_ids == -1) { 449 driver = strdup(driver_map[i].driver); 450 goto out; 451 } 452 453 for (j = 0; j < driver_map[i].num_chips_ids; j++) 454 if (driver_map[i].chip_ids[j] == chip_id) { 455 driver = strdup(driver_map[i].driver); 456 goto out; 457 } 458 } 459 460out: 461 log_(driver ? _LOADER_DEBUG : _LOADER_WARNING, 462 "pci id for fd %d: %04x:%04x, driver %s\n", 463 fd, vendor_id, chip_id, driver); 464 return driver; 465} 466 467char * 468loader_get_driver_for_fd(int fd) 469{ 470 char *driver; 471 472 /* Allow an environment variable to force choosing a different driver 473 * binary. If that driver binary can't survive on this FD, that's the 474 * user's problem, but this allows vc4 simulator to run on an i965 host, 475 * and may be useful for some touch testing of i915 on an i965 host. 476 */ 477 if (geteuid() == getuid()) { 478 driver = getenv("MESA_LOADER_DRIVER_OVERRIDE"); 479 if (driver) 480 return strdup(driver); 481 } 482 483#if defined(HAVE_LIBDRM) && defined(USE_DRICONF) 484 driver = loader_get_dri_config_driver(fd); 485 if (driver) 486 return driver; 487#endif 488 489 driver = loader_get_pci_driver(fd); 490 if (!driver) 491 driver = loader_get_kernel_driver_name(fd); 492 493 return driver; 494} 495 496void 497loader_set_logger(loader_logger *logger) 498{ 499 log_ = logger; 500} 501 502char * 503loader_get_extensions_name(const char *driver_name) 504{ 505 char *name = NULL; 506 507 if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0) 508 return NULL; 509 510 const size_t len = strlen(name); 511 for (size_t i = 0; i < len; i++) { 512 if (name[i] == '-') 513 name[i] = '_'; 514 } 515 516 return name; 517} 518 519/** 520 * Opens a driver or backend using its name, returning the library handle. 521 * 522 * \param driverName - a name like "i965", "radeon", "nouveau", etc. 523 * \param lib_suffix - a suffix to append to the driver name to generate the 524 * full library name. 525 * \param search_path_vars - NULL-terminated list of env vars that can be used 526 * \param default_search_path - a colon-separted list of directories used if 527 * search_path_vars is NULL or none of the vars are set in the environment. 528 * \param warn_on_fail - Log a warning if the driver is not found. 529 */ 530void * 531loader_open_driver_lib(const char *driver_name, 532 const char *lib_suffix, 533 const char **search_path_vars, 534 const char *default_search_path, 535 bool warn_on_fail) 536{ 537 char path[PATH_MAX]; 538 const char *search_paths, *next, *end; 539 540 search_paths = NULL; 541 if (!issetugid() && search_path_vars) { 542 for (int i = 0; search_path_vars[i] != NULL; i++) { 543 search_paths = getenv(search_path_vars[i]); 544 if (search_paths) 545 break; 546 } 547 } 548 if (search_paths == NULL) 549 search_paths = default_search_path; 550 551 void *driver = NULL; 552 const char *dl_error = NULL; 553 end = search_paths + strlen(search_paths); 554 for (const char *p = search_paths; p < end; p = next + 1) { 555 int len; 556 next = strchr(p, ':'); 557 if (next == NULL) 558 next = end; 559 560 len = next - p; 561#if USE_ELF_TLS 562 snprintf(path, sizeof(path), "%.*s/tls/%s%s.so", len, 563 p, driver_name, lib_suffix); 564 driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL); 565#endif 566 if (driver == NULL) { 567 snprintf(path, sizeof(path), "%.*s/%s%s.so", len, 568 p, driver_name, lib_suffix); 569 driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL); 570 if (driver == NULL) { 571 dl_error = dlerror(); 572 log_(_LOADER_DEBUG, "MESA-LOADER: failed to open %s: %s\n", 573 path, dl_error); 574 } 575 } 576 /* not need continue to loop all paths once the driver is found */ 577 if (driver != NULL) 578 break; 579 } 580 581 if (driver == NULL) { 582 if (warn_on_fail) { 583 log_(_LOADER_WARNING, 584 "MESA-LOADER: failed to open %s: %s (search paths %s, suffix %s)\n", 585 driver_name, dl_error, search_paths, lib_suffix); 586 } 587 return NULL; 588 } 589 590 log_(_LOADER_DEBUG, "MESA-LOADER: dlopen(%s)\n", path); 591 592 return driver; 593} 594 595/** 596 * Opens a DRI driver using its driver name, returning the __DRIextension 597 * entrypoints. 598 * 599 * \param driverName - a name like "i965", "radeon", "nouveau", etc. 600 * \param out_driver - Address where the dlopen() return value will be stored. 601 * \param search_path_vars - NULL-terminated list of env vars that can be used 602 * to override the DEFAULT_DRIVER_DIR search path. 603 */ 604const struct __DRIextensionRec ** 605loader_open_driver(const char *driver_name, 606 void **out_driver_handle, 607 const char **search_path_vars) 608{ 609 char *get_extensions_name; 610 const struct __DRIextensionRec **extensions = NULL; 611 const struct __DRIextensionRec **(*get_extensions)(void); 612 void *driver = loader_open_driver_lib(driver_name, "_dri", search_path_vars, 613 DEFAULT_DRIVER_DIR, true); 614 615 if (!driver) 616 goto failed; 617 618 get_extensions_name = loader_get_extensions_name(driver_name); 619 if (get_extensions_name) { 620 get_extensions = dlsym(driver, get_extensions_name); 621 if (get_extensions) { 622 extensions = get_extensions(); 623 } else { 624 log_(_LOADER_DEBUG, "MESA-LOADER: driver does not expose %s(): %s\n", 625 get_extensions_name, dlerror()); 626 } 627 free(get_extensions_name); 628 } 629 630 if (!extensions) 631 extensions = dlsym(driver, __DRI_DRIVER_EXTENSIONS); 632 if (extensions == NULL) { 633 log_(_LOADER_WARNING, 634 "MESA-LOADER: driver exports no extensions (%s)\n", dlerror()); 635 dlclose(driver); 636 driver = NULL; 637 } 638 639failed: 640 *out_driver_handle = driver; 641 return extensions; 642} 643