/* * Copyright © 2013 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include #include #include "dix/dix_priv.h" #include "os/client_priv.h" #include "dri3_priv.h" #include #include #include "../Xext/syncsdk.h" #include #include #include "randrstr_priv.h" #include "dixstruct_priv.h" static Bool dri3_screen_can_one_point_four(ScreenPtr screen) { dri3_screen_priv_ptr dri3 = dri3_screen_priv(screen); return dri3 && dri3->info && dri3->info->version >= 4 && dri3->info->import_syncobj; } static Bool dri3_screen_can_one_point_two(ScreenPtr screen) { dri3_screen_priv_ptr dri3 = dri3_screen_priv(screen); if (dri3 && dri3->info && dri3->info->version >= 2 && dri3->info->pixmap_from_fds && dri3->info->fds_from_pixmap && dri3->info->get_formats && dri3->info->get_modifiers && dri3->info->get_drawable_modifiers) return TRUE; return FALSE; } static int proc_dri3_query_version(ClientPtr client) { REQUEST(xDRI3QueryVersionReq); xDRI3QueryVersionReply rep = { .type = X_Reply, .sequenceNumber = client->sequence, .majorVersion = SERVER_DRI3_MAJOR_VERSION, .minorVersion = SERVER_DRI3_MINOR_VERSION }; REQUEST_SIZE_MATCH(xDRI3QueryVersionReq); for (int i = 0; i < screenInfo.numScreens; i++) { if (!dri3_screen_can_one_point_two(screenInfo.screens[i])) { rep.minorVersion = 0; break; } if (!dri3_screen_can_one_point_four(screenInfo.screens[i])) { rep.minorVersion = 2; break; } } for (int i = 0; i < screenInfo.numGPUScreens; i++) { if (!dri3_screen_can_one_point_two(screenInfo.gpuscreens[i])) { rep.minorVersion = 0; break; } if (!dri3_screen_can_one_point_four(screenInfo.gpuscreens[i])) { rep.minorVersion = 2; break; } } /* From DRI3 proto: * * The client sends the highest supported version to the server * and the server sends the highest version it supports, but no * higher than the requested version. */ if (rep.majorVersion > stuff->majorVersion || (rep.majorVersion == stuff->majorVersion && rep.minorVersion > stuff->minorVersion)) { rep.majorVersion = stuff->majorVersion; rep.minorVersion = stuff->minorVersion; } if (client->swapped) { swaps(&rep.sequenceNumber); swapl(&rep.length); swapl(&rep.majorVersion); swapl(&rep.minorVersion); } WriteToClient(client, sizeof(rep), &rep); return Success; } int dri3_send_open_reply(ClientPtr client, int fd) { xDRI3OpenReply rep = { .type = X_Reply, .nfd = 1, .sequenceNumber = client->sequence, }; if (client->swapped) { swaps(&rep.sequenceNumber); swapl(&rep.length); } if (WriteFdToClient(client, fd, TRUE) < 0) { close(fd); return BadAlloc; } WriteToClient(client, sizeof (rep), &rep); return Success; } static int proc_dri3_open(ClientPtr client) { REQUEST(xDRI3OpenReq); RRProviderPtr provider; DrawablePtr drawable; ScreenPtr screen; int fd; int status; REQUEST_SIZE_MATCH(xDRI3OpenReq); status = dixLookupDrawable(&drawable, stuff->drawable, client, 0, DixGetAttrAccess); if (status != Success) return status; if (stuff->provider == None) provider = NULL; else if (!RRProviderType) { return BadMatch; } else { VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess); if (drawable->pScreen != provider->pScreen) return BadMatch; } screen = drawable->pScreen; status = dri3_open(client, screen, provider, &fd); if (status != Success) return status; if (client->ignoreCount == 0) return dri3_send_open_reply(client, fd); return Success; } static int proc_dri3_pixmap_from_buffer(ClientPtr client) { REQUEST(xDRI3PixmapFromBufferReq); int fd; DrawablePtr drawable; PixmapPtr pixmap; CARD32 stride, offset; int rc; SetReqFds(client, 1); REQUEST_SIZE_MATCH(xDRI3PixmapFromBufferReq); LEGAL_NEW_RESOURCE(stuff->pixmap, client); rc = dixLookupDrawable(&drawable, stuff->drawable, client, M_ANY, DixGetAttrAccess); if (rc != Success) { client->errorValue = stuff->drawable; return rc; } if (!stuff->width || !stuff->height) { client->errorValue = 0; return BadValue; } if (stuff->width > 32767 || stuff->height > 32767) return BadAlloc; if (stuff->depth != 1) { DepthPtr depth = drawable->pScreen->allowedDepths; int i; for (i = 0; i < drawable->pScreen->numDepths; i++, depth++) if (depth->depth == stuff->depth) break; if (i == drawable->pScreen->numDepths) { client->errorValue = stuff->depth; return BadValue; } } fd = ReadFdFromClient(client); if (fd < 0) return BadValue; offset = 0; stride = stuff->stride; rc = dri3_pixmap_from_fds(&pixmap, drawable->pScreen, 1, &fd, stuff->width, stuff->height, &stride, &offset, stuff->depth, stuff->bpp, DRM_FORMAT_MOD_INVALID); close (fd); if (rc != Success) return rc; pixmap->drawable.id = stuff->pixmap; /* security creation/labeling check */ rc = XaceHookResourceAccess(client, stuff->pixmap, X11_RESTYPE_PIXMAP, pixmap, X11_RESTYPE_NONE, NULL, DixCreateAccess); if (rc != Success) { dixDestroyPixmap(pixmap, 0); return rc; } if (!AddResource(stuff->pixmap, X11_RESTYPE_PIXMAP, (void *) pixmap)) return BadAlloc; return Success; } static int proc_dri3_buffer_from_pixmap(ClientPtr client) { REQUEST(xDRI3BufferFromPixmapReq); int rc; int fd; PixmapPtr pixmap; REQUEST_SIZE_MATCH(xDRI3BufferFromPixmapReq); rc = dixLookupResourceByType((void **) &pixmap, stuff->pixmap, X11_RESTYPE_PIXMAP, client, DixWriteAccess); if (rc != Success) { client->errorValue = stuff->pixmap; return rc; } xDRI3BufferFromPixmapReply rep = { .type = X_Reply, .nfd = 1, .sequenceNumber = client->sequence, .width = pixmap->drawable.width, .height = pixmap->drawable.height, .depth = pixmap->drawable.depth, .bpp = pixmap->drawable.bitsPerPixel, }; fd = dri3_fd_from_pixmap(pixmap, &rep.stride, &rep.size); if (fd < 0) return BadPixmap; if (client->swapped) { swaps(&rep.sequenceNumber); swapl(&rep.length); swapl(&rep.size); swaps(&rep.width); swaps(&rep.height); swaps(&rep.stride); } if (WriteFdToClient(client, fd, TRUE) < 0) { close(fd); return BadAlloc; } WriteToClient(client, sizeof(rep), &rep); return Success; } static int proc_dri3_fence_from_fd(ClientPtr client) { REQUEST(xDRI3FenceFromFDReq); DrawablePtr drawable; int fd; int status; SetReqFds(client, 1); REQUEST_SIZE_MATCH(xDRI3FenceFromFDReq); LEGAL_NEW_RESOURCE(stuff->fence, client); status = dixLookupDrawable(&drawable, stuff->drawable, client, M_ANY, DixGetAttrAccess); if (status != Success) return status; fd = ReadFdFromClient(client); if (fd < 0) return BadValue; status = SyncCreateFenceFromFD(client, drawable, stuff->fence, fd, stuff->initially_triggered); return status; } static int proc_dri3_fd_from_fence(ClientPtr client) { REQUEST(xDRI3FDFromFenceReq); xDRI3FDFromFenceReply rep = { .type = X_Reply, .nfd = 1, .sequenceNumber = client->sequence, }; DrawablePtr drawable; int fd; int status; SyncFence *fence; REQUEST_SIZE_MATCH(xDRI3FDFromFenceReq); status = dixLookupDrawable(&drawable, stuff->drawable, client, M_ANY, DixGetAttrAccess); if (status != Success) return status; status = SyncVerifyFence(&fence, stuff->fence, client, DixWriteAccess); if (status != Success) return status; fd = SyncFDFromFence(client, drawable, fence); if (fd < 0) return BadMatch; if (client->swapped) { swaps(&rep.sequenceNumber); swapl(&rep.length); } if (WriteFdToClient(client, fd, FALSE) < 0) return BadAlloc; WriteToClient(client, sizeof(rep), &rep); return Success; } static int proc_dri3_get_supported_modifiers(ClientPtr client) { REQUEST(xDRI3GetSupportedModifiersReq); WindowPtr window; ScreenPtr pScreen; CARD64 *window_modifiers = NULL; CARD64 *screen_modifiers = NULL; CARD32 nwindowmodifiers = 0; CARD32 nscreenmodifiers = 0; int status; int i; REQUEST_SIZE_MATCH(xDRI3GetSupportedModifiersReq); status = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess); if (status != Success) return status; pScreen = window->drawable.pScreen; dri3_get_supported_modifiers(pScreen, &window->drawable, stuff->depth, stuff->bpp, &nwindowmodifiers, &window_modifiers, &nscreenmodifiers, &screen_modifiers); const size_t bufsz = (nwindowmodifiers + nscreenmodifiers) * sizeof(CARD64); CARD64 *buf = calloc(1, bufsz); if (!buf) { free(window_modifiers); free(screen_modifiers); return BadAlloc; } memcpy(buf, window_modifiers, sizeof(CARD64) * nwindowmodifiers); memcpy(&buf[nwindowmodifiers], screen_modifiers, sizeof(CARD64) * nscreenmodifiers); free(window_modifiers); free(screen_modifiers); xDRI3GetSupportedModifiersReply rep = { .type = X_Reply, .sequenceNumber = client->sequence, .numWindowModifiers = nwindowmodifiers, .numScreenModifiers = nscreenmodifiers, .length = bytes_to_int32(bufsz), }; if (client->swapped) { swaps(&rep.sequenceNumber); swapl(&rep.length); swapl(&rep.numWindowModifiers); swapl(&rep.numScreenModifiers); for (i = 0; i < nwindowmodifiers+nscreenmodifiers; i++) swapll(&buf[i]); } WriteToClient(client, sizeof(rep), &rep); WriteToClient(client, bufsz, buf); free(buf); return Success; } static int proc_dri3_pixmap_from_buffers(ClientPtr client) { REQUEST(xDRI3PixmapFromBuffersReq); int fds[4]; CARD32 strides[4], offsets[4]; ScreenPtr screen; WindowPtr window; PixmapPtr pixmap; int rc; int i; SetReqFds(client, stuff->num_buffers); REQUEST_SIZE_MATCH(xDRI3PixmapFromBuffersReq); LEGAL_NEW_RESOURCE(stuff->pixmap, client); rc = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess); if (rc != Success) { client->errorValue = stuff->window; return rc; } screen = window->drawable.pScreen; if (!stuff->width || !stuff->height || !stuff->bpp || !stuff->depth) { client->errorValue = 0; return BadValue; } if (stuff->width > 32767 || stuff->height > 32767) return BadAlloc; if (stuff->depth != 1) { DepthPtr depth = screen->allowedDepths; int j; for (j = 0; j < screen->numDepths; j++, depth++) if (depth->depth == stuff->depth) break; if (j == screen->numDepths) { client->errorValue = stuff->depth; return BadValue; } } if (!stuff->num_buffers || stuff->num_buffers > 4) { client->errorValue = stuff->num_buffers; return BadValue; } for (i = 0; i < stuff->num_buffers; i++) { fds[i] = ReadFdFromClient(client); if (fds[i] < 0) { while (--i >= 0) close(fds[i]); return BadValue; } } strides[0] = stuff->stride0; strides[1] = stuff->stride1; strides[2] = stuff->stride2; strides[3] = stuff->stride3; offsets[0] = stuff->offset0; offsets[1] = stuff->offset1; offsets[2] = stuff->offset2; offsets[3] = stuff->offset3; rc = dri3_pixmap_from_fds(&pixmap, screen, stuff->num_buffers, fds, stuff->width, stuff->height, strides, offsets, stuff->depth, stuff->bpp, stuff->modifier); for (i = 0; i < stuff->num_buffers; i++) close (fds[i]); if (rc != Success) return rc; pixmap->drawable.id = stuff->pixmap; /* security creation/labeling check */ rc = XaceHookResourceAccess(client, stuff->pixmap, X11_RESTYPE_PIXMAP, pixmap, X11_RESTYPE_NONE, NULL, DixCreateAccess); if (rc != Success) { dixDestroyPixmap(pixmap, 0); return rc; } if (!AddResource(stuff->pixmap, X11_RESTYPE_PIXMAP, (void *) pixmap)) return BadAlloc; return Success; } static int proc_dri3_buffers_from_pixmap(ClientPtr client) { REQUEST(xDRI3BuffersFromPixmapReq); int rc; int fds[4]; int num_fds; uint32_t strides[4], offsets[4]; uint64_t modifier; int i; PixmapPtr pixmap; REQUEST_SIZE_MATCH(xDRI3BuffersFromPixmapReq); rc = dixLookupResourceByType((void **) &pixmap, stuff->pixmap, X11_RESTYPE_PIXMAP, client, DixWriteAccess); if (rc != Success) { client->errorValue = stuff->pixmap; return rc; } num_fds = dri3_fds_from_pixmap(pixmap, fds, strides, offsets, &modifier); if (num_fds == 0) return BadPixmap; for (i = 0; i < num_fds; i++) { if (WriteFdToClient(client, fds[i], TRUE) < 0) { while (i--) close(fds[i]); return BadAlloc; } } const size_t bufsz = num_fds * 2 * sizeof(CARD32); CARD32 *buf = calloc(1, bufsz); if (!buf) return BadAlloc; memcpy(buf, strides, num_fds * sizeof(CARD32)); memcpy(&buf[num_fds], offsets, num_fds * sizeof(CARD32)); xDRI3BuffersFromPixmapReply rep = { .type = X_Reply, .sequenceNumber = client->sequence, .nfd = num_fds, .length = bytes_to_int32(bufsz), .width = pixmap->drawable.width, .height = pixmap->drawable.height, .depth = pixmap->drawable.depth, .bpp = pixmap->drawable.bitsPerPixel, .modifier = modifier, }; if (client->swapped) { swaps(&rep.sequenceNumber); swapl(&rep.length); swaps(&rep.width); swaps(&rep.height); swapll(&rep.modifier); for (i = 0; i < num_fds * 2; i++) swapl(&buf[i]); } WriteToClient(client, sizeof(rep), &rep); WriteToClient(client, bufsz, buf); free(buf); return Success; } static int proc_dri3_set_drm_device_in_use(ClientPtr client) { REQUEST(xDRI3SetDRMDeviceInUseReq); WindowPtr window; int status; REQUEST_SIZE_MATCH(xDRI3SetDRMDeviceInUseReq); status = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess); if (status != Success) return status; /* TODO Eventually we should use this information to have * DRI3GetSupportedModifiers return device-specific modifiers, but for now * we will ignore it until multi-device support is more complete. * Otherwise we can't advertise support for DRI3 1.4. */ return Success; } static int proc_dri3_import_syncobj(ClientPtr client) { REQUEST(xDRI3ImportSyncobjReq); DrawablePtr drawable; ScreenPtr screen; int fd; int status; SetReqFds(client, 1); REQUEST_SIZE_MATCH(xDRI3ImportSyncobjReq); LEGAL_NEW_RESOURCE(stuff->syncobj, client); status = dixLookupDrawable(&drawable, stuff->drawable, client, M_ANY, DixGetAttrAccess); if (status != Success) return status; screen = drawable->pScreen; fd = ReadFdFromClient(client); if (fd < 0) return BadValue; return dri3_import_syncobj(client, screen, stuff->syncobj, fd); } static int proc_dri3_free_syncobj(ClientPtr client) { REQUEST(xDRI3FreeSyncobjReq); struct dri3_syncobj *syncobj; int status; REQUEST_SIZE_MATCH(xDRI3FreeSyncobjReq); status = dixLookupResourceByType((void **) &syncobj, stuff->syncobj, dri3_syncobj_type, client, DixWriteAccess); if (status != Success) return status; FreeResource(stuff->syncobj, RT_NONE); return Success; } int proc_dri3_dispatch(ClientPtr client) { REQUEST(xReq); if (!client->local) return BadMatch; switch (stuff->data) { case X_DRI3QueryVersion: return proc_dri3_query_version(client); case X_DRI3Open: return proc_dri3_open(client); case X_DRI3PixmapFromBuffer: return proc_dri3_pixmap_from_buffer(client); case X_DRI3BufferFromPixmap: return proc_dri3_buffer_from_pixmap(client); case X_DRI3FenceFromFD: return proc_dri3_fence_from_fd(client); case X_DRI3FDFromFence: return proc_dri3_fd_from_fence(client); /* v1.2 */ case xDRI3GetSupportedModifiers: return proc_dri3_get_supported_modifiers(client); case xDRI3PixmapFromBuffers: return proc_dri3_pixmap_from_buffers(client); case xDRI3BuffersFromPixmap: return proc_dri3_buffers_from_pixmap(client); /* v1.3 */ case xDRI3SetDRMDeviceInUse: return proc_dri3_set_drm_device_in_use(client); /* v1.4 */ case xDRI3ImportSyncobj: return proc_dri3_import_syncobj(client); case xDRI3FreeSyncobj: return proc_dri3_free_syncobj(client); default: return BadRequest; } } static int _X_COLD sproc_dri3_query_version(ClientPtr client) { REQUEST(xDRI3QueryVersionReq); REQUEST_SIZE_MATCH(xDRI3QueryVersionReq); swapl(&stuff->majorVersion); swapl(&stuff->minorVersion); return proc_dri3_query_version(client); } static int _X_COLD sproc_dri3_open(ClientPtr client) { REQUEST(xDRI3OpenReq); REQUEST_SIZE_MATCH(xDRI3OpenReq); swapl(&stuff->drawable); swapl(&stuff->provider); return proc_dri3_open(client); } static int _X_COLD sproc_dri3_pixmap_from_buffer(ClientPtr client) { REQUEST(xDRI3PixmapFromBufferReq); REQUEST_SIZE_MATCH(xDRI3PixmapFromBufferReq); swapl(&stuff->pixmap); swapl(&stuff->drawable); swapl(&stuff->size); swaps(&stuff->width); swaps(&stuff->height); swaps(&stuff->stride); return proc_dri3_pixmap_from_buffer(client); } static int _X_COLD sproc_dri3_buffer_from_pixmap(ClientPtr client) { REQUEST(xDRI3BufferFromPixmapReq); REQUEST_SIZE_MATCH(xDRI3BufferFromPixmapReq); swapl(&stuff->pixmap); return proc_dri3_buffer_from_pixmap(client); } static int _X_COLD sproc_dri3_fence_from_fd(ClientPtr client) { REQUEST(xDRI3FenceFromFDReq); REQUEST_SIZE_MATCH(xDRI3FenceFromFDReq); swapl(&stuff->drawable); swapl(&stuff->fence); return proc_dri3_fence_from_fd(client); } static int _X_COLD sproc_dri3_fd_from_fence(ClientPtr client) { REQUEST(xDRI3FDFromFenceReq); REQUEST_SIZE_MATCH(xDRI3FDFromFenceReq); swapl(&stuff->drawable); swapl(&stuff->fence); return proc_dri3_fd_from_fence(client); } static int _X_COLD sproc_dri3_get_supported_modifiers(ClientPtr client) { REQUEST(xDRI3GetSupportedModifiersReq); REQUEST_SIZE_MATCH(xDRI3GetSupportedModifiersReq); swapl(&stuff->window); return proc_dri3_get_supported_modifiers(client); } static int _X_COLD sproc_dri3_pixmap_from_buffers(ClientPtr client) { REQUEST(xDRI3PixmapFromBuffersReq); REQUEST_SIZE_MATCH(xDRI3PixmapFromBuffersReq); swapl(&stuff->pixmap); swapl(&stuff->window); swaps(&stuff->width); swaps(&stuff->height); swapl(&stuff->stride0); swapl(&stuff->offset0); swapl(&stuff->stride1); swapl(&stuff->offset1); swapl(&stuff->stride2); swapl(&stuff->offset2); swapl(&stuff->stride3); swapl(&stuff->offset3); swapll(&stuff->modifier); return proc_dri3_pixmap_from_buffers(client); } static int _X_COLD sproc_dri3_buffers_from_pixmap(ClientPtr client) { REQUEST(xDRI3BuffersFromPixmapReq); REQUEST_SIZE_MATCH(xDRI3BuffersFromPixmapReq); swapl(&stuff->pixmap); return proc_dri3_buffers_from_pixmap(client); } static int _X_COLD sproc_dri3_set_drm_device_in_use(ClientPtr client) { REQUEST(xDRI3SetDRMDeviceInUseReq); REQUEST_SIZE_MATCH(xDRI3SetDRMDeviceInUseReq); swapl(&stuff->window); swapl(&stuff->drmMajor); swapl(&stuff->drmMinor); return proc_dri3_set_drm_device_in_use(client); } static int _X_COLD sproc_dri3_import_syncobj(ClientPtr client) { REQUEST(xDRI3ImportSyncobjReq); REQUEST_SIZE_MATCH(xDRI3ImportSyncobjReq); swapl(&stuff->syncobj); swapl(&stuff->drawable); return proc_dri3_import_syncobj(client); } static int _X_COLD sproc_dri3_free_syncobj(ClientPtr client) { REQUEST(xDRI3FreeSyncobjReq); REQUEST_SIZE_MATCH(xDRI3FreeSyncobjReq); swapl(&stuff->syncobj); return proc_dri3_free_syncobj(client); } int _X_COLD sproc_dri3_dispatch(ClientPtr client) { REQUEST(xReq); if (!client->local) return BadMatch; switch (stuff->data) { case X_DRI3QueryVersion: return sproc_dri3_query_version(client); case X_DRI3Open: return sproc_dri3_open(client); case X_DRI3PixmapFromBuffer: return sproc_dri3_pixmap_from_buffer(client); case X_DRI3BufferFromPixmap: return sproc_dri3_buffer_from_pixmap(client); case X_DRI3FenceFromFD: return sproc_dri3_fence_from_fd(client); case X_DRI3FDFromFence: return sproc_dri3_fd_from_fence(client); /* v1.2 */ case xDRI3GetSupportedModifiers: return sproc_dri3_get_supported_modifiers(client); case xDRI3PixmapFromBuffers: return sproc_dri3_pixmap_from_buffers(client); case xDRI3BuffersFromPixmap: return sproc_dri3_buffers_from_pixmap(client); /* v1.3 */ case xDRI3SetDRMDeviceInUse: return sproc_dri3_set_drm_device_in_use(client); /* v1.4 */ case xDRI3ImportSyncobj: return sproc_dri3_import_syncobj(client); case xDRI3FreeSyncobj: return sproc_dri3_free_syncobj(client); default: return BadRequest; } }