blob: c2fe170506530a2647b8ef883b6425c4dbd7b45b [file] [log] [blame]
Courtney Goeltzenleuchtere06e72d2014-08-01 12:44:23 -06001/*
2 * XGL 3-D graphics library
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 * Courtney Goeltzenleuchter <courtney@lunarg.com>
26 */
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <assert.h>
35#include <string.h>
36#include <unistd.h>
37#include <fcntl.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <dirent.h>
41#include <errno.h>
42#include <fnmatch.h>
43
44#include <pciaccess.h>
45
46#include <xf86drm.h>
Chia-I Wu155be032014-08-02 09:14:28 +080047#include <i915_drm.h>
Courtney Goeltzenleuchtere06e72d2014-08-01 12:44:23 -060048
49#include <libudev.h>
50#include <dlfcn.h>
51
52#ifdef HAVE_VALGRIND
53#include <valgrind.h>
54#include <memcheck.h>
55#define VG(x) x
56#else
57#define VG(x)
58#endif
59
60#define VG_CLEAR(s) VG(memset(&s, 0, sizeof(s)))
61
62#include "xgl.h"
63#include "xglDbg.h"
64
65#include "driver.h"
66#include "intel_chipset.h"
67#include "gen7_functions.h"
68
69XGL_RESULT XGLAPI GetExtensionSupport(XGL_PHYSICAL_GPU gpu, const XGL_CHAR* pExtName);
70
71
72#if defined(__WIN32__) && !defined(__CYGWIN__)
73#elif defined(__CYGWIN__) && defined(USE_OPENGL32) /* use native windows opengl32 */
74# define ICDENTRY extern
75#elif (defined(__GNUC__) && __GNUC__ >= 4) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
76# define ICDENTRY __attribute__((visibility("default")))
77#endif /* WIN32 && !CYGWIN */
78
79#define LOADER_DBG_MSG_SIZE 256
80
81/*
82 * Global to contain application allocation call-backs
83 * and GPU enumeration info.
84 */
85struct _xgl_config icd_data;
86
87struct icd_msg_callback {
88 XGL_DBG_MSG_CALLBACK_FUNCTION func;
89 XGL_VOID *data;
90
91 struct icd_msg_callback *next;
92};
93
94static XGL_VOID *_xglAlloc(XGL_VOID * pUserData, XGL_SIZE size,
95 XGL_SIZE alignment, XGL_SYSTEM_ALLOC_TYPE allocType)
96{
97 // TODO: ensure alignment
98 // can we use? aligned_alloc(alignment, size);
99 return (XGL_VOID *) malloc(size);
100}
101
102static XGL_VOID _xglFree(XGL_VOID * pUserData, XGL_VOID * pMem)
103{
104 free(pMem);
105}
106
107static struct icd_msg_callback *icd_msg_callbacks = NULL;
108
109static XGL_RESULT icd_msg_callback_add(XGL_DBG_MSG_CALLBACK_FUNCTION func,
110 XGL_VOID * data)
111{
112 struct icd_msg_callback *cb;
113
114 cb = malloc(sizeof(*cb));
115 if (!cb)
116 return XGL_ERROR_OUT_OF_MEMORY;
117
118 cb->func = func;
119 cb->data = data;
120
121 cb->next = icd_msg_callbacks;
122 icd_msg_callbacks = cb;
123
124 return XGL_SUCCESS;
125}
126
127static XGL_RESULT icd_msg_callback_remove(XGL_DBG_MSG_CALLBACK_FUNCTION func)
128{
129 struct icd_msg_callback *cb = icd_msg_callbacks;
130
131 /*
132 * Find the first match.
133 *
134 * XXX What if the same callback function is registered more
135 * than once?
136 */
137 while (cb) {
138 if (cb->func == func)
139 break;
140 cb = cb->next;
141 }
142
143 if (!cb)
144 return XGL_ERROR_INVALID_POINTER;
145
146 free(cb);
147
148 return XGL_SUCCESS;
149}
150
151static void icd_msg_callback_clear(void)
152{
153 struct icd_msg_callback *cb = icd_msg_callbacks;
154
155 cb = icd_msg_callbacks;
156 while (cb) {
157 struct icd_msg_callback *next = cb->next;
158 free(cb);
159 cb = next;
160 }
161}
162
163static void loader_err(XGL_INT msg_code, const char *format, ...)
164{
165 const struct icd_msg_callback *cb = icd_msg_callbacks;
166 char msg[LOADER_DBG_MSG_SIZE];
167 va_list ap;
168 int ret;
169
170 if (!cb) {
171#if 1
172 fputs(msg, stderr);
173 fputc('\n', stderr);
174#endif
175 return;
176 }
177
178 va_start(ap, format);
179 ret = vsnprintf(msg, sizeof(msg), format, ap);
180 if (ret >= sizeof(msg) || ret < 0)
181 msg[sizeof(msg) - 1] = '\0';
182 va_end(ap);
183
184 while (cb) {
185 cb->func(0, 0, XGL_NULL_HANDLE, 0, msg_code, (const XGL_CHAR *) msg,
186 cb->data);
187 cb = cb->next;
188 }
189}
190
191ICDENTRY XGL_RESULT XGLAPI xglLoad()
192{
193 memset(&icd_data, 0, sizeof(icd_data));
194 return XGL_SUCCESS;
195}
196
197ICDENTRY XGL_RESULT XGLAPI xglUnload()
198{
199 // TODO: Free resources
200 icd_msg_callback_clear();
201 return XGL_SUCCESS;
202}
203
204ICDENTRY XGL_RESULT XGLAPI xglDbgSetGlobalOption(XGL_DBG_GLOBAL_OPTION dbgOption,
205 XGL_SIZE dataSize,
206 const XGL_VOID * pData)
207{
208 return XGL_SUCCESS;
209}
210
211ICDENTRY XGL_RESULT XGLAPI xglDbgRegisterMsgCallback(XGL_DBG_MSG_CALLBACK_FUNCTION
212 pfnMsgCallback,
213 XGL_VOID * pUserData)
214{
215 return icd_msg_callback_add(pfnMsgCallback, pUserData);
216}
217
218ICDENTRY XGL_RESULT XGLAPI xglDbgUnregisterMsgCallback(XGL_DBG_MSG_CALLBACK_FUNCTION
219 pfnMsgCallback)
220{
221 return icd_msg_callback_remove(pfnMsgCallback);
222}
223
224static int is_render_node(int fd, struct stat *st)
225{
226 if (fstat(fd, st))
227 return 0;
228
229 if (!S_ISCHR(st->st_mode))
230 return 0;
231
232 return st->st_rdev & 0x80;
233}
234
235static void init_xgl_dispatch_table(struct icd_dispatch_table *pDispatch)
236{
237#define LOAD(xglfunc, driverfunc) do { \
238 pDispatch->xglfunc = driverfunc; \
239} while (0)
240 LOAD(GetGpuInfo, gen7_GetGpuInfo);
241 LOAD(GetExtensionSupport, GetExtensionSupport);
242}
243
244static void init_validation_dispatch_table(struct icd_dispatch_table *pDispatch)
245{
246#define LOAD(xglfunc, driverfunc) do { \
247 pDispatch->xglfunc = driverfunc; \
248} while (0)
249 LOAD(GetGpuInfo, gen7_GetGpuInfo);
250 LOAD(GetExtensionSupport, GetExtensionSupport);
251}
252
253ICDENTRY XGL_RESULT XGLAPI xglInitAndEnumerateGpus(const XGL_APPLICATION_INFO *
254 pAppInfo,
255 const XGL_ALLOC_CALLBACKS *
256 pAllocCb, XGL_UINT maxGpus,
257 XGL_UINT * pGpuCount,
258 XGL_PHYSICAL_GPU * pGpus)
259{
260 struct udev *udev;
261 struct udev_enumerate *e;
262 struct udev_device *device, *parent;
263 struct udev_list_entry *entry;
264 const char *pci_id, *path;
265 const char *usub, *dnode;
266 int fd;
267 struct stat st;
268 char *pci_glob = "*:*";
269 XGL_RESULT ret = XGL_ERROR_UNAVAILABLE;
270 XGL_UINT count = 0;
271 struct _xgl_device *pXglDev;
272
273 // TODO: Do we need to keep track of previous calls to xglInitAndEnumerageGpus?
274 /*
275 * xglInitAndEnumerateGpus() can be called multiple times. Calling it more than once
276 * forces driver reinitialization.
277 */
278
279 if (icd_data.num_gpus > 0) {
280 /*
281 * When xglInitAndEnumerateGpus() is called multiple times,
282 * the same callbacks have to be provided on each invocation.
283 * Changing the callbacks on subsequent calls to xglInitAndEnumerateGpus()
284 * causes it to fail with XGL_ERROR_INVALID_POINTER error.
285 */
286 if (icd_data.UserDefinedAlloc
287 && (icd_data.xgl_alloc.pfnAlloc != pAllocCb->pfnAlloc
288 || icd_data.xgl_alloc.pfnFree != pAllocCb->pfnFree)) {
289 // TODO: Do we still re-initialize the structure?
290 return XGL_ERROR_INVALID_POINTER;
291 }
292 // TODO: Free resources and re-initialize
293 }
294 // TODO: Do we need any other validation for incoming pointers?
295
296 if (pAllocCb != XGL_NULL_HANDLE && pAllocCb->pfnAlloc != XGL_NULL_HANDLE) {
297 icd_data.UserDefinedAlloc = XGL_TRUE;
298 icd_data.xgl_alloc.pfnAlloc = pAllocCb->pfnAlloc;
299 icd_data.xgl_alloc.pfnFree = pAllocCb->pfnFree;
300 icd_data.xgl_alloc.pUserData = pAllocCb->pUserData;
301 } else {
302 icd_data.xgl_alloc.pfnAlloc = _xglAlloc;
303 icd_data.xgl_alloc.pfnFree = _xglFree;
304 icd_data.xgl_alloc.pUserData = XGL_NULL_HANDLE;
305 }
306
307 udev = udev_new();
308 if (udev == NULL) {
309 loader_err(0, "failed to initialize udev context");
310 abort();
311 }
312
313 memset(icd_data.gpu_info, 0,
314 sizeof(icd_data.gpu_info[XGL_MAX_PHYSICAL_GPUS]));
315 memset(icd_data.render_node_list, 0,
316 sizeof(icd_data.render_node_list[XGL_MAX_PHYSICAL_GPUS]));
317
318 fd = -1;
319 e = udev_enumerate_new(udev);
320 udev_enumerate_add_match_subsystem(e, "drm");
321 udev_enumerate_scan_devices(e);
322 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
323 path = udev_list_entry_get_name(entry);
324 device = udev_device_new_from_syspath(udev, path);
325 parent = udev_device_get_parent(device);
326 usub = udev_device_get_subsystem(parent);
327 /* Filter out KMS output devices. */
328 if (!usub || (strcmp(usub, "pci") != 0))
329 continue;
330 pci_id = udev_device_get_property_value(parent, "PCI_ID");
331 if (fnmatch(pci_glob, pci_id, 0) != 0)
332 continue;
333 dnode = udev_device_get_devnode(device);
334 fd = open(dnode, O_RDWR);
335 if (fd < 0)
336 continue;
337 if (!is_render_node(fd, &st)) {
338 close(fd);
339 fd = -1;
340 continue;
341 }
342
343 pXglDev = &icd_data.gpu_info[count];
344
345 sscanf(pci_id, "%x:%x", &pXglDev->ven_id, &pXglDev->dev_id);
346 /*
347 * Currently only allowing Ivybridge and Haswell
348 */
349 if (!IS_HASWELL(pXglDev->dev_id) && !IS_IVYBRIDGE(pXglDev->dev_id)) {
350 close(fd);
351 fd = -1;
352 continue;
353 }
354
355 /*
356 * We'll keep the fd open for any subsequent
357 * xglGetGpuInfo calls.
358 */
359 pXglDev->fd = fd;
360 strncpy(pXglDev->device_path, dnode, XGL_MAX_PHYSICAL_GPU_NAME);
361 icd_data.render_node_list[count] = pXglDev;
362 init_xgl_dispatch_table(&pXglDev->xgl);
363 init_validation_dispatch_table(&pXglDev->validation);
364 pXglDev->exec = & pXglDev->xgl;
365
366 ret = XGL_SUCCESS;
367 count++;
368 }
369 icd_data.num_gpus = count;
370 udev_enumerate_unref(e);
371 udev_unref(udev);
372
373 if (ret == XGL_SUCCESS) {
374 *pGpuCount = icd_data.num_gpus;
375 memcpy(pGpus, icd_data.render_node_list,
376 (icd_data.num_gpus >
377 maxGpus ? maxGpus : icd_data.num_gpus) *
378 sizeof(XGL_PHYSICAL_GPU));
379 }
380
381 return ret;
382}
383
384XGL_RESULT XGLAPI GetExtensionSupport(XGL_PHYSICAL_GPU gpu, const XGL_CHAR* pExtName)
385{
386 // struct _xgl_device *pdev = (struct _xgl_device *) gpu;
387
388 // TODO: Check requested extension
389
390 return XGL_ERROR_INVALID_EXTENSION;
391}