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