/* * Virtio vhost-user GPU Device * * DRM helpers * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #include "vugbm.h" static bool mem_alloc_bo(struct vugbm_buffer *buf) { buf->mmap = g_malloc(buf->width * buf->height * 4); buf->stride = buf->width * 4; return true; } static void mem_free_bo(struct vugbm_buffer *buf) { g_free(buf->mmap); } static bool mem_map_bo(struct vugbm_buffer *buf) { return buf->mmap != NULL; } static void mem_unmap_bo(struct vugbm_buffer *buf) { } static void mem_device_destroy(struct vugbm_device *dev) { } #ifdef CONFIG_MEMFD struct udmabuf_create { uint32_t memfd; uint32_t flags; uint64_t offset; uint64_t size; }; #define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create) static size_t udmabuf_get_size(struct vugbm_buffer *buf) { return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size); } static bool udmabuf_alloc_bo(struct vugbm_buffer *buf) { int ret; buf->memfd = memfd_create("udmabuf-bo", MFD_ALLOW_SEALING); if (buf->memfd < 0) { return false; } ret = ftruncate(buf->memfd, udmabuf_get_size(buf)); if (ret < 0) { close(buf->memfd); return false; } ret = fcntl(buf->memfd, F_ADD_SEALS, F_SEAL_SHRINK); if (ret < 0) { close(buf->memfd); return false; } buf->stride = buf->width * 4; return true; } static void udmabuf_free_bo(struct vugbm_buffer *buf) { close(buf->memfd); } static bool udmabuf_map_bo(struct vugbm_buffer *buf) { buf->mmap = mmap(NULL, udmabuf_get_size(buf), PROT_READ | PROT_WRITE, MAP_SHARED, buf->memfd, 0); if (buf->mmap == MAP_FAILED) { return false; } return true; } static bool udmabuf_get_fd(struct vugbm_buffer *buf, int *fd) { struct udmabuf_create create = { .memfd = buf->memfd, .offset = 0, .size = udmabuf_get_size(buf), }; *fd = ioctl(buf->dev->fd, UDMABUF_CREATE, &create); return *fd >= 0; } static void udmabuf_unmap_bo(struct vugbm_buffer *buf) { munmap(buf->mmap, udmabuf_get_size(buf)); } static void udmabuf_device_destroy(struct vugbm_device *dev) { close(dev->fd); } #endif #ifdef CONFIG_GBM static bool alloc_bo(struct vugbm_buffer *buf) { struct gbm_device *dev = buf->dev->dev; assert(!buf->bo); buf->bo = gbm_bo_create(dev, buf->width, buf->height, buf->format, GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR); if (buf->bo) { buf->stride = gbm_bo_get_stride(buf->bo); return true; } return false; } static void free_bo(struct vugbm_buffer *buf) { gbm_bo_destroy(buf->bo); } static bool map_bo(struct vugbm_buffer *buf) { uint32_t stride; buf->mmap = gbm_bo_map(buf->bo, 0, 0, buf->width, buf->height, GBM_BO_TRANSFER_READ_WRITE, &stride, &buf->mmap_data); assert(stride == buf->stride); return buf->mmap != NULL; } static void unmap_bo(struct vugbm_buffer *buf) { gbm_bo_unmap(buf->bo, buf->mmap_data); } static bool get_fd(struct vugbm_buffer *buf, int *fd) { *fd = gbm_bo_get_fd(buf->bo); return *fd >= 0; } static void device_destroy(struct vugbm_device *dev) { gbm_device_destroy(dev->dev); } #endif void vugbm_device_destroy(struct vugbm_device *dev) { if (!dev->inited) { return; } dev->device_destroy(dev); } bool vugbm_device_init(struct vugbm_device *dev, int fd) { dev->fd = fd; #ifdef CONFIG_GBM dev->dev = gbm_create_device(fd); #endif if (0) { /* nothing */ } #ifdef CONFIG_GBM else if (dev->dev != NULL) { dev->alloc_bo = alloc_bo; dev->free_bo = free_bo; dev->get_fd = get_fd; dev->map_bo = map_bo; dev->unmap_bo = unmap_bo; dev->device_destroy = device_destroy; } #endif #ifdef CONFIG_MEMFD else if (g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) { dev->fd = open("/dev/udmabuf", O_RDWR); if (dev->fd < 0) { return false; } g_debug("Using experimental udmabuf backend"); dev->alloc_bo = udmabuf_alloc_bo; dev->free_bo = udmabuf_free_bo; dev->get_fd = udmabuf_get_fd; dev->map_bo = udmabuf_map_bo; dev->unmap_bo = udmabuf_unmap_bo; dev->device_destroy = udmabuf_device_destroy; } #endif else { g_debug("Using mem fallback"); dev->alloc_bo = mem_alloc_bo; dev->free_bo = mem_free_bo; dev->map_bo = mem_map_bo; dev->unmap_bo = mem_unmap_bo; dev->device_destroy = mem_device_destroy; return false; } dev->inited = true; return true; } static bool vugbm_buffer_map(struct vugbm_buffer *buf) { struct vugbm_device *dev = buf->dev; return dev->map_bo(buf); } static void vugbm_buffer_unmap(struct vugbm_buffer *buf) { struct vugbm_device *dev = buf->dev; dev->unmap_bo(buf); } bool vugbm_buffer_can_get_dmabuf_fd(struct vugbm_buffer *buffer) { if (!buffer->dev->get_fd) { return false; } return true; } bool vugbm_buffer_get_dmabuf_fd(struct vugbm_buffer *buffer, int *fd) { if (!vugbm_buffer_can_get_dmabuf_fd(buffer) || !buffer->dev->get_fd(buffer, fd)) { g_warning("Failed to get dmabuf"); return false; } if (*fd < 0) { g_warning("error: dmabuf_fd < 0"); return false; } return true; } bool vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev, uint32_t width, uint32_t height) { buffer->dev = dev; buffer->width = width; buffer->height = height; buffer->format = GBM_FORMAT_XRGB8888; buffer->stride = 0; /* modified during alloc */ if (!dev->alloc_bo(buffer)) { g_warning("alloc_bo failed"); return false; } if (!vugbm_buffer_map(buffer)) { g_warning("map_bo failed"); goto err; } return true; err: dev->free_bo(buffer); return false; } void vugbm_buffer_destroy(struct vugbm_buffer *buffer) { struct vugbm_device *dev = buffer->dev; vugbm_buffer_unmap(buffer); dev->free_bo(buffer); }