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