loader_dri3/glx/egl: Reinstate the loader_dri3_vtable get_dri_screen callback
[mesa.git] / src / loader / loader.c
1 /*
2 * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
3 * Copyright (C) 2014-2016 Emil Velikov <emil.l.velikov@gmail.com>
4 * Copyright (C) 2016 Intel Corporation
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * Authors:
26 * Rob Clark <robclark@freedesktop.org>
27 */
28
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdbool.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #ifdef MAJOR_IN_MKDEV
39 #include <sys/mkdev.h>
40 #endif
41 #ifdef MAJOR_IN_SYSMACROS
42 #include <sys/sysmacros.h>
43 #endif
44 #include "loader.h"
45
46 #ifdef HAVE_LIBDRM
47 #include <xf86drm.h>
48 #ifdef USE_DRICONF
49 #include "util/xmlconfig.h"
50 #include "util/xmlpool.h"
51 #endif
52 #endif
53
54 #define __IS_LOADER
55 #include "pci_id_driver_map.h"
56
57 static void default_logger(int level, const char *fmt, ...)
58 {
59 if (level <= _LOADER_WARNING) {
60 va_list args;
61 va_start(args, fmt);
62 vfprintf(stderr, fmt, args);
63 va_end(args);
64 }
65 }
66
67 static void (*log_)(int level, const char *fmt, ...) = default_logger;
68
69 int
70 loader_open_device(const char *device_name)
71 {
72 int fd;
73 #ifdef O_CLOEXEC
74 fd = open(device_name, O_RDWR | O_CLOEXEC);
75 if (fd == -1 && errno == EINVAL)
76 #endif
77 {
78 fd = open(device_name, O_RDWR);
79 if (fd != -1)
80 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
81 }
82 return fd;
83 }
84
85 #if defined(HAVE_LIBDRM)
86 #ifdef USE_DRICONF
87 static const char __driConfigOptionsLoader[] =
88 DRI_CONF_BEGIN
89 DRI_CONF_SECTION_INITIALIZATION
90 DRI_CONF_DEVICE_ID_PATH_TAG()
91 DRI_CONF_SECTION_END
92 DRI_CONF_END;
93
94 static char *loader_get_dri_config_device_id(void)
95 {
96 driOptionCache defaultInitOptions;
97 driOptionCache userInitOptions;
98 char *prime = NULL;
99
100 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader);
101 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, "loader");
102 if (driCheckOption(&userInitOptions, "device_id", DRI_STRING))
103 prime = strdup(driQueryOptionstr(&userInitOptions, "device_id"));
104 driDestroyOptionCache(&userInitOptions);
105 driDestroyOptionInfo(&defaultInitOptions);
106
107 return prime;
108 }
109 #endif
110
111 static char *drm_construct_id_path_tag(drmDevicePtr device)
112 {
113 #define PCI_ID_PATH_TAG_LENGTH sizeof("pci-xxxx_xx_xx_x")
114 char *tag = NULL;
115
116 if (device->bustype == DRM_BUS_PCI) {
117 tag = calloc(PCI_ID_PATH_TAG_LENGTH, sizeof(char));
118 if (tag == NULL)
119 return NULL;
120
121 snprintf(tag, PCI_ID_PATH_TAG_LENGTH, "pci-%04x_%02x_%02x_%1u",
122 device->businfo.pci->domain, device->businfo.pci->bus,
123 device->businfo.pci->dev, device->businfo.pci->func);
124 }
125 return tag;
126 }
127
128 static bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag)
129 {
130 char *tag = drm_construct_id_path_tag(device);
131 int ret;
132
133 if (tag == NULL)
134 return false;
135
136 ret = strcmp(tag, prime_tag);
137
138 free(tag);
139 return ret == 0;
140 }
141
142 static char *drm_get_id_path_tag_for_fd(int fd)
143 {
144 drmDevicePtr device;
145 char *tag;
146
147 if (drmGetDevice2(fd, 0, &device) != 0)
148 return NULL;
149
150 tag = drm_construct_id_path_tag(device);
151 drmFreeDevice(&device);
152 return tag;
153 }
154
155 int loader_get_user_preferred_fd(int default_fd, bool *different_device)
156 {
157 /* Arbitrary "maximum" value of drm devices. */
158 #define MAX_DRM_DEVICES 32
159 const char *dri_prime = getenv("DRI_PRIME");
160 char *default_tag, *prime = NULL;
161 drmDevicePtr devices[MAX_DRM_DEVICES];
162 int i, num_devices, fd;
163 bool found = false;
164
165 if (dri_prime)
166 prime = strdup(dri_prime);
167 #ifdef USE_DRICONF
168 else
169 prime = loader_get_dri_config_device_id();
170 #endif
171
172 if (prime == NULL) {
173 *different_device = false;
174 return default_fd;
175 }
176
177 default_tag = drm_get_id_path_tag_for_fd(default_fd);
178 if (default_tag == NULL)
179 goto err;
180
181 num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
182 if (num_devices < 0)
183 goto err;
184
185 /* two format are supported:
186 * "1": choose any other card than the card used by default.
187 * id_path_tag: (for example "pci-0000_02_00_0") choose the card
188 * with this id_path_tag.
189 */
190 if (!strcmp(prime,"1")) {
191 /* Hmm... detection for 2-7 seems to be broken. Oh well ...
192 * Pick the first render device that is not our own.
193 */
194 for (i = 0; i < num_devices; i++) {
195 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
196 !drm_device_matches_tag(devices[i], default_tag)) {
197
198 found = true;
199 break;
200 }
201 }
202 } else {
203 for (i = 0; i < num_devices; i++) {
204 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
205 drm_device_matches_tag(devices[i], prime)) {
206
207 found = true;
208 break;
209 }
210 }
211 }
212
213 if (!found) {
214 drmFreeDevices(devices, num_devices);
215 goto err;
216 }
217
218 fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]);
219 drmFreeDevices(devices, num_devices);
220 if (fd < 0)
221 goto err;
222
223 close(default_fd);
224
225 *different_device = !!strcmp(default_tag, prime);
226
227 free(default_tag);
228 free(prime);
229 return fd;
230
231 err:
232 *different_device = false;
233
234 free(default_tag);
235 free(prime);
236 return default_fd;
237 }
238 #else
239 int loader_get_user_preferred_fd(int default_fd, bool *different_device)
240 {
241 *different_device = false;
242 return default_fd;
243 }
244 #endif
245
246 #if defined(HAVE_LIBDRM)
247
248 static int
249 drm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
250 {
251 drmDevicePtr device;
252 int ret;
253
254 if (drmGetDevice2(fd, 0, &device) == 0) {
255 if (device->bustype == DRM_BUS_PCI) {
256 *vendor_id = device->deviceinfo.pci->vendor_id;
257 *chip_id = device->deviceinfo.pci->device_id;
258 ret = 1;
259 }
260 else {
261 log_(_LOADER_DEBUG, "MESA-LOADER: device is not located on the PCI bus\n");
262 ret = 0;
263 }
264 drmFreeDevice(&device);
265 }
266 else {
267 log_(_LOADER_WARNING, "MESA-LOADER: failed to retrieve device information\n");
268 ret = 0;
269 }
270
271 return ret;
272 }
273 #endif
274
275
276 int
277 loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
278 {
279 #if HAVE_LIBDRM
280 if (drm_get_pci_id_for_fd(fd, vendor_id, chip_id))
281 return 1;
282 #endif
283 return 0;
284 }
285
286 char *
287 loader_get_device_name_for_fd(int fd)
288 {
289 char *result = NULL;
290
291 #if HAVE_LIBDRM
292 result = drmGetDeviceNameFromFd2(fd);
293 #endif
294
295 return result;
296 }
297
298 char *
299 loader_get_driver_for_fd(int fd)
300 {
301 int vendor_id, chip_id, i, j;
302 char *driver = NULL;
303
304 /* Allow an environment variable to force choosing a different driver
305 * binary. If that driver binary can't survive on this FD, that's the
306 * user's problem, but this allows vc4 simulator to run on an i965 host,
307 * and may be useful for some touch testing of i915 on an i965 host.
308 */
309 if (geteuid() == getuid()) {
310 driver = getenv("MESA_LOADER_DRIVER_OVERRIDE");
311 if (driver)
312 return strdup(driver);
313 }
314
315 if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) {
316
317 #if HAVE_LIBDRM
318 /* fallback to drmGetVersion(): */
319 drmVersionPtr version = drmGetVersion(fd);
320
321 if (!version) {
322 log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd);
323 return NULL;
324 }
325
326 driver = strndup(version->name, version->name_len);
327 log_(_LOADER_INFO, "using driver %s for %d\n", driver, fd);
328
329 drmFreeVersion(version);
330 #endif
331
332 return driver;
333 }
334
335 for (i = 0; driver_map[i].driver; i++) {
336 if (vendor_id != driver_map[i].vendor_id)
337 continue;
338
339 if (driver_map[i].predicate && !driver_map[i].predicate(fd))
340 continue;
341
342 if (driver_map[i].num_chips_ids == -1) {
343 driver = strdup(driver_map[i].driver);
344 goto out;
345 }
346
347 for (j = 0; j < driver_map[i].num_chips_ids; j++)
348 if (driver_map[i].chip_ids[j] == chip_id) {
349 driver = strdup(driver_map[i].driver);
350 goto out;
351 }
352 }
353
354 out:
355 log_(driver ? _LOADER_DEBUG : _LOADER_WARNING,
356 "pci id for fd %d: %04x:%04x, driver %s\n",
357 fd, vendor_id, chip_id, driver);
358 return driver;
359 }
360
361 void
362 loader_set_logger(void (*logger)(int level, const char *fmt, ...))
363 {
364 log_ = logger;
365 }
366
367 /* XXX: Local definition to avoid pulling the heavyweight GL/gl.h and
368 * GL/internal/dri_interface.h
369 */
370
371 #ifndef __DRI_DRIVER_GET_EXTENSIONS
372 #define __DRI_DRIVER_GET_EXTENSIONS "__driDriverGetExtensions"
373 #endif
374
375 char *
376 loader_get_extensions_name(const char *driver_name)
377 {
378 char *name = NULL;
379
380 if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0)
381 return NULL;
382
383 const size_t len = strlen(name);
384 for (size_t i = 0; i < len; i++) {
385 if (name[i] == '-')
386 name[i] = '_';
387 }
388
389 return name;
390 }