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