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