diff options
Diffstat (limited to 'hw/display/virtio-gpu-udmabuf.c')
-rw-r--r-- | hw/display/virtio-gpu-udmabuf.c | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c new file mode 100644 index 0000000000..71c4672e32 --- /dev/null +++ b/hw/display/virtio-gpu-udmabuf.c @@ -0,0 +1,158 @@ +/* + * Virtio GPU Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * Gerd Hoffmann <kraxel@redhat.com> + * + * 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 "qemu/units.h" +#include "qemu-common.h" +#include "qemu/iov.h" +#include "ui/console.h" +#include "hw/virtio/virtio-gpu.h" +#include "hw/virtio/virtio-gpu-pixman.h" +#include "trace.h" +#include "exec/ramblock.h" +#include "sysemu/hostmem.h" +#include <sys/ioctl.h> +#include <fcntl.h> +#include <linux/memfd.h> +#include "qemu/memfd.h" +#include "standard-headers/linux/udmabuf.h" + +static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource *res) +{ + struct udmabuf_create_list *list; + RAMBlock *rb; + ram_addr_t offset; + int udmabuf, i; + + udmabuf = udmabuf_fd(); + if (udmabuf < 0) { + return; + } + + list = g_malloc0(sizeof(struct udmabuf_create_list) + + sizeof(struct udmabuf_create_item) * res->iov_cnt); + + for (i = 0; i < res->iov_cnt; i++) { + rcu_read_lock(); + rb = qemu_ram_block_from_host(res->iov[i].iov_base, false, &offset); + rcu_read_unlock(); + + if (!rb || rb->fd < 0) { + g_free(list); + return; + } + + list->list[i].memfd = rb->fd; + list->list[i].offset = offset; + list->list[i].size = res->iov[i].iov_len; + } + + list->count = res->iov_cnt; + list->flags = UDMABUF_FLAGS_CLOEXEC; + + res->dmabuf_fd = ioctl(udmabuf, UDMABUF_CREATE_LIST, list); + if (res->dmabuf_fd < 0) { + warn_report("%s: UDMABUF_CREATE_LIST: %s", __func__, + strerror(errno)); + } + g_free(list); +} + +static void virtio_gpu_remap_udmabuf(struct virtio_gpu_simple_resource *res) +{ + res->remapped = mmap(NULL, res->blob_size, PROT_READ, + MAP_SHARED, res->dmabuf_fd, 0); + if (res->remapped == MAP_FAILED) { + warn_report("%s: dmabuf mmap failed: %s", __func__, + strerror(errno)); + res->remapped = NULL; + } +} + +static void virtio_gpu_destroy_udmabuf(struct virtio_gpu_simple_resource *res) +{ + if (res->remapped) { + munmap(res->remapped, res->blob_size); + res->remapped = NULL; + } + if (res->dmabuf_fd >= 0) { + close(res->dmabuf_fd); + res->dmabuf_fd = -1; + } +} + +static int find_memory_backend_type(Object *obj, void *opaque) +{ + bool *memfd_backend = opaque; + int ret; + + if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) { + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + RAMBlock *rb = backend->mr.ram_block; + + if (rb && rb->fd > 0) { + ret = fcntl(rb->fd, F_GET_SEALS); + if (ret > 0) { + *memfd_backend = true; + } + } + } + + return 0; +} + +bool virtio_gpu_have_udmabuf(void) +{ + Object *memdev_root; + int udmabuf; + bool memfd_backend = false; + + udmabuf = udmabuf_fd(); + if (udmabuf < 0) { + return false; + } + + memdev_root = object_resolve_path("/objects", NULL); + object_child_foreach(memdev_root, find_memory_backend_type, &memfd_backend); + + return memfd_backend; +} + +void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) +{ + void *pdata = NULL; + + res->dmabuf_fd = -1; + if (res->iov_cnt == 1) { + pdata = res->iov[0].iov_base; + } else { + virtio_gpu_create_udmabuf(res); + if (res->dmabuf_fd < 0) { + return; + } + virtio_gpu_remap_udmabuf(res); + if (!res->remapped) { + return; + } + pdata = res->remapped; + } + + res->blob = pdata; +} + +void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res) +{ + if (res->remapped) { + virtio_gpu_destroy_udmabuf(res); + } +} |