loader.c revision 01e04c3f
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 <errno.h> 30#include <fcntl.h> 31#include <sys/stat.h> 32#include <stdarg.h> 33#include <stdio.h> 34#include <stdbool.h> 35#include <string.h> 36#include <unistd.h> 37#include <stdlib.h> 38#ifdef MAJOR_IN_MKDEV 39#include <sys/mkdev.h> 40#endif 41#ifdef MAJOR_IN_SYSMACROS 42#include <sys/sysmacros.h> 43#endif 44#include "loader.h" 45 46#ifdef HAVE_LIBDRM 47#include <xf86drm.h> 48#ifdef USE_DRICONF 49#include "util/xmlconfig.h" 50#include "util/xmlpool.h" 51#endif 52#endif 53 54#define __IS_LOADER 55#include "pci_id_driver_map.h" 56 57static void default_logger(int level, const char *fmt, ...) 58{ 59 if (level <= _LOADER_WARNING) { 60 va_list args; 61 va_start(args, fmt); 62 vfprintf(stderr, fmt, args); 63 va_end(args); 64 } 65} 66 67static void (*log_)(int level, const char *fmt, ...) = default_logger; 68 69int 70loader_open_device(const char *device_name) 71{ 72 int fd; 73#ifdef O_CLOEXEC 74 fd = open(device_name, O_RDWR | O_CLOEXEC); 75 if (fd == -1 && errno == EINVAL) 76#endif 77 { 78 fd = open(device_name, O_RDWR); 79 if (fd != -1) 80 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 81 } 82 return fd; 83} 84 85static char *loader_get_kernel_driver_name(int fd) 86{ 87#if HAVE_LIBDRM 88 char *driver; 89 drmVersionPtr version = drmGetVersion(fd); 90 91 if (!version) { 92 log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd); 93 return NULL; 94 } 95 96 driver = strndup(version->name, version->name_len); 97 98 drmFreeVersion(version); 99 return driver; 100#else 101 return NULL; 102#endif 103} 104 105#if defined(HAVE_LIBDRM) 106int 107loader_open_render_node(const char *name) 108{ 109 drmDevicePtr *devices, device; 110 int err, render = -ENOENT, fd; 111 unsigned int num, i; 112 113 err = drmGetDevices2(0, NULL, 0); 114 if (err < 0) 115 return err; 116 117 num = err; 118 119 devices = calloc(num, sizeof(*devices)); 120 if (!devices) 121 return -ENOMEM; 122 123 err = drmGetDevices2(0, devices, num); 124 if (err < 0) { 125 render = err; 126 goto free; 127 } 128 129 for (i = 0; i < num; i++) { 130 device = devices[i]; 131 132 if ((device->available_nodes & (1 << DRM_NODE_RENDER)) && 133 (device->bustype == DRM_BUS_PLATFORM)) { 134 drmVersionPtr version; 135 136 fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR | O_CLOEXEC); 137 if (fd < 0) 138 continue; 139 140 version = drmGetVersion(fd); 141 if (!version) { 142 close(fd); 143 continue; 144 } 145 146 if (strcmp(version->name, name) != 0) { 147 drmFreeVersion(version); 148 close(fd); 149 continue; 150 } 151 152 drmFreeVersion(version); 153 render = fd; 154 break; 155 } 156 } 157 158 drmFreeDevices(devices, num); 159 160free: 161 free(devices); 162 return render; 163} 164 165#ifdef USE_DRICONF 166static const char __driConfigOptionsLoader[] = 167DRI_CONF_BEGIN 168 DRI_CONF_SECTION_INITIALIZATION 169 DRI_CONF_DEVICE_ID_PATH_TAG() 170 DRI_CONF_DRI_DRIVER() 171 DRI_CONF_SECTION_END 172DRI_CONF_END; 173 174static char *loader_get_dri_config_driver(int fd) 175{ 176 driOptionCache defaultInitOptions; 177 driOptionCache userInitOptions; 178 char *dri_driver = NULL; 179 char *kernel_driver = loader_get_kernel_driver_name(fd); 180 181 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader); 182 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, 183 "loader", kernel_driver); 184 if (driCheckOption(&userInitOptions, "dri_driver", DRI_STRING)) { 185 char *opt = driQueryOptionstr(&userInitOptions, "dri_driver"); 186 /* not an empty string */ 187 if (*opt) 188 dri_driver = strdup(opt); 189 } 190 driDestroyOptionCache(&userInitOptions); 191 driDestroyOptionInfo(&defaultInitOptions); 192 193 free(kernel_driver); 194 return dri_driver; 195} 196 197static char *loader_get_dri_config_device_id(void) 198{ 199 driOptionCache defaultInitOptions; 200 driOptionCache userInitOptions; 201 char *prime = NULL; 202 203 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader); 204 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, "loader", NULL); 205 if (driCheckOption(&userInitOptions, "device_id", DRI_STRING)) 206 prime = strdup(driQueryOptionstr(&userInitOptions, "device_id")); 207 driDestroyOptionCache(&userInitOptions); 208 driDestroyOptionInfo(&defaultInitOptions); 209 210 return prime; 211} 212#endif 213 214static char *drm_construct_id_path_tag(drmDevicePtr device) 215{ 216 char *tag = NULL; 217 218 if (device->bustype == DRM_BUS_PCI) { 219 if (asprintf(&tag, "pci-%04x_%02x_%02x_%1u", 220 device->businfo.pci->domain, 221 device->businfo.pci->bus, 222 device->businfo.pci->dev, 223 device->businfo.pci->func) < 0) { 224 return NULL; 225 } 226 } else if (device->bustype == DRM_BUS_PLATFORM || 227 device->bustype == DRM_BUS_HOST1X) { 228 char *fullname, *name, *address; 229 230 if (device->bustype == DRM_BUS_PLATFORM) 231 fullname = device->businfo.platform->fullname; 232 else 233 fullname = device->businfo.host1x->fullname; 234 235 name = strrchr(fullname, '/'); 236 if (!name) 237 name = strdup(fullname); 238 else 239 name = strdup(name + 1); 240 241 address = strchr(name, '@'); 242 if (address) { 243 *address++ = '\0'; 244 245 if (asprintf(&tag, "platform-%s_%s", address, name) < 0) 246 tag = NULL; 247 } else { 248 if (asprintf(&tag, "platform-%s", name) < 0) 249 tag = NULL; 250 } 251 252 free(name); 253 } 254 return tag; 255} 256 257static bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag) 258{ 259 char *tag = drm_construct_id_path_tag(device); 260 int ret; 261 262 if (tag == NULL) 263 return false; 264 265 ret = strcmp(tag, prime_tag); 266 267 free(tag); 268 return ret == 0; 269} 270 271static char *drm_get_id_path_tag_for_fd(int fd) 272{ 273 drmDevicePtr device; 274 char *tag; 275 276 if (drmGetDevice2(fd, 0, &device) != 0) 277 return NULL; 278 279 tag = drm_construct_id_path_tag(device); 280 drmFreeDevice(&device); 281 return tag; 282} 283 284int loader_get_user_preferred_fd(int default_fd, bool *different_device) 285{ 286/* Arbitrary "maximum" value of drm devices. */ 287#define MAX_DRM_DEVICES 32 288 const char *dri_prime = getenv("DRI_PRIME"); 289 char *default_tag, *prime = NULL; 290 drmDevicePtr devices[MAX_DRM_DEVICES]; 291 int i, num_devices, fd; 292 bool found = false; 293 294 if (dri_prime) 295 prime = strdup(dri_prime); 296#ifdef USE_DRICONF 297 else 298 prime = loader_get_dri_config_device_id(); 299#endif 300 301 if (prime == NULL) { 302 *different_device = false; 303 return default_fd; 304 } 305 306 default_tag = drm_get_id_path_tag_for_fd(default_fd); 307 if (default_tag == NULL) 308 goto err; 309 310 num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); 311 if (num_devices < 0) 312 goto err; 313 314 /* two format are supported: 315 * "1": choose any other card than the card used by default. 316 * id_path_tag: (for example "pci-0000_02_00_0") choose the card 317 * with this id_path_tag. 318 */ 319 if (!strcmp(prime,"1")) { 320 /* Hmm... detection for 2-7 seems to be broken. Oh well ... 321 * Pick the first render device that is not our own. 322 */ 323 for (i = 0; i < num_devices; i++) { 324 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER && 325 !drm_device_matches_tag(devices[i], default_tag)) { 326 327 found = true; 328 break; 329 } 330 } 331 } else { 332 for (i = 0; i < num_devices; i++) { 333 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER && 334 drm_device_matches_tag(devices[i], prime)) { 335 336 found = true; 337 break; 338 } 339 } 340 } 341 342 if (!found) { 343 drmFreeDevices(devices, num_devices); 344 goto err; 345 } 346 347 fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]); 348 drmFreeDevices(devices, num_devices); 349 if (fd < 0) 350 goto err; 351 352 close(default_fd); 353 354 *different_device = !!strcmp(default_tag, prime); 355 356 free(default_tag); 357 free(prime); 358 return fd; 359 360 err: 361 *different_device = false; 362 363 free(default_tag); 364 free(prime); 365 return default_fd; 366} 367#else 368int 369loader_open_render_node(const char *name) 370{ 371 return -1; 372} 373 374int loader_get_user_preferred_fd(int default_fd, bool *different_device) 375{ 376 *different_device = false; 377 return default_fd; 378} 379#endif 380 381#if defined(HAVE_LIBDRM) 382 383static int 384drm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) 385{ 386 drmDevicePtr device; 387 int ret; 388 389 if (drmGetDevice2(fd, 0, &device) == 0) { 390 if (device->bustype == DRM_BUS_PCI) { 391 *vendor_id = device->deviceinfo.pci->vendor_id; 392 *chip_id = device->deviceinfo.pci->device_id; 393 ret = 1; 394 } 395 else { 396 log_(_LOADER_DEBUG, "MESA-LOADER: device is not located on the PCI bus\n"); 397 ret = 0; 398 } 399 drmFreeDevice(&device); 400 } 401 else { 402 log_(_LOADER_WARNING, "MESA-LOADER: failed to retrieve device information\n"); 403 ret = 0; 404 } 405 406 return ret; 407} 408#endif 409 410 411int 412loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) 413{ 414#if HAVE_LIBDRM 415 if (drm_get_pci_id_for_fd(fd, vendor_id, chip_id)) 416 return 1; 417#endif 418 return 0; 419} 420 421char * 422loader_get_device_name_for_fd(int fd) 423{ 424 char *result = NULL; 425 426#if HAVE_LIBDRM 427 result = drmGetDeviceNameFromFd2(fd); 428#endif 429 430 return result; 431} 432 433char * 434loader_get_driver_for_fd(int fd) 435{ 436 int vendor_id, chip_id, i, j; 437 char *driver = NULL; 438 439 /* Allow an environment variable to force choosing a different driver 440 * binary. If that driver binary can't survive on this FD, that's the 441 * user's problem, but this allows vc4 simulator to run on an i965 host, 442 * and may be useful for some touch testing of i915 on an i965 host. 443 */ 444 if (geteuid() == getuid()) { 445 driver = getenv("MESA_LOADER_DRIVER_OVERRIDE"); 446 if (driver) 447 return strdup(driver); 448 } 449 450#if defined(HAVE_LIBDRM) && defined(USE_DRICONF) 451 driver = loader_get_dri_config_driver(fd); 452 if (driver) 453 return driver; 454#endif 455 456 if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) { 457 driver = loader_get_kernel_driver_name(fd); 458 if (driver) 459 log_(_LOADER_INFO, "using driver %s for %d\n", driver, fd); 460 return driver; 461 } 462 463 for (i = 0; driver_map[i].driver; i++) { 464 if (vendor_id != driver_map[i].vendor_id) 465 continue; 466 467 if (driver_map[i].predicate && !driver_map[i].predicate(fd)) 468 continue; 469 470 if (driver_map[i].num_chips_ids == -1) { 471 driver = strdup(driver_map[i].driver); 472 goto out; 473 } 474 475 for (j = 0; j < driver_map[i].num_chips_ids; j++) 476 if (driver_map[i].chip_ids[j] == chip_id) { 477 driver = strdup(driver_map[i].driver); 478 goto out; 479 } 480 } 481 482out: 483 log_(driver ? _LOADER_DEBUG : _LOADER_WARNING, 484 "pci id for fd %d: %04x:%04x, driver %s\n", 485 fd, vendor_id, chip_id, driver); 486 return driver; 487} 488 489void 490loader_set_logger(void (*logger)(int level, const char *fmt, ...)) 491{ 492 log_ = logger; 493} 494 495/* XXX: Local definition to avoid pulling the heavyweight GL/gl.h and 496 * GL/internal/dri_interface.h 497 */ 498 499#ifndef __DRI_DRIVER_GET_EXTENSIONS 500#define __DRI_DRIVER_GET_EXTENSIONS "__driDriverGetExtensions" 501#endif 502 503char * 504loader_get_extensions_name(const char *driver_name) 505{ 506 char *name = NULL; 507 508 if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0) 509 return NULL; 510 511 const size_t len = strlen(name); 512 for (size_t i = 0; i < len; i++) { 513 if (name[i] == '-') 514 name[i] = '_'; 515 } 516 517 return name; 518} 519