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