egl: avoid dereferencing a null display
[mesa.git] / src / egl / main / egldisplay.c
1 /**************************************************************************
2 *
3 * Copyright 2008 VMware, Inc.
4 * Copyright 2009-2010 Chia-I Wu <olvaffe@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 related to EGLDisplay.
33 */
34
35 #include <assert.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include "c11/threads.h"
39
40 #include "eglcontext.h"
41 #include "eglcurrent.h"
42 #include "eglsurface.h"
43 #include "egldisplay.h"
44 #include "egldriver.h"
45 #include "eglglobals.h"
46 #include "egllog.h"
47 #include "eglimage.h"
48 #include "eglsync.h"
49
50 /* Includes for _eglNativePlatformDetectNativeDisplay */
51 #ifdef HAVE_MINCORE
52 #include <unistd.h>
53 #include <sys/mman.h>
54 #endif
55 #ifdef HAVE_WAYLAND_PLATFORM
56 #include <wayland-client.h>
57 #endif
58 #ifdef HAVE_DRM_PLATFORM
59 #include <gbm.h>
60 #endif
61
62
63 /**
64 * Map --with-egl-platforms names to platform types.
65 */
66 static const struct {
67 _EGLPlatformType platform;
68 const char *name;
69 } egl_platforms[_EGL_NUM_PLATFORMS] = {
70 { _EGL_PLATFORM_X11, "x11" },
71 { _EGL_PLATFORM_WAYLAND, "wayland" },
72 { _EGL_PLATFORM_DRM, "drm" },
73 { _EGL_PLATFORM_ANDROID, "android" },
74 { _EGL_PLATFORM_HAIKU, "haiku" },
75 { _EGL_PLATFORM_SURFACELESS, "surfaceless" },
76 };
77
78
79 /**
80 * Return the native platform by parsing EGL_PLATFORM.
81 */
82 static _EGLPlatformType
83 _eglGetNativePlatformFromEnv(void)
84 {
85 _EGLPlatformType plat = _EGL_INVALID_PLATFORM;
86 const char *plat_name;
87 EGLint i;
88
89 plat_name = getenv("EGL_PLATFORM");
90 /* try deprecated env variable */
91 if (!plat_name || !plat_name[0])
92 plat_name = getenv("EGL_DISPLAY");
93 if (!plat_name || !plat_name[0])
94 return _EGL_INVALID_PLATFORM;
95
96 for (i = 0; i < _EGL_NUM_PLATFORMS; i++) {
97 if (strcmp(egl_platforms[i].name, plat_name) == 0) {
98 plat = egl_platforms[i].platform;
99 break;
100 }
101 }
102
103 return plat;
104 }
105
106
107 /**
108 * Perform validity checks on a generic pointer.
109 */
110 static EGLBoolean
111 _eglPointerIsDereferencable(void *p)
112 {
113 #ifdef HAVE_MINCORE
114 uintptr_t addr = (uintptr_t) p;
115 unsigned char valid = 0;
116 const long page_size = getpagesize();
117
118 if (p == NULL)
119 return EGL_FALSE;
120
121 /* align addr to page_size */
122 addr &= ~(page_size - 1);
123
124 if (mincore((void *) addr, page_size, &valid) < 0) {
125 _eglLog(_EGL_DEBUG, "mincore failed: %m");
126 return EGL_FALSE;
127 }
128
129 return (valid & 0x01) == 0x01;
130 #else
131 return p != NULL;
132 #endif
133 }
134
135
136 /**
137 * Try detecting native platform with the help of native display characteristcs.
138 */
139 static _EGLPlatformType
140 _eglNativePlatformDetectNativeDisplay(void *nativeDisplay)
141 {
142 if (nativeDisplay == EGL_DEFAULT_DISPLAY)
143 return _EGL_INVALID_PLATFORM;
144
145 if (_eglPointerIsDereferencable(nativeDisplay)) {
146 void *first_pointer = *(void **) nativeDisplay;
147
148 (void) first_pointer; /* silence unused var warning */
149
150 #ifdef HAVE_WAYLAND_PLATFORM
151 /* wl_display is a wl_proxy, which is a wl_object.
152 * wl_object's first element points to the interfacetype. */
153 if (first_pointer == &wl_display_interface)
154 return _EGL_PLATFORM_WAYLAND;
155 #endif
156
157 #ifdef HAVE_DRM_PLATFORM
158 /* gbm has a pointer to its constructor as first element. */
159 if (first_pointer == gbm_create_device)
160 return _EGL_PLATFORM_DRM;
161 #endif
162
163 #ifdef HAVE_X11_PLATFORM
164 /* If not matched to any other platform, fallback to x11. */
165 return _EGL_PLATFORM_X11;
166 #endif
167
168 #ifdef HAVE_HAIKU_PLATFORM
169 return _EGL_PLATFORM_HAIKU;
170 #endif
171 }
172
173 return _EGL_INVALID_PLATFORM;
174 }
175
176
177 /**
178 * Return the native platform. It is the platform of the EGL native types.
179 */
180 _EGLPlatformType
181 _eglGetNativePlatform(void *nativeDisplay)
182 {
183 static _EGLPlatformType native_platform;
184 char *detection_method;
185
186 native_platform = _eglGetNativePlatformFromEnv();
187 detection_method = "environment overwrite";
188
189 if (native_platform == _EGL_INVALID_PLATFORM) {
190 native_platform = _eglNativePlatformDetectNativeDisplay(nativeDisplay);
191 detection_method = "autodetected";
192 }
193
194 if (native_platform == _EGL_INVALID_PLATFORM) {
195 native_platform = _EGL_NATIVE_PLATFORM;
196 detection_method = "build-time configuration";
197 }
198
199 _eglLog(_EGL_DEBUG, "Native platform type: %s (%s)",
200 egl_platforms[native_platform].name, detection_method);
201
202 return native_platform;
203 }
204
205
206 /**
207 * Finish display management.
208 */
209 void
210 _eglFiniDisplay(void)
211 {
212 _EGLDisplay *dpyList, *dpy;
213
214 /* atexit function is called with global mutex locked */
215 dpyList = _eglGlobal.DisplayList;
216 while (dpyList) {
217 EGLint i;
218
219 /* pop list head */
220 dpy = dpyList;
221 dpyList = dpyList->Next;
222
223 for (i = 0; i < _EGL_NUM_RESOURCES; i++) {
224 if (dpy->ResourceLists[i]) {
225 _eglLog(_EGL_DEBUG, "Display %p is destroyed with resources", dpy);
226 break;
227 }
228 }
229
230 free(dpy);
231 }
232 _eglGlobal.DisplayList = NULL;
233 }
234
235
236 /**
237 * Find the display corresponding to the specified native display, or create a
238 * new one.
239 */
240 _EGLDisplay *
241 _eglFindDisplay(_EGLPlatformType plat, void *plat_dpy)
242 {
243 _EGLDisplay *dpy;
244
245 if (plat == _EGL_INVALID_PLATFORM)
246 return NULL;
247
248 mtx_lock(_eglGlobal.Mutex);
249
250 /* search the display list first */
251 dpy = _eglGlobal.DisplayList;
252 while (dpy) {
253 if (dpy->Platform == plat && dpy->PlatformDisplay == plat_dpy)
254 break;
255 dpy = dpy->Next;
256 }
257
258 /* create a new display */
259 if (!dpy) {
260 dpy = calloc(1, sizeof(_EGLDisplay));
261 if (dpy) {
262 mtx_init(&dpy->Mutex, mtx_plain);
263 dpy->Platform = plat;
264 dpy->PlatformDisplay = plat_dpy;
265
266 /* add to the display list */
267 dpy->Next = _eglGlobal.DisplayList;
268 _eglGlobal.DisplayList = dpy;
269 }
270 }
271
272 mtx_unlock(_eglGlobal.Mutex);
273
274 return dpy;
275 }
276
277
278 /**
279 * Destroy the contexts and surfaces that are linked to the display.
280 */
281 void
282 _eglReleaseDisplayResources(_EGLDriver *drv, _EGLDisplay *display)
283 {
284 _EGLResource *list;
285
286 list = display->ResourceLists[_EGL_RESOURCE_CONTEXT];
287 while (list) {
288 _EGLContext *ctx = (_EGLContext *) list;
289 list = list->Next;
290
291 _eglUnlinkContext(ctx);
292 drv->API.DestroyContext(drv, display, ctx);
293 }
294 assert(!display->ResourceLists[_EGL_RESOURCE_CONTEXT]);
295
296 list = display->ResourceLists[_EGL_RESOURCE_SURFACE];
297 while (list) {
298 _EGLSurface *surf = (_EGLSurface *) list;
299 list = list->Next;
300
301 _eglUnlinkSurface(surf);
302 drv->API.DestroySurface(drv, display, surf);
303 }
304 assert(!display->ResourceLists[_EGL_RESOURCE_SURFACE]);
305
306 list = display->ResourceLists[_EGL_RESOURCE_IMAGE];
307 while (list) {
308 _EGLImage *image = (_EGLImage *) list;
309 list = list->Next;
310
311 _eglUnlinkImage(image);
312 drv->API.DestroyImageKHR(drv, display, image);
313 }
314 assert(!display->ResourceLists[_EGL_RESOURCE_IMAGE]);
315
316 list = display->ResourceLists[_EGL_RESOURCE_SYNC];
317 while (list) {
318 _EGLSync *sync = (_EGLSync *) list;
319 list = list->Next;
320
321 _eglUnlinkSync(sync);
322 drv->API.DestroySyncKHR(drv, display, sync);
323 }
324 assert(!display->ResourceLists[_EGL_RESOURCE_SYNC]);
325 }
326
327
328 /**
329 * Free all the data hanging of an _EGLDisplay object, but not
330 * the object itself.
331 */
332 void
333 _eglCleanupDisplay(_EGLDisplay *disp)
334 {
335 if (disp->Configs) {
336 _eglDestroyArray(disp->Configs, free);
337 disp->Configs = NULL;
338 }
339
340 /* XXX incomplete */
341 }
342
343
344 /**
345 * Return EGL_TRUE if the given handle is a valid handle to a display.
346 */
347 EGLBoolean
348 _eglCheckDisplayHandle(EGLDisplay dpy)
349 {
350 _EGLDisplay *cur;
351
352 mtx_lock(_eglGlobal.Mutex);
353 cur = _eglGlobal.DisplayList;
354 while (cur) {
355 if (cur == (_EGLDisplay *) dpy)
356 break;
357 cur = cur->Next;
358 }
359 mtx_unlock(_eglGlobal.Mutex);
360 return (cur != NULL);
361 }
362
363
364 /**
365 * Return EGL_TRUE if the given resource is valid. That is, the display does
366 * own the resource.
367 */
368 EGLBoolean
369 _eglCheckResource(void *res, _EGLResourceType type, _EGLDisplay *dpy)
370 {
371 _EGLResource *list = dpy->ResourceLists[type];
372
373 if (!res)
374 return EGL_FALSE;
375
376 while (list) {
377 if (res == (void *) list) {
378 assert(list->Display == dpy);
379 break;
380 }
381 list = list->Next;
382 }
383
384 return (list != NULL);
385 }
386
387
388 /**
389 * Initialize a display resource. The size of the subclass object is
390 * specified.
391 *
392 * This is supposed to be called from the initializers of subclasses, such as
393 * _eglInitContext or _eglInitSurface.
394 */
395 void
396 _eglInitResource(_EGLResource *res, EGLint size, _EGLDisplay *dpy)
397 {
398 memset(res, 0, size);
399 res->Display = dpy;
400 res->RefCount = 1;
401 }
402
403
404 /**
405 * Increment reference count for the resource.
406 */
407 void
408 _eglGetResource(_EGLResource *res)
409 {
410 assert(res && res->RefCount > 0);
411 /* hopefully a resource is always manipulated with its display locked */
412 res->RefCount++;
413 }
414
415
416 /**
417 * Decrement reference count for the resource.
418 */
419 EGLBoolean
420 _eglPutResource(_EGLResource *res)
421 {
422 assert(res && res->RefCount > 0);
423 res->RefCount--;
424 return (!res->RefCount);
425 }
426
427
428 /**
429 * Link a resource to its display.
430 */
431 void
432 _eglLinkResource(_EGLResource *res, _EGLResourceType type)
433 {
434 assert(res->Display);
435
436 res->IsLinked = EGL_TRUE;
437 res->Next = res->Display->ResourceLists[type];
438 res->Display->ResourceLists[type] = res;
439 _eglGetResource(res);
440 }
441
442
443 /**
444 * Unlink a linked resource from its display.
445 */
446 void
447 _eglUnlinkResource(_EGLResource *res, _EGLResourceType type)
448 {
449 _EGLResource *prev;
450
451 prev = res->Display->ResourceLists[type];
452 if (prev != res) {
453 while (prev) {
454 if (prev->Next == res)
455 break;
456 prev = prev->Next;
457 }
458 assert(prev);
459 prev->Next = res->Next;
460 }
461 else {
462 res->Display->ResourceLists[type] = res->Next;
463 }
464
465 res->Next = NULL;
466 res->IsLinked = EGL_FALSE;
467 _eglPutResource(res);
468
469 /* We always unlink before destroy. The driver still owns a reference */
470 assert(res->RefCount);
471 }
472
473 #ifdef HAVE_X11_PLATFORM
474 static EGLBoolean
475 _eglParseX11DisplayAttribList(_EGLDisplay *display, const EGLint *attrib_list)
476 {
477 int i;
478
479 if (attrib_list == NULL) {
480 return EGL_TRUE;
481 }
482
483 for (i = 0; attrib_list[i] != EGL_NONE; i += 2) {
484 EGLint attrib = attrib_list[i];
485 EGLint value = attrib_list[i + 1];
486
487 /* EGL_EXT_platform_x11 recognizes exactly one attribute,
488 * EGL_PLATFORM_X11_SCREEN_EXT, which is optional.
489 */
490 if (attrib == EGL_PLATFORM_X11_SCREEN_EXT) {
491 display->Options.Platform = (void *)value;
492 } else {
493 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
494 return EGL_FALSE;
495 }
496 }
497
498 return EGL_TRUE;
499 }
500
501 _EGLDisplay*
502 _eglGetX11Display(Display *native_display,
503 const EGLint *attrib_list)
504 {
505 _EGLDisplay *display = _eglFindDisplay(_EGL_PLATFORM_X11,
506 native_display);
507
508 if (!display) {
509 _eglError(EGL_BAD_ALLOC, "eglGetPlatformDisplay");
510 return NULL;
511 }
512
513 if (!_eglParseX11DisplayAttribList(display, attrib_list)) {
514 return NULL;
515 }
516
517 return display;
518 }
519 #endif /* HAVE_X11_PLATFORM */
520
521 #ifdef HAVE_DRM_PLATFORM
522 _EGLDisplay*
523 _eglGetGbmDisplay(struct gbm_device *native_display,
524 const EGLint *attrib_list)
525 {
526 /* EGL_MESA_platform_gbm recognizes no attributes. */
527 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
528 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
529 return NULL;
530 }
531
532 return _eglFindDisplay(_EGL_PLATFORM_DRM, native_display);
533 }
534 #endif /* HAVE_DRM_PLATFORM */
535
536 #ifdef HAVE_WAYLAND_PLATFORM
537 _EGLDisplay*
538 _eglGetWaylandDisplay(struct wl_display *native_display,
539 const EGLint *attrib_list)
540 {
541 /* EGL_EXT_platform_wayland recognizes no attributes. */
542 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
543 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
544 return NULL;
545 }
546
547 return _eglFindDisplay(_EGL_PLATFORM_WAYLAND, native_display);
548 }
549 #endif /* HAVE_WAYLAND_PLATFORM */
550
551 #ifdef HAVE_SURFACELESS_PLATFORM
552 _EGLDisplay*
553 _eglGetSurfacelessDisplay(void *native_display,
554 const EGLint *attrib_list)
555 {
556 /* This platform has no native display. */
557 if (native_display != NULL) {
558 _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay");
559 return NULL;
560 }
561
562 /* This platform recognizes no display attributes. */
563 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
564 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
565 return NULL;
566 }
567
568 return _eglFindDisplay(_EGL_PLATFORM_SURFACELESS, native_display);
569 }
570 #endif /* HAVE_SURFACELESS_PLATFORM */