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