xref: /aosp_15_r20/external/mesa3d/src/egl/main/egldevice.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /**************************************************************************
2  *
3  * Copyright 2015, 2018 Collabora
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 
28 #ifdef HAVE_LIBDRM
29 #include <xf86drm.h>
30 #endif
31 #include "util/compiler.h"
32 #include "util/macros.h"
33 
34 #include "eglcurrent.h"
35 #include "egldevice.h"
36 #include "eglglobals.h"
37 #include "egllog.h"
38 #include "egltypedefs.h"
39 
40 struct _egl_device {
41    _EGLDevice *Next;
42 
43    const char *extensions;
44 
45    EGLBoolean MESA_device_software;
46    EGLBoolean EXT_device_drm;
47    EGLBoolean EXT_device_drm_render_node;
48 
49 #ifdef HAVE_LIBDRM
50    drmDevicePtr device;
51 #endif
52 };
53 
54 void
_eglFiniDevice(void)55 _eglFiniDevice(void)
56 {
57    _EGLDevice *dev_list, *dev;
58 
59    /* atexit function is called with global mutex locked */
60 
61    dev_list = _eglGlobal.DeviceList;
62 
63    /* The first device is static allocated SW device */
64    assert(dev_list);
65    assert(_eglDeviceSupports(dev_list, _EGL_DEVICE_SOFTWARE));
66    dev_list = dev_list->Next;
67 
68    while (dev_list) {
69       /* pop list head */
70       dev = dev_list;
71       dev_list = dev_list->Next;
72 
73 #ifdef HAVE_LIBDRM
74       assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
75       drmFreeDevice(&dev->device);
76 #endif
77       free(dev);
78    }
79 
80    _eglGlobal.DeviceList = NULL;
81 }
82 
83 EGLBoolean
_eglCheckDeviceHandle(EGLDeviceEXT device)84 _eglCheckDeviceHandle(EGLDeviceEXT device)
85 {
86    _EGLDevice *cur;
87 
88    simple_mtx_lock(_eglGlobal.Mutex);
89    cur = _eglGlobal.DeviceList;
90    while (cur) {
91       if (cur == (_EGLDevice *)device)
92          break;
93       cur = cur->Next;
94    }
95    simple_mtx_unlock(_eglGlobal.Mutex);
96    return (cur != NULL);
97 }
98 
99 _EGLDevice _eglSoftwareDevice = {
100    /* TODO: EGL_EXT_device_drm support for KMS + llvmpipe */
101    .extensions = "EGL_MESA_device_software EGL_EXT_device_drm_render_node",
102    .MESA_device_software = EGL_TRUE,
103    .EXT_device_drm_render_node = EGL_TRUE,
104 };
105 
106 #ifdef HAVE_LIBDRM
107 /*
108  * Negative value on error, zero if newly added, one if already in list.
109  */
110 static int
_eglAddDRMDevice(drmDevicePtr device)111 _eglAddDRMDevice(drmDevicePtr device)
112 {
113    _EGLDevice *dev;
114 
115    assert(device->available_nodes & ((1 << DRM_NODE_RENDER)));
116 
117    /* TODO: uncomment this assert, which is a sanity check.
118     *
119     * assert(device->available_nodes & ((1 << DRM_NODE_PRIMARY)));
120     *
121     * DRM shim does not expose a primary node, so the CI would fail if we had
122     * this assert. DRM shim is being used to run shader-db. We need to
123     * investigate what should be done (probably fixing DRM shim).
124     */
125 
126    dev = _eglGlobal.DeviceList;
127 
128    /* The first device is always software */
129    assert(dev);
130    assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
131 
132    while (dev->Next) {
133       dev = dev->Next;
134 
135       assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
136       if (drmDevicesEqual(device, dev->device) != 0)
137          return 1;
138    }
139 
140    dev->Next = calloc(1, sizeof(_EGLDevice));
141    if (!dev->Next)
142       return -1;
143 
144    dev = dev->Next;
145    dev->extensions = "EGL_EXT_device_drm EGL_EXT_device_drm_render_node";
146    dev->EXT_device_drm = EGL_TRUE;
147    dev->EXT_device_drm_render_node = EGL_TRUE;
148    dev->device = device;
149 
150    return 0;
151 }
152 #endif
153 
154 /* Finds a device in DeviceList, for the given fd.
155  *
156  * The fd must be of a render-capable device, as there are only render-capable
157  * devices in DeviceList.
158  *
159  * If a software device, the fd is ignored.
160  */
161 _EGLDevice *
_eglFindDevice(int fd,bool software)162 _eglFindDevice(int fd, bool software)
163 {
164    _EGLDevice *dev;
165 
166    simple_mtx_lock(_eglGlobal.Mutex);
167    dev = _eglGlobal.DeviceList;
168 
169    /* The first device is always software */
170    assert(dev);
171    assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
172    if (software)
173       goto out;
174 
175 #ifdef HAVE_LIBDRM
176    drmDevicePtr device;
177 
178    if (drmGetDevice2(fd, 0, &device) != 0) {
179       dev = NULL;
180       goto out;
181    }
182 
183    while (dev->Next) {
184       dev = dev->Next;
185 
186       if (_eglDeviceSupports(dev, _EGL_DEVICE_DRM) &&
187           drmDevicesEqual(device, dev->device) != 0) {
188          goto cleanup_drm;
189       }
190    }
191 
192    /* Couldn't find an EGLDevice for the device. */
193    dev = NULL;
194 
195 cleanup_drm:
196    drmFreeDevice(&device);
197 
198 #else
199    _eglLog(_EGL_FATAL,
200            "Driver bug: Built without libdrm, yet looking for HW device");
201    dev = NULL;
202 #endif
203 
204 out:
205    simple_mtx_unlock(_eglGlobal.Mutex);
206    return dev;
207 }
208 
209 #ifdef HAVE_LIBDRM
210 drmDevicePtr
_eglDeviceDrm(_EGLDevice * dev)211 _eglDeviceDrm(_EGLDevice *dev)
212 {
213    if (!dev)
214       return NULL;
215 
216    return dev->device;
217 }
218 #endif
219 
220 _EGLDevice *
_eglDeviceNext(_EGLDevice * dev)221 _eglDeviceNext(_EGLDevice *dev)
222 {
223    if (!dev)
224       return NULL;
225 
226    return dev->Next;
227 }
228 
229 EGLBoolean
_eglDeviceSupports(_EGLDevice * dev,_EGLDeviceExtension ext)230 _eglDeviceSupports(_EGLDevice *dev, _EGLDeviceExtension ext)
231 {
232    switch (ext) {
233    case _EGL_DEVICE_SOFTWARE:
234       return dev->MESA_device_software;
235    case _EGL_DEVICE_DRM:
236       return dev->EXT_device_drm;
237    case _EGL_DEVICE_DRM_RENDER_NODE:
238       return dev->EXT_device_drm_render_node;
239    default:
240       assert(0);
241       return EGL_FALSE;
242    };
243 }
244 
245 EGLBoolean
_eglQueryDeviceAttribEXT(_EGLDevice * dev,EGLint attribute,EGLAttrib * value)246 _eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute, EGLAttrib *value)
247 {
248    switch (attribute) {
249    default:
250       _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceAttribEXT");
251       return EGL_FALSE;
252    }
253 }
254 
255 const char *
_eglQueryDeviceStringEXT(_EGLDevice * dev,EGLint name)256 _eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name)
257 {
258    switch (name) {
259    case EGL_EXTENSIONS:
260       return dev->extensions;
261    case EGL_DRM_DEVICE_FILE_EXT:
262       if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM))
263          break;
264 #ifdef HAVE_LIBDRM
265       return dev->device->nodes[DRM_NODE_PRIMARY];
266 #else
267       /* This should never happen: we don't yet support EGL_DEVICE_DRM for the
268        * software device, and physical devices are only exposed when libdrm is
269        * available. */
270       assert(0);
271       break;
272 #endif
273    case EGL_DRM_RENDER_NODE_FILE_EXT:
274       if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM_RENDER_NODE))
275          break;
276 #ifdef HAVE_LIBDRM
277       /* EGLDevice represents a software device, so no render node
278        * should be advertised. */
279       if (_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE))
280          return NULL;
281       /* We create EGLDevice's only for render capable devices. */
282       assert(dev->device->available_nodes & (1 << DRM_NODE_RENDER));
283       return dev->device->nodes[DRM_NODE_RENDER];
284 #else
285       /* Physical devices are only exposed when libdrm is available. */
286       assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
287       return NULL;
288 #endif
289    }
290    _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT");
291    return NULL;
292 }
293 
294 /* Do a fresh lookup for devices.
295  *
296  * Walks through the DeviceList, discarding no longer available ones
297  * and adding new ones as applicable.
298  *
299  * Must be called with the global lock held.
300  */
301 int
_eglDeviceRefreshList(void)302 _eglDeviceRefreshList(void)
303 {
304    ASSERTED _EGLDevice *dev;
305    int count = 0;
306 
307    dev = _eglGlobal.DeviceList;
308 
309    /* The first device is always software */
310    assert(dev);
311    assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
312    count++;
313 
314 #ifdef HAVE_LIBDRM
315    drmDevicePtr devices[64];
316    int num_devs, ret;
317 
318    num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
319    for (int i = 0; i < num_devs; i++) {
320       if (!(devices[i]->available_nodes & (1 << DRM_NODE_RENDER))) {
321          drmFreeDevice(&devices[i]);
322          continue;
323       }
324 
325       ret = _eglAddDRMDevice(devices[i]);
326 
327       /* Device is not added - error or already present */
328       if (ret != 0)
329          drmFreeDevice(&devices[i]);
330 
331       if (ret >= 0)
332          count++;
333    }
334 #endif
335 
336    return count;
337 }
338 
339 EGLBoolean
_eglQueryDevicesEXT(EGLint max_devices,_EGLDevice ** devices,EGLint * num_devices)340 _eglQueryDevicesEXT(EGLint max_devices, _EGLDevice **devices,
341                     EGLint *num_devices)
342 {
343    _EGLDevice *dev, *devs, *swrast;
344    int i = 0, num_devs;
345 
346    if ((devices && max_devices <= 0) || !num_devices)
347       return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT");
348 
349    simple_mtx_lock(_eglGlobal.Mutex);
350 
351    num_devs = _eglDeviceRefreshList();
352    devs = _eglGlobal.DeviceList;
353 
354 #ifdef HAVE_SWRAST
355    swrast = devs;
356 #else
357    swrast = NULL;
358    num_devs--;
359 #endif
360 
361    /* The first device is swrast. Start with the non-swrast device. */
362    devs = devs->Next;
363 
364    /* bail early if we only care about the count */
365    if (!devices) {
366       *num_devices = num_devs;
367       goto out;
368    }
369 
370    *num_devices = MIN2(num_devs, max_devices);
371 
372    /* Add non-swrast devices first and add swrast last.
373     *
374     * By default, the user is likely to pick the first device so having the
375     * software (aka least performant) one is not a good idea.
376     */
377    for (i = 0, dev = devs; dev && i < max_devices; i++) {
378       devices[i] = dev;
379       dev = dev->Next;
380    }
381 
382    /* User requested the full device list, add the software device. */
383    if (max_devices >= num_devs && swrast) {
384       assert(_eglDeviceSupports(swrast, _EGL_DEVICE_SOFTWARE));
385       devices[num_devs - 1] = swrast;
386    }
387 
388 out:
389    simple_mtx_unlock(_eglGlobal.Mutex);
390 
391    return EGL_TRUE;
392 }
393