Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/drm-shim/drm_shim.c
4558 views
1
/*
2
* Copyright © 2018 Broadcom
3
*
4
* Permission is hereby granted, free of charge, to any person obtaining a
5
* copy of this software and associated documentation files (the "Software"),
6
* to deal in the Software without restriction, including without limitation
7
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
* and/or sell copies of the Software, and to permit persons to whom the
9
* Software is furnished to do so, subject to the following conditions:
10
*
11
* The above copyright notice and this permission notice (including the next
12
* paragraph) shall be included in all copies or substantial portions of the
13
* Software.
14
*
15
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
* DEALINGS IN THE SOFTWARE.
22
*/
23
24
/**
25
* @file
26
*
27
* Implements wrappers of libc functions to fake having a DRM device that
28
* isn't actually present in the kernel.
29
*/
30
31
/* Prevent glibc from defining open64 when we want to alias it. */
32
#undef _FILE_OFFSET_BITS
33
#define _LARGEFILE64_SOURCE
34
35
#include <stdbool.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
#include <sys/ioctl.h>
41
#include <sys/mman.h>
42
#include <sys/stat.h>
43
#include <sys/sysmacros.h>
44
#include <stdarg.h>
45
#include <fcntl.h>
46
#include <dlfcn.h>
47
#include <dirent.h>
48
#include <c11/threads.h>
49
#include <drm-uapi/drm.h>
50
51
#include "util/set.h"
52
#include "util/u_debug.h"
53
#include "drm_shim.h"
54
55
#define REAL_FUNCTION_POINTER(x) typeof(x) *real_##x
56
57
static mtx_t shim_lock = _MTX_INITIALIZER_NP;
58
struct set *opendir_set;
59
bool drm_shim_debug;
60
61
/* If /dev/dri doesn't exist, we'll need an arbitrary pointer that wouldn't be
62
* returned by any other opendir() call so we can return just our fake node.
63
*/
64
DIR *fake_dev_dri = (void *)&opendir_set;
65
66
/* XXX: implement REAL_FUNCTION_POINTER(close); */
67
REAL_FUNCTION_POINTER(closedir);
68
REAL_FUNCTION_POINTER(dup);
69
REAL_FUNCTION_POINTER(fcntl);
70
REAL_FUNCTION_POINTER(fopen);
71
REAL_FUNCTION_POINTER(ioctl);
72
REAL_FUNCTION_POINTER(mmap);
73
REAL_FUNCTION_POINTER(open);
74
REAL_FUNCTION_POINTER(opendir);
75
REAL_FUNCTION_POINTER(readdir);
76
REAL_FUNCTION_POINTER(readdir64);
77
REAL_FUNCTION_POINTER(readlink);
78
REAL_FUNCTION_POINTER(realpath);
79
80
#define HAS_XSTAT __GLIBC__ == 2 && __GLIBC_MINOR__ < 33
81
82
#if HAS_XSTAT
83
REAL_FUNCTION_POINTER(__xstat);
84
REAL_FUNCTION_POINTER(__xstat64);
85
REAL_FUNCTION_POINTER(__fxstat);
86
REAL_FUNCTION_POINTER(__fxstat64);
87
#else
88
REAL_FUNCTION_POINTER(stat);
89
REAL_FUNCTION_POINTER(stat64);
90
REAL_FUNCTION_POINTER(fstat);
91
REAL_FUNCTION_POINTER(fstat64);
92
#endif
93
94
/* Full path of /dev/dri/renderD* */
95
static char *render_node_path;
96
/* renderD* */
97
static char *render_node_dirent_name;
98
/* /sys/dev/char/major:minor/device */
99
static char *device_path;
100
/* /sys/dev/char/major:minor/device/subsystem */
101
static char *subsystem_path;
102
int render_node_minor = -1;
103
104
struct file_override {
105
const char *path;
106
char *contents;
107
};
108
static struct file_override file_overrides[10];
109
static int file_overrides_count;
110
extern bool drm_shim_driver_prefers_first_render_node;
111
112
#define nfasprintf(...) \
113
{ \
114
UNUSED int __ret = asprintf(__VA_ARGS__); \
115
assert(__ret >= 0); \
116
}
117
#define nfvasprintf(...) \
118
{ \
119
UNUSED int __ret = vasprintf(__VA_ARGS__); \
120
assert(__ret >= 0); \
121
}
122
123
/* Pick the minor and filename for our shimmed render node. This can be
124
* either a new one that didn't exist on the system, or if the driver wants,
125
* it can replace the first render node.
126
*/
127
static void
128
get_dri_render_node_minor(void)
129
{
130
for (int i = 0; i < 10; i++) {
131
UNUSED int minor = 128 + i;
132
nfasprintf(&render_node_dirent_name, "renderD%d", minor);
133
nfasprintf(&render_node_path, "/dev/dri/%s",
134
render_node_dirent_name);
135
struct stat st;
136
if (drm_shim_driver_prefers_first_render_node ||
137
stat(render_node_path, &st) == -1) {
138
139
render_node_minor = minor;
140
return;
141
}
142
}
143
144
fprintf(stderr, "Couldn't find a spare render node slot\n");
145
}
146
147
static void *get_function_pointer(const char *name)
148
{
149
void *func = dlsym(RTLD_NEXT, name);
150
if (!func) {
151
fprintf(stderr, "Failed to resolve %s\n", name);
152
abort();
153
}
154
return func;
155
}
156
157
#define GET_FUNCTION_POINTER(x) real_##x = get_function_pointer(#x)
158
159
void
160
drm_shim_override_file(const char *contents, const char *path_format, ...)
161
{
162
assert(file_overrides_count < ARRAY_SIZE(file_overrides));
163
164
char *path;
165
va_list ap;
166
va_start(ap, path_format);
167
nfvasprintf(&path, path_format, ap);
168
va_end(ap);
169
170
struct file_override *override = &file_overrides[file_overrides_count++];
171
override->path = path;
172
override->contents = strdup(contents);
173
}
174
175
static void
176
destroy_shim(void)
177
{
178
_mesa_set_destroy(opendir_set, NULL);
179
free(render_node_path);
180
free(render_node_dirent_name);
181
free(subsystem_path);
182
}
183
184
/* Initialization, which will be called from the first general library call
185
* that might need to be wrapped with the shim.
186
*/
187
static void
188
init_shim(void)
189
{
190
static bool inited = false;
191
drm_shim_debug = debug_get_bool_option("DRM_SHIM_DEBUG", false);
192
193
/* We can't lock this, because we recurse during initialization. */
194
if (inited)
195
return;
196
197
/* This comes first (and we're locked), to make sure we don't recurse
198
* during initialization.
199
*/
200
inited = true;
201
202
opendir_set = _mesa_set_create(NULL,
203
_mesa_hash_string,
204
_mesa_key_string_equal);
205
206
GET_FUNCTION_POINTER(closedir);
207
GET_FUNCTION_POINTER(dup);
208
GET_FUNCTION_POINTER(fcntl);
209
GET_FUNCTION_POINTER(fopen);
210
GET_FUNCTION_POINTER(ioctl);
211
GET_FUNCTION_POINTER(mmap);
212
GET_FUNCTION_POINTER(open);
213
GET_FUNCTION_POINTER(opendir);
214
GET_FUNCTION_POINTER(readdir);
215
GET_FUNCTION_POINTER(readdir64);
216
GET_FUNCTION_POINTER(readlink);
217
GET_FUNCTION_POINTER(realpath);
218
219
#if HAS_XSTAT
220
GET_FUNCTION_POINTER(__xstat);
221
GET_FUNCTION_POINTER(__xstat64);
222
GET_FUNCTION_POINTER(__fxstat);
223
GET_FUNCTION_POINTER(__fxstat64);
224
#else
225
GET_FUNCTION_POINTER(stat);
226
GET_FUNCTION_POINTER(stat64);
227
GET_FUNCTION_POINTER(fstat);
228
GET_FUNCTION_POINTER(fstat64);
229
#endif
230
231
get_dri_render_node_minor();
232
233
if (drm_shim_debug) {
234
fprintf(stderr, "Initializing DRM shim on %s\n",
235
render_node_path);
236
}
237
238
nfasprintf(&device_path,
239
"/sys/dev/char/%d:%d/device",
240
DRM_MAJOR, render_node_minor);
241
242
nfasprintf(&subsystem_path,
243
"/sys/dev/char/%d:%d/device/subsystem",
244
DRM_MAJOR, render_node_minor);
245
246
drm_shim_device_init();
247
248
atexit(destroy_shim);
249
}
250
251
/* Override libdrm's reading of various sysfs files for device enumeration. */
252
PUBLIC FILE *fopen(const char *path, const char *mode)
253
{
254
init_shim();
255
256
for (int i = 0; i < file_overrides_count; i++) {
257
if (strcmp(file_overrides[i].path, path) == 0) {
258
int fds[2];
259
pipe(fds);
260
write(fds[1], file_overrides[i].contents,
261
strlen(file_overrides[i].contents));
262
close(fds[1]);
263
return fdopen(fds[0], "r");
264
}
265
}
266
267
return real_fopen(path, mode);
268
}
269
PUBLIC FILE *fopen64(const char *path, const char *mode)
270
__attribute__((alias("fopen")));
271
272
/* Intercepts open(render_node_path) to redirect it to the simulator. */
273
PUBLIC int open(const char *path, int flags, ...)
274
{
275
init_shim();
276
277
va_list ap;
278
va_start(ap, flags);
279
mode_t mode = va_arg(ap, mode_t);
280
va_end(ap);
281
282
if (strcmp(path, render_node_path) != 0)
283
return real_open(path, flags, mode);
284
285
int fd = real_open("/dev/null", O_RDWR, 0);
286
287
drm_shim_fd_register(fd, NULL);
288
289
return fd;
290
}
291
PUBLIC int open64(const char*, int, ...) __attribute__((alias("open")));
292
293
#if HAS_XSTAT
294
/* Fakes stat to return character device stuff for our fake render node. */
295
PUBLIC int __xstat(int ver, const char *path, struct stat *st)
296
{
297
init_shim();
298
299
/* Note: call real stat if we're in the process of probing for a free
300
* render node!
301
*/
302
if (render_node_minor == -1)
303
return real___xstat(ver, path, st);
304
305
/* Fool libdrm's probe of whether the /sys dir for this char dev is
306
* there.
307
*/
308
char *sys_dev_drm_dir;
309
nfasprintf(&sys_dev_drm_dir,
310
"/sys/dev/char/%d:%d/device/drm",
311
DRM_MAJOR, render_node_minor);
312
if (strcmp(path, sys_dev_drm_dir) == 0) {
313
free(sys_dev_drm_dir);
314
return 0;
315
}
316
free(sys_dev_drm_dir);
317
318
if (strcmp(path, render_node_path) != 0)
319
return real___xstat(ver, path, st);
320
321
memset(st, 0, sizeof(*st));
322
st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
323
st->st_mode = S_IFCHR;
324
325
return 0;
326
}
327
328
/* Fakes stat to return character device stuff for our fake render node. */
329
PUBLIC int __xstat64(int ver, const char *path, struct stat64 *st)
330
{
331
init_shim();
332
333
/* Note: call real stat if we're in the process of probing for a free
334
* render node!
335
*/
336
if (render_node_minor == -1)
337
return real___xstat64(ver, path, st);
338
339
/* Fool libdrm's probe of whether the /sys dir for this char dev is
340
* there.
341
*/
342
char *sys_dev_drm_dir;
343
nfasprintf(&sys_dev_drm_dir,
344
"/sys/dev/char/%d:%d/device/drm",
345
DRM_MAJOR, render_node_minor);
346
if (strcmp(path, sys_dev_drm_dir) == 0) {
347
free(sys_dev_drm_dir);
348
return 0;
349
}
350
free(sys_dev_drm_dir);
351
352
if (strcmp(path, render_node_path) != 0)
353
return real___xstat64(ver, path, st);
354
355
memset(st, 0, sizeof(*st));
356
st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
357
st->st_mode = S_IFCHR;
358
359
return 0;
360
}
361
362
/* Fakes fstat to return character device stuff for our fake render node. */
363
PUBLIC int __fxstat(int ver, int fd, struct stat *st)
364
{
365
init_shim();
366
367
struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
368
369
if (!shim_fd)
370
return real___fxstat(ver, fd, st);
371
372
memset(st, 0, sizeof(*st));
373
st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
374
st->st_mode = S_IFCHR;
375
376
return 0;
377
}
378
379
PUBLIC int __fxstat64(int ver, int fd, struct stat64 *st)
380
{
381
init_shim();
382
383
struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
384
385
if (!shim_fd)
386
return real___fxstat64(ver, fd, st);
387
388
memset(st, 0, sizeof(*st));
389
st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
390
st->st_mode = S_IFCHR;
391
392
return 0;
393
}
394
395
#else
396
397
PUBLIC int stat(const char* path, struct stat* stat_buf)
398
{
399
init_shim();
400
401
/* Note: call real stat if we're in the process of probing for a free
402
* render node!
403
*/
404
if (render_node_minor == -1)
405
return real_stat(path, stat_buf);
406
407
/* Fool libdrm's probe of whether the /sys dir for this char dev is
408
* there.
409
*/
410
char *sys_dev_drm_dir;
411
nfasprintf(&sys_dev_drm_dir,
412
"/sys/dev/char/%d:%d/device/drm",
413
DRM_MAJOR, render_node_minor);
414
if (strcmp(path, sys_dev_drm_dir) == 0) {
415
free(sys_dev_drm_dir);
416
return 0;
417
}
418
free(sys_dev_drm_dir);
419
420
if (strcmp(path, render_node_path) != 0)
421
return real_stat(path, stat_buf);
422
423
memset(stat_buf, 0, sizeof(*stat_buf));
424
stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
425
stat_buf->st_mode = S_IFCHR;
426
427
return 0;
428
}
429
430
PUBLIC int stat64(const char* path, struct stat64* stat_buf)
431
{
432
init_shim();
433
434
/* Note: call real stat if we're in the process of probing for a free
435
* render node!
436
*/
437
if (render_node_minor == -1)
438
return real_stat64(path, stat_buf);
439
440
/* Fool libdrm's probe of whether the /sys dir for this char dev is
441
* there.
442
*/
443
char *sys_dev_drm_dir;
444
nfasprintf(&sys_dev_drm_dir,
445
"/sys/dev/char/%d:%d/device/drm",
446
DRM_MAJOR, render_node_minor);
447
if (strcmp(path, sys_dev_drm_dir) == 0) {
448
free(sys_dev_drm_dir);
449
return 0;
450
}
451
free(sys_dev_drm_dir);
452
453
if (strcmp(path, render_node_path) != 0)
454
return real_stat64(path, stat_buf);
455
456
memset(stat_buf, 0, sizeof(*stat_buf));
457
stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
458
stat_buf->st_mode = S_IFCHR;
459
460
return 0;
461
}
462
463
PUBLIC int fstat(int fd, struct stat* stat_buf)
464
{
465
init_shim();
466
467
struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
468
469
if (!shim_fd)
470
return real_fstat(fd, stat_buf);
471
472
memset(stat_buf, 0, sizeof(*stat_buf));
473
stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
474
stat_buf->st_mode = S_IFCHR;
475
476
return 0;
477
}
478
479
PUBLIC int fstat64(int fd, struct stat64* stat_buf)
480
{
481
init_shim();
482
483
struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
484
485
if (!shim_fd)
486
return real_fstat64(fd, stat_buf);
487
488
memset(stat_buf, 0, sizeof(*stat_buf));
489
stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
490
stat_buf->st_mode = S_IFCHR;
491
492
return 0;
493
}
494
#endif
495
496
/* Tracks if the opendir was on /dev/dri. */
497
PUBLIC DIR *
498
opendir(const char *name)
499
{
500
init_shim();
501
502
DIR *dir = real_opendir(name);
503
if (strcmp(name, "/dev/dri") == 0) {
504
if (!dir) {
505
/* If /dev/dri didn't exist, we still want to be able to return our
506
* fake /dev/dri/render* even though we probably can't
507
* mkdir("/dev/dri"). Return a fake DIR pointer for that.
508
*/
509
dir = fake_dev_dri;
510
}
511
512
mtx_lock(&shim_lock);
513
_mesa_set_add(opendir_set, dir);
514
mtx_unlock(&shim_lock);
515
}
516
517
return dir;
518
}
519
520
/* If we've reached the end of the real directory list and we're
521
* looking at /dev/dri, add our render node to the list.
522
*/
523
PUBLIC struct dirent *
524
readdir(DIR *dir)
525
{
526
init_shim();
527
528
struct dirent *ent = NULL;
529
530
if (dir != fake_dev_dri)
531
ent = real_readdir(dir);
532
static struct dirent render_node_dirent = { 0 };
533
534
if (!ent) {
535
mtx_lock(&shim_lock);
536
if (_mesa_set_search(opendir_set, dir)) {
537
strcpy(render_node_dirent.d_name,
538
render_node_dirent_name);
539
ent = &render_node_dirent;
540
_mesa_set_remove_key(opendir_set, dir);
541
}
542
mtx_unlock(&shim_lock);
543
}
544
545
return ent;
546
}
547
548
/* If we've reached the end of the real directory list and we're
549
* looking at /dev/dri, add our render node to the list.
550
*/
551
PUBLIC struct dirent64 *
552
readdir64(DIR *dir)
553
{
554
init_shim();
555
556
struct dirent64 *ent = NULL;
557
if (dir != fake_dev_dri)
558
ent = real_readdir64(dir);
559
static struct dirent64 render_node_dirent = { 0 };
560
561
if (!ent) {
562
mtx_lock(&shim_lock);
563
if (_mesa_set_search(opendir_set, dir)) {
564
strcpy(render_node_dirent.d_name,
565
render_node_dirent_name);
566
ent = &render_node_dirent;
567
_mesa_set_remove_key(opendir_set, dir);
568
}
569
mtx_unlock(&shim_lock);
570
}
571
572
return ent;
573
}
574
575
/* Cleans up tracking of opendir("/dev/dri") */
576
PUBLIC int
577
closedir(DIR *dir)
578
{
579
init_shim();
580
581
mtx_lock(&shim_lock);
582
_mesa_set_remove_key(opendir_set, dir);
583
mtx_unlock(&shim_lock);
584
585
if (dir != fake_dev_dri)
586
return real_closedir(dir);
587
else
588
return 0;
589
}
590
591
/* Handles libdrm's readlink to figure out what kind of device we have. */
592
PUBLIC ssize_t
593
readlink(const char *path, char *buf, size_t size)
594
{
595
init_shim();
596
597
if (strcmp(path, subsystem_path) != 0)
598
return real_readlink(path, buf, size);
599
600
static const struct {
601
const char *name;
602
int bus_type;
603
} bus_types[] = {
604
{ "/pci", DRM_BUS_PCI },
605
{ "/usb", DRM_BUS_USB },
606
{ "/platform", DRM_BUS_PLATFORM },
607
{ "/spi", DRM_BUS_PLATFORM },
608
{ "/host1x", DRM_BUS_HOST1X },
609
};
610
611
for (uint32_t i = 0; i < ARRAY_SIZE(bus_types); i++) {
612
if (bus_types[i].bus_type != shim_device.bus_type)
613
continue;
614
615
strncpy(buf, bus_types[i].name, size);
616
buf[size - 1] = 0;
617
break;
618
}
619
620
return strlen(buf) + 1;
621
}
622
623
/* Handles libdrm's realpath to figure out what kind of device we have. */
624
PUBLIC char *
625
realpath(const char *path, char *resolved_path)
626
{
627
init_shim();
628
629
if (strcmp(path, device_path) != 0)
630
return real_realpath(path, resolved_path);
631
632
strcpy(resolved_path, path);
633
634
return resolved_path;
635
}
636
637
/* Main entrypoint to DRM drivers: the ioctl syscall. We send all ioctls on
638
* our DRM fd to drm_shim_ioctl().
639
*/
640
PUBLIC int
641
ioctl(int fd, unsigned long request, ...)
642
{
643
init_shim();
644
645
va_list ap;
646
va_start(ap, request);
647
void *arg = va_arg(ap, void *);
648
va_end(ap);
649
650
struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
651
if (!shim_fd)
652
return real_ioctl(fd, request, arg);
653
654
return drm_shim_ioctl(fd, request, arg);
655
}
656
657
/* Gallium uses this to dup the incoming fd on gbm screen creation */
658
PUBLIC int
659
fcntl(int fd, int cmd, ...)
660
{
661
init_shim();
662
663
struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
664
665
va_list ap;
666
va_start(ap, cmd);
667
void *arg = va_arg(ap, void *);
668
va_end(ap);
669
670
int ret = real_fcntl(fd, cmd, arg);
671
672
if (shim_fd && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC))
673
drm_shim_fd_register(ret, shim_fd);
674
675
return ret;
676
}
677
PUBLIC int fcntl64(int, int, ...)
678
__attribute__((alias("fcntl")));
679
680
/* I wrote this when trying to fix gallium screen creation, leaving it around
681
* since it's probably good to have.
682
*/
683
PUBLIC int
684
dup(int fd)
685
{
686
init_shim();
687
688
int ret = real_dup(fd);
689
690
struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
691
if (shim_fd && ret >= 0)
692
drm_shim_fd_register(ret, shim_fd);
693
694
return ret;
695
}
696
697
PUBLIC void *
698
mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
699
{
700
init_shim();
701
702
struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
703
if (shim_fd)
704
return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset);
705
706
return real_mmap(addr, length, prot, flags, fd, offset);
707
}
708
PUBLIC void *mmap64(void*, size_t, int, int, int, off_t)
709
__attribute__((alias("mmap")));
710
711