master
c 1,586 lines 49.4 KB
Raw
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/iov.h"
17 #include "trace.h"
18 #include "hw/virtio/virtio.h"
19 #include "hw/virtio/virtio-gpu.h"
20 #include "hw/virtio/virtio-gpu-bswap.h"
21 #include "hw/virtio/virtio-gpu-pixman.h"
22
23 #include "ui/egl-helpers.h"
24
25 #include <virglrenderer.h>
26
27 /*
28 * VIRGL_CHECK_VERSION available since libvirglrenderer 1.0.1 and was fixed
29 * in 1.1.0. Undefine bugged version of the macro and provide our own.
30 */
31 #if defined(VIRGL_CHECK_VERSION) && \
32 VIRGL_VERSION_MAJOR == 1 && VIRGL_VERSION_MINOR < 1
33 #undef VIRGL_CHECK_VERSION
34 #endif
35
36 #ifndef VIRGL_CHECK_VERSION
37 #define VIRGL_CHECK_VERSION(major, minor, micro) \
38 (VIRGL_VERSION_MAJOR > (major) || \
39 VIRGL_VERSION_MAJOR == (major) && VIRGL_VERSION_MINOR > (minor) || \
40 VIRGL_VERSION_MAJOR == (major) && VIRGL_VERSION_MINOR == (minor) && \
41 VIRGL_VERSION_MICRO >= (micro))
42 #endif
43
44 #define VIRGL_HAS_MAP_FIXED \
45 (VIRGL_CHECK_VERSION(1, 3, 0) && !IS_ENABLED(CONFIG_WIN32))
46
47 struct virtio_gpu_virgl_resource {
48 struct virtio_gpu_simple_resource base;
49 MemoryRegion *mr;
50 void *map_fixed;
51 };
52
53 static struct virtio_gpu_virgl_resource *
54 virtio_gpu_virgl_find_resource(VirtIOGPU *g, uint32_t resource_id)
55 {
56 struct virtio_gpu_simple_resource *res;
57
58 res = virtio_gpu_find_resource(g, resource_id);
59 if (!res) {
60 return NULL;
61 }
62
63 return container_of(res, struct virtio_gpu_virgl_resource, base);
64 }
65
66 #if VIRGL_RENDERER_CALLBACKS_VERSION >= 4
67 static void *
68 virgl_get_egl_display(G_GNUC_UNUSED void *cookie)
69 {
70 return qemu_egl_display;
71 }
72 #endif
73
74 #if VIRGL_VERSION_MAJOR >= 1
75 enum virtio_gpu_virgl_hostmem_region_mapping_state {
76 VIRTIO_GPU_MR_MAPPED,
77 VIRTIO_GPU_MR_UNMAP_STARTED,
78 VIRTIO_GPU_MR_UNMAP_COMPLETED,
79 };
80
81 struct virtio_gpu_virgl_hostmem_region {
82 Object parent_obj;
83 MemoryRegion mr;
84 struct VirtIOGPU *g;
85 enum virtio_gpu_virgl_hostmem_region_mapping_state mapping_state;
86 };
87
88 #define TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION "virtio-gpu-virgl-hostmem-region"
89
90 OBJECT_DECLARE_SIMPLE_TYPE(virtio_gpu_virgl_hostmem_region,
91 VIRTIO_GPU_VIRGL_HOSTMEM_REGION)
92
93 static struct virtio_gpu_virgl_hostmem_region *
94 to_hostmem_region(MemoryRegion *mr)
95 {
96 return container_of(mr, struct virtio_gpu_virgl_hostmem_region, mr);
97 }
98
99 static void virtio_gpu_virgl_resume_cmdq_bh(void *opaque)
100 {
101 VirtIOGPU *g = opaque;
102
103 if (!virtio_gpu_virgl_update_render_state(g)) {
104 return;
105 }
106
107 virtio_gpu_process_cmdq(g);
108 }
109
110 /*
111 * MR could outlive the resource if MR's reference is held outside of
112 * virtio-gpu. In order to prevent unmapping resource while MR is alive,
113 * and thus, making the data pointer invalid, we will block virtio-gpu
114 * command processing until MR is fully unreferenced and freed.
115 */
116 static void virtio_gpu_virgl_hostmem_region_finalize(Object *obj)
117 {
118 struct virtio_gpu_virgl_hostmem_region *vmr = VIRTIO_GPU_VIRGL_HOSTMEM_REGION(obj);
119 VirtIOGPUBase *b;
120 VirtIOGPUGL *gl;
121
122 if (!vmr->g) {
123 return;
124 }
125
126 vmr->mapping_state = VIRTIO_GPU_MR_UNMAP_COMPLETED;
127
128 b = VIRTIO_GPU_BASE(vmr->g);
129 b->renderer_blocked--;
130
131 /*
132 * memory_region_unref() is executed from RCU thread context, while
133 * virglrenderer works only on the main-loop thread that's holding GL
134 * context.
135 */
136 gl = VIRTIO_GPU_GL(vmr->g);
137 qemu_bh_schedule(gl->cmdq_resume_bh);
138 }
139
140 static const TypeInfo virtio_gpu_virgl_hostmem_region_info = {
141 .parent = TYPE_OBJECT,
142 .name = TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION,
143 .instance_size = sizeof(struct virtio_gpu_virgl_hostmem_region),
144 .instance_finalize = virtio_gpu_virgl_hostmem_region_finalize
145 };
146
147 static void virtio_gpu_virgl_types(void)
148 {
149 type_register_static(&virtio_gpu_virgl_hostmem_region_info);
150 }
151
152 type_init(virtio_gpu_virgl_types)
153
154 static int
155 virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g,
156 struct virtio_gpu_virgl_resource *res,
157 uint64_t offset)
158 {
159 g_autofree char *name = NULL;
160 struct virtio_gpu_virgl_hostmem_region *vmr;
161 VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
162 #if VIRGL_HAS_MAP_FIXED
163 VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
164 #endif
165 MemoryRegion *mr;
166 uint64_t size;
167 void *data;
168 int ret;
169
170 if (!virtio_gpu_hostmem_enabled(b->conf)) {
171 qemu_log_mask(LOG_GUEST_ERROR, "%s: hostmem disabled\n", __func__);
172 return -EOPNOTSUPP;
173 }
174
175 #if VIRGL_HAS_MAP_FIXED
176 /*
177 * virgl_renderer_resource_map_fixed() allows to create multiple
178 * mappings of the same resource, while virgl_renderer_resource_map()
179 * not. Don't allow mapping same resource twice.
180 */
181 if (res->map_fixed || res->mr) {
182 qemu_log_mask(LOG_GUEST_ERROR,
183 "%s: failed to map(fixed) virgl resource: already mapped\n",
184 __func__);
185 return -EBUSY;
186 }
187
188 ret = virgl_renderer_resource_map_fixed(res->base.resource_id,
189 gl->hostmem_mmap + offset);
190 switch (ret) {
191 case 0:
192 res->map_fixed = gl->hostmem_mmap + offset;
193 return 0;
194
195 case -EOPNOTSUPP:
196 /*
197 * MAP_FIXED is unsupported by this resource.
198 * Mapping falls back to a blob subregion method in that case.
199 */
200 break;
201
202 default:
203 qemu_log_mask(LOG_GUEST_ERROR,
204 "%s: failed to map(fixed) virgl resource: %s\n",
205 __func__, strerror(-ret));
206 return ret;
207 }
208 #endif
209
210 ret = virgl_renderer_resource_map(res->base.resource_id, &data, &size);
211 if (ret) {
212 qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map virgl resource: %s\n",
213 __func__, strerror(-ret));
214 return ret;
215 }
216
217 vmr = g_new0(struct virtio_gpu_virgl_hostmem_region, 1);
218 name = g_strdup_printf("blob[%" PRIu32 "]", res->base.resource_id);
219 object_initialize_child(OBJECT(g), name, vmr,
220 TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION);
221 vmr->g = g;
222 vmr->mapping_state = VIRTIO_GPU_MR_MAPPED;
223
224 mr = &vmr->mr;
225 memory_region_init_ram_ptr(mr, OBJECT(vmr), "mr", size, data);
226 memory_region_add_subregion_overlap(&b->hostmem, offset, mr, 1);
227
228 res->mr = mr;
229
230 trace_virtio_gpu_cmd_res_map_blob(res->base.resource_id, vmr, mr);
231
232 return 0;
233 }
234
235 static int
236 virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g,
237 struct virtio_gpu_virgl_resource *res,
238 bool *cmd_suspended)
239 {
240 struct virtio_gpu_virgl_hostmem_region *vmr;
241 VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
242 MemoryRegion *mr = res->mr;
243 int ret;
244
245 #if VIRGL_HAS_MAP_FIXED
246 if (res->map_fixed) {
247 if (mmap(res->map_fixed, res->base.blob_size, PROT_READ | PROT_WRITE,
248 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
249 -1, 0) == MAP_FAILED) {
250 ret = -errno;
251 error_report("%s: failed to unmap(fixed) virgl resource: %s",
252 __func__, strerror(-ret));
253 return ret;
254 }
255
256 res->map_fixed = NULL;
257 }
258 #endif
259
260 if (!mr) {
261 return 0;
262 }
263
264 vmr = to_hostmem_region(res->mr);
265
266 trace_virtio_gpu_cmd_res_unmap_blob(res->base.resource_id, mr,
267 vmr->mapping_state);
268
269 /*
270 * Perform async unmapping in 3 steps:
271 *
272 * 1. Begin async unmapping with memory_region_del_subregion()
273 * and suspend/block cmd processing.
274 * 2. Wait for res->mr to be freed and cmd processing resumed
275 * asynchronously by virtio_gpu_virgl_hostmem_region_finalize().
276 * 3. Finish the unmapping with final virgl_renderer_resource_unmap().
277 */
278 switch (vmr->mapping_state) {
279 case VIRTIO_GPU_MR_UNMAP_COMPLETED:
280 res->mr = NULL;
281 g_free(vmr);
282
283 ret = virgl_renderer_resource_unmap(res->base.resource_id);
284 if (ret) {
285 qemu_log_mask(LOG_GUEST_ERROR,
286 "%s: failed to unmap virgl resource: %s\n",
287 __func__, strerror(-ret));
288 return ret;
289 }
290 break;
291
292 case VIRTIO_GPU_MR_MAPPED:
293 /* render will be unblocked once MR is freed */
294 b->renderer_blocked++;
295
296 vmr->mapping_state = VIRTIO_GPU_MR_UNMAP_STARTED;
297
298 /* memory region owns self res->mr object and frees it by itself */
299 memory_region_del_subregion(&b->hostmem, mr);
300 object_unparent(OBJECT(vmr));
301
302 /* Fallthrough */
303 case VIRTIO_GPU_MR_UNMAP_STARTED:
304 *cmd_suspended = true;
305 break;
306 }
307
308 return 0;
309 }
310 #endif
311
312 static void virgl_cmd_create_resource_2d(VirtIOGPU *g,
313 struct virtio_gpu_ctrl_command *cmd)
314 {
315 struct virtio_gpu_resource_create_2d c2d;
316 struct virgl_renderer_resource_create_args args;
317 struct virtio_gpu_virgl_resource *res;
318
319 VIRTIO_GPU_FILL_CMD(c2d);
320 trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format,
321 c2d.width, c2d.height);
322
323 if (c2d.resource_id == 0) {
324 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
325 __func__);
326 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
327 return;
328 }
329
330 res = virtio_gpu_virgl_find_resource(g, c2d.resource_id);
331 if (res) {
332 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
333 __func__, c2d.resource_id);
334 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
335 return;
336 }
337
338 res = g_new0(struct virtio_gpu_virgl_resource, 1);
339 res->base.width = c2d.width;
340 res->base.height = c2d.height;
341 res->base.format = c2d.format;
342 res->base.resource_id = c2d.resource_id;
343 res->base.dmabuf_fd = -1;
344 QTAILQ_INSERT_HEAD(&g->reslist, &res->base, next);
345
346 args.handle = c2d.resource_id;
347 args.target = 2;
348 args.format = c2d.format;
349 args.bind = (1 << 1);
350 args.width = c2d.width;
351 args.height = c2d.height;
352 args.depth = 1;
353 args.array_size = 1;
354 args.last_level = 0;
355 args.nr_samples = 0;
356 args.flags = VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP;
357 virgl_renderer_resource_create(&args, NULL, 0);
358 }
359
360 static void virgl_cmd_create_resource_3d(VirtIOGPU *g,
361 struct virtio_gpu_ctrl_command *cmd)
362 {
363 struct virtio_gpu_resource_create_3d c3d;
364 struct virgl_renderer_resource_create_args args;
365 struct virtio_gpu_virgl_resource *res;
366
367 VIRTIO_GPU_FILL_CMD(c3d);
368 trace_virtio_gpu_cmd_res_create_3d(c3d.resource_id, c3d.format,
369 c3d.width, c3d.height, c3d.depth);
370
371 if (c3d.resource_id == 0) {
372 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
373 __func__);
374 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
375 return;
376 }
377
378 res = virtio_gpu_virgl_find_resource(g, c3d.resource_id);
379 if (res) {
380 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
381 __func__, c3d.resource_id);
382 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
383 return;
384 }
385
386 res = g_new0(struct virtio_gpu_virgl_resource, 1);
387 res->base.width = c3d.width;
388 res->base.height = c3d.height;
389 res->base.format = c3d.format;
390 res->base.resource_id = c3d.resource_id;
391 res->base.dmabuf_fd = -1;
392 QTAILQ_INSERT_HEAD(&g->reslist, &res->base, next);
393
394 args.handle = c3d.resource_id;
395 args.target = c3d.target;
396 args.format = c3d.format;
397 args.bind = c3d.bind;
398 args.width = c3d.width;
399 args.height = c3d.height;
400 args.depth = c3d.depth;
401 args.array_size = c3d.array_size;
402 args.last_level = c3d.last_level;
403 args.nr_samples = c3d.nr_samples;
404 args.flags = c3d.flags;
405 virgl_renderer_resource_create(&args, NULL, 0);
406 }
407
408 static int
409 virtio_gpu_virgl_resource_unref(VirtIOGPU *g,
410 struct virtio_gpu_virgl_resource *res,
411 bool *suspended)
412 {
413 struct iovec *res_iovs = NULL;
414 int num_iovs = 0;
415 #if VIRGL_VERSION_MAJOR >= 1
416 int ret;
417
418 ret = virtio_gpu_virgl_unmap_resource_blob(g, res, suspended);
419 if (ret) {
420 return ret;
421 }
422 if (*suspended) {
423 return 0;
424 }
425 #endif
426
427 virgl_renderer_resource_detach_iov(res->base.resource_id,
428 &res_iovs,
429 &num_iovs);
430 if (res_iovs != NULL && num_iovs != 0) {
431 virtio_gpu_cleanup_mapping_iov(g, res_iovs, num_iovs);
432 }
433 virgl_renderer_resource_unref(res->base.resource_id);
434
435 QTAILQ_REMOVE(&g->reslist, &res->base, next);
436
437 g_free(res);
438
439 return 0;
440 }
441
442 static void virgl_cmd_resource_unref(VirtIOGPU *g,
443 struct virtio_gpu_ctrl_command *cmd,
444 bool *cmd_suspended)
445 {
446 struct virtio_gpu_resource_unref unref;
447 struct virtio_gpu_virgl_resource *res;
448
449 VIRTIO_GPU_FILL_CMD(unref);
450 trace_virtio_gpu_cmd_res_unref(unref.resource_id);
451
452 res = virtio_gpu_virgl_find_resource(g, unref.resource_id);
453 if (!res) {
454 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n",
455 __func__, unref.resource_id);
456 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
457 return;
458 }
459
460 virtio_gpu_virgl_resource_unref(g, res, cmd_suspended);
461 }
462
463 void virtio_gpu_virgl_resource_destroy(VirtIOGPU *g,
464 struct virtio_gpu_simple_resource *base,
465 Error **errp)
466 {
467 struct virtio_gpu_virgl_resource *res;
468 bool suspended = false;
469
470 res = container_of(base, struct virtio_gpu_virgl_resource, base);
471
472 if (virtio_gpu_virgl_resource_unref(g, res, &suspended)) {
473 error_setg(errp, "failed to destroy virgl resource");
474 }
475 }
476
477 static void virgl_cmd_context_create(VirtIOGPU *g,
478 struct virtio_gpu_ctrl_command *cmd)
479 {
480 struct virtio_gpu_ctx_create cc;
481
482 VIRTIO_GPU_FILL_CMD(cc);
483 trace_virtio_gpu_cmd_ctx_create(cc.hdr.ctx_id,
484 cc.debug_name);
485
486 if (cc.context_init) {
487 if (!virtio_gpu_context_init_enabled(g->parent_obj.conf)) {
488 qemu_log_mask(LOG_GUEST_ERROR, "%s: context_init disabled",
489 __func__);
490 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
491 return;
492 }
493
494 #if VIRGL_VERSION_MAJOR >= 1
495 virgl_renderer_context_create_with_flags(cc.hdr.ctx_id,
496 cc.context_init,
497 cc.nlen,
498 cc.debug_name);
499 return;
500 #endif
501 }
502
503 virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen, cc.debug_name);
504 }
505
506 static void virgl_cmd_context_destroy(VirtIOGPU *g,
507 struct virtio_gpu_ctrl_command *cmd)
508 {
509 struct virtio_gpu_ctx_destroy cd;
510
511 VIRTIO_GPU_FILL_CMD(cd);
512 trace_virtio_gpu_cmd_ctx_destroy(cd.hdr.ctx_id);
513
514 virgl_renderer_context_destroy(cd.hdr.ctx_id);
515 }
516
517 static void virtio_gpu_rect_update(VirtIOGPU *g, int idx, int x, int y,
518 int width, int height)
519 {
520 if (!g->parent_obj.scanout[idx].con) {
521 return;
522 }
523
524 qemu_console_gl_update(g->parent_obj.scanout[idx].con, x, y, width, height);
525 }
526
527 static void virgl_cmd_resource_flush(VirtIOGPU *g,
528 struct virtio_gpu_ctrl_command *cmd)
529 {
530 struct virtio_gpu_resource_flush rf;
531 int i;
532
533 VIRTIO_GPU_FILL_CMD(rf);
534 trace_virtio_gpu_cmd_res_flush(rf.resource_id,
535 rf.r.width, rf.r.height, rf.r.x, rf.r.y);
536
537 for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
538 if (g->parent_obj.scanout[i].resource_id != rf.resource_id) {
539 continue;
540 }
541 virtio_gpu_rect_update(g, i, rf.r.x, rf.r.y, rf.r.width, rf.r.height);
542 }
543 }
544
545 static void virgl_cmd_set_scanout(VirtIOGPU *g,
546 struct virtio_gpu_ctrl_command *cmd)
547 {
548 struct virtio_gpu_set_scanout ss;
549 int ret;
550
551 VIRTIO_GPU_FILL_CMD(ss);
552 trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
553 ss.r.width, ss.r.height, ss.r.x, ss.r.y);
554
555 if (ss.scanout_id >= g->parent_obj.conf.max_outputs) {
556 qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
557 __func__, ss.scanout_id);
558 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
559 return;
560 }
561 g->parent_obj.enable = 1;
562
563 if (ss.resource_id) {
564 struct virgl_renderer_resource_info info;
565 void *d3d_tex2d = NULL;
566
567 #if VIRGL_VERSION_MAJOR >= 1
568 struct virgl_renderer_resource_info_ext ext;
569 memset(&ext, 0, sizeof(ext));
570 ret = virgl_renderer_resource_get_info_ext(ss.resource_id, &ext);
571 info = ext.base;
572 d3d_tex2d = ext.d3d_tex2d;
573 #else
574 memset(&info, 0, sizeof(info));
575 ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
576 #endif
577 if (ret) {
578 qemu_log_mask(LOG_GUEST_ERROR,
579 "%s: illegal resource specified %d\n",
580 __func__, ss.resource_id);
581 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
582 return;
583 }
584 if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id,
585 info.width, info.height, &ss.r,
586 &cmd->error)) {
587 return;
588 }
589 qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con,
590 ss.r.width, ss.r.height);
591 virgl_renderer_force_ctx_0();
592 qemu_console_gl_scanout_texture(
593 g->parent_obj.scanout[ss.scanout_id].con, info.tex_id,
594 info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP,
595 info.width, info.height,
596 ss.r.x, ss.r.y, ss.r.width, ss.r.height,
597 d3d_tex2d);
598 } else {
599 qemu_console_set_surface(g->parent_obj.scanout[ss.scanout_id].con, NULL);
600 qemu_console_gl_scanout_disable(g->parent_obj.scanout[ss.scanout_id].con);
601 }
602 g->parent_obj.scanout[ss.scanout_id].resource_id = ss.resource_id;
603 }
604
605 static void virgl_cmd_submit_3d(VirtIOGPU *g,
606 struct virtio_gpu_ctrl_command *cmd)
607 {
608 struct virtio_gpu_cmd_submit cs;
609 void *buf;
610 size_t s;
611
612 VIRTIO_GPU_FILL_CMD(cs);
613 trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size);
614
615 if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) {
616 qemu_log_mask(LOG_GUEST_ERROR,
617 "%s: command buffer too large (%u)\n",
618 __func__, cs.size);
619 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
620 return;
621 }
622
623 buf = g_try_malloc(cs.size);
624 if (!buf && cs.size) {
625 cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
626 return;
627 }
628 s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
629 sizeof(cs), buf, cs.size);
630 if (s != cs.size) {
631 qemu_log_mask(LOG_GUEST_ERROR, "%s: size mismatch (%zd/%d)",
632 __func__, s, cs.size);
633 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
634 goto out;
635 }
636
637 if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
638 g->stats.req_3d++;
639 g->stats.bytes_3d += cs.size;
640 }
641
642 virgl_renderer_submit_cmd(buf, cs.hdr.ctx_id, cs.size / 4);
643
644 out:
645 g_free(buf);
646 }
647
648 static void virgl_cmd_transfer_to_host_2d(VirtIOGPU *g,
649 struct virtio_gpu_ctrl_command *cmd)
650 {
651 struct virtio_gpu_transfer_to_host_2d t2d;
652 struct virtio_gpu_box box;
653
654 VIRTIO_GPU_FILL_CMD(t2d);
655 trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id);
656
657 box.x = t2d.r.x;
658 box.y = t2d.r.y;
659 box.z = 0;
660 box.w = t2d.r.width;
661 box.h = t2d.r.height;
662 box.d = 1;
663
664 virgl_renderer_transfer_write_iov(t2d.resource_id,
665 0,
666 0,
667 0,
668 0,
669 (struct virgl_box *)&box,
670 t2d.offset, NULL, 0);
671 }
672
673 static void virgl_cmd_transfer_to_host_3d(VirtIOGPU *g,
674 struct virtio_gpu_ctrl_command *cmd)
675 {
676 struct virtio_gpu_transfer_host_3d t3d;
677
678 VIRTIO_GPU_FILL_CMD(t3d);
679 trace_virtio_gpu_cmd_res_xfer_toh_3d(t3d.resource_id);
680
681 virgl_renderer_transfer_write_iov(t3d.resource_id,
682 t3d.hdr.ctx_id,
683 t3d.level,
684 t3d.stride,
685 t3d.layer_stride,
686 (struct virgl_box *)&t3d.box,
687 t3d.offset, NULL, 0);
688 }
689
690 static void
691 virgl_cmd_transfer_from_host_3d(VirtIOGPU *g,
692 struct virtio_gpu_ctrl_command *cmd)
693 {
694 struct virtio_gpu_transfer_host_3d tf3d;
695
696 VIRTIO_GPU_FILL_CMD(tf3d);
697 trace_virtio_gpu_cmd_res_xfer_fromh_3d(tf3d.resource_id);
698
699 virgl_renderer_transfer_read_iov(tf3d.resource_id,
700 tf3d.hdr.ctx_id,
701 tf3d.level,
702 tf3d.stride,
703 tf3d.layer_stride,
704 (struct virgl_box *)&tf3d.box,
705 tf3d.offset, NULL, 0);
706 }
707
708
709 static void virgl_resource_attach_backing(VirtIOGPU *g,
710 struct virtio_gpu_ctrl_command *cmd)
711 {
712 struct virtio_gpu_resource_attach_backing att_rb;
713 struct iovec *res_iovs;
714 uint32_t res_niov;
715 int ret;
716
717 VIRTIO_GPU_FILL_CMD(att_rb);
718 trace_virtio_gpu_cmd_res_back_attach(att_rb.resource_id);
719
720 ret = virtio_gpu_create_mapping_iov(g, att_rb.nr_entries, sizeof(att_rb),
721 cmd, NULL, &res_iovs, &res_niov);
722 if (ret < 0) {
723 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
724 return;
725 }
726
727 ret = virgl_renderer_resource_attach_iov(att_rb.resource_id,
728 res_iovs, res_niov);
729
730 if (ret != 0)
731 virtio_gpu_cleanup_mapping_iov(g, res_iovs, res_niov);
732 }
733
734 static void virgl_resource_detach_backing(VirtIOGPU *g,
735 struct virtio_gpu_ctrl_command *cmd)
736 {
737 struct virtio_gpu_resource_detach_backing detach_rb;
738 struct iovec *res_iovs = NULL;
739 int num_iovs = 0;
740
741 VIRTIO_GPU_FILL_CMD(detach_rb);
742 trace_virtio_gpu_cmd_res_back_detach(detach_rb.resource_id);
743
744 virgl_renderer_resource_detach_iov(detach_rb.resource_id,
745 &res_iovs,
746 &num_iovs);
747 if (res_iovs == NULL || num_iovs == 0) {
748 return;
749 }
750 virtio_gpu_cleanup_mapping_iov(g, res_iovs, num_iovs);
751 }
752
753
754 static void virgl_cmd_ctx_attach_resource(VirtIOGPU *g,
755 struct virtio_gpu_ctrl_command *cmd)
756 {
757 struct virtio_gpu_ctx_resource att_res;
758
759 VIRTIO_GPU_FILL_CMD(att_res);
760 trace_virtio_gpu_cmd_ctx_res_attach(att_res.hdr.ctx_id,
761 att_res.resource_id);
762
763 virgl_renderer_ctx_attach_resource(att_res.hdr.ctx_id, att_res.resource_id);
764 }
765
766 static void virgl_cmd_ctx_detach_resource(VirtIOGPU *g,
767 struct virtio_gpu_ctrl_command *cmd)
768 {
769 struct virtio_gpu_ctx_resource det_res;
770
771 VIRTIO_GPU_FILL_CMD(det_res);
772 trace_virtio_gpu_cmd_ctx_res_detach(det_res.hdr.ctx_id,
773 det_res.resource_id);
774
775 virgl_renderer_ctx_detach_resource(det_res.hdr.ctx_id, det_res.resource_id);
776 }
777
778 static void virgl_cmd_get_capset_info(VirtIOGPU *g,
779 struct virtio_gpu_ctrl_command *cmd)
780 {
781 struct virtio_gpu_get_capset_info info;
782 struct virtio_gpu_resp_capset_info resp;
783
784 VIRTIO_GPU_FILL_CMD(info);
785
786 memset(&resp, 0, sizeof(resp));
787
788 if (info.capset_index < g->capset_ids->len) {
789 resp.capset_id = g_array_index(g->capset_ids, uint32_t,
790 info.capset_index);
791 virgl_renderer_get_cap_set(resp.capset_id,
792 &resp.capset_max_version,
793 &resp.capset_max_size);
794 }
795 resp.hdr.type = VIRTIO_GPU_RESP_OK_CAPSET_INFO;
796 virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp));
797 }
798
799 static void virgl_cmd_get_capset(VirtIOGPU *g,
800 struct virtio_gpu_ctrl_command *cmd)
801 {
802 struct virtio_gpu_get_capset gc;
803 struct virtio_gpu_resp_capset *resp;
804 uint32_t max_ver, max_size;
805 VIRTIO_GPU_FILL_CMD(gc);
806
807 virgl_renderer_get_cap_set(gc.capset_id, &max_ver,
808 &max_size);
809 if (!max_size) {
810 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
811 return;
812 }
813
814 resp = g_malloc0(sizeof(*resp) + max_size);
815 resp->hdr.type = VIRTIO_GPU_RESP_OK_CAPSET;
816 virgl_renderer_fill_caps(gc.capset_id,
817 gc.capset_version,
818 (void *)resp->capset_data);
819 virtio_gpu_ctrl_response(g, cmd, &resp->hdr, sizeof(*resp) + max_size);
820 g_free(resp);
821 }
822
823 #if VIRGL_VERSION_MAJOR >= 1
824 static void virgl_cmd_resource_create_blob(VirtIOGPU *g,
825 struct virtio_gpu_ctrl_command *cmd)
826 {
827 struct virgl_renderer_resource_create_blob_args virgl_args = { 0 };
828 g_autofree struct virtio_gpu_virgl_resource *res = NULL;
829 struct virtio_gpu_resource_create_blob cblob;
830 struct virgl_renderer_resource_info info;
831 int ret;
832
833 if (!virtio_gpu_blob_enabled(g->parent_obj.conf)) {
834 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
835 return;
836 }
837
838 VIRTIO_GPU_FILL_CMD(cblob);
839 virtio_gpu_create_blob_bswap(&cblob);
840 trace_virtio_gpu_cmd_res_create_blob(cblob.resource_id, cblob.size);
841
842 if (cblob.resource_id == 0) {
843 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
844 __func__);
845 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
846 return;
847 }
848
849 if (virtio_gpu_virgl_find_resource(g, cblob.resource_id)) {
850 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
851 __func__, cblob.resource_id);
852 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
853 return;
854 }
855
856 res = g_new0(struct virtio_gpu_virgl_resource, 1);
857 res->base.resource_id = cblob.resource_id;
858 res->base.blob_size = cblob.size;
859 res->base.dmabuf_fd = -1;
860
861 if (cblob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D) {
862 ret = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, sizeof(cblob),
863 cmd, &res->base.addrs,
864 &res->base.iov, &res->base.iov_cnt);
865 if (ret < 0) {
866 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
867 return;
868 }
869 }
870
871 virgl_args.res_handle = cblob.resource_id;
872 virgl_args.ctx_id = cblob.hdr.ctx_id;
873 virgl_args.blob_mem = cblob.blob_mem;
874 virgl_args.blob_id = cblob.blob_id;
875 virgl_args.blob_flags = cblob.blob_flags;
876 virgl_args.size = cblob.size;
877 virgl_args.iovecs = res->base.iov;
878 virgl_args.num_iovs = res->base.iov_cnt;
879
880 ret = virgl_renderer_resource_create_blob(&virgl_args);
881 if (ret) {
882 qemu_log_mask(LOG_GUEST_ERROR, "%s: virgl blob create error: %s\n",
883 __func__, strerror(-ret));
884 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
885 virtio_gpu_cleanup_mapping(g, &res->base);
886 return;
887 }
888
889 ret = virgl_renderer_resource_get_info(cblob.resource_id, &info);
890 if (ret) {
891 qemu_log_mask(LOG_GUEST_ERROR,
892 "%s: resource does not have info %d: %s\n",
893 __func__, cblob.resource_id, strerror(-ret));
894 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
895 virtio_gpu_cleanup_mapping(g, &res->base);
896 virgl_renderer_resource_unref(cblob.resource_id);
897 return;
898 }
899
900 res->base.dmabuf_fd = info.fd;
901
902 /* Now live, cleaned up in virtio_gpu_virgl_resource_unref */
903 QTAILQ_INSERT_HEAD(&g->reslist, &res->base, next);
904 g_steal_pointer(&res);
905 }
906
907 static void virgl_cmd_resource_map_blob(VirtIOGPU *g,
908 struct virtio_gpu_ctrl_command *cmd)
909 {
910 struct virtio_gpu_resource_map_blob mblob;
911 struct virtio_gpu_virgl_resource *res;
912 struct virtio_gpu_resp_map_info resp;
913 VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
914 int ret;
915
916 VIRTIO_GPU_FILL_CMD(mblob);
917 virtio_gpu_map_blob_bswap(&mblob);
918
919 res = virtio_gpu_virgl_find_resource(g, mblob.resource_id);
920 if (!res) {
921 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n",
922 __func__, mblob.resource_id);
923 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
924 return;
925 }
926
927 if (mblob.offset + res->base.blob_size > b->conf.hostmem ||
928 mblob.offset + res->base.blob_size < mblob.offset) {
929 qemu_log_mask(LOG_GUEST_ERROR,
930 "%s: failed to map virgl resource: invalid offset\n",
931 __func__);
932 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
933 return;
934 }
935
936 ret = virtio_gpu_virgl_map_resource_blob(g, res, mblob.offset);
937 if (ret) {
938 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
939 return;
940 }
941
942 memset(&resp, 0, sizeof(resp));
943 resp.hdr.type = VIRTIO_GPU_RESP_OK_MAP_INFO;
944 virgl_renderer_resource_get_map_info(mblob.resource_id, &resp.map_info);
945 virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp));
946 }
947
948 static void virgl_cmd_resource_unmap_blob(VirtIOGPU *g,
949 struct virtio_gpu_ctrl_command *cmd,
950 bool *cmd_suspended)
951 {
952 struct virtio_gpu_resource_unmap_blob ublob;
953 struct virtio_gpu_virgl_resource *res;
954 int ret;
955
956 VIRTIO_GPU_FILL_CMD(ublob);
957 virtio_gpu_unmap_blob_bswap(&ublob);
958
959 res = virtio_gpu_virgl_find_resource(g, ublob.resource_id);
960 if (!res) {
961 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n",
962 __func__, ublob.resource_id);
963 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
964 return;
965 }
966
967 ret = virtio_gpu_virgl_unmap_resource_blob(g, res, cmd_suspended);
968 if (ret) {
969 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
970 return;
971 }
972 }
973
974 static void virgl_cmd_set_scanout_blob(VirtIOGPU *g,
975 struct virtio_gpu_ctrl_command *cmd)
976 {
977 struct virtio_gpu_framebuffer fb = { 0 };
978 struct virtio_gpu_virgl_resource *res;
979 struct virtio_gpu_set_scanout_blob ss;
980
981 VIRTIO_GPU_FILL_CMD(ss);
982 virtio_gpu_scanout_blob_bswap(&ss);
983 trace_virtio_gpu_cmd_set_scanout_blob(ss.scanout_id, ss.resource_id,
984 ss.r.width, ss.r.height, ss.r.x,
985 ss.r.y);
986
987 if (ss.scanout_id >= g->parent_obj.conf.max_outputs) {
988 qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
989 __func__, ss.scanout_id);
990 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
991 return;
992 }
993
994 if (ss.resource_id == 0) {
995 virtio_gpu_disable_scanout(g, ss.scanout_id);
996 return;
997 }
998
999 if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id,
1000 ss.width, ss.height, &ss.r,
1001 &cmd->error)) {
1002 return;
1003 }
1004
1005 res = virtio_gpu_virgl_find_resource(g, ss.resource_id);
1006 if (!res) {
1007 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n",
1008 __func__, ss.resource_id);
1009 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
1010 return;
1011 }
1012 if (res->base.dmabuf_fd < 0) {
1013 qemu_log_mask(LOG_GUEST_ERROR, "%s: resource not backed by dmabuf %d\n",
1014 __func__, ss.resource_id);
1015 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
1016 return;
1017 }
1018
1019 if (!virtio_gpu_scanout_blob_to_fb(&fb, &ss, res->base.blob_size)) {
1020 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
1021 return;
1022 }
1023
1024 g->parent_obj.enable = 1;
1025 if (virtio_gpu_update_dmabuf(g, ss.scanout_id, &res->base, &fb, &ss.r)) {
1026 qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to update dmabuf\n",
1027 __func__);
1028 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
1029 return;
1030 }
1031
1032 virtio_gpu_update_scanout(g, ss.scanout_id, &res->base, &fb, &ss.r);
1033 }
1034 #endif
1035
1036 void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
1037 struct virtio_gpu_ctrl_command *cmd)
1038 {
1039 bool cmd_suspended = false;
1040 int ret;
1041
1042 VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
1043
1044 virgl_renderer_force_ctx_0();
1045 switch (cmd->cmd_hdr.type) {
1046 case VIRTIO_GPU_CMD_CTX_CREATE:
1047 virgl_cmd_context_create(g, cmd);
1048 break;
1049 case VIRTIO_GPU_CMD_CTX_DESTROY:
1050 virgl_cmd_context_destroy(g, cmd);
1051 break;
1052 case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
1053 virgl_cmd_create_resource_2d(g, cmd);
1054 break;
1055 case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D:
1056 virgl_cmd_create_resource_3d(g, cmd);
1057 break;
1058 case VIRTIO_GPU_CMD_SUBMIT_3D:
1059 virgl_cmd_submit_3d(g, cmd);
1060 break;
1061 case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
1062 virgl_cmd_transfer_to_host_2d(g, cmd);
1063 break;
1064 case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D:
1065 virgl_cmd_transfer_to_host_3d(g, cmd);
1066 break;
1067 case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D:
1068 virgl_cmd_transfer_from_host_3d(g, cmd);
1069 break;
1070 case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING:
1071 virgl_resource_attach_backing(g, cmd);
1072 break;
1073 case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING:
1074 virgl_resource_detach_backing(g, cmd);
1075 break;
1076 case VIRTIO_GPU_CMD_SET_SCANOUT:
1077 virgl_cmd_set_scanout(g, cmd);
1078 break;
1079 case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
1080 virgl_cmd_resource_flush(g, cmd);
1081 break;
1082 case VIRTIO_GPU_CMD_RESOURCE_UNREF:
1083 virgl_cmd_resource_unref(g, cmd, &cmd_suspended);
1084 break;
1085 case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE:
1086 /* TODO add security */
1087 virgl_cmd_ctx_attach_resource(g, cmd);
1088 break;
1089 case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE:
1090 /* TODO add security */
1091 virgl_cmd_ctx_detach_resource(g, cmd);
1092 break;
1093 case VIRTIO_GPU_CMD_GET_CAPSET_INFO:
1094 virgl_cmd_get_capset_info(g, cmd);
1095 break;
1096 case VIRTIO_GPU_CMD_GET_CAPSET:
1097 virgl_cmd_get_capset(g, cmd);
1098 break;
1099 case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
1100 virtio_gpu_get_display_info(g, cmd);
1101 break;
1102 case VIRTIO_GPU_CMD_GET_EDID:
1103 virtio_gpu_get_edid(g, cmd);
1104 break;
1105 #if VIRGL_VERSION_MAJOR >= 1
1106 case VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB:
1107 virgl_cmd_resource_create_blob(g, cmd);
1108 break;
1109 case VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB:
1110 virgl_cmd_resource_map_blob(g, cmd);
1111 break;
1112 case VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB:
1113 virgl_cmd_resource_unmap_blob(g, cmd, &cmd_suspended);
1114 break;
1115 case VIRTIO_GPU_CMD_SET_SCANOUT_BLOB:
1116 virgl_cmd_set_scanout_blob(g, cmd);
1117 break;
1118 #endif
1119 default:
1120 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
1121 break;
1122 }
1123
1124 if (cmd_suspended || cmd->finished) {
1125 return;
1126 }
1127 if (cmd->error) {
1128 fprintf(stderr, "%s: ctrl 0x%x, error 0x%x\n", __func__,
1129 cmd->cmd_hdr.type, cmd->error);
1130 virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error);
1131 return;
1132 }
1133 if (!(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) {
1134 virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
1135 return;
1136 }
1137
1138 trace_virtio_gpu_fence_ctrl(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
1139 #if VIRGL_VERSION_MAJOR >= 1
1140 if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_INFO_RING_IDX) {
1141 const uint32_t flags = VIRGL_RENDERER_FENCE_FLAG_MERGEABLE;
1142
1143 ret = virgl_renderer_context_create_fence(cmd->cmd_hdr.ctx_id, flags,
1144 cmd->cmd_hdr.ring_idx,
1145 cmd->cmd_hdr.fence_id);
1146 if (ret) {
1147 qemu_log_mask(LOG_GUEST_ERROR,
1148 "%s: virgl_renderer_context_create_fence error: %s",
1149 __func__, strerror(-ret));
1150 }
1151 return;
1152 }
1153 #endif
1154
1155 /*
1156 * Unlike other virglrenderer functions, this one returns a positive
1157 * error code.
1158 */
1159 ret = virgl_renderer_create_fence(cmd->cmd_hdr.fence_id, 0);
1160 if (ret) {
1161 qemu_log_mask(LOG_GUEST_ERROR,
1162 "%s: virgl_renderer_create_fence error: %s",
1163 __func__, strerror(ret));
1164 }
1165 }
1166
1167 static void virgl_write_fence(void *opaque, uint32_t fence)
1168 {
1169 VirtIOGPU *g = opaque;
1170 struct virtio_gpu_ctrl_command *cmd, *tmp;
1171
1172 QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) {
1173 /*
1174 * the guest can end up emitting fences out of order
1175 * so we should check all fenced cmds not just the first one.
1176 */
1177 #if VIRGL_VERSION_MAJOR >= 1
1178 if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_INFO_RING_IDX) {
1179 continue;
1180 }
1181 #endif
1182 if (cmd->cmd_hdr.fence_id > fence) {
1183 continue;
1184 }
1185 trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id);
1186 virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
1187 QTAILQ_REMOVE(&g->fenceq, cmd, next);
1188 g_free(cmd);
1189 g->inflight--;
1190 if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
1191 trace_virtio_gpu_dec_inflight_fences(g->inflight);
1192 }
1193 }
1194 }
1195
1196 #if VIRGL_VERSION_MAJOR >= 1
1197 static void virgl_write_context_fence(void *opaque, uint32_t ctx_id,
1198 uint32_t ring_idx, uint64_t fence_id) {
1199 VirtIOGPU *g = opaque;
1200 struct virtio_gpu_ctrl_command *cmd, *tmp;
1201
1202 QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) {
1203 if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_INFO_RING_IDX &&
1204 cmd->cmd_hdr.ctx_id == ctx_id && cmd->cmd_hdr.ring_idx == ring_idx &&
1205 cmd->cmd_hdr.fence_id <= fence_id) {
1206 trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id);
1207 virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
1208 QTAILQ_REMOVE(&g->fenceq, cmd, next);
1209 g_free(cmd);
1210 g->inflight--;
1211 if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
1212 trace_virtio_gpu_dec_inflight_fences(g->inflight);
1213 }
1214 }
1215 }
1216 }
1217 #endif
1218
1219 void virtio_gpu_virgl_reset_async_fences(VirtIOGPU *g)
1220 {
1221 struct virtio_gpu_virgl_context_fence *f;
1222 VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1223
1224 while (!QSLIST_EMPTY(&gl->async_fenceq)) {
1225 f = QSLIST_FIRST(&gl->async_fenceq);
1226
1227 QSLIST_REMOVE_HEAD(&gl->async_fenceq, next);
1228
1229 g_free(f);
1230 }
1231 }
1232
1233 #if VIRGL_CHECK_VERSION(1, 1, 2)
1234 static void virtio_gpu_virgl_async_fence_bh(void *opaque)
1235 {
1236 QSLIST_HEAD(, virtio_gpu_virgl_context_fence) async_fenceq;
1237 struct virtio_gpu_ctrl_command *cmd, *tmp;
1238 struct virtio_gpu_virgl_context_fence *f;
1239 VirtIOGPU *g = opaque;
1240 VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1241
1242 if (gl->renderer_state != RS_INITED) {
1243 return;
1244 }
1245
1246 QSLIST_MOVE_ATOMIC(&async_fenceq, &gl->async_fenceq);
1247
1248 while (!QSLIST_EMPTY(&async_fenceq)) {
1249 f = QSLIST_FIRST(&async_fenceq);
1250
1251 QSLIST_REMOVE_HEAD(&async_fenceq, next);
1252
1253 QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) {
1254 /*
1255 * the guest can end up emitting fences out of order
1256 * so we should check all fenced cmds not just the first one.
1257 */
1258 if (cmd->cmd_hdr.fence_id > f->fence_id) {
1259 continue;
1260 }
1261 if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_INFO_RING_IDX) {
1262 if (cmd->cmd_hdr.ring_idx != f->ring_idx) {
1263 continue;
1264 }
1265 if (cmd->cmd_hdr.ctx_id != f->ctx_id) {
1266 continue;
1267 }
1268 }
1269 virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
1270 QTAILQ_REMOVE(&g->fenceq, cmd, next);
1271 g_free(cmd);
1272 }
1273
1274 trace_virtio_gpu_fence_resp(f->fence_id);
1275 g_free(f);
1276 g->inflight--;
1277 if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
1278 trace_virtio_gpu_dec_inflight_fences(g->inflight);
1279 }
1280 }
1281 }
1282
1283 static void
1284 virtio_gpu_virgl_push_async_fence(VirtIOGPU *g, uint32_t ctx_id,
1285 uint32_t ring_idx, uint64_t fence_id)
1286 {
1287 struct virtio_gpu_virgl_context_fence *f;
1288 VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1289
1290 f = g_new(struct virtio_gpu_virgl_context_fence, 1);
1291 f->ctx_id = ctx_id;
1292 f->ring_idx = ring_idx;
1293 f->fence_id = fence_id;
1294
1295 QSLIST_INSERT_HEAD_ATOMIC(&gl->async_fenceq, f, next);
1296
1297 qemu_bh_schedule(gl->async_fence_bh);
1298 }
1299
1300 static void virgl_write_async_fence(void *opaque, uint32_t fence)
1301 {
1302 VirtIOGPU *g = opaque;
1303
1304 virtio_gpu_virgl_push_async_fence(g, 0, UINT32_MAX, fence);
1305 }
1306
1307 static void virgl_write_async_context_fence(void *opaque, uint32_t ctx_id,
1308 uint32_t ring_idx, uint64_t fence)
1309 {
1310 VirtIOGPU *g = opaque;
1311
1312 virtio_gpu_virgl_push_async_fence(g, ctx_id, ring_idx, fence);
1313 }
1314 #endif
1315
1316 static virgl_renderer_gl_context
1317 virgl_create_context(void *opaque, int scanout_idx,
1318 struct virgl_renderer_gl_ctx_param *params)
1319 {
1320 VirtIOGPU *g = opaque;
1321 QEMUGLContext ctx;
1322 QEMUGLParams qparams;
1323
1324 qparams.major_ver = params->major_ver;
1325 qparams.minor_ver = params->minor_ver;
1326
1327 ctx = qemu_console_gl_ctx_create(g->parent_obj.scanout[scanout_idx].con, &qparams);
1328 return (virgl_renderer_gl_context)ctx;
1329 }
1330
1331 static void virgl_destroy_context(void *opaque, virgl_renderer_gl_context ctx)
1332 {
1333 VirtIOGPU *g = opaque;
1334 QEMUGLContext qctx = (QEMUGLContext)ctx;
1335
1336 qemu_console_gl_ctx_destroy(g->parent_obj.scanout[0].con, qctx);
1337 }
1338
1339 static int virgl_make_context_current(void *opaque, int scanout_idx,
1340 virgl_renderer_gl_context ctx)
1341 {
1342 VirtIOGPU *g = opaque;
1343 QEMUGLContext qctx = (QEMUGLContext)ctx;
1344
1345 return qemu_console_gl_ctx_make_current(g->parent_obj.scanout[scanout_idx].con, qctx);
1346 }
1347
1348 static struct virgl_renderer_callbacks virtio_gpu_3d_cbs = {
1349 #if VIRGL_VERSION_MAJOR >= 1
1350 .version = 3,
1351 #else
1352 .version = 1,
1353 #endif
1354 .write_fence = virgl_write_fence,
1355 .create_gl_context = virgl_create_context,
1356 .destroy_gl_context = virgl_destroy_context,
1357 .make_current = virgl_make_context_current,
1358 #if VIRGL_VERSION_MAJOR >= 1
1359 .write_context_fence = virgl_write_context_fence,
1360 #endif
1361 };
1362
1363 static void virtio_gpu_print_stats(void *opaque)
1364 {
1365 VirtIOGPU *g = opaque;
1366 VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1367
1368 if (g->stats.requests) {
1369 fprintf(stderr, "stats: vq req %4d, %3d -- 3D %4d (%5d)\n",
1370 g->stats.requests,
1371 g->stats.max_inflight,
1372 g->stats.req_3d,
1373 g->stats.bytes_3d);
1374 g->stats.requests = 0;
1375 g->stats.max_inflight = 0;
1376 g->stats.req_3d = 0;
1377 g->stats.bytes_3d = 0;
1378 } else {
1379 fprintf(stderr, "stats: idle\r");
1380 }
1381 timer_mod(gl->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
1382 }
1383
1384 static void virtio_gpu_fence_poll(void *opaque)
1385 {
1386 VirtIOGPU *g = opaque;
1387 VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1388
1389 if (!virtio_gpu_virgl_update_render_state(g)) {
1390 return;
1391 }
1392
1393 virgl_renderer_poll();
1394 virtio_gpu_process_cmdq(g);
1395 if (!QTAILQ_EMPTY(&g->cmdq) || !QTAILQ_EMPTY(&g->fenceq)) {
1396 timer_mod(gl->fence_poll, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10);
1397 }
1398 }
1399
1400 void virtio_gpu_virgl_fence_poll(VirtIOGPU *g)
1401 {
1402 virtio_gpu_fence_poll(g);
1403 }
1404
1405 void virtio_gpu_virgl_reset_scanout(VirtIOGPU *g)
1406 {
1407 int i;
1408
1409 for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
1410 qemu_console_set_surface(g->parent_obj.scanout[i].con, NULL);
1411 qemu_console_gl_scanout_disable(g->parent_obj.scanout[i].con);
1412 }
1413 }
1414
1415 static bool virtio_gpu_virgl_reset(VirtIOGPU *g)
1416 {
1417 struct virtio_gpu_simple_resource *res, *tmp;
1418
1419 /*
1420 * Virgl blob resource unmapping can be suspended and
1421 * deferred on unref, ensure that destruction is completed.
1422 */
1423 QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
1424 virtio_gpu_virgl_resource_destroy(g, res, NULL);
1425 }
1426
1427 if (!QTAILQ_EMPTY(&g->reslist)) {
1428 return false;
1429 }
1430
1431 virgl_renderer_reset();
1432
1433 virtio_gpu_virgl_reset_async_fences(g);
1434
1435 return true;
1436 }
1437
1438 static int virtio_gpu_virgl_init(VirtIOGPU *g)
1439 {
1440 int ret;
1441 uint32_t flags = 0;
1442 VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1443
1444 #if VIRGL_RENDERER_CALLBACKS_VERSION >= 4
1445 if (qemu_egl_display) {
1446 virtio_gpu_3d_cbs.version = 4;
1447 virtio_gpu_3d_cbs.get_egl_display = virgl_get_egl_display;
1448 #if VIRGL_CHECK_VERSION(1, 1, 2)
1449 virtio_gpu_3d_cbs.write_fence = virgl_write_async_fence;
1450 virtio_gpu_3d_cbs.write_context_fence = virgl_write_async_context_fence;
1451 flags |= VIRGL_RENDERER_ASYNC_FENCE_CB;
1452 flags |= VIRGL_RENDERER_THREAD_SYNC;
1453 #endif
1454 }
1455 #endif
1456 #ifdef VIRGL_RENDERER_D3D11_SHARE_TEXTURE
1457 if (qemu_egl_angle_d3d) {
1458 flags |= VIRGL_RENDERER_D3D11_SHARE_TEXTURE;
1459 }
1460 #endif
1461 #if VIRGL_VERSION_MAJOR >= 1
1462 if (virtio_gpu_venus_enabled(g->parent_obj.conf)) {
1463 flags |= VIRGL_RENDERER_VENUS | VIRGL_RENDERER_RENDER_SERVER;
1464 }
1465 if (virtio_gpu_drm_enabled(g->parent_obj.conf)) {
1466 flags |= VIRGL_RENDERER_DRM;
1467
1468 if (!(flags & VIRGL_RENDERER_ASYNC_FENCE_CB)) {
1469 /*
1470 * Virglrenderer skips enabling DRM context support without
1471 * enabled async-fence feature. VirtIO-GPU will initialize
1472 * successfully, but DRM context won't be available in guest.
1473 *
1474 * For vrend async-fencing can be enabled only if EGL display
1475 * is used. Vrend can't be disabled in QEMU, hence DRM implicitly
1476 * requires EGL too.
1477 *
1478 * Async-fence was bugged in virglrenderer versions <= 1.1.1.
1479 */
1480 error_report("drm requires egl display and virglrenderer >= 1.2.0");
1481 return -EINVAL;
1482 }
1483 }
1484 #endif
1485
1486 ret = virgl_renderer_init(g, flags, &virtio_gpu_3d_cbs);
1487 if (ret != 0) {
1488 error_report("virgl could not be initialized: %d", ret);
1489 return ret;
1490 }
1491
1492 gl->fence_poll = timer_new_ms(QEMU_CLOCK_VIRTUAL,
1493 virtio_gpu_fence_poll, g);
1494
1495 if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
1496 gl->print_stats = timer_new_ms(QEMU_CLOCK_VIRTUAL,
1497 virtio_gpu_print_stats, g);
1498 timer_mod(gl->print_stats,
1499 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
1500 }
1501
1502 #if VIRGL_VERSION_MAJOR >= 1
1503 gl->cmdq_resume_bh = virtio_bh_io_new_guarded(DEVICE(g),
1504 virtio_gpu_virgl_resume_cmdq_bh,
1505 g);
1506 #if VIRGL_CHECK_VERSION(1, 1, 2)
1507 gl->async_fence_bh = virtio_bh_io_new_guarded(DEVICE(g),
1508 virtio_gpu_virgl_async_fence_bh,
1509 g);
1510 #endif
1511 #endif
1512
1513 return 0;
1514 }
1515
1516 bool virtio_gpu_virgl_update_render_state(VirtIOGPU *g)
1517 {
1518 VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1519
1520 switch (gl->renderer_state) {
1521 case RS_RESET:
1522 virgl_renderer_force_ctx_0();
1523
1524 if (!virtio_gpu_virgl_reset(g)) {
1525 return false;
1526 }
1527 /* fallthrough */
1528 case RS_START:
1529 if (virtio_gpu_virgl_init(g)) {
1530 gl->renderer_state = RS_INIT_FAILED;
1531 return false;
1532 }
1533
1534 gl->renderer_state = RS_INITED;
1535 break;
1536 case RS_INIT_FAILED:
1537 return false;
1538 case RS_INITED:
1539 break;
1540 }
1541
1542 return true;
1543 }
1544
1545 static void virtio_gpu_virgl_add_capset(GArray *capset_ids, uint32_t capset_id)
1546 {
1547 g_array_append_val(capset_ids, capset_id);
1548 }
1549
1550 GArray *virtio_gpu_virgl_get_capsets(VirtIOGPU *g)
1551 {
1552 uint32_t capset_max_ver, capset_max_size;
1553 GArray *capset_ids;
1554
1555 capset_ids = g_array_new(false, false, sizeof(uint32_t));
1556
1557 /* VIRGL is always supported. */
1558 virtio_gpu_virgl_add_capset(capset_ids, VIRTIO_GPU_CAPSET_VIRGL);
1559
1560 virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VIRGL2,
1561 &capset_max_ver,
1562 &capset_max_size);
1563 if (capset_max_ver) {
1564 virtio_gpu_virgl_add_capset(capset_ids, VIRTIO_GPU_CAPSET_VIRGL2);
1565 }
1566
1567 if (virtio_gpu_venus_enabled(g->parent_obj.conf)) {
1568 virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VENUS,
1569 &capset_max_ver,
1570 &capset_max_size);
1571 if (capset_max_size) {
1572 virtio_gpu_virgl_add_capset(capset_ids, VIRTIO_GPU_CAPSET_VENUS);
1573 }
1574 }
1575
1576 if (virtio_gpu_drm_enabled(g->parent_obj.conf)) {
1577 virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_DRM,
1578 &capset_max_ver,
1579 &capset_max_size);
1580 if (capset_max_size) {
1581 virtio_gpu_virgl_add_capset(capset_ids, VIRTIO_GPU_CAPSET_DRM);
1582 }
1583 }
1584
1585 return capset_ids;
1586 }