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