xref: /aosp_15_r20/external/mesa3d/src/gallium/auxiliary/pipe-loader/pipe_loader_drm.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /**************************************************************************
2  *
3  * Copyright 2011 Intel Corporation
4  * Copyright 2012 Francisco Jerez
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sub license, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the
16  * next paragraph) shall be included in all copies or substantial portions
17  * of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
23  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * Authors:
28  *    Kristian Høgsberg <[email protected]>
29  *    Benjamin Franzke <[email protected]>
30  *
31  **************************************************************************/
32 
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <xf86drm.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 
40 #include "loader.h"
41 #include "target-helpers/drm_helper_public.h"
42 #include "frontend/drm_driver.h"
43 #include "pipe_loader_priv.h"
44 
45 #include "util/log.h"
46 #include "util/os_file.h"
47 #include "util/u_memory.h"
48 #include "util/u_dl.h"
49 #include "util/u_debug.h"
50 #include "util/xmlconfig.h"
51 
52 #include "virtio/virtio-gpu/drm_hw.h"
53 #include "virtio/virtio-gpu/virglrenderer_hw.h"
54 #include "drm-uapi/virtgpu_drm.h"
55 
56 #define DRM_RENDER_NODE_DEV_NAME_FORMAT "%s/renderD%d"
57 #define DRM_RENDER_NODE_MAX_NODES 63
58 #define DRM_RENDER_NODE_MIN_MINOR 128
59 #define DRM_RENDER_NODE_MAX_MINOR (DRM_RENDER_NODE_MIN_MINOR + DRM_RENDER_NODE_MAX_NODES)
60 
61 struct pipe_loader_drm_device {
62    struct pipe_loader_device base;
63    const struct drm_driver_descriptor *dd;
64 #ifndef GALLIUM_STATIC_TARGETS
65    struct util_dl_library *lib;
66 #endif
67    int fd;
68 };
69 
70 #define pipe_loader_drm_device(dev) ((struct pipe_loader_drm_device *)dev)
71 
72 static const struct pipe_loader_ops pipe_loader_drm_ops;
73 
74 #ifdef GALLIUM_STATIC_TARGETS
75 static const struct drm_driver_descriptor *driver_descriptors[] = {
76    &i915_driver_descriptor,
77    &iris_driver_descriptor,
78    &crocus_driver_descriptor,
79    &nouveau_driver_descriptor,
80    &r300_driver_descriptor,
81    &r600_driver_descriptor,
82    &radeonsi_driver_descriptor,
83    &vmwgfx_driver_descriptor,
84    &kgsl_driver_descriptor,
85    &msm_driver_descriptor,
86    &virtio_gpu_driver_descriptor,
87    &v3d_driver_descriptor,
88    &vc4_driver_descriptor,
89    &panfrost_driver_descriptor,
90    &panthor_driver_descriptor,
91    &etnaviv_driver_descriptor,
92    &tegra_driver_descriptor,
93    &lima_driver_descriptor,
94    &zink_driver_descriptor,
95 };
96 #endif
97 
98 static const struct drm_driver_descriptor *
get_driver_descriptor(const char * driver_name,struct util_dl_library ** plib)99 get_driver_descriptor(const char *driver_name, struct util_dl_library **plib)
100 {
101 #ifdef GALLIUM_STATIC_TARGETS
102    for (int i = 0; i < ARRAY_SIZE(driver_descriptors); i++) {
103       if (strcmp(driver_descriptors[i]->driver_name, driver_name) == 0)
104          return driver_descriptors[i];
105    }
106    return &kmsro_driver_descriptor;
107 #else
108    const char *search_dir = os_get_option("GALLIUM_PIPE_SEARCH_DIR");
109    if (search_dir == NULL)
110       search_dir = PIPE_SEARCH_DIR;
111 
112    *plib = pipe_loader_find_module(driver_name, search_dir);
113    if (!*plib)
114       return NULL;
115 
116    const struct drm_driver_descriptor *dd =
117          (const struct drm_driver_descriptor *)
118          util_dl_get_proc_address(*plib, "driver_descriptor");
119 
120    /* sanity check on the driver name */
121    if (dd && strcmp(dd->driver_name, driver_name) == 0)
122       return dd;
123 #endif
124 
125    return NULL;
126 }
127 
128 static int
get_nctx_caps(int fd,struct virgl_renderer_capset_drm * caps)129 get_nctx_caps(int fd, struct virgl_renderer_capset_drm *caps)
130 {
131    struct drm_virtgpu_get_caps args = {
132          .cap_set_id = VIRGL_RENDERER_CAPSET_DRM,
133          .cap_set_ver = 0,
134          .addr = (uintptr_t)caps,
135          .size = sizeof(*caps),
136    };
137 
138    return drmIoctl(fd, DRM_IOCTL_VIRTGPU_GET_CAPS, &args);
139 }
140 
141 static bool
pipe_loader_drm_probe_fd_nodup(struct pipe_loader_device ** dev,int fd,bool zink)142 pipe_loader_drm_probe_fd_nodup(struct pipe_loader_device **dev, int fd, bool zink)
143 {
144    struct pipe_loader_drm_device *ddev = CALLOC_STRUCT(pipe_loader_drm_device);
145    int vendor_id, chip_id;
146 
147    if (!ddev)
148       return false;
149 
150    if (loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) {
151       ddev->base.type = PIPE_LOADER_DEVICE_PCI;
152       ddev->base.u.pci.vendor_id = vendor_id;
153       ddev->base.u.pci.chip_id = chip_id;
154    } else {
155       ddev->base.type = PIPE_LOADER_DEVICE_PLATFORM;
156    }
157    ddev->base.ops = &pipe_loader_drm_ops;
158    ddev->fd = fd;
159 
160    if (zink)
161       ddev->base.driver_name = strdup("zink");
162    else
163       ddev->base.driver_name = loader_get_driver_for_fd(fd);
164    if (!ddev->base.driver_name)
165       goto fail;
166 
167    /* For the closed source AMD OpenGL driver, we want libgbm to load
168     * "amdgpu_dri.so", but we want Gallium multimedia drivers to load
169     * "radeonsi". So change amdgpu to radeonsi for Gallium.
170     */
171    if (strcmp(ddev->base.driver_name, "amdgpu") == 0) {
172       FREE(ddev->base.driver_name);
173       ddev->base.driver_name = strdup("radeonsi");
174    }
175 
176    if (strcmp(ddev->base.driver_name, "virtio_gpu") == 0) {
177       struct virgl_renderer_capset_drm caps;
178       if (get_nctx_caps(fd, &caps) == 0) {
179 #ifdef GALLIUM_STATIC_TARGETS
180          for (int i = 0; i < ARRAY_SIZE(driver_descriptors); i++) {
181             if (!driver_descriptors[i]->probe_nctx)
182                continue;
183             if (!driver_descriptors[i]->probe_nctx(fd, &caps))
184                continue;
185 
186             FREE(ddev->base.driver_name);
187             ddev->base.driver_name = strdup(driver_descriptors[i]->driver_name);
188             break;
189          }
190 #else
191 	 mesa_logw("Dynamic pipe loader does not support virtgpu native context");
192 #endif
193       }
194    }
195 
196    struct util_dl_library **plib = NULL;
197 #ifndef GALLIUM_STATIC_TARGETS
198    plib = &ddev->lib;
199 #endif
200    ddev->dd = get_driver_descriptor(ddev->base.driver_name, plib);
201 
202    /* vgem is a virtual device; don't try using it with kmsro */
203    if (strcmp(ddev->base.driver_name, "vgem") == 0)
204       goto fail;
205 
206    /* kmsro supports lots of drivers, try as a fallback */
207    if (!ddev->dd && !zink)
208       ddev->dd = get_driver_descriptor("kmsro", plib);
209 
210    if (!ddev->dd)
211       goto fail;
212 
213    *dev = &ddev->base;
214    return true;
215 
216   fail:
217 #ifndef GALLIUM_STATIC_TARGETS
218    if (ddev->lib)
219       util_dl_close(ddev->lib);
220 #endif
221    FREE(ddev->base.driver_name);
222    FREE(ddev);
223    return false;
224 }
225 
226 bool
pipe_loader_drm_probe_fd(struct pipe_loader_device ** dev,int fd,bool zink)227 pipe_loader_drm_probe_fd(struct pipe_loader_device **dev, int fd, bool zink)
228 {
229    bool ret;
230    int new_fd;
231 
232    if (fd < 0 || (new_fd = os_dupfd_cloexec(fd)) < 0)
233      return false;
234 
235    ret = pipe_loader_drm_probe_fd_nodup(dev, new_fd, zink);
236    if (!ret)
237       close(new_fd);
238 
239    return ret;
240 }
241 
242 static int
open_drm_render_node_minor(int minor)243 open_drm_render_node_minor(int minor)
244 {
245    char path[PATH_MAX];
246    snprintf(path, sizeof(path), DRM_RENDER_NODE_DEV_NAME_FORMAT, DRM_DIR_NAME,
247             minor);
248    return loader_open_device(path);
249 }
250 
251 static int
pipe_loader_drm_probe_internal(struct pipe_loader_device ** devs,int ndev,bool zink)252 pipe_loader_drm_probe_internal(struct pipe_loader_device **devs, int ndev, bool zink)
253 {
254    int i, j, fd;
255 
256    for (i = DRM_RENDER_NODE_MIN_MINOR, j = 0;
257         i <= DRM_RENDER_NODE_MAX_MINOR; i++) {
258       struct pipe_loader_device *dev;
259 
260       fd = open_drm_render_node_minor(i);
261       if (fd < 0)
262          continue;
263 
264       if (!pipe_loader_drm_probe_fd_nodup(&dev, fd, zink)) {
265          close(fd);
266          continue;
267       }
268 
269       if (j < ndev) {
270          devs[j] = dev;
271       } else {
272          close(fd);
273          dev->ops->release(&dev);
274       }
275       j++;
276    }
277 
278    return j;
279 }
280 
281 int
pipe_loader_drm_probe(struct pipe_loader_device ** devs,int ndev)282 pipe_loader_drm_probe(struct pipe_loader_device **devs, int ndev)
283 {
284    return pipe_loader_drm_probe_internal(devs, ndev, false);
285 }
286 
287 #ifdef HAVE_ZINK
288 int
pipe_loader_drm_zink_probe(struct pipe_loader_device ** devs,int ndev)289 pipe_loader_drm_zink_probe(struct pipe_loader_device **devs, int ndev)
290 {
291    return pipe_loader_drm_probe_internal(devs, ndev, true);
292 }
293 #endif
294 
295 static void
pipe_loader_drm_release(struct pipe_loader_device ** dev)296 pipe_loader_drm_release(struct pipe_loader_device **dev)
297 {
298    struct pipe_loader_drm_device *ddev = pipe_loader_drm_device(*dev);
299 
300 #ifndef GALLIUM_STATIC_TARGETS
301    if (ddev->lib)
302       util_dl_close(ddev->lib);
303 #endif
304 
305    close(ddev->fd);
306    FREE(ddev->base.driver_name);
307    pipe_loader_base_release(dev);
308 }
309 
310 int
pipe_loader_get_compatible_render_capable_device_fd(int kms_only_fd)311 pipe_loader_get_compatible_render_capable_device_fd(int kms_only_fd)
312 {
313    bool is_platform_device;
314    struct pipe_loader_device *dev;
315    const char * const drivers[] = {
316 #if defined GALLIUM_ETNAVIV
317       "etnaviv",
318 #endif
319 #if defined GALLIUM_FREEDRENO
320       "msm",
321 #endif
322 #if defined GALLIUM_LIMA
323       "lima",
324 #endif
325 #if defined GALLIUM_PANFROST
326       "panfrost",
327       "panthor",
328 #endif
329 #if defined GALLIUM_V3D
330       "v3d",
331 #endif
332 #if defined GALLIUM_VC4
333       "vc4",
334 #endif
335    };
336 
337    if (!pipe_loader_drm_probe_fd(&dev, kms_only_fd, false))
338       return -1;
339    is_platform_device = (dev->type == PIPE_LOADER_DEVICE_PLATFORM);
340    pipe_loader_release(&dev, 1);
341 
342    /* For display-only devices that are not on the platform bus, we can't assume
343     * that any of the rendering devices are compatible. */
344    if (!is_platform_device)
345       return -1;
346 
347    /* For platform display-only devices, we try to find a render-capable device
348     * on the platform bus and that should be compatible with the display-only
349     * device. */
350    if (ARRAY_SIZE(drivers) == 0)
351       return -1;
352 
353    return loader_open_render_node_platform_device(drivers, ARRAY_SIZE(drivers));
354 }
355 
356 static const struct driOptionDescription *
pipe_loader_drm_get_driconf(struct pipe_loader_device * dev,unsigned * count)357 pipe_loader_drm_get_driconf(struct pipe_loader_device *dev, unsigned *count)
358 {
359    struct pipe_loader_drm_device *ddev = pipe_loader_drm_device(dev);
360 
361    *count = ddev->dd->driconf_count;
362    return ddev->dd->driconf;
363 }
364 
365 static struct pipe_screen *
pipe_loader_drm_create_screen(struct pipe_loader_device * dev,const struct pipe_screen_config * config,bool sw_vk)366 pipe_loader_drm_create_screen(struct pipe_loader_device *dev,
367                               const struct pipe_screen_config *config, bool sw_vk)
368 {
369    struct pipe_loader_drm_device *ddev = pipe_loader_drm_device(dev);
370 
371    return ddev->dd->create_screen(ddev->fd, config);
372 }
373 
374 const struct driOptionDescription *
pipe_loader_drm_get_driconf_by_name(const char * driver_name,unsigned * count)375 pipe_loader_drm_get_driconf_by_name(const char *driver_name, unsigned *count)
376 {
377    driOptionDescription *driconf = NULL;
378    struct util_dl_library *lib = NULL;
379    const struct drm_driver_descriptor *dd =
380       get_driver_descriptor(driver_name, &lib);
381 
382    if (!dd) {
383       *count = 0;
384    } else {
385       *count = dd->driconf_count;
386       size_t size = sizeof(*driconf) * *count;
387       size_t base_size = size;
388       /* factor in all the statically allocated string lengths */
389       for (unsigned i = 0; i < dd->driconf_count; i++) {
390          if (dd->driconf[i].desc)
391             size += strlen(dd->driconf[i].desc) + 1;
392          if (dd->driconf[i].info.name)
393             size += strlen(dd->driconf[i].info.name) + 1;
394          if (dd->driconf[i].info.type == DRI_STRING)
395             size += strlen(dd->driconf[i].value._string) + 1;
396       }
397       driconf = malloc(size);
398       memcpy(driconf, dd->driconf, size);
399 
400       uint8_t *ptr = (void*)driconf;
401       ptr += base_size;
402       /* manually set up pointers and copy in all the statically allocated strings */
403       for (unsigned i = 0; i < dd->driconf_count; i++) {
404          if (dd->driconf[i].desc) {
405             driconf[i].desc = (void*)ptr;
406             size_t str_size = strlen(dd->driconf[i].desc) + 1;
407             memcpy((void*)driconf[i].desc, dd->driconf[i].desc, str_size);
408             ptr += str_size;
409          }
410          if (dd->driconf[i].info.name) {
411             driconf[i].info.name = (void*)ptr;
412             size_t str_size = strlen(dd->driconf[i].info.name) + 1;
413             memcpy((void*)driconf[i].info.name, dd->driconf[i].info.name, str_size);
414             ptr += str_size;
415          }
416          if (dd->driconf[i].info.type == DRI_STRING) {
417             driconf[i].value._string = (void*)ptr;
418             size_t str_size = strlen(dd->driconf[i].value._string) + 1;
419             memcpy((void*)driconf[i].value._string, dd->driconf[i].value._string, str_size);
420             ptr += str_size;
421          }
422       }
423    }
424    if (lib)
425       util_dl_close(lib);
426 
427    return driconf;
428 }
429 
430 static const struct pipe_loader_ops pipe_loader_drm_ops = {
431    .create_screen = pipe_loader_drm_create_screen,
432    .get_driconf = pipe_loader_drm_get_driconf,
433    .release = pipe_loader_drm_release
434 };
435