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