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