|  | /* | 
|  | * 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 "qemu/osdep.h" | 
|  | #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); | 
|  | } | 
|  |  | 
|  | void | 
|  | vugbm_device_init(struct vugbm_device *dev, int fd) | 
|  | { | 
|  | assert(!dev->inited); | 
|  |  | 
|  | #ifdef CONFIG_GBM | 
|  | if (fd >= 0) { | 
|  | dev->dev = gbm_create_device(fd); | 
|  | } | 
|  | if (dev->dev != NULL) { | 
|  | dev->fd = fd; | 
|  | 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; | 
|  | dev->inited = true; | 
|  | } | 
|  | #endif | 
|  | #ifdef CONFIG_MEMFD | 
|  | if (!dev->inited && g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) { | 
|  | dev->fd = open("/dev/udmabuf", O_RDWR); | 
|  | if (dev->fd >= 0) { | 
|  | 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; | 
|  | dev->inited = true; | 
|  | } | 
|  | } | 
|  | #endif | 
|  | if (!dev->inited) { | 
|  | 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; | 
|  | dev->inited = true; | 
|  | } | 
|  | assert(dev->inited); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } |