1af69d88dSmrg/*
2af69d88dSmrg * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
301e04c3fSmrg * Copyright (C) 2014-2016 Emil Velikov <emil.l.velikov@gmail.com>
401e04c3fSmrg * Copyright (C) 2016 Intel Corporation
5af69d88dSmrg *
6af69d88dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
7af69d88dSmrg * copy of this software and associated documentation files (the "Software"),
8af69d88dSmrg * to deal in the Software without restriction, including without limitation
9af69d88dSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10af69d88dSmrg * and/or sell copies of the Software, and to permit persons to whom the
11af69d88dSmrg * Software is furnished to do so, subject to the following conditions:
12af69d88dSmrg *
13af69d88dSmrg * The above copyright notice and this permission notice (including the next
14af69d88dSmrg * paragraph) shall be included in all copies or substantial portions of the
15af69d88dSmrg * Software.
16af69d88dSmrg *
17af69d88dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18af69d88dSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19af69d88dSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21af69d88dSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22af69d88dSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23af69d88dSmrg * SOFTWARE.
24af69d88dSmrg *
25af69d88dSmrg * Authors:
26af69d88dSmrg *    Rob Clark <robclark@freedesktop.org>
27af69d88dSmrg */
28af69d88dSmrg
299f464c52Smaya#include <dlfcn.h>
3001e04c3fSmrg#include <errno.h>
3101e04c3fSmrg#include <fcntl.h>
3201e04c3fSmrg#include <sys/stat.h>
33af69d88dSmrg#include <stdarg.h>
34af69d88dSmrg#include <stdio.h>
3501e04c3fSmrg#include <stdbool.h>
36af69d88dSmrg#include <string.h>
37af69d88dSmrg#include <unistd.h>
38af69d88dSmrg#include <stdlib.h>
391463c08dSmrg#include <limits.h>
409f464c52Smaya#include <sys/param.h>
4101e04c3fSmrg#ifdef MAJOR_IN_MKDEV
4201e04c3fSmrg#include <sys/mkdev.h>
43af69d88dSmrg#endif
4401e04c3fSmrg#ifdef MAJOR_IN_SYSMACROS
4501e04c3fSmrg#include <sys/sysmacros.h>
46af69d88dSmrg#endif
479f464c52Smaya#include <GL/gl.h>
489f464c52Smaya#include <GL/internal/dri_interface.h>
49af69d88dSmrg#include "loader.h"
50af69d88dSmrg
5101e04c3fSmrg#ifdef HAVE_LIBDRM
52af69d88dSmrg#include <xf86drm.h>
531463c08dSmrg#define MAX_DRM_DEVICES 64
5401e04c3fSmrg#ifdef USE_DRICONF
5501e04c3fSmrg#include "util/xmlconfig.h"
561463c08dSmrg#include "util/driconf.h"
5701e04c3fSmrg#endif
58af69d88dSmrg#endif
59af69d88dSmrg
601463c08dSmrg#include "util/macros.h"
611463c08dSmrg
62af69d88dSmrg#define __IS_LOADER
63af69d88dSmrg#include "pci_id_driver_map.h"
64af69d88dSmrg
651463c08dSmrg/* For systems like Hurd */
661463c08dSmrg#ifndef PATH_MAX
671463c08dSmrg#define PATH_MAX 4096
681463c08dSmrg#endif
691463c08dSmrg
70af69d88dSmrgstatic void default_logger(int level, const char *fmt, ...)
71af69d88dSmrg{
72af69d88dSmrg   if (level <= _LOADER_WARNING) {
73af69d88dSmrg      va_list args;
74af69d88dSmrg      va_start(args, fmt);
75af69d88dSmrg      vfprintf(stderr, fmt, args);
76af69d88dSmrg      va_end(args);
77af69d88dSmrg   }
78af69d88dSmrg}
79af69d88dSmrg
809f464c52Smayastatic loader_logger *log_ = default_logger;
81af69d88dSmrg
8201e04c3fSmrgint
8301e04c3fSmrgloader_open_device(const char *device_name)
84af69d88dSmrg{
8501e04c3fSmrg   int fd;
8601e04c3fSmrg#ifdef O_CLOEXEC
8701e04c3fSmrg   fd = open(device_name, O_RDWR | O_CLOEXEC);
8801e04c3fSmrg   if (fd == -1 && errno == EINVAL)
8901e04c3fSmrg#endif
9001e04c3fSmrg   {
9101e04c3fSmrg      fd = open(device_name, O_RDWR);
9201e04c3fSmrg      if (fd != -1)
9301e04c3fSmrg         fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
94af69d88dSmrg   }
951463c08dSmrg   if (fd == -1 && errno == EACCES) {
961463c08dSmrg      log_(_LOADER_WARNING, "failed to open %s: %s\n",
971463c08dSmrg           device_name, strerror(errno));
981463c08dSmrg   }
9901e04c3fSmrg   return fd;
100af69d88dSmrg}
101af69d88dSmrg
10201e04c3fSmrgstatic char *loader_get_kernel_driver_name(int fd)
103af69d88dSmrg{
10401e04c3fSmrg#if HAVE_LIBDRM
10501e04c3fSmrg   char *driver;
10601e04c3fSmrg   drmVersionPtr version = drmGetVersion(fd);
10701e04c3fSmrg
10801e04c3fSmrg   if (!version) {
10901e04c3fSmrg      log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd);
11001e04c3fSmrg      return NULL;
11101e04c3fSmrg   }
112af69d88dSmrg
11301e04c3fSmrg   driver = strndup(version->name, version->name_len);
1141463c08dSmrg   log_(driver ? _LOADER_DEBUG : _LOADER_WARNING, "using driver %s for %d\n",
1151463c08dSmrg        driver, fd);
116af69d88dSmrg
11701e04c3fSmrg   drmFreeVersion(version);
11801e04c3fSmrg   return driver;
11901e04c3fSmrg#else
12001e04c3fSmrg   return NULL;
12101e04c3fSmrg#endif
12201e04c3fSmrg}
123af69d88dSmrg
1241463c08dSmrgbool
1251463c08dSmrgis_kernel_i915(int fd)
1261463c08dSmrg{
1271463c08dSmrg   char *kernel_driver = loader_get_kernel_driver_name(fd);
1281463c08dSmrg   bool is_i915 = kernel_driver && strcmp(kernel_driver, "i915") == 0;
1291463c08dSmrg
1301463c08dSmrg   free(kernel_driver);
1311463c08dSmrg   return is_i915;
1321463c08dSmrg}
1331463c08dSmrg
13401e04c3fSmrg#if defined(HAVE_LIBDRM)
13501e04c3fSmrgint
13601e04c3fSmrgloader_open_render_node(const char *name)
137af69d88dSmrg{
1381463c08dSmrg   drmDevicePtr devices[MAX_DRM_DEVICES], device;
1391463c08dSmrg   int i, num_devices, fd = -1;
14001e04c3fSmrg
1411463c08dSmrg   num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
1421463c08dSmrg   if (num_devices <= 0)
1431463c08dSmrg      return -ENOENT;
144af69d88dSmrg
1451463c08dSmrg   for (i = 0; i < num_devices; i++) {
14601e04c3fSmrg      device = devices[i];
14701e04c3fSmrg
14801e04c3fSmrg      if ((device->available_nodes & (1 << DRM_NODE_RENDER)) &&
14901e04c3fSmrg          (device->bustype == DRM_BUS_PLATFORM)) {
15001e04c3fSmrg         drmVersionPtr version;
15101e04c3fSmrg
1529f464c52Smaya         fd = loader_open_device(device->nodes[DRM_NODE_RENDER]);
15301e04c3fSmrg         if (fd < 0)
15401e04c3fSmrg            continue;
15501e04c3fSmrg
15601e04c3fSmrg         version = drmGetVersion(fd);
15701e04c3fSmrg         if (!version) {
15801e04c3fSmrg            close(fd);
15901e04c3fSmrg            continue;
16001e04c3fSmrg         }
16101e04c3fSmrg
16201e04c3fSmrg         if (strcmp(version->name, name) != 0) {
16301e04c3fSmrg            drmFreeVersion(version);
16401e04c3fSmrg            close(fd);
16501e04c3fSmrg            continue;
16601e04c3fSmrg         }
16701e04c3fSmrg
16801e04c3fSmrg         drmFreeVersion(version);
16901e04c3fSmrg         break;
17001e04c3fSmrg      }
171af69d88dSmrg   }
1721463c08dSmrg   drmFreeDevices(devices, num_devices);
173af69d88dSmrg
1741463c08dSmrg   if (i == num_devices)
1751463c08dSmrg      return -ENOENT;
17601e04c3fSmrg
1771463c08dSmrg   return fd;
178af69d88dSmrg}
179af69d88dSmrg
18001e04c3fSmrg#ifdef USE_DRICONF
1811463c08dSmrgstatic const driOptionDescription __driConfigOptionsLoader[] = {
18201e04c3fSmrg    DRI_CONF_SECTION_INITIALIZATION
18301e04c3fSmrg        DRI_CONF_DEVICE_ID_PATH_TAG()
18401e04c3fSmrg        DRI_CONF_DRI_DRIVER()
18501e04c3fSmrg    DRI_CONF_SECTION_END
1861463c08dSmrg};
18701e04c3fSmrg
18801e04c3fSmrgstatic char *loader_get_dri_config_driver(int fd)
189af69d88dSmrg{
19001e04c3fSmrg   driOptionCache defaultInitOptions;
19101e04c3fSmrg   driOptionCache userInitOptions;
19201e04c3fSmrg   char *dri_driver = NULL;
19301e04c3fSmrg   char *kernel_driver = loader_get_kernel_driver_name(fd);
19401e04c3fSmrg
1951463c08dSmrg   driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader,
1961463c08dSmrg                      ARRAY_SIZE(__driConfigOptionsLoader));
19701e04c3fSmrg   driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0,
1981463c08dSmrg                       "loader", kernel_driver, NULL, NULL, 0, NULL, 0);
19901e04c3fSmrg   if (driCheckOption(&userInitOptions, "dri_driver", DRI_STRING)) {
20001e04c3fSmrg      char *opt = driQueryOptionstr(&userInitOptions, "dri_driver");
20101e04c3fSmrg      /* not an empty string */
20201e04c3fSmrg      if (*opt)
20301e04c3fSmrg         dri_driver = strdup(opt);
204af69d88dSmrg   }
20501e04c3fSmrg   driDestroyOptionCache(&userInitOptions);
20601e04c3fSmrg   driDestroyOptionInfo(&defaultInitOptions);
207af69d88dSmrg
20801e04c3fSmrg   free(kernel_driver);
20901e04c3fSmrg   return dri_driver;
21001e04c3fSmrg}
211af69d88dSmrg
21201e04c3fSmrgstatic char *loader_get_dri_config_device_id(void)
21301e04c3fSmrg{
21401e04c3fSmrg   driOptionCache defaultInitOptions;
21501e04c3fSmrg   driOptionCache userInitOptions;
21601e04c3fSmrg   char *prime = NULL;
217af69d88dSmrg
2181463c08dSmrg   driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader,
2191463c08dSmrg                      ARRAY_SIZE(__driConfigOptionsLoader));
2201463c08dSmrg   driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0,
2211463c08dSmrg                       "loader", NULL, NULL, NULL, 0, NULL, 0);
22201e04c3fSmrg   if (driCheckOption(&userInitOptions, "device_id", DRI_STRING))
22301e04c3fSmrg      prime = strdup(driQueryOptionstr(&userInitOptions, "device_id"));
22401e04c3fSmrg   driDestroyOptionCache(&userInitOptions);
22501e04c3fSmrg   driDestroyOptionInfo(&defaultInitOptions);
22601e04c3fSmrg
22701e04c3fSmrg   return prime;
228af69d88dSmrg}
22901e04c3fSmrg#endif
230af69d88dSmrg
23101e04c3fSmrgstatic char *drm_construct_id_path_tag(drmDevicePtr device)
232af69d88dSmrg{
23301e04c3fSmrg   char *tag = NULL;
23401e04c3fSmrg
23501e04c3fSmrg   if (device->bustype == DRM_BUS_PCI) {
23601e04c3fSmrg      if (asprintf(&tag, "pci-%04x_%02x_%02x_%1u",
23701e04c3fSmrg                   device->businfo.pci->domain,
23801e04c3fSmrg                   device->businfo.pci->bus,
23901e04c3fSmrg                   device->businfo.pci->dev,
24001e04c3fSmrg                   device->businfo.pci->func) < 0) {
24101e04c3fSmrg         return NULL;
242af69d88dSmrg      }
24301e04c3fSmrg   } else if (device->bustype == DRM_BUS_PLATFORM ||
24401e04c3fSmrg              device->bustype == DRM_BUS_HOST1X) {
24501e04c3fSmrg      char *fullname, *name, *address;
24601e04c3fSmrg
24701e04c3fSmrg      if (device->bustype == DRM_BUS_PLATFORM)
24801e04c3fSmrg         fullname = device->businfo.platform->fullname;
24901e04c3fSmrg      else
25001e04c3fSmrg         fullname = device->businfo.host1x->fullname;
251af69d88dSmrg
25201e04c3fSmrg      name = strrchr(fullname, '/');
25301e04c3fSmrg      if (!name)
25401e04c3fSmrg         name = strdup(fullname);
25501e04c3fSmrg      else
25601e04c3fSmrg         name = strdup(name + 1);
25701e04c3fSmrg
25801e04c3fSmrg      address = strchr(name, '@');
25901e04c3fSmrg      if (address) {
26001e04c3fSmrg         *address++ = '\0';
26101e04c3fSmrg
26201e04c3fSmrg         if (asprintf(&tag, "platform-%s_%s", address, name) < 0)
26301e04c3fSmrg            tag = NULL;
26401e04c3fSmrg      } else {
26501e04c3fSmrg         if (asprintf(&tag, "platform-%s", name) < 0)
26601e04c3fSmrg            tag = NULL;
26701e04c3fSmrg      }
26801e04c3fSmrg
26901e04c3fSmrg      free(name);
270af69d88dSmrg   }
27101e04c3fSmrg   return tag;
272af69d88dSmrg}
273af69d88dSmrg
27401e04c3fSmrgstatic bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag)
275af69d88dSmrg{
27601e04c3fSmrg   char *tag = drm_construct_id_path_tag(device);
27701e04c3fSmrg   int ret;
278af69d88dSmrg
27901e04c3fSmrg   if (tag == NULL)
28001e04c3fSmrg      return false;
281af69d88dSmrg
28201e04c3fSmrg   ret = strcmp(tag, prime_tag);
283af69d88dSmrg
28401e04c3fSmrg   free(tag);
28501e04c3fSmrg   return ret == 0;
286af69d88dSmrg}
287af69d88dSmrg
28801e04c3fSmrgstatic char *drm_get_id_path_tag_for_fd(int fd)
289af69d88dSmrg{
29001e04c3fSmrg   drmDevicePtr device;
29101e04c3fSmrg   char *tag;
292af69d88dSmrg
29301e04c3fSmrg   if (drmGetDevice2(fd, 0, &device) != 0)
29401e04c3fSmrg       return NULL;
29501e04c3fSmrg
29601e04c3fSmrg   tag = drm_construct_id_path_tag(device);
29701e04c3fSmrg   drmFreeDevice(&device);
29801e04c3fSmrg   return tag;
29901e04c3fSmrg}
300af69d88dSmrg
30101e04c3fSmrgint loader_get_user_preferred_fd(int default_fd, bool *different_device)
302af69d88dSmrg{
303af69d88dSmrg   const char *dri_prime = getenv("DRI_PRIME");
30401e04c3fSmrg   char *default_tag, *prime = NULL;
30501e04c3fSmrg   drmDevicePtr devices[MAX_DRM_DEVICES];
3061463c08dSmrg   int i, num_devices, fd = -1;
307af69d88dSmrg
308af69d88dSmrg   if (dri_prime)
309af69d88dSmrg      prime = strdup(dri_prime);
310af69d88dSmrg#ifdef USE_DRICONF
31101e04c3fSmrg   else
31201e04c3fSmrg      prime = loader_get_dri_config_device_id();
313af69d88dSmrg#endif
314af69d88dSmrg
315af69d88dSmrg   if (prime == NULL) {
31601e04c3fSmrg      *different_device = false;
317af69d88dSmrg      return default_fd;
318af69d88dSmrg   }
319af69d88dSmrg
32001e04c3fSmrg   default_tag = drm_get_id_path_tag_for_fd(default_fd);
32101e04c3fSmrg   if (default_tag == NULL)
32201e04c3fSmrg      goto err;
323af69d88dSmrg
32401e04c3fSmrg   num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
3251463c08dSmrg   if (num_devices <= 0)
32601e04c3fSmrg      goto err;
327af69d88dSmrg
3281463c08dSmrg   for (i = 0; i < num_devices; i++) {
3291463c08dSmrg      if (!(devices[i]->available_nodes & 1 << DRM_NODE_RENDER))
3301463c08dSmrg         continue;
33101e04c3fSmrg
3321463c08dSmrg      /* two formats of DRI_PRIME are supported:
3331463c08dSmrg       * "1": choose any other card than the card used by default.
3341463c08dSmrg       * id_path_tag: (for example "pci-0000_02_00_0") choose the card
3351463c08dSmrg       * with this id_path_tag.
3361463c08dSmrg       */
3371463c08dSmrg      if (!strcmp(prime,"1")) {
3381463c08dSmrg         if (drm_device_matches_tag(devices[i], default_tag))
3391463c08dSmrg            continue;
3401463c08dSmrg      } else {
3411463c08dSmrg         if (!drm_device_matches_tag(devices[i], prime))
3421463c08dSmrg            continue;
34301e04c3fSmrg      }
34401e04c3fSmrg
3451463c08dSmrg      fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]);
3461463c08dSmrg      break;
347af69d88dSmrg   }
3481463c08dSmrg   drmFreeDevices(devices, num_devices);
349af69d88dSmrg
3501463c08dSmrg   if (i == num_devices)
35101e04c3fSmrg      goto err;
352af69d88dSmrg
35301e04c3fSmrg   if (fd < 0)
35401e04c3fSmrg      goto err;
355af69d88dSmrg
35601e04c3fSmrg   close(default_fd);
357af69d88dSmrg
35801e04c3fSmrg   *different_device = !!strcmp(default_tag, prime);
359af69d88dSmrg
36001e04c3fSmrg   free(default_tag);
36101e04c3fSmrg   free(prime);
36201e04c3fSmrg   return fd;
363af69d88dSmrg
36401e04c3fSmrg err:
36501e04c3fSmrg   *different_device = false;
366af69d88dSmrg
36701e04c3fSmrg   free(default_tag);
36801e04c3fSmrg   free(prime);
36901e04c3fSmrg   return default_fd;
370af69d88dSmrg}
37101e04c3fSmrg#else
37201e04c3fSmrgint
37301e04c3fSmrgloader_open_render_node(const char *name)
374af69d88dSmrg{
37501e04c3fSmrg   return -1;
37601e04c3fSmrg}
377af69d88dSmrg
37801e04c3fSmrgint loader_get_user_preferred_fd(int default_fd, bool *different_device)
37901e04c3fSmrg{
38001e04c3fSmrg   *different_device = false;
38101e04c3fSmrg   return default_fd;
382af69d88dSmrg}
383af69d88dSmrg#endif
384af69d88dSmrg
38501e04c3fSmrg#if defined(HAVE_LIBDRM)
386af69d88dSmrg
3871463c08dSmrgstatic bool
388af69d88dSmrgdrm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
389af69d88dSmrg{
39001e04c3fSmrg   drmDevicePtr device;
39101e04c3fSmrg
3921463c08dSmrg   if (drmGetDevice2(fd, 0, &device) != 0) {
39301e04c3fSmrg      log_(_LOADER_WARNING, "MESA-LOADER: failed to retrieve device information\n");
3941463c08dSmrg      return false;
3951463c08dSmrg   }
3961463c08dSmrg
3971463c08dSmrg   if (device->bustype != DRM_BUS_PCI) {
3981463c08dSmrg      drmFreeDevice(&device);
3991463c08dSmrg      log_(_LOADER_DEBUG, "MESA-LOADER: device is not located on the PCI bus\n");
4001463c08dSmrg      return false;
401af69d88dSmrg   }
402af69d88dSmrg
4031463c08dSmrg   *vendor_id = device->deviceinfo.pci->vendor_id;
4041463c08dSmrg   *chip_id = device->deviceinfo.pci->device_id;
4051463c08dSmrg   drmFreeDevice(&device);
4061463c08dSmrg   return true;
407af69d88dSmrg}
408af69d88dSmrg#endif
409af69d88dSmrg
410af69d88dSmrg
4111463c08dSmrgbool
412af69d88dSmrgloader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
413af69d88dSmrg{
41401e04c3fSmrg#if HAVE_LIBDRM
4151463c08dSmrg   return drm_get_pci_id_for_fd(fd, vendor_id, chip_id);
416af69d88dSmrg#endif
4171463c08dSmrg   return false;
418af69d88dSmrg}
419af69d88dSmrg
420af69d88dSmrgchar *
421af69d88dSmrgloader_get_device_name_for_fd(int fd)
422af69d88dSmrg{
423af69d88dSmrg   char *result = NULL;
424af69d88dSmrg
42501e04c3fSmrg#if HAVE_LIBDRM
42601e04c3fSmrg   result = drmGetDeviceNameFromFd2(fd);
427af69d88dSmrg#endif
42801e04c3fSmrg
429af69d88dSmrg   return result;
430af69d88dSmrg}
431af69d88dSmrg
4321463c08dSmrgstatic char *
4331463c08dSmrgloader_get_pci_driver(int fd)
434af69d88dSmrg{
435af69d88dSmrg   int vendor_id, chip_id, i, j;
436af69d88dSmrg   char *driver = NULL;
437af69d88dSmrg
4381463c08dSmrg   if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id))
4391463c08dSmrg      return NULL;
440af69d88dSmrg
4411463c08dSmrg   for (i = 0; i < ARRAY_SIZE(driver_map); i++) {
442af69d88dSmrg      if (vendor_id != driver_map[i].vendor_id)
443af69d88dSmrg         continue;
444af69d88dSmrg
445af69d88dSmrg      if (driver_map[i].predicate && !driver_map[i].predicate(fd))
446af69d88dSmrg         continue;
447af69d88dSmrg
448af69d88dSmrg      if (driver_map[i].num_chips_ids == -1) {
449af69d88dSmrg         driver = strdup(driver_map[i].driver);
450af69d88dSmrg         goto out;
451af69d88dSmrg      }
452af69d88dSmrg
453af69d88dSmrg      for (j = 0; j < driver_map[i].num_chips_ids; j++)
454af69d88dSmrg         if (driver_map[i].chip_ids[j] == chip_id) {
455af69d88dSmrg            driver = strdup(driver_map[i].driver);
456af69d88dSmrg            goto out;
457af69d88dSmrg         }
458af69d88dSmrg   }
459af69d88dSmrg
460af69d88dSmrgout:
461af69d88dSmrg   log_(driver ? _LOADER_DEBUG : _LOADER_WARNING,
462af69d88dSmrg         "pci id for fd %d: %04x:%04x, driver %s\n",
463af69d88dSmrg         fd, vendor_id, chip_id, driver);
464af69d88dSmrg   return driver;
465af69d88dSmrg}
466af69d88dSmrg
4671463c08dSmrgchar *
4681463c08dSmrgloader_get_driver_for_fd(int fd)
4691463c08dSmrg{
4701463c08dSmrg   char *driver;
4711463c08dSmrg
4721463c08dSmrg   /* Allow an environment variable to force choosing a different driver
4731463c08dSmrg    * binary.  If that driver binary can't survive on this FD, that's the
4741463c08dSmrg    * user's problem, but this allows vc4 simulator to run on an i965 host,
4751463c08dSmrg    * and may be useful for some touch testing of i915 on an i965 host.
4761463c08dSmrg    */
4771463c08dSmrg   if (geteuid() == getuid()) {
4781463c08dSmrg      driver = getenv("MESA_LOADER_DRIVER_OVERRIDE");
4791463c08dSmrg      if (driver)
4801463c08dSmrg         return strdup(driver);
4811463c08dSmrg   }
4821463c08dSmrg
4831463c08dSmrg#if defined(HAVE_LIBDRM) && defined(USE_DRICONF)
4841463c08dSmrg   driver = loader_get_dri_config_driver(fd);
4851463c08dSmrg   if (driver)
4861463c08dSmrg      return driver;
4871463c08dSmrg#endif
4881463c08dSmrg
4891463c08dSmrg   driver = loader_get_pci_driver(fd);
4901463c08dSmrg   if (!driver)
4911463c08dSmrg      driver = loader_get_kernel_driver_name(fd);
4921463c08dSmrg
4931463c08dSmrg   return driver;
4941463c08dSmrg}
4951463c08dSmrg
496af69d88dSmrgvoid
4979f464c52Smayaloader_set_logger(loader_logger *logger)
498af69d88dSmrg{
499af69d88dSmrg   log_ = logger;
500af69d88dSmrg}
50101e04c3fSmrg
50201e04c3fSmrgchar *
50301e04c3fSmrgloader_get_extensions_name(const char *driver_name)
50401e04c3fSmrg{
50501e04c3fSmrg   char *name = NULL;
50601e04c3fSmrg
50701e04c3fSmrg   if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0)
50801e04c3fSmrg      return NULL;
50901e04c3fSmrg
51001e04c3fSmrg   const size_t len = strlen(name);
51101e04c3fSmrg   for (size_t i = 0; i < len; i++) {
51201e04c3fSmrg      if (name[i] == '-')
51301e04c3fSmrg         name[i] = '_';
51401e04c3fSmrg   }
51501e04c3fSmrg
51601e04c3fSmrg   return name;
51701e04c3fSmrg}
5189f464c52Smaya
5199f464c52Smaya/**
5201463c08dSmrg * Opens a driver or backend using its name, returning the library handle.
5219f464c52Smaya *
5229f464c52Smaya * \param driverName - a name like "i965", "radeon", "nouveau", etc.
5231463c08dSmrg * \param lib_suffix - a suffix to append to the driver name to generate the
5241463c08dSmrg * full library name.
5259f464c52Smaya * \param search_path_vars - NULL-terminated list of env vars that can be used
5261463c08dSmrg * \param default_search_path - a colon-separted list of directories used if
5271463c08dSmrg * search_path_vars is NULL or none of the vars are set in the environment.
5281463c08dSmrg * \param warn_on_fail - Log a warning if the driver is not found.
5299f464c52Smaya */
5301463c08dSmrgvoid *
5311463c08dSmrgloader_open_driver_lib(const char *driver_name,
5321463c08dSmrg                       const char *lib_suffix,
5331463c08dSmrg                       const char **search_path_vars,
5341463c08dSmrg                       const char *default_search_path,
5351463c08dSmrg                       bool warn_on_fail)
5369f464c52Smaya{
5371463c08dSmrg   char path[PATH_MAX];
5381463c08dSmrg   const char *search_paths, *next, *end;
5399f464c52Smaya
5409f464c52Smaya   search_paths = NULL;
541757f638fSmaya   if (!issetugid() && search_path_vars) {
5429f464c52Smaya      for (int i = 0; search_path_vars[i] != NULL; i++) {
5439f464c52Smaya         search_paths = getenv(search_path_vars[i]);
5449f464c52Smaya         if (search_paths)
5459f464c52Smaya            break;
5469f464c52Smaya      }
5479f464c52Smaya   }
5489f464c52Smaya   if (search_paths == NULL)
5491463c08dSmrg      search_paths = default_search_path;
5509f464c52Smaya
5519f464c52Smaya   void *driver = NULL;
5521463c08dSmrg   const char *dl_error = NULL;
5539f464c52Smaya   end = search_paths + strlen(search_paths);
5541463c08dSmrg   for (const char *p = search_paths; p < end; p = next + 1) {
5559f464c52Smaya      int len;
5569f464c52Smaya      next = strchr(p, ':');
5579f464c52Smaya      if (next == NULL)
5589f464c52Smaya         next = end;
5599f464c52Smaya
5609f464c52Smaya      len = next - p;
5611463c08dSmrg#if USE_ELF_TLS
5621463c08dSmrg      snprintf(path, sizeof(path), "%.*s/tls/%s%s.so", len,
5631463c08dSmrg               p, driver_name, lib_suffix);
5649f464c52Smaya      driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
5659f464c52Smaya#endif
5669f464c52Smaya      if (driver == NULL) {
5671463c08dSmrg         snprintf(path, sizeof(path), "%.*s/%s%s.so", len,
5681463c08dSmrg                  p, driver_name, lib_suffix);
5699f464c52Smaya         driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
5701463c08dSmrg         if (driver == NULL) {
5711463c08dSmrg            dl_error = dlerror();
5729f464c52Smaya            log_(_LOADER_DEBUG, "MESA-LOADER: failed to open %s: %s\n",
5731463c08dSmrg                 path, dl_error);
5741463c08dSmrg         }
5759f464c52Smaya      }
5769f464c52Smaya      /* not need continue to loop all paths once the driver is found */
5779f464c52Smaya      if (driver != NULL)
5789f464c52Smaya         break;
5799f464c52Smaya   }
5809f464c52Smaya
5819f464c52Smaya   if (driver == NULL) {
5821463c08dSmrg      if (warn_on_fail) {
5831463c08dSmrg         log_(_LOADER_WARNING,
5841463c08dSmrg              "MESA-LOADER: failed to open %s: %s (search paths %s, suffix %s)\n",
5851463c08dSmrg              driver_name, dl_error, search_paths, lib_suffix);
5861463c08dSmrg      }
5879f464c52Smaya      return NULL;
5889f464c52Smaya   }
5899f464c52Smaya
5909f464c52Smaya   log_(_LOADER_DEBUG, "MESA-LOADER: dlopen(%s)\n", path);
5919f464c52Smaya
5921463c08dSmrg   return driver;
5931463c08dSmrg}
5941463c08dSmrg
5951463c08dSmrg/**
5961463c08dSmrg * Opens a DRI driver using its driver name, returning the __DRIextension
5971463c08dSmrg * entrypoints.
5981463c08dSmrg *
5991463c08dSmrg * \param driverName - a name like "i965", "radeon", "nouveau", etc.
6001463c08dSmrg * \param out_driver - Address where the dlopen() return value will be stored.
6011463c08dSmrg * \param search_path_vars - NULL-terminated list of env vars that can be used
6021463c08dSmrg * to override the DEFAULT_DRIVER_DIR search path.
6031463c08dSmrg */
6041463c08dSmrgconst struct __DRIextensionRec **
6051463c08dSmrgloader_open_driver(const char *driver_name,
6061463c08dSmrg                   void **out_driver_handle,
6071463c08dSmrg                   const char **search_path_vars)
6081463c08dSmrg{
6091463c08dSmrg   char *get_extensions_name;
6101463c08dSmrg   const struct __DRIextensionRec **extensions = NULL;
6111463c08dSmrg   const struct __DRIextensionRec **(*get_extensions)(void);
6121463c08dSmrg   void *driver = loader_open_driver_lib(driver_name, "_dri", search_path_vars,
6131463c08dSmrg                                         DEFAULT_DRIVER_DIR, true);
6141463c08dSmrg
6151463c08dSmrg   if (!driver)
6161463c08dSmrg      goto failed;
6171463c08dSmrg
6189f464c52Smaya   get_extensions_name = loader_get_extensions_name(driver_name);
6199f464c52Smaya   if (get_extensions_name) {
6209f464c52Smaya      get_extensions = dlsym(driver, get_extensions_name);
6219f464c52Smaya      if (get_extensions) {
6229f464c52Smaya         extensions = get_extensions();
6239f464c52Smaya      } else {
6249f464c52Smaya         log_(_LOADER_DEBUG, "MESA-LOADER: driver does not expose %s(): %s\n",
6259f464c52Smaya              get_extensions_name, dlerror());
6269f464c52Smaya      }
6279f464c52Smaya      free(get_extensions_name);
6289f464c52Smaya   }
6299f464c52Smaya
6309f464c52Smaya   if (!extensions)
6319f464c52Smaya      extensions = dlsym(driver, __DRI_DRIVER_EXTENSIONS);
6329f464c52Smaya   if (extensions == NULL) {
6339f464c52Smaya      log_(_LOADER_WARNING,
6349f464c52Smaya           "MESA-LOADER: driver exports no extensions (%s)\n", dlerror());
6359f464c52Smaya      dlclose(driver);
6361463c08dSmrg      driver = NULL;
6379f464c52Smaya   }
6389f464c52Smaya
6391463c08dSmrgfailed:
6409f464c52Smaya   *out_driver_handle = driver;
6419f464c52Smaya   return extensions;
6429f464c52Smaya}
643