46876d00565e50beba74ab39bb2eb2aa7d5a306a
[mesa.git] / src / egl / main / egldriver.c
1 /**
2 * Functions for choosing and opening/loading device drivers.
3 */
4
5
6 #include <assert.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #include "eglstring.h"
12 #include "egldefines.h"
13 #include "egldisplay.h"
14 #include "egldriver.h"
15 #include "egllog.h"
16 #include "eglmutex.h"
17
18 #if defined(_EGL_OS_UNIX)
19 #include <dlfcn.h>
20 #include <sys/types.h>
21 #include <dirent.h>
22 #include <unistd.h>
23 #endif
24
25
26 typedef struct _egl_module {
27 char *Path;
28 _EGLMain_t BuiltIn;
29 void *Handle;
30 _EGLDriver *Driver;
31 } _EGLModule;
32
33 static _EGL_DECLARE_MUTEX(_eglModuleMutex);
34 static _EGLArray *_eglModules;
35
36 const struct {
37 const char *name;
38 _EGLMain_t main;
39 } _eglBuiltInDrivers[] = {
40 #ifdef _EGL_BUILT_IN_DRIVER_DRI2
41 { "egl_dri2", _eglBuiltInDriverDRI2 },
42 #endif
43 #ifdef _EGL_BUILT_IN_DRIVER_GLX
44 { "egl_glx", _eglBuiltInDriverGLX },
45 #endif
46 { NULL, NULL }
47 };
48
49 /**
50 * Wrappers for dlopen/dlclose()
51 */
52 #if defined(_EGL_OS_WINDOWS)
53
54
55 typedef HMODULE lib_handle;
56
57 static HMODULE
58 open_library(const char *filename)
59 {
60 return LoadLibrary(filename);
61 }
62
63 static void
64 close_library(HMODULE lib)
65 {
66 FreeLibrary(lib);
67 }
68
69
70 static const char *
71 library_suffix(void)
72 {
73 return ".dll";
74 }
75
76
77 #elif defined(_EGL_OS_UNIX)
78
79
80 typedef void * lib_handle;
81
82 static void *
83 open_library(const char *filename)
84 {
85 return dlopen(filename, RTLD_LAZY);
86 }
87
88 static void
89 close_library(void *lib)
90 {
91 dlclose(lib);
92 }
93
94
95 static const char *
96 library_suffix(void)
97 {
98 return ".so";
99 }
100
101
102 #endif
103
104
105 /**
106 * Open the named driver and find its bootstrap function: _eglMain().
107 */
108 static _EGLMain_t
109 _eglOpenLibrary(const char *driverPath, lib_handle *handle)
110 {
111 lib_handle lib;
112 _EGLMain_t mainFunc = NULL;
113 const char *error = "unknown error";
114
115 assert(driverPath);
116
117 _eglLog(_EGL_DEBUG, "dlopen(%s)", driverPath);
118 lib = open_library(driverPath);
119
120 #if defined(_EGL_OS_WINDOWS)
121 /* XXX untested */
122 if (lib)
123 mainFunc = (_EGLMain_t) GetProcAddress(lib, "_eglMain");
124 #elif defined(_EGL_OS_UNIX)
125 if (lib) {
126 union {
127 _EGLMain_t func;
128 void *ptr;
129 } tmp = { NULL };
130 /* direct cast gives a warning when compiled with -pedantic */
131 tmp.ptr = dlsym(lib, "_eglMain");
132 mainFunc = tmp.func;
133 if (!mainFunc)
134 error = dlerror();
135 }
136 else {
137 error = dlerror();
138 }
139 #endif
140
141 if (!lib) {
142 _eglLog(_EGL_WARNING, "Could not open driver %s (%s)",
143 driverPath, error);
144 return NULL;
145 }
146
147 if (!mainFunc) {
148 _eglLog(_EGL_WARNING, "_eglMain not found in %s (%s)",
149 driverPath, error);
150 if (lib)
151 close_library(lib);
152 return NULL;
153 }
154
155 *handle = lib;
156 return mainFunc;
157 }
158
159
160 /**
161 * Load a module and create the driver object.
162 */
163 static EGLBoolean
164 _eglLoadModule(_EGLModule *mod)
165 {
166 _EGLMain_t mainFunc;
167 lib_handle lib;
168 _EGLDriver *drv;
169
170 if (mod->Driver)
171 return EGL_TRUE;
172
173 if (mod->BuiltIn) {
174 lib = (lib_handle) NULL;
175 mainFunc = mod->BuiltIn;
176 }
177 else {
178 mainFunc = _eglOpenLibrary(mod->Path, &lib);
179 if (!mainFunc)
180 return EGL_FALSE;
181 }
182
183 drv = mainFunc(NULL);
184 if (!drv) {
185 if (lib)
186 close_library(lib);
187 return EGL_FALSE;
188 }
189
190 if (!drv->Name) {
191 _eglLog(_EGL_WARNING, "Driver loaded from %s has no name", mod->Path);
192 drv->Name = "UNNAMED";
193 }
194
195 mod->Handle = (void *) lib;
196 mod->Driver = drv;
197
198 return EGL_TRUE;
199 }
200
201
202 /**
203 * Unload a module.
204 */
205 static void
206 _eglUnloadModule(_EGLModule *mod)
207 {
208 #if defined(_EGL_OS_UNIX)
209 /* destroy the driver */
210 if (mod->Driver && mod->Driver->Unload)
211 mod->Driver->Unload(mod->Driver);
212
213 /*
214 * XXX At this point (atexit), the module might be the last reference to
215 * libEGL. Closing the module might unmap libEGL and give problems.
216 */
217 #if 0
218 if (mod->Handle)
219 close_library(mod->Handle);
220 #endif
221 #elif defined(_EGL_OS_WINDOWS)
222 /* XXX Windows unloads DLLs before atexit */
223 #endif
224
225 mod->Driver = NULL;
226 mod->Handle = NULL;
227 }
228
229
230 /**
231 * Add a module to the module array.
232 */
233 static _EGLModule *
234 _eglAddModule(const char *path)
235 {
236 _EGLModule *mod;
237 EGLint i;
238
239 if (!_eglModules) {
240 _eglModules = _eglCreateArray("Module", 8);
241 if (!_eglModules)
242 return NULL;
243 }
244
245 /* find duplicates */
246 for (i = 0; i < _eglModules->Size; i++) {
247 mod = _eglModules->Elements[i];
248 if (strcmp(mod->Path, path) == 0)
249 return mod;
250 }
251
252 /* allocate a new one */
253 mod = calloc(1, sizeof(*mod));
254 if (mod) {
255 mod->Path = _eglstrdup(path);
256 if (!mod->Path) {
257 free(mod);
258 mod = NULL;
259 }
260 }
261 if (mod) {
262 _eglAppendArray(_eglModules, (void *) mod);
263 _eglLog(_EGL_DEBUG, "added %s to module array", mod->Path);
264 }
265
266 return mod;
267 }
268
269
270 /**
271 * Free a module.
272 */
273 static void
274 _eglFreeModule(void *module)
275 {
276 _EGLModule *mod = (_EGLModule *) module;
277
278 _eglUnloadModule(mod);
279 free(mod->Path);
280 free(mod);
281 }
282
283
284 /**
285 * A loader function for use with _eglPreloadForEach. The loader data is the
286 * filename of the driver. This function stops on the first valid driver.
287 */
288 static EGLBoolean
289 _eglLoaderFile(const char *dir, size_t len, void *loader_data)
290 {
291 char path[1024];
292 const char *filename = (const char *) loader_data;
293 size_t flen = strlen(filename);
294
295 /* make a full path */
296 if (len + flen + 2 > sizeof(path))
297 return EGL_TRUE;
298 if (len) {
299 memcpy(path, dir, len);
300 path[len++] = '/';
301 }
302 memcpy(path + len, filename, flen);
303 len += flen;
304 path[len] = '\0';
305
306 if (library_suffix()) {
307 const char *suffix = library_suffix();
308 size_t slen = strlen(suffix);
309 const char *p;
310 EGLBoolean need_suffix;
311
312 p = filename + flen - slen;
313 need_suffix = (p < filename || strcmp(p, suffix) != 0);
314 if (need_suffix) {
315 /* overflow */
316 if (len + slen + 1 > sizeof(path))
317 return EGL_TRUE;
318 strcpy(path + len, suffix);
319 }
320 }
321
322 #if defined(_EGL_OS_UNIX)
323 /* check if the file exists */
324 if (access(path, F_OK))
325 return EGL_TRUE;
326 #endif
327
328 _eglAddModule(path);
329
330 return EGL_TRUE;
331 }
332
333
334 /**
335 * Run the callback function on each driver directory.
336 *
337 * The process may end prematurely if the callback function returns false.
338 */
339 static void
340 _eglPreloadForEach(const char *search_path,
341 EGLBoolean (*loader)(const char *, size_t, void *),
342 void *loader_data)
343 {
344 const char *cur, *next;
345 size_t len;
346
347 cur = search_path;
348 while (cur) {
349 next = strchr(cur, ':');
350 len = (next) ? next - cur : strlen(cur);
351
352 if (!loader(cur, len, loader_data))
353 break;
354
355 cur = (next) ? next + 1 : NULL;
356 }
357 }
358
359
360 /**
361 * Return a list of colon-separated driver directories.
362 */
363 static const char *
364 _eglGetSearchPath(void)
365 {
366 static char search_path[1024];
367
368 #if defined(_EGL_OS_UNIX) || defined(_EGL_OS_WINDOWS)
369 if (search_path[0] == '\0') {
370 char *buf = search_path;
371 size_t len = sizeof(search_path);
372 EGLBoolean use_env;
373 char dir_sep;
374 int ret;
375
376 #if defined(_EGL_OS_UNIX)
377 use_env = (geteuid() == getuid() && getegid() == getgid());
378 dir_sep = '/';
379 #else
380 use_env = EGL_TRUE;
381 dir_sep = '\\';
382 #endif
383
384 if (use_env) {
385 char *p;
386
387 /* extract the dirname from EGL_DRIVER */
388 p = getenv("EGL_DRIVER");
389 if (p && strchr(p, dir_sep)) {
390 ret = _eglsnprintf(buf, len, "%s", p);
391 if (ret > 0 && ret < len) {
392 p = strrchr(buf, dir_sep);
393 *p++ = ':';
394
395 len -= p - buf;
396 buf = p;
397 }
398 }
399
400 /* append EGL_DRIVERS_PATH */
401 p = getenv("EGL_DRIVERS_PATH");
402 if (p) {
403 ret = _eglsnprintf(buf, len, "%s:", p);
404 if (ret > 0 && ret < len) {
405 buf += ret;
406 len -= ret;
407 }
408 }
409 }
410 else {
411 _eglLog(_EGL_DEBUG,
412 "ignore EGL_DRIVERS_PATH for setuid/setgid binaries");
413 }
414
415 ret = _eglsnprintf(buf, len, "%s", _EGL_DRIVER_SEARCH_DIR);
416 if (ret < 0 || ret >= len)
417 search_path[0] = '\0';
418
419 _eglLog(_EGL_DEBUG, "EGL search path is %s", search_path);
420 }
421 #endif /* defined(_EGL_OS_UNIX) || defined(_EGL_OS_WINDOWS) */
422
423 return search_path;
424 }
425
426
427 /**
428 * Add the user driver to the module array.
429 *
430 * The user driver is specified by EGL_DRIVER.
431 */
432 static void
433 _eglAddUserDriver(void)
434 {
435 const char *search_path = _eglGetSearchPath();
436 char *env;
437
438 env = getenv("EGL_DRIVER");
439 #if defined(_EGL_OS_UNIX)
440 if (env && strchr(env, '/')) {
441 search_path = "";
442 if ((geteuid() != getuid() || getegid() != getgid())) {
443 _eglLog(_EGL_DEBUG,
444 "ignore EGL_DRIVER for setuid/setgid binaries");
445 env = NULL;
446 }
447 }
448 #endif /* _EGL_OS_UNIX */
449 if (env) {
450 _EGLModule *mod;
451 EGLint i;
452
453 /* env can be a path */
454 _eglPreloadForEach(search_path, _eglLoaderFile, (void *) env);
455 /* or the name of a built-in driver */
456 for (i = 0; _eglBuiltInDrivers[i].name; i++) {
457 if (!strcmp(_eglBuiltInDrivers[i].name, env)) {
458 mod = _eglAddModule(env);
459 if (mod)
460 mod->BuiltIn = _eglBuiltInDrivers[i].main;
461 }
462 }
463 }
464 }
465
466
467 /**
468 * Add egl_gallium to the module array.
469 */
470 static void
471 _eglAddGalliumDriver(void)
472 {
473 #ifndef _EGL_BUILT_IN_DRIVER_GALLIUM
474 void *external = (void *) "egl_gallium";
475 _eglPreloadForEach(_eglGetSearchPath(), _eglLoaderFile, external);
476 #endif
477 }
478
479
480 /**
481 * Add built-in drivers to the module array.
482 */
483 static void
484 _eglAddBuiltInDrivers(void)
485 {
486 _EGLModule *mod;
487 EGLint i;
488
489 for (i = 0; _eglBuiltInDrivers[i].name; i++) {
490 mod = _eglAddModule(_eglBuiltInDrivers[i].name);
491 if (mod)
492 mod->BuiltIn = _eglBuiltInDrivers[i].main;
493 }
494 }
495
496
497 /**
498 * Add drivers to the module array. Drivers will be loaded as they are matched
499 * to displays.
500 */
501 static EGLBoolean
502 _eglAddDrivers(void)
503 {
504 if (_eglModules)
505 return EGL_TRUE;
506
507 /* the order here decides the priorities of the drivers */
508 _eglAddUserDriver();
509 _eglAddGalliumDriver();
510 _eglAddBuiltInDrivers();
511
512 return (_eglModules != NULL);
513 }
514
515
516 /**
517 * Match a display to a driver. The display is initialized unless use_probe is
518 * true.
519 *
520 * The matching is done by finding the first driver that can initialize the
521 * display, or when use_probe is true, the driver with highest score.
522 */
523 _EGLDriver *
524 _eglMatchDriver(_EGLDisplay *dpy, EGLBoolean use_probe)
525 {
526 _EGLModule *mod;
527 _EGLDriver *best_drv = NULL;
528 EGLint best_score = 0;
529 EGLint major, minor, i;
530
531 _eglLockMutex(&_eglModuleMutex);
532
533 if (!_eglAddDrivers()) {
534 _eglUnlockMutex(&_eglModuleMutex);
535 return EGL_FALSE;
536 }
537
538 /* match the loaded modules */
539 for (i = 0; i < _eglModules->Size; i++) {
540 mod = (_EGLModule *) _eglModules->Elements[i];
541 if (!mod->Driver)
542 break;
543
544 if (use_probe) {
545 EGLint score = (mod->Driver->Probe) ?
546 mod->Driver->Probe(mod->Driver, dpy) : 1;
547 if (score > best_score) {
548 best_drv = mod->Driver;
549 best_score = score;
550 }
551 }
552 else {
553 if (mod->Driver->API.Initialize(mod->Driver, dpy, &major, &minor)) {
554 best_drv = mod->Driver;
555 best_score = 100;
556 }
557 }
558 /* perfect match */
559 if (best_score >= 100)
560 break;
561 }
562
563 /* load more modules */
564 if (!best_drv) {
565 EGLint first_unloaded = i;
566
567 while (i < _eglModules->Size) {
568 mod = (_EGLModule *) _eglModules->Elements[i];
569 assert(!mod->Driver);
570
571 if (!_eglLoadModule(mod)) {
572 /* remove invalid modules */
573 _eglEraseArray(_eglModules, i, _eglFreeModule);
574 continue;
575 }
576
577 if (use_probe) {
578 best_score = (mod->Driver->Probe) ?
579 mod->Driver->Probe(mod->Driver, dpy) : 1;
580 }
581 else {
582 if (mod->Driver->API.Initialize(mod->Driver, dpy, &major, &minor))
583 best_score = 100;
584 }
585
586 if (best_score > 0) {
587 best_drv = mod->Driver;
588 /* loaded modules come before unloaded ones */
589 if (first_unloaded != i) {
590 void *tmp = _eglModules->Elements[i];
591 _eglModules->Elements[i] =
592 _eglModules->Elements[first_unloaded];
593 _eglModules->Elements[first_unloaded] = tmp;
594 }
595 break;
596 }
597 else {
598 _eglUnloadModule(mod);
599 i++;
600 }
601 }
602 }
603
604 _eglUnlockMutex(&_eglModuleMutex);
605
606 if (best_drv) {
607 _eglLog(_EGL_DEBUG, "the best driver is %s (score %d)",
608 best_drv->Name, best_score);
609 if (!use_probe) {
610 dpy->Driver = best_drv;
611 dpy->Initialized = EGL_TRUE;
612 dpy->APImajor = major;
613 dpy->APIminor = minor;
614 }
615 }
616
617 return best_drv;
618 }
619
620
621 __eglMustCastToProperFunctionPointerType
622 _eglGetDriverProc(const char *procname)
623 {
624 EGLint i;
625 _EGLProc proc = NULL;
626
627 if (!_eglModules) {
628 /* load the driver for the default display */
629 EGLDisplay egldpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
630 _EGLDisplay *dpy = _eglLookupDisplay(egldpy);
631 if (!dpy || !_eglMatchDriver(dpy, EGL_TRUE))
632 return NULL;
633 }
634
635 for (i = 0; i < _eglModules->Size; i++) {
636 _EGLModule *mod = (_EGLModule *) _eglModules->Elements[i];
637
638 if (!mod->Driver)
639 break;
640 proc = mod->Driver->API.GetProcAddress(mod->Driver, procname);
641 if (proc)
642 break;
643 }
644
645 return proc;
646 }
647
648
649 /**
650 * Unload all drivers.
651 */
652 void
653 _eglUnloadDrivers(void)
654 {
655 /* this is called at atexit time */
656 if (_eglModules) {
657 _eglDestroyArray(_eglModules, _eglFreeModule);
658 _eglModules = NULL;
659 }
660 }
661
662
663 /**
664 * Invoke a callback function on each EGL search path.
665 *
666 * The first argument of the callback function is the name of the search path.
667 * The second argument is the length of the name.
668 */
669 void
670 _eglSearchPathForEach(EGLBoolean (*callback)(const char *, size_t, void *),
671 void *callback_data)
672 {
673 const char *search_path = _eglGetSearchPath();
674 _eglPreloadForEach(search_path, callback, callback_data);
675 }