radeon_dri3.c revision 3ed65abb
1/*
2 * Copyright © 2013-2014 Intel Corporation
3 * Copyright © 2015 Advanced Micro Devices, Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission.  The copyright holders make no representations
12 * about the suitability of this software for any purpose.  It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "radeon.h"
30
31#ifdef HAVE_DRI3_H
32
33#include "radeon_bo_gem.h"
34#include "radeon_glamor.h"
35#include "dri3.h"
36
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <fcntl.h>
40#include <errno.h>
41#include <libgen.h>
42
43static int open_master_node(ScreenPtr screen, int *out)
44{
45	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
46	RADEONInfoPtr info = RADEONPTR(scrn);
47	drm_magic_t magic;
48	int fd;
49
50	fd = open(info->dri2.device_name, O_RDWR | O_CLOEXEC);
51	if (fd < 0)
52		return BadAlloc;
53
54	/* Before FD passing in the X protocol with DRI3 (and increased
55	 * security of rendering with per-process address spaces on the
56	 * GPU), the kernel had to come up with a way to have the server
57	 * decide which clients got to access the GPU, which was done by
58	 * each client getting a unique (magic) number from the kernel,
59	 * passing it to the server, and the server then telling the
60	 * kernel which clients were authenticated for using the device.
61	 *
62	 * Now that we have FD passing, the server can just set up the
63	 * authentication on its own and hand the prepared FD off to the
64	 * client.
65	 */
66	if (drmGetMagic(fd, &magic) < 0) {
67		if (errno == EACCES) {
68			/* Assume that we're on a render node, and the fd is
69			 * already as authenticated as it should be.
70			 */
71			*out = fd;
72			return Success;
73		} else {
74			close(fd);
75			return BadMatch;
76		}
77	}
78
79	if (drmAuthMagic(info->dri2.drm_fd, magic) < 0) {
80		close(fd);
81		return BadMatch;
82	}
83
84	*out = fd;
85	return Success;
86}
87
88static int open_render_node(ScreenPtr screen, int *out)
89{
90	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
91	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
92	int fd;
93
94	fd = open(pRADEONEnt->render_node, O_RDWR | O_CLOEXEC);
95	if (fd < 0)
96		return BadAlloc;
97
98	*out = fd;
99	return Success;
100}
101
102static int
103radeon_dri3_open(ScreenPtr screen, RRProviderPtr provider, int *out)
104{
105	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
106	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
107	int ret = BadAlloc;
108
109	if (pRADEONEnt->render_node)
110		ret = open_render_node(screen, out);
111
112	if (ret != Success)
113		ret = open_master_node(screen, out);
114
115	return ret;
116}
117
118#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
119
120static int
121radeon_dri3_open_client(ClientPtr client, ScreenPtr screen,
122			RRProviderPtr provider, int *out)
123{
124	const char *cmdname = GetClientCmdName(client);
125	Bool is_ssh = FALSE;
126
127	/* If the executable name is "ssh", assume that this client connection
128	 * is forwarded from another host via SSH
129	 */
130	if (cmdname) {
131		char *cmd = strdup(cmdname);
132
133		/* Cut off any colon and whatever comes after it, see
134		 * https://lists.freedesktop.org/archives/xorg-devel/2015-December/048164.html
135		 */
136		cmd = strtok(cmd, ":");
137
138		is_ssh = strcmp(basename(cmd), "ssh") == 0;
139		free(cmd);
140	}
141
142	if (!is_ssh)
143		return radeon_dri3_open(screen, provider, out);
144
145	return BadAccess;
146}
147
148#endif /* DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0) */
149
150static PixmapPtr radeon_dri3_pixmap_from_fd(ScreenPtr screen,
151					    int fd,
152					    CARD16 width,
153					    CARD16 height,
154					    CARD16 stride,
155					    CARD8 depth,
156					    CARD8 bpp)
157{
158	PixmapPtr pixmap;
159
160#ifdef USE_GLAMOR
161	/* Avoid generating a GEM flink name if possible */
162	if (RADEONPTR(xf86ScreenToScrn(screen))->use_glamor) {
163		pixmap = glamor_pixmap_from_fd(screen, fd, width, height,
164					       stride, depth, bpp);
165		if (pixmap) {
166			struct radeon_pixmap *priv =
167				calloc(1, sizeof(struct radeon_pixmap));
168
169			if (priv) {
170				radeon_set_pixmap_private(pixmap, priv);
171				return pixmap;
172			}
173
174			screen->DestroyPixmap(pixmap);
175			return NULL;
176		}
177	}
178#endif
179
180	if (depth < 8)
181		return NULL;
182
183	switch (bpp) {
184	case 8:
185	case 16:
186	case 32:
187		break;
188	default:
189		return NULL;
190	}
191
192	pixmap = screen->CreatePixmap(screen, 0, 0, depth, RADEON_CREATE_PIXMAP_DRI2);
193	if (!pixmap)
194		return NULL;
195
196	if (!screen->ModifyPixmapHeader(pixmap, width, height, 0, bpp, stride,
197					NULL))
198		goto free_pixmap;
199
200	if (screen->SetSharedPixmapBacking(pixmap, (void*)(intptr_t)fd))
201		return pixmap;
202
203free_pixmap:
204	fbDestroyPixmap(pixmap);
205	return NULL;
206}
207
208static int radeon_dri3_fd_from_pixmap(ScreenPtr screen,
209				      PixmapPtr pixmap,
210				      CARD16 *stride,
211				      CARD32 *size)
212{
213	struct radeon_bo *bo;
214	int fd;
215
216	bo = radeon_get_pixmap_bo(pixmap);
217	if (!bo) {
218#ifdef USE_GLAMOR
219		ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
220		RADEONInfoPtr info = RADEONPTR(scrn);
221
222		if (info->use_glamor)
223			return glamor_fd_from_pixmap(screen, pixmap, stride, size);
224#endif
225
226		exaMoveInPixmap(pixmap);
227		bo = radeon_get_pixmap_bo(pixmap);
228		if (!bo)
229			return -1;
230	}
231
232	if (pixmap->devKind > UINT16_MAX)
233		return -1;
234
235	if (radeon_gem_prime_share_bo(bo, &fd) < 0)
236		return -1;
237
238	*stride = pixmap->devKind;
239	*size = bo->size;
240	return fd;
241}
242
243static dri3_screen_info_rec radeon_dri3_screen_info = {
244#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
245	.version = 1,
246	.open_client = radeon_dri3_open_client,
247#else
248	.version = 0,
249	.open = radeon_dri3_open,
250#endif
251	.pixmap_from_fd = radeon_dri3_pixmap_from_fd,
252	.fd_from_pixmap = radeon_dri3_fd_from_pixmap
253};
254
255Bool
256radeon_dri3_screen_init(ScreenPtr screen)
257{
258	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
259	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
260
261	pRADEONEnt->render_node = drmGetRenderDeviceNameFromFd(pRADEONEnt->fd);
262
263	if (!dri3_screen_init(screen, &radeon_dri3_screen_info)) {
264		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
265			   "dri3_screen_init failed\n");
266		return FALSE;
267	}
268
269	return TRUE;
270}
271
272#else /* !HAVE_DRI3_H */
273
274Bool
275radeon_dri3_screen_init(ScreenPtr screen)
276{
277	xf86DrvMsg(xf86ScreenToScrn(screen)->scrnIndex, X_INFO,
278		   "Can't initialize DRI3 because dri3.h not available at "
279		   "build time\n");
280
281	return FALSE;
282}
283
284#endif
285