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> 4090f2b693Smrg#include <gbm.h> 41d6c0b56eSmrg#include <errno.h> 42d6c0b56eSmrg#include <libgen.h> 43d6c0b56eSmrg 4446845023Smrgstatic int open_card_node(ScreenPtr screen, int *out) 45d6c0b56eSmrg{ 46d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 47d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 48d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(scrn); 49d6c0b56eSmrg drm_magic_t magic; 50d6c0b56eSmrg int fd; 51d6c0b56eSmrg 52d6c0b56eSmrg fd = open(info->dri2.device_name, O_RDWR | O_CLOEXEC); 53d6c0b56eSmrg if (fd < 0) 54d6c0b56eSmrg return BadAlloc; 55d6c0b56eSmrg 56d6c0b56eSmrg /* Before FD passing in the X protocol with DRI3 (and increased 57d6c0b56eSmrg * security of rendering with per-process address spaces on the 58d6c0b56eSmrg * GPU), the kernel had to come up with a way to have the server 59d6c0b56eSmrg * decide which clients got to access the GPU, which was done by 60d6c0b56eSmrg * each client getting a unique (magic) number from the kernel, 61d6c0b56eSmrg * passing it to the server, and the server then telling the 62d6c0b56eSmrg * kernel which clients were authenticated for using the device. 63d6c0b56eSmrg * 64d6c0b56eSmrg * Now that we have FD passing, the server can just set up the 65d6c0b56eSmrg * authentication on its own and hand the prepared FD off to the 66d6c0b56eSmrg * client. 67d6c0b56eSmrg */ 68d6c0b56eSmrg if (drmGetMagic(fd, &magic) < 0) { 69d6c0b56eSmrg if (errno == EACCES) { 70d6c0b56eSmrg /* Assume that we're on a render node, and the fd is 71d6c0b56eSmrg * already as authenticated as it should be. 72d6c0b56eSmrg */ 73d6c0b56eSmrg *out = fd; 74d6c0b56eSmrg return Success; 75d6c0b56eSmrg } else { 76d6c0b56eSmrg close(fd); 77d6c0b56eSmrg return BadMatch; 78d6c0b56eSmrg } 79d6c0b56eSmrg } 80d6c0b56eSmrg 81d6c0b56eSmrg if (drmAuthMagic(pAMDGPUEnt->fd, magic) < 0) { 82d6c0b56eSmrg close(fd); 83d6c0b56eSmrg return BadMatch; 84d6c0b56eSmrg } 85d6c0b56eSmrg 86d6c0b56eSmrg *out = fd; 87d6c0b56eSmrg return Success; 88d6c0b56eSmrg} 89d6c0b56eSmrg 9011bf0794Smrgstatic int open_render_node(ScreenPtr screen, int *out) 9111bf0794Smrg{ 9211bf0794Smrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 9311bf0794Smrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 9411bf0794Smrg int fd; 9511bf0794Smrg 9611bf0794Smrg fd = open(pAMDGPUEnt->render_node, O_RDWR | O_CLOEXEC); 9711bf0794Smrg if (fd < 0) 9811bf0794Smrg return BadAlloc; 9911bf0794Smrg 10011bf0794Smrg *out = fd; 10111bf0794Smrg return Success; 10211bf0794Smrg} 10311bf0794Smrg 10411bf0794Smrgstatic int 10511bf0794Smrgamdgpu_dri3_open(ScreenPtr screen, RRProviderPtr provider, int *out) 10611bf0794Smrg{ 10711bf0794Smrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 10811bf0794Smrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 10911bf0794Smrg int ret = BadAlloc; 11011bf0794Smrg 11111bf0794Smrg if (pAMDGPUEnt->render_node) 11211bf0794Smrg ret = open_render_node(screen, out); 11311bf0794Smrg 11411bf0794Smrg if (ret != Success) 11546845023Smrg ret = open_card_node(screen, out); 11611bf0794Smrg 11711bf0794Smrg return ret; 11811bf0794Smrg} 11911bf0794Smrg 120d6c0b56eSmrg#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0) 121d6c0b56eSmrg 122d6c0b56eSmrgstatic int 123d6c0b56eSmrgamdgpu_dri3_open_client(ClientPtr client, ScreenPtr screen, 124d6c0b56eSmrg RRProviderPtr provider, int *out) 125d6c0b56eSmrg{ 126d6c0b56eSmrg const char *cmdname = GetClientCmdName(client); 127d6c0b56eSmrg Bool is_ssh = FALSE; 128d6c0b56eSmrg 129d6c0b56eSmrg /* If the executable name is "ssh", assume that this client connection 130d6c0b56eSmrg * is forwarded from another host via SSH 131d6c0b56eSmrg */ 132d6c0b56eSmrg if (cmdname) { 133d6c0b56eSmrg char *cmd = strdup(cmdname); 134d6c0b56eSmrg 135d6c0b56eSmrg /* Cut off any colon and whatever comes after it, see 136d6c0b56eSmrg * https://lists.freedesktop.org/archives/xorg-devel/2015-December/048164.html 137d6c0b56eSmrg */ 138d6c0b56eSmrg cmd = strtok(cmd, ":"); 139d6c0b56eSmrg 140d6c0b56eSmrg is_ssh = strcmp(basename(cmd), "ssh") == 0; 141d6c0b56eSmrg free(cmd); 142d6c0b56eSmrg } 143d6c0b56eSmrg 144d6c0b56eSmrg if (!is_ssh) 145d6c0b56eSmrg return amdgpu_dri3_open(screen, provider, out); 146d6c0b56eSmrg 147d6c0b56eSmrg return BadAccess; 148d6c0b56eSmrg} 149d6c0b56eSmrg 150d6c0b56eSmrg#endif /* DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0) */ 151d6c0b56eSmrg 152d6c0b56eSmrgstatic PixmapPtr amdgpu_dri3_pixmap_from_fd(ScreenPtr screen, 153d6c0b56eSmrg int fd, 154d6c0b56eSmrg CARD16 width, 155d6c0b56eSmrg CARD16 height, 156d6c0b56eSmrg CARD16 stride, 157d6c0b56eSmrg CARD8 depth, 158d6c0b56eSmrg CARD8 bpp) 159d6c0b56eSmrg{ 160d6c0b56eSmrg PixmapPtr pixmap; 161d6c0b56eSmrg 162d6c0b56eSmrg#ifdef USE_GLAMOR 163d6c0b56eSmrg /* Avoid generating a GEM flink name if possible */ 164d6c0b56eSmrg if (AMDGPUPTR(xf86ScreenToScrn(screen))->use_glamor) { 165d6c0b56eSmrg pixmap = glamor_pixmap_from_fd(screen, fd, width, height, 166d6c0b56eSmrg stride, depth, bpp); 167d6c0b56eSmrg if (pixmap) { 168d6c0b56eSmrg struct amdgpu_pixmap *priv = calloc(1, sizeof(*priv)); 169d6c0b56eSmrg 170504d986fSmrg if (priv) { 171504d986fSmrg amdgpu_set_pixmap_private(pixmap, priv); 17235d5b7c7Smrg pixmap->usage_hint |= AMDGPU_CREATE_PIXMAP_DRI2; 173504d986fSmrg return pixmap; 174504d986fSmrg } 175504d986fSmrg 176504d986fSmrg screen->DestroyPixmap(pixmap); 177504d986fSmrg return NULL; 178d6c0b56eSmrg } 179d6c0b56eSmrg } 180d6c0b56eSmrg#endif 181d6c0b56eSmrg 182d6c0b56eSmrg if (depth < 8) 183d6c0b56eSmrg return NULL; 184d6c0b56eSmrg 185d6c0b56eSmrg switch (bpp) { 186d6c0b56eSmrg case 8: 187d6c0b56eSmrg case 16: 188d6c0b56eSmrg case 32: 189d6c0b56eSmrg break; 190d6c0b56eSmrg default: 191d6c0b56eSmrg return NULL; 192d6c0b56eSmrg } 193d6c0b56eSmrg 194d6c0b56eSmrg pixmap = screen->CreatePixmap(screen, 0, 0, depth, 195d6c0b56eSmrg AMDGPU_CREATE_PIXMAP_DRI2); 196d6c0b56eSmrg if (!pixmap) 197d6c0b56eSmrg return NULL; 198d6c0b56eSmrg 199d6c0b56eSmrg if (!screen->ModifyPixmapHeader(pixmap, width, height, 0, bpp, stride, 200d6c0b56eSmrg NULL)) 201d6c0b56eSmrg goto free_pixmap; 202d6c0b56eSmrg 203d6c0b56eSmrg if (screen->SetSharedPixmapBacking(pixmap, (void*)(intptr_t)fd)) 204d6c0b56eSmrg return pixmap; 205d6c0b56eSmrg 206d6c0b56eSmrgfree_pixmap: 207d6c0b56eSmrg fbDestroyPixmap(pixmap); 208d6c0b56eSmrg return NULL; 209d6c0b56eSmrg} 210d6c0b56eSmrg 211d6c0b56eSmrgstatic int amdgpu_dri3_fd_from_pixmap(ScreenPtr screen, 212d6c0b56eSmrg PixmapPtr pixmap, 213d6c0b56eSmrg CARD16 *stride, 214d6c0b56eSmrg CARD32 *size) 215d6c0b56eSmrg{ 216d6c0b56eSmrg struct amdgpu_buffer *bo; 217d6c0b56eSmrg struct amdgpu_bo_info bo_info; 218d6c0b56eSmrg uint32_t fd; 219d6c0b56eSmrg#ifdef USE_GLAMOR 220d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 221d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(scrn); 222d6c0b56eSmrg 22390f2b693Smrg if (info->use_glamor) { 22477d6d1ecSmrg int ret = glamor_fd_from_pixmap(screen, pixmap, stride, size); 22590f2b693Smrg 22677d6d1ecSmrg /* Any pending drawing operations need to be flushed to the 22777d6d1ecSmrg * kernel driver before the client starts using the pixmap 22877d6d1ecSmrg * storage for direct rendering. 22990f2b693Smrg */ 23077d6d1ecSmrg if (ret >= 0) 23190f2b693Smrg amdgpu_glamor_flush(scrn); 23290f2b693Smrg 23390f2b693Smrg return ret; 23490f2b693Smrg } 235d6c0b56eSmrg#endif 236d6c0b56eSmrg 237d6c0b56eSmrg bo = amdgpu_get_pixmap_bo(pixmap); 238d6c0b56eSmrg if (!bo) 239d6c0b56eSmrg return -1; 240d6c0b56eSmrg 241d6c0b56eSmrg if (pixmap->devKind > UINT16_MAX) 242d6c0b56eSmrg return -1; 243d6c0b56eSmrg 244d6c0b56eSmrg if (amdgpu_bo_query_info(bo->bo.amdgpu, &bo_info) != 0) 245d6c0b56eSmrg return -1; 246d6c0b56eSmrg 247d6c0b56eSmrg if (amdgpu_bo_export(bo->bo.amdgpu, amdgpu_bo_handle_type_dma_buf_fd, 248d6c0b56eSmrg &fd) != 0) 249d6c0b56eSmrg return -1; 250d6c0b56eSmrg 251d6c0b56eSmrg *stride = pixmap->devKind; 252d6c0b56eSmrg *size = bo_info.alloc_size; 253d6c0b56eSmrg return fd; 254d6c0b56eSmrg} 255d6c0b56eSmrg 256d6c0b56eSmrgstatic dri3_screen_info_rec amdgpu_dri3_screen_info = { 257d6c0b56eSmrg#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0) 258d6c0b56eSmrg .version = 1, 259d6c0b56eSmrg .open_client = amdgpu_dri3_open_client, 260d6c0b56eSmrg#else 261d6c0b56eSmrg .version = 0, 262d6c0b56eSmrg .open = amdgpu_dri3_open, 263d6c0b56eSmrg#endif 264d6c0b56eSmrg .pixmap_from_fd = amdgpu_dri3_pixmap_from_fd, 265d6c0b56eSmrg .fd_from_pixmap = amdgpu_dri3_fd_from_pixmap 266d6c0b56eSmrg}; 267d6c0b56eSmrg 268d6c0b56eSmrgBool 269d6c0b56eSmrgamdgpu_dri3_screen_init(ScreenPtr screen) 270d6c0b56eSmrg{ 271d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 27211bf0794Smrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 27311bf0794Smrg 27411bf0794Smrg pAMDGPUEnt->render_node = drmGetRenderDeviceNameFromFd(pAMDGPUEnt->fd); 275d6c0b56eSmrg 276d6c0b56eSmrg if (!dri3_screen_init(screen, &amdgpu_dri3_screen_info)) { 277d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 278d6c0b56eSmrg "dri3_screen_init failed\n"); 279d6c0b56eSmrg return FALSE; 280d6c0b56eSmrg } 281d6c0b56eSmrg 282d6c0b56eSmrg return TRUE; 283d6c0b56eSmrg} 284d6c0b56eSmrg 285d6c0b56eSmrg#else /* !HAVE_DRI3_H */ 286d6c0b56eSmrg 287d6c0b56eSmrgBool 288d6c0b56eSmrgamdgpu_dri3_screen_init(ScreenPtr screen) 289d6c0b56eSmrg{ 290d6c0b56eSmrg xf86DrvMsg(xf86ScreenToScrn(screen)->scrnIndex, X_INFO, 291d6c0b56eSmrg "Can't initialize DRI3 because dri3.h not available at " 292d6c0b56eSmrg "build time\n"); 293d6c0b56eSmrg 294d6c0b56eSmrg return FALSE; 295d6c0b56eSmrg} 296d6c0b56eSmrg 297d6c0b56eSmrg#endif 298