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