loader.c revision 6f928160a3b46db44196485cf6acb8bc902f2d08
1/* 2 * XGL 3 * 4 * Copyright (C) 2014 LunarG, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included 14 * in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: 25 * Chia-I Wu <olv@lunarg.com> 26 * Courtney Goeltzenleuchter <courtney@lunarg.com> 27 */ 28 29#include <stdio.h> 30#include <stdlib.h> 31#include <stdarg.h> 32#include <stdbool.h> 33#include <string.h> 34 35#include <sys/types.h> 36#include <dirent.h> 37#include <unistd.h> 38#include <dlfcn.h> 39#include <pthread.h> 40 41#include "loader.h" 42 43typedef XGL_RESULT (XGLAPI *InitAndEnumerateGpusT)(const XGL_APPLICATION_INFO* pAppInfo, const XGL_ALLOC_CALLBACKS* pAllocCb, XGL_UINT maxGpus, XGL_UINT* pGpuCount, XGL_PHYSICAL_GPU* pGpus); 44typedef XGL_RESULT (XGLAPI *DbgRegisterMsgCallbackT)(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback, XGL_VOID* pUserData); 45typedef XGL_RESULT (XGLAPI *DbgUnregisterMsgCallbackT)(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback); 46typedef XGL_RESULT (XGLAPI *DbgSetGlobalOptionT)(XGL_INT dbgOption, XGL_SIZE dataSize, const XGL_VOID* pData); 47 48struct loader_icd { 49 void *handle; 50 51 InitAndEnumerateGpusT InitAndEnumerateGpus; 52 DbgRegisterMsgCallbackT DbgRegisterMsgCallback; 53 DbgUnregisterMsgCallbackT DbgUnregisterMsgCallback; 54 DbgSetGlobalOptionT DbgSetGlobalOption; 55 56 struct loader_icd *next; 57}; 58 59struct loader_msg_callback { 60 XGL_DBG_MSG_CALLBACK_FUNCTION func; 61 XGL_VOID *data; 62 63 struct loader_msg_callback *next; 64}; 65 66static struct { 67 bool scanned; 68 struct loader_icd *icds; 69 70 struct loader_msg_callback *msg_callbacks; 71 72 bool debug_echo_enable; 73 bool break_on_error; 74 bool break_on_warning; 75} loader; 76 77static XGL_RESULT loader_msg_callback_add(XGL_DBG_MSG_CALLBACK_FUNCTION func, 78 XGL_VOID *data) 79{ 80 struct loader_msg_callback *cb; 81 82 cb = malloc(sizeof(*cb)); 83 if (!cb) 84 return XGL_ERROR_OUT_OF_MEMORY; 85 86 cb->func = func; 87 cb->data = data; 88 89 cb->next = loader.msg_callbacks; 90 loader.msg_callbacks = cb; 91 92 return XGL_SUCCESS; 93} 94 95static XGL_RESULT loader_msg_callback_remove(XGL_DBG_MSG_CALLBACK_FUNCTION func) 96{ 97 struct loader_msg_callback *cb = loader.msg_callbacks; 98 99 /* 100 * Find the first match (last registered). 101 * 102 * XXX What if the same callback function is registered more than once? 103 */ 104 while (cb) { 105 if (cb->func == func) { 106 break; 107 } 108 109 cb = cb->next; 110 } 111 112 if (!cb) 113 return XGL_ERROR_INVALID_POINTER; 114 115 free(cb); 116 117 return XGL_SUCCESS; 118} 119 120static void loader_msg_callback_clear(void) 121{ 122 struct loader_msg_callback *cb = loader.msg_callbacks; 123 124 while (cb) { 125 struct loader_msg_callback *next = cb->next; 126 free(cb); 127 cb = next; 128 } 129 130 loader.msg_callbacks = NULL; 131} 132 133static void loader_log(XGL_DBG_MSG_TYPE msg_type, XGL_INT msg_code, 134 const char *format, ...) 135{ 136 const struct loader_msg_callback *cb = loader.msg_callbacks; 137 char msg[256]; 138 va_list ap; 139 int ret; 140 141 va_start(ap, format); 142 ret = vsnprintf(msg, sizeof(msg), format, ap); 143 if (ret >= sizeof(msg) || ret < 0) { 144 msg[sizeof(msg) - 1] = '\0'; 145 } 146 va_end(ap); 147 148 if (loader.debug_echo_enable || !cb) { 149 fputs(msg, stderr); 150 fputc('\n', stderr); 151 } 152 153 while (cb) { 154 cb->func(msg_type, XGL_VALIDATION_LEVEL_0, XGL_NULL_HANDLE, 0, 155 msg_code, (const XGL_CHAR *) msg, cb->data); 156 cb = cb->next; 157 } 158 159 switch (msg_type) { 160 case XGL_DBG_MSG_ERROR: 161 if (loader.break_on_error) { 162 exit(1); 163 } 164 /* fall through */ 165 case XGL_DBG_MSG_WARNING: 166 if (loader.break_on_warning) { 167 exit(1); 168 } 169 break; 170 default: 171 break; 172 } 173} 174 175static void 176loader_icd_destroy(struct loader_icd *icd) 177{ 178 dlclose(icd->handle); 179 free(icd); 180} 181 182static struct loader_icd * 183loader_icd_create(const char *filename) 184{ 185 struct loader_icd *icd; 186 187 icd = malloc(sizeof(*icd)); 188 if (!icd) 189 return NULL; 190 191 memset(icd, 0, sizeof(*icd)); 192 193 icd->handle = dlopen(filename, RTLD_LAZY | RTLD_LOCAL); 194 if (!icd->handle) { 195 loader_log(XGL_DBG_MSG_WARNING, 0, dlerror()); 196 free(icd); 197 return NULL; 198 } 199 200#define LOOKUP(icd, func) do { \ 201 icd->func = (func## T) dlsym(icd->handle, "xgl" #func); \ 202 if (!icd->func) { \ 203 loader_log(XGL_DBG_MSG_WARNING, 0, dlerror()); \ 204 loader_icd_destroy(icd); \ 205 return NULL; \ 206 } \ 207} while (0) 208 LOOKUP(icd, InitAndEnumerateGpus); 209 LOOKUP(icd, DbgRegisterMsgCallback); 210 LOOKUP(icd, DbgUnregisterMsgCallback); 211 LOOKUP(icd, DbgSetGlobalOption); 212#undef LOOKUP 213 214 return icd; 215} 216 217static XGL_RESULT loader_icd_register_msg_callbacks(const struct loader_icd *icd) 218{ 219 const struct loader_msg_callback *cb = loader.msg_callbacks; 220 XGL_RESULT res; 221 222 while (cb) { 223 res = icd->DbgRegisterMsgCallback(cb->func, cb->data); 224 if (res != XGL_SUCCESS) { 225 break; 226 } 227 228 cb = cb->next; 229 } 230 231 /* roll back on errors */ 232 if (cb) { 233 const struct loader_msg_callback *tmp = loader.msg_callbacks; 234 235 while (tmp != cb) { 236 icd->DbgUnregisterMsgCallback(cb->func); 237 tmp = tmp->next; 238 } 239 240 return res; 241 } 242 243 return XGL_SUCCESS; 244} 245 246static XGL_RESULT loader_icd_set_global_options(const struct loader_icd *icd) 247{ 248#define SETB(icd, opt, val) do { \ 249 if (val) { \ 250 const XGL_RESULT res = \ 251 icd->DbgSetGlobalOption(opt, sizeof(val), &val); \ 252 if (res != XGL_SUCCESS) \ 253 return res; \ 254 } \ 255} while (0) 256 SETB(icd, XGL_DBG_OPTION_DEBUG_ECHO_ENABLE, loader.debug_echo_enable); 257 SETB(icd, XGL_DBG_OPTION_BREAK_ON_ERROR, loader.break_on_error); 258 SETB(icd, XGL_DBG_OPTION_BREAK_ON_WARNING, loader.break_on_warning); 259#undef SETB 260 261return XGL_SUCCESS; 262} 263 264static struct loader_icd *loader_icd_add(const char *filename) 265{ 266 struct loader_icd *icd; 267 268 icd = loader_icd_create(filename); 269 if (!icd) 270 return NULL; 271 272 if (loader_icd_set_global_options(icd) != XGL_SUCCESS || 273 loader_icd_register_msg_callbacks(icd) != XGL_SUCCESS) { 274 loader_log(XGL_DBG_MSG_WARNING, 0, 275 "%s ignored: failed to migrate settings", filename); 276 loader_icd_destroy(icd); 277 } 278 279 /* prepend to the list */ 280 icd->next = loader.icds; 281 loader.icds = icd; 282 283 return icd; 284} 285 286#ifndef DEFAULT_XGL_DRIVERS_PATH 287// TODO: Is this a good default location? 288// Need to search for both 32bit and 64bit ICDs 289#define DEFAULT_XGL_DRIVERS_PATH "/usr/lib/i386-linux-gnu/xgl:/usr/lib/x86_64-linux-gnu/xgl" 290#endif 291 292/** 293 * Try to \c loader_icd_scan XGL driver(s). 294 * 295 * This function scans the default system path or path 296 * specified by the \c LIBXGL_DRIVERS_PATH environment variable in 297 * order to find loadable XGL ICDs with the name of libXGL_*. 298 * 299 * \returns 300 * void; but side effect is to set loader_icd_scanned to true 301 */ 302static void loader_icd_scan(void) 303{ 304 const char *libPaths, *p, *next; 305 DIR *sysdir; 306 struct dirent *dent; 307 char icd_library[1024]; 308 char path[1024]; 309 int len; 310 311 libPaths = NULL; 312 if (geteuid() == getuid()) { 313 /* don't allow setuid apps to use LIBXGL_DRIVERS_PATH */ 314 libPaths = getenv("LIBXGL_DRIVERS_PATH"); 315 } 316 if (libPaths == NULL) 317 libPaths = DEFAULT_XGL_DRIVERS_PATH; 318 319 for (p = libPaths; *p; p = next) { 320 next = strchr(p, ':'); 321 if (next == NULL) { 322 len = strlen(p); 323 next = p + len; 324 } 325 else { 326 len = next - p; 327 sprintf(path, "%.*s", (len > sizeof(path) - 1) ? (int) sizeof(path) - 1 : len, p); 328 p = path; 329 next++; 330 } 331 332 sysdir = opendir(p); 333 if (sysdir) { 334 dent = readdir(sysdir); 335 while (dent) { 336 /* look for ICDs starting with "libXGL_" */ 337 if (!strncmp(dent->d_name, "libXGL_", 7)) { 338 snprintf(icd_library, 1024, "%s/%s",p,dent->d_name); 339 loader_icd_add(icd_library); 340 } 341 342 dent = readdir(sysdir); 343 } 344 closedir(sysdir); 345 } 346 } 347 348 /* we have nothing to log anymore */ 349 loader_msg_callback_clear(); 350 351 loader.scanned = true; 352} 353 354LOADER_EXPORT XGL_RESULT XGLAPI xglInitAndEnumerateGpus(const XGL_APPLICATION_INFO* pAppInfo, const XGL_ALLOC_CALLBACKS* pAllocCb, XGL_UINT maxGpus, XGL_UINT* pGpuCount, XGL_PHYSICAL_GPU* pGpus) 355{ 356 static pthread_once_t once = PTHREAD_ONCE_INIT; 357 const struct loader_icd *icd; 358 XGL_UINT count = 0; 359 XGL_RESULT res; 360 361 pthread_once(&once, loader_icd_scan); 362 363 if (!loader.icds) 364 return XGL_ERROR_UNAVAILABLE; 365 366 icd = loader.icds; 367 while (icd) { 368 XGL_PHYSICAL_GPU gpus[XGL_MAX_PHYSICAL_GPUS]; 369 XGL_UINT n, max = maxGpus - count; 370 371 if (max > XGL_MAX_PHYSICAL_GPUS) { 372 max = XGL_MAX_PHYSICAL_GPUS; 373 } 374 375 res = icd->InitAndEnumerateGpus(pAppInfo, pAllocCb, max, &n, gpus); 376 if (res == XGL_SUCCESS && n) { 377 memcpy(pGpus + count, gpus, sizeof(*pGpus) * n); 378 count += n; 379 380 if (count >= maxGpus) { 381 break; 382 } 383 } 384 385 icd = icd->next; 386 } 387 388 *pGpuCount = count; 389 390 return (count > 0) ? XGL_SUCCESS : res; 391} 392 393LOADER_EXPORT XGL_RESULT XGLAPI xglDbgRegisterMsgCallback(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback, XGL_VOID* pUserData) 394{ 395 const struct loader_icd *icd = loader.icds; 396 XGL_RESULT res; 397 398 if (!loader.scanned) { 399 return loader_msg_callback_add(pfnMsgCallback, pUserData); 400 } 401 402 while (icd) { 403 res = icd->DbgRegisterMsgCallback(pfnMsgCallback, pUserData); 404 if (res != XGL_SUCCESS) { 405 break; 406 } 407 408 icd = icd->next; 409 } 410 411 /* roll back on errors */ 412 if (icd) { 413 const struct loader_icd *tmp = loader.icds; 414 415 while (tmp != icd) { 416 tmp->DbgUnregisterMsgCallback(pfnMsgCallback); 417 tmp = tmp->next; 418 } 419 420 return res; 421 } 422 423 return XGL_SUCCESS; 424} 425 426LOADER_EXPORT XGL_RESULT XGLAPI xglDbgUnregisterMsgCallback(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback) 427{ 428 const struct loader_icd *icd = loader.icds; 429 XGL_RESULT res = XGL_SUCCESS; 430 431 if (!loader.scanned) { 432 return loader_msg_callback_remove(pfnMsgCallback); 433 } 434 435 while (icd) { 436 XGL_RESULT r = icd->DbgUnregisterMsgCallback(pfnMsgCallback); 437 if (r != XGL_SUCCESS) { 438 res = r; 439 } 440 icd = icd->next; 441 } 442 443 return res; 444} 445 446LOADER_EXPORT XGL_RESULT XGLAPI xglDbgSetGlobalOption(XGL_DBG_GLOBAL_OPTION dbgOption, XGL_SIZE dataSize, const XGL_VOID* pData) 447{ 448 const struct loader_icd *icd = loader.icds; 449 XGL_RESULT res = XGL_SUCCESS; 450 451 if (!loader.scanned) { 452 if (dataSize == 0) 453 return XGL_ERROR_INVALID_VALUE; 454 455 switch (dbgOption) { 456 case XGL_DBG_OPTION_DEBUG_ECHO_ENABLE: 457 loader.debug_echo_enable = *((const bool *) pData); 458 break; 459 case XGL_DBG_OPTION_BREAK_ON_ERROR: 460 loader.break_on_error = *((const bool *) pData); 461 break; 462 case XGL_DBG_OPTION_BREAK_ON_WARNING: 463 loader.break_on_warning = *((const bool *) pData); 464 break; 465 default: 466 res = XGL_ERROR_INVALID_VALUE; 467 break; 468 } 469 470 return res; 471 } 472 473 while (icd) { 474 XGL_RESULT r = icd->DbgSetGlobalOption(dbgOption, dataSize, pData); 475 /* unfortunately we cannot roll back */ 476 if (r != XGL_SUCCESS) { 477 res = r; 478 } 479 480 icd = icd->next; 481 } 482 483 return res; 484} 485