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