xref: /qemu/hw/display/virtio-gpu.c (revision 62232bf4)
1*62232bf4SGerd Hoffmann /*
2*62232bf4SGerd Hoffmann  * Virtio GPU Device
3*62232bf4SGerd Hoffmann  *
4*62232bf4SGerd Hoffmann  * Copyright Red Hat, Inc. 2013-2014
5*62232bf4SGerd Hoffmann  *
6*62232bf4SGerd Hoffmann  * Authors:
7*62232bf4SGerd Hoffmann  *     Dave Airlie <airlied@redhat.com>
8*62232bf4SGerd Hoffmann  *     Gerd Hoffmann <kraxel@redhat.com>
9*62232bf4SGerd Hoffmann  *
10*62232bf4SGerd Hoffmann  * This work is licensed under the terms of the GNU GPL, version 2.
11*62232bf4SGerd Hoffmann  * See the COPYING file in the top-level directory.
12*62232bf4SGerd Hoffmann  */
13*62232bf4SGerd Hoffmann 
14*62232bf4SGerd Hoffmann #include "qemu-common.h"
15*62232bf4SGerd Hoffmann #include "qemu/iov.h"
16*62232bf4SGerd Hoffmann #include "ui/console.h"
17*62232bf4SGerd Hoffmann #include "trace.h"
18*62232bf4SGerd Hoffmann #include "hw/virtio/virtio.h"
19*62232bf4SGerd Hoffmann #include "hw/virtio/virtio-gpu.h"
20*62232bf4SGerd Hoffmann #include "hw/virtio/virtio-bus.h"
21*62232bf4SGerd Hoffmann 
22*62232bf4SGerd Hoffmann static struct virtio_gpu_simple_resource*
23*62232bf4SGerd Hoffmann virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id);
24*62232bf4SGerd Hoffmann 
25*62232bf4SGerd Hoffmann static void update_cursor_data_simple(VirtIOGPU *g,
26*62232bf4SGerd Hoffmann                                       struct virtio_gpu_scanout *s,
27*62232bf4SGerd Hoffmann                                       uint32_t resource_id)
28*62232bf4SGerd Hoffmann {
29*62232bf4SGerd Hoffmann     struct virtio_gpu_simple_resource *res;
30*62232bf4SGerd Hoffmann     uint32_t pixels;
31*62232bf4SGerd Hoffmann 
32*62232bf4SGerd Hoffmann     res = virtio_gpu_find_resource(g, resource_id);
33*62232bf4SGerd Hoffmann     if (!res) {
34*62232bf4SGerd Hoffmann         return;
35*62232bf4SGerd Hoffmann     }
36*62232bf4SGerd Hoffmann 
37*62232bf4SGerd Hoffmann     if (pixman_image_get_width(res->image)  != s->current_cursor->width ||
38*62232bf4SGerd Hoffmann         pixman_image_get_height(res->image) != s->current_cursor->height) {
39*62232bf4SGerd Hoffmann         return;
40*62232bf4SGerd Hoffmann     }
41*62232bf4SGerd Hoffmann 
42*62232bf4SGerd Hoffmann     pixels = s->current_cursor->width * s->current_cursor->height;
43*62232bf4SGerd Hoffmann     memcpy(s->current_cursor->data,
44*62232bf4SGerd Hoffmann            pixman_image_get_data(res->image),
45*62232bf4SGerd Hoffmann            pixels * sizeof(uint32_t));
46*62232bf4SGerd Hoffmann }
47*62232bf4SGerd Hoffmann 
48*62232bf4SGerd Hoffmann static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
49*62232bf4SGerd Hoffmann {
50*62232bf4SGerd Hoffmann     struct virtio_gpu_scanout *s;
51*62232bf4SGerd Hoffmann 
52*62232bf4SGerd Hoffmann     if (cursor->pos.scanout_id >= g->conf.max_outputs) {
53*62232bf4SGerd Hoffmann         return;
54*62232bf4SGerd Hoffmann     }
55*62232bf4SGerd Hoffmann     s = &g->scanout[cursor->pos.scanout_id];
56*62232bf4SGerd Hoffmann 
57*62232bf4SGerd Hoffmann     if (cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR) {
58*62232bf4SGerd Hoffmann         if (!s->current_cursor) {
59*62232bf4SGerd Hoffmann             s->current_cursor = cursor_alloc(64, 64);
60*62232bf4SGerd Hoffmann         }
61*62232bf4SGerd Hoffmann 
62*62232bf4SGerd Hoffmann         s->current_cursor->hot_x = cursor->hot_x;
63*62232bf4SGerd Hoffmann         s->current_cursor->hot_y = cursor->hot_y;
64*62232bf4SGerd Hoffmann 
65*62232bf4SGerd Hoffmann         if (cursor->resource_id > 0) {
66*62232bf4SGerd Hoffmann             update_cursor_data_simple(g, s, cursor->resource_id);
67*62232bf4SGerd Hoffmann         }
68*62232bf4SGerd Hoffmann         dpy_cursor_define(s->con, s->current_cursor);
69*62232bf4SGerd Hoffmann     }
70*62232bf4SGerd Hoffmann     dpy_mouse_set(s->con, cursor->pos.x, cursor->pos.y,
71*62232bf4SGerd Hoffmann                   cursor->resource_id ? 1 : 0);
72*62232bf4SGerd Hoffmann }
73*62232bf4SGerd Hoffmann 
74*62232bf4SGerd Hoffmann static void virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config)
75*62232bf4SGerd Hoffmann {
76*62232bf4SGerd Hoffmann     VirtIOGPU *g = VIRTIO_GPU(vdev);
77*62232bf4SGerd Hoffmann     memcpy(config, &g->virtio_config, sizeof(g->virtio_config));
78*62232bf4SGerd Hoffmann }
79*62232bf4SGerd Hoffmann 
80*62232bf4SGerd Hoffmann static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
81*62232bf4SGerd Hoffmann {
82*62232bf4SGerd Hoffmann     VirtIOGPU *g = VIRTIO_GPU(vdev);
83*62232bf4SGerd Hoffmann     struct virtio_gpu_config vgconfig;
84*62232bf4SGerd Hoffmann 
85*62232bf4SGerd Hoffmann     memcpy(&vgconfig, config, sizeof(g->virtio_config));
86*62232bf4SGerd Hoffmann 
87*62232bf4SGerd Hoffmann     if (vgconfig.events_clear) {
88*62232bf4SGerd Hoffmann         g->virtio_config.events_read &= ~vgconfig.events_clear;
89*62232bf4SGerd Hoffmann     }
90*62232bf4SGerd Hoffmann }
91*62232bf4SGerd Hoffmann 
92*62232bf4SGerd Hoffmann static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features)
93*62232bf4SGerd Hoffmann {
94*62232bf4SGerd Hoffmann     return features;
95*62232bf4SGerd Hoffmann }
96*62232bf4SGerd Hoffmann 
97*62232bf4SGerd Hoffmann static void virtio_gpu_notify_event(VirtIOGPU *g, uint32_t event_type)
98*62232bf4SGerd Hoffmann {
99*62232bf4SGerd Hoffmann     g->virtio_config.events_read |= event_type;
100*62232bf4SGerd Hoffmann     virtio_notify_config(&g->parent_obj);
101*62232bf4SGerd Hoffmann }
102*62232bf4SGerd Hoffmann 
103*62232bf4SGerd Hoffmann static struct virtio_gpu_simple_resource *
104*62232bf4SGerd Hoffmann virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id)
105*62232bf4SGerd Hoffmann {
106*62232bf4SGerd Hoffmann     struct virtio_gpu_simple_resource *res;
107*62232bf4SGerd Hoffmann 
108*62232bf4SGerd Hoffmann     QTAILQ_FOREACH(res, &g->reslist, next) {
109*62232bf4SGerd Hoffmann         if (res->resource_id == resource_id) {
110*62232bf4SGerd Hoffmann             return res;
111*62232bf4SGerd Hoffmann         }
112*62232bf4SGerd Hoffmann     }
113*62232bf4SGerd Hoffmann     return NULL;
114*62232bf4SGerd Hoffmann }
115*62232bf4SGerd Hoffmann 
116*62232bf4SGerd Hoffmann void virtio_gpu_ctrl_response(VirtIOGPU *g,
117*62232bf4SGerd Hoffmann                               struct virtio_gpu_ctrl_command *cmd,
118*62232bf4SGerd Hoffmann                               struct virtio_gpu_ctrl_hdr *resp,
119*62232bf4SGerd Hoffmann                               size_t resp_len)
120*62232bf4SGerd Hoffmann {
121*62232bf4SGerd Hoffmann     size_t s;
122*62232bf4SGerd Hoffmann 
123*62232bf4SGerd Hoffmann     if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE) {
124*62232bf4SGerd Hoffmann         resp->flags |= VIRTIO_GPU_FLAG_FENCE;
125*62232bf4SGerd Hoffmann         resp->fence_id = cmd->cmd_hdr.fence_id;
126*62232bf4SGerd Hoffmann         resp->ctx_id = cmd->cmd_hdr.ctx_id;
127*62232bf4SGerd Hoffmann     }
128*62232bf4SGerd Hoffmann     s = iov_from_buf(cmd->elem.in_sg, cmd->elem.in_num, 0, resp, resp_len);
129*62232bf4SGerd Hoffmann     if (s != resp_len) {
130*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR,
131*62232bf4SGerd Hoffmann                       "%s: response size incorrect %zu vs %zu\n",
132*62232bf4SGerd Hoffmann                       __func__, s, resp_len);
133*62232bf4SGerd Hoffmann     }
134*62232bf4SGerd Hoffmann     virtqueue_push(cmd->vq, &cmd->elem, s);
135*62232bf4SGerd Hoffmann     virtio_notify(VIRTIO_DEVICE(g), cmd->vq);
136*62232bf4SGerd Hoffmann     cmd->finished = true;
137*62232bf4SGerd Hoffmann }
138*62232bf4SGerd Hoffmann 
139*62232bf4SGerd Hoffmann void virtio_gpu_ctrl_response_nodata(VirtIOGPU *g,
140*62232bf4SGerd Hoffmann                                      struct virtio_gpu_ctrl_command *cmd,
141*62232bf4SGerd Hoffmann                                      enum virtio_gpu_ctrl_type type)
142*62232bf4SGerd Hoffmann {
143*62232bf4SGerd Hoffmann     struct virtio_gpu_ctrl_hdr resp;
144*62232bf4SGerd Hoffmann 
145*62232bf4SGerd Hoffmann     memset(&resp, 0, sizeof(resp));
146*62232bf4SGerd Hoffmann     resp.type = type;
147*62232bf4SGerd Hoffmann     virtio_gpu_ctrl_response(g, cmd, &resp, sizeof(resp));
148*62232bf4SGerd Hoffmann }
149*62232bf4SGerd Hoffmann 
150*62232bf4SGerd Hoffmann static void
151*62232bf4SGerd Hoffmann virtio_gpu_fill_display_info(VirtIOGPU *g,
152*62232bf4SGerd Hoffmann                              struct virtio_gpu_resp_display_info *dpy_info)
153*62232bf4SGerd Hoffmann {
154*62232bf4SGerd Hoffmann     int i;
155*62232bf4SGerd Hoffmann 
156*62232bf4SGerd Hoffmann     for (i = 0; i < g->conf.max_outputs; i++) {
157*62232bf4SGerd Hoffmann         if (g->enabled_output_bitmask & (1 << i)) {
158*62232bf4SGerd Hoffmann             dpy_info->pmodes[i].enabled = 1;
159*62232bf4SGerd Hoffmann             dpy_info->pmodes[i].r.width = g->req_state[i].width;
160*62232bf4SGerd Hoffmann             dpy_info->pmodes[i].r.height = g->req_state[i].height;
161*62232bf4SGerd Hoffmann         }
162*62232bf4SGerd Hoffmann     }
163*62232bf4SGerd Hoffmann }
164*62232bf4SGerd Hoffmann 
165*62232bf4SGerd Hoffmann void virtio_gpu_get_display_info(VirtIOGPU *g,
166*62232bf4SGerd Hoffmann                                  struct virtio_gpu_ctrl_command *cmd)
167*62232bf4SGerd Hoffmann {
168*62232bf4SGerd Hoffmann     struct virtio_gpu_resp_display_info display_info;
169*62232bf4SGerd Hoffmann 
170*62232bf4SGerd Hoffmann     trace_virtio_gpu_cmd_get_display_info();
171*62232bf4SGerd Hoffmann     memset(&display_info, 0, sizeof(display_info));
172*62232bf4SGerd Hoffmann     display_info.hdr.type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO;
173*62232bf4SGerd Hoffmann     virtio_gpu_fill_display_info(g, &display_info);
174*62232bf4SGerd Hoffmann     virtio_gpu_ctrl_response(g, cmd, &display_info.hdr,
175*62232bf4SGerd Hoffmann                              sizeof(display_info));
176*62232bf4SGerd Hoffmann }
177*62232bf4SGerd Hoffmann 
178*62232bf4SGerd Hoffmann static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format)
179*62232bf4SGerd Hoffmann {
180*62232bf4SGerd Hoffmann     switch (virtio_gpu_format) {
181*62232bf4SGerd Hoffmann #ifdef HOST_WORDS_BIGENDIAN
182*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
183*62232bf4SGerd Hoffmann         return PIXMAN_b8g8r8x8;
184*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
185*62232bf4SGerd Hoffmann         return PIXMAN_b8g8r8a8;
186*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
187*62232bf4SGerd Hoffmann         return PIXMAN_x8r8g8b8;
188*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
189*62232bf4SGerd Hoffmann         return PIXMAN_a8r8g8b8;
190*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
191*62232bf4SGerd Hoffmann         return PIXMAN_r8g8b8x8;
192*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
193*62232bf4SGerd Hoffmann         return PIXMAN_r8g8b8a8;
194*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
195*62232bf4SGerd Hoffmann         return PIXMAN_x8b8g8r8;
196*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
197*62232bf4SGerd Hoffmann         return PIXMAN_a8b8g8r8;
198*62232bf4SGerd Hoffmann #else
199*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
200*62232bf4SGerd Hoffmann         return PIXMAN_x8r8g8b8;
201*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
202*62232bf4SGerd Hoffmann         return PIXMAN_a8r8g8b8;
203*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
204*62232bf4SGerd Hoffmann         return PIXMAN_b8g8r8x8;
205*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
206*62232bf4SGerd Hoffmann         return PIXMAN_b8g8r8a8;
207*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
208*62232bf4SGerd Hoffmann         return PIXMAN_x8b8g8r8;
209*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
210*62232bf4SGerd Hoffmann         return PIXMAN_a8b8g8r8;
211*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
212*62232bf4SGerd Hoffmann         return PIXMAN_r8g8b8x8;
213*62232bf4SGerd Hoffmann     case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
214*62232bf4SGerd Hoffmann         return PIXMAN_r8g8b8a8;
215*62232bf4SGerd Hoffmann #endif
216*62232bf4SGerd Hoffmann     default:
217*62232bf4SGerd Hoffmann         return 0;
218*62232bf4SGerd Hoffmann     }
219*62232bf4SGerd Hoffmann }
220*62232bf4SGerd Hoffmann 
221*62232bf4SGerd Hoffmann static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
222*62232bf4SGerd Hoffmann                                           struct virtio_gpu_ctrl_command *cmd)
223*62232bf4SGerd Hoffmann {
224*62232bf4SGerd Hoffmann     pixman_format_code_t pformat;
225*62232bf4SGerd Hoffmann     struct virtio_gpu_simple_resource *res;
226*62232bf4SGerd Hoffmann     struct virtio_gpu_resource_create_2d c2d;
227*62232bf4SGerd Hoffmann 
228*62232bf4SGerd Hoffmann     VIRTIO_GPU_FILL_CMD(c2d);
229*62232bf4SGerd Hoffmann     trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format,
230*62232bf4SGerd Hoffmann                                        c2d.width, c2d.height);
231*62232bf4SGerd Hoffmann 
232*62232bf4SGerd Hoffmann     if (c2d.resource_id == 0) {
233*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
234*62232bf4SGerd Hoffmann                       __func__);
235*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
236*62232bf4SGerd Hoffmann         return;
237*62232bf4SGerd Hoffmann     }
238*62232bf4SGerd Hoffmann 
239*62232bf4SGerd Hoffmann     res = virtio_gpu_find_resource(g, c2d.resource_id);
240*62232bf4SGerd Hoffmann     if (res) {
241*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
242*62232bf4SGerd Hoffmann                       __func__, c2d.resource_id);
243*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
244*62232bf4SGerd Hoffmann         return;
245*62232bf4SGerd Hoffmann     }
246*62232bf4SGerd Hoffmann 
247*62232bf4SGerd Hoffmann     res = g_new0(struct virtio_gpu_simple_resource, 1);
248*62232bf4SGerd Hoffmann 
249*62232bf4SGerd Hoffmann     res->width = c2d.width;
250*62232bf4SGerd Hoffmann     res->height = c2d.height;
251*62232bf4SGerd Hoffmann     res->format = c2d.format;
252*62232bf4SGerd Hoffmann     res->resource_id = c2d.resource_id;
253*62232bf4SGerd Hoffmann 
254*62232bf4SGerd Hoffmann     pformat = get_pixman_format(c2d.format);
255*62232bf4SGerd Hoffmann     if (!pformat) {
256*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR,
257*62232bf4SGerd Hoffmann                       "%s: host couldn't handle guest format %d\n",
258*62232bf4SGerd Hoffmann                       __func__, c2d.format);
259*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
260*62232bf4SGerd Hoffmann         return;
261*62232bf4SGerd Hoffmann     }
262*62232bf4SGerd Hoffmann     res->image = pixman_image_create_bits(pformat,
263*62232bf4SGerd Hoffmann                                           c2d.width,
264*62232bf4SGerd Hoffmann                                           c2d.height,
265*62232bf4SGerd Hoffmann                                           NULL, 0);
266*62232bf4SGerd Hoffmann 
267*62232bf4SGerd Hoffmann     if (!res->image) {
268*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR,
269*62232bf4SGerd Hoffmann                       "%s: resource creation failed %d %d %d\n",
270*62232bf4SGerd Hoffmann                       __func__, c2d.resource_id, c2d.width, c2d.height);
271*62232bf4SGerd Hoffmann         g_free(res);
272*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
273*62232bf4SGerd Hoffmann         return;
274*62232bf4SGerd Hoffmann     }
275*62232bf4SGerd Hoffmann 
276*62232bf4SGerd Hoffmann     QTAILQ_INSERT_HEAD(&g->reslist, res, next);
277*62232bf4SGerd Hoffmann }
278*62232bf4SGerd Hoffmann 
279*62232bf4SGerd Hoffmann static void virtio_gpu_resource_destroy(VirtIOGPU *g,
280*62232bf4SGerd Hoffmann                                         struct virtio_gpu_simple_resource *res)
281*62232bf4SGerd Hoffmann {
282*62232bf4SGerd Hoffmann     pixman_image_unref(res->image);
283*62232bf4SGerd Hoffmann     QTAILQ_REMOVE(&g->reslist, res, next);
284*62232bf4SGerd Hoffmann     g_free(res);
285*62232bf4SGerd Hoffmann }
286*62232bf4SGerd Hoffmann 
287*62232bf4SGerd Hoffmann static void virtio_gpu_resource_unref(VirtIOGPU *g,
288*62232bf4SGerd Hoffmann                                       struct virtio_gpu_ctrl_command *cmd)
289*62232bf4SGerd Hoffmann {
290*62232bf4SGerd Hoffmann     struct virtio_gpu_simple_resource *res;
291*62232bf4SGerd Hoffmann     struct virtio_gpu_resource_unref unref;
292*62232bf4SGerd Hoffmann 
293*62232bf4SGerd Hoffmann     VIRTIO_GPU_FILL_CMD(unref);
294*62232bf4SGerd Hoffmann     trace_virtio_gpu_cmd_res_unref(unref.resource_id);
295*62232bf4SGerd Hoffmann 
296*62232bf4SGerd Hoffmann     res = virtio_gpu_find_resource(g, unref.resource_id);
297*62232bf4SGerd Hoffmann     if (!res) {
298*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
299*62232bf4SGerd Hoffmann                       __func__, unref.resource_id);
300*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
301*62232bf4SGerd Hoffmann         return;
302*62232bf4SGerd Hoffmann     }
303*62232bf4SGerd Hoffmann     virtio_gpu_resource_destroy(g, res);
304*62232bf4SGerd Hoffmann }
305*62232bf4SGerd Hoffmann 
306*62232bf4SGerd Hoffmann static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
307*62232bf4SGerd Hoffmann                                            struct virtio_gpu_ctrl_command *cmd)
308*62232bf4SGerd Hoffmann {
309*62232bf4SGerd Hoffmann     struct virtio_gpu_simple_resource *res;
310*62232bf4SGerd Hoffmann     int h;
311*62232bf4SGerd Hoffmann     uint32_t src_offset, dst_offset, stride;
312*62232bf4SGerd Hoffmann     int bpp;
313*62232bf4SGerd Hoffmann     pixman_format_code_t format;
314*62232bf4SGerd Hoffmann     struct virtio_gpu_transfer_to_host_2d t2d;
315*62232bf4SGerd Hoffmann 
316*62232bf4SGerd Hoffmann     VIRTIO_GPU_FILL_CMD(t2d);
317*62232bf4SGerd Hoffmann     trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id);
318*62232bf4SGerd Hoffmann 
319*62232bf4SGerd Hoffmann     res = virtio_gpu_find_resource(g, t2d.resource_id);
320*62232bf4SGerd Hoffmann     if (!res || !res->iov) {
321*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
322*62232bf4SGerd Hoffmann                       __func__, t2d.resource_id);
323*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
324*62232bf4SGerd Hoffmann         return;
325*62232bf4SGerd Hoffmann     }
326*62232bf4SGerd Hoffmann 
327*62232bf4SGerd Hoffmann     if (t2d.r.x > res->width ||
328*62232bf4SGerd Hoffmann         t2d.r.y > res->height ||
329*62232bf4SGerd Hoffmann         t2d.r.width > res->width ||
330*62232bf4SGerd Hoffmann         t2d.r.height > res->height ||
331*62232bf4SGerd Hoffmann         t2d.r.x + t2d.r.width > res->width ||
332*62232bf4SGerd Hoffmann         t2d.r.y + t2d.r.height > res->height) {
333*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: transfer bounds outside resource"
334*62232bf4SGerd Hoffmann                       " bounds for resource %d: %d %d %d %d vs %d %d\n",
335*62232bf4SGerd Hoffmann                       __func__, t2d.resource_id, t2d.r.x, t2d.r.y,
336*62232bf4SGerd Hoffmann                       t2d.r.width, t2d.r.height, res->width, res->height);
337*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
338*62232bf4SGerd Hoffmann         return;
339*62232bf4SGerd Hoffmann     }
340*62232bf4SGerd Hoffmann 
341*62232bf4SGerd Hoffmann     format = pixman_image_get_format(res->image);
342*62232bf4SGerd Hoffmann     bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
343*62232bf4SGerd Hoffmann     stride = pixman_image_get_stride(res->image);
344*62232bf4SGerd Hoffmann 
345*62232bf4SGerd Hoffmann     if (t2d.offset || t2d.r.x || t2d.r.y ||
346*62232bf4SGerd Hoffmann         t2d.r.width != pixman_image_get_width(res->image)) {
347*62232bf4SGerd Hoffmann         void *img_data = pixman_image_get_data(res->image);
348*62232bf4SGerd Hoffmann         for (h = 0; h < t2d.r.height; h++) {
349*62232bf4SGerd Hoffmann             src_offset = t2d.offset + stride * h;
350*62232bf4SGerd Hoffmann             dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp);
351*62232bf4SGerd Hoffmann 
352*62232bf4SGerd Hoffmann             iov_to_buf(res->iov, res->iov_cnt, src_offset,
353*62232bf4SGerd Hoffmann                        (uint8_t *)img_data
354*62232bf4SGerd Hoffmann                        + dst_offset, t2d.r.width * bpp);
355*62232bf4SGerd Hoffmann         }
356*62232bf4SGerd Hoffmann     } else {
357*62232bf4SGerd Hoffmann         iov_to_buf(res->iov, res->iov_cnt, 0,
358*62232bf4SGerd Hoffmann                    pixman_image_get_data(res->image),
359*62232bf4SGerd Hoffmann                    pixman_image_get_stride(res->image)
360*62232bf4SGerd Hoffmann                    * pixman_image_get_height(res->image));
361*62232bf4SGerd Hoffmann     }
362*62232bf4SGerd Hoffmann }
363*62232bf4SGerd Hoffmann 
364*62232bf4SGerd Hoffmann static void virtio_gpu_resource_flush(VirtIOGPU *g,
365*62232bf4SGerd Hoffmann                                       struct virtio_gpu_ctrl_command *cmd)
366*62232bf4SGerd Hoffmann {
367*62232bf4SGerd Hoffmann     struct virtio_gpu_simple_resource *res;
368*62232bf4SGerd Hoffmann     struct virtio_gpu_resource_flush rf;
369*62232bf4SGerd Hoffmann     pixman_region16_t flush_region;
370*62232bf4SGerd Hoffmann     int i;
371*62232bf4SGerd Hoffmann 
372*62232bf4SGerd Hoffmann     VIRTIO_GPU_FILL_CMD(rf);
373*62232bf4SGerd Hoffmann     trace_virtio_gpu_cmd_res_flush(rf.resource_id,
374*62232bf4SGerd Hoffmann                                    rf.r.width, rf.r.height, rf.r.x, rf.r.y);
375*62232bf4SGerd Hoffmann 
376*62232bf4SGerd Hoffmann     res = virtio_gpu_find_resource(g, rf.resource_id);
377*62232bf4SGerd Hoffmann     if (!res) {
378*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
379*62232bf4SGerd Hoffmann                       __func__, rf.resource_id);
380*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
381*62232bf4SGerd Hoffmann         return;
382*62232bf4SGerd Hoffmann     }
383*62232bf4SGerd Hoffmann 
384*62232bf4SGerd Hoffmann     if (rf.r.x > res->width ||
385*62232bf4SGerd Hoffmann         rf.r.y > res->height ||
386*62232bf4SGerd Hoffmann         rf.r.width > res->width ||
387*62232bf4SGerd Hoffmann         rf.r.height > res->height ||
388*62232bf4SGerd Hoffmann         rf.r.x + rf.r.width > res->width ||
389*62232bf4SGerd Hoffmann         rf.r.y + rf.r.height > res->height) {
390*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside resource"
391*62232bf4SGerd Hoffmann                       " bounds for resource %d: %d %d %d %d vs %d %d\n",
392*62232bf4SGerd Hoffmann                       __func__, rf.resource_id, rf.r.x, rf.r.y,
393*62232bf4SGerd Hoffmann                       rf.r.width, rf.r.height, res->width, res->height);
394*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
395*62232bf4SGerd Hoffmann         return;
396*62232bf4SGerd Hoffmann     }
397*62232bf4SGerd Hoffmann 
398*62232bf4SGerd Hoffmann     pixman_region_init_rect(&flush_region,
399*62232bf4SGerd Hoffmann                             rf.r.x, rf.r.y, rf.r.width, rf.r.height);
400*62232bf4SGerd Hoffmann     for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) {
401*62232bf4SGerd Hoffmann         struct virtio_gpu_scanout *scanout;
402*62232bf4SGerd Hoffmann         pixman_region16_t region, finalregion;
403*62232bf4SGerd Hoffmann         pixman_box16_t *extents;
404*62232bf4SGerd Hoffmann 
405*62232bf4SGerd Hoffmann         if (!(res->scanout_bitmask & (1 << i))) {
406*62232bf4SGerd Hoffmann             continue;
407*62232bf4SGerd Hoffmann         }
408*62232bf4SGerd Hoffmann         scanout = &g->scanout[i];
409*62232bf4SGerd Hoffmann 
410*62232bf4SGerd Hoffmann         pixman_region_init(&finalregion);
411*62232bf4SGerd Hoffmann         pixman_region_init_rect(&region, scanout->x, scanout->y,
412*62232bf4SGerd Hoffmann                                 scanout->width, scanout->height);
413*62232bf4SGerd Hoffmann 
414*62232bf4SGerd Hoffmann         pixman_region_intersect(&finalregion, &flush_region, &region);
415*62232bf4SGerd Hoffmann         pixman_region_translate(&finalregion, -scanout->x, -scanout->y);
416*62232bf4SGerd Hoffmann         extents = pixman_region_extents(&finalregion);
417*62232bf4SGerd Hoffmann         /* work out the area we need to update for each console */
418*62232bf4SGerd Hoffmann         dpy_gfx_update(g->scanout[i].con,
419*62232bf4SGerd Hoffmann                        extents->x1, extents->y1,
420*62232bf4SGerd Hoffmann                        extents->x2 - extents->x1,
421*62232bf4SGerd Hoffmann                        extents->y2 - extents->y1);
422*62232bf4SGerd Hoffmann 
423*62232bf4SGerd Hoffmann         pixman_region_fini(&region);
424*62232bf4SGerd Hoffmann         pixman_region_fini(&finalregion);
425*62232bf4SGerd Hoffmann     }
426*62232bf4SGerd Hoffmann     pixman_region_fini(&flush_region);
427*62232bf4SGerd Hoffmann }
428*62232bf4SGerd Hoffmann 
429*62232bf4SGerd Hoffmann static void virtio_gpu_set_scanout(VirtIOGPU *g,
430*62232bf4SGerd Hoffmann                                    struct virtio_gpu_ctrl_command *cmd)
431*62232bf4SGerd Hoffmann {
432*62232bf4SGerd Hoffmann     struct virtio_gpu_simple_resource *res;
433*62232bf4SGerd Hoffmann     struct virtio_gpu_scanout *scanout;
434*62232bf4SGerd Hoffmann     pixman_format_code_t format;
435*62232bf4SGerd Hoffmann     uint32_t offset;
436*62232bf4SGerd Hoffmann     int bpp;
437*62232bf4SGerd Hoffmann     struct virtio_gpu_set_scanout ss;
438*62232bf4SGerd Hoffmann 
439*62232bf4SGerd Hoffmann     VIRTIO_GPU_FILL_CMD(ss);
440*62232bf4SGerd Hoffmann     trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
441*62232bf4SGerd Hoffmann                                      ss.r.width, ss.r.height, ss.r.x, ss.r.y);
442*62232bf4SGerd Hoffmann 
443*62232bf4SGerd Hoffmann     g->enable = 1;
444*62232bf4SGerd Hoffmann     if (ss.resource_id == 0) {
445*62232bf4SGerd Hoffmann         scanout = &g->scanout[ss.scanout_id];
446*62232bf4SGerd Hoffmann         if (scanout->resource_id) {
447*62232bf4SGerd Hoffmann             res = virtio_gpu_find_resource(g, scanout->resource_id);
448*62232bf4SGerd Hoffmann             if (res) {
449*62232bf4SGerd Hoffmann                 res->scanout_bitmask &= ~(1 << ss.scanout_id);
450*62232bf4SGerd Hoffmann             }
451*62232bf4SGerd Hoffmann         }
452*62232bf4SGerd Hoffmann         if (ss.scanout_id == 0 ||
453*62232bf4SGerd Hoffmann             ss.scanout_id >= g->conf.max_outputs) {
454*62232bf4SGerd Hoffmann             qemu_log_mask(LOG_GUEST_ERROR,
455*62232bf4SGerd Hoffmann                           "%s: illegal scanout id specified %d",
456*62232bf4SGerd Hoffmann                           __func__, ss.scanout_id);
457*62232bf4SGerd Hoffmann             cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
458*62232bf4SGerd Hoffmann             return;
459*62232bf4SGerd Hoffmann         }
460*62232bf4SGerd Hoffmann         dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
461*62232bf4SGerd Hoffmann         scanout->ds = NULL;
462*62232bf4SGerd Hoffmann         scanout->width = 0;
463*62232bf4SGerd Hoffmann         scanout->height = 0;
464*62232bf4SGerd Hoffmann         return;
465*62232bf4SGerd Hoffmann     }
466*62232bf4SGerd Hoffmann 
467*62232bf4SGerd Hoffmann     /* create a surface for this scanout */
468*62232bf4SGerd Hoffmann     if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT ||
469*62232bf4SGerd Hoffmann         ss.scanout_id >= g->conf.max_outputs) {
470*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
471*62232bf4SGerd Hoffmann                       __func__, ss.scanout_id);
472*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
473*62232bf4SGerd Hoffmann         return;
474*62232bf4SGerd Hoffmann     }
475*62232bf4SGerd Hoffmann 
476*62232bf4SGerd Hoffmann     res = virtio_gpu_find_resource(g, ss.resource_id);
477*62232bf4SGerd Hoffmann     if (!res) {
478*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
479*62232bf4SGerd Hoffmann                       __func__, ss.resource_id);
480*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
481*62232bf4SGerd Hoffmann         return;
482*62232bf4SGerd Hoffmann     }
483*62232bf4SGerd Hoffmann 
484*62232bf4SGerd Hoffmann     if (ss.r.x > res->width ||
485*62232bf4SGerd Hoffmann         ss.r.y > res->height ||
486*62232bf4SGerd Hoffmann         ss.r.width > res->width ||
487*62232bf4SGerd Hoffmann         ss.r.height > res->height ||
488*62232bf4SGerd Hoffmann         ss.r.x + ss.r.width > res->width ||
489*62232bf4SGerd Hoffmann         ss.r.y + ss.r.height > res->height) {
490*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for"
491*62232bf4SGerd Hoffmann                       " resource %d, (%d,%d)+%d,%d vs %d %d\n",
492*62232bf4SGerd Hoffmann                       __func__, ss.scanout_id, ss.resource_id, ss.r.x, ss.r.y,
493*62232bf4SGerd Hoffmann                       ss.r.width, ss.r.height, res->width, res->height);
494*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
495*62232bf4SGerd Hoffmann         return;
496*62232bf4SGerd Hoffmann     }
497*62232bf4SGerd Hoffmann 
498*62232bf4SGerd Hoffmann     scanout = &g->scanout[ss.scanout_id];
499*62232bf4SGerd Hoffmann 
500*62232bf4SGerd Hoffmann     format = pixman_image_get_format(res->image);
501*62232bf4SGerd Hoffmann     bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
502*62232bf4SGerd Hoffmann     offset = (ss.r.x * bpp) + ss.r.y * pixman_image_get_stride(res->image);
503*62232bf4SGerd Hoffmann     if (!scanout->ds || surface_data(scanout->ds)
504*62232bf4SGerd Hoffmann         != ((uint8_t *)pixman_image_get_data(res->image) + offset) ||
505*62232bf4SGerd Hoffmann         scanout->width != ss.r.width ||
506*62232bf4SGerd Hoffmann         scanout->height != ss.r.height) {
507*62232bf4SGerd Hoffmann         /* realloc the surface ptr */
508*62232bf4SGerd Hoffmann         scanout->ds = qemu_create_displaysurface_from
509*62232bf4SGerd Hoffmann             (ss.r.width, ss.r.height, format,
510*62232bf4SGerd Hoffmann              pixman_image_get_stride(res->image),
511*62232bf4SGerd Hoffmann              (uint8_t *)pixman_image_get_data(res->image) + offset);
512*62232bf4SGerd Hoffmann         if (!scanout->ds) {
513*62232bf4SGerd Hoffmann             cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
514*62232bf4SGerd Hoffmann             return;
515*62232bf4SGerd Hoffmann         }
516*62232bf4SGerd Hoffmann         dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds);
517*62232bf4SGerd Hoffmann     }
518*62232bf4SGerd Hoffmann 
519*62232bf4SGerd Hoffmann     res->scanout_bitmask |= (1 << ss.scanout_id);
520*62232bf4SGerd Hoffmann     scanout->resource_id = ss.resource_id;
521*62232bf4SGerd Hoffmann     scanout->x = ss.r.x;
522*62232bf4SGerd Hoffmann     scanout->y = ss.r.y;
523*62232bf4SGerd Hoffmann     scanout->width = ss.r.width;
524*62232bf4SGerd Hoffmann     scanout->height = ss.r.height;
525*62232bf4SGerd Hoffmann }
526*62232bf4SGerd Hoffmann 
527*62232bf4SGerd Hoffmann int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab,
528*62232bf4SGerd Hoffmann                                   struct virtio_gpu_ctrl_command *cmd,
529*62232bf4SGerd Hoffmann                                   struct iovec **iov)
530*62232bf4SGerd Hoffmann {
531*62232bf4SGerd Hoffmann     struct virtio_gpu_mem_entry *ents;
532*62232bf4SGerd Hoffmann     size_t esize, s;
533*62232bf4SGerd Hoffmann     int i;
534*62232bf4SGerd Hoffmann 
535*62232bf4SGerd Hoffmann     if (ab->nr_entries > 16384) {
536*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR,
537*62232bf4SGerd Hoffmann                       "%s: nr_entries is too big (%d > 1024)\n",
538*62232bf4SGerd Hoffmann                       __func__, ab->nr_entries);
539*62232bf4SGerd Hoffmann         return -1;
540*62232bf4SGerd Hoffmann     }
541*62232bf4SGerd Hoffmann 
542*62232bf4SGerd Hoffmann     esize = sizeof(*ents) * ab->nr_entries;
543*62232bf4SGerd Hoffmann     ents = g_malloc(esize);
544*62232bf4SGerd Hoffmann     s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
545*62232bf4SGerd Hoffmann                    sizeof(*ab), ents, esize);
546*62232bf4SGerd Hoffmann     if (s != esize) {
547*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR,
548*62232bf4SGerd Hoffmann                       "%s: command data size incorrect %zu vs %zu\n",
549*62232bf4SGerd Hoffmann                       __func__, s, esize);
550*62232bf4SGerd Hoffmann         g_free(ents);
551*62232bf4SGerd Hoffmann         return -1;
552*62232bf4SGerd Hoffmann     }
553*62232bf4SGerd Hoffmann 
554*62232bf4SGerd Hoffmann     *iov = g_malloc0(sizeof(struct iovec) * ab->nr_entries);
555*62232bf4SGerd Hoffmann     for (i = 0; i < ab->nr_entries; i++) {
556*62232bf4SGerd Hoffmann         hwaddr len = ents[i].length;
557*62232bf4SGerd Hoffmann         (*iov)[i].iov_len = ents[i].length;
558*62232bf4SGerd Hoffmann         (*iov)[i].iov_base = cpu_physical_memory_map(ents[i].addr, &len, 1);
559*62232bf4SGerd Hoffmann         if (!(*iov)[i].iov_base || len != ents[i].length) {
560*62232bf4SGerd Hoffmann             qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory for"
561*62232bf4SGerd Hoffmann                           " resource %d element %d\n",
562*62232bf4SGerd Hoffmann                           __func__, ab->resource_id, i);
563*62232bf4SGerd Hoffmann             virtio_gpu_cleanup_mapping_iov(*iov, i);
564*62232bf4SGerd Hoffmann             g_free(ents);
565*62232bf4SGerd Hoffmann             g_free(*iov);
566*62232bf4SGerd Hoffmann             *iov = NULL;
567*62232bf4SGerd Hoffmann             return -1;
568*62232bf4SGerd Hoffmann         }
569*62232bf4SGerd Hoffmann     }
570*62232bf4SGerd Hoffmann     g_free(ents);
571*62232bf4SGerd Hoffmann     return 0;
572*62232bf4SGerd Hoffmann }
573*62232bf4SGerd Hoffmann 
574*62232bf4SGerd Hoffmann void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count)
575*62232bf4SGerd Hoffmann {
576*62232bf4SGerd Hoffmann     int i;
577*62232bf4SGerd Hoffmann 
578*62232bf4SGerd Hoffmann     for (i = 0; i < count; i++) {
579*62232bf4SGerd Hoffmann         cpu_physical_memory_unmap(iov[i].iov_base, iov[i].iov_len, 1,
580*62232bf4SGerd Hoffmann                                   iov[i].iov_len);
581*62232bf4SGerd Hoffmann     }
582*62232bf4SGerd Hoffmann }
583*62232bf4SGerd Hoffmann 
584*62232bf4SGerd Hoffmann static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res)
585*62232bf4SGerd Hoffmann {
586*62232bf4SGerd Hoffmann     virtio_gpu_cleanup_mapping_iov(res->iov, res->iov_cnt);
587*62232bf4SGerd Hoffmann     g_free(res->iov);
588*62232bf4SGerd Hoffmann     res->iov = NULL;
589*62232bf4SGerd Hoffmann     res->iov_cnt = 0;
590*62232bf4SGerd Hoffmann }
591*62232bf4SGerd Hoffmann 
592*62232bf4SGerd Hoffmann static void
593*62232bf4SGerd Hoffmann virtio_gpu_resource_attach_backing(VirtIOGPU *g,
594*62232bf4SGerd Hoffmann                                    struct virtio_gpu_ctrl_command *cmd)
595*62232bf4SGerd Hoffmann {
596*62232bf4SGerd Hoffmann     struct virtio_gpu_simple_resource *res;
597*62232bf4SGerd Hoffmann     struct virtio_gpu_resource_attach_backing ab;
598*62232bf4SGerd Hoffmann     int ret;
599*62232bf4SGerd Hoffmann 
600*62232bf4SGerd Hoffmann     VIRTIO_GPU_FILL_CMD(ab);
601*62232bf4SGerd Hoffmann     trace_virtio_gpu_cmd_res_back_attach(ab.resource_id);
602*62232bf4SGerd Hoffmann 
603*62232bf4SGerd Hoffmann     res = virtio_gpu_find_resource(g, ab.resource_id);
604*62232bf4SGerd Hoffmann     if (!res) {
605*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
606*62232bf4SGerd Hoffmann                       __func__, ab.resource_id);
607*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
608*62232bf4SGerd Hoffmann         return;
609*62232bf4SGerd Hoffmann     }
610*62232bf4SGerd Hoffmann 
611*62232bf4SGerd Hoffmann     ret = virtio_gpu_create_mapping_iov(&ab, cmd, &res->iov);
612*62232bf4SGerd Hoffmann     if (ret != 0) {
613*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
614*62232bf4SGerd Hoffmann         return;
615*62232bf4SGerd Hoffmann     }
616*62232bf4SGerd Hoffmann 
617*62232bf4SGerd Hoffmann     res->iov_cnt = ab.nr_entries;
618*62232bf4SGerd Hoffmann }
619*62232bf4SGerd Hoffmann 
620*62232bf4SGerd Hoffmann static void
621*62232bf4SGerd Hoffmann virtio_gpu_resource_detach_backing(VirtIOGPU *g,
622*62232bf4SGerd Hoffmann                                    struct virtio_gpu_ctrl_command *cmd)
623*62232bf4SGerd Hoffmann {
624*62232bf4SGerd Hoffmann     struct virtio_gpu_simple_resource *res;
625*62232bf4SGerd Hoffmann     struct virtio_gpu_resource_detach_backing detach;
626*62232bf4SGerd Hoffmann 
627*62232bf4SGerd Hoffmann     VIRTIO_GPU_FILL_CMD(detach);
628*62232bf4SGerd Hoffmann     trace_virtio_gpu_cmd_res_back_detach(detach.resource_id);
629*62232bf4SGerd Hoffmann 
630*62232bf4SGerd Hoffmann     res = virtio_gpu_find_resource(g, detach.resource_id);
631*62232bf4SGerd Hoffmann     if (!res || !res->iov) {
632*62232bf4SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
633*62232bf4SGerd Hoffmann                       __func__, detach.resource_id);
634*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
635*62232bf4SGerd Hoffmann         return;
636*62232bf4SGerd Hoffmann     }
637*62232bf4SGerd Hoffmann     virtio_gpu_cleanup_mapping(res);
638*62232bf4SGerd Hoffmann }
639*62232bf4SGerd Hoffmann 
640*62232bf4SGerd Hoffmann static void virtio_gpu_simple_process_cmd(VirtIOGPU *g,
641*62232bf4SGerd Hoffmann                                           struct virtio_gpu_ctrl_command *cmd)
642*62232bf4SGerd Hoffmann {
643*62232bf4SGerd Hoffmann     VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
644*62232bf4SGerd Hoffmann 
645*62232bf4SGerd Hoffmann     switch (cmd->cmd_hdr.type) {
646*62232bf4SGerd Hoffmann     case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
647*62232bf4SGerd Hoffmann         virtio_gpu_get_display_info(g, cmd);
648*62232bf4SGerd Hoffmann         break;
649*62232bf4SGerd Hoffmann     case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
650*62232bf4SGerd Hoffmann         virtio_gpu_resource_create_2d(g, cmd);
651*62232bf4SGerd Hoffmann         break;
652*62232bf4SGerd Hoffmann     case VIRTIO_GPU_CMD_RESOURCE_UNREF:
653*62232bf4SGerd Hoffmann         virtio_gpu_resource_unref(g, cmd);
654*62232bf4SGerd Hoffmann         break;
655*62232bf4SGerd Hoffmann     case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
656*62232bf4SGerd Hoffmann         virtio_gpu_resource_flush(g, cmd);
657*62232bf4SGerd Hoffmann         break;
658*62232bf4SGerd Hoffmann     case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
659*62232bf4SGerd Hoffmann         virtio_gpu_transfer_to_host_2d(g, cmd);
660*62232bf4SGerd Hoffmann         break;
661*62232bf4SGerd Hoffmann     case VIRTIO_GPU_CMD_SET_SCANOUT:
662*62232bf4SGerd Hoffmann         virtio_gpu_set_scanout(g, cmd);
663*62232bf4SGerd Hoffmann         break;
664*62232bf4SGerd Hoffmann     case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING:
665*62232bf4SGerd Hoffmann         virtio_gpu_resource_attach_backing(g, cmd);
666*62232bf4SGerd Hoffmann         break;
667*62232bf4SGerd Hoffmann     case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING:
668*62232bf4SGerd Hoffmann         virtio_gpu_resource_detach_backing(g, cmd);
669*62232bf4SGerd Hoffmann         break;
670*62232bf4SGerd Hoffmann     default:
671*62232bf4SGerd Hoffmann         cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
672*62232bf4SGerd Hoffmann         break;
673*62232bf4SGerd Hoffmann     }
674*62232bf4SGerd Hoffmann     if (!cmd->finished) {
675*62232bf4SGerd Hoffmann         virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
676*62232bf4SGerd Hoffmann                                         VIRTIO_GPU_RESP_OK_NODATA);
677*62232bf4SGerd Hoffmann     }
678*62232bf4SGerd Hoffmann }
679*62232bf4SGerd Hoffmann 
680*62232bf4SGerd Hoffmann static void virtio_gpu_handle_ctrl_cb(VirtIODevice *vdev, VirtQueue *vq)
681*62232bf4SGerd Hoffmann {
682*62232bf4SGerd Hoffmann     VirtIOGPU *g = VIRTIO_GPU(vdev);
683*62232bf4SGerd Hoffmann     qemu_bh_schedule(g->ctrl_bh);
684*62232bf4SGerd Hoffmann }
685*62232bf4SGerd Hoffmann 
686*62232bf4SGerd Hoffmann static void virtio_gpu_handle_cursor_cb(VirtIODevice *vdev, VirtQueue *vq)
687*62232bf4SGerd Hoffmann {
688*62232bf4SGerd Hoffmann     VirtIOGPU *g = VIRTIO_GPU(vdev);
689*62232bf4SGerd Hoffmann     qemu_bh_schedule(g->cursor_bh);
690*62232bf4SGerd Hoffmann }
691*62232bf4SGerd Hoffmann 
692*62232bf4SGerd Hoffmann static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
693*62232bf4SGerd Hoffmann {
694*62232bf4SGerd Hoffmann     VirtIOGPU *g = VIRTIO_GPU(vdev);
695*62232bf4SGerd Hoffmann     struct virtio_gpu_ctrl_command *cmd;
696*62232bf4SGerd Hoffmann 
697*62232bf4SGerd Hoffmann     if (!virtio_queue_ready(vq)) {
698*62232bf4SGerd Hoffmann         return;
699*62232bf4SGerd Hoffmann     }
700*62232bf4SGerd Hoffmann 
701*62232bf4SGerd Hoffmann     cmd = g_new(struct virtio_gpu_ctrl_command, 1);
702*62232bf4SGerd Hoffmann     while (virtqueue_pop(vq, &cmd->elem)) {
703*62232bf4SGerd Hoffmann         cmd->vq = vq;
704*62232bf4SGerd Hoffmann         cmd->error = 0;
705*62232bf4SGerd Hoffmann         cmd->finished = false;
706*62232bf4SGerd Hoffmann         g->stats.requests++;
707*62232bf4SGerd Hoffmann 
708*62232bf4SGerd Hoffmann         virtio_gpu_simple_process_cmd(g, cmd);
709*62232bf4SGerd Hoffmann         if (!cmd->finished) {
710*62232bf4SGerd Hoffmann             QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next);
711*62232bf4SGerd Hoffmann             g->stats.inflight++;
712*62232bf4SGerd Hoffmann             if (g->stats.max_inflight < g->stats.inflight) {
713*62232bf4SGerd Hoffmann                 g->stats.max_inflight = g->stats.inflight;
714*62232bf4SGerd Hoffmann             }
715*62232bf4SGerd Hoffmann             fprintf(stderr, "inflight: %3d (+)\r", g->stats.inflight);
716*62232bf4SGerd Hoffmann             cmd = g_new(struct virtio_gpu_ctrl_command, 1);
717*62232bf4SGerd Hoffmann         }
718*62232bf4SGerd Hoffmann     }
719*62232bf4SGerd Hoffmann     g_free(cmd);
720*62232bf4SGerd Hoffmann }
721*62232bf4SGerd Hoffmann 
722*62232bf4SGerd Hoffmann static void virtio_gpu_ctrl_bh(void *opaque)
723*62232bf4SGerd Hoffmann {
724*62232bf4SGerd Hoffmann     VirtIOGPU *g = opaque;
725*62232bf4SGerd Hoffmann     virtio_gpu_handle_ctrl(&g->parent_obj, g->ctrl_vq);
726*62232bf4SGerd Hoffmann }
727*62232bf4SGerd Hoffmann 
728*62232bf4SGerd Hoffmann static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq)
729*62232bf4SGerd Hoffmann {
730*62232bf4SGerd Hoffmann     VirtIOGPU *g = VIRTIO_GPU(vdev);
731*62232bf4SGerd Hoffmann     VirtQueueElement elem;
732*62232bf4SGerd Hoffmann     size_t s;
733*62232bf4SGerd Hoffmann     struct virtio_gpu_update_cursor cursor_info;
734*62232bf4SGerd Hoffmann 
735*62232bf4SGerd Hoffmann     if (!virtio_queue_ready(vq)) {
736*62232bf4SGerd Hoffmann         return;
737*62232bf4SGerd Hoffmann     }
738*62232bf4SGerd Hoffmann     while (virtqueue_pop(vq, &elem)) {
739*62232bf4SGerd Hoffmann         s = iov_to_buf(elem.out_sg, elem.out_num, 0,
740*62232bf4SGerd Hoffmann                        &cursor_info, sizeof(cursor_info));
741*62232bf4SGerd Hoffmann         if (s != sizeof(cursor_info)) {
742*62232bf4SGerd Hoffmann             qemu_log_mask(LOG_GUEST_ERROR,
743*62232bf4SGerd Hoffmann                           "%s: cursor size incorrect %zu vs %zu\n",
744*62232bf4SGerd Hoffmann                           __func__, s, sizeof(cursor_info));
745*62232bf4SGerd Hoffmann         } else {
746*62232bf4SGerd Hoffmann             update_cursor(g, &cursor_info);
747*62232bf4SGerd Hoffmann         }
748*62232bf4SGerd Hoffmann         virtqueue_push(vq, &elem, 0);
749*62232bf4SGerd Hoffmann         virtio_notify(vdev, vq);
750*62232bf4SGerd Hoffmann     }
751*62232bf4SGerd Hoffmann }
752*62232bf4SGerd Hoffmann 
753*62232bf4SGerd Hoffmann static void virtio_gpu_cursor_bh(void *opaque)
754*62232bf4SGerd Hoffmann {
755*62232bf4SGerd Hoffmann     VirtIOGPU *g = opaque;
756*62232bf4SGerd Hoffmann     virtio_gpu_handle_cursor(&g->parent_obj, g->cursor_vq);
757*62232bf4SGerd Hoffmann }
758*62232bf4SGerd Hoffmann 
759*62232bf4SGerd Hoffmann static void virtio_gpu_invalidate_display(void *opaque)
760*62232bf4SGerd Hoffmann {
761*62232bf4SGerd Hoffmann }
762*62232bf4SGerd Hoffmann 
763*62232bf4SGerd Hoffmann static void virtio_gpu_update_display(void *opaque)
764*62232bf4SGerd Hoffmann {
765*62232bf4SGerd Hoffmann }
766*62232bf4SGerd Hoffmann 
767*62232bf4SGerd Hoffmann static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata)
768*62232bf4SGerd Hoffmann {
769*62232bf4SGerd Hoffmann }
770*62232bf4SGerd Hoffmann 
771*62232bf4SGerd Hoffmann static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
772*62232bf4SGerd Hoffmann {
773*62232bf4SGerd Hoffmann     VirtIOGPU *g = opaque;
774*62232bf4SGerd Hoffmann 
775*62232bf4SGerd Hoffmann     if (idx > g->conf.max_outputs) {
776*62232bf4SGerd Hoffmann         return -1;
777*62232bf4SGerd Hoffmann     }
778*62232bf4SGerd Hoffmann 
779*62232bf4SGerd Hoffmann     g->req_state[idx].x = info->xoff;
780*62232bf4SGerd Hoffmann     g->req_state[idx].y = info->yoff;
781*62232bf4SGerd Hoffmann     g->req_state[idx].width = info->width;
782*62232bf4SGerd Hoffmann     g->req_state[idx].height = info->height;
783*62232bf4SGerd Hoffmann 
784*62232bf4SGerd Hoffmann     if (info->width && info->height) {
785*62232bf4SGerd Hoffmann         g->enabled_output_bitmask |= (1 << idx);
786*62232bf4SGerd Hoffmann     } else {
787*62232bf4SGerd Hoffmann         g->enabled_output_bitmask &= ~(1 << idx);
788*62232bf4SGerd Hoffmann     }
789*62232bf4SGerd Hoffmann 
790*62232bf4SGerd Hoffmann     /* send event to guest */
791*62232bf4SGerd Hoffmann     virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY);
792*62232bf4SGerd Hoffmann     return 0;
793*62232bf4SGerd Hoffmann }
794*62232bf4SGerd Hoffmann 
795*62232bf4SGerd Hoffmann const GraphicHwOps virtio_gpu_ops = {
796*62232bf4SGerd Hoffmann     .invalidate = virtio_gpu_invalidate_display,
797*62232bf4SGerd Hoffmann     .gfx_update = virtio_gpu_update_display,
798*62232bf4SGerd Hoffmann     .text_update = virtio_gpu_text_update,
799*62232bf4SGerd Hoffmann     .ui_info = virtio_gpu_ui_info,
800*62232bf4SGerd Hoffmann };
801*62232bf4SGerd Hoffmann 
802*62232bf4SGerd Hoffmann static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
803*62232bf4SGerd Hoffmann {
804*62232bf4SGerd Hoffmann     VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
805*62232bf4SGerd Hoffmann     VirtIOGPU *g = VIRTIO_GPU(qdev);
806*62232bf4SGerd Hoffmann     int i;
807*62232bf4SGerd Hoffmann 
808*62232bf4SGerd Hoffmann     g->config_size = sizeof(struct virtio_gpu_config);
809*62232bf4SGerd Hoffmann     g->virtio_config.num_scanouts = g->conf.max_outputs;
810*62232bf4SGerd Hoffmann     virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
811*62232bf4SGerd Hoffmann                 g->config_size);
812*62232bf4SGerd Hoffmann 
813*62232bf4SGerd Hoffmann     g->req_state[0].width = 1024;
814*62232bf4SGerd Hoffmann     g->req_state[0].height = 768;
815*62232bf4SGerd Hoffmann 
816*62232bf4SGerd Hoffmann     g->ctrl_vq   = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
817*62232bf4SGerd Hoffmann     g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
818*62232bf4SGerd Hoffmann 
819*62232bf4SGerd Hoffmann     g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g);
820*62232bf4SGerd Hoffmann     g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g);
821*62232bf4SGerd Hoffmann     QTAILQ_INIT(&g->reslist);
822*62232bf4SGerd Hoffmann     QTAILQ_INIT(&g->fenceq);
823*62232bf4SGerd Hoffmann 
824*62232bf4SGerd Hoffmann     g->enabled_output_bitmask = 1;
825*62232bf4SGerd Hoffmann     g->qdev = qdev;
826*62232bf4SGerd Hoffmann 
827*62232bf4SGerd Hoffmann     for (i = 0; i < g->conf.max_outputs; i++) {
828*62232bf4SGerd Hoffmann         g->scanout[i].con =
829*62232bf4SGerd Hoffmann             graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g);
830*62232bf4SGerd Hoffmann         if (i > 0) {
831*62232bf4SGerd Hoffmann             dpy_gfx_replace_surface(g->scanout[i].con, NULL);
832*62232bf4SGerd Hoffmann         }
833*62232bf4SGerd Hoffmann     }
834*62232bf4SGerd Hoffmann }
835*62232bf4SGerd Hoffmann 
836*62232bf4SGerd Hoffmann static void virtio_gpu_instance_init(Object *obj)
837*62232bf4SGerd Hoffmann {
838*62232bf4SGerd Hoffmann }
839*62232bf4SGerd Hoffmann 
840*62232bf4SGerd Hoffmann static void virtio_gpu_reset(VirtIODevice *vdev)
841*62232bf4SGerd Hoffmann {
842*62232bf4SGerd Hoffmann     VirtIOGPU *g = VIRTIO_GPU(vdev);
843*62232bf4SGerd Hoffmann     struct virtio_gpu_simple_resource *res, *tmp;
844*62232bf4SGerd Hoffmann     int i;
845*62232bf4SGerd Hoffmann 
846*62232bf4SGerd Hoffmann     g->enable = 0;
847*62232bf4SGerd Hoffmann 
848*62232bf4SGerd Hoffmann     QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
849*62232bf4SGerd Hoffmann         virtio_gpu_resource_destroy(g, res);
850*62232bf4SGerd Hoffmann     }
851*62232bf4SGerd Hoffmann     for (i = 0; i < g->conf.max_outputs; i++) {
852*62232bf4SGerd Hoffmann #if 0
853*62232bf4SGerd Hoffmann         g->req_state[i].x = 0;
854*62232bf4SGerd Hoffmann         g->req_state[i].y = 0;
855*62232bf4SGerd Hoffmann         if (i == 0) {
856*62232bf4SGerd Hoffmann             g->req_state[0].width = 1024;
857*62232bf4SGerd Hoffmann             g->req_state[0].height = 768;
858*62232bf4SGerd Hoffmann         } else {
859*62232bf4SGerd Hoffmann             g->req_state[i].width = 0;
860*62232bf4SGerd Hoffmann             g->req_state[i].height = 0;
861*62232bf4SGerd Hoffmann         }
862*62232bf4SGerd Hoffmann #endif
863*62232bf4SGerd Hoffmann         g->scanout[i].resource_id = 0;
864*62232bf4SGerd Hoffmann         g->scanout[i].width = 0;
865*62232bf4SGerd Hoffmann         g->scanout[i].height = 0;
866*62232bf4SGerd Hoffmann         g->scanout[i].x = 0;
867*62232bf4SGerd Hoffmann         g->scanout[i].y = 0;
868*62232bf4SGerd Hoffmann         g->scanout[i].ds = NULL;
869*62232bf4SGerd Hoffmann     }
870*62232bf4SGerd Hoffmann     g->enabled_output_bitmask = 1;
871*62232bf4SGerd Hoffmann }
872*62232bf4SGerd Hoffmann 
873*62232bf4SGerd Hoffmann static Property virtio_gpu_properties[] = {
874*62232bf4SGerd Hoffmann     DEFINE_VIRTIO_GPU_PROPERTIES(VirtIOGPU, conf),
875*62232bf4SGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
876*62232bf4SGerd Hoffmann };
877*62232bf4SGerd Hoffmann 
878*62232bf4SGerd Hoffmann static void virtio_gpu_class_init(ObjectClass *klass, void *data)
879*62232bf4SGerd Hoffmann {
880*62232bf4SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
881*62232bf4SGerd Hoffmann     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
882*62232bf4SGerd Hoffmann 
883*62232bf4SGerd Hoffmann     vdc->realize = virtio_gpu_device_realize;
884*62232bf4SGerd Hoffmann     vdc->get_config = virtio_gpu_get_config;
885*62232bf4SGerd Hoffmann     vdc->set_config = virtio_gpu_set_config;
886*62232bf4SGerd Hoffmann     vdc->get_features = virtio_gpu_get_features;
887*62232bf4SGerd Hoffmann 
888*62232bf4SGerd Hoffmann     vdc->reset = virtio_gpu_reset;
889*62232bf4SGerd Hoffmann 
890*62232bf4SGerd Hoffmann     dc->props = virtio_gpu_properties;
891*62232bf4SGerd Hoffmann }
892*62232bf4SGerd Hoffmann 
893*62232bf4SGerd Hoffmann static const TypeInfo virtio_gpu_info = {
894*62232bf4SGerd Hoffmann     .name = TYPE_VIRTIO_GPU,
895*62232bf4SGerd Hoffmann     .parent = TYPE_VIRTIO_DEVICE,
896*62232bf4SGerd Hoffmann     .instance_size = sizeof(VirtIOGPU),
897*62232bf4SGerd Hoffmann     .instance_init = virtio_gpu_instance_init,
898*62232bf4SGerd Hoffmann     .class_init = virtio_gpu_class_init,
899*62232bf4SGerd Hoffmann };
900*62232bf4SGerd Hoffmann 
901*62232bf4SGerd Hoffmann static void virtio_register_types(void)
902*62232bf4SGerd Hoffmann {
903*62232bf4SGerd Hoffmann     type_register_static(&virtio_gpu_info);
904*62232bf4SGerd Hoffmann }
905*62232bf4SGerd Hoffmann 
906*62232bf4SGerd Hoffmann type_init(virtio_register_types)
907*62232bf4SGerd Hoffmann 
908*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr)                != 24);
909*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor)           != 56);
910*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref)          != 32);
911*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d)      != 40);
912*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout)             != 48);
913*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush)          != 48);
914*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d)     != 56);
915*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry)               != 16);
916*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) != 32);
917*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) != 32);
918*62232bf4SGerd Hoffmann QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info)       != 408);
919