master
c 739 lines 21.5 KB
Raw
1 /*
2 * vhost-user GPU Device
3 *
4 * Copyright Red Hat, Inc. 2018
5 *
6 * Authors:
7 * Marc-André Lureau <marcandre.lureau@redhat.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
11 */
12
13 #include "qemu/osdep.h"
14 #include "qemu/error-report.h"
15 #include "qemu/sockets.h"
16 #include "hw/core/qdev-properties.h"
17 #include "hw/virtio/virtio-gpu.h"
18 #include "chardev/char-fe.h"
19 #include "qapi/error.h"
20 #include "migration/blocker.h"
21 #include "standard-headers/drm/drm_fourcc.h"
22
23 typedef enum VhostUserGpuRequest {
24 VHOST_USER_GPU_NONE = 0,
25 VHOST_USER_GPU_GET_PROTOCOL_FEATURES,
26 VHOST_USER_GPU_SET_PROTOCOL_FEATURES,
27 VHOST_USER_GPU_GET_DISPLAY_INFO,
28 VHOST_USER_GPU_CURSOR_POS,
29 VHOST_USER_GPU_CURSOR_POS_HIDE,
30 VHOST_USER_GPU_CURSOR_UPDATE,
31 VHOST_USER_GPU_SCANOUT,
32 VHOST_USER_GPU_UPDATE,
33 VHOST_USER_GPU_DMABUF_SCANOUT,
34 VHOST_USER_GPU_DMABUF_UPDATE,
35 VHOST_USER_GPU_GET_EDID,
36 VHOST_USER_GPU_DMABUF_SCANOUT2,
37 } VhostUserGpuRequest;
38
39 typedef struct VhostUserGpuDisplayInfoReply {
40 struct virtio_gpu_resp_display_info info;
41 } VhostUserGpuDisplayInfoReply;
42
43 typedef struct VhostUserGpuCursorPos {
44 uint32_t scanout_id;
45 uint32_t x;
46 uint32_t y;
47 } QEMU_PACKED VhostUserGpuCursorPos;
48
49 typedef struct VhostUserGpuCursorUpdate {
50 VhostUserGpuCursorPos pos;
51 uint32_t hot_x;
52 uint32_t hot_y;
53 uint32_t data[64 * 64];
54 } QEMU_PACKED VhostUserGpuCursorUpdate;
55
56 typedef struct VhostUserGpuScanout {
57 uint32_t scanout_id;
58 uint32_t width;
59 uint32_t height;
60 } QEMU_PACKED VhostUserGpuScanout;
61
62 typedef struct VhostUserGpuUpdate {
63 uint32_t scanout_id;
64 uint32_t x;
65 uint32_t y;
66 uint32_t width;
67 uint32_t height;
68 uint8_t data[];
69 } QEMU_PACKED VhostUserGpuUpdate;
70
71 typedef struct VhostUserGpuDMABUFScanout {
72 uint32_t scanout_id;
73 uint32_t x;
74 uint32_t y;
75 uint32_t width;
76 uint32_t height;
77 uint32_t fd_width;
78 uint32_t fd_height;
79 uint32_t fd_stride;
80 uint32_t fd_flags;
81 int fd_drm_fourcc;
82 } QEMU_PACKED VhostUserGpuDMABUFScanout;
83
84 typedef struct VhostUserGpuDMABUFScanout2 {
85 struct VhostUserGpuDMABUFScanout dmabuf_scanout;
86 uint64_t modifier;
87 } QEMU_PACKED VhostUserGpuDMABUFScanout2;
88
89 typedef struct VhostUserGpuEdidRequest {
90 uint32_t scanout_id;
91 } QEMU_PACKED VhostUserGpuEdidRequest;
92
93 typedef struct VhostUserGpuMsg {
94 uint32_t request; /* VhostUserGpuRequest */
95 uint32_t flags;
96 uint32_t size; /* the following payload size */
97 union {
98 VhostUserGpuCursorPos cursor_pos;
99 VhostUserGpuCursorUpdate cursor_update;
100 VhostUserGpuScanout scanout;
101 VhostUserGpuUpdate update;
102 VhostUserGpuDMABUFScanout dmabuf_scanout;
103 VhostUserGpuDMABUFScanout2 dmabuf_scanout2;
104 VhostUserGpuEdidRequest edid_req;
105 struct virtio_gpu_resp_edid resp_edid;
106 struct virtio_gpu_resp_display_info display_info;
107 uint64_t u64;
108 } payload;
109 } QEMU_PACKED VhostUserGpuMsg;
110
111 static VhostUserGpuMsg m __attribute__ ((unused));
112 #define VHOST_USER_GPU_HDR_SIZE \
113 (sizeof(m.request) + sizeof(m.size) + sizeof(m.flags))
114
115 #define VHOST_USER_GPU_MSG_FLAG_REPLY 0x4
116
117 #define VHOST_USER_GPU_PROTOCOL_F_EDID 0
118 #define VHOST_USER_GPU_PROTOCOL_F_DMABUF2 1
119
120 static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked);
121
122 static size_t
123 vhost_user_gpu_min_payload_size(VhostUserGpuRequest request)
124 {
125 switch (request) {
126 case VHOST_USER_GPU_CURSOR_POS:
127 case VHOST_USER_GPU_CURSOR_POS_HIDE:
128 return sizeof(VhostUserGpuCursorPos);
129 case VHOST_USER_GPU_CURSOR_UPDATE:
130 return sizeof(VhostUserGpuCursorUpdate);
131 case VHOST_USER_GPU_GET_EDID:
132 return sizeof(VhostUserGpuEdidRequest);
133 case VHOST_USER_GPU_SCANOUT:
134 return sizeof(VhostUserGpuScanout);
135 case VHOST_USER_GPU_DMABUF_SCANOUT:
136 return sizeof(VhostUserGpuDMABUFScanout);
137 case VHOST_USER_GPU_DMABUF_SCANOUT2:
138 return sizeof(VhostUserGpuDMABUFScanout2);
139 case VHOST_USER_GPU_DMABUF_UPDATE:
140 case VHOST_USER_GPU_UPDATE:
141 return sizeof(VhostUserGpuUpdate);
142 default:
143 return 0;
144 }
145 }
146
147 static void
148 vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg)
149 {
150 VhostUserGpuCursorPos *pos = &msg->payload.cursor_pos;
151 struct virtio_gpu_scanout *s;
152
153 if (pos->scanout_id >= g->parent_obj.conf.max_outputs) {
154 return;
155 }
156 s = &g->parent_obj.scanout[pos->scanout_id];
157
158 if (msg->request == VHOST_USER_GPU_CURSOR_UPDATE) {
159 VhostUserGpuCursorUpdate *up = &msg->payload.cursor_update;
160 if (!s->current_cursor) {
161 s->current_cursor = cursor_alloc(64, 64);
162 }
163
164 s->current_cursor->hot_x = up->hot_x;
165 s->current_cursor->hot_y = up->hot_y;
166
167 memcpy(s->current_cursor->data, up->data,
168 64 * 64 * sizeof(uint32_t));
169
170 qemu_console_set_cursor(s->con, s->current_cursor);
171 }
172
173 qemu_console_set_mouse(s->con, pos->x, pos->y,
174 msg->request != VHOST_USER_GPU_CURSOR_POS_HIDE);
175 }
176
177 static void
178 vhost_user_gpu_send_msg(VhostUserGPU *g, const VhostUserGpuMsg *msg)
179 {
180 qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)msg,
181 VHOST_USER_GPU_HDR_SIZE + msg->size);
182 }
183
184 static void
185 vhost_user_gpu_unblock(VhostUserGPU *g)
186 {
187 VhostUserGpuMsg msg = {
188 .request = VHOST_USER_GPU_DMABUF_UPDATE,
189 .flags = VHOST_USER_GPU_MSG_FLAG_REPLY,
190 };
191
192 vhost_user_gpu_send_msg(g, &msg);
193 }
194
195 static void
196 vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
197 {
198 QemuConsole *con = NULL;
199 struct virtio_gpu_scanout *s;
200
201 switch (msg->request) {
202 case VHOST_USER_GPU_GET_PROTOCOL_FEATURES: {
203 VhostUserGpuMsg reply = {
204 .request = msg->request,
205 .flags = VHOST_USER_GPU_MSG_FLAG_REPLY,
206 .size = sizeof(uint64_t),
207 .payload = {
208 .u64 = (1 << VHOST_USER_GPU_PROTOCOL_F_EDID) |
209 (1 << VHOST_USER_GPU_PROTOCOL_F_DMABUF2)
210 }
211 };
212
213 vhost_user_gpu_send_msg(g, &reply);
214 break;
215 }
216 case VHOST_USER_GPU_SET_PROTOCOL_FEATURES: {
217 break;
218 }
219 case VHOST_USER_GPU_GET_DISPLAY_INFO: {
220 struct virtio_gpu_resp_display_info display_info = { {} };
221 VhostUserGpuMsg reply = {
222 .request = msg->request,
223 .flags = VHOST_USER_GPU_MSG_FLAG_REPLY,
224 .size = sizeof(struct virtio_gpu_resp_display_info),
225 };
226
227 display_info.hdr.type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO;
228 virtio_gpu_base_fill_display_info(VIRTIO_GPU_BASE(g), &display_info);
229 memcpy(&reply.payload.display_info, &display_info,
230 sizeof(display_info));
231 vhost_user_gpu_send_msg(g, &reply);
232 break;
233 }
234 case VHOST_USER_GPU_GET_EDID: {
235 VhostUserGpuEdidRequest *m = &msg->payload.edid_req;
236 struct virtio_gpu_resp_edid resp = { {} };
237 VhostUserGpuMsg reply = {
238 .request = msg->request,
239 .flags = VHOST_USER_GPU_MSG_FLAG_REPLY,
240 .size = sizeof(reply.payload.resp_edid),
241 };
242
243 if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
244 error_report("invalid scanout: %d", m->scanout_id);
245 break;
246 }
247
248 resp.hdr.type = VIRTIO_GPU_RESP_OK_EDID;
249 virtio_gpu_base_generate_edid(VIRTIO_GPU_BASE(g), m->scanout_id, &resp);
250 memcpy(&reply.payload.resp_edid, &resp, sizeof(resp));
251 vhost_user_gpu_send_msg(g, &reply);
252 break;
253 }
254 case VHOST_USER_GPU_SCANOUT: {
255 VhostUserGpuScanout *m = &msg->payload.scanout;
256
257 if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
258 return;
259 }
260
261 g->parent_obj.enable = 1;
262 s = &g->parent_obj.scanout[m->scanout_id];
263 con = s->con;
264
265 if (m->width == 0) {
266 qemu_console_set_surface(con, NULL);
267 } else {
268 s->ds = qemu_create_displaysurface(m->width, m->height);
269 /* replace surface on next update */
270 }
271
272 break;
273 }
274 case VHOST_USER_GPU_DMABUF_SCANOUT2:
275 case VHOST_USER_GPU_DMABUF_SCANOUT: {
276 VhostUserGpuDMABUFScanout *m = &msg->payload.dmabuf_scanout;
277 int fd = qemu_chr_fe_get_msgfd(&g->vhost_chr);
278 uint32_t offset = 0;
279 uint32_t stride = m->fd_stride;
280 uint64_t modifier = DRM_FORMAT_MOD_INVALID;
281 QemuDmaBuf *dmabuf;
282
283 if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
284 error_report("invalid scanout: %d", m->scanout_id);
285 if (fd >= 0) {
286 close(fd);
287 }
288 break;
289 }
290
291 g->parent_obj.enable = 1;
292 con = g->parent_obj.scanout[m->scanout_id].con;
293 dmabuf = g->dmabuf[m->scanout_id];
294
295 if (dmabuf) {
296 qemu_dmabuf_close(dmabuf);
297 qemu_console_gl_release_dmabuf(con, dmabuf);
298 g_clear_pointer(&dmabuf, qemu_dmabuf_free);
299 }
300
301 if (fd == -1) {
302 qemu_console_gl_scanout_disable(con);
303 g->dmabuf[m->scanout_id] = NULL;
304 break;
305 }
306
307 if (msg->request == VHOST_USER_GPU_DMABUF_SCANOUT2) {
308 VhostUserGpuDMABUFScanout2 *m2 = &msg->payload.dmabuf_scanout2;
309 modifier = m2->modifier;
310 }
311
312 dmabuf = qemu_dmabuf_new(m->width, m->height,
313 &offset, &stride, 0, 0,
314 m->fd_width, m->fd_height,
315 m->fd_drm_fourcc, modifier,
316 &fd, 1, false, m->fd_flags &
317 VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP);
318
319 qemu_console_gl_scanout_dmabuf(con, dmabuf);
320 g->dmabuf[m->scanout_id] = dmabuf;
321 break;
322 }
323 case VHOST_USER_GPU_DMABUF_UPDATE: {
324 VhostUserGpuUpdate *m = &msg->payload.update;
325
326 if (m->scanout_id >= g->parent_obj.conf.max_outputs ||
327 !g->parent_obj.scanout[m->scanout_id].con) {
328 error_report("invalid scanout update: %d", m->scanout_id);
329 vhost_user_gpu_unblock(g);
330 break;
331 }
332
333 con = g->parent_obj.scanout[m->scanout_id].con;
334 if (!qemu_console_has_gl(con)) {
335 error_report("console doesn't support GL!");
336 vhost_user_gpu_unblock(g);
337 break;
338 }
339 g->backend_blocked = true;
340 qemu_console_gl_update(con, m->x, m->y, m->width, m->height);
341 break;
342 }
343 #ifdef CONFIG_PIXMAN
344 case VHOST_USER_GPU_UPDATE: {
345 VhostUserGpuUpdate *m = &msg->payload.update;
346
347 if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
348 break;
349 }
350
351 if ((uint64_t)m->width * m->height >
352 (msg->size - sizeof(VhostUserGpuUpdate)) / sizeof(uint32_t)) {
353 error_report("vhost-user-gpu: update payload too small"
354 " for %ux%u", m->width, m->height);
355 break;
356 }
357
358 s = &g->parent_obj.scanout[m->scanout_id];
359 con = s->con;
360 pixman_image_t *image =
361 pixman_image_create_bits(PIXMAN_x8r8g8b8,
362 m->width,
363 m->height,
364 (uint32_t *)m->data,
365 m->width * 4);
366
367 pixman_image_composite(PIXMAN_OP_SRC,
368 image, NULL, s->ds->image,
369 0, 0, 0, 0, m->x, m->y, m->width, m->height);
370
371 pixman_image_unref(image);
372 if (qemu_console_surface(con) != s->ds) {
373 qemu_console_set_surface(con, s->ds);
374 } else {
375 qemu_console_update(con, m->x, m->y, m->width, m->height);
376 }
377 break;
378 }
379 #endif
380 default:
381 g_warning("unhandled message %d %d", msg->request, msg->size);
382 }
383
384 if (con && qemu_console_is_gl_blocked(con)) {
385 vhost_user_gpu_update_blocked(g, true);
386 }
387 }
388
389 static void
390 vhost_user_gpu_chr_read(void *opaque)
391 {
392 VhostUserGPU *g = opaque;
393 VhostUserGpuMsg *msg = NULL;
394 VhostUserGpuRequest request;
395 uint32_t size, flags;
396 int r;
397
398 r = qemu_chr_fe_read_all(&g->vhost_chr,
399 (uint8_t *)&request, sizeof(uint32_t));
400 if (r != sizeof(uint32_t)) {
401 error_report("failed to read msg header: %d, %d", r, errno);
402 goto end;
403 }
404
405 r = qemu_chr_fe_read_all(&g->vhost_chr,
406 (uint8_t *)&flags, sizeof(uint32_t));
407 if (r != sizeof(uint32_t)) {
408 error_report("failed to read msg flags");
409 goto end;
410 }
411
412 r = qemu_chr_fe_read_all(&g->vhost_chr,
413 (uint8_t *)&size, sizeof(uint32_t));
414 if (r != sizeof(uint32_t)) {
415 error_report("failed to read msg size");
416 goto end;
417 }
418
419 msg = g_malloc(VHOST_USER_GPU_HDR_SIZE + size);
420
421 r = qemu_chr_fe_read_all(&g->vhost_chr,
422 (uint8_t *)&msg->payload, size);
423 if (r != size) {
424 error_report("failed to read msg payload %d != %d", r, size);
425 goto end;
426 }
427
428 msg->request = request;
429 msg->flags = flags;
430 msg->size = size;
431
432 if (size < vhost_user_gpu_min_payload_size(request)) {
433 error_report("vhost-user-gpu: message %d payload too small", request);
434 goto end;
435 }
436
437 if (request == VHOST_USER_GPU_CURSOR_UPDATE ||
438 request == VHOST_USER_GPU_CURSOR_POS ||
439 request == VHOST_USER_GPU_CURSOR_POS_HIDE) {
440 vhost_user_gpu_handle_cursor(g, msg);
441 } else {
442 vhost_user_gpu_handle_display(g, msg);
443 }
444
445 end:
446 g_free(msg);
447 }
448
449 static void
450 vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked)
451 {
452 qemu_set_fd_handler(g->vhost_gpu_fd,
453 blocked ? NULL : vhost_user_gpu_chr_read, NULL, g);
454 }
455
456 static void
457 vhost_user_gpu_gl_flushed(VirtIOGPUBase *b)
458 {
459 VhostUserGPU *g = VHOST_USER_GPU(b);
460
461 if (g->backend_blocked) {
462 vhost_user_gpu_unblock(g);
463 g->backend_blocked = false;
464 }
465
466 vhost_user_gpu_update_blocked(g, false);
467 }
468
469 static bool
470 vhost_user_gpu_do_set_socket(VhostUserGPU *g, Error **errp)
471 {
472 Chardev *chr;
473 int sv[2];
474
475 if (qemu_socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
476 error_setg_errno(errp, errno, "socketpair() failed");
477 return false;
478 }
479
480 chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET));
481 if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) {
482 error_setg(errp, "Failed to make socket chardev");
483 goto err;
484 }
485 if (!qemu_chr_fe_init(&g->vhost_chr, chr, errp)) {
486 goto err;
487 }
488 if (vhost_user_gpu_set_socket(&g->vhost->dev, sv[1]) < 0) {
489 error_setg(errp, "Failed to set vhost-user-gpu socket");
490 qemu_chr_fe_deinit(&g->vhost_chr, false);
491 goto err;
492 }
493
494 g->vhost_gpu_fd = sv[0];
495 vhost_user_gpu_update_blocked(g, false);
496 close(sv[1]);
497 return true;
498
499 err:
500 close(sv[0]);
501 close(sv[1]);
502 if (chr) {
503 object_unref(OBJECT(chr));
504 }
505 return false;
506 }
507
508 static void
509 vhost_user_gpu_get_config(VirtIODevice *vdev, uint8_t *config_data)
510 {
511 VhostUserGPU *g = VHOST_USER_GPU(vdev);
512 VirtIOGPUBase *b = VIRTIO_GPU_BASE(vdev);
513 struct virtio_gpu_config *vgconfig =
514 (struct virtio_gpu_config *)config_data;
515 Error *local_err = NULL;
516 int ret;
517
518 memset(config_data, 0, sizeof(struct virtio_gpu_config));
519
520 ret = vhost_dev_get_config(&g->vhost->dev,
521 config_data, sizeof(struct virtio_gpu_config),
522 &local_err);
523 if (ret) {
524 error_report_err(local_err);
525 return;
526 }
527
528 /* those fields are managed by qemu */
529 vgconfig->num_scanouts = b->virtio_config.num_scanouts;
530 vgconfig->events_read = b->virtio_config.events_read;
531 vgconfig->events_clear = b->virtio_config.events_clear;
532 }
533
534 static void
535 vhost_user_gpu_set_config(VirtIODevice *vdev,
536 const uint8_t *config_data)
537 {
538 VhostUserGPU *g = VHOST_USER_GPU(vdev);
539 VirtIOGPUBase *b = VIRTIO_GPU_BASE(vdev);
540 const struct virtio_gpu_config *vgconfig =
541 (const struct virtio_gpu_config *)config_data;
542 int ret;
543
544 if (vgconfig->events_clear) {
545 b->virtio_config.events_read &= ~vgconfig->events_clear;
546 }
547
548 ret = vhost_dev_set_config(&g->vhost->dev, config_data,
549 0, sizeof(struct virtio_gpu_config),
550 VHOST_SET_CONFIG_TYPE_FRONTEND);
551 if (ret) {
552 error_report("vhost-user-gpu: set device config space failed");
553 return;
554 }
555 }
556
557 static int
558 vhost_user_gpu_set_status(VirtIODevice *vdev, uint8_t val)
559 {
560 VhostUserGPU *g = VHOST_USER_GPU(vdev);
561 Error *err = NULL;
562
563 if (val & VIRTIO_CONFIG_S_DRIVER_OK && vdev->vm_running) {
564 if (!vhost_user_gpu_do_set_socket(g, &err)) {
565 error_report_err(err);
566 return 0;
567 }
568 vhost_user_backend_start(g->vhost);
569 } else {
570 int ret;
571
572 /* unblock any wait and stop processing */
573 if (g->vhost_gpu_fd != -1) {
574 vhost_user_gpu_update_blocked(g, true);
575 qemu_chr_fe_deinit(&g->vhost_chr, true);
576 g->vhost_gpu_fd = -1;
577 }
578 ret = vhost_user_backend_stop(g->vhost);
579 if (ret < 0) {
580 return ret;
581 }
582 }
583 return 0;
584 }
585
586 static bool
587 vhost_user_gpu_guest_notifier_pending(VirtIODevice *vdev, int idx)
588 {
589 VhostUserGPU *g = VHOST_USER_GPU(vdev);
590
591 /*
592 * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1
593 * as the macro of configure interrupt's IDX, If this driver does not
594 * support, the function will return
595 */
596
597 if (idx == VIRTIO_CONFIG_IRQ_IDX) {
598 return false;
599 }
600 return vhost_virtqueue_pending(&g->vhost->dev, idx);
601 }
602
603 static void
604 vhost_user_gpu_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask)
605 {
606 VhostUserGPU *g = VHOST_USER_GPU(vdev);
607
608 /*
609 * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1
610 * as the macro of configure interrupt's IDX, If this driver does not
611 * support, the function will return
612 */
613
614 if (idx == VIRTIO_CONFIG_IRQ_IDX) {
615 return;
616 }
617 vhost_virtqueue_mask(&g->vhost->dev, vdev, idx, mask);
618 }
619
620 static void
621 vhost_user_gpu_instance_init(Object *obj)
622 {
623 VhostUserGPU *g = VHOST_USER_GPU(obj);
624
625 g->vhost = VHOST_USER_BACKEND(object_new(TYPE_VHOST_USER_BACKEND));
626 object_property_add_alias(obj, "chardev",
627 OBJECT(g->vhost), "chardev");
628 }
629
630 static void
631 vhost_user_gpu_instance_finalize(Object *obj)
632 {
633 VhostUserGPU *g = VHOST_USER_GPU(obj);
634
635 object_unref(OBJECT(g->vhost));
636 }
637
638 static void
639 vhost_user_gpu_reset(VirtIODevice *vdev)
640 {
641 VhostUserGPU *g = VHOST_USER_GPU(vdev);
642
643 virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev));
644
645 vhost_user_backend_stop(g->vhost);
646 }
647
648 static int
649 vhost_user_gpu_config_change(struct vhost_dev *dev)
650 {
651 error_report("vhost-user-gpu: unhandled backend config change");
652 return -1;
653 }
654
655 static const VhostDevConfigOps config_ops = {
656 .vhost_dev_config_notifier = vhost_user_gpu_config_change,
657 };
658
659 static void
660 vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp)
661 {
662 VhostUserGPU *g = VHOST_USER_GPU(qdev);
663 VirtIODevice *vdev = VIRTIO_DEVICE(g);
664
665 vhost_dev_set_config_notifier(&g->vhost->dev, &config_ops);
666 if (vhost_user_backend_dev_init(g->vhost, vdev, 2, errp) < 0) {
667 return;
668 }
669
670 /* existing backend may send DMABUF, so let's add that requirement */
671 g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_DMABUF_ENABLED;
672 if (vhost_dev_has_feature(&g->vhost->dev, VIRTIO_GPU_F_VIRGL)) {
673 g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED;
674 }
675 if (vhost_dev_has_feature(&g->vhost->dev, VIRTIO_GPU_F_EDID)) {
676 g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_EDID_ENABLED;
677 } else {
678 error_report("EDID requested but the backend doesn't support it.");
679 g->parent_obj.conf.flags &= ~(1 << VIRTIO_GPU_FLAG_EDID_ENABLED);
680 }
681 if (vhost_dev_has_feature(&g->vhost->dev, VIRTIO_GPU_F_RESOURCE_UUID)) {
682 g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_RESOURCE_UUID_ENABLED;
683 }
684
685 if (!virtio_gpu_base_device_realize(qdev, NULL, NULL, errp)) {
686 return;
687 }
688
689 g->vhost_gpu_fd = -1;
690 }
691
692 static struct vhost_dev *vhost_user_gpu_get_vhost(VirtIODevice *vdev)
693 {
694 VhostUserGPU *g = VHOST_USER_GPU(vdev);
695 return g->vhost ? &g->vhost->dev : NULL;
696 }
697
698 static const Property vhost_user_gpu_properties[] = {
699 VIRTIO_GPU_BASE_PROPERTIES(VhostUserGPU, parent_obj.conf),
700 };
701
702 static void
703 vhost_user_gpu_class_init(ObjectClass *klass, const void *data)
704 {
705 DeviceClass *dc = DEVICE_CLASS(klass);
706 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
707 VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_CLASS(klass);
708
709 vgc->gl_flushed = vhost_user_gpu_gl_flushed;
710
711 vdc->realize = vhost_user_gpu_device_realize;
712 vdc->reset = vhost_user_gpu_reset;
713 vdc->set_status = vhost_user_gpu_set_status;
714 vdc->guest_notifier_mask = vhost_user_gpu_guest_notifier_mask;
715 vdc->guest_notifier_pending = vhost_user_gpu_guest_notifier_pending;
716 vdc->get_config = vhost_user_gpu_get_config;
717 vdc->set_config = vhost_user_gpu_set_config;
718 vdc->get_vhost = vhost_user_gpu_get_vhost;
719
720 device_class_set_props(dc, vhost_user_gpu_properties);
721 }
722
723 static const TypeInfo vhost_user_gpu_info = {
724 .name = TYPE_VHOST_USER_GPU,
725 .parent = TYPE_VIRTIO_GPU_BASE,
726 .instance_size = sizeof(VhostUserGPU),
727 .instance_init = vhost_user_gpu_instance_init,
728 .instance_finalize = vhost_user_gpu_instance_finalize,
729 .class_init = vhost_user_gpu_class_init,
730 };
731 module_obj(TYPE_VHOST_USER_GPU);
732 module_kconfig(VHOST_USER_GPU);
733
734 static void vhost_user_gpu_register_types(void)
735 {
736 type_register_static(&vhost_user_gpu_info);
737 }
738
739 type_init(vhost_user_gpu_register_types)