qemu/contrib/vhost-user-gpu/virgl.c
<<
>>
Prefs
   1/*
   2 * Virtio vhost-user GPU Device
   3 *
   4 * Copyright Red Hat, Inc. 2013-2018
   5 *
   6 * Authors:
   7 *     Dave Airlie <airlied@redhat.com>
   8 *     Gerd Hoffmann <kraxel@redhat.com>
   9 *     Marc-André Lureau <marcandre.lureau@redhat.com>
  10 *
  11 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  12 * See the COPYING file in the top-level directory.
  13 */
  14
  15#include "qemu/osdep.h"
  16#include <virglrenderer.h>
  17#include "virgl.h"
  18
  19#include <epoxy/gl.h>
  20
  21void
  22vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id,
  23                            gpointer data)
  24{
  25    uint32_t width, height;
  26    uint32_t *cursor;
  27
  28    cursor = virgl_renderer_get_cursor_data(resource_id, &width, &height);
  29    g_return_if_fail(cursor != NULL);
  30    g_return_if_fail(width == 64);
  31    g_return_if_fail(height == 64);
  32
  33    memcpy(data, cursor, 64 * 64 * sizeof(uint32_t));
  34    free(cursor);
  35}
  36
  37static void
  38virgl_cmd_context_create(VuGpu *g,
  39                         struct virtio_gpu_ctrl_command *cmd)
  40{
  41    struct virtio_gpu_ctx_create cc;
  42
  43    VUGPU_FILL_CMD(cc);
  44
  45    virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen,
  46                                  cc.debug_name);
  47}
  48
  49static void
  50virgl_cmd_context_destroy(VuGpu *g,
  51                          struct virtio_gpu_ctrl_command *cmd)
  52{
  53    struct virtio_gpu_ctx_destroy cd;
  54
  55    VUGPU_FILL_CMD(cd);
  56
  57    virgl_renderer_context_destroy(cd.hdr.ctx_id);
  58}
  59
  60static void
  61virgl_cmd_create_resource_2d(VuGpu *g,
  62                             struct virtio_gpu_ctrl_command *cmd)
  63{
  64    struct virtio_gpu_resource_create_2d c2d;
  65    struct virgl_renderer_resource_create_args args;
  66
  67    VUGPU_FILL_CMD(c2d);
  68
  69    args.handle = c2d.resource_id;
  70    args.target = 2;
  71    args.format = c2d.format;
  72    args.bind = (1 << 1);
  73    args.width = c2d.width;
  74    args.height = c2d.height;
  75    args.depth = 1;
  76    args.array_size = 1;
  77    args.last_level = 0;
  78    args.nr_samples = 0;
  79    args.flags = VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP;
  80    virgl_renderer_resource_create(&args, NULL, 0);
  81}
  82
  83static void
  84virgl_cmd_create_resource_3d(VuGpu *g,
  85                             struct virtio_gpu_ctrl_command *cmd)
  86{
  87    struct virtio_gpu_resource_create_3d c3d;
  88    struct virgl_renderer_resource_create_args args;
  89
  90    VUGPU_FILL_CMD(c3d);
  91
  92    args.handle = c3d.resource_id;
  93    args.target = c3d.target;
  94    args.format = c3d.format;
  95    args.bind = c3d.bind;
  96    args.width = c3d.width;
  97    args.height = c3d.height;
  98    args.depth = c3d.depth;
  99    args.array_size = c3d.array_size;
 100    args.last_level = c3d.last_level;
 101    args.nr_samples = c3d.nr_samples;
 102    args.flags = c3d.flags;
 103    virgl_renderer_resource_create(&args, NULL, 0);
 104}
 105
 106static void
 107virgl_cmd_resource_unref(VuGpu *g,
 108                         struct virtio_gpu_ctrl_command *cmd)
 109{
 110    struct virtio_gpu_resource_unref unref;
 111    struct iovec *res_iovs = NULL;
 112    int num_iovs = 0;
 113
 114    VUGPU_FILL_CMD(unref);
 115
 116    virgl_renderer_resource_detach_iov(unref.resource_id,
 117                                       &res_iovs,
 118                                       &num_iovs);
 119    if (res_iovs != NULL && num_iovs != 0) {
 120        vg_cleanup_mapping_iov(g, res_iovs, num_iovs);
 121    }
 122    virgl_renderer_resource_unref(unref.resource_id);
 123}
 124
 125/* Not yet(?) defined in standard-headers, remove when possible */
 126#ifndef VIRTIO_GPU_CAPSET_VIRGL2
 127#define VIRTIO_GPU_CAPSET_VIRGL2 2
 128#endif
 129
 130static void
 131virgl_cmd_get_capset_info(VuGpu *g,
 132                          struct virtio_gpu_ctrl_command *cmd)
 133{
 134    struct virtio_gpu_get_capset_info info;
 135    struct virtio_gpu_resp_capset_info resp;
 136
 137    VUGPU_FILL_CMD(info);
 138
 139    memset(&resp, 0, sizeof(resp));
 140    if (info.capset_index == 0) {
 141        resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL;
 142        virgl_renderer_get_cap_set(resp.capset_id,
 143                                   &resp.capset_max_version,
 144                                   &resp.capset_max_size);
 145    } else if (info.capset_index == 1) {
 146        resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL2;
 147        virgl_renderer_get_cap_set(resp.capset_id,
 148                                   &resp.capset_max_version,
 149                                   &resp.capset_max_size);
 150    } else {
 151        resp.capset_max_version = 0;
 152        resp.capset_max_size = 0;
 153    }
 154    resp.hdr.type = VIRTIO_GPU_RESP_OK_CAPSET_INFO;
 155    vg_ctrl_response(g, cmd, &resp.hdr, sizeof(resp));
 156}
 157
 158uint32_t
 159vg_virgl_get_num_capsets(void)
 160{
 161    uint32_t capset2_max_ver, capset2_max_size;
 162    virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VIRGL2,
 163                               &capset2_max_ver,
 164                               &capset2_max_size);
 165
 166    return capset2_max_ver ? 2 : 1;
 167}
 168
 169static void
 170virgl_cmd_get_capset(VuGpu *g,
 171                     struct virtio_gpu_ctrl_command *cmd)
 172{
 173    struct virtio_gpu_get_capset gc;
 174    struct virtio_gpu_resp_capset *resp;
 175    uint32_t max_ver, max_size;
 176
 177    VUGPU_FILL_CMD(gc);
 178
 179    virgl_renderer_get_cap_set(gc.capset_id, &max_ver,
 180                               &max_size);
 181    if (!max_size) {
 182        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
 183        return;
 184    }
 185    resp = g_malloc0(sizeof(*resp) + max_size);
 186
 187    resp->hdr.type = VIRTIO_GPU_RESP_OK_CAPSET;
 188    virgl_renderer_fill_caps(gc.capset_id,
 189                             gc.capset_version,
 190                             (void *)resp->capset_data);
 191    vg_ctrl_response(g, cmd, &resp->hdr, sizeof(*resp) + max_size);
 192    g_free(resp);
 193}
 194
 195static void
 196virgl_cmd_submit_3d(VuGpu *g,
 197                    struct virtio_gpu_ctrl_command *cmd)
 198{
 199    struct virtio_gpu_cmd_submit cs;
 200    void *buf;
 201    size_t s;
 202
 203    VUGPU_FILL_CMD(cs);
 204
 205    buf = g_malloc(cs.size);
 206    s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
 207                   sizeof(cs), buf, cs.size);
 208    if (s != cs.size) {
 209        g_critical("%s: size mismatch (%zd/%d)", __func__, s, cs.size);
 210        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
 211        goto out;
 212    }
 213
 214    virgl_renderer_submit_cmd(buf, cs.hdr.ctx_id, cs.size / 4);
 215
 216out:
 217    g_free(buf);
 218}
 219
 220static void
 221virgl_cmd_transfer_to_host_2d(VuGpu *g,
 222                              struct virtio_gpu_ctrl_command *cmd)
 223{
 224    struct virtio_gpu_transfer_to_host_2d t2d;
 225    struct virtio_gpu_box box;
 226
 227    VUGPU_FILL_CMD(t2d);
 228
 229    box.x = t2d.r.x;
 230    box.y = t2d.r.y;
 231    box.z = 0;
 232    box.w = t2d.r.width;
 233    box.h = t2d.r.height;
 234    box.d = 1;
 235
 236    virgl_renderer_transfer_write_iov(t2d.resource_id,
 237                                      0,
 238                                      0,
 239                                      0,
 240                                      0,
 241                                      (struct virgl_box *)&box,
 242                                      t2d.offset, NULL, 0);
 243}
 244
 245static void
 246virgl_cmd_transfer_to_host_3d(VuGpu *g,
 247                              struct virtio_gpu_ctrl_command *cmd)
 248{
 249    struct virtio_gpu_transfer_host_3d t3d;
 250
 251    VUGPU_FILL_CMD(t3d);
 252
 253    virgl_renderer_transfer_write_iov(t3d.resource_id,
 254                                      t3d.hdr.ctx_id,
 255                                      t3d.level,
 256                                      t3d.stride,
 257                                      t3d.layer_stride,
 258                                      (struct virgl_box *)&t3d.box,
 259                                      t3d.offset, NULL, 0);
 260}
 261
 262static void
 263virgl_cmd_transfer_from_host_3d(VuGpu *g,
 264                                struct virtio_gpu_ctrl_command *cmd)
 265{
 266    struct virtio_gpu_transfer_host_3d tf3d;
 267
 268    VUGPU_FILL_CMD(tf3d);
 269
 270    virgl_renderer_transfer_read_iov(tf3d.resource_id,
 271                                     tf3d.hdr.ctx_id,
 272                                     tf3d.level,
 273                                     tf3d.stride,
 274                                     tf3d.layer_stride,
 275                                     (struct virgl_box *)&tf3d.box,
 276                                     tf3d.offset, NULL, 0);
 277}
 278
 279static void
 280virgl_resource_attach_backing(VuGpu *g,
 281                              struct virtio_gpu_ctrl_command *cmd)
 282{
 283    struct virtio_gpu_resource_attach_backing att_rb;
 284    struct iovec *res_iovs;
 285    int ret;
 286
 287    VUGPU_FILL_CMD(att_rb);
 288
 289    ret = vg_create_mapping_iov(g, &att_rb, cmd, &res_iovs);
 290    if (ret != 0) {
 291        cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
 292        return;
 293    }
 294
 295    ret = virgl_renderer_resource_attach_iov(att_rb.resource_id,
 296                                       res_iovs, att_rb.nr_entries);
 297    if (ret != 0) {
 298        vg_cleanup_mapping_iov(g, res_iovs, att_rb.nr_entries);
 299    }
 300}
 301
 302static void
 303virgl_resource_detach_backing(VuGpu *g,
 304                              struct virtio_gpu_ctrl_command *cmd)
 305{
 306    struct virtio_gpu_resource_detach_backing detach_rb;
 307    struct iovec *res_iovs = NULL;
 308    int num_iovs = 0;
 309
 310    VUGPU_FILL_CMD(detach_rb);
 311
 312    virgl_renderer_resource_detach_iov(detach_rb.resource_id,
 313                                       &res_iovs,
 314                                       &num_iovs);
 315    if (res_iovs == NULL || num_iovs == 0) {
 316        return;
 317    }
 318    vg_cleanup_mapping_iov(g, res_iovs, num_iovs);
 319}
 320
 321static void
 322virgl_cmd_set_scanout(VuGpu *g,
 323                      struct virtio_gpu_ctrl_command *cmd)
 324{
 325    struct virtio_gpu_set_scanout ss;
 326    struct virgl_renderer_resource_info info;
 327    int ret;
 328
 329    VUGPU_FILL_CMD(ss);
 330
 331    if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUTS) {
 332        g_critical("%s: illegal scanout id specified %d",
 333                   __func__, ss.scanout_id);
 334        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
 335        return;
 336    }
 337
 338    memset(&info, 0, sizeof(info));
 339
 340    if (ss.resource_id && ss.r.width && ss.r.height) {
 341        ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
 342        if (ret == -1) {
 343            g_critical("%s: illegal resource specified %d\n",
 344                       __func__, ss.resource_id);
 345            cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
 346            return;
 347        }
 348
 349        int fd = -1;
 350        if (virgl_renderer_get_fd_for_texture(info.tex_id, &fd) < 0) {
 351            g_critical("%s: failed to get fd for texture\n", __func__);
 352            cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
 353            return;
 354        }
 355        assert(fd >= 0);
 356        VhostUserGpuMsg msg = {
 357            .request = VHOST_USER_GPU_DMABUF_SCANOUT,
 358            .size = sizeof(VhostUserGpuDMABUFScanout),
 359            .payload.dmabuf_scanout.scanout_id = ss.scanout_id,
 360            .payload.dmabuf_scanout.x =  ss.r.x,
 361            .payload.dmabuf_scanout.y =  ss.r.y,
 362            .payload.dmabuf_scanout.width = ss.r.width,
 363            .payload.dmabuf_scanout.height = ss.r.height,
 364            .payload.dmabuf_scanout.fd_width = info.width,
 365            .payload.dmabuf_scanout.fd_height = info.height,
 366            .payload.dmabuf_scanout.fd_stride = info.stride,
 367            .payload.dmabuf_scanout.fd_flags = info.flags,
 368            .payload.dmabuf_scanout.fd_drm_fourcc = info.drm_fourcc
 369        };
 370        vg_send_msg(g, &msg, fd);
 371        close(fd);
 372    } else {
 373        VhostUserGpuMsg msg = {
 374            .request = VHOST_USER_GPU_DMABUF_SCANOUT,
 375            .size = sizeof(VhostUserGpuDMABUFScanout),
 376            .payload.dmabuf_scanout.scanout_id = ss.scanout_id,
 377        };
 378        g_debug("disable scanout");
 379        vg_send_msg(g, &msg, -1);
 380    }
 381    g->scanout[ss.scanout_id].resource_id = ss.resource_id;
 382}
 383
 384static void
 385virgl_cmd_resource_flush(VuGpu *g,
 386                         struct virtio_gpu_ctrl_command *cmd)
 387{
 388    struct virtio_gpu_resource_flush rf;
 389    int i;
 390
 391    VUGPU_FILL_CMD(rf);
 392
 393    glFlush();
 394    if (!rf.resource_id) {
 395        g_debug("bad resource id for flush..?");
 396        return;
 397    }
 398    for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) {
 399        if (g->scanout[i].resource_id != rf.resource_id) {
 400            continue;
 401        }
 402        VhostUserGpuMsg msg = {
 403            .request = VHOST_USER_GPU_DMABUF_UPDATE,
 404            .size = sizeof(VhostUserGpuUpdate),
 405            .payload.update.scanout_id = i,
 406            .payload.update.x = rf.r.x,
 407            .payload.update.y = rf.r.y,
 408            .payload.update.width = rf.r.width,
 409            .payload.update.height = rf.r.height
 410        };
 411        vg_send_msg(g, &msg, -1);
 412        vg_wait_ok(g);
 413    }
 414}
 415
 416static void
 417virgl_cmd_ctx_attach_resource(VuGpu *g,
 418                              struct virtio_gpu_ctrl_command *cmd)
 419{
 420    struct virtio_gpu_ctx_resource att_res;
 421
 422    VUGPU_FILL_CMD(att_res);
 423
 424    virgl_renderer_ctx_attach_resource(att_res.hdr.ctx_id, att_res.resource_id);
 425}
 426
 427static void
 428virgl_cmd_ctx_detach_resource(VuGpu *g,
 429                              struct virtio_gpu_ctrl_command *cmd)
 430{
 431    struct virtio_gpu_ctx_resource det_res;
 432
 433    VUGPU_FILL_CMD(det_res);
 434
 435    virgl_renderer_ctx_detach_resource(det_res.hdr.ctx_id, det_res.resource_id);
 436}
 437
 438void vg_virgl_process_cmd(VuGpu *g, struct virtio_gpu_ctrl_command *cmd)
 439{
 440    virgl_renderer_force_ctx_0();
 441    switch (cmd->cmd_hdr.type) {
 442    case VIRTIO_GPU_CMD_CTX_CREATE:
 443        virgl_cmd_context_create(g, cmd);
 444        break;
 445    case VIRTIO_GPU_CMD_CTX_DESTROY:
 446        virgl_cmd_context_destroy(g, cmd);
 447        break;
 448    case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
 449        virgl_cmd_create_resource_2d(g, cmd);
 450        break;
 451    case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D:
 452        virgl_cmd_create_resource_3d(g, cmd);
 453        break;
 454    case VIRTIO_GPU_CMD_SUBMIT_3D:
 455        virgl_cmd_submit_3d(g, cmd);
 456        break;
 457    case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
 458        virgl_cmd_transfer_to_host_2d(g, cmd);
 459        break;
 460    case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D:
 461        virgl_cmd_transfer_to_host_3d(g, cmd);
 462        break;
 463    case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D:
 464        virgl_cmd_transfer_from_host_3d(g, cmd);
 465        break;
 466    case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING:
 467        virgl_resource_attach_backing(g, cmd);
 468        break;
 469    case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING:
 470        virgl_resource_detach_backing(g, cmd);
 471        break;
 472    case VIRTIO_GPU_CMD_SET_SCANOUT:
 473        virgl_cmd_set_scanout(g, cmd);
 474        break;
 475    case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
 476        virgl_cmd_resource_flush(g, cmd);
 477        break;
 478    case VIRTIO_GPU_CMD_RESOURCE_UNREF:
 479        virgl_cmd_resource_unref(g, cmd);
 480        break;
 481    case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE:
 482        /* TODO add security */
 483        virgl_cmd_ctx_attach_resource(g, cmd);
 484        break;
 485    case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE:
 486        /* TODO add security */
 487        virgl_cmd_ctx_detach_resource(g, cmd);
 488        break;
 489    case VIRTIO_GPU_CMD_GET_CAPSET_INFO:
 490        virgl_cmd_get_capset_info(g, cmd);
 491        break;
 492    case VIRTIO_GPU_CMD_GET_CAPSET:
 493        virgl_cmd_get_capset(g, cmd);
 494        break;
 495    case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
 496        vg_get_display_info(g, cmd);
 497        break;
 498    default:
 499        g_debug("TODO handle ctrl %x\n", cmd->cmd_hdr.type);
 500        cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
 501        break;
 502    }
 503
 504    if (cmd->state != VG_CMD_STATE_NEW) {
 505        return;
 506    }
 507
 508    if (cmd->error) {
 509        g_warning("%s: ctrl 0x%x, error 0x%x\n", __func__,
 510                  cmd->cmd_hdr.type, cmd->error);
 511        vg_ctrl_response_nodata(g, cmd, cmd->error);
 512        return;
 513    }
 514
 515    if (!(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) {
 516        vg_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
 517        return;
 518    }
 519
 520    g_debug("Creating fence id:%" PRId64 " type:%d",
 521            cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
 522    virgl_renderer_create_fence(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
 523}
 524
 525static void
 526virgl_write_fence(void *opaque, uint32_t fence)
 527{
 528    VuGpu *g = opaque;
 529    struct virtio_gpu_ctrl_command *cmd, *tmp;
 530
 531    QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) {
 532        /*
 533         * the guest can end up emitting fences out of order
 534         * so we should check all fenced cmds not just the first one.
 535         */
 536        if (cmd->cmd_hdr.fence_id > fence) {
 537            continue;
 538        }
 539        g_debug("FENCE %" PRIu64, cmd->cmd_hdr.fence_id);
 540        vg_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
 541        QTAILQ_REMOVE(&g->fenceq, cmd, next);
 542        free(cmd);
 543        g->inflight--;
 544    }
 545}
 546
 547#if defined(VIRGL_RENDERER_CALLBACKS_VERSION) && \
 548    VIRGL_RENDERER_CALLBACKS_VERSION >= 2
 549static int
 550virgl_get_drm_fd(void *opaque)
 551{
 552    VuGpu *g = opaque;
 553
 554    return g->drm_rnode_fd;
 555}
 556#endif
 557
 558static struct virgl_renderer_callbacks virgl_cbs = {
 559#if defined(VIRGL_RENDERER_CALLBACKS_VERSION) &&    \
 560    VIRGL_RENDERER_CALLBACKS_VERSION >= 2
 561    .get_drm_fd  = virgl_get_drm_fd,
 562    .version     = 2,
 563#else
 564    .version     = 1,
 565#endif
 566    .write_fence = virgl_write_fence,
 567};
 568
 569static void
 570vg_virgl_poll(VuDev *dev, int condition, void *data)
 571{
 572    virgl_renderer_poll();
 573}
 574
 575bool
 576vg_virgl_init(VuGpu *g)
 577{
 578    int ret;
 579
 580    if (g->drm_rnode_fd && virgl_cbs.version == 1) {
 581        g_warning("virgl will use the default rendernode");
 582    }
 583
 584    ret = virgl_renderer_init(g,
 585                              VIRGL_RENDERER_USE_EGL |
 586                              VIRGL_RENDERER_THREAD_SYNC,
 587                              &virgl_cbs);
 588    if (ret != 0) {
 589        return false;
 590    }
 591
 592    ret = virgl_renderer_get_poll_fd();
 593    if (ret != -1) {
 594        g->renderer_source =
 595            vug_source_new(&g->dev, ret, G_IO_IN, vg_virgl_poll, g);
 596    }
 597
 598    return true;
 599}
 600