amdgpu_dri3.c revision 11bf0794
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 "amdgpu_drv.h"
30
31#ifdef HAVE_DRI3_H
32
33#include "amdgpu_glamor.h"
34#include "amdgpu_pixmap.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	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
47	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
48	drm_magic_t magic;
49	int fd;
50
51	fd = open(info->dri2.device_name, O_RDWR | O_CLOEXEC);
52	if (fd < 0)
53		return BadAlloc;
54
55	/* Before FD passing in the X protocol with DRI3 (and increased
56	 * security of rendering with per-process address spaces on the
57	 * GPU), the kernel had to come up with a way to have the server
58	 * decide which clients got to access the GPU, which was done by
59	 * each client getting a unique (magic) number from the kernel,
60	 * passing it to the server, and the server then telling the
61	 * kernel which clients were authenticated for using the device.
62	 *
63	 * Now that we have FD passing, the server can just set up the
64	 * authentication on its own and hand the prepared FD off to the
65	 * client.
66	 */
67	if (drmGetMagic(fd, &magic) < 0) {
68		if (errno == EACCES) {
69			/* Assume that we're on a render node, and the fd is
70			 * already as authenticated as it should be.
71			 */
72			*out = fd;
73			return Success;
74		} else {
75			close(fd);
76			return BadMatch;
77		}
78	}
79
80	if (drmAuthMagic(pAMDGPUEnt->fd, magic) < 0) {
81		close(fd);
82		return BadMatch;
83	}
84
85	*out = fd;
86	return Success;
87}
88
89static int open_render_node(ScreenPtr screen, int *out)
90{
91	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
92	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
93	int fd;
94
95	fd = open(pAMDGPUEnt->render_node, O_RDWR | O_CLOEXEC);
96	if (fd < 0)
97		return BadAlloc;
98
99	*out = fd;
100	return Success;
101}
102
103static int
104amdgpu_dri3_open(ScreenPtr screen, RRProviderPtr provider, int *out)
105{
106	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
107	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
108	int ret = BadAlloc;
109
110	if (pAMDGPUEnt->render_node)
111		ret = open_render_node(screen, out);
112
113	if (ret != Success)
114		ret = open_master_node(screen, out);
115
116	return ret;
117}
118
119#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
120
121static int
122amdgpu_dri3_open_client(ClientPtr client, ScreenPtr screen,
123			RRProviderPtr provider, int *out)
124{
125	const char *cmdname = GetClientCmdName(client);
126	Bool is_ssh = FALSE;
127
128	/* If the executable name is "ssh", assume that this client connection
129	 * is forwarded from another host via SSH
130	 */
131	if (cmdname) {
132		char *cmd = strdup(cmdname);
133
134		/* Cut off any colon and whatever comes after it, see
135		 * https://lists.freedesktop.org/archives/xorg-devel/2015-December/048164.html
136		 */
137		cmd = strtok(cmd, ":");
138
139		is_ssh = strcmp(basename(cmd), "ssh") == 0;
140		free(cmd);
141	}
142
143	if (!is_ssh)
144		return amdgpu_dri3_open(screen, provider, out);
145
146	return BadAccess;
147}
148
149#endif /* DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0) */
150
151static PixmapPtr amdgpu_dri3_pixmap_from_fd(ScreenPtr screen,
152					    int fd,
153					    CARD16 width,
154					    CARD16 height,
155					    CARD16 stride,
156					    CARD8 depth,
157					    CARD8 bpp)
158{
159	PixmapPtr pixmap;
160
161#ifdef USE_GLAMOR
162	/* Avoid generating a GEM flink name if possible */
163	if (AMDGPUPTR(xf86ScreenToScrn(screen))->use_glamor) {
164		pixmap = glamor_pixmap_from_fd(screen, fd, width, height,
165					       stride, depth, bpp);
166		if (pixmap) {
167			struct amdgpu_pixmap *priv = calloc(1, sizeof(*priv));
168
169			if (priv) {
170				amdgpu_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,
193				      AMDGPU_CREATE_PIXMAP_DRI2);
194	if (!pixmap)
195		return NULL;
196
197	if (!screen->ModifyPixmapHeader(pixmap, width, height, 0, bpp, stride,
198					NULL))
199		goto free_pixmap;
200
201	if (screen->SetSharedPixmapBacking(pixmap, (void*)(intptr_t)fd))
202		return pixmap;
203
204free_pixmap:
205	fbDestroyPixmap(pixmap);
206	return NULL;
207}
208
209static int amdgpu_dri3_fd_from_pixmap(ScreenPtr screen,
210				      PixmapPtr pixmap,
211				      CARD16 *stride,
212				      CARD32 *size)
213{
214	struct amdgpu_buffer *bo;
215	struct amdgpu_bo_info bo_info;
216	uint32_t fd;
217#ifdef USE_GLAMOR
218	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
219	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
220
221	if (info->use_glamor)
222		return glamor_fd_from_pixmap(screen, pixmap, stride, size);
223#endif
224
225	bo = amdgpu_get_pixmap_bo(pixmap);
226	if (!bo)
227		return -1;
228
229	if (pixmap->devKind > UINT16_MAX)
230		return -1;
231
232	if (amdgpu_bo_query_info(bo->bo.amdgpu, &bo_info) != 0)
233		return -1;
234
235	if (amdgpu_bo_export(bo->bo.amdgpu, amdgpu_bo_handle_type_dma_buf_fd,
236			     &fd) != 0)
237		return -1;
238
239	*stride = pixmap->devKind;
240	*size = bo_info.alloc_size;
241	return fd;
242}
243
244static dri3_screen_info_rec amdgpu_dri3_screen_info = {
245#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
246	.version = 1,
247	.open_client = amdgpu_dri3_open_client,
248#else
249	.version = 0,
250	.open = amdgpu_dri3_open,
251#endif
252	.pixmap_from_fd = amdgpu_dri3_pixmap_from_fd,
253	.fd_from_pixmap = amdgpu_dri3_fd_from_pixmap
254};
255
256Bool
257amdgpu_dri3_screen_init(ScreenPtr screen)
258{
259	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
260	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
261
262	pAMDGPUEnt->render_node = drmGetRenderDeviceNameFromFd(pAMDGPUEnt->fd);
263
264	if (!dri3_screen_init(screen, &amdgpu_dri3_screen_info)) {
265		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
266			   "dri3_screen_init failed\n");
267		return FALSE;
268	}
269
270	return TRUE;
271}
272
273#else /* !HAVE_DRI3_H */
274
275Bool
276amdgpu_dri3_screen_init(ScreenPtr screen)
277{
278	xf86DrvMsg(xf86ScreenToScrn(screen)->scrnIndex, X_INFO,
279		   "Can't initialize DRI3 because dri3.h not available at "
280		   "build time\n");
281
282	return FALSE;
283}
284
285#endif
286