blob: 20da6783f8a05d3f2e472415864af11ec112ba92 [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.
Chia-I Wu44e42362014-09-02 08:32:09 +080023 *
24 * Authors:
25 * Chia-I Wu <olv@lunarg.com>
26 * Courtney Goeltzenleuchter <courtney@lunarg.com>
Chia-I Wu5f72d0f2014-08-01 11:21:23 +080027 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <stdarg.h>
32#include <stdbool.h>
33#include <string.h>
34
Chia-I Wu894a1172014-08-04 11:18:20 +080035#include <sys/types.h>
36#include <dirent.h>
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -060037#include <unistd.h>
Chia-I Wu5f72d0f2014-08-01 11:21:23 +080038#include <dlfcn.h>
39#include <pthread.h>
40
Chia-I Wu468e3c32014-08-04 08:03:57 +080041#include "loader.h"
Chia-I Wu5f72d0f2014-08-01 11:21:23 +080042
Chia-I Wu5f72d0f2014-08-01 11:21:23 +080043typedef 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 icd->handle = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);
192 if (!icd->handle) {
193 loader_log(XGL_DBG_MSG_WARNING, 0, dlerror());
194 free(icd);
195 return NULL;
196 }
197
198#define LOOKUP(icd, func) do { \
199 icd->func = (func## T) dlsym(icd->handle, "xgl" #func); \
200 if (!icd->func) { \
201 loader_log(XGL_DBG_MSG_WARNING, 0, dlerror()); \
202 loader_icd_destroy(icd); \
203 return NULL; \
204 } \
205} while (0)
206 LOOKUP(icd, InitAndEnumerateGpus);
207 LOOKUP(icd, DbgRegisterMsgCallback);
208 LOOKUP(icd, DbgUnregisterMsgCallback);
209 LOOKUP(icd, DbgSetGlobalOption);
210#undef LOOKUP
211
212 return icd;
213}
214
215static XGL_RESULT loader_icd_register_msg_callbacks(const struct loader_icd *icd)
216{
217 const struct loader_msg_callback *cb = loader.msg_callbacks;
218 XGL_RESULT res;
219
220 while (cb) {
221 res = icd->DbgRegisterMsgCallback(cb->func, cb->data);
222 if (res != XGL_SUCCESS) {
223 break;
224 }
225
226 cb = cb->next;
227 }
228
229 /* roll back on errors */
230 if (cb) {
231 const struct loader_msg_callback *tmp = loader.msg_callbacks;
232
233 while (tmp != cb) {
234 icd->DbgUnregisterMsgCallback(cb->func);
235 tmp = tmp->next;
236 }
237
238 return res;
239 }
240
241 return XGL_SUCCESS;
242}
243
244static XGL_RESULT loader_icd_set_global_options(const struct loader_icd *icd)
245{
246#define SETB(icd, opt, val) do { \
247 if (val) { \
248 const XGL_RESULT res = \
249 icd->DbgSetGlobalOption(opt, sizeof(val), &val); \
250 if (res != XGL_SUCCESS) \
251 return res; \
252 } \
253} while (0)
254 SETB(icd, XGL_DBG_OPTION_DEBUG_ECHO_ENABLE, loader.debug_echo_enable);
255 SETB(icd, XGL_DBG_OPTION_BREAK_ON_ERROR, loader.break_on_error);
256 SETB(icd, XGL_DBG_OPTION_BREAK_ON_WARNING, loader.break_on_warning);
257#undef SETB
258
259return XGL_SUCCESS;
260}
261
Chia-I Wu894a1172014-08-04 11:18:20 +0800262static struct loader_icd *loader_icd_add(const char *filename)
263{
264 struct loader_icd *icd;
265
266 icd = loader_icd_create(filename);
267 if (!icd)
268 return NULL;
269
270 if (loader_icd_set_global_options(icd) != XGL_SUCCESS ||
271 loader_icd_register_msg_callbacks(icd) != XGL_SUCCESS) {
272 loader_log(XGL_DBG_MSG_WARNING, 0,
273 "%s ignored: failed to migrate settings", filename);
274 loader_icd_destroy(icd);
275 }
276
277 /* prepend to the list */
278 icd->next = loader.icds;
279 loader.icds = icd;
280
281 return icd;
282}
283
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600284#ifndef DEFAULT_XGL_DRIVERS_PATH
285// TODO: Is this a good default location?
286// Need to search for both 32bit and 64bit ICDs
287#define DEFAULT_XGL_DRIVERS_PATH "/usr/lib/i386-linux-gnu/xgl:/usr/lib/x86_64-linux-gnu/xgl"
288#endif
289
290/**
291 * Try to \c loader_icd_scan XGL driver(s).
292 *
293 * This function scans the default system path or path
294 * specified by the \c LIBXGL_DRIVERS_PATH environment variable in
295 * order to find loadable XGL ICDs with the name of libXGL_*.
296 *
297 * \returns
298 * void; but side effect is to set loader_icd_scanned to true
299 */
300static void loader_icd_scan(void)
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800301{
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600302 const char *libPaths, *p, *next;
303 DIR *sysdir;
304 struct dirent *dent;
305 char icd_library[1024];
306 int len;
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800307
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600308 libPaths = NULL;
309 if (geteuid() == getuid()) {
310 /* don't allow setuid apps to use LIBXGL_DRIVERS_PATH */
311 libPaths = getenv("LIBXGL_DRIVERS_PATH");
312 }
313 if (libPaths == NULL)
314 libPaths = DEFAULT_XGL_DRIVERS_PATH;
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800315
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600316 for (p = libPaths; *p; p = next) {
317 next = strchr(p, ':');
318 if (next == NULL) {
319 len = strlen(p);
320 next = p + len;
321 }
322 else {
323 len = next - p;
324 next++;
325 }
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800326
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600327 sysdir = opendir(p);
328 if (sysdir) {
329 dent = readdir(sysdir);
330 while (dent) {
331 /* look for ICDs starting with "libXGL_" */
332 if (!strncmp(dent->d_name, "libXGL_", 7)) {
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600333 snprintf(icd_library, 1024, "%s/%s",p,dent->d_name);
Chia-I Wu894a1172014-08-04 11:18:20 +0800334 loader_icd_add(icd_library);
Courtney Goeltzenleuchter4af9bd12014-08-01 14:18:11 -0600335 }
336
337 dent = readdir(sysdir);
338 }
339 closedir(sysdir);
340 }
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800341 }
342
343 /* we have nothing to log anymore */
344 loader_msg_callback_clear();
345
346 loader.scanned = true;
347}
348
Chia-I Wu468e3c32014-08-04 08:03:57 +0800349LOADER_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 +0800350{
351 static pthread_once_t once = PTHREAD_ONCE_INIT;
352 const struct loader_icd *icd;
353 XGL_UINT count = 0;
354 XGL_RESULT res;
355
356 pthread_once(&once, loader_icd_scan);
357
358 if (!loader.icds)
359 return XGL_ERROR_UNAVAILABLE;
360
361 icd = loader.icds;
362 while (icd) {
363 XGL_PHYSICAL_GPU gpus[XGL_MAX_PHYSICAL_GPUS];
364 XGL_UINT n, max = maxGpus - count;
365
366 if (max > XGL_MAX_PHYSICAL_GPUS) {
367 max = XGL_MAX_PHYSICAL_GPUS;
368 }
369
370 res = icd->InitAndEnumerateGpus(pAppInfo, pAllocCb, max, &n, gpus);
Chia-I Wu74916ed2014-08-06 12:17:04 +0800371 if (res == XGL_SUCCESS && n) {
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800372 memcpy(pGpus + count, gpus, sizeof(*pGpus) * n);
373 count += n;
374
375 if (count >= maxGpus) {
376 break;
377 }
378 }
379
380 icd = icd->next;
381 }
382
383 *pGpuCount = count;
384
385 return (count > 0) ? XGL_SUCCESS : res;
386}
387
Chia-I Wu468e3c32014-08-04 08:03:57 +0800388LOADER_EXPORT XGL_RESULT XGLAPI xglDbgRegisterMsgCallback(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback, XGL_VOID* pUserData)
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800389{
390 const struct loader_icd *icd = loader.icds;
391 XGL_RESULT res;
392
393 if (!loader.scanned) {
394 return loader_msg_callback_add(pfnMsgCallback, pUserData);
395 }
396
397 while (icd) {
398 res = icd->DbgRegisterMsgCallback(pfnMsgCallback, pUserData);
399 if (res != XGL_SUCCESS) {
400 break;
401 }
402
403 icd = icd->next;
404 }
405
406 /* roll back on errors */
407 if (icd) {
408 const struct loader_icd *tmp = loader.icds;
409
410 while (tmp != icd) {
411 tmp->DbgUnregisterMsgCallback(pfnMsgCallback);
412 tmp = tmp->next;
413 }
414
415 return res;
416 }
417
418 return XGL_SUCCESS;
419}
420
Chia-I Wu468e3c32014-08-04 08:03:57 +0800421LOADER_EXPORT XGL_RESULT XGLAPI xglDbgUnregisterMsgCallback(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback)
Chia-I Wu5f72d0f2014-08-01 11:21:23 +0800422{
423 const struct loader_icd *icd = loader.icds;
424 XGL_RESULT res = XGL_SUCCESS;
425
426 if (!loader.scanned) {
427 return loader_msg_callback_remove(pfnMsgCallback);
428 }
429
430 while (icd) {
431 XGL_RESULT r = icd->DbgUnregisterMsgCallback(pfnMsgCallback);
432 if (r != XGL_SUCCESS) {
433 res = r;
434 }
435 icd = icd->next;
436 }
437
438 return res;
439}
440
Chia-I Wu468e3c32014-08-04 08:03:57 +0800441LOADER_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 +0800442{
443 const struct loader_icd *icd = loader.icds;
444 XGL_RESULT res = XGL_SUCCESS;
445
446 if (!loader.scanned) {
447 if (dataSize == 0)
448 return XGL_ERROR_INVALID_VALUE;
449
450 switch (dbgOption) {
451 case XGL_DBG_OPTION_DEBUG_ECHO_ENABLE:
452 loader.debug_echo_enable = *((const bool *) pData);
453 break;
454 case XGL_DBG_OPTION_BREAK_ON_ERROR:
455 loader.break_on_error = *((const bool *) pData);
456 break;
457 case XGL_DBG_OPTION_BREAK_ON_WARNING:
458 loader.break_on_warning = *((const bool *) pData);
459 break;
460 default:
461 res = XGL_ERROR_INVALID_VALUE;
462 break;
463 }
464
465 return res;
466 }
467
468 while (icd) {
469 XGL_RESULT r = icd->DbgSetGlobalOption(dbgOption, dataSize, pData);
470 /* unfortunately we cannot roll back */
471 if (r != XGL_SUCCESS) {
472 res = r;
473 }
474
475 icd = icd->next;
476 }
477
478 return res;
479}