summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/display/meson.build1
-rw-r--r--hw/display/virtio-gpu-udmabuf.c158
-rw-r--r--include/hw/virtio/virtio-gpu.h11
3 files changed, 170 insertions, 0 deletions
diff --git a/hw/display/meson.build b/hw/display/meson.build
index aaf797c5e9..e1f473c1df 100644
--- a/hw/display/meson.build
+++ b/hw/display/meson.build
@@ -56,6 +56,7 @@ if config_all_devices.has_key('CONFIG_VIRTIO_GPU')
virtio_gpu_ss = ss.source_set()
virtio_gpu_ss.add(when: 'CONFIG_VIRTIO_GPU',
if_true: [files('virtio-gpu-base.c', 'virtio-gpu.c'), pixman])
+ virtio_gpu_ss.add(when: 'CONFIG_LINUX', if_true: files('virtio-gpu-udmabuf.c'))
virtio_gpu_ss.add(when: 'CONFIG_VHOST_USER_GPU', if_true: files('vhost-user-gpu.c'))
hw_display_modules += {'virtio-gpu': virtio_gpu_ss}
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);
+ }
+}
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 8ca2c55d9a..265b1c516c 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -50,6 +50,12 @@ struct virtio_gpu_simple_resource {
uint32_t scanout_bitmask;
pixman_image_t *image;
uint64_t hostmem;
+
+ uint64_t blob_size;
+ void *blob;
+ int dmabuf_fd;
+ uint8_t *remapped;
+
QTAILQ_ENTRY(virtio_gpu_simple_resource) next;
};
@@ -238,6 +244,11 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g,
struct virtio_gpu_scanout *s,
uint32_t resource_id);
+/* virtio-gpu-udmabuf.c */
+bool virtio_gpu_have_udmabuf(void);
+void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res);
+void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res);
+
/* virtio-gpu-3d.c */
void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd);