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#include <stdlib.h>
32#include <stdio.h>
33#include <string.h>
34#include <assert.h>
35#include "c11/threads.h"
36
37#include "eglglobals.h"
38#include "egldevice.h"
39#include "egldisplay.h"
40#include "egldriver.h"
41#include "egllog.h"
42
43#include "util/macros.h"
44
45#ifdef HAVE_MINCORE
46#include <unistd.h>
47#include <sys/mman.h>
48#endif
49
50
51static mtx_t _eglGlobalMutex = _MTX_INITIALIZER_NP;
52
53struct _egl_global _eglGlobal =
54{
55   .Mutex = &_eglGlobalMutex,
56   .DisplayList = NULL,
57   .DeviceList = &_eglSoftwareDevice,
58   .NumAtExitCalls = 3,
59   .AtExitCalls = {
60      /* default AtExitCalls, called in reverse order */
61      _eglFiniDevice, /* always called last */
62      _eglUnloadDrivers,
63      _eglFiniDisplay,
64   },
65
66   .ClientOnlyExtensionString =
67   "EGL_EXT_client_extensions"
68   " EGL_EXT_device_base"
69   " EGL_EXT_device_enumeration"
70   " EGL_EXT_device_query"
71   " EGL_EXT_platform_base"
72   " EGL_KHR_client_get_all_proc_addresses"
73   " EGL_KHR_debug",
74
75   .PlatformExtensionString =
76#ifdef HAVE_WAYLAND_PLATFORM
77   " EGL_EXT_platform_wayland"
78#endif
79#ifdef HAVE_X11_PLATFORM
80   " EGL_EXT_platform_x11"
81#endif
82#ifdef HAVE_DRM_PLATFORM
83   " EGL_MESA_platform_gbm"
84#endif
85#ifdef HAVE_SURFACELESS_PLATFORM
86   " EGL_MESA_platform_surfaceless"
87#endif
88   "",
89
90   .ClientExtensionString = NULL,
91
92   .debugCallback = NULL,
93   .debugTypesEnabled = _EGL_DEBUG_BIT_CRITICAL | _EGL_DEBUG_BIT_ERROR,
94};
95
96static EGLBoolean registered = EGL_FALSE;
97
98static void __attribute__((__destructor__))
99_eglAtExit(void)
100{
101   EGLint i;
102
103   if (!registered)
104      return;
105
106   for (i = _eglGlobal.NumAtExitCalls - 1; i >= 0; i--)
107      _eglGlobal.AtExitCalls[i]();
108}
109
110
111void
112_eglAddAtExitCall(void (*func)(void))
113{
114   if (func) {
115      static EGLBoolean registered = EGL_FALSE;
116
117      mtx_lock(_eglGlobal.Mutex);
118
119      registered = EGL_TRUE;
120
121      assert(_eglGlobal.NumAtExitCalls < ARRAY_SIZE(_eglGlobal.AtExitCalls));
122      _eglGlobal.AtExitCalls[_eglGlobal.NumAtExitCalls++] = func;
123
124      mtx_unlock(_eglGlobal.Mutex);
125   }
126}
127
128const char *
129_eglGetClientExtensionString(void)
130{
131   const char *ret;
132
133   mtx_lock(_eglGlobal.Mutex);
134
135   if (_eglGlobal.ClientExtensionString == NULL) {
136      size_t clientLen = strlen(_eglGlobal.ClientOnlyExtensionString);
137      size_t platformLen = strlen(_eglGlobal.PlatformExtensionString);
138
139      _eglGlobal.ClientExtensionString = (char *) malloc(clientLen + platformLen + 1);
140      if (_eglGlobal.ClientExtensionString != NULL) {
141         char *ptr = _eglGlobal.ClientExtensionString;
142
143         memcpy(ptr, _eglGlobal.ClientOnlyExtensionString, clientLen);
144         ptr += clientLen;
145
146         if (platformLen > 0) {
147            // Note that if PlatformExtensionString is not empty, then it will
148            // already have a leading space.
149            assert(_eglGlobal.PlatformExtensionString[0] == ' ');
150            memcpy(ptr, _eglGlobal.PlatformExtensionString, platformLen);
151            ptr += platformLen;
152         }
153         *ptr = '\0';
154      }
155   }
156   ret = _eglGlobal.ClientExtensionString;
157
158   mtx_unlock(_eglGlobal.Mutex);
159   return ret;
160}
161
162EGLBoolean
163_eglPointerIsDereferencable(void *p)
164{
165#ifdef HAVE_MINCORE
166   uintptr_t addr = (uintptr_t) p;
167#ifdef __linux__
168   unsigned
169#endif
170   char valid = 0;
171   const long page_size = getpagesize();
172
173   if (p == NULL)
174      return EGL_FALSE;
175
176   /* align addr to page_size */
177   addr &= ~(page_size - 1);
178
179   if (mincore((void *) addr, page_size, &valid) < 0) {
180      _eglLog(_EGL_DEBUG, "mincore failed: %m");
181      return EGL_FALSE;
182   }
183
184   /* mincore() returns 0 on success, and -1 on failure.  The last parameter
185    * is a vector of bytes with one entry for each page queried.  mincore
186    * returns page residency information in the first bit of each byte in the
187    * vector.
188    *
189    * Residency doesn't actually matter when determining whether a pointer is
190    * dereferenceable, so the output vector can be ignored.  What matters is
191    * whether mincore succeeds. See:
192    *
193    *   http://man7.org/linux/man-pages/man2/mincore.2.html
194    */
195   return EGL_TRUE;
196#else
197   return p != NULL;
198#endif
199}
200