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 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(mmap64);
74 REAL_FUNCTION_POINTER(open);
75 REAL_FUNCTION_POINTER(opendir);
76 REAL_FUNCTION_POINTER(readdir);
77 REAL_FUNCTION_POINTER(readdir64);
78 REAL_FUNCTION_POINTER(readlink);
79 REAL_FUNCTION_POINTER(realpath);
80
81 #define HAS_XSTAT __GLIBC__ == 2 && __GLIBC_MINOR__ < 33
82
83 #if HAS_XSTAT
84 REAL_FUNCTION_POINTER(__xstat);
85 REAL_FUNCTION_POINTER(__xstat64);
86 REAL_FUNCTION_POINTER(__fxstat);
87 REAL_FUNCTION_POINTER(__fxstat64);
88 #else
89 REAL_FUNCTION_POINTER(stat);
90 REAL_FUNCTION_POINTER(stat64);
91 REAL_FUNCTION_POINTER(fstat);
92 REAL_FUNCTION_POINTER(fstat64);
93 #endif
94
95 /* Full path of /dev/dri/renderD* */
96 static char *render_node_path;
97 /* renderD* */
98 static char *render_node_dirent_name;
99 /* /sys/dev/char/major:minor/device */
100 static char *device_path;
101 /* /sys/dev/char/major:minor/device/subsystem */
102 static char *subsystem_path;
103 int render_node_minor = -1;
104
105 struct file_override {
106 const char *path;
107 char *contents;
108 };
109 static struct file_override file_overrides[10];
110 static int file_overrides_count;
111 extern bool drm_shim_driver_prefers_first_render_node;
112
113 #define nfasprintf(...) \
114 { \
115 UNUSED int __ret = asprintf(__VA_ARGS__); \
116 assert(__ret >= 0); \
117 }
118 #define nfvasprintf(...) \
119 { \
120 UNUSED int __ret = vasprintf(__VA_ARGS__); \
121 assert(__ret >= 0); \
122 }
123
124 /* Pick the minor and filename for our shimmed render node. This can be
125 * either a new one that didn't exist on the system, or if the driver wants,
126 * it can replace the first render node.
127 */
128 static void
get_dri_render_node_minor(void)129 get_dri_render_node_minor(void)
130 {
131 for (int i = 0; i < 10; i++) {
132 UNUSED int minor = 128 + i;
133 nfasprintf(&render_node_dirent_name, "renderD%d", minor);
134 nfasprintf(&render_node_path, "/dev/dri/%s",
135 render_node_dirent_name);
136 struct stat st;
137 if (drm_shim_driver_prefers_first_render_node ||
138 stat(render_node_path, &st) == -1) {
139
140 render_node_minor = minor;
141 return;
142 }
143 }
144
145 fprintf(stderr, "Couldn't find a spare render node slot\n");
146 }
147
get_function_pointer(const char * name)148 static void *get_function_pointer(const char *name)
149 {
150 void *func = dlsym(RTLD_NEXT, name);
151 if (!func) {
152 fprintf(stderr, "Failed to resolve %s\n", name);
153 abort();
154 }
155 return func;
156 }
157
158 #define GET_FUNCTION_POINTER(x) real_##x = get_function_pointer(#x)
159
160 void
drm_shim_override_file(const char * contents,const char * path_format,...)161 drm_shim_override_file(const char *contents, const char *path_format, ...)
162 {
163 assert(file_overrides_count < ARRAY_SIZE(file_overrides));
164
165 char *path;
166 va_list ap;
167 va_start(ap, path_format);
168 nfvasprintf(&path, path_format, ap);
169 va_end(ap);
170
171 struct file_override *override = &file_overrides[file_overrides_count++];
172 override->path = path;
173 override->contents = strdup(contents);
174 }
175
176 static void
destroy_shim(void)177 destroy_shim(void)
178 {
179 _mesa_set_destroy(opendir_set, NULL);
180 free(render_node_path);
181 free(render_node_dirent_name);
182 free(subsystem_path);
183 }
184
185 /* Initialization, which will be called from the first general library call
186 * that might need to be wrapped with the shim.
187 */
188 static void
init_shim(void)189 init_shim(void)
190 {
191 static bool inited = false;
192 drm_shim_debug = debug_get_bool_option("DRM_SHIM_DEBUG", false);
193
194 /* We can't lock this, because we recurse during initialization. */
195 if (inited)
196 return;
197
198 /* This comes first (and we're locked), to make sure we don't recurse
199 * during initialization.
200 */
201 inited = true;
202
203 opendir_set = _mesa_set_create(NULL,
204 _mesa_hash_string,
205 _mesa_key_string_equal);
206
207 GET_FUNCTION_POINTER(close);
208 GET_FUNCTION_POINTER(closedir);
209 GET_FUNCTION_POINTER(dup);
210 GET_FUNCTION_POINTER(fcntl);
211 GET_FUNCTION_POINTER(fopen);
212 GET_FUNCTION_POINTER(ioctl);
213 GET_FUNCTION_POINTER(mmap);
214 GET_FUNCTION_POINTER(mmap64);
215 GET_FUNCTION_POINTER(open);
216 GET_FUNCTION_POINTER(opendir);
217 GET_FUNCTION_POINTER(readdir);
218 GET_FUNCTION_POINTER(readdir64);
219 GET_FUNCTION_POINTER(readlink);
220 GET_FUNCTION_POINTER(realpath);
221
222 #if HAS_XSTAT
223 GET_FUNCTION_POINTER(__xstat);
224 GET_FUNCTION_POINTER(__xstat64);
225 GET_FUNCTION_POINTER(__fxstat);
226 GET_FUNCTION_POINTER(__fxstat64);
227 #else
228 GET_FUNCTION_POINTER(stat);
229 GET_FUNCTION_POINTER(stat64);
230 GET_FUNCTION_POINTER(fstat);
231 GET_FUNCTION_POINTER(fstat64);
232 #endif
233
234 get_dri_render_node_minor();
235
236 if (drm_shim_debug) {
237 fprintf(stderr, "Initializing DRM shim on %s\n",
238 render_node_path);
239 }
240
241 nfasprintf(&device_path,
242 "/sys/dev/char/%d:%d/device",
243 DRM_MAJOR, render_node_minor);
244
245 nfasprintf(&subsystem_path,
246 "/sys/dev/char/%d:%d/device/subsystem",
247 DRM_MAJOR, render_node_minor);
248
249 drm_shim_device_init();
250
251 atexit(destroy_shim);
252 }
253
254 /* Override libdrm's reading of various sysfs files for device enumeration. */
fopen(const char * path,const char * mode)255 PUBLIC FILE *fopen(const char *path, const char *mode)
256 {
257 init_shim();
258
259 for (int i = 0; i < file_overrides_count; i++) {
260 if (strcmp(file_overrides[i].path, path) == 0) {
261 int fds[2];
262 pipe(fds);
263 write(fds[1], file_overrides[i].contents,
264 strlen(file_overrides[i].contents));
265 close(fds[1]);
266 return fdopen(fds[0], "r");
267 }
268 }
269
270 return real_fopen(path, mode);
271 }
272 PUBLIC FILE *fopen64(const char *path, const char *mode)
273 __attribute__((alias("fopen")));
274
275 /* Intercepts open(render_node_path) to redirect it to the simulator. */
open(const char * path,int flags,...)276 PUBLIC int open(const char *path, int flags, ...)
277 {
278 init_shim();
279
280 va_list ap;
281 va_start(ap, flags);
282 mode_t mode = va_arg(ap, mode_t);
283 va_end(ap);
284
285 if (strcmp(path, render_node_path) != 0)
286 return real_open(path, flags, mode);
287
288 int fd = real_open("/dev/null", O_RDWR, 0);
289
290 drm_shim_fd_register(fd, NULL);
291
292 return fd;
293 }
294 PUBLIC int open64(const char*, int, ...) __attribute__((alias("open")));
295
close(int fd)296 PUBLIC int close(int fd)
297 {
298 init_shim();
299
300 drm_shim_fd_unregister(fd);
301
302 return real_close(fd);
303 }
304
305 #if HAS_XSTAT
306 /* Fakes stat to return character device stuff for our fake render node. */
__xstat(int ver,const char * path,struct stat * st)307 PUBLIC int __xstat(int ver, const char *path, struct stat *st)
308 {
309 init_shim();
310
311 /* Note: call real stat if we're in the process of probing for a free
312 * render node!
313 */
314 if (render_node_minor == -1)
315 return real___xstat(ver, path, st);
316
317 /* Fool libdrm's probe of whether the /sys dir for this char dev is
318 * there.
319 */
320 char *sys_dev_drm_dir;
321 nfasprintf(&sys_dev_drm_dir,
322 "/sys/dev/char/%d:%d/device/drm",
323 DRM_MAJOR, render_node_minor);
324 if (strcmp(path, sys_dev_drm_dir) == 0) {
325 free(sys_dev_drm_dir);
326 return 0;
327 }
328 free(sys_dev_drm_dir);
329
330 if (strcmp(path, render_node_path) != 0)
331 return real___xstat(ver, path, st);
332
333 memset(st, 0, sizeof(*st));
334 st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
335 st->st_mode = S_IFCHR;
336
337 return 0;
338 }
339
340 /* Fakes stat to return character device stuff for our fake render node. */
__xstat64(int ver,const char * path,struct stat64 * st)341 PUBLIC int __xstat64(int ver, const char *path, struct stat64 *st)
342 {
343 init_shim();
344
345 /* Note: call real stat if we're in the process of probing for a free
346 * render node!
347 */
348 if (render_node_minor == -1)
349 return real___xstat64(ver, path, st);
350
351 /* Fool libdrm's probe of whether the /sys dir for this char dev is
352 * there.
353 */
354 char *sys_dev_drm_dir;
355 nfasprintf(&sys_dev_drm_dir,
356 "/sys/dev/char/%d:%d/device/drm",
357 DRM_MAJOR, render_node_minor);
358 if (strcmp(path, sys_dev_drm_dir) == 0) {
359 free(sys_dev_drm_dir);
360 return 0;
361 }
362 free(sys_dev_drm_dir);
363
364 if (strcmp(path, render_node_path) != 0)
365 return real___xstat64(ver, path, st);
366
367 memset(st, 0, sizeof(*st));
368 st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
369 st->st_mode = S_IFCHR;
370
371 return 0;
372 }
373
374 /* Fakes fstat to return character device stuff for our fake render node. */
__fxstat(int ver,int fd,struct stat * st)375 PUBLIC int __fxstat(int ver, int fd, struct stat *st)
376 {
377 init_shim();
378
379 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
380
381 if (!shim_fd)
382 return real___fxstat(ver, fd, st);
383
384 memset(st, 0, sizeof(*st));
385 st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
386 st->st_mode = S_IFCHR;
387
388 return 0;
389 }
390
__fxstat64(int ver,int fd,struct stat64 * st)391 PUBLIC int __fxstat64(int ver, int fd, struct stat64 *st)
392 {
393 init_shim();
394
395 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
396
397 if (!shim_fd)
398 return real___fxstat64(ver, fd, st);
399
400 memset(st, 0, sizeof(*st));
401 st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
402 st->st_mode = S_IFCHR;
403
404 return 0;
405 }
406
407 #else
408
stat(const char * path,struct stat * stat_buf)409 PUBLIC int stat(const char* path, struct stat* stat_buf)
410 {
411 init_shim();
412
413 /* Note: call real stat if we're in the process of probing for a free
414 * render node!
415 */
416 if (render_node_minor == -1)
417 return real_stat(path, stat_buf);
418
419 /* Fool libdrm's probe of whether the /sys dir for this char dev is
420 * there.
421 */
422 char *sys_dev_drm_dir;
423 nfasprintf(&sys_dev_drm_dir,
424 "/sys/dev/char/%d:%d/device/drm",
425 DRM_MAJOR, render_node_minor);
426 if (strcmp(path, sys_dev_drm_dir) == 0) {
427 free(sys_dev_drm_dir);
428 return 0;
429 }
430 free(sys_dev_drm_dir);
431
432 if (strcmp(path, render_node_path) != 0)
433 return real_stat(path, stat_buf);
434
435 memset(stat_buf, 0, sizeof(*stat_buf));
436 stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
437 stat_buf->st_mode = S_IFCHR;
438
439 return 0;
440 }
441
stat64(const char * path,struct stat64 * stat_buf)442 PUBLIC int stat64(const char* path, struct stat64* stat_buf)
443 {
444 init_shim();
445
446 /* Note: call real stat if we're in the process of probing for a free
447 * render node!
448 */
449 if (render_node_minor == -1)
450 return real_stat64(path, stat_buf);
451
452 /* Fool libdrm's probe of whether the /sys dir for this char dev is
453 * there.
454 */
455 char *sys_dev_drm_dir;
456 nfasprintf(&sys_dev_drm_dir,
457 "/sys/dev/char/%d:%d/device/drm",
458 DRM_MAJOR, render_node_minor);
459 if (strcmp(path, sys_dev_drm_dir) == 0) {
460 free(sys_dev_drm_dir);
461 return 0;
462 }
463 free(sys_dev_drm_dir);
464
465 if (strcmp(path, render_node_path) != 0)
466 return real_stat64(path, stat_buf);
467
468 memset(stat_buf, 0, sizeof(*stat_buf));
469 stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
470 stat_buf->st_mode = S_IFCHR;
471
472 return 0;
473 }
474
fstat(int fd,struct stat * stat_buf)475 PUBLIC int fstat(int fd, struct stat* stat_buf)
476 {
477 init_shim();
478
479 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
480
481 if (!shim_fd)
482 return real_fstat(fd, stat_buf);
483
484 memset(stat_buf, 0, sizeof(*stat_buf));
485 stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
486 stat_buf->st_mode = S_IFCHR;
487
488 return 0;
489 }
490
fstat64(int fd,struct stat64 * stat_buf)491 PUBLIC int fstat64(int fd, struct stat64* stat_buf)
492 {
493 init_shim();
494
495 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
496
497 if (!shim_fd)
498 return real_fstat64(fd, stat_buf);
499
500 memset(stat_buf, 0, sizeof(*stat_buf));
501 stat_buf->st_rdev = makedev(DRM_MAJOR, render_node_minor);
502 stat_buf->st_mode = S_IFCHR;
503
504 return 0;
505 }
506 #endif
507
508 /* Tracks if the opendir was on /dev/dri. */
509 PUBLIC DIR *
opendir(const char * name)510 opendir(const char *name)
511 {
512 init_shim();
513
514 DIR *dir = real_opendir(name);
515 if (strcmp(name, "/dev/dri") == 0) {
516 if (!dir) {
517 /* If /dev/dri didn't exist, we still want to be able to return our
518 * fake /dev/dri/render* even though we probably can't
519 * mkdir("/dev/dri"). Return a fake DIR pointer for that.
520 */
521 dir = fake_dev_dri;
522 }
523
524 mtx_lock(&shim_lock);
525 _mesa_set_add(opendir_set, dir);
526 mtx_unlock(&shim_lock);
527 }
528
529 return dir;
530 }
531
532 /* If we're looking at /dev/dri, add our render node to the list
533 * before the real entries in the directory.
534 */
535 PUBLIC struct dirent *
readdir(DIR * dir)536 readdir(DIR *dir)
537 {
538 init_shim();
539
540 struct dirent *ent = NULL;
541
542 static struct dirent render_node_dirent = { 0 };
543
544 mtx_lock(&shim_lock);
545 if (_mesa_set_search(opendir_set, dir)) {
546 strcpy(render_node_dirent.d_name,
547 render_node_dirent_name);
548 ent = &render_node_dirent;
549 _mesa_set_remove_key(opendir_set, dir);
550 }
551 mtx_unlock(&shim_lock);
552
553 if (!ent && dir != fake_dev_dri)
554 ent = real_readdir(dir);
555
556 return ent;
557 }
558
559 /* If we're looking at /dev/dri, add our render node to the list
560 * before the real entries in the directory.
561 */
562 PUBLIC struct dirent64 *
readdir64(DIR * dir)563 readdir64(DIR *dir)
564 {
565 init_shim();
566
567 struct dirent64 *ent = NULL;
568
569 static struct dirent64 render_node_dirent = { 0 };
570
571 mtx_lock(&shim_lock);
572 if (_mesa_set_search(opendir_set, dir)) {
573 strcpy(render_node_dirent.d_name,
574 render_node_dirent_name);
575 ent = &render_node_dirent;
576 _mesa_set_remove_key(opendir_set, dir);
577 }
578 mtx_unlock(&shim_lock);
579
580 if (!ent && dir != fake_dev_dri)
581 ent = real_readdir64(dir);
582
583 return ent;
584 }
585
586 /* Cleans up tracking of opendir("/dev/dri") */
587 PUBLIC int
closedir(DIR * dir)588 closedir(DIR *dir)
589 {
590 init_shim();
591
592 mtx_lock(&shim_lock);
593 _mesa_set_remove_key(opendir_set, dir);
594 mtx_unlock(&shim_lock);
595
596 if (dir != fake_dev_dri)
597 return real_closedir(dir);
598 else
599 return 0;
600 }
601
602 /* Handles libdrm's readlink to figure out what kind of device we have. */
603 PUBLIC ssize_t
readlink(const char * path,char * buf,size_t size)604 readlink(const char *path, char *buf, size_t size)
605 {
606 init_shim();
607
608 if (strcmp(path, subsystem_path) != 0)
609 return real_readlink(path, buf, size);
610
611 static const struct {
612 const char *name;
613 int bus_type;
614 } bus_types[] = {
615 { "/pci", DRM_BUS_PCI },
616 { "/usb", DRM_BUS_USB },
617 { "/platform", DRM_BUS_PLATFORM },
618 { "/spi", DRM_BUS_PLATFORM },
619 { "/host1x", DRM_BUS_HOST1X },
620 };
621
622 for (uint32_t i = 0; i < ARRAY_SIZE(bus_types); i++) {
623 if (bus_types[i].bus_type != shim_device.bus_type)
624 continue;
625
626 strncpy(buf, bus_types[i].name, size);
627 buf[size - 1] = 0;
628 break;
629 }
630
631 return strlen(buf) + 1;
632 }
633
634 /* Handles libdrm's realpath to figure out what kind of device we have. */
635 PUBLIC char *
realpath(const char * path,char * resolved_path)636 realpath(const char *path, char *resolved_path)
637 {
638 init_shim();
639
640 if (strcmp(path, device_path) != 0)
641 return real_realpath(path, resolved_path);
642
643 strcpy(resolved_path, path);
644
645 return resolved_path;
646 }
647
648 /* Main entrypoint to DRM drivers: the ioctl syscall. We send all ioctls on
649 * our DRM fd to drm_shim_ioctl().
650 */
651 PUBLIC int
ioctl(int fd,unsigned long request,...)652 ioctl(int fd, unsigned long request, ...)
653 {
654 init_shim();
655
656 va_list ap;
657 va_start(ap, request);
658 void *arg = va_arg(ap, void *);
659 va_end(ap);
660
661 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
662 if (!shim_fd)
663 return real_ioctl(fd, request, arg);
664
665 return drm_shim_ioctl(fd, request, arg);
666 }
667
668 /* Gallium uses this to dup the incoming fd on gbm screen creation */
669 PUBLIC int
fcntl(int fd,int cmd,...)670 fcntl(int fd, int cmd, ...)
671 {
672 init_shim();
673
674 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
675
676 va_list ap;
677 va_start(ap, cmd);
678 void *arg = va_arg(ap, void *);
679 va_end(ap);
680
681 int ret = real_fcntl(fd, cmd, arg);
682
683 if (shim_fd && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC))
684 drm_shim_fd_register(ret, shim_fd);
685
686 return ret;
687 }
688 PUBLIC int fcntl64(int, int, ...)
689 __attribute__((alias("fcntl")));
690
691 /* I wrote this when trying to fix gallium screen creation, leaving it around
692 * since it's probably good to have.
693 */
694 PUBLIC int
dup(int fd)695 dup(int fd)
696 {
697 init_shim();
698
699 int ret = real_dup(fd);
700
701 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
702 if (shim_fd && ret >= 0)
703 drm_shim_fd_register(ret, shim_fd);
704
705 return ret;
706 }
707
708 PUBLIC void *
mmap(void * addr,size_t length,int prot,int flags,int fd,off_t offset)709 mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
710 {
711 init_shim();
712
713 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
714 if (shim_fd)
715 return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset);
716
717 return real_mmap(addr, length, prot, flags, fd, offset);
718 }
719
720 PUBLIC void *
mmap64(void * addr,size_t length,int prot,int flags,int fd,off64_t offset)721 mmap64(void* addr, size_t length, int prot, int flags, int fd, off64_t offset)
722 {
723 init_shim();
724
725 struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
726 if (shim_fd)
727 return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset);
728
729 return real_mmap64(addr, length, prot, flags, fd, offset);
730 }
731