loader.c revision 757f638f
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>
399f464c52Smaya#include <sys/param.h>
4001e04c3fSmrg#ifdef MAJOR_IN_MKDEV
4101e04c3fSmrg#include <sys/mkdev.h>
42af69d88dSmrg#endif
4301e04c3fSmrg#ifdef MAJOR_IN_SYSMACROS
4401e04c3fSmrg#include <sys/sysmacros.h>
45af69d88dSmrg#endif
469f464c52Smaya#include <GL/gl.h>
479f464c52Smaya#include <GL/internal/dri_interface.h>
48af69d88dSmrg#include "loader.h"
49af69d88dSmrg
5001e04c3fSmrg#ifdef HAVE_LIBDRM
51af69d88dSmrg#include <xf86drm.h>
5201e04c3fSmrg#ifdef USE_DRICONF
5301e04c3fSmrg#include "util/xmlconfig.h"
5401e04c3fSmrg#include "util/xmlpool.h"
5501e04c3fSmrg#endif
56af69d88dSmrg#endif
57af69d88dSmrg
58af69d88dSmrg#define __IS_LOADER
59af69d88dSmrg#include "pci_id_driver_map.h"
60af69d88dSmrg
61af69d88dSmrgstatic void default_logger(int level, const char *fmt, ...)
62af69d88dSmrg{
63af69d88dSmrg   if (level <= _LOADER_WARNING) {
64af69d88dSmrg      va_list args;
65af69d88dSmrg      va_start(args, fmt);
66af69d88dSmrg      vfprintf(stderr, fmt, args);
67af69d88dSmrg      va_end(args);
68af69d88dSmrg   }
69af69d88dSmrg}
70af69d88dSmrg
719f464c52Smayastatic loader_logger *log_ = default_logger;
72af69d88dSmrg
7301e04c3fSmrgint
7401e04c3fSmrgloader_open_device(const char *device_name)
75af69d88dSmrg{
7601e04c3fSmrg   int fd;
7701e04c3fSmrg#ifdef O_CLOEXEC
7801e04c3fSmrg   fd = open(device_name, O_RDWR | O_CLOEXEC);
7901e04c3fSmrg   if (fd == -1 && errno == EINVAL)
8001e04c3fSmrg#endif
8101e04c3fSmrg   {
8201e04c3fSmrg      fd = open(device_name, O_RDWR);
8301e04c3fSmrg      if (fd != -1)
8401e04c3fSmrg         fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
85af69d88dSmrg   }
8601e04c3fSmrg   return fd;
87af69d88dSmrg}
88af69d88dSmrg
8901e04c3fSmrgstatic char *loader_get_kernel_driver_name(int fd)
90af69d88dSmrg{
9101e04c3fSmrg#if HAVE_LIBDRM
9201e04c3fSmrg   char *driver;
9301e04c3fSmrg   drmVersionPtr version = drmGetVersion(fd);
9401e04c3fSmrg
9501e04c3fSmrg   if (!version) {
9601e04c3fSmrg      log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd);
9701e04c3fSmrg      return NULL;
9801e04c3fSmrg   }
99af69d88dSmrg
10001e04c3fSmrg   driver = strndup(version->name, version->name_len);
101af69d88dSmrg
10201e04c3fSmrg   drmFreeVersion(version);
10301e04c3fSmrg   return driver;
10401e04c3fSmrg#else
10501e04c3fSmrg   return NULL;
10601e04c3fSmrg#endif
10701e04c3fSmrg}
108af69d88dSmrg
10901e04c3fSmrg#if defined(HAVE_LIBDRM)
11001e04c3fSmrgint
11101e04c3fSmrgloader_open_render_node(const char *name)
112af69d88dSmrg{
11301e04c3fSmrg   drmDevicePtr *devices, device;
11401e04c3fSmrg   int err, render = -ENOENT, fd;
11501e04c3fSmrg   unsigned int num, i;
116af69d88dSmrg
11701e04c3fSmrg   err = drmGetDevices2(0, NULL, 0);
11801e04c3fSmrg   if (err < 0)
11901e04c3fSmrg      return err;
120af69d88dSmrg
12101e04c3fSmrg   num = err;
12201e04c3fSmrg
12301e04c3fSmrg   devices = calloc(num, sizeof(*devices));
12401e04c3fSmrg   if (!devices)
12501e04c3fSmrg      return -ENOMEM;
12601e04c3fSmrg
12701e04c3fSmrg   err = drmGetDevices2(0, devices, num);
12801e04c3fSmrg   if (err < 0) {
12901e04c3fSmrg      render = err;
13001e04c3fSmrg      goto free;
131af69d88dSmrg   }
132af69d88dSmrg
13301e04c3fSmrg   for (i = 0; i < num; i++) {
13401e04c3fSmrg      device = devices[i];
13501e04c3fSmrg
13601e04c3fSmrg      if ((device->available_nodes & (1 << DRM_NODE_RENDER)) &&
13701e04c3fSmrg          (device->bustype == DRM_BUS_PLATFORM)) {
13801e04c3fSmrg         drmVersionPtr version;
13901e04c3fSmrg
1409f464c52Smaya         fd = loader_open_device(device->nodes[DRM_NODE_RENDER]);
14101e04c3fSmrg         if (fd < 0)
14201e04c3fSmrg            continue;
14301e04c3fSmrg
14401e04c3fSmrg         version = drmGetVersion(fd);
14501e04c3fSmrg         if (!version) {
14601e04c3fSmrg            close(fd);
14701e04c3fSmrg            continue;
14801e04c3fSmrg         }
14901e04c3fSmrg
15001e04c3fSmrg         if (strcmp(version->name, name) != 0) {
15101e04c3fSmrg            drmFreeVersion(version);
15201e04c3fSmrg            close(fd);
15301e04c3fSmrg            continue;
15401e04c3fSmrg         }
15501e04c3fSmrg
15601e04c3fSmrg         drmFreeVersion(version);
15701e04c3fSmrg         render = fd;
15801e04c3fSmrg         break;
15901e04c3fSmrg      }
160af69d88dSmrg   }
161af69d88dSmrg
16201e04c3fSmrg   drmFreeDevices(devices, num);
16301e04c3fSmrg
16401e04c3fSmrgfree:
16501e04c3fSmrg   free(devices);
16601e04c3fSmrg   return render;
167af69d88dSmrg}
168af69d88dSmrg
16901e04c3fSmrg#ifdef USE_DRICONF
17001e04c3fSmrgstatic const char __driConfigOptionsLoader[] =
17101e04c3fSmrgDRI_CONF_BEGIN
17201e04c3fSmrg    DRI_CONF_SECTION_INITIALIZATION
17301e04c3fSmrg        DRI_CONF_DEVICE_ID_PATH_TAG()
17401e04c3fSmrg        DRI_CONF_DRI_DRIVER()
17501e04c3fSmrg    DRI_CONF_SECTION_END
17601e04c3fSmrgDRI_CONF_END;
17701e04c3fSmrg
17801e04c3fSmrgstatic char *loader_get_dri_config_driver(int fd)
179af69d88dSmrg{
18001e04c3fSmrg   driOptionCache defaultInitOptions;
18101e04c3fSmrg   driOptionCache userInitOptions;
18201e04c3fSmrg   char *dri_driver = NULL;
18301e04c3fSmrg   char *kernel_driver = loader_get_kernel_driver_name(fd);
18401e04c3fSmrg
18501e04c3fSmrg   driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader);
18601e04c3fSmrg   driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0,
18701e04c3fSmrg                       "loader", kernel_driver);
18801e04c3fSmrg   if (driCheckOption(&userInitOptions, "dri_driver", DRI_STRING)) {
18901e04c3fSmrg      char *opt = driQueryOptionstr(&userInitOptions, "dri_driver");
19001e04c3fSmrg      /* not an empty string */
19101e04c3fSmrg      if (*opt)
19201e04c3fSmrg         dri_driver = strdup(opt);
193af69d88dSmrg   }
19401e04c3fSmrg   driDestroyOptionCache(&userInitOptions);
19501e04c3fSmrg   driDestroyOptionInfo(&defaultInitOptions);
196af69d88dSmrg
19701e04c3fSmrg   free(kernel_driver);
19801e04c3fSmrg   return dri_driver;
19901e04c3fSmrg}
200af69d88dSmrg
20101e04c3fSmrgstatic char *loader_get_dri_config_device_id(void)
20201e04c3fSmrg{
20301e04c3fSmrg   driOptionCache defaultInitOptions;
20401e04c3fSmrg   driOptionCache userInitOptions;
20501e04c3fSmrg   char *prime = NULL;
206af69d88dSmrg
20701e04c3fSmrg   driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader);
20801e04c3fSmrg   driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, "loader", NULL);
20901e04c3fSmrg   if (driCheckOption(&userInitOptions, "device_id", DRI_STRING))
21001e04c3fSmrg      prime = strdup(driQueryOptionstr(&userInitOptions, "device_id"));
21101e04c3fSmrg   driDestroyOptionCache(&userInitOptions);
21201e04c3fSmrg   driDestroyOptionInfo(&defaultInitOptions);
21301e04c3fSmrg
21401e04c3fSmrg   return prime;
215af69d88dSmrg}
21601e04c3fSmrg#endif
217af69d88dSmrg
21801e04c3fSmrgstatic char *drm_construct_id_path_tag(drmDevicePtr device)
219af69d88dSmrg{
22001e04c3fSmrg   char *tag = NULL;
22101e04c3fSmrg
22201e04c3fSmrg   if (device->bustype == DRM_BUS_PCI) {
22301e04c3fSmrg      if (asprintf(&tag, "pci-%04x_%02x_%02x_%1u",
22401e04c3fSmrg                   device->businfo.pci->domain,
22501e04c3fSmrg                   device->businfo.pci->bus,
22601e04c3fSmrg                   device->businfo.pci->dev,
22701e04c3fSmrg                   device->businfo.pci->func) < 0) {
22801e04c3fSmrg         return NULL;
229af69d88dSmrg      }
23001e04c3fSmrg   } else if (device->bustype == DRM_BUS_PLATFORM ||
23101e04c3fSmrg              device->bustype == DRM_BUS_HOST1X) {
23201e04c3fSmrg      char *fullname, *name, *address;
23301e04c3fSmrg
23401e04c3fSmrg      if (device->bustype == DRM_BUS_PLATFORM)
23501e04c3fSmrg         fullname = device->businfo.platform->fullname;
23601e04c3fSmrg      else
23701e04c3fSmrg         fullname = device->businfo.host1x->fullname;
238af69d88dSmrg
23901e04c3fSmrg      name = strrchr(fullname, '/');
24001e04c3fSmrg      if (!name)
24101e04c3fSmrg         name = strdup(fullname);
24201e04c3fSmrg      else
24301e04c3fSmrg         name = strdup(name + 1);
24401e04c3fSmrg
24501e04c3fSmrg      address = strchr(name, '@');
24601e04c3fSmrg      if (address) {
24701e04c3fSmrg         *address++ = '\0';
24801e04c3fSmrg
24901e04c3fSmrg         if (asprintf(&tag, "platform-%s_%s", address, name) < 0)
25001e04c3fSmrg            tag = NULL;
25101e04c3fSmrg      } else {
25201e04c3fSmrg         if (asprintf(&tag, "platform-%s", name) < 0)
25301e04c3fSmrg            tag = NULL;
25401e04c3fSmrg      }
25501e04c3fSmrg
25601e04c3fSmrg      free(name);
257af69d88dSmrg   }
25801e04c3fSmrg   return tag;
259af69d88dSmrg}
260af69d88dSmrg
26101e04c3fSmrgstatic bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag)
262af69d88dSmrg{
26301e04c3fSmrg   char *tag = drm_construct_id_path_tag(device);
26401e04c3fSmrg   int ret;
265af69d88dSmrg
26601e04c3fSmrg   if (tag == NULL)
26701e04c3fSmrg      return false;
268af69d88dSmrg
26901e04c3fSmrg   ret = strcmp(tag, prime_tag);
270af69d88dSmrg
27101e04c3fSmrg   free(tag);
27201e04c3fSmrg   return ret == 0;
273af69d88dSmrg}
274af69d88dSmrg
27501e04c3fSmrgstatic char *drm_get_id_path_tag_for_fd(int fd)
276af69d88dSmrg{
27701e04c3fSmrg   drmDevicePtr device;
27801e04c3fSmrg   char *tag;
279af69d88dSmrg
28001e04c3fSmrg   if (drmGetDevice2(fd, 0, &device) != 0)
28101e04c3fSmrg       return NULL;
28201e04c3fSmrg
28301e04c3fSmrg   tag = drm_construct_id_path_tag(device);
28401e04c3fSmrg   drmFreeDevice(&device);
28501e04c3fSmrg   return tag;
28601e04c3fSmrg}
287af69d88dSmrg
28801e04c3fSmrgint loader_get_user_preferred_fd(int default_fd, bool *different_device)
289af69d88dSmrg{
29001e04c3fSmrg/* Arbitrary "maximum" value of drm devices. */
29101e04c3fSmrg#define MAX_DRM_DEVICES 32
292af69d88dSmrg   const char *dri_prime = getenv("DRI_PRIME");
29301e04c3fSmrg   char *default_tag, *prime = NULL;
29401e04c3fSmrg   drmDevicePtr devices[MAX_DRM_DEVICES];
29501e04c3fSmrg   int i, num_devices, fd;
29601e04c3fSmrg   bool found = false;
297af69d88dSmrg
298af69d88dSmrg   if (dri_prime)
299af69d88dSmrg      prime = strdup(dri_prime);
300af69d88dSmrg#ifdef USE_DRICONF
30101e04c3fSmrg   else
30201e04c3fSmrg      prime = loader_get_dri_config_device_id();
303af69d88dSmrg#endif
304af69d88dSmrg
305af69d88dSmrg   if (prime == NULL) {
30601e04c3fSmrg      *different_device = false;
307af69d88dSmrg      return default_fd;
308af69d88dSmrg   }
309af69d88dSmrg
31001e04c3fSmrg   default_tag = drm_get_id_path_tag_for_fd(default_fd);
31101e04c3fSmrg   if (default_tag == NULL)
31201e04c3fSmrg      goto err;
313af69d88dSmrg
31401e04c3fSmrg   num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
31501e04c3fSmrg   if (num_devices < 0)
31601e04c3fSmrg      goto err;
317af69d88dSmrg
318af69d88dSmrg   /* two format are supported:
319af69d88dSmrg    * "1": choose any other card than the card used by default.
320af69d88dSmrg    * id_path_tag: (for example "pci-0000_02_00_0") choose the card
321af69d88dSmrg    * with this id_path_tag.
322af69d88dSmrg    */
323af69d88dSmrg   if (!strcmp(prime,"1")) {
32401e04c3fSmrg      /* Hmm... detection for 2-7 seems to be broken. Oh well ...
32501e04c3fSmrg       * Pick the first render device that is not our own.
32601e04c3fSmrg       */
32701e04c3fSmrg      for (i = 0; i < num_devices; i++) {
32801e04c3fSmrg         if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
32901e04c3fSmrg             !drm_device_matches_tag(devices[i], default_tag)) {
33001e04c3fSmrg
33101e04c3fSmrg            found = true;
33201e04c3fSmrg            break;
33301e04c3fSmrg         }
33401e04c3fSmrg      }
33501e04c3fSmrg   } else {
33601e04c3fSmrg      for (i = 0; i < num_devices; i++) {
33701e04c3fSmrg         if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
33801e04c3fSmrg            drm_device_matches_tag(devices[i], prime)) {
33901e04c3fSmrg
34001e04c3fSmrg            found = true;
34101e04c3fSmrg            break;
34201e04c3fSmrg         }
34301e04c3fSmrg      }
344af69d88dSmrg   }
345af69d88dSmrg
34601e04c3fSmrg   if (!found) {
34701e04c3fSmrg      drmFreeDevices(devices, num_devices);
34801e04c3fSmrg      goto err;
349af69d88dSmrg   }
350af69d88dSmrg
35101e04c3fSmrg   fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]);
35201e04c3fSmrg   drmFreeDevices(devices, num_devices);
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
387af69d88dSmrgstatic int
388af69d88dSmrgdrm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
389af69d88dSmrg{
39001e04c3fSmrg   drmDevicePtr device;
39101e04c3fSmrg   int ret;
39201e04c3fSmrg
39301e04c3fSmrg   if (drmGetDevice2(fd, 0, &device) == 0) {
39401e04c3fSmrg      if (device->bustype == DRM_BUS_PCI) {
39501e04c3fSmrg         *vendor_id = device->deviceinfo.pci->vendor_id;
39601e04c3fSmrg         *chip_id = device->deviceinfo.pci->device_id;
39701e04c3fSmrg         ret = 1;
398af69d88dSmrg      }
39901e04c3fSmrg      else {
40001e04c3fSmrg         log_(_LOADER_DEBUG, "MESA-LOADER: device is not located on the PCI bus\n");
40101e04c3fSmrg         ret = 0;
402af69d88dSmrg      }
40301e04c3fSmrg      drmFreeDevice(&device);
404af69d88dSmrg   }
40501e04c3fSmrg   else {
40601e04c3fSmrg      log_(_LOADER_WARNING, "MESA-LOADER: failed to retrieve device information\n");
40701e04c3fSmrg      ret = 0;
408af69d88dSmrg   }
409af69d88dSmrg
41001e04c3fSmrg   return ret;
411af69d88dSmrg}
412af69d88dSmrg#endif
413af69d88dSmrg
414af69d88dSmrg
415af69d88dSmrgint
416af69d88dSmrgloader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
417af69d88dSmrg{
41801e04c3fSmrg#if HAVE_LIBDRM
419af69d88dSmrg   if (drm_get_pci_id_for_fd(fd, vendor_id, chip_id))
420af69d88dSmrg      return 1;
421af69d88dSmrg#endif
422af69d88dSmrg   return 0;
423af69d88dSmrg}
424af69d88dSmrg
425af69d88dSmrgchar *
426af69d88dSmrgloader_get_device_name_for_fd(int fd)
427af69d88dSmrg{
428af69d88dSmrg   char *result = NULL;
429af69d88dSmrg
43001e04c3fSmrg#if HAVE_LIBDRM
43101e04c3fSmrg   result = drmGetDeviceNameFromFd2(fd);
432af69d88dSmrg#endif
43301e04c3fSmrg
434af69d88dSmrg   return result;
435af69d88dSmrg}
436af69d88dSmrg
437af69d88dSmrgchar *
43801e04c3fSmrgloader_get_driver_for_fd(int fd)
439af69d88dSmrg{
440af69d88dSmrg   int vendor_id, chip_id, i, j;
441af69d88dSmrg   char *driver = NULL;
442af69d88dSmrg
44301e04c3fSmrg   /* Allow an environment variable to force choosing a different driver
44401e04c3fSmrg    * binary.  If that driver binary can't survive on this FD, that's the
44501e04c3fSmrg    * user's problem, but this allows vc4 simulator to run on an i965 host,
44601e04c3fSmrg    * and may be useful for some touch testing of i915 on an i965 host.
44701e04c3fSmrg    */
448757f638fSmaya   if (!issetugid()) {
44901e04c3fSmrg      driver = getenv("MESA_LOADER_DRIVER_OVERRIDE");
45001e04c3fSmrg      if (driver)
45101e04c3fSmrg         return strdup(driver);
45201e04c3fSmrg   }
453af69d88dSmrg
45401e04c3fSmrg#if defined(HAVE_LIBDRM) && defined(USE_DRICONF)
45501e04c3fSmrg   driver = loader_get_dri_config_driver(fd);
45601e04c3fSmrg   if (driver)
45701e04c3fSmrg      return driver;
458af69d88dSmrg#endif
459af69d88dSmrg
46001e04c3fSmrg   if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) {
46101e04c3fSmrg      driver = loader_get_kernel_driver_name(fd);
46201e04c3fSmrg      if (driver)
46301e04c3fSmrg         log_(_LOADER_INFO, "using driver %s for %d\n", driver, fd);
464af69d88dSmrg      return driver;
465af69d88dSmrg   }
466af69d88dSmrg
467af69d88dSmrg   for (i = 0; driver_map[i].driver; i++) {
468af69d88dSmrg      if (vendor_id != driver_map[i].vendor_id)
469af69d88dSmrg         continue;
470af69d88dSmrg
471af69d88dSmrg      if (driver_map[i].predicate && !driver_map[i].predicate(fd))
472af69d88dSmrg         continue;
473af69d88dSmrg
474af69d88dSmrg      if (driver_map[i].num_chips_ids == -1) {
475af69d88dSmrg         driver = strdup(driver_map[i].driver);
476af69d88dSmrg         goto out;
477af69d88dSmrg      }
478af69d88dSmrg
479af69d88dSmrg      for (j = 0; j < driver_map[i].num_chips_ids; j++)
480af69d88dSmrg         if (driver_map[i].chip_ids[j] == chip_id) {
481af69d88dSmrg            driver = strdup(driver_map[i].driver);
482af69d88dSmrg            goto out;
483af69d88dSmrg         }
484af69d88dSmrg   }
485af69d88dSmrg
486af69d88dSmrgout:
487af69d88dSmrg   log_(driver ? _LOADER_DEBUG : _LOADER_WARNING,
488af69d88dSmrg         "pci id for fd %d: %04x:%04x, driver %s\n",
489af69d88dSmrg         fd, vendor_id, chip_id, driver);
490af69d88dSmrg   return driver;
491af69d88dSmrg}
492af69d88dSmrg
493af69d88dSmrgvoid
4949f464c52Smayaloader_set_logger(loader_logger *logger)
495af69d88dSmrg{
496af69d88dSmrg   log_ = logger;
497af69d88dSmrg}
49801e04c3fSmrg
49901e04c3fSmrgchar *
50001e04c3fSmrgloader_get_extensions_name(const char *driver_name)
50101e04c3fSmrg{
50201e04c3fSmrg   char *name = NULL;
50301e04c3fSmrg
50401e04c3fSmrg   if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0)
50501e04c3fSmrg      return NULL;
50601e04c3fSmrg
50701e04c3fSmrg   const size_t len = strlen(name);
50801e04c3fSmrg   for (size_t i = 0; i < len; i++) {
50901e04c3fSmrg      if (name[i] == '-')
51001e04c3fSmrg         name[i] = '_';
51101e04c3fSmrg   }
51201e04c3fSmrg
51301e04c3fSmrg   return name;
51401e04c3fSmrg}
5159f464c52Smaya
5169f464c52Smaya/**
5179f464c52Smaya * Opens a DRI driver using its driver name, returning the __DRIextension
5189f464c52Smaya * entrypoints.
5199f464c52Smaya *
5209f464c52Smaya * \param driverName - a name like "i965", "radeon", "nouveau", etc.
5219f464c52Smaya * \param out_driver - Address where the dlopen() return value will be stored.
5229f464c52Smaya * \param search_path_vars - NULL-terminated list of env vars that can be used
5239f464c52Smaya * to override the DEFAULT_DRIVER_DIR search path.
5249f464c52Smaya */
5259f464c52Smayaconst struct __DRIextensionRec **
5269f464c52Smayaloader_open_driver(const char *driver_name,
5279f464c52Smaya                   void **out_driver_handle,
5289f464c52Smaya                   const char **search_path_vars)
5299f464c52Smaya{
5309f464c52Smaya   char path[PATH_MAX], *search_paths, *next, *end;
5319f464c52Smaya   char *get_extensions_name;
5329f464c52Smaya   const struct __DRIextensionRec **extensions = NULL;
5339f464c52Smaya   const struct __DRIextensionRec **(*get_extensions)(void);
5349f464c52Smaya
5359f464c52Smaya   search_paths = NULL;
536757f638fSmaya   if (!issetugid() && search_path_vars) {
5379f464c52Smaya      for (int i = 0; search_path_vars[i] != NULL; i++) {
5389f464c52Smaya         search_paths = getenv(search_path_vars[i]);
5399f464c52Smaya         if (search_paths)
5409f464c52Smaya            break;
5419f464c52Smaya      }
5429f464c52Smaya   }
5439f464c52Smaya   if (search_paths == NULL)
5449f464c52Smaya      search_paths = DEFAULT_DRIVER_DIR;
5459f464c52Smaya
5469f464c52Smaya   void *driver = NULL;
5479f464c52Smaya   end = search_paths + strlen(search_paths);
5489f464c52Smaya   for (char *p = search_paths; p < end; p = next + 1) {
5499f464c52Smaya      int len;
5509f464c52Smaya      next = strchr(p, ':');
5519f464c52Smaya      if (next == NULL)
5529f464c52Smaya         next = end;
5539f464c52Smaya
5549f464c52Smaya      len = next - p;
5559f464c52Smaya#if GLX_USE_TLS
5569f464c52Smaya      snprintf(path, sizeof(path), "%.*s/tls/%s_dri.so", len, p, driver_name);
5579f464c52Smaya      driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
5589f464c52Smaya#endif
5599f464c52Smaya      if (driver == NULL) {
5609f464c52Smaya         snprintf(path, sizeof(path), "%.*s/%s_dri.so", len, p, driver_name);
5619f464c52Smaya         driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
5629f464c52Smaya         if (driver == NULL)
5639f464c52Smaya            log_(_LOADER_DEBUG, "MESA-LOADER: failed to open %s: %s\n",
5649f464c52Smaya                 path, dlerror());
5659f464c52Smaya      }
5669f464c52Smaya      /* not need continue to loop all paths once the driver is found */
5679f464c52Smaya      if (driver != NULL)
5689f464c52Smaya         break;
5699f464c52Smaya   }
5709f464c52Smaya
5719f464c52Smaya   if (driver == NULL) {
5729f464c52Smaya      log_(_LOADER_WARNING, "MESA-LOADER: failed to open %s (search paths %s)\n",
5739f464c52Smaya           driver_name, search_paths);
5749f464c52Smaya      *out_driver_handle = NULL;
5759f464c52Smaya      return NULL;
5769f464c52Smaya   }
5779f464c52Smaya
5789f464c52Smaya   log_(_LOADER_DEBUG, "MESA-LOADER: dlopen(%s)\n", path);
5799f464c52Smaya
5809f464c52Smaya   get_extensions_name = loader_get_extensions_name(driver_name);
5819f464c52Smaya   if (get_extensions_name) {
5829f464c52Smaya      get_extensions = dlsym(driver, get_extensions_name);
5839f464c52Smaya      if (get_extensions) {
5849f464c52Smaya         extensions = get_extensions();
5859f464c52Smaya      } else {
5869f464c52Smaya         log_(_LOADER_DEBUG, "MESA-LOADER: driver does not expose %s(): %s\n",
5879f464c52Smaya              get_extensions_name, dlerror());
5889f464c52Smaya      }
5899f464c52Smaya      free(get_extensions_name);
5909f464c52Smaya   }
5919f464c52Smaya
5929f464c52Smaya   if (!extensions)
5939f464c52Smaya      extensions = dlsym(driver, __DRI_DRIVER_EXTENSIONS);
5949f464c52Smaya   if (extensions == NULL) {
5959f464c52Smaya      log_(_LOADER_WARNING,
5969f464c52Smaya           "MESA-LOADER: driver exports no extensions (%s)\n", dlerror());
5979f464c52Smaya      dlclose(driver);
5989f464c52Smaya   }
5999f464c52Smaya
6009f464c52Smaya   *out_driver_handle = driver;
6019f464c52Smaya   return extensions;
6029f464c52Smaya}
603