Home | History | Annotate | Download | only in main
      1 /**************************************************************************
      2  *
      3  * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
      4  * Copyright 2009-2010 Chia-I Wu <olvaffe (at) 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 
     56 typedef struct _egl_module {
     57    char *Path;
     58    _EGLMain_t BuiltIn;
     59    void *Handle;
     60    _EGLDriver *Driver;
     61 } _EGLModule;
     62 
     63 static _EGL_DECLARE_MUTEX(_eglModuleMutex);
     64 static _EGLArray *_eglModules;
     65 
     66 const 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 
     88 typedef HMODULE lib_handle;
     89 
     90 static HMODULE
     91 open_library(const char *filename)
     92 {
     93    return LoadLibrary(filename);
     94 }
     95 
     96 static void
     97 close_library(HMODULE lib)
     98 {
     99    FreeLibrary(lib);
    100 }
    101 
    102 
    103 static const char *
    104 library_suffix(void)
    105 {
    106    return ".dll";
    107 }
    108 
    109 
    110 #elif defined(_EGL_OS_UNIX)
    111 
    112 
    113 typedef void * lib_handle;
    114 
    115 static void *
    116 open_library(const char *filename)
    117 {
    118    return dlopen(filename, RTLD_LAZY);
    119 }
    120 
    121 static void
    122 close_library(void *lib)
    123 {
    124    dlclose(lib);
    125 }
    126 
    127 
    128 static const char *
    129 library_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  */
    141 static _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  */
    196 static 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  */
    238 static 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  */
    266 static _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  */
    306 static 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  */
    321 static 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  */
    372 static 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  */
    396 static 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  */
    465 static 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  */
    526 static 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  */
    539 static 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  */
    557 static 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  */
    580 static _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  */
    691 void
    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  */
    708 void
    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