Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/loader/loader.c
4545 views
1
/*
2
* Copyright (C) 2013 Rob Clark <[email protected]>
3
* Copyright (C) 2014-2016 Emil Velikov <[email protected]>
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 <[email protected]>
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
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
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
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
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
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
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
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
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
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
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
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
373
loader_open_render_node(const char *name)
374
{
375
return -1;
376
}
377
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
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
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 *
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 *
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 *
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
497
loader_set_logger(loader_logger *logger)
498
{
499
log_ = logger;
500
}
501
502
char *
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 driver or backend using its name, returning the library handle.
521
*
522
* \param driverName - a name like "i965", "radeon", "nouveau", etc.
523
* \param lib_suffix - a suffix to append to the driver name to generate the
524
* full library name.
525
* \param search_path_vars - NULL-terminated list of env vars that can be used
526
* \param default_search_path - a colon-separted list of directories used if
527
* search_path_vars is NULL or none of the vars are set in the environment.
528
* \param warn_on_fail - Log a warning if the driver is not found.
529
*/
530
void *
531
loader_open_driver_lib(const char *driver_name,
532
const char *lib_suffix,
533
const char **search_path_vars,
534
const char *default_search_path,
535
bool warn_on_fail)
536
{
537
char path[PATH_MAX];
538
const char *search_paths, *next, *end;
539
540
search_paths = NULL;
541
if (geteuid() == getuid() && search_path_vars) {
542
for (int i = 0; search_path_vars[i] != NULL; i++) {
543
search_paths = getenv(search_path_vars[i]);
544
if (search_paths)
545
break;
546
}
547
}
548
if (search_paths == NULL)
549
search_paths = default_search_path;
550
551
void *driver = NULL;
552
const char *dl_error = NULL;
553
end = search_paths + strlen(search_paths);
554
for (const char *p = search_paths; p < end; p = next + 1) {
555
int len;
556
next = strchr(p, ':');
557
if (next == NULL)
558
next = end;
559
560
len = next - p;
561
#if USE_ELF_TLS
562
snprintf(path, sizeof(path), "%.*s/tls/%s%s.so", len,
563
p, driver_name, lib_suffix);
564
driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
565
#endif
566
if (driver == NULL) {
567
snprintf(path, sizeof(path), "%.*s/%s%s.so", len,
568
p, driver_name, lib_suffix);
569
driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
570
if (driver == NULL) {
571
dl_error = dlerror();
572
log_(_LOADER_DEBUG, "MESA-LOADER: failed to open %s: %s\n",
573
path, dl_error);
574
}
575
}
576
/* not need continue to loop all paths once the driver is found */
577
if (driver != NULL)
578
break;
579
}
580
581
if (driver == NULL) {
582
if (warn_on_fail) {
583
log_(_LOADER_WARNING,
584
"MESA-LOADER: failed to open %s: %s (search paths %s, suffix %s)\n",
585
driver_name, dl_error, search_paths, lib_suffix);
586
}
587
return NULL;
588
}
589
590
log_(_LOADER_DEBUG, "MESA-LOADER: dlopen(%s)\n", path);
591
592
return driver;
593
}
594
595
/**
596
* Opens a DRI driver using its driver name, returning the __DRIextension
597
* entrypoints.
598
*
599
* \param driverName - a name like "i965", "radeon", "nouveau", etc.
600
* \param out_driver - Address where the dlopen() return value will be stored.
601
* \param search_path_vars - NULL-terminated list of env vars that can be used
602
* to override the DEFAULT_DRIVER_DIR search path.
603
*/
604
const struct __DRIextensionRec **
605
loader_open_driver(const char *driver_name,
606
void **out_driver_handle,
607
const char **search_path_vars)
608
{
609
char *get_extensions_name;
610
const struct __DRIextensionRec **extensions = NULL;
611
const struct __DRIextensionRec **(*get_extensions)(void);
612
void *driver = loader_open_driver_lib(driver_name, "_dri", search_path_vars,
613
DEFAULT_DRIVER_DIR, true);
614
615
if (!driver)
616
goto failed;
617
618
get_extensions_name = loader_get_extensions_name(driver_name);
619
if (get_extensions_name) {
620
get_extensions = dlsym(driver, get_extensions_name);
621
if (get_extensions) {
622
extensions = get_extensions();
623
} else {
624
log_(_LOADER_DEBUG, "MESA-LOADER: driver does not expose %s(): %s\n",
625
get_extensions_name, dlerror());
626
}
627
free(get_extensions_name);
628
}
629
630
if (!extensions)
631
extensions = dlsym(driver, __DRI_DRIVER_EXTENSIONS);
632
if (extensions == NULL) {
633
log_(_LOADER_WARNING,
634
"MESA-LOADER: driver exports no extensions (%s)\n", dlerror());
635
dlclose(driver);
636
driver = NULL;
637
}
638
639
failed:
640
*out_driver_handle = driver;
641
return extensions;
642
}
643
644