1 /* 2 * Copyright (C) 2013 Rob Clark <robclark (at) freedesktop.org> 3 * Copyright (C) 2014-2016 Emil Velikov <emil.l.velikov (at) 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 (at) 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 #ifdef MAJOR_IN_MKDEV 37 #include <sys/mkdev.h> 38 #endif 39 #ifdef MAJOR_IN_SYSMACROS 40 #include <sys/sysmacros.h> 41 #endif 42 #include "loader.h" 43 44 #ifdef HAVE_LIBDRM 45 #include <stdlib.h> 46 #include <unistd.h> 47 #include <xf86drm.h> 48 #ifdef USE_DRICONF 49 #include "xmlconfig.h" 50 #include "xmlpool.h" 51 #endif 52 #endif 53 54 #define __IS_LOADER 55 #include "pci_id_driver_map.h" 56 57 static 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 67 static void (*log_)(int level, const char *fmt, ...) = default_logger; 68 69 int 70 loader_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 85 #if defined(HAVE_LIBDRM) 86 #ifdef USE_DRICONF 87 static const char __driConfigOptionsLoader[] = 88 DRI_CONF_BEGIN 89 DRI_CONF_SECTION_INITIALIZATION 90 DRI_CONF_DEVICE_ID_PATH_TAG() 91 DRI_CONF_SECTION_END 92 DRI_CONF_END; 93 94 static char *loader_get_dri_config_device_id(void) 95 { 96 driOptionCache defaultInitOptions; 97 driOptionCache userInitOptions; 98 char *prime = NULL; 99 100 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader); 101 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, "loader"); 102 if (driCheckOption(&userInitOptions, "device_id", DRI_STRING)) 103 prime = strdup(driQueryOptionstr(&userInitOptions, "device_id")); 104 driDestroyOptionCache(&userInitOptions); 105 driDestroyOptionInfo(&defaultInitOptions); 106 107 return prime; 108 } 109 #endif 110 111 static char *drm_construct_id_path_tag(drmDevicePtr device) 112 { 113 /* Length of "pci-xxxx_xx_xx_x\0" */ 114 #define PCI_ID_PATH_TAG_LENGTH 17 115 char *tag = NULL; 116 117 if (device->bustype == DRM_BUS_PCI) { 118 tag = calloc(PCI_ID_PATH_TAG_LENGTH, sizeof(char)); 119 if (tag == NULL) 120 return NULL; 121 122 snprintf(tag, PCI_ID_PATH_TAG_LENGTH, "pci-%04x_%02x_%02x_%1u", 123 device->businfo.pci->domain, device->businfo.pci->bus, 124 device->businfo.pci->dev, device->businfo.pci->func); 125 } 126 return tag; 127 } 128 129 static bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag) 130 { 131 char *tag = drm_construct_id_path_tag(device); 132 int ret; 133 134 if (tag == NULL) 135 return false; 136 137 ret = strcmp(tag, prime_tag); 138 139 free(tag); 140 return ret == 0; 141 } 142 143 static char *drm_get_id_path_tag_for_fd(int fd) 144 { 145 drmDevicePtr device; 146 char *tag; 147 148 if (drmGetDevice(fd, &device) != 0) 149 return NULL; 150 151 tag = drm_construct_id_path_tag(device); 152 drmFreeDevice(&device); 153 return tag; 154 } 155 156 int loader_get_user_preferred_fd(int default_fd, int *different_device) 157 { 158 /* Arbitrary "maximum" value of drm devices. */ 159 #define MAX_DRM_DEVICES 32 160 const char *dri_prime = getenv("DRI_PRIME"); 161 char *default_tag, *prime = NULL; 162 drmDevicePtr devices[MAX_DRM_DEVICES]; 163 int i, num_devices, fd; 164 bool found = false; 165 166 if (dri_prime) 167 prime = strdup(dri_prime); 168 #ifdef USE_DRICONF 169 else 170 prime = loader_get_dri_config_device_id(); 171 #endif 172 173 if (prime == NULL) { 174 *different_device = 0; 175 return default_fd; 176 } 177 178 default_tag = drm_get_id_path_tag_for_fd(default_fd); 179 if (default_tag == NULL) 180 goto err; 181 182 num_devices = drmGetDevices(devices, MAX_DRM_DEVICES); 183 if (num_devices < 0) 184 goto err; 185 186 /* two format are supported: 187 * "1": choose any other card than the card used by default. 188 * id_path_tag: (for example "pci-0000_02_00_0") choose the card 189 * with this id_path_tag. 190 */ 191 if (!strcmp(prime,"1")) { 192 /* Hmm... detection for 2-7 seems to be broken. Oh well ... 193 * Pick the first render device that is not our own. 194 */ 195 for (i = 0; i < num_devices; i++) { 196 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER && 197 !drm_device_matches_tag(devices[i], default_tag)) { 198 199 found = true; 200 break; 201 } 202 } 203 } else { 204 for (i = 0; i < num_devices; i++) { 205 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER && 206 drm_device_matches_tag(devices[i], prime)) { 207 208 found = true; 209 break; 210 } 211 } 212 } 213 214 if (!found) { 215 drmFreeDevices(devices, num_devices); 216 goto err; 217 } 218 219 fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]); 220 drmFreeDevices(devices, num_devices); 221 if (fd < 0) 222 goto err; 223 224 close(default_fd); 225 226 *different_device = !!strcmp(default_tag, prime); 227 228 free(default_tag); 229 free(prime); 230 return fd; 231 232 err: 233 *different_device = 0; 234 235 free(default_tag); 236 free(prime); 237 return default_fd; 238 } 239 #else 240 int loader_get_user_preferred_fd(int default_fd, int *different_device) 241 { 242 *different_device = 0; 243 return default_fd; 244 } 245 #endif 246 247 #if defined(HAVE_LIBDRM) 248 static int 249 dev_node_from_fd(int fd, unsigned int *maj, unsigned int *min) 250 { 251 struct stat buf; 252 253 if (fstat(fd, &buf) < 0) { 254 log_(_LOADER_WARNING, "MESA-LOADER: failed to stat fd %d\n", fd); 255 return -1; 256 } 257 258 if (!S_ISCHR(buf.st_mode)) { 259 log_(_LOADER_WARNING, "MESA-LOADER: fd %d not a character device\n", fd); 260 return -1; 261 } 262 263 *maj = major(buf.st_rdev); 264 *min = minor(buf.st_rdev); 265 266 return 0; 267 } 268 #endif 269 270 #if defined(HAVE_LIBDRM) 271 272 static int 273 drm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) 274 { 275 drmDevicePtr device; 276 int ret; 277 278 if (drmGetDevice(fd, &device) == 0) { 279 if (device->bustype == DRM_BUS_PCI) { 280 *vendor_id = device->deviceinfo.pci->vendor_id; 281 *chip_id = device->deviceinfo.pci->device_id; 282 ret = 1; 283 } 284 else { 285 log_(_LOADER_DEBUG, "MESA-LOADER: device is not located on the PCI bus\n"); 286 ret = 0; 287 } 288 drmFreeDevice(&device); 289 } 290 else { 291 log_(_LOADER_WARNING, "MESA-LOADER: failed to retrieve device information\n"); 292 ret = 0; 293 } 294 295 return ret; 296 } 297 #endif 298 299 300 int 301 loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) 302 { 303 #if HAVE_LIBDRM 304 if (drm_get_pci_id_for_fd(fd, vendor_id, chip_id)) 305 return 1; 306 #endif 307 return 0; 308 } 309 310 311 #if defined(HAVE_LIBDRM) 312 static char * 313 drm_get_device_name_for_fd(int fd) 314 { 315 unsigned int maj, min; 316 char buf[0x40]; 317 int n; 318 319 if (dev_node_from_fd(fd, &maj, &min) < 0) 320 return NULL; 321 322 n = snprintf(buf, sizeof(buf), DRM_DEV_NAME, DRM_DIR_NAME, min); 323 if (n == -1 || n >= sizeof(buf)) 324 return NULL; 325 326 return strdup(buf); 327 } 328 #endif 329 330 char * 331 loader_get_device_name_for_fd(int fd) 332 { 333 char *result = NULL; 334 335 #if HAVE_LIBDRM 336 if ((result = drm_get_device_name_for_fd(fd))) 337 return result; 338 #endif 339 return result; 340 } 341 342 char * 343 loader_get_driver_for_fd(int fd) 344 { 345 int vendor_id, chip_id, i, j; 346 char *driver = NULL; 347 348 if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) { 349 350 #if HAVE_LIBDRM 351 /* fallback to drmGetVersion(): */ 352 drmVersionPtr version = drmGetVersion(fd); 353 354 if (!version) { 355 log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd); 356 return NULL; 357 } 358 359 driver = strndup(version->name, version->name_len); 360 log_(_LOADER_INFO, "using driver %s for %d\n", driver, fd); 361 362 drmFreeVersion(version); 363 #endif 364 365 return driver; 366 } 367 368 for (i = 0; driver_map[i].driver; i++) { 369 if (vendor_id != driver_map[i].vendor_id) 370 continue; 371 372 if (driver_map[i].predicate && !driver_map[i].predicate(fd)) 373 continue; 374 375 if (driver_map[i].num_chips_ids == -1) { 376 driver = strdup(driver_map[i].driver); 377 goto out; 378 } 379 380 for (j = 0; j < driver_map[i].num_chips_ids; j++) 381 if (driver_map[i].chip_ids[j] == chip_id) { 382 driver = strdup(driver_map[i].driver); 383 goto out; 384 } 385 } 386 387 out: 388 log_(driver ? _LOADER_DEBUG : _LOADER_WARNING, 389 "pci id for fd %d: %04x:%04x, driver %s\n", 390 fd, vendor_id, chip_id, driver); 391 return driver; 392 } 393 394 void 395 loader_set_logger(void (*logger)(int level, const char *fmt, ...)) 396 { 397 log_ = logger; 398 } 399 400 /* XXX: Local definition to avoid pulling the heavyweight GL/gl.h and 401 * GL/internal/dri_interface.h 402 */ 403 404 #ifndef __DRI_DRIVER_GET_EXTENSIONS 405 #define __DRI_DRIVER_GET_EXTENSIONS "__driDriverGetExtensions" 406 #endif 407 408 char * 409 loader_get_extensions_name(const char *driver_name) 410 { 411 char *name = NULL; 412 413 if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0) 414 return NULL; 415 416 const size_t len = strlen(name); 417 for (size_t i = 0; i < len; i++) { 418 if (name[i] == '-') 419 name[i] = '_'; 420 } 421 422 return name; 423 } 424