loader: Drop unused argument from dri3_update_drawable().
[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 static char *loader_get_kernel_driver_name(int fd)
86 {
87 #if HAVE_LIBDRM
88 char *driver;
89 drmVersionPtr version = drmGetVersion(fd);
90
91 if (!version) {
92 log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd);
93 return NULL;
94 }
95
96 driver = strndup(version->name, version->name_len);
97
98 drmFreeVersion(version);
99 return driver;
100 #else
101 return NULL;
102 #endif
103 }
104
105 #if defined(HAVE_LIBDRM)
106 int
107 loader_open_render_node(const char *name)
108 {
109 drmDevicePtr *devices, device;
110 int err, render = -ENOENT, fd;
111 unsigned int num, i;
112
113 err = drmGetDevices2(0, NULL, 0);
114 if (err < 0)
115 return err;
116
117 num = err;
118
119 devices = calloc(num, sizeof(*devices));
120 if (!devices)
121 return -ENOMEM;
122
123 err = drmGetDevices2(0, devices, num);
124 if (err < 0) {
125 render = err;
126 goto free;
127 }
128
129 for (i = 0; i < num; i++) {
130 device = devices[i];
131
132 if ((device->available_nodes & (1 << DRM_NODE_RENDER)) &&
133 (device->bustype == DRM_BUS_PLATFORM)) {
134 drmVersionPtr version;
135
136 fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR | O_CLOEXEC);
137 if (fd < 0)
138 continue;
139
140 version = drmGetVersion(fd);
141 if (!version) {
142 close(fd);
143 continue;
144 }
145
146 if (strcmp(version->name, name) != 0) {
147 drmFreeVersion(version);
148 close(fd);
149 continue;
150 }
151
152 drmFreeVersion(version);
153 render = fd;
154 break;
155 }
156 }
157
158 drmFreeDevices(devices, num);
159
160 free:
161 free(devices);
162 return render;
163 }
164
165 #ifdef USE_DRICONF
166 static const char __driConfigOptionsLoader[] =
167 DRI_CONF_BEGIN
168 DRI_CONF_SECTION_INITIALIZATION
169 DRI_CONF_DEVICE_ID_PATH_TAG()
170 DRI_CONF_DRI_DRIVER()
171 DRI_CONF_SECTION_END
172 DRI_CONF_END;
173
174 static char *loader_get_dri_config_driver(int fd)
175 {
176 driOptionCache defaultInitOptions;
177 driOptionCache userInitOptions;
178 char *dri_driver = NULL;
179 char *kernel_driver = loader_get_kernel_driver_name(fd);
180
181 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader);
182 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0,
183 "loader", kernel_driver);
184 if (driCheckOption(&userInitOptions, "dri_driver", DRI_STRING)) {
185 char *opt = driQueryOptionstr(&userInitOptions, "dri_driver");
186 /* not an empty string */
187 if (*opt)
188 dri_driver = strdup(opt);
189 }
190 driDestroyOptionCache(&userInitOptions);
191 driDestroyOptionInfo(&defaultInitOptions);
192
193 free(kernel_driver);
194 return dri_driver;
195 }
196
197 static char *loader_get_dri_config_device_id(void)
198 {
199 driOptionCache defaultInitOptions;
200 driOptionCache userInitOptions;
201 char *prime = NULL;
202
203 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader);
204 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, "loader", NULL);
205 if (driCheckOption(&userInitOptions, "device_id", DRI_STRING))
206 prime = strdup(driQueryOptionstr(&userInitOptions, "device_id"));
207 driDestroyOptionCache(&userInitOptions);
208 driDestroyOptionInfo(&defaultInitOptions);
209
210 return prime;
211 }
212 #endif
213
214 static char *drm_construct_id_path_tag(drmDevicePtr device)
215 {
216 char *tag = NULL;
217
218 if (device->bustype == DRM_BUS_PCI) {
219 if (asprintf(&tag, "pci-%04x_%02x_%02x_%1u",
220 device->businfo.pci->domain,
221 device->businfo.pci->bus,
222 device->businfo.pci->dev,
223 device->businfo.pci->func) < 0) {
224 return NULL;
225 }
226 } else if (device->bustype == DRM_BUS_PLATFORM ||
227 device->bustype == DRM_BUS_HOST1X) {
228 char *fullname, *name, *address;
229
230 if (device->bustype == DRM_BUS_PLATFORM)
231 fullname = device->businfo.platform->fullname;
232 else
233 fullname = device->businfo.host1x->fullname;
234
235 name = strrchr(fullname, '/');
236 if (!name)
237 name = strdup(fullname);
238 else
239 name = strdup(name + 1);
240
241 address = strchr(name, '@');
242 if (address) {
243 *address++ = '\0';
244
245 if (asprintf(&tag, "platform-%s_%s", address, name) < 0)
246 tag = NULL;
247 } else {
248 if (asprintf(&tag, "platform-%s", name) < 0)
249 tag = NULL;
250 }
251
252 free(name);
253 }
254 return tag;
255 }
256
257 static bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag)
258 {
259 char *tag = drm_construct_id_path_tag(device);
260 int ret;
261
262 if (tag == NULL)
263 return false;
264
265 ret = strcmp(tag, prime_tag);
266
267 free(tag);
268 return ret == 0;
269 }
270
271 static char *drm_get_id_path_tag_for_fd(int fd)
272 {
273 drmDevicePtr device;
274 char *tag;
275
276 if (drmGetDevice2(fd, 0, &device) != 0)
277 return NULL;
278
279 tag = drm_construct_id_path_tag(device);
280 drmFreeDevice(&device);
281 return tag;
282 }
283
284 int loader_get_user_preferred_fd(int default_fd, bool *different_device)
285 {
286 /* Arbitrary "maximum" value of drm devices. */
287 #define MAX_DRM_DEVICES 32
288 const char *dri_prime = getenv("DRI_PRIME");
289 char *default_tag, *prime = NULL;
290 drmDevicePtr devices[MAX_DRM_DEVICES];
291 int i, num_devices, fd;
292 bool found = false;
293
294 if (dri_prime)
295 prime = strdup(dri_prime);
296 #ifdef USE_DRICONF
297 else
298 prime = loader_get_dri_config_device_id();
299 #endif
300
301 if (prime == NULL) {
302 *different_device = false;
303 return default_fd;
304 }
305
306 default_tag = drm_get_id_path_tag_for_fd(default_fd);
307 if (default_tag == NULL)
308 goto err;
309
310 num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
311 if (num_devices < 0)
312 goto err;
313
314 /* two format are supported:
315 * "1": choose any other card than the card used by default.
316 * id_path_tag: (for example "pci-0000_02_00_0") choose the card
317 * with this id_path_tag.
318 */
319 if (!strcmp(prime,"1")) {
320 /* Hmm... detection for 2-7 seems to be broken. Oh well ...
321 * Pick the first render device that is not our own.
322 */
323 for (i = 0; i < num_devices; i++) {
324 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
325 !drm_device_matches_tag(devices[i], default_tag)) {
326
327 found = true;
328 break;
329 }
330 }
331 } else {
332 for (i = 0; i < num_devices; i++) {
333 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
334 drm_device_matches_tag(devices[i], prime)) {
335
336 found = true;
337 break;
338 }
339 }
340 }
341
342 if (!found) {
343 drmFreeDevices(devices, num_devices);
344 goto err;
345 }
346
347 fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]);
348 drmFreeDevices(devices, num_devices);
349 if (fd < 0)
350 goto err;
351
352 close(default_fd);
353
354 *different_device = !!strcmp(default_tag, prime);
355
356 free(default_tag);
357 free(prime);
358 return fd;
359
360 err:
361 *different_device = false;
362
363 free(default_tag);
364 free(prime);
365 return default_fd;
366 }
367 #else
368 int
369 loader_open_render_node(const char *name)
370 {
371 return -1;
372 }
373
374 int loader_get_user_preferred_fd(int default_fd, bool *different_device)
375 {
376 *different_device = false;
377 return default_fd;
378 }
379 #endif
380
381 #if defined(HAVE_LIBDRM)
382
383 static int
384 drm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
385 {
386 drmDevicePtr device;
387 int ret;
388
389 if (drmGetDevice2(fd, 0, &device) == 0) {
390 if (device->bustype == DRM_BUS_PCI) {
391 *vendor_id = device->deviceinfo.pci->vendor_id;
392 *chip_id = device->deviceinfo.pci->device_id;
393 ret = 1;
394 }
395 else {
396 log_(_LOADER_DEBUG, "MESA-LOADER: device is not located on the PCI bus\n");
397 ret = 0;
398 }
399 drmFreeDevice(&device);
400 }
401 else {
402 log_(_LOADER_WARNING, "MESA-LOADER: failed to retrieve device information\n");
403 ret = 0;
404 }
405
406 return ret;
407 }
408 #endif
409
410
411 int
412 loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
413 {
414 #if HAVE_LIBDRM
415 if (drm_get_pci_id_for_fd(fd, vendor_id, chip_id))
416 return 1;
417 #endif
418 return 0;
419 }
420
421 char *
422 loader_get_device_name_for_fd(int fd)
423 {
424 char *result = NULL;
425
426 #if HAVE_LIBDRM
427 result = drmGetDeviceNameFromFd2(fd);
428 #endif
429
430 return result;
431 }
432
433 char *
434 loader_get_driver_for_fd(int fd)
435 {
436 int vendor_id, chip_id, i, j;
437 char *driver = NULL;
438
439 /* Allow an environment variable to force choosing a different driver
440 * binary. If that driver binary can't survive on this FD, that's the
441 * user's problem, but this allows vc4 simulator to run on an i965 host,
442 * and may be useful for some touch testing of i915 on an i965 host.
443 */
444 if (geteuid() == getuid()) {
445 driver = getenv("MESA_LOADER_DRIVER_OVERRIDE");
446 if (driver)
447 return strdup(driver);
448 }
449
450 #if defined(HAVE_LIBDRM) && defined(USE_DRICONF)
451 driver = loader_get_dri_config_driver(fd);
452 if (driver)
453 return driver;
454 #endif
455
456 if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) {
457 driver = loader_get_kernel_driver_name(fd);
458 if (driver)
459 log_(_LOADER_INFO, "using driver %s for %d\n", driver, fd);
460 return driver;
461 }
462
463 for (i = 0; driver_map[i].driver; i++) {
464 if (vendor_id != driver_map[i].vendor_id)
465 continue;
466
467 if (driver_map[i].predicate && !driver_map[i].predicate(fd))
468 continue;
469
470 if (driver_map[i].num_chips_ids == -1) {
471 driver = strdup(driver_map[i].driver);
472 goto out;
473 }
474
475 for (j = 0; j < driver_map[i].num_chips_ids; j++)
476 if (driver_map[i].chip_ids[j] == chip_id) {
477 driver = strdup(driver_map[i].driver);
478 goto out;
479 }
480 }
481
482 out:
483 log_(driver ? _LOADER_DEBUG : _LOADER_WARNING,
484 "pci id for fd %d: %04x:%04x, driver %s\n",
485 fd, vendor_id, chip_id, driver);
486 return driver;
487 }
488
489 void
490 loader_set_logger(void (*logger)(int level, const char *fmt, ...))
491 {
492 log_ = logger;
493 }
494
495 /* XXX: Local definition to avoid pulling the heavyweight GL/gl.h and
496 * GL/internal/dri_interface.h
497 */
498
499 #ifndef __DRI_DRIVER_GET_EXTENSIONS
500 #define __DRI_DRIVER_GET_EXTENSIONS "__driDriverGetExtensions"
501 #endif
502
503 char *
504 loader_get_extensions_name(const char *driver_name)
505 {
506 char *name = NULL;
507
508 if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0)
509 return NULL;
510
511 const size_t len = strlen(name);
512 for (size_t i = 0; i < len; i++) {
513 if (name[i] == '-')
514 name[i] = '_';
515 }
516
517 return name;
518 }