103b705cfSriastradh/***************************************************************************
203b705cfSriastradh
303b705cfSriastradh Copyright 2013 Intel Corporation.  All Rights Reserved.
403b705cfSriastradh
503b705cfSriastradh Permission is hereby granted, free of charge, to any person obtaining a
603b705cfSriastradh copy of this software and associated documentation files (the
703b705cfSriastradh "Software"), to deal in the Software without restriction, including
803b705cfSriastradh without limitation the rights to use, copy, modify, merge, publish,
903b705cfSriastradh distribute, sub license, and/or sell copies of the Software, and to
1003b705cfSriastradh permit persons to whom the Software is furnished to do so, subject to
1103b705cfSriastradh the following conditions:
1203b705cfSriastradh
1303b705cfSriastradh The above copyright notice and this permission notice (including the
1403b705cfSriastradh next paragraph) shall be included in all copies or substantial portions
1503b705cfSriastradh of the Software.
1603b705cfSriastradh
1703b705cfSriastradh THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1803b705cfSriastradh OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1903b705cfSriastradh MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
2003b705cfSriastradh IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
2103b705cfSriastradh DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2203b705cfSriastradh OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
2303b705cfSriastradh THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2403b705cfSriastradh
2503b705cfSriastradh **************************************************************************/
2603b705cfSriastradh
2742542f5fSchristos#ifdef HAVE_CONFIG_H
2842542f5fSchristos#include "config.h"
2942542f5fSchristos#endif
3042542f5fSchristos
3142542f5fSchristos#include <sys/types.h>
3242542f5fSchristos#include <sys/stat.h>
3303b705cfSriastradh#include <assert.h>
3403b705cfSriastradh#include <string.h>
3503b705cfSriastradh#include <unistd.h>
3603b705cfSriastradh#include <fcntl.h>
3703b705cfSriastradh#include <stdlib.h>
3842542f5fSchristos#include <dirent.h>
3903b705cfSriastradh#include <errno.h>
4003b705cfSriastradh
41fe8aea9eSmrg#if MAJOR_IN_MKDEV
42fe8aea9eSmrg#include <sys/mkdev.h>
43fe8aea9eSmrg#elif MAJOR_IN_SYSMACROS
44fe8aea9eSmrg#include <sys/sysmacros.h>
45fe8aea9eSmrg#endif
46fe8aea9eSmrg
4703b705cfSriastradh#include <pciaccess.h>
4842542f5fSchristos
4942542f5fSchristos#include <xorg-server.h>
5003b705cfSriastradh#include <xf86.h>
5103b705cfSriastradh#include <xf86drm.h>
5203b705cfSriastradh#include <xf86drmMode.h>
5303b705cfSriastradh#include <xf86_OSproc.h>
5403b705cfSriastradh#include <i915_drm.h>
5503b705cfSriastradh
5642542f5fSchristos#ifdef XSERVER_PLATFORM_BUS
5742542f5fSchristos#include <xf86platformBus.h>
5842542f5fSchristos#endif
5942542f5fSchristos
6042542f5fSchristos#ifdef HAVE_VALGRIND
6142542f5fSchristos#include <valgrind.h>
6242542f5fSchristos#include <memcheck.h>
6342542f5fSchristos#define VG(x) x
6442542f5fSchristos#else
6542542f5fSchristos#define VG(x)
6642542f5fSchristos#endif
6742542f5fSchristos
6842542f5fSchristos#define VG_CLEAR(s) VG(memset(&s, 0, sizeof(s)))
6942542f5fSchristos
7003b705cfSriastradh#include "intel_driver.h"
7142542f5fSchristos#include "fd.h"
7203b705cfSriastradh
7303b705cfSriastradhstruct intel_device {
7413496ba1Ssnj	int idx;
7542542f5fSchristos	char *master_node;
7642542f5fSchristos	char *render_node;
7703b705cfSriastradh	int fd;
7813496ba1Ssnj	int device_id;
7903b705cfSriastradh	int open_count;
8003b705cfSriastradh	int master_count;
8103b705cfSriastradh};
8203b705cfSriastradh
8303b705cfSriastradhstatic int intel_device_key = -1;
8403b705cfSriastradh
8542542f5fSchristosstatic int dump_file(ScrnInfoPtr scrn, const char *path)
8642542f5fSchristos{
8742542f5fSchristos	FILE *file;
8842542f5fSchristos	size_t len = 0;
8942542f5fSchristos	char *line = NULL;
9042542f5fSchristos
9142542f5fSchristos	file = fopen(path, "r");
9242542f5fSchristos	if (file == NULL)
9342542f5fSchristos		return 0;
9442542f5fSchristos
9542542f5fSchristos	xf86DrvMsg(scrn->scrnIndex, X_INFO, "[drm] Contents of '%s':\n", path);
9642542f5fSchristos	while (getline(&line, &len, file) != -1)
9742542f5fSchristos		xf86DrvMsg(scrn->scrnIndex, X_INFO, "[drm] %s", line);
9842542f5fSchristos
9942542f5fSchristos	free(line);
10042542f5fSchristos	fclose(file);
10142542f5fSchristos	return 1;
10242542f5fSchristos}
10342542f5fSchristos
10442542f5fSchristosstatic int __find_debugfs(void)
10542542f5fSchristos{
10642542f5fSchristos	int i;
10742542f5fSchristos
10842542f5fSchristos	for (i = 0; i < DRM_MAX_MINOR; i++) {
10942542f5fSchristos		char path[80];
11042542f5fSchristos
11142542f5fSchristos		sprintf(path, "/sys/kernel/debug/dri/%d/i915_wedged", i);
11242542f5fSchristos		if (access(path, R_OK) == 0)
11342542f5fSchristos			return i;
11442542f5fSchristos
11542542f5fSchristos		sprintf(path, "/debug/dri/%d/i915_wedged", i);
11642542f5fSchristos		if (access(path, R_OK) == 0)
11742542f5fSchristos			return i;
11842542f5fSchristos	}
11942542f5fSchristos
12042542f5fSchristos	return -1;
12142542f5fSchristos}
12242542f5fSchristos
12342542f5fSchristosstatic int drm_get_minor(int fd)
12442542f5fSchristos{
12542542f5fSchristos	struct stat st;
12642542f5fSchristos
12742542f5fSchristos	if (fstat(fd, &st))
12842542f5fSchristos		return __find_debugfs();
12942542f5fSchristos
13042542f5fSchristos	if (!S_ISCHR(st.st_mode))
13142542f5fSchristos		return __find_debugfs();
13242542f5fSchristos
13342542f5fSchristos	return st.st_rdev & 0x63;
13442542f5fSchristos}
13542542f5fSchristos
13642542f5fSchristos#if __linux__
13742542f5fSchristos#include <sys/mount.h>
13842542f5fSchristos
13942542f5fSchristosstatic void dump_debugfs(ScrnInfoPtr scrn, int fd, const char *name)
14042542f5fSchristos{
14142542f5fSchristos	char path[80];
14242542f5fSchristos	int minor;
14342542f5fSchristos
14442542f5fSchristos	minor = drm_get_minor(fd);
14542542f5fSchristos	if (minor < 0)
14642542f5fSchristos		return;
14742542f5fSchristos
14842542f5fSchristos	sprintf(path, "/sys/kernel/debug/dri/%d/%s", minor, name);
14942542f5fSchristos	if (dump_file(scrn, path))
15042542f5fSchristos		return;
15142542f5fSchristos
15242542f5fSchristos	sprintf(path, "/debug/dri/%d/%s", minor, name);
15342542f5fSchristos	if (dump_file(scrn, path))
15442542f5fSchristos		return;
15542542f5fSchristos
15642542f5fSchristos	if (mount("X-debug", "/sys/kernel/debug", "debugfs", 0, 0) == 0) {
15742542f5fSchristos		sprintf(path, "/sys/kernel/debug/dri/%d/%s", minor, name);
15842542f5fSchristos		dump_file(scrn, path);
15942542f5fSchristos		umount("X-debug");
16042542f5fSchristos		return;
16142542f5fSchristos	}
16242542f5fSchristos}
16342542f5fSchristos#else
16442542f5fSchristosstatic void dump_debugfs(ScrnInfoPtr scrn, int fd, const char *name) { }
16542542f5fSchristos#endif
16642542f5fSchristos
16742542f5fSchristosstatic void dump_clients_info(ScrnInfoPtr scrn, int fd)
16842542f5fSchristos{
16942542f5fSchristos	dump_debugfs(scrn, fd, "clients");
17042542f5fSchristos}
17142542f5fSchristos
17242542f5fSchristosstatic int __intel_get_device_id(int fd)
17342542f5fSchristos{
17442542f5fSchristos	struct drm_i915_getparam gp;
17542542f5fSchristos	int devid = 0;
17642542f5fSchristos
17742542f5fSchristos	VG_CLEAR(gp);
17842542f5fSchristos	gp.param = I915_PARAM_CHIPSET_ID;
17942542f5fSchristos	gp.value = &devid;
18042542f5fSchristos
18142542f5fSchristos	if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
18242542f5fSchristos		return 0;
18342542f5fSchristos
18442542f5fSchristos	return devid;
18542542f5fSchristos}
18642542f5fSchristos
18742542f5fSchristosint intel_entity_get_devid(int idx)
18842542f5fSchristos{
18942542f5fSchristos	struct intel_device *dev;
19042542f5fSchristos
19142542f5fSchristos	dev = xf86GetEntityPrivate(idx, intel_device_key)->ptr;
19242542f5fSchristos	if (dev == NULL)
19342542f5fSchristos		return 0;
19442542f5fSchristos
19513496ba1Ssnj	return dev->device_id;
19642542f5fSchristos}
19742542f5fSchristos
19803b705cfSriastradhstatic inline struct intel_device *intel_device(ScrnInfoPtr scrn)
19903b705cfSriastradh{
20003b705cfSriastradh	if (scrn->entityList == NULL)
20103b705cfSriastradh		return NULL;
20203b705cfSriastradh
20303b705cfSriastradh	return xf86GetEntityPrivate(scrn->entityList[0], intel_device_key)->ptr;
20403b705cfSriastradh}
20503b705cfSriastradh
206fe8aea9eSmrgstatic const char *kernel_module_names[] ={
207fe8aea9eSmrg	"i915",
208fe8aea9eSmrg	NULL,
209fe8aea9eSmrg};
210fe8aea9eSmrg
21142542f5fSchristosstatic int is_i915_device(int fd)
21203b705cfSriastradh{
21303b705cfSriastradh	drm_version_t version;
214fe8aea9eSmrg	const char **kn;
21503b705cfSriastradh	char name[5] = "";
21603b705cfSriastradh
21703b705cfSriastradh	memset(&version, 0, sizeof(version));
21803b705cfSriastradh	version.name_len = 4;
21903b705cfSriastradh	version.name = name;
22003b705cfSriastradh
22103b705cfSriastradh	if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
22242542f5fSchristos		return 0;
22303b705cfSriastradh
224fe8aea9eSmrg	for (kn = kernel_module_names; *kn; kn++)
225fe8aea9eSmrg		if (strcmp(*kn, name) == 0)
226fe8aea9eSmrg			return 1;
227fe8aea9eSmrg
228fe8aea9eSmrg	return 0;
229fe8aea9eSmrg}
230fe8aea9eSmrg
231fe8aea9eSmrgstatic int load_i915_kernel_module(void)
232fe8aea9eSmrg{
233fe8aea9eSmrg	const char **kn;
234fe8aea9eSmrg
235fe8aea9eSmrg	for (kn = kernel_module_names; *kn; kn++)
236fe8aea9eSmrg		if (xf86LoadKernelModule(*kn))
237fe8aea9eSmrg			return 0;
238fe8aea9eSmrg
239fe8aea9eSmrg	return -1;
24003b705cfSriastradh}
24103b705cfSriastradh
24242542f5fSchristosstatic int is_i915_gem(int fd)
24303b705cfSriastradh{
24442542f5fSchristos	int ret = is_i915_device(fd);
24503b705cfSriastradh
24603b705cfSriastradh	if (ret) {
24703b705cfSriastradh		struct drm_i915_getparam gp;
24842542f5fSchristos
24942542f5fSchristos		VG_CLEAR(gp);
25003b705cfSriastradh		gp.param = I915_PARAM_HAS_GEM;
25103b705cfSriastradh		gp.value = &ret;
25242542f5fSchristos
25303b705cfSriastradh		if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
25442542f5fSchristos			ret = 0;
25503b705cfSriastradh	}
25642542f5fSchristos
25742542f5fSchristos	return ret;
25842542f5fSchristos}
25942542f5fSchristos
26042542f5fSchristosstatic int __intel_check_device(int fd)
26142542f5fSchristos{
26242542f5fSchristos	int ret;
26342542f5fSchristos
26442542f5fSchristos	/* Confirm that this is a i915.ko device with GEM/KMS enabled */
26542542f5fSchristos	ret = is_i915_gem(fd);
26603b705cfSriastradh	if (ret && !hosted()) {
26703b705cfSriastradh		struct drm_mode_card_res res;
26803b705cfSriastradh
26903b705cfSriastradh		memset(&res, 0, sizeof(res));
27003b705cfSriastradh		if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
27142542f5fSchristos			ret = 0;
27203b705cfSriastradh	}
27303b705cfSriastradh
27403b705cfSriastradh	return ret;
27503b705cfSriastradh}
27603b705cfSriastradh
27742542f5fSchristosstatic int open_cloexec(const char *path)
27803b705cfSriastradh{
27942542f5fSchristos	struct stat st;
28042542f5fSchristos	int loop = 1000;
28142542f5fSchristos	int fd;
28203b705cfSriastradh
28342542f5fSchristos	/* No file? Assume the driver is loading slowly */
28442542f5fSchristos	while (stat(path, &st) == -1 && errno == ENOENT && --loop)
28542542f5fSchristos		usleep(50000);
28642542f5fSchristos
28742542f5fSchristos	if (loop != 1000)
28842542f5fSchristos		ErrorF("intel: waited %d ms for '%s' to appear\n",
28942542f5fSchristos		       (1000 - loop) * 50, path);
29042542f5fSchristos
29142542f5fSchristos	fd = -1;
29242542f5fSchristos#ifdef O_CLOEXEC
29342542f5fSchristos	fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC);
29442542f5fSchristos#endif
29503b705cfSriastradh	if (fd == -1)
29642542f5fSchristos		fd = fd_set_cloexec(open(path, O_RDWR | O_NONBLOCK));
29703b705cfSriastradh
29842542f5fSchristos	return fd;
29942542f5fSchristos}
30042542f5fSchristos
30142542f5fSchristos#ifdef __linux__
30242542f5fSchristosstatic int __intel_open_device__major_minor(int _major, int _minor)
30342542f5fSchristos{
30442542f5fSchristos	char path[256];
30542542f5fSchristos	DIR *dir;
30642542f5fSchristos	struct dirent *de;
30742542f5fSchristos	int base, fd = -1;
30842542f5fSchristos
30942542f5fSchristos	base = sprintf(path, "/dev/dri/");
31042542f5fSchristos
31142542f5fSchristos	dir = opendir(path);
31242542f5fSchristos	if (dir == NULL)
31342542f5fSchristos		return -1;
31442542f5fSchristos
31542542f5fSchristos	while ((de = readdir(dir)) != NULL) {
31642542f5fSchristos		struct stat st;
31742542f5fSchristos
31842542f5fSchristos		if (*de->d_name == '.')
31942542f5fSchristos			continue;
32042542f5fSchristos
32142542f5fSchristos		sprintf(path + base, "%s", de->d_name);
32242542f5fSchristos		if (stat(path, &st) == 0 &&
32342542f5fSchristos		    major(st.st_rdev) == _major &&
32442542f5fSchristos		    minor(st.st_rdev) == _minor) {
32542542f5fSchristos			fd = open_cloexec(path);
32642542f5fSchristos			break;
32742542f5fSchristos		}
32803b705cfSriastradh	}
32942542f5fSchristos
33042542f5fSchristos	closedir(dir);
33103b705cfSriastradh
33203b705cfSriastradh	return fd;
33303b705cfSriastradh}
33403b705cfSriastradh
33542542f5fSchristosstatic int __intel_open_device__pci(const struct pci_device *pci)
33603b705cfSriastradh{
33742542f5fSchristos	struct stat st;
33842542f5fSchristos	char path[256];
33942542f5fSchristos	DIR *dir;
34042542f5fSchristos	struct dirent *de;
34142542f5fSchristos	int base;
34203b705cfSriastradh	int fd;
34303b705cfSriastradh
34442542f5fSchristos	/* Look up the major:minor for the drm device through sysfs.
34542542f5fSchristos	 * First we need to check that sysfs is available, then
34642542f5fSchristos	 * check that we have loaded our driver. When we are happy
34742542f5fSchristos	 * that our KMS module is loaded, we can then search for our
34842542f5fSchristos	 * device node. We make the assumption that it uses the same
34942542f5fSchristos	 * name, but after that we read the major:minor assigned to us
35042542f5fSchristos	 * and search for a matching entry in /dev.
35142542f5fSchristos	 */
35242542f5fSchristos
35342542f5fSchristos	base = sprintf(path,
35442542f5fSchristos		       "/sys/bus/pci/devices/%04x:%02x:%02x.%d/",
35542542f5fSchristos		       pci->domain, pci->bus, pci->dev, pci->func);
35642542f5fSchristos	if (stat(path, &st))
35742542f5fSchristos		return -1;
35803b705cfSriastradh
35942542f5fSchristos	sprintf(path + base, "drm");
36042542f5fSchristos	dir = opendir(path);
36142542f5fSchristos	if (dir == NULL) {
36242542f5fSchristos		int loop = 0;
36303b705cfSriastradh
36442542f5fSchristos		sprintf(path + base, "driver");
36542542f5fSchristos		if (stat(path, &st)) {
366fe8aea9eSmrg			if (load_i915_kernel_module())
36703b705cfSriastradh				return -1;
36803b705cfSriastradh			(void)xf86LoadKernelModule("fbcon");
36903b705cfSriastradh		}
37003b705cfSriastradh
37142542f5fSchristos		sprintf(path + base, "drm");
37242542f5fSchristos		while ((dir = opendir(path)) == NULL && loop++ < 100)
37342542f5fSchristos			usleep(20000);
37442542f5fSchristos
37542542f5fSchristos		ErrorF("intel: waited %d ms for i915.ko driver to load\n", loop * 20000 / 1000);
37642542f5fSchristos
37742542f5fSchristos		if (dir == NULL)
37842542f5fSchristos			return -1;
37942542f5fSchristos	}
38042542f5fSchristos
38142542f5fSchristos	fd = -1;
38242542f5fSchristos	while ((de = readdir(dir)) != NULL) {
38342542f5fSchristos		if (*de->d_name == '.')
38442542f5fSchristos			continue;
38542542f5fSchristos
38642542f5fSchristos		if (strncmp(de->d_name, "card", 4) == 0) {
38742542f5fSchristos			sprintf(path + base + 4, "/dev/dri/%s", de->d_name);
38842542f5fSchristos			fd = open_cloexec(path + base + 4);
38942542f5fSchristos			if (fd != -1)
39042542f5fSchristos				break;
39142542f5fSchristos
39242542f5fSchristos			sprintf(path + base + 3, "/%s/dev", de->d_name);
39342542f5fSchristos			fd = open(path, O_RDONLY);
39442542f5fSchristos			if (fd == -1)
39542542f5fSchristos				break;
39642542f5fSchristos
39742542f5fSchristos			base = read(fd, path, sizeof(path) - 1);
39842542f5fSchristos			close(fd);
39942542f5fSchristos
40042542f5fSchristos			fd = -1;
40142542f5fSchristos			if (base > 0) {
40242542f5fSchristos				int major, minor;
40342542f5fSchristos				path[base] = '\0';
40442542f5fSchristos				if (sscanf(path, "%d:%d", &major, &minor) == 2)
40542542f5fSchristos					fd = __intel_open_device__major_minor(major, minor);
40603b705cfSriastradh			}
40742542f5fSchristos			break;
40803b705cfSriastradh		}
40942542f5fSchristos	}
41042542f5fSchristos	closedir(dir);
41142542f5fSchristos
41242542f5fSchristos	return fd;
41342542f5fSchristos}
41403b705cfSriastradh#else
41542542f5fSchristosstatic int __intel_open_device__pci(const struct pci_device *pci) { return -1; }
41603b705cfSriastradh#endif
41742542f5fSchristos
41842542f5fSchristosstatic int __intel_open_device__legacy(const struct pci_device *pci)
41942542f5fSchristos{
42042542f5fSchristos	char id[20];
42142542f5fSchristos	int ret;
42242542f5fSchristos
42342542f5fSchristos	snprintf(id, sizeof(id),
42442542f5fSchristos		 "pci:%04x:%02x:%02x.%d",
42542542f5fSchristos		 pci->domain, pci->bus, pci->dev, pci->func);
42642542f5fSchristos
42742542f5fSchristos	ret = drmCheckModesettingSupported(id);
42842542f5fSchristos	if (ret) {
429fe8aea9eSmrg		if (load_i915_kernel_module() == 0)
43042542f5fSchristos			ret = drmCheckModesettingSupported(id);
43142542f5fSchristos		if (ret)
43242542f5fSchristos			return -1;
43342542f5fSchristos		/* Be nice to the user and load fbcon too */
43442542f5fSchristos		(void)xf86LoadKernelModule("fbcon");
43503b705cfSriastradh	}
43603b705cfSriastradh
43742542f5fSchristos	return fd_set_nonblock(drmOpen(NULL, id));
43842542f5fSchristos}
43942542f5fSchristos
44042542f5fSchristosstatic int __intel_open_device(const struct pci_device *pci, const char *path)
44142542f5fSchristos{
44242542f5fSchristos	int fd;
44342542f5fSchristos
44442542f5fSchristos	if (path == NULL) {
44542542f5fSchristos		if (pci == NULL)
44642542f5fSchristos			return -1;
44742542f5fSchristos
44842542f5fSchristos		fd = __intel_open_device__pci(pci);
44942542f5fSchristos		if (fd == -1)
45042542f5fSchristos			fd = __intel_open_device__legacy(pci);
45142542f5fSchristos	} else
45242542f5fSchristos		fd = open_cloexec(path);
45342542f5fSchristos
45403b705cfSriastradh	return fd;
45503b705cfSriastradh}
45603b705cfSriastradh
45742542f5fSchristosstatic char *find_master_node(int fd)
45842542f5fSchristos{
45942542f5fSchristos	struct stat st, master;
46042542f5fSchristos	char buf[128];
46142542f5fSchristos
46242542f5fSchristos	if (fstat(fd, &st))
46342542f5fSchristos		return NULL;
46442542f5fSchristos
46542542f5fSchristos	if (!S_ISCHR(st.st_mode))
46642542f5fSchristos		return NULL;
46742542f5fSchristos
46842542f5fSchristos	sprintf(buf, "/dev/dri/card%d", (int)(st.st_rdev & 0x7f));
46942542f5fSchristos	if (stat(buf, &master) == 0 &&
470fe8aea9eSmrg	    S_ISCHR(master.st_mode) &&
47142542f5fSchristos	    (st.st_rdev & 0x7f) == master.st_rdev)
47242542f5fSchristos		return strdup(buf);
47342542f5fSchristos
47442542f5fSchristos	/* Fallback to iterating over the usual suspects */
47542542f5fSchristos	return drmGetDeviceNameFromFd(fd);
47642542f5fSchristos}
47742542f5fSchristos
47842542f5fSchristosstatic int is_render_node(int fd, struct stat *st)
47942542f5fSchristos{
48042542f5fSchristos	if (fstat(fd, st))
481fe8aea9eSmrg		return -1;
48242542f5fSchristos
48342542f5fSchristos	if (!S_ISCHR(st->st_mode))
484fe8aea9eSmrg		return -1;
48542542f5fSchristos
48642542f5fSchristos	return st->st_rdev & 0x80;
48742542f5fSchristos}
48842542f5fSchristos
48942542f5fSchristosstatic char *find_render_node(int fd)
49042542f5fSchristos{
49142542f5fSchristos	struct stat master, render;
49242542f5fSchristos	char buf[128];
493fe8aea9eSmrg	int i;
49442542f5fSchristos
49542542f5fSchristos	/* Are we a render-node ourselves? */
49642542f5fSchristos	if (is_render_node(fd, &master))
49742542f5fSchristos		return NULL;
49842542f5fSchristos
49942542f5fSchristos	sprintf(buf, "/dev/dri/renderD%d", (int)((master.st_rdev | 0x80) & 0xbf));
50042542f5fSchristos	if (stat(buf, &render) == 0 &&
501fe8aea9eSmrg	    S_ISCHR(render.st_mode) &&
502fe8aea9eSmrg	    render.st_rdev == (master.st_rdev | 0x80))
50342542f5fSchristos		return strdup(buf);
504fe8aea9eSmrg
505fe8aea9eSmrg	/* Misaligned card <-> renderD, do a full search */
506fe8aea9eSmrg	for (i = 0; i < 16; i++) {
507fe8aea9eSmrg		sprintf(buf, "/dev/dri/renderD%d", i + 128);
508fe8aea9eSmrg		if (stat(buf, &render) == 0 &&
509fe8aea9eSmrg		    S_ISCHR(render.st_mode) &&
510fe8aea9eSmrg		    render.st_rdev == (master.st_rdev | 0x80))
511fe8aea9eSmrg			return strdup(buf);
512fe8aea9eSmrg	}
51342542f5fSchristos
51442542f5fSchristos	return NULL;
51542542f5fSchristos}
51642542f5fSchristos
51742542f5fSchristos#if defined(ODEV_ATTRIB_PATH)
51842542f5fSchristosstatic char *get_path(struct xf86_platform_device *dev)
51942542f5fSchristos{
52042542f5fSchristos	const char *path;
52142542f5fSchristos
52242542f5fSchristos	if (dev == NULL)
52342542f5fSchristos		return NULL;
52442542f5fSchristos
52542542f5fSchristos	path = xf86_get_platform_device_attrib(dev, ODEV_ATTRIB_PATH);
52642542f5fSchristos	if (path == NULL)
52742542f5fSchristos		return NULL;
52842542f5fSchristos
52942542f5fSchristos	return strdup(path);
53042542f5fSchristos}
53142542f5fSchristos
53242542f5fSchristos#else
53342542f5fSchristos
53442542f5fSchristosstatic char *get_path(struct xf86_platform_device *dev)
53542542f5fSchristos{
53642542f5fSchristos	return NULL;
53742542f5fSchristos}
53842542f5fSchristos#endif
53942542f5fSchristos
54042542f5fSchristos
54142542f5fSchristos#if defined(ODEV_ATTRIB_FD)
54242542f5fSchristosstatic int get_fd(struct xf86_platform_device *dev)
54342542f5fSchristos{
54442542f5fSchristos	if (dev == NULL)
54542542f5fSchristos		return -1;
54642542f5fSchristos
54742542f5fSchristos	return xf86_get_platform_device_int_attrib(dev, ODEV_ATTRIB_FD, -1);
54842542f5fSchristos}
54942542f5fSchristos
55042542f5fSchristos#else
55142542f5fSchristos
55242542f5fSchristosstatic int get_fd(struct xf86_platform_device *dev)
55342542f5fSchristos{
55442542f5fSchristos	return -1;
55542542f5fSchristos}
55642542f5fSchristos#endif
55742542f5fSchristos
55842542f5fSchristosstatic int is_master(int fd)
55942542f5fSchristos{
56042542f5fSchristos	drmSetVersion sv;
56142542f5fSchristos
56242542f5fSchristos	sv.drm_di_major = 1;
56342542f5fSchristos	sv.drm_di_minor = 1;
56442542f5fSchristos	sv.drm_dd_major = -1;
56542542f5fSchristos	sv.drm_dd_minor = -1;
56642542f5fSchristos
56742542f5fSchristos	return drmIoctl(fd, DRM_IOCTL_SET_VERSION, &sv) == 0;
56842542f5fSchristos}
56942542f5fSchristos
57003b705cfSriastradhint intel_open_device(int entity_num,
57103b705cfSriastradh		      const struct pci_device *pci,
57242542f5fSchristos		      struct xf86_platform_device *platform)
57303b705cfSriastradh{
57403b705cfSriastradh	struct intel_device *dev;
57542542f5fSchristos	char *path;
57642542f5fSchristos	int fd, master_count;
57703b705cfSriastradh
57803b705cfSriastradh	if (intel_device_key == -1)
57903b705cfSriastradh		intel_device_key = xf86AllocateEntityPrivateIndex();
58003b705cfSriastradh	if (intel_device_key == -1)
58103b705cfSriastradh		return -1;
58203b705cfSriastradh
58303b705cfSriastradh	dev = xf86GetEntityPrivate(entity_num, intel_device_key)->ptr;
58403b705cfSriastradh	if (dev)
58503b705cfSriastradh		return dev->fd;
58603b705cfSriastradh
58742542f5fSchristos	path = get_path(platform);
58803b705cfSriastradh
58942542f5fSchristos	master_count = 1; /* DRM_MASTER is managed by Xserver */
59042542f5fSchristos	fd = get_fd(platform);
59142542f5fSchristos	if (fd == -1) {
59242542f5fSchristos		fd = __intel_open_device(pci, path);
59342542f5fSchristos		if (fd == -1)
59442542f5fSchristos			goto err_path;
59542542f5fSchristos
59642542f5fSchristos		master_count = 0;
59742542f5fSchristos	}
59842542f5fSchristos
59942542f5fSchristos	if (path == NULL) {
60042542f5fSchristos		path = find_master_node(fd);
60142542f5fSchristos		if (path == NULL)
60242542f5fSchristos			goto err_close;
60342542f5fSchristos	}
60403b705cfSriastradh
60503b705cfSriastradh	if (!__intel_check_device(fd))
60603b705cfSriastradh		goto err_close;
60703b705cfSriastradh
60803b705cfSriastradh	dev = malloc(sizeof(*dev));
60903b705cfSriastradh	if (dev == NULL)
61003b705cfSriastradh		goto err_close;
61103b705cfSriastradh
61203b705cfSriastradh	/* If hosted under a system compositor, just pretend to be master */
61342542f5fSchristos	if (hosted())
61442542f5fSchristos		master_count++;
61542542f5fSchristos
61642542f5fSchristos	/* Non-root user holding MASTER, don't let go */
61742542f5fSchristos	if (geteuid() && is_master(fd))
61842542f5fSchristos		master_count++;
61942542f5fSchristos
62013496ba1Ssnj	if (pci)
62113496ba1Ssnj		dev->device_id = pci->device_id;
62213496ba1Ssnj	else
62313496ba1Ssnj		dev->device_id = __intel_get_device_id(fd);
62413496ba1Ssnj
62513496ba1Ssnj	dev->idx = entity_num;
62642542f5fSchristos	dev->fd = fd;
62742542f5fSchristos	dev->open_count = master_count;
62842542f5fSchristos	dev->master_count = master_count;
62942542f5fSchristos	dev->master_node = path;
63042542f5fSchristos	dev->render_node = find_render_node(fd);
63142542f5fSchristos	if (dev->render_node == NULL)
63242542f5fSchristos		dev->render_node = dev->master_node;
63303b705cfSriastradh
63403b705cfSriastradh	xf86GetEntityPrivate(entity_num, intel_device_key)->ptr = dev;
63503b705cfSriastradh
63603b705cfSriastradh	return fd;
63703b705cfSriastradh
63803b705cfSriastradherr_close:
63942542f5fSchristos	if (master_count == 0) /* Don't close server-fds */
64042542f5fSchristos		close(fd);
64103b705cfSriastradherr_path:
64242542f5fSchristos	free(path);
64303b705cfSriastradh	return -1;
64403b705cfSriastradh}
64503b705cfSriastradh
646fe8aea9eSmrgvoid intel_close_device(int entity_num)
647fe8aea9eSmrg{
648fe8aea9eSmrg	struct intel_device *dev;
649fe8aea9eSmrg
650fe8aea9eSmrg	if (intel_device_key == -1)
651fe8aea9eSmrg		return;
652fe8aea9eSmrg
653fe8aea9eSmrg	dev = xf86GetEntityPrivate(entity_num, intel_device_key)->ptr;
654fe8aea9eSmrg	xf86GetEntityPrivate(entity_num, intel_device_key)->ptr = NULL;
655fe8aea9eSmrg	if (!dev)
656fe8aea9eSmrg		return;
657fe8aea9eSmrg
658fe8aea9eSmrg	if (dev->master_count == 0) /* Don't close server-fds */
659fe8aea9eSmrg		close(dev->fd);
660fe8aea9eSmrg
661fe8aea9eSmrg	if (dev->render_node != dev->master_node)
662fe8aea9eSmrg		free(dev->render_node);
663fe8aea9eSmrg	free(dev->master_node);
664fe8aea9eSmrg	free(dev);
665fe8aea9eSmrg}
666fe8aea9eSmrg
66742542f5fSchristosint __intel_peek_fd(ScrnInfoPtr scrn)
66842542f5fSchristos{
66942542f5fSchristos	struct intel_device *dev;
67042542f5fSchristos
67142542f5fSchristos	dev = intel_device(scrn);
67242542f5fSchristos	assert(dev && dev->fd != -1);
67342542f5fSchristos
67442542f5fSchristos	return dev->fd;
67542542f5fSchristos}
67642542f5fSchristos
67713496ba1Ssnjint intel_has_render_node(struct intel_device *dev)
67842542f5fSchristos{
67942542f5fSchristos	struct stat st;
68042542f5fSchristos
68142542f5fSchristos	assert(dev && dev->fd != -1);
68242542f5fSchristos	return is_render_node(dev->fd, &st);
68342542f5fSchristos}
68442542f5fSchristos
68513496ba1Ssnjstruct intel_device *intel_get_device(ScrnInfoPtr scrn, int *fd)
68603b705cfSriastradh{
68703b705cfSriastradh	struct intel_device *dev;
68803b705cfSriastradh	int ret;
68903b705cfSriastradh
69003b705cfSriastradh	dev = intel_device(scrn);
69113496ba1Ssnj	if (dev == NULL)
69213496ba1Ssnj		return NULL;
69313496ba1Ssnj
69413496ba1Ssnj	assert(dev->fd != -1);
69503b705cfSriastradh
69603b705cfSriastradh	if (dev->open_count++ == 0) {
69703b705cfSriastradh		drmSetVersion sv;
69803b705cfSriastradh		int retry = 2000;
69903b705cfSriastradh
70003b705cfSriastradh		assert(!hosted());
70103b705cfSriastradh
70203b705cfSriastradh		/* Check that what we opened was a master or a
70303b705cfSriastradh		 * master-capable FD, by setting the version of the
70403b705cfSriastradh		 * interface we'll use to talk to it.
70503b705cfSriastradh		 */
70603b705cfSriastradh		do {
70703b705cfSriastradh			sv.drm_di_major = 1;
70803b705cfSriastradh			sv.drm_di_minor = 1;
70903b705cfSriastradh			sv.drm_dd_major = -1;
71003b705cfSriastradh			sv.drm_dd_minor = -1;
71103b705cfSriastradh			ret = drmIoctl(dev->fd, DRM_IOCTL_SET_VERSION, &sv);
71203b705cfSriastradh			if (ret == 0)
71303b705cfSriastradh				break;
71403b705cfSriastradh
71503b705cfSriastradh			usleep(1000);
71603b705cfSriastradh		} while (--retry);
71703b705cfSriastradh		if (ret != 0) {
71803b705cfSriastradh			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
71903b705cfSriastradh				   "[drm] failed to set drm interface version: %s [%d].\n",
72003b705cfSriastradh				   strerror(errno), errno);
72142542f5fSchristos			dump_clients_info(scrn, dev->fd);
72203b705cfSriastradh			dev->open_count--;
72313496ba1Ssnj			return NULL;
72403b705cfSriastradh		}
72503b705cfSriastradh	}
72603b705cfSriastradh
72713496ba1Ssnj	*fd = dev->fd;
72813496ba1Ssnj	return dev;
72903b705cfSriastradh}
73003b705cfSriastradh
731fe8aea9eSmrgconst char *intel_get_master_name(struct intel_device *dev)
732fe8aea9eSmrg{
733fe8aea9eSmrg	assert(dev && dev->master_node);
734fe8aea9eSmrg	return dev->master_node;
735fe8aea9eSmrg}
736fe8aea9eSmrg
73713496ba1Ssnjconst char *intel_get_client_name(struct intel_device *dev)
73842542f5fSchristos{
73942542f5fSchristos	assert(dev && dev->render_node);
74042542f5fSchristos	return dev->render_node;
74142542f5fSchristos}
74242542f5fSchristos
74342542f5fSchristosstatic int authorise(struct intel_device *dev, int fd)
74442542f5fSchristos{
74542542f5fSchristos	struct stat st;
74642542f5fSchristos	drm_magic_t magic;
74742542f5fSchristos
74842542f5fSchristos	if (is_render_node(fd, &st)) /* restricted authority, do not elevate */
74942542f5fSchristos		return 1;
75042542f5fSchristos
75142542f5fSchristos	return drmGetMagic(fd, &magic) == 0 && drmAuthMagic(dev->fd, magic) == 0;
75242542f5fSchristos}
75342542f5fSchristos
75413496ba1Ssnjint intel_get_client_fd(struct intel_device *dev)
75542542f5fSchristos{
75642542f5fSchristos	int fd = -1;
75742542f5fSchristos
75813496ba1Ssnj	assert(dev && dev->fd != -1);
75942542f5fSchristos	assert(dev->render_node);
76042542f5fSchristos
76142542f5fSchristos#ifdef O_CLOEXEC
76242542f5fSchristos	fd = open(dev->render_node, O_RDWR | O_CLOEXEC);
76342542f5fSchristos#endif
76442542f5fSchristos	if (fd < 0)
76542542f5fSchristos		fd = fd_set_cloexec(open(dev->render_node, O_RDWR));
76642542f5fSchristos	if (fd < 0)
76742542f5fSchristos		return -BadAlloc;
76842542f5fSchristos
76942542f5fSchristos	if (!authorise(dev, fd)) {
77042542f5fSchristos		close(fd);
77142542f5fSchristos		return -BadMatch;
77242542f5fSchristos	}
77342542f5fSchristos
77442542f5fSchristos	assert(is_i915_gem(fd));
77542542f5fSchristos
77642542f5fSchristos	return fd;
77742542f5fSchristos}
77842542f5fSchristos
77913496ba1Ssnjint intel_get_device_id(struct intel_device *dev)
78003b705cfSriastradh{
78142542f5fSchristos	assert(dev && dev->fd != -1);
78213496ba1Ssnj	return dev->device_id;
78303b705cfSriastradh}
78403b705cfSriastradh
78513496ba1Ssnjint intel_get_master(struct intel_device *dev)
78603b705cfSriastradh{
78703b705cfSriastradh	int ret;
78803b705cfSriastradh
78903b705cfSriastradh	assert(dev && dev->fd != -1);
79003b705cfSriastradh
79103b705cfSriastradh	ret = 0;
79203b705cfSriastradh	if (dev->master_count++ == 0) {
79303b705cfSriastradh		int retry = 2000;
79403b705cfSriastradh
79503b705cfSriastradh		assert(!hosted());
79603b705cfSriastradh		do {
79703b705cfSriastradh			ret = drmSetMaster(dev->fd);
79803b705cfSriastradh			if (ret == 0)
79903b705cfSriastradh				break;
80003b705cfSriastradh			usleep(1000);
80103b705cfSriastradh		} while (--retry);
80203b705cfSriastradh	}
80303b705cfSriastradh
80403b705cfSriastradh	return ret;
80503b705cfSriastradh}
80603b705cfSriastradh
80713496ba1Ssnjint intel_put_master(struct intel_device *dev)
80803b705cfSriastradh{
80903b705cfSriastradh	int ret;
81003b705cfSriastradh
81103b705cfSriastradh	assert(dev && dev->fd != -1);
81203b705cfSriastradh
81303b705cfSriastradh	ret = 0;
81403b705cfSriastradh	assert(dev->master_count);
81503b705cfSriastradh	if (--dev->master_count == 0) {
81603b705cfSriastradh		assert(!hosted());
81703b705cfSriastradh		assert(drmSetMaster(dev->fd) == 0);
81803b705cfSriastradh		ret = drmDropMaster(dev->fd);
81903b705cfSriastradh	}
82003b705cfSriastradh
82103b705cfSriastradh	return ret;
82203b705cfSriastradh}
82303b705cfSriastradh
82413496ba1Ssnjvoid intel_put_device(struct intel_device *dev)
82503b705cfSriastradh{
82603b705cfSriastradh	assert(dev && dev->fd != -1);
82703b705cfSriastradh
82803b705cfSriastradh	assert(dev->open_count);
82903b705cfSriastradh	if (--dev->open_count)
83003b705cfSriastradh		return;
83103b705cfSriastradh
83203b705cfSriastradh	assert(!hosted());
83313496ba1Ssnj	xf86GetEntityPrivate(dev->idx, intel_device_key)->ptr = NULL;
83403b705cfSriastradh
83503b705cfSriastradh	drmClose(dev->fd);
83642542f5fSchristos	if (dev->render_node != dev->master_node)
83742542f5fSchristos		free(dev->render_node);
83842542f5fSchristos	free(dev->master_node);
83903b705cfSriastradh	free(dev);
84003b705cfSriastradh}
841