blob: 563baae5f3d925f5834e84df340ca96f96b28716 [file] [log] [blame]
Chia-I Wu5f72d0f2014-08-01 11:21:23 +08001/*
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
25#include <stdio.h>
26#include <stdlib.h>
27#include <stdarg.h>
28#include <stdbool.h>
29#include <string.h>
30
Chia-I Wu894a1172014-08-04 11:18:20 +080031#include <sys/types.h>
32#include <dirent.h>
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -060033#include <unistd.h>
Chia-I Wu5f72d0f2014-08-01 11:21:23 +080034#include <dlfcn.h>
35#include <pthread.h>
36
Chia-I Wu468e3c32014-08-04 08:03:57 +080037#include "loader.h"
Chia-I Wu5f72d0f2014-08-01 11:21:23 +080038
Chia-I Wu5f72d0f2014-08-01 11:21:23 +080039typedef XGL_RESULT (XGLAPI *InitAndEnumerateGpusT)(const XGL_APPLICATION_INFO* pAppInfo, const XGL_ALLOC_CALLBACKS* pAllocCb, XGL_UINT maxGpus, XGL_UINT* pGpuCount, XGL_PHYSICAL_GPU* pGpus);
40typedef XGL_RESULT (XGLAPI *DbgRegisterMsgCallbackT)(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback, XGL_VOID* pUserData);
41typedef XGL_RESULT (XGLAPI *DbgUnregisterMsgCallbackT)(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback);
42typedef XGL_RESULT (XGLAPI *DbgSetGlobalOptionT)(XGL_INT dbgOption, XGL_SIZE dataSize, const XGL_VOID* pData);
43
44struct loader_icd {
45 void *handle;
46
47 InitAndEnumerateGpusT InitAndEnumerateGpus;
48 DbgRegisterMsgCallbackT DbgRegisterMsgCallback;
49 DbgUnregisterMsgCallbackT DbgUnregisterMsgCallback;
50 DbgSetGlobalOptionT DbgSetGlobalOption;
51
52 struct loader_icd *next;
53};
54
55struct loader_msg_callback {
56 XGL_DBG_MSG_CALLBACK_FUNCTION func;
57 XGL_VOID *data;
58
59 struct loader_msg_callback *next;
60};
61
62static struct {
63 bool scanned;
64 struct loader_icd *icds;
65
66 struct loader_msg_callback *msg_callbacks;
67
68 bool debug_echo_enable;
69 bool break_on_error;
70 bool break_on_warning;
71} loader;
72
73static XGL_RESULT loader_msg_callback_add(XGL_DBG_MSG_CALLBACK_FUNCTION func,
74 XGL_VOID *data)
75{
76 struct loader_msg_callback *cb;
77
78 cb = malloc(sizeof(*cb));
79 if (!cb)
80 return XGL_ERROR_OUT_OF_MEMORY;
81
82 cb->func = func;
83 cb->data = data;
84
85 cb->next = loader.msg_callbacks;
86 loader.msg_callbacks = cb;
87
88 return XGL_SUCCESS;
89}
90
91static XGL_RESULT loader_msg_callback_remove(XGL_DBG_MSG_CALLBACK_FUNCTION func)
92{
93 struct loader_msg_callback *cb = loader.msg_callbacks;
94
95 /*
96 * Find the first match (last registered).
97 *
98 * XXX What if the same callback function is registered more than once?
99 */
100 while (cb) {
101 if (cb->func == func) {
102 break;
103 }
104
105 cb = cb->next;
106 }
107
108 if (!cb)
109 return XGL_ERROR_INVALID_POINTER;
110
111 free(cb);
112
113 return XGL_SUCCESS;
114}
115
116static void loader_msg_callback_clear(void)
117{
118 struct loader_msg_callback *cb = loader.msg_callbacks;
119
120 while (cb) {
121 struct loader_msg_callback *next = cb->next;
122 free(cb);
123 cb = next;
124 }
125
126 loader.msg_callbacks = NULL;
127}
128
129static void loader_log(XGL_DBG_MSG_TYPE msg_type, XGL_INT msg_code,
130 const char *format, ...)
131{
132 const struct loader_msg_callback *cb = loader.msg_callbacks;
133 char msg[256];
134 va_list ap;
135 int ret;
136
137 va_start(ap, format);
138 ret = vsnprintf(msg, sizeof(msg), format, ap);
139 if (ret >= sizeof(msg) || ret < 0) {
140 msg[sizeof(msg) - 1] = '\0';
141 }
142 va_end(ap);
143
144 if (loader.debug_echo_enable || !cb) {
145 fputs(msg, stderr);
146 fputc('\n', stderr);
147 }
148
149 while (cb) {
150 cb->func(msg_type, XGL_VALIDATION_LEVEL_0, XGL_NULL_HANDLE, 0,
151 msg_code, (const XGL_CHAR *) msg, cb->data);
152 cb = cb->next;
153 }
154
155 switch (msg_type) {
156 case XGL_DBG_MSG_ERROR:
157 if (loader.break_on_error) {
158 exit(1);
159 }
160 /* fall through */
161 case XGL_DBG_MSG_WARNING:
162 if (loader.break_on_warning) {
163 exit(1);
164 }
165 break;
166 default:
167 break;
168 }
169}
170
171static void
172loader_icd_destroy(struct loader_icd *icd)
173{
174 dlclose(icd->handle);
175 free(icd);
176}
177
178static struct loader_icd *
179loader_icd_create(const char *filename)
180{
181 struct loader_icd *icd;
182
183 icd = malloc(sizeof(*icd));
184 if (!icd)
185 return NULL;
186
187 icd->handle = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);
188 if (!icd->handle) {
189 loader_log(XGL_DBG_MSG_WARNING, 0, dlerror());
190 free(icd);
191 return NULL;
192 }
193
194#define LOOKUP(icd, func) do { \
195 icd->func = (func## T) dlsym(icd->handle, "xgl" #func); \
196 if (!icd->func) { \
197 loader_log(XGL_DBG_MSG_WARNING, 0, dlerror()); \
198 loader_icd_destroy(icd); \
199 return NULL; \
200 } \
201} while (0)
202 LOOKUP(icd, InitAndEnumerateGpus);
203 LOOKUP(icd, DbgRegisterMsgCallback);
204 LOOKUP(icd, DbgUnregisterMsgCallback);
205 LOOKUP(icd, DbgSetGlobalOption);
206#undef LOOKUP
207
208 return icd;
209}
210
211static XGL_RESULT loader_icd_register_msg_callbacks(const struct loader_icd *icd)
212{
213 const struct loader_msg_callback *cb = loader.msg_callbacks;
214 XGL_RESULT res;
215
216 while (cb) {
217 res = icd->DbgRegisterMsgCallback(cb->func, cb->data);
218 if (res != XGL_SUCCESS) {
219 break;
220 }
221
222 cb = cb->next;
223 }
224
225 /* roll back on errors */
226 if (cb) {
227 const struct loader_msg_callback *tmp = loader.msg_callbacks;
228
229 while (tmp != cb) {
230 icd->DbgUnregisterMsgCallback(cb->func);
231 tmp = tmp->next;
232 }
233
234 return res;
235 }
236
237 return XGL_SUCCESS;
238}
239
240static XGL_RESULT loader_icd_set_global_options(const struct loader_icd *icd)
241{
242#define SETB(icd, opt, val) do { \
243 if (val) { \
244 const XGL_RESULT res = \
245 icd->DbgSetGlobalOption(opt, sizeof(val), &val); \
246 if (res != XGL_SUCCESS) \
247 return res; \
248 } \
249} while (0)
250 SETB(icd, XGL_DBG_OPTION_DEBUG_ECHO_ENABLE, loader.debug_echo_enable);
251 SETB(icd, XGL_DBG_OPTION_BREAK_ON_ERROR, loader.break_on_error);
252 SETB(icd, XGL_DBG_OPTION_BREAK_ON_WARNING, loader.break_on_warning);
253#undef SETB
254
255return XGL_SUCCESS;
256}
257
Chia-I Wu894a1172014-08-04 11:18:20 +0800258static struct loader_icd *loader_icd_add(const char *filename)
259{
260 struct loader_icd *icd;
261
262 icd = loader_icd_create(filename);
263 if (!icd)
264 return NULL;
265
266 if (loader_icd_set_global_options(icd) != XGL_SUCCESS ||
267 loader_icd_register_msg_callbacks(icd) != XGL_SUCCESS) {
268 loader_log(XGL_DBG_MSG_WARNING, 0,
269 "%s ignored: failed to migrate settings", filename);
270 loader_icd_destroy(icd);
271 }
272
273 /* prepend to the list */
274 icd->next = loader.icds;
275 loader.icds = icd;
276
277 return icd;
278}
279
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600280#ifndef DEFAULT_XGL_DRIVERS_PATH
281// TODO: Is this a good default location?
282// Need to search for both 32bit and 64bit ICDs
283#define DEFAULT_XGL_DRIVERS_PATH "/usr/lib/i386-linux-gnu/xgl:/usr/lib/x86_64-linux-gnu/xgl"
284#endif
285
286/**
287 * Try to \c loader_icd_scan XGL driver(s).
288 *
289 * This function scans the default system path or path
290 * specified by the \c LIBXGL_DRIVERS_PATH environment variable in
291 * order to find loadable XGL ICDs with the name of libXGL_*.
292 *
293 * \returns
294 * void; but side effect is to set loader_icd_scanned to true
295 */
296static void loader_icd_scan(void)
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800297{
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600298 const char *libPaths, *p, *next;
299 DIR *sysdir;
300 struct dirent *dent;
301 char icd_library[1024];
302 int len;
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800303
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600304 libPaths = NULL;
305 if (geteuid() == getuid()) {
306 /* don't allow setuid apps to use LIBXGL_DRIVERS_PATH */
307 libPaths = getenv("LIBXGL_DRIVERS_PATH");
308 }
309 if (libPaths == NULL)
310 libPaths = DEFAULT_XGL_DRIVERS_PATH;
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800311
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600312 for (p = libPaths; *p; p = next) {
313 next = strchr(p, ':');
314 if (next == NULL) {
315 len = strlen(p);
316 next = p + len;
317 }
318 else {
319 len = next - p;
320 next++;
321 }
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800322
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600323 sysdir = opendir(p);
324 if (sysdir) {
325 dent = readdir(sysdir);
326 while (dent) {
327 /* look for ICDs starting with "libXGL_" */
328 if (!strncmp(dent->d_name, "libXGL_", 7)) {
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600329 snprintf(icd_library, 1024, "%s/%s",p,dent->d_name);
Chia-I Wu894a1172014-08-04 11:18:20 +0800330 loader_icd_add(icd_library);
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600331 }
332
333 dent = readdir(sysdir);
334 }
335 closedir(sysdir);
336 }
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800337 }
338
339 /* we have nothing to log anymore */
340 loader_msg_callback_clear();
341
342 loader.scanned = true;
343}
344
Chia-I Wu468e3c32014-08-04 08:03:57 +0800345LOADER_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)
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800346{
347 static pthread_once_t once = PTHREAD_ONCE_INIT;
348 const struct loader_icd *icd;
349 XGL_UINT count = 0;
350 XGL_RESULT res;
351
352 pthread_once(&once, loader_icd_scan);
353
354 if (!loader.icds)
355 return XGL_ERROR_UNAVAILABLE;
356
357 icd = loader.icds;
358 while (icd) {
359 XGL_PHYSICAL_GPU gpus[XGL_MAX_PHYSICAL_GPUS];
360 XGL_UINT n, max = maxGpus - count;
361
362 if (max > XGL_MAX_PHYSICAL_GPUS) {
363 max = XGL_MAX_PHYSICAL_GPUS;
364 }
365
366 res = icd->InitAndEnumerateGpus(pAppInfo, pAllocCb, max, &n, gpus);
Chia-I Wu74916ed2014-08-06 12:17:04 +0800367 if (res == XGL_SUCCESS && n) {
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800368 memcpy(pGpus + count, gpus, sizeof(*pGpus) * n);
369 count += n;
370
371 if (count >= maxGpus) {
372 break;
373 }
374 }
375
376 icd = icd->next;
377 }
378
379 *pGpuCount = count;
380
381 return (count > 0) ? XGL_SUCCESS : res;
382}
383
Chia-I Wu468e3c32014-08-04 08:03:57 +0800384LOADER_EXPORT XGL_RESULT XGLAPI xglDbgRegisterMsgCallback(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback, XGL_VOID* pUserData)
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800385{
386 const struct loader_icd *icd = loader.icds;
387 XGL_RESULT res;
388
389 if (!loader.scanned) {
390 return loader_msg_callback_add(pfnMsgCallback, pUserData);
391 }
392
393 while (icd) {
394 res = icd->DbgRegisterMsgCallback(pfnMsgCallback, pUserData);
395 if (res != XGL_SUCCESS) {
396 break;
397 }
398
399 icd = icd->next;
400 }
401
402 /* roll back on errors */
403 if (icd) {
404 const struct loader_icd *tmp = loader.icds;
405
406 while (tmp != icd) {
407 tmp->DbgUnregisterMsgCallback(pfnMsgCallback);
408 tmp = tmp->next;
409 }
410
411 return res;
412 }
413
414 return XGL_SUCCESS;
415}
416
Chia-I Wu468e3c32014-08-04 08:03:57 +0800417LOADER_EXPORT XGL_RESULT XGLAPI xglDbgUnregisterMsgCallback(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback)
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800418{
419 const struct loader_icd *icd = loader.icds;
420 XGL_RESULT res = XGL_SUCCESS;
421
422 if (!loader.scanned) {
423 return loader_msg_callback_remove(pfnMsgCallback);
424 }
425
426 while (icd) {
427 XGL_RESULT r = icd->DbgUnregisterMsgCallback(pfnMsgCallback);
428 if (r != XGL_SUCCESS) {
429 res = r;
430 }
431 icd = icd->next;
432 }
433
434 return res;
435}
436
Chia-I Wu468e3c32014-08-04 08:03:57 +0800437LOADER_EXPORT XGL_RESULT XGLAPI xglDbgSetGlobalOption(XGL_DBG_GLOBAL_OPTION dbgOption, XGL_SIZE dataSize, const XGL_VOID* pData)
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800438{
439 const struct loader_icd *icd = loader.icds;
440 XGL_RESULT res = XGL_SUCCESS;
441
442 if (!loader.scanned) {
443 if (dataSize == 0)
444 return XGL_ERROR_INVALID_VALUE;
445
446 switch (dbgOption) {
447 case XGL_DBG_OPTION_DEBUG_ECHO_ENABLE:
448 loader.debug_echo_enable = *((const bool *) pData);
449 break;
450 case XGL_DBG_OPTION_BREAK_ON_ERROR:
451 loader.break_on_error = *((const bool *) pData);
452 break;
453 case XGL_DBG_OPTION_BREAK_ON_WARNING:
454 loader.break_on_warning = *((const bool *) pData);
455 break;
456 default:
457 res = XGL_ERROR_INVALID_VALUE;
458 break;
459 }
460
461 return res;
462 }
463
464 while (icd) {
465 XGL_RESULT r = icd->DbgSetGlobalOption(dbgOption, dataSize, pData);
466 /* unfortunately we cannot roll back */
467 if (r != XGL_SUCCESS) {
468 res = r;
469 }
470
471 icd = icd->next;
472 }
473
474 return res;
475}