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