Home | History | Annotate | Download | only in loader
      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