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(®ion, scanout->x, scanout->y, 412*62232bf4SGerd Hoffmann scanout->width, scanout->height); 413*62232bf4SGerd Hoffmann 414*62232bf4SGerd Hoffmann pixman_region_intersect(&finalregion, &flush_region, ®ion); 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(®ion); 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