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 <krh@bitplanet.net>
29 *    Benjamin Franzke <benjaminfranzke@googlemail.com>
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/os_file.h"
46#include "util/u_memory.h"
47#include "util/u_dl.h"
48#include "util/u_debug.h"
49#include "util/xmlconfig.h"
50
51#define DRM_RENDER_NODE_DEV_NAME_FORMAT "%s/renderD%d"
52#define DRM_RENDER_NODE_MAX_NODES 63
53#define DRM_RENDER_NODE_MIN_MINOR 128
54#define DRM_RENDER_NODE_MAX_MINOR (DRM_RENDER_NODE_MIN_MINOR + DRM_RENDER_NODE_MAX_NODES)
55
56struct pipe_loader_drm_device {
57   struct pipe_loader_device base;
58   const struct drm_driver_descriptor *dd;
59#ifndef GALLIUM_STATIC_TARGETS
60   struct util_dl_library *lib;
61#endif
62   int fd;
63};
64
65#define pipe_loader_drm_device(dev) ((struct pipe_loader_drm_device *)dev)
66
67static const struct pipe_loader_ops pipe_loader_drm_ops;
68
69#ifdef GALLIUM_STATIC_TARGETS
70static const struct drm_driver_descriptor *driver_descriptors[] = {
71   &i915_driver_descriptor,
72   &iris_driver_descriptor,
73   &crocus_driver_descriptor,
74   &nouveau_driver_descriptor,
75   &r300_driver_descriptor,
76   &r600_driver_descriptor,
77   &radeonsi_driver_descriptor,
78   &vmwgfx_driver_descriptor,
79   &kgsl_driver_descriptor,
80   &msm_driver_descriptor,
81   &virtio_gpu_driver_descriptor,
82   &v3d_driver_descriptor,
83   &vc4_driver_descriptor,
84   &panfrost_driver_descriptor,
85   &etnaviv_driver_descriptor,
86   &tegra_driver_descriptor,
87   &lima_driver_descriptor,
88   &zink_driver_descriptor,
89};
90#endif
91
92static const struct drm_driver_descriptor *
93get_driver_descriptor(const char *driver_name, struct util_dl_library **plib)
94{
95#ifdef GALLIUM_STATIC_TARGETS
96   for (int i = 0; i < ARRAY_SIZE(driver_descriptors); i++) {
97      if (strcmp(driver_descriptors[i]->driver_name, driver_name) == 0)
98         return driver_descriptors[i];
99   }
100   return &kmsro_driver_descriptor;
101#else
102   const char *search_dir = getenv("GALLIUM_PIPE_SEARCH_DIR");
103   if (search_dir == NULL)
104      search_dir = PIPE_SEARCH_DIR;
105
106   *plib = pipe_loader_find_module(driver_name, search_dir);
107   if (!*plib)
108      return NULL;
109
110   const struct drm_driver_descriptor *dd =
111         (const struct drm_driver_descriptor *)
112         util_dl_get_proc_address(*plib, "driver_descriptor");
113
114   /* sanity check on the driver name */
115   if (dd && strcmp(dd->driver_name, driver_name) == 0)
116      return dd;
117#endif
118
119   return NULL;
120}
121
122static bool
123pipe_loader_drm_probe_fd_nodup(struct pipe_loader_device **dev, int fd)
124{
125   struct pipe_loader_drm_device *ddev = CALLOC_STRUCT(pipe_loader_drm_device);
126   int vendor_id, chip_id;
127
128   if (!ddev)
129      return false;
130
131   if (loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) {
132      ddev->base.type = PIPE_LOADER_DEVICE_PCI;
133      ddev->base.u.pci.vendor_id = vendor_id;
134      ddev->base.u.pci.chip_id = chip_id;
135   } else {
136      ddev->base.type = PIPE_LOADER_DEVICE_PLATFORM;
137   }
138   ddev->base.ops = &pipe_loader_drm_ops;
139   ddev->fd = fd;
140
141   ddev->base.driver_name = loader_get_driver_for_fd(fd);
142   if (!ddev->base.driver_name)
143      goto fail;
144
145   /* For the closed source AMD OpenGL driver, we want libgbm to load
146    * "amdgpu_dri.so", but we want Gallium multimedia drivers to load
147    * "radeonsi". So change amdgpu to radeonsi for Gallium.
148    */
149   if (strcmp(ddev->base.driver_name, "amdgpu") == 0) {
150      FREE(ddev->base.driver_name);
151      ddev->base.driver_name = strdup("radeonsi");
152   }
153
154   struct util_dl_library **plib = NULL;
155#ifndef GALLIUM_STATIC_TARGETS
156   plib = &ddev->lib;
157#endif
158   ddev->dd = get_driver_descriptor(ddev->base.driver_name, plib);
159
160   /* vgem is a virtual device; don't try using it with kmsro */
161   if (strcmp(ddev->base.driver_name, "vgem") == 0)
162      goto fail;
163
164   /* kmsro supports lots of drivers, try as a fallback */
165   if (!ddev->dd)
166      ddev->dd = get_driver_descriptor("kmsro", plib);
167
168   if (!ddev->dd)
169      goto fail;
170
171   *dev = &ddev->base;
172   return true;
173
174  fail:
175#ifndef GALLIUM_STATIC_TARGETS
176   if (ddev->lib)
177      util_dl_close(ddev->lib);
178#endif
179   FREE(ddev->base.driver_name);
180   FREE(ddev);
181   return false;
182}
183
184bool
185pipe_loader_drm_probe_fd(struct pipe_loader_device **dev, int fd)
186{
187   bool ret;
188   int new_fd;
189
190   if (fd < 0 || (new_fd = os_dupfd_cloexec(fd)) < 0)
191     return false;
192
193   ret = pipe_loader_drm_probe_fd_nodup(dev, new_fd);
194   if (!ret)
195      close(new_fd);
196
197   return ret;
198}
199
200static int
201open_drm_render_node_minor(int minor)
202{
203   char path[PATH_MAX];
204   snprintf(path, sizeof(path), DRM_RENDER_NODE_DEV_NAME_FORMAT, DRM_DIR_NAME,
205            minor);
206   return loader_open_device(path);
207}
208
209int
210pipe_loader_drm_probe(struct pipe_loader_device **devs, int ndev)
211{
212   int i, j, fd;
213
214   for (i = DRM_RENDER_NODE_MIN_MINOR, j = 0;
215        i <= DRM_RENDER_NODE_MAX_MINOR; i++) {
216      struct pipe_loader_device *dev;
217
218      fd = open_drm_render_node_minor(i);
219      if (fd < 0)
220         continue;
221
222      if (!pipe_loader_drm_probe_fd_nodup(&dev, fd)) {
223         close(fd);
224         continue;
225      }
226
227      if (j < ndev) {
228         devs[j] = dev;
229      } else {
230         close(fd);
231         dev->ops->release(&dev);
232      }
233      j++;
234   }
235
236   return j;
237}
238
239static void
240pipe_loader_drm_release(struct pipe_loader_device **dev)
241{
242   struct pipe_loader_drm_device *ddev = pipe_loader_drm_device(*dev);
243
244#ifndef GALLIUM_STATIC_TARGETS
245   if (ddev->lib)
246      util_dl_close(ddev->lib);
247#endif
248
249   close(ddev->fd);
250   FREE(ddev->base.driver_name);
251   pipe_loader_base_release(dev);
252}
253
254static const struct driOptionDescription *
255pipe_loader_drm_get_driconf(struct pipe_loader_device *dev, unsigned *count)
256{
257   struct pipe_loader_drm_device *ddev = pipe_loader_drm_device(dev);
258
259   *count = ddev->dd->driconf_count;
260   return ddev->dd->driconf;
261}
262
263static struct pipe_screen *
264pipe_loader_drm_create_screen(struct pipe_loader_device *dev,
265                              const struct pipe_screen_config *config, bool sw_vk)
266{
267   struct pipe_loader_drm_device *ddev = pipe_loader_drm_device(dev);
268
269   return ddev->dd->create_screen(ddev->fd, config);
270}
271
272const struct driOptionDescription *
273pipe_loader_drm_get_driconf_by_name(const char *driver_name, unsigned *count)
274{
275   driOptionDescription *driconf = NULL;
276   struct util_dl_library *lib = NULL;
277   const struct drm_driver_descriptor *dd =
278      get_driver_descriptor(driver_name, &lib);
279
280   if (!dd) {
281      *count = 0;
282   } else {
283      *count = dd->driconf_count;
284      size_t size = sizeof(*driconf) * *count;
285      driconf = malloc(size);
286      memcpy(driconf, dd->driconf, size);
287   }
288   if (lib)
289      util_dl_close(lib);
290
291   return driconf;
292}
293
294static const struct pipe_loader_ops pipe_loader_drm_ops = {
295   .create_screen = pipe_loader_drm_create_screen,
296   .get_driconf = pipe_loader_drm_get_driconf,
297   .release = pipe_loader_drm_release
298};
299