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 <gbm.h>
41 #include <errno.h>
42 #include <libgen.h>
43 
open_master_node(ScreenPtr screen,int * out)44 static int open_master_node(ScreenPtr screen, int *out)
45 {
46 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
47 	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
48 	RADEONInfoPtr info = RADEONPTR(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(pRADEONEnt->fd, magic) < 0) {
82 		close(fd);
83 		return BadMatch;
84 	}
85 
86 	*out = fd;
87 	return Success;
88 }
89 
open_render_node(ScreenPtr screen,int * out)90 static int open_render_node(ScreenPtr screen, int *out)
91 {
92 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
93 	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
94 	int fd;
95 
96 	fd = open(pRADEONEnt->render_node, O_RDWR | O_CLOEXEC);
97 	if (fd < 0)
98 		return BadAlloc;
99 
100 	*out = fd;
101 	return Success;
102 }
103 
104 static int
radeon_dri3_open(ScreenPtr screen,RRProviderPtr provider,int * out)105 radeon_dri3_open(ScreenPtr screen, RRProviderPtr provider, int *out)
106 {
107 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
108 	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
109 	int ret = BadAlloc;
110 
111 	if (pRADEONEnt->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 
122 static int
radeon_dri3_open_client(ClientPtr client,ScreenPtr screen,RRProviderPtr provider,int * out)123 radeon_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 radeon_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 
radeon_dri3_pixmap_from_fd(ScreenPtr screen,int fd,CARD16 width,CARD16 height,CARD16 stride,CARD8 depth,CARD8 bpp)152 static PixmapPtr radeon_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 (RADEONPTR(xf86ScreenToScrn(screen))->use_glamor) {
165 		pixmap = glamor_pixmap_from_fd(screen, fd, width, height,
166 					       stride, depth, bpp);
167 		if (pixmap) {
168 			struct radeon_pixmap *priv =
169 				calloc(1, sizeof(struct radeon_pixmap));
170 
171 			if (priv) {
172 				radeon_set_pixmap_private(pixmap, priv);
173 				pixmap->usage_hint |= RADEON_CREATE_PIXMAP_DRI2;
174 				return pixmap;
175 			}
176 
177 			screen->DestroyPixmap(pixmap);
178 			return NULL;
179 		}
180 	}
181 #endif
182 
183 	if (depth < 8)
184 		return NULL;
185 
186 	switch (bpp) {
187 	case 8:
188 	case 16:
189 	case 32:
190 		break;
191 	default:
192 		return NULL;
193 	}
194 
195 	pixmap = screen->CreatePixmap(screen, 0, 0, depth, RADEON_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 
206 free_pixmap:
207 	fbDestroyPixmap(pixmap);
208 	return NULL;
209 }
210 
radeon_dri3_fd_from_pixmap(ScreenPtr screen,PixmapPtr pixmap,CARD16 * stride,CARD32 * size)211 static int radeon_dri3_fd_from_pixmap(ScreenPtr screen,
212 				      PixmapPtr pixmap,
213 				      CARD16 *stride,
214 				      CARD32 *size)
215 {
216 	struct radeon_buffer *bo;
217 	int fd;
218 #ifdef USE_GLAMOR
219 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
220 	RADEONInfoPtr info = RADEONPTR(scrn);
221 
222 	if (info->use_glamor) {
223 		int ret = glamor_fd_from_pixmap(screen, pixmap, stride, size);
224 
225 		/* Any pending drawing operations need to be flushed to the
226 		 * kernel driver before the client starts using the pixmap
227 		 * storage for direct rendering.
228 		 */
229 		if (ret >= 0)
230 			radeon_cs_flush_indirect(scrn);
231 
232 		return ret;
233 	}
234 #endif
235 
236 	bo = radeon_get_pixmap_bo(pixmap);
237 	if (!bo) {
238 		exaMoveInPixmap(pixmap);
239 		bo = radeon_get_pixmap_bo(pixmap);
240 		if (!bo)
241 			return -1;
242 	}
243 
244 	if (pixmap->devKind > UINT16_MAX)
245 		return -1;
246 
247 	if (radeon_gem_prime_share_bo(bo->bo.radeon, &fd) < 0)
248 		return -1;
249 
250 	*stride = pixmap->devKind;
251 	*size = bo->bo.radeon->size;
252 	return fd;
253 }
254 
255 static dri3_screen_info_rec radeon_dri3_screen_info = {
256 #if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
257 	.version = 1,
258 	.open_client = radeon_dri3_open_client,
259 #else
260 	.version = 0,
261 	.open = radeon_dri3_open,
262 #endif
263 	.pixmap_from_fd = radeon_dri3_pixmap_from_fd,
264 	.fd_from_pixmap = radeon_dri3_fd_from_pixmap
265 };
266 
267 Bool
radeon_dri3_screen_init(ScreenPtr screen)268 radeon_dri3_screen_init(ScreenPtr screen)
269 {
270 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
271 	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
272 
273 	pRADEONEnt->render_node = drmGetRenderDeviceNameFromFd(pRADEONEnt->fd);
274 
275 	if (!dri3_screen_init(screen, &radeon_dri3_screen_info)) {
276 		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
277 			   "dri3_screen_init failed\n");
278 		return FALSE;
279 	}
280 
281 	return TRUE;
282 }
283 
284 #else /* !HAVE_DRI3_H */
285 
286 Bool
radeon_dri3_screen_init(ScreenPtr screen)287 radeon_dri3_screen_init(ScreenPtr screen)
288 {
289 	xf86DrvMsg(xf86ScreenToScrn(screen)->scrnIndex, X_INFO,
290 		   "Can't initialize DRI3 because dri3.h not available at "
291 		   "build time\n");
292 
293 	return FALSE;
294 }
295 
296 #endif
297