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
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:
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
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
26 * Rob Clark <robclark@freedesktop.org>
40 #include <sys/param.h>
42 #include <sys/mkdev.h>
44 #ifdef MAJOR_IN_SYSMACROS
45 #include <sys/sysmacros.h>
48 #include <GL/internal/dri_interface.h>
53 #define MAX_DRM_DEVICES 64
55 #include "util/xmlconfig.h"
56 #include "util/driconf.h"
60 #include "util/macros.h"
63 #include "pci_id_driver_map.h"
65 /* For systems like Hurd */
70 static void default_logger(int level
, const char *fmt
, ...)
72 if (level
<= _LOADER_WARNING
) {
75 vfprintf(stderr
, fmt
, args
);
80 static loader_logger
*log_
= default_logger
;
83 loader_open_device(const char *device_name
)
87 fd
= open(device_name
, O_RDWR
| O_CLOEXEC
);
88 if (fd
== -1 && errno
== EINVAL
)
91 fd
= open(device_name
, O_RDWR
);
93 fcntl(fd
, F_SETFD
, fcntl(fd
, F_GETFD
) | FD_CLOEXEC
);
95 if (fd
== -1 && errno
== EACCES
) {
96 log_(_LOADER_WARNING
, "failed to open %s: %s\n",
97 device_name
, strerror(errno
));
102 static char *loader_get_kernel_driver_name(int fd
)
106 drmVersionPtr version
= drmGetVersion(fd
);
109 log_(_LOADER_WARNING
, "failed to get driver name for fd %d\n", fd
);
113 driver
= strndup(version
->name
, version
->name_len
);
114 log_(driver
? _LOADER_DEBUG
: _LOADER_WARNING
, "using driver %s for %d\n",
117 drmFreeVersion(version
);
125 is_kernel_i915(int fd
)
127 char *kernel_driver
= loader_get_kernel_driver_name(fd
);
128 bool is_i915
= kernel_driver
&& strcmp(kernel_driver
, "i915") == 0;
134 #if defined(HAVE_LIBDRM)
136 loader_open_render_node(const char *name
)
138 drmDevicePtr devices
[MAX_DRM_DEVICES
], device
;
139 int i
, num_devices
, fd
= -1;
141 num_devices
= drmGetDevices2(0, devices
, MAX_DRM_DEVICES
);
142 if (num_devices
<= 0)
145 for (i
= 0; i
< num_devices
; i
++) {
148 if ((device
->available_nodes
& (1 << DRM_NODE_RENDER
)) &&
149 (device
->bustype
== DRM_BUS_PLATFORM
)) {
150 drmVersionPtr version
;
152 fd
= loader_open_device(device
->nodes
[DRM_NODE_RENDER
]);
156 version
= drmGetVersion(fd
);
162 if (strcmp(version
->name
, name
) != 0) {
163 drmFreeVersion(version
);
168 drmFreeVersion(version
);
172 drmFreeDevices(devices
, num_devices
);
174 if (i
== num_devices
)
181 static const char __driConfigOptionsLoader
[] =
183 DRI_CONF_SECTION_INITIALIZATION
184 DRI_CONF_DEVICE_ID_PATH_TAG()
185 DRI_CONF_DRI_DRIVER()
189 static char *loader_get_dri_config_driver(int fd
)
191 driOptionCache defaultInitOptions
;
192 driOptionCache userInitOptions
;
193 char *dri_driver
= NULL
;
194 char *kernel_driver
= loader_get_kernel_driver_name(fd
);
196 driParseOptionInfo(&defaultInitOptions
, __driConfigOptionsLoader
);
197 driParseConfigFiles(&userInitOptions
, &defaultInitOptions
, 0,
198 "loader", kernel_driver
, NULL
, 0);
199 if (driCheckOption(&userInitOptions
, "dri_driver", DRI_STRING
)) {
200 char *opt
= driQueryOptionstr(&userInitOptions
, "dri_driver");
201 /* not an empty string */
203 dri_driver
= strdup(opt
);
205 driDestroyOptionCache(&userInitOptions
);
206 driDestroyOptionInfo(&defaultInitOptions
);
212 static char *loader_get_dri_config_device_id(void)
214 driOptionCache defaultInitOptions
;
215 driOptionCache userInitOptions
;
218 driParseOptionInfo(&defaultInitOptions
, __driConfigOptionsLoader
);
219 driParseConfigFiles(&userInitOptions
, &defaultInitOptions
, 0,
220 "loader", NULL
, NULL
, 0);
221 if (driCheckOption(&userInitOptions
, "device_id", DRI_STRING
))
222 prime
= strdup(driQueryOptionstr(&userInitOptions
, "device_id"));
223 driDestroyOptionCache(&userInitOptions
);
224 driDestroyOptionInfo(&defaultInitOptions
);
230 static char *drm_construct_id_path_tag(drmDevicePtr device
)
234 if (device
->bustype
== DRM_BUS_PCI
) {
235 if (asprintf(&tag
, "pci-%04x_%02x_%02x_%1u",
236 device
->businfo
.pci
->domain
,
237 device
->businfo
.pci
->bus
,
238 device
->businfo
.pci
->dev
,
239 device
->businfo
.pci
->func
) < 0) {
242 } else if (device
->bustype
== DRM_BUS_PLATFORM
||
243 device
->bustype
== DRM_BUS_HOST1X
) {
244 char *fullname
, *name
, *address
;
246 if (device
->bustype
== DRM_BUS_PLATFORM
)
247 fullname
= device
->businfo
.platform
->fullname
;
249 fullname
= device
->businfo
.host1x
->fullname
;
251 name
= strrchr(fullname
, '/');
253 name
= strdup(fullname
);
255 name
= strdup(name
+ 1);
257 address
= strchr(name
, '@');
261 if (asprintf(&tag
, "platform-%s_%s", address
, name
) < 0)
264 if (asprintf(&tag
, "platform-%s", name
) < 0)
273 static bool drm_device_matches_tag(drmDevicePtr device
, const char *prime_tag
)
275 char *tag
= drm_construct_id_path_tag(device
);
281 ret
= strcmp(tag
, prime_tag
);
287 static char *drm_get_id_path_tag_for_fd(int fd
)
292 if (drmGetDevice2(fd
, 0, &device
) != 0)
295 tag
= drm_construct_id_path_tag(device
);
296 drmFreeDevice(&device
);
300 int loader_get_user_preferred_fd(int default_fd
, bool *different_device
)
302 const char *dri_prime
= getenv("DRI_PRIME");
303 char *default_tag
, *prime
= NULL
;
304 drmDevicePtr devices
[MAX_DRM_DEVICES
];
305 int i
, num_devices
, fd
= -1;
308 prime
= strdup(dri_prime
);
311 prime
= loader_get_dri_config_device_id();
315 *different_device
= false;
319 default_tag
= drm_get_id_path_tag_for_fd(default_fd
);
320 if (default_tag
== NULL
)
323 num_devices
= drmGetDevices2(0, devices
, MAX_DRM_DEVICES
);
324 if (num_devices
<= 0)
327 for (i
= 0; i
< num_devices
; i
++) {
328 if (!(devices
[i
]->available_nodes
& 1 << DRM_NODE_RENDER
))
331 /* two formats of DRI_PRIME are supported:
332 * "1": choose any other card than the card used by default.
333 * id_path_tag: (for example "pci-0000_02_00_0") choose the card
334 * with this id_path_tag.
336 if (!strcmp(prime
,"1")) {
337 if (drm_device_matches_tag(devices
[i
], default_tag
))
340 if (!drm_device_matches_tag(devices
[i
], prime
))
344 fd
= loader_open_device(devices
[i
]->nodes
[DRM_NODE_RENDER
]);
347 drmFreeDevices(devices
, num_devices
);
349 if (i
== num_devices
)
357 *different_device
= !!strcmp(default_tag
, prime
);
364 *different_device
= false;
372 loader_open_render_node(const char *name
)
377 int loader_get_user_preferred_fd(int default_fd
, bool *different_device
)
379 *different_device
= false;
384 #if defined(HAVE_LIBDRM)
387 drm_get_pci_id_for_fd(int fd
, int *vendor_id
, int *chip_id
)
391 if (drmGetDevice2(fd
, 0, &device
) != 0) {
392 log_(_LOADER_WARNING
, "MESA-LOADER: failed to retrieve device information\n");
396 if (device
->bustype
!= DRM_BUS_PCI
) {
397 drmFreeDevice(&device
);
398 log_(_LOADER_DEBUG
, "MESA-LOADER: device is not located on the PCI bus\n");
402 *vendor_id
= device
->deviceinfo
.pci
->vendor_id
;
403 *chip_id
= device
->deviceinfo
.pci
->device_id
;
404 drmFreeDevice(&device
);
411 loader_get_pci_id_for_fd(int fd
, int *vendor_id
, int *chip_id
)
414 return drm_get_pci_id_for_fd(fd
, vendor_id
, chip_id
);
420 loader_get_device_name_for_fd(int fd
)
425 result
= drmGetDeviceNameFromFd2(fd
);
432 loader_get_pci_driver(int fd
)
434 int vendor_id
, chip_id
, i
, j
;
437 if (!loader_get_pci_id_for_fd(fd
, &vendor_id
, &chip_id
))
440 for (i
= 0; i
< ARRAY_SIZE(driver_map
); i
++) {
441 if (vendor_id
!= driver_map
[i
].vendor_id
)
444 if (driver_map
[i
].predicate
&& !driver_map
[i
].predicate(fd
))
447 if (driver_map
[i
].num_chips_ids
== -1) {
448 driver
= strdup(driver_map
[i
].driver
);
452 for (j
= 0; j
< driver_map
[i
].num_chips_ids
; j
++)
453 if (driver_map
[i
].chip_ids
[j
] == chip_id
) {
454 driver
= strdup(driver_map
[i
].driver
);
460 log_(driver
? _LOADER_DEBUG
: _LOADER_WARNING
,
461 "pci id for fd %d: %04x:%04x, driver %s\n",
462 fd
, vendor_id
, chip_id
, driver
);
467 loader_get_driver_for_fd(int fd
)
471 /* Allow an environment variable to force choosing a different driver
472 * binary. If that driver binary can't survive on this FD, that's the
473 * user's problem, but this allows vc4 simulator to run on an i965 host,
474 * and may be useful for some touch testing of i915 on an i965 host.
476 if (geteuid() == getuid()) {
477 driver
= getenv("MESA_LOADER_DRIVER_OVERRIDE");
479 return strdup(driver
);
482 #if defined(HAVE_LIBDRM) && defined(USE_DRICONF)
483 driver
= loader_get_dri_config_driver(fd
);
488 driver
= loader_get_pci_driver(fd
);
490 driver
= loader_get_kernel_driver_name(fd
);
496 loader_set_logger(loader_logger
*logger
)
502 loader_get_extensions_name(const char *driver_name
)
506 if (asprintf(&name
, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS
, driver_name
) < 0)
509 const size_t len
= strlen(name
);
510 for (size_t i
= 0; i
< len
; i
++) {
519 * Opens a DRI driver using its driver name, returning the __DRIextension
522 * \param driverName - a name like "i965", "radeon", "nouveau", etc.
523 * \param out_driver - Address where the dlopen() return value will be stored.
524 * \param search_path_vars - NULL-terminated list of env vars that can be used
525 * to override the DEFAULT_DRIVER_DIR search path.
527 const struct __DRIextensionRec
**
528 loader_open_driver(const char *driver_name
,
529 void **out_driver_handle
,
530 const char **search_path_vars
)
532 char path
[PATH_MAX
], *search_paths
, *next
, *end
;
533 char *get_extensions_name
;
534 const struct __DRIextensionRec
**extensions
= NULL
;
535 const struct __DRIextensionRec
**(*get_extensions
)(void);
538 if (geteuid() == getuid() && search_path_vars
) {
539 for (int i
= 0; search_path_vars
[i
] != NULL
; i
++) {
540 search_paths
= getenv(search_path_vars
[i
]);
545 if (search_paths
== NULL
)
546 search_paths
= DEFAULT_DRIVER_DIR
;
549 end
= search_paths
+ strlen(search_paths
);
550 for (char *p
= search_paths
; p
< end
; p
= next
+ 1) {
552 next
= strchr(p
, ':');
558 snprintf(path
, sizeof(path
), "%.*s/tls/%s_dri.so", len
, p
, driver_name
);
559 driver
= dlopen(path
, RTLD_NOW
| RTLD_GLOBAL
);
561 if (driver
== NULL
) {
562 snprintf(path
, sizeof(path
), "%.*s/%s_dri.so", len
, p
, driver_name
);
563 driver
= dlopen(path
, RTLD_NOW
| RTLD_GLOBAL
);
565 log_(_LOADER_DEBUG
, "MESA-LOADER: failed to open %s: %s\n",
568 /* not need continue to loop all paths once the driver is found */
573 if (driver
== NULL
) {
574 log_(_LOADER_WARNING
, "MESA-LOADER: failed to open %s (search paths %s)\n",
575 driver_name
, search_paths
);
576 *out_driver_handle
= NULL
;
580 log_(_LOADER_DEBUG
, "MESA-LOADER: dlopen(%s)\n", path
);
582 get_extensions_name
= loader_get_extensions_name(driver_name
);
583 if (get_extensions_name
) {
584 get_extensions
= dlsym(driver
, get_extensions_name
);
585 if (get_extensions
) {
586 extensions
= get_extensions();
588 log_(_LOADER_DEBUG
, "MESA-LOADER: driver does not expose %s(): %s\n",
589 get_extensions_name
, dlerror());
591 free(get_extensions_name
);
595 extensions
= dlsym(driver
, __DRI_DRIVER_EXTENSIONS
);
596 if (extensions
== NULL
) {
597 log_(_LOADER_WARNING
,
598 "MESA-LOADER: driver exports no extensions (%s)\n", dlerror());
602 *out_driver_handle
= driver
;