1 /* 2 * Virtio GPU Device 3 * 4 * Copyright Red Hat, Inc. 2013-2014 5 * 6 * Authors: 7 * Dave Airlie <airlied@redhat.com> 8 * Gerd Hoffmann <kraxel@redhat.com> 9 * 10 * This work is licensed under the terms of the GNU GPL, version 2 or later. 11 * See the COPYING file in the top-level directory. 12 */ 13 14 #include "qemu/osdep.h" 15 #include "qemu/error-report.h" 16 #include "qemu/units.h" 17 #include "qemu/iov.h" 18 #include "ui/console.h" 19 #include "hw/virtio/virtio-gpu.h" 20 #include "hw/virtio/virtio-gpu-pixman.h" 21 #include "trace.h" 22 #include "exec/ramblock.h" 23 #include "sysemu/hostmem.h" 24 #include <sys/ioctl.h> 25 #include <linux/memfd.h> 26 #include "qemu/memfd.h" 27 #include "standard-headers/linux/udmabuf.h" 28 29 static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource *res) 30 { 31 struct udmabuf_create_list *list; 32 RAMBlock *rb; 33 ram_addr_t offset; 34 int udmabuf, i; 35 36 udmabuf = udmabuf_fd(); 37 if (udmabuf < 0) { 38 return; 39 } 40 41 list = g_malloc0(sizeof(struct udmabuf_create_list) + 42 sizeof(struct udmabuf_create_item) * res->iov_cnt); 43 44 for (i = 0; i < res->iov_cnt; i++) { 45 rcu_read_lock(); 46 rb = qemu_ram_block_from_host(res->iov[i].iov_base, false, &offset); 47 rcu_read_unlock(); 48 49 if (!rb || rb->fd < 0) { 50 g_free(list); 51 return; 52 } 53 54 list->list[i].memfd = rb->fd; 55 list->list[i].offset = offset; 56 list->list[i].size = res->iov[i].iov_len; 57 } 58 59 list->count = res->iov_cnt; 60 list->flags = UDMABUF_FLAGS_CLOEXEC; 61 62 res->dmabuf_fd = ioctl(udmabuf, UDMABUF_CREATE_LIST, list); 63 if (res->dmabuf_fd < 0) { 64 warn_report("%s: UDMABUF_CREATE_LIST: %s", __func__, 65 strerror(errno)); 66 } 67 g_free(list); 68 } 69 70 static void virtio_gpu_remap_udmabuf(struct virtio_gpu_simple_resource *res) 71 { 72 res->remapped = mmap(NULL, res->blob_size, PROT_READ, 73 MAP_SHARED, res->dmabuf_fd, 0); 74 if (res->remapped == MAP_FAILED) { 75 warn_report("%s: dmabuf mmap failed: %s", __func__, 76 strerror(errno)); 77 res->remapped = NULL; 78 } 79 } 80 81 static void virtio_gpu_destroy_udmabuf(struct virtio_gpu_simple_resource *res) 82 { 83 if (res->remapped) { 84 munmap(res->remapped, res->blob_size); 85 res->remapped = NULL; 86 } 87 if (res->dmabuf_fd >= 0) { 88 close(res->dmabuf_fd); 89 res->dmabuf_fd = -1; 90 } 91 } 92 93 static int find_memory_backend_type(Object *obj, void *opaque) 94 { 95 bool *memfd_backend = opaque; 96 int ret; 97 98 if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) { 99 HostMemoryBackend *backend = MEMORY_BACKEND(obj); 100 RAMBlock *rb = backend->mr.ram_block; 101 102 if (rb && rb->fd > 0) { 103 ret = fcntl(rb->fd, F_GET_SEALS); 104 if (ret > 0) { 105 *memfd_backend = true; 106 } 107 } 108 } 109 110 return 0; 111 } 112 113 bool virtio_gpu_have_udmabuf(void) 114 { 115 Object *memdev_root; 116 int udmabuf; 117 bool memfd_backend = false; 118 119 udmabuf = udmabuf_fd(); 120 if (udmabuf < 0) { 121 return false; 122 } 123 124 memdev_root = object_resolve_path("/objects", NULL); 125 object_child_foreach(memdev_root, find_memory_backend_type, &memfd_backend); 126 127 return memfd_backend; 128 } 129 130 void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) 131 { 132 void *pdata = NULL; 133 134 res->dmabuf_fd = -1; 135 if (res->iov_cnt == 1 && 136 res->iov[0].iov_len < 4096) { 137 pdata = res->iov[0].iov_base; 138 } else { 139 virtio_gpu_create_udmabuf(res); 140 if (res->dmabuf_fd < 0) { 141 return; 142 } 143 virtio_gpu_remap_udmabuf(res); 144 if (!res->remapped) { 145 return; 146 } 147 pdata = res->remapped; 148 } 149 150 res->blob = pdata; 151 } 152 153 void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res) 154 { 155 if (res->remapped) { 156 virtio_gpu_destroy_udmabuf(res); 157 } 158 } 159 160 static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf) 161 { 162 struct virtio_gpu_scanout *scanout; 163 164 scanout = &g->parent_obj.scanout[dmabuf->scanout_id]; 165 dpy_gl_release_dmabuf(scanout->con, dmabuf->buf); 166 g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free); 167 QTAILQ_REMOVE(&g->dmabuf.bufs, dmabuf, next); 168 g_free(dmabuf); 169 } 170 171 static VGPUDMABuf 172 *virtio_gpu_create_dmabuf(VirtIOGPU *g, 173 uint32_t scanout_id, 174 struct virtio_gpu_simple_resource *res, 175 struct virtio_gpu_framebuffer *fb, 176 struct virtio_gpu_rect *r) 177 { 178 VGPUDMABuf *dmabuf; 179 180 if (res->dmabuf_fd < 0) { 181 return NULL; 182 } 183 184 dmabuf = g_new0(VGPUDMABuf, 1); 185 dmabuf->buf = qemu_dmabuf_new(r->width, r->height, fb->stride, 186 r->x, r->y, fb->width, fb->height, 187 qemu_pixman_to_drm_format(fb->format), 188 0, res->dmabuf_fd, true, false); 189 dmabuf->scanout_id = scanout_id; 190 QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next); 191 192 return dmabuf; 193 } 194 195 int virtio_gpu_update_dmabuf(VirtIOGPU *g, 196 uint32_t scanout_id, 197 struct virtio_gpu_simple_resource *res, 198 struct virtio_gpu_framebuffer *fb, 199 struct virtio_gpu_rect *r) 200 { 201 struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id]; 202 VGPUDMABuf *new_primary, *old_primary = NULL; 203 uint32_t width, height; 204 205 new_primary = virtio_gpu_create_dmabuf(g, scanout_id, res, fb, r); 206 if (!new_primary) { 207 return -EINVAL; 208 } 209 210 if (g->dmabuf.primary[scanout_id]) { 211 old_primary = g->dmabuf.primary[scanout_id]; 212 } 213 214 width = qemu_dmabuf_get_width(new_primary->buf); 215 height = qemu_dmabuf_get_height(new_primary->buf); 216 g->dmabuf.primary[scanout_id] = new_primary; 217 qemu_console_resize(scanout->con, width, height); 218 dpy_gl_scanout_dmabuf(scanout->con, new_primary->buf); 219 220 if (old_primary) { 221 virtio_gpu_free_dmabuf(g, old_primary); 222 } 223 224 return 0; 225 } 226