From nobody Fri Apr 19 02:12:17 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1558704329; cv=none; d=zoho.com; s=zohoarc; b=gGHS2+z6moyr93pdlSGIkyDdtaCshF24NX3JnyJ6BcfQl6ZotP0FA5KevfDc979bpi2tlRn2XCJOvD58PpoNKKyXNrhiGV/WvaBmqyR0z8eG5eMWRtYZT5g+gemEh9VuNmPitJ2W6ySCOpaxAijJ+B1NGN8SNmIg2EXFckylTPs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1558704329; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=Fn0ToRq/N1RuQ6355wszQaK/4637+aOkcSoPtoK125E=; b=FI4a6M3ICxgd7S7ZruUeCFh37xiPtmukKNNFE1g2CYjmVUUwJOAlxnkPZJtMjWk1Z/WuOwYpPFY5UebdFxFOOILYNwwf26azJ5Q9itPL+auw+ASnpbAM3bfaj3kD9crVQkrjtkvQ8e9FE9+hU0M2S+wk04N2xBT64iEJdUip1Lg= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1558704329495840.0858680565033; Fri, 24 May 2019 06:25:29 -0700 (PDT) Received: from localhost ([127.0.0.1]:54588 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUABg-00077N-P0 for importer@patchew.org; Fri, 24 May 2019 09:24:44 -0400 Received: from eggs.gnu.org ([209.51.188.92]:58521) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA1A-0007aL-3W for qemu-devel@nongnu.org; Fri, 24 May 2019 09:13:56 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hU9xw-0001NM-Qh for qemu-devel@nongnu.org; Fri, 24 May 2019 09:10:34 -0400 Received: from mx1.redhat.com ([209.132.183.28]:39384) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hU9xo-0001FZ-Hh for qemu-devel@nongnu.org; Fri, 24 May 2019 09:10:28 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E6AF0F9E89 for ; Fri, 24 May 2019 13:10:07 +0000 (UTC) Received: from localhost (ovpn-112-23.ams2.redhat.com [10.36.112.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id 26B107D57C; Fri, 24 May 2019 13:09:57 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 24 May 2019 15:09:38 +0200 Message-Id: <20190524130946.31736-2-marcandre.lureau@redhat.com> In-Reply-To: <20190524130946.31736-1-marcandre.lureau@redhat.com> References: <20190524130946.31736-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Fri, 24 May 2019 13:10:12 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v9 1/9] vhost-user: add vhost_user_gpu_set_socket() X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a new vhost-user message to give a unix socket to a vhost-user backend for GPU display updates. Back when I started that work, I added a new GPU channel because the vhost-user protocol wasn't bidirectional. Since then, there is a vhost-user-slave channel for the slave to send requests to the master. We could extend it with GPU messages. However, the GPU protocol is quite orthogonal to vhost-user, thus I chose to have a new dedicated channel. See vhost-user-gpu.rst for the protocol details. Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/libvhost-user/libvhost-user.h | 1 + include/hw/virtio/vhost-backend.h | 2 + contrib/libvhost-user/libvhost-user.c | 1 + hw/virtio/vhost-user.c | 11 ++ MAINTAINERS | 6 + docs/interop/index.rst | 1 + docs/interop/vhost-user-gpu.rst | 242 ++++++++++++++++++++++++++ docs/interop/vhost-user.rst | 9 + 8 files changed, 273 insertions(+) create mode 100644 docs/interop/vhost-user-gpu.rst diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/= libvhost-user.h index 78b33306e8..3b888ff0a5 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -94,6 +94,7 @@ typedef enum VhostUserRequest { VHOST_USER_POSTCOPY_END =3D 30, VHOST_USER_GET_INFLIGHT_FD =3D 31, VHOST_USER_SET_INFLIGHT_FD =3D 32, + VHOST_USER_GPU_SET_SOCKET =3D 33, VHOST_USER_MAX } VhostUserRequest; =20 diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-ba= ckend.h index d6632a18e6..6f6670783f 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -170,4 +170,6 @@ int vhost_backend_invalidate_device_iotlb(struct vhost_= dev *dev, int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev, struct vhost_iotlb_msg *imsg); =20 +int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd); + #endif /* VHOST_BACKEND_H */ diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/= libvhost-user.c index c56f2dfe44..443b7e08c3 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -130,6 +130,7 @@ vu_request_to_string(unsigned int req) REQ(VHOST_USER_POSTCOPY_END), REQ(VHOST_USER_GET_INFLIGHT_FD), REQ(VHOST_USER_SET_INFLIGHT_FD), + REQ(VHOST_USER_GPU_SET_SOCKET), REQ(VHOST_USER_MAX), }; #undef REQ diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 553319c7ac..4ca5b2551e 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -96,6 +96,7 @@ typedef enum VhostUserRequest { VHOST_USER_POSTCOPY_END =3D 30, VHOST_USER_GET_INFLIGHT_FD =3D 31, VHOST_USER_SET_INFLIGHT_FD =3D 32, + VHOST_USER_GPU_SET_SOCKET =3D 33, VHOST_USER_MAX } VhostUserRequest; =20 @@ -353,6 +354,16 @@ static int vhost_user_write(struct vhost_dev *dev, Vho= stUserMsg *msg, return 0; } =20 +int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd) +{ + VhostUserMsg msg =3D { + .hdr.request =3D VHOST_USER_GPU_SET_SOCKET, + .hdr.flags =3D VHOST_USER_VERSION, + }; + + return vhost_user_write(dev, &msg, &fd, 1); +} + static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, struct vhost_log *log) { diff --git a/MAINTAINERS b/MAINTAINERS index 3cacd751bf..e04ae7817c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1676,6 +1676,12 @@ F: hw/display/virtio-gpu* F: hw/display/virtio-vga.c F: include/hw/virtio/virtio-gpu.h =20 +vhost-user-gpu +M: Marc-Andr=C3=A9 Lureau +M: Gerd Hoffmann +S: Maintained +F: docs/interop/vhost-user-gpu.rst + Cirrus VGA M: Gerd Hoffmann S: Odd Fixes diff --git a/docs/interop/index.rst b/docs/interop/index.rst index a037bd67ec..b4bfcab417 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -16,3 +16,4 @@ Contents: live-block-operations pr-helper vhost-user + vhost-user-gpu diff --git a/docs/interop/vhost-user-gpu.rst b/docs/interop/vhost-user-gpu.= rst new file mode 100644 index 0000000000..688f8b4259 --- /dev/null +++ b/docs/interop/vhost-user-gpu.rst @@ -0,0 +1,242 @@ +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Vhost-user-gpu Protocol +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +:Licence: This work is licensed under the terms of the GNU GPL, + version 2 or later. See the COPYING file in the top-level + directory. + +.. contents:: Table of Contents + +Introduction +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The vhost-user-gpu protocol is aiming at sharing the rendering result +of a virtio-gpu, done from a vhost-user slave process to a vhost-user +master process (such as QEMU). It bears a resemblance to a display +server protocol, if you consider QEMU as the display server and the +slave as the client, but in a very limited way. Typically, it will +work by setting a scanout/display configuration, before sending flush +events for the display updates. It will also update the cursor shape +and position. + +The protocol is sent over a UNIX domain stream socket, since it uses +socket ancillary data to share opened file descriptors (DMABUF fds or +shared memory). The socket is usually obtained via +``VHOST_USER_GPU_SET_SOCKET``. + +Requests are sent by the *slave*, and the optional replies by the +*master*. + +Wire format +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Unless specified differently, numbers are in the machine native byte +order. + +A vhost-user-gpu message (request and reply) consists of 3 header +fields and a payload. + ++---------+-------+------+---------+ +| request | flags | size | payload | ++---------+-------+------+---------+ + +Header +------ + +:request: ``u32``, type of the request + +:flags: ``u32``, 32-bit bit field: + + - Bit 2 is the reply flag - needs to be set on each reply + +:size: ``u32``, size of the payload + +Payload types +------------- + +Depending on the request type, **payload** can be: + +VhostUserGpuCursorPos +^^^^^^^^^^^^^^^^^^^^^ + ++------------+---+---+ +| scanout-id | x | y | ++------------+---+---+ + +:scanout-id: ``u32``, the scanout where the cursor is located + +:x/y: ``u32``, the cursor postion + +VhostUserGpuCursorUpdate +^^^^^^^^^^^^^^^^^^^^^^^^ + ++-----+-------+-------+--------+ +| pos | hot_x | hot_y | cursor | ++-----+-------+-------+--------+ + +:pos: a ``VhostUserGpuCursorPos``, the cursor location + +:hot_x/hot_y: ``u32``, the cursor hot location + +:cursor: ``[u32; 64 * 64]``, 64x64 RGBA cursor data (PIXMAN_a8r8g8b8 forma= t) + +VhostUserGpuScanout +^^^^^^^^^^^^^^^^^^^ + ++------------+---+---+ +| scanout-id | w | h | ++------------+---+---+ + +:scanout-id: ``u32``, the scanout configuration to set + +:w/h: ``u32``, the scanout width/height size + +VhostUserGpuUpdate +^^^^^^^^^^^^^^^^^^ + ++------------+---+---+---+---+------+ +| scanout-id | x | y | w | h | data | ++------------+---+---+---+---+------+ + +:scanout-id: ``u32``, the scanout content to update + +:x/y/w/h: ``u32``, region of the update + +:data: RGB data (PIXMAN_x8r8g8b8 format) + +VhostUserGpuDMABUFScanout +^^^^^^^^^^^^^^^^^^^^^^^^^ + ++------------+---+---+---+---+-----+-----+--------+-------+--------+ +| scanout-id | x | y | w | h | fdw | fwh | stride | flags | fourcc | ++------------+---+---+---+---+-----+-----+--------+-------+--------+ + +:scanout-id: ``u32``, the scanout configuration to set + +:x/y: ``u32``, the location of the scanout within the DMABUF + +:w/h: ``u32``, the scanout width/height size + +:fdw/fdh/stride/flags: ``u32``, the DMABUF width/height/stride/flags + +:fourcc: ``i32``, the DMABUF fourcc + + +C structure +----------- + +In QEMU the vhost-user-gpu message is implemented with the following struc= t: + +.. code:: c + + typedef struct VhostUserGpuMsg { + uint32_t request; /* VhostUserGpuRequest */ + uint32_t flags; + uint32_t size; /* the following payload size */ + union { + VhostUserGpuCursorPos cursor_pos; + VhostUserGpuCursorUpdate cursor_update; + VhostUserGpuScanout scanout; + VhostUserGpuUpdate update; + VhostUserGpuDMABUFScanout dmabuf_scanout; + struct virtio_gpu_resp_display_info display_info; + uint64_t u64; + } payload; + } QEMU_PACKED VhostUserGpuMsg; + +Protocol features +----------------- + +None yet. + +As the protocol may need to evolve, new messages and communication +changes are negotiated thanks to preliminary +``VHOST_USER_GPU_GET_PROTOCOL_FEATURES`` and +``VHOST_USER_GPU_SET_PROTOCOL_FEATURES`` requests. + +Communication +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Message types +------------- + +``VHOST_USER_GPU_GET_PROTOCOL_FEATURES`` + :id: 1 + :request payload: N/A + :reply payload: ``u64`` + + Get the supported protocol features bitmask. + +``VHOST_USER_GPU_SET_PROTOCOL_FEATURES`` + :id: 2 + :request payload: ``u64`` + :reply payload: N/A + + Enable protocol features using a bitmask. + +``VHOST_USER_GPU_GET_DISPLAY_INFO`` + :id: 3 + :request payload: N/A + :reply payload: ``struct virtio_gpu_resp_display_info`` (from virtio spe= cification) + + Get the preferred display configuration. + +``VHOST_USER_GPU_CURSOR_POS`` + :id: 4 + :request payload: ``VhostUserGpuCursorPos`` + :reply payload: N/A + + Set/show the cursor position. + +``VHOST_USER_GPU_CURSOR_POS_HIDE`` + :id: 5 + :request payload: ``VhostUserGpuCursorPos`` + :reply payload: N/A + + Set/hide the cursor. + +``VHOST_USER_GPU_CURSOR_UPDATE`` + :id: 6 + :request payload: ``VhostUserGpuCursorUpdate`` + :reply payload: N/A + + Update the cursor shape and location. + +``VHOST_USER_GPU_SCANOUT`` + :id: 7 + :request payload: ``VhostUserGpuScanout`` + :reply payload: N/A + + Set the scanout resolution. To disable a scanout, the dimensions + width/height are set to 0. + +``VHOST_USER_GPU_UPDATE`` + :id: 8 + :request payload: ``VhostUserGpuUpdate`` + :reply payload: N/A + + Update the scanout content. The data payload contains the graphical bits. + The display should be flushed and presented. + +``VHOST_USER_GPU_DMABUF_SCANOUT`` + :id: 9 + :request payload: ``VhostUserGpuDMABUFScanout`` + :reply payload: N/A + + Set the scanout resolution/configuration, and share a DMABUF file + descriptor for the scanout content, which is passed as ancillary + data. To disable a scanout, the dimensions width/height are set + to 0, there is no file descriptor passed. + +``VHOST_USER_GPU_DMABUF_UPDATE`` + :id: 10 + :request payload: ``VhostUserGpuUpdate`` + :reply payload: empty payload + + The display should be flushed and presented according to updated + region from ``VhostUserGpuUpdate``. + + Note: there is no data payload, since the scanout is shared thanks + to DMABUF, that must have been set previously with + ``VHOST_USER_GPU_DMABUF_SCANOUT``. diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index 7f3232c798..dc0ff9211f 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -1163,6 +1163,15 @@ Master message types send the shared inflight buffer back to slave so that slave could get inflight I/O after a crash or restart. =20 +``VHOST_USER_GPU_SET_SOCKET`` + :id: 33 + :equivalent ioctl: N/A + :master payload: N/A + + Sets the GPU protocol socket file descriptor, which is passed as + ancillary data. The GPU protocol is used to inform the master of + rendering state and updates. See vhost-user-gpu.rst for details. + Slave message types ------------------- =20 --=20 2.22.0.rc1.1.g079e7d2849.dirty From nobody Fri Apr 19 02:12:17 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1558704441; cv=none; d=zoho.com; s=zohoarc; b=cXTgROHwtiC+h5jBCxCdALf2cwxZFkM2azB2x4zgZ8V9w3AW7tDeSVNQfJ/6g8lOSbPpkcrl7QNtgvaw0eTvXcLha+/jTiutoLNw5WNoQvLmaIKKD533U96H88hQWvoygSNJoe331IJpluZkhHik/ltx6/OaWFdK4StbQXuUnao= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1558704441; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=zkJbQYNWaibdvN6PFIMVldd7McehfvZESDmeIhvP5Rg=; b=PBRl/4HpfkK+qK7I/dJ3OFoLSy6Vpq1+FF9hQYAjVYqzPOaqmkwUkBmSX/6mn6GyVTekI/gAtbUshPZYl2ltuIcNS5fUdU/r0yZfOPleEfWGyS3bfZJ7BSeg1YPVtaLsuJXttwxuHPnvFxSI6C2rtCg6BvdR2lPHFnv2wNvHTAQ= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (209.51.188.17 [209.51.188.17]) by mx.zohomail.com with SMTPS id 1558704441656743.4589939996567; Fri, 24 May 2019 06:27:21 -0700 (PDT) Received: from localhost ([127.0.0.1]:54630 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUAE5-0000E1-Oc for importer@patchew.org; Fri, 24 May 2019 09:27:13 -0400 Received: from eggs.gnu.org ([209.51.188.92]:58536) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA1B-0007cO-1Z for qemu-devel@nongnu.org; Fri, 24 May 2019 09:13:56 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hU9xw-0001N9-Q8 for qemu-devel@nongnu.org; Fri, 24 May 2019 09:10:34 -0400 Received: from mx1.redhat.com ([209.132.183.28]:43058) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hU9xo-0001FO-HZ for qemu-devel@nongnu.org; Fri, 24 May 2019 09:10:26 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 6D4BA75733 for ; Fri, 24 May 2019 13:10:17 +0000 (UTC) Received: from localhost (ovpn-112-23.ams2.redhat.com [10.36.112.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8EF021001284; Fri, 24 May 2019 13:10:09 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 24 May 2019 15:09:39 +0200 Message-Id: <20190524130946.31736-3-marcandre.lureau@redhat.com> In-Reply-To: <20190524130946.31736-1-marcandre.lureau@redhat.com> References: <20190524130946.31736-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Fri, 24 May 2019 13:10:17 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v9 2/9] virtio-gpu: add bswap helpers header X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" The helper functions are useful to build the vhost-user-gpu backend. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/hw/virtio/virtio-gpu-bswap.h | 61 ++++++++++++++++++++++++++++ hw/display/virtio-gpu.c | 43 +------------------- 2 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 include/hw/virtio/virtio-gpu-bswap.h diff --git a/include/hw/virtio/virtio-gpu-bswap.h b/include/hw/virtio/virti= o-gpu-bswap.h new file mode 100644 index 0000000000..38d12160f6 --- /dev/null +++ b/include/hw/virtio/virtio-gpu-bswap.h @@ -0,0 +1,61 @@ +/* + * Virtio GPU Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_VIRTIO_GPU_BSWAP_H +#define HW_VIRTIO_GPU_BSWAP_H + +#include "qemu/bswap.h" + +static inline void +virtio_gpu_ctrl_hdr_bswap(struct virtio_gpu_ctrl_hdr *hdr) +{ + le32_to_cpus(&hdr->type); + le32_to_cpus(&hdr->flags); + le64_to_cpus(&hdr->fence_id); + le32_to_cpus(&hdr->ctx_id); + le32_to_cpus(&hdr->padding); +} + +static inline void +virtio_gpu_bswap_32(void *ptr, size_t size) +{ +#ifdef HOST_WORDS_BIGENDIAN + + size_t i; + struct virtio_gpu_ctrl_hdr *hdr =3D (struct virtio_gpu_ctrl_hdr *) ptr; + + virtio_gpu_ctrl_hdr_bswap(hdr); + + i =3D sizeof(struct virtio_gpu_ctrl_hdr); + while (i < size) { + le32_to_cpus((uint32_t *)(ptr + i)); + i =3D i + sizeof(uint32_t); + } + +#endif +} + +static inline void +virtio_gpu_t2d_bswap(struct virtio_gpu_transfer_to_host_2d *t2d) +{ + virtio_gpu_ctrl_hdr_bswap(&t2d->hdr); + le32_to_cpus(&t2d->r.x); + le32_to_cpus(&t2d->r.y); + le32_to_cpus(&t2d->r.width); + le32_to_cpus(&t2d->r.height); + le64_to_cpus(&t2d->offset); + le32_to_cpus(&t2d->resource_id); + le32_to_cpus(&t2d->padding); +} + +#endif diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 9e37e0ac96..c35025b6fb 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -20,6 +20,7 @@ #include "sysemu/dma.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-gpu.h" +#include "hw/virtio/virtio-gpu-bswap.h" #include "hw/virtio/virtio-bus.h" #include "hw/display/edid.h" #include "migration/blocker.h" @@ -34,48 +35,6 @@ virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource= _id); static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, struct virtio_gpu_simple_resource *= res); =20 -static void -virtio_gpu_ctrl_hdr_bswap(struct virtio_gpu_ctrl_hdr *hdr) -{ - le32_to_cpus(&hdr->type); - le32_to_cpus(&hdr->flags); - le64_to_cpus(&hdr->fence_id); - le32_to_cpus(&hdr->ctx_id); - le32_to_cpus(&hdr->padding); -} - -static void virtio_gpu_bswap_32(void *ptr, - size_t size) -{ -#ifdef HOST_WORDS_BIGENDIAN - - size_t i; - struct virtio_gpu_ctrl_hdr *hdr =3D (struct virtio_gpu_ctrl_hdr *) ptr; - - virtio_gpu_ctrl_hdr_bswap(hdr); - - i =3D sizeof(struct virtio_gpu_ctrl_hdr); - while (i < size) { - le32_to_cpus((uint32_t *)(ptr + i)); - i =3D i + sizeof(uint32_t); - } - -#endif -} - -static void -virtio_gpu_t2d_bswap(struct virtio_gpu_transfer_to_host_2d *t2d) -{ - virtio_gpu_ctrl_hdr_bswap(&t2d->hdr); - le32_to_cpus(&t2d->r.x); - le32_to_cpus(&t2d->r.y); - le32_to_cpus(&t2d->r.width); - le32_to_cpus(&t2d->r.height); - le64_to_cpus(&t2d->offset); - le32_to_cpus(&t2d->resource_id); - le32_to_cpus(&t2d->padding); -} - #ifdef CONFIG_VIRGL #include #define VIRGL(_g, _virgl, _simple, ...) \ --=20 2.22.0.rc1.1.g079e7d2849.dirty From nobody Fri Apr 19 02:12:17 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1558703885; cv=none; d=zoho.com; s=zohoarc; b=SyAn5TbOTtFUmHiQNOlcf4oLNzmEJcRciu7H6Y09vdXjHlW6a/ZFxV+l9LUjhTDhp6NB12YPkprTKtmgdOgE06DUkD5mhQ5CBqqRzNQ/Th0umPn0nB0wuafWvKWpMnrDIlk3ovo9SuzgLWMPFimniBnJJtz59tODNn3ppdtimUk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1558703885; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=dREthaXYGB0BxIN/NYR53F5aHd63ZQaQT+KU032XZAU=; b=GwZOhKl3Tw5iaybpx6qrFVSjOHVDG+ZSus0sN81U7bWaNjhjIx+fN4VfgE5rFDJ5KW+RLVN/gOyiCTywtEUpB/Ia8WbT7GeLGYiAO8Mwdz+Cg8SkoUOawbY1yBQl+VAqqj6SgSzSIQPi4pK0nV9wqy4qIfL1ECAYYkCEx+52AE4= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1558703885941515.9963770497961; Fri, 24 May 2019 06:18:05 -0700 (PDT) Received: from localhost ([127.0.0.1]:54498 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA58-0002WQ-VP for importer@patchew.org; Fri, 24 May 2019 09:17:59 -0400 Received: from eggs.gnu.org ([209.51.188.92]:58631) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA19-0007kR-Sf for qemu-devel@nongnu.org; Fri, 24 May 2019 09:13:53 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hU9yI-0001V9-81 for qemu-devel@nongnu.org; Fri, 24 May 2019 09:10:56 -0400 Received: from mx1.redhat.com ([209.132.183.28]:60548) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hU9yA-0001Qq-KC for qemu-devel@nongnu.org; Fri, 24 May 2019 09:10:50 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 6819B30BBE98 for ; Fri, 24 May 2019 13:10:31 +0000 (UTC) Received: from localhost (ovpn-112-23.ams2.redhat.com [10.36.112.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id E3A1D7D901; Fri, 24 May 2019 13:10:18 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 24 May 2019 15:09:40 +0200 Message-Id: <20190524130946.31736-4-marcandre.lureau@redhat.com> In-Reply-To: <20190524130946.31736-1-marcandre.lureau@redhat.com> References: <20190524130946.31736-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.49]); Fri, 24 May 2019 13:10:36 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v9 3/9] virtio-gpu: add a pixman helper header X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" This will allow to share the format conversion function with vhost-user-gpu. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/hw/virtio/virtio-gpu-pixman.h | 45 +++++++++++++++++++++++++++ hw/display/virtio-gpu.c | 29 ++--------------- 2 files changed, 48 insertions(+), 26 deletions(-) create mode 100644 include/hw/virtio/virtio-gpu-pixman.h diff --git a/include/hw/virtio/virtio-gpu-pixman.h b/include/hw/virtio/virt= io-gpu-pixman.h new file mode 100644 index 0000000000..4dba782758 --- /dev/null +++ b/include/hw/virtio/virtio-gpu-pixman.h @@ -0,0 +1,45 @@ +/* + * Virtio GPU Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_VIRTIO_GPU_PIXMAN_H +#define HW_VIRTIO_GPU_PIXMAN_H + +#include "ui/qemu-pixman.h" +#include "standard-headers/linux/virtio_gpu.h" + +static inline pixman_format_code_t +virtio_gpu_get_pixman_format(uint32_t virtio_gpu_format) +{ + switch (virtio_gpu_format) { + case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: + return PIXMAN_BE_b8g8r8x8; + case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: + return PIXMAN_BE_b8g8r8a8; + case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: + return PIXMAN_BE_x8r8g8b8; + case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: + return PIXMAN_BE_a8r8g8b8; + case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: + return PIXMAN_BE_r8g8b8x8; + case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: + return PIXMAN_BE_r8g8b8a8; + case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: + return PIXMAN_BE_x8b8g8r8; + case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: + return PIXMAN_BE_a8b8g8r8; + default: + return 0; + } +} + +#endif diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index c35025b6fb..3262f7f1f1 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -21,6 +21,7 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-gpu-bswap.h" +#include "hw/virtio/virtio-gpu-pixman.h" #include "hw/virtio/virtio-bus.h" #include "hw/display/edid.h" #include "migration/blocker.h" @@ -298,30 +299,6 @@ void virtio_gpu_get_edid(VirtIOGPU *g, virtio_gpu_ctrl_response(g, cmd, &edid.hdr, sizeof(edid)); } =20 -static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format) -{ - switch (virtio_gpu_format) { - case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: - return PIXMAN_BE_b8g8r8x8; - case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: - return PIXMAN_BE_b8g8r8a8; - case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: - return PIXMAN_BE_x8r8g8b8; - case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: - return PIXMAN_BE_a8r8g8b8; - case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: - return PIXMAN_BE_r8g8b8x8; - case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: - return PIXMAN_BE_r8g8b8a8; - case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: - return PIXMAN_BE_x8b8g8r8; - case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: - return PIXMAN_BE_a8b8g8r8; - default: - return 0; - } -} - static uint32_t calc_image_hostmem(pixman_format_code_t pformat, uint32_t width, uint32_t height) { @@ -368,7 +345,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, res->format =3D c2d.format; res->resource_id =3D c2d.resource_id; =20 - pformat =3D get_pixman_format(c2d.format); + pformat =3D virtio_gpu_get_pixman_format(c2d.format); if (!pformat) { qemu_log_mask(LOG_GUEST_ERROR, "%s: host couldn't handle guest format %d\n", @@ -1142,7 +1119,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque,= size_t size, res->iov_cnt =3D qemu_get_be32(f); =20 /* allocate */ - pformat =3D get_pixman_format(res->format); + pformat =3D virtio_gpu_get_pixman_format(res->format); if (!pformat) { g_free(res); return -EINVAL; --=20 2.22.0.rc1.1.g079e7d2849.dirty From nobody Fri Apr 19 02:12:17 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1558704220; cv=none; d=zoho.com; s=zohoarc; b=n3DHJd1BQcaAE8yBjQqcEOKwqppTyKa7jRyBbuA3zfYJNHfcwKbKBMlpdLYpyr5F2H6CKVZaN4mtaOa9WWV6xWrSHBQefGCiHU4yr8en/+WYmQ7tnYNJUEDVGRJtbcgJQJG33Jn8k/84FFVUpG0mbBFHcdVYSufKA6TJmABMl1M= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1558704220; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=FJlBQdtHL7yFBYOD8KiRSgs11LZYQijY1ydy1EKF4zA=; b=gosXUWECN5QXea7b5hQP7PihrF/AVXUD/PmYZOa20lRVtkL7wtjdMtrOw4EiMImEjsPBEzM5bqG5/yLT5O2hA6FiEdB8ExAaWQjs9Zx7ct6NOutJsNO0fQIGjiFBCGr34IWxdWWRedQxhJXb87UZAayAgBaRhSseI8hkjLrzrJg= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1558704220340375.9934445494184; Fri, 24 May 2019 06:23:40 -0700 (PDT) Received: from localhost ([127.0.0.1]:54582 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUAAX-0006UV-Dm for importer@patchew.org; Fri, 24 May 2019 09:23:33 -0400 Received: from eggs.gnu.org ([209.51.188.92]:58536) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA19-0007cO-TE for qemu-devel@nongnu.org; Fri, 24 May 2019 09:13:52 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hU9yI-0001VX-Gy for qemu-devel@nongnu.org; Fri, 24 May 2019 09:10:56 -0400 Received: from mx1.redhat.com ([209.132.183.28]:44710) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hU9yI-0001Sl-9N for qemu-devel@nongnu.org; Fri, 24 May 2019 09:10:54 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 2C2357BF08 for ; Fri, 24 May 2019 13:10:43 +0000 (UTC) Received: from localhost (ovpn-112-23.ams2.redhat.com [10.36.112.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1AFD27E5D2; Fri, 24 May 2019 13:10:32 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 24 May 2019 15:09:41 +0200 Message-Id: <20190524130946.31736-5-marcandre.lureau@redhat.com> In-Reply-To: <20190524130946.31736-1-marcandre.lureau@redhat.com> References: <20190524130946.31736-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Fri, 24 May 2019 13:10:43 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v9 4/9] util: compile drm.o on Linux X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" OpenGL isn't required to use DRM rendernodes. The following patches uses it for 2d resources for ex. Signed-off-by: Marc-Andr=C3=A9 Lureau --- util/Makefile.objs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/Makefile.objs b/util/Makefile.objs index c27a923dbe..75e02cea12 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -53,7 +53,7 @@ util-obj-y +=3D systemd.o util-obj-y +=3D iova-tree.o util-obj-$(CONFIG_INOTIFY1) +=3D filemonitor-inotify.o util-obj-$(CONFIG_LINUX) +=3D vfio-helpers.o -util-obj-$(CONFIG_OPENGL) +=3D drm.o +util-obj-$(CONFIG_LINUX) +=3D drm.o util-obj-y +=3D guest-random.o =20 stub-obj-y +=3D filemonitor-stub.o --=20 2.22.0.rc1.1.g079e7d2849.dirty From nobody Fri Apr 19 02:12:17 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1558704045; cv=none; d=zoho.com; s=zohoarc; b=Y3iC1MqD3zscKRjf34Od0kBa3PCcJMTC5FpI5Vde2uoYRKx8NVD8JIhIAFnuNJeLDBpZCDEhyssSW9YHuPJXPUNFvBD4P6wa45wGnkU20g2IX7fy9OFE4MJtowhgbxHQ9W/5Tb8Ac7qj6OXsrQBoMeyUTDImxo0rAg3IYqnsTh8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1558704045; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=57hjqQl6vAXPX4dV+Tdbo2BQJpFQh4pTLdmPLaUycqs=; b=PVeAqw1plPwJrlg6cbOgjTEU2eVzqtTIi1nw6y7fT16XqJ5fGLOX951lX3WZ+Y2GGuiJlH7xlaWg5tCSKYCOvYzpmxTa3XLJT808y/E/AfMXnBnbu1ssZxy6EDB7vYIyEyK0sWBcxVOA0FKapWIHk+OYSd1g+2HCU04kdbsFQx0= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (209.51.188.17 [209.51.188.17]) by mx.zohomail.com with SMTPS id 1558704045487141.43182721656297; Fri, 24 May 2019 06:20:45 -0700 (PDT) Received: from localhost ([127.0.0.1]:54527 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA7c-0004AH-Vq for importer@patchew.org; Fri, 24 May 2019 09:20:33 -0400 Received: from eggs.gnu.org ([209.51.188.92]:58529) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA18-0007aO-Rv for qemu-devel@nongnu.org; Fri, 24 May 2019 09:13:58 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hU9yO-0001aB-DC for qemu-devel@nongnu.org; Fri, 24 May 2019 09:11:06 -0400 Received: from mx1.redhat.com ([209.132.183.28]:45176) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hU9yO-0001Z9-08 for qemu-devel@nongnu.org; Fri, 24 May 2019 09:11:00 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 4A7176FAC1 for ; Fri, 24 May 2019 13:10:54 +0000 (UTC) Received: from localhost (ovpn-112-23.ams2.redhat.com [10.36.112.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id D8A0C7D899; Fri, 24 May 2019 13:10:44 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 24 May 2019 15:09:42 +0200 Message-Id: <20190524130946.31736-6-marcandre.lureau@redhat.com> In-Reply-To: <20190524130946.31736-1-marcandre.lureau@redhat.com> References: <20190524130946.31736-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Fri, 24 May 2019 13:10:59 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v9 5/9] contrib: add vhost-user-gpu X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a vhost-user gpu backend, based on virtio-gpu/3d device. It is associated with a vhost-user-gpu device. Various TODO and nice to have items: - multi-head support - crash & resume handling - accelerated rendering/display that avoids the waiting round trips - edid support Signed-off-by: Marc-Andr=C3=A9 Lureau --- configure | 17 + contrib/vhost-user-gpu/virgl.h | 25 + contrib/vhost-user-gpu/vugbm.h | 67 ++ contrib/vhost-user-gpu/vugpu.h | 177 +++ contrib/vhost-user-gpu/main.c | 1185 ++++++++++++++++++++ contrib/vhost-user-gpu/virgl.c | 579 ++++++++++ contrib/vhost-user-gpu/vugbm.c | 328 ++++++ MAINTAINERS | 2 + Makefile | 24 +- Makefile.objs | 1 + contrib/vhost-user-gpu/50-qemu-gpu.json.in | 5 + contrib/vhost-user-gpu/Makefile.objs | 10 + rules.mak | 9 +- 13 files changed, 2427 insertions(+), 2 deletions(-) create mode 100644 contrib/vhost-user-gpu/virgl.h create mode 100644 contrib/vhost-user-gpu/vugbm.h create mode 100644 contrib/vhost-user-gpu/vugpu.h create mode 100644 contrib/vhost-user-gpu/main.c create mode 100644 contrib/vhost-user-gpu/virgl.c create mode 100644 contrib/vhost-user-gpu/vugbm.c create mode 100644 contrib/vhost-user-gpu/50-qemu-gpu.json.in create mode 100644 contrib/vhost-user-gpu/Makefile.objs diff --git a/configure b/configure index 528b9ff705..8148ac6cae 100755 --- a/configure +++ b/configure @@ -4099,6 +4099,13 @@ libs_softmmu=3D"$libs_softmmu $fdt_libs" ########################################## # opengl probe (for sdl2, gtk, milkymist-tmu2) =20 +gbm=3D"no" +if $pkg_config gbm; then + gbm_cflags=3D"$($pkg_config --cflags gbm)" + gbm_libs=3D"$($pkg_config --libs gbm)" + gbm=3D"yes" +fi + if test "$opengl" !=3D "no" ; then opengl_pkgs=3D"epoxy gbm" if $pkg_config $opengl_pkgs; then @@ -6964,6 +6971,13 @@ if test "$opengl" =3D "yes" ; then fi fi =20 +if test "$gbm" =3D "yes" ; then + echo "CONFIG_GBM=3Dy" >> $config_host_mak + echo "GBM_LIBS=3D$gbm_libs" >> $config_host_mak + echo "GBM_CFLAGS=3D$gbm_cflags" >> $config_host_mak +fi + + if test "$malloc_trim" =3D "yes" ; then echo "CONFIG_MALLOC_TRIM=3Dy" >> $config_host_mak fi @@ -7859,6 +7873,9 @@ echo "QEMU_CFLAGS+=3D$cflags" >> $config_target_mak =20 done # for target in $targets =20 +echo "PIXMAN_CFLAGS=3D$pixman_cflags" >> $config_host_mak +echo "PIXMAN_LIBS=3D$pixman_libs" >> $config_host_mak + if test -n "$enabled_cross_compilers"; then echo echo "NOTE: cross-compilers enabled: $enabled_cross_compilers" diff --git a/contrib/vhost-user-gpu/virgl.h b/contrib/vhost-user-gpu/virgl.h new file mode 100644 index 0000000000..f952bc9d4f --- /dev/null +++ b/contrib/vhost-user-gpu/virgl.h @@ -0,0 +1,25 @@ +/* + * Virtio vhost-user GPU Device + * + * Copyright Red Hat, Inc. 2013-2018 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ +#ifndef VUGPU_VIRGL_H_ +#define VUGPU_VIRGL_H_ + +#include "vugpu.h" + +bool vg_virgl_init(VuGpu *g); +uint32_t vg_virgl_get_num_capsets(void); +void vg_virgl_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd); +void vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id, + gpointer data); + +#endif diff --git a/contrib/vhost-user-gpu/vugbm.h b/contrib/vhost-user-gpu/vugbm.h new file mode 100644 index 0000000000..c0bf27af9b --- /dev/null +++ b/contrib/vhost-user-gpu/vugbm.h @@ -0,0 +1,67 @@ +/* + * Virtio vhost-user GPU Device + * + * GBM helpers + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ +#ifndef VHOST_USER_GPU_GBM_H +#define VHOST_USER_GPU_GBM_H + +#include "qemu/osdep.h" + +#ifdef CONFIG_MEMFD +#include +#include +#endif + +#ifdef CONFIG_GBM +#include +#endif + +struct vugbm_buffer; + +struct vugbm_device { + bool inited; + int fd; +#ifdef CONFIG_GBM + struct gbm_device *dev; +#endif + + bool (*alloc_bo)(struct vugbm_buffer *buf); + void (*free_bo)(struct vugbm_buffer *buf); + bool (*get_fd)(struct vugbm_buffer *buf, int *fd); + bool (*map_bo)(struct vugbm_buffer *buf); + void (*unmap_bo)(struct vugbm_buffer *buf); + void (*device_destroy)(struct vugbm_device *dev); +}; + +struct vugbm_buffer { + struct vugbm_device *dev; + +#ifdef CONFIG_MEMFD + int memfd; +#endif +#ifdef CONFIG_GBM + struct gbm_bo *bo; + void *mmap_data; +#endif + + uint8_t *mmap; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t format; +}; + +bool vugbm_device_init(struct vugbm_device *dev, int fd); +void vugbm_device_destroy(struct vugbm_device *dev); + +bool vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device = *dev, + uint32_t width, uint32_t height); +bool vugbm_buffer_can_get_dmabuf_fd(struct vugbm_buffer *buffer); +bool vugbm_buffer_get_dmabuf_fd(struct vugbm_buffer *buffer, int *fd); +void vugbm_buffer_destroy(struct vugbm_buffer *buffer); + +#endif diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h new file mode 100644 index 0000000000..458e92a1b3 --- /dev/null +++ b/contrib/vhost-user-gpu/vugpu.h @@ -0,0 +1,177 @@ +/* + * Virtio vhost-user GPU Device + * + * Copyright Red Hat, Inc. 2013-2018 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ +#ifndef VUGPU_H_ +#define VUGPU_H_ + +#include "qemu/osdep.h" + +#include "contrib/libvhost-user/libvhost-user-glib.h" +#include "standard-headers/linux/virtio_gpu.h" + +#include "qemu/queue.h" +#include "qemu/iov.h" +#include "qemu/bswap.h" +#include "vugbm.h" + +typedef enum VhostUserGpuRequest { + VHOST_USER_GPU_NONE =3D 0, + VHOST_USER_GPU_GET_PROTOCOL_FEATURES, + VHOST_USER_GPU_SET_PROTOCOL_FEATURES, + VHOST_USER_GPU_GET_DISPLAY_INFO, + VHOST_USER_GPU_CURSOR_POS, + VHOST_USER_GPU_CURSOR_POS_HIDE, + VHOST_USER_GPU_CURSOR_UPDATE, + VHOST_USER_GPU_SCANOUT, + VHOST_USER_GPU_UPDATE, + VHOST_USER_GPU_DMABUF_SCANOUT, + VHOST_USER_GPU_DMABUF_UPDATE, +} VhostUserGpuRequest; + +typedef struct VhostUserGpuDisplayInfoReply { + struct virtio_gpu_resp_display_info info; +} VhostUserGpuDisplayInfoReply; + +typedef struct VhostUserGpuCursorPos { + uint32_t scanout_id; + uint32_t x; + uint32_t y; +} QEMU_PACKED VhostUserGpuCursorPos; + +typedef struct VhostUserGpuCursorUpdate { + VhostUserGpuCursorPos pos; + uint32_t hot_x; + uint32_t hot_y; + uint32_t data[64 * 64]; +} QEMU_PACKED VhostUserGpuCursorUpdate; + +typedef struct VhostUserGpuScanout { + uint32_t scanout_id; + uint32_t width; + uint32_t height; +} QEMU_PACKED VhostUserGpuScanout; + +typedef struct VhostUserGpuUpdate { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint8_t data[]; +} QEMU_PACKED VhostUserGpuUpdate; + +typedef struct VhostUserGpuDMABUFScanout { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint32_t fd_width; + uint32_t fd_height; + uint32_t fd_stride; + uint32_t fd_flags; + int fd_drm_fourcc; +} QEMU_PACKED VhostUserGpuDMABUFScanout; + +typedef struct VhostUserGpuMsg { + uint32_t request; /* VhostUserGpuRequest */ + uint32_t flags; + uint32_t size; /* the following payload size */ + union { + VhostUserGpuCursorPos cursor_pos; + VhostUserGpuCursorUpdate cursor_update; + VhostUserGpuScanout scanout; + VhostUserGpuUpdate update; + VhostUserGpuDMABUFScanout dmabuf_scanout; + struct virtio_gpu_resp_display_info display_info; + uint64_t u64; + } payload; +} QEMU_PACKED VhostUserGpuMsg; + +static VhostUserGpuMsg m __attribute__ ((unused)); +#define VHOST_USER_GPU_HDR_SIZE \ + (sizeof(m.request) + sizeof(m.flags) + sizeof(m.size)) + +#define VHOST_USER_GPU_MSG_FLAG_REPLY 0x4 + +struct virtio_gpu_scanout { + uint32_t width, height; + int x, y; + int invalidate; + uint32_t resource_id; +}; + +typedef struct VuGpu { + VugDev dev; + struct virtio_gpu_config virtio_config; + struct vugbm_device gdev; + int sock_fd; + int drm_rnode_fd; + GSource *renderer_source; + guint wait_ok; + + bool virgl; + bool virgl_inited; + uint32_t inflight; + + struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; + QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; + QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; +} VuGpu; + +struct virtio_gpu_ctrl_command { + VuVirtqElement elem; + VuVirtq *vq; + struct virtio_gpu_ctrl_hdr cmd_hdr; + uint32_t error; + bool finished; + QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; +}; + +#define VUGPU_FILL_CMD(out) do { \ + size_t s; \ + s =3D iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ + &out, sizeof(out)); \ + if (s !=3D sizeof(out)) { \ + g_critical("%s: command size incorrect %zu vs %zu", \ + __func__, s, sizeof(out)); \ + return; \ + } \ + } while (0) + + +void vg_ctrl_response(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd, + struct virtio_gpu_ctrl_hdr *resp, + size_t resp_len); + +void vg_ctrl_response_nodata(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd, + enum virtio_gpu_ctrl_type type); + +int vg_create_mapping_iov(VuGpu *g, + struct virtio_gpu_resource_attach_backing *a= b, + struct virtio_gpu_ctrl_command *cmd, + struct iovec **iov); + +void vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd= ); + +void vg_wait_ok(VuGpu *g); + +void vg_send_msg(VuGpu *g, const VhostUserGpuMsg *msg, int fd); + +bool vg_recv_msg(VuGpu *g, uint32_t expect_req, uint32_t expect_size, + gpointer payload); + + +#endif diff --git a/contrib/vhost-user-gpu/main.c b/contrib/vhost-user-gpu/main.c new file mode 100644 index 0000000000..f9e2146b69 --- /dev/null +++ b/contrib/vhost-user-gpu/main.c @@ -0,0 +1,1185 @@ +/* + * Virtio vhost-user GPU Device + * + * Copyright Red Hat, Inc. 2013-2018 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/drm.h" +#include "qapi/error.h" +#include "qemu/sockets.h" + +#include +#include + +#include "vugpu.h" +#include "hw/virtio/virtio-gpu-bswap.h" +#include "hw/virtio/virtio-gpu-pixman.h" +#include "virgl.h" +#include "vugbm.h" + +struct virtio_gpu_simple_resource { + uint32_t resource_id; + uint32_t width; + uint32_t height; + uint32_t format; + struct iovec *iov; + unsigned int iov_cnt; + uint32_t scanout_bitmask; + pixman_image_t *image; + struct vugbm_buffer buffer; + QTAILQ_ENTRY(virtio_gpu_simple_resource) next; +}; + +static gboolean opt_print_caps; +static int opt_fdnum =3D -1; +static char *opt_socket_path; +static char *opt_render_node; +static gboolean opt_virgl; + +static void vg_handle_ctrl(VuDev *dev, int qidx); + +static const char * +vg_cmd_to_string(int cmd) +{ +#define CMD(cmd) [cmd] =3D #cmd + static const char *vg_cmd_str[] =3D { + CMD(VIRTIO_GPU_UNDEFINED), + + /* 2d commands */ + CMD(VIRTIO_GPU_CMD_GET_DISPLAY_INFO), + CMD(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D), + CMD(VIRTIO_GPU_CMD_RESOURCE_UNREF), + CMD(VIRTIO_GPU_CMD_SET_SCANOUT), + CMD(VIRTIO_GPU_CMD_RESOURCE_FLUSH), + CMD(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D), + CMD(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING), + CMD(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING), + CMD(VIRTIO_GPU_CMD_GET_CAPSET_INFO), + CMD(VIRTIO_GPU_CMD_GET_CAPSET), + + /* 3d commands */ + CMD(VIRTIO_GPU_CMD_CTX_CREATE), + CMD(VIRTIO_GPU_CMD_CTX_DESTROY), + CMD(VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE), + CMD(VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE), + CMD(VIRTIO_GPU_CMD_RESOURCE_CREATE_3D), + CMD(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D), + CMD(VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D), + CMD(VIRTIO_GPU_CMD_SUBMIT_3D), + + /* cursor commands */ + CMD(VIRTIO_GPU_CMD_UPDATE_CURSOR), + CMD(VIRTIO_GPU_CMD_MOVE_CURSOR), + }; +#undef REQ + + if (cmd >=3D 0 && cmd < G_N_ELEMENTS(vg_cmd_str)) { + return vg_cmd_str[cmd]; + } else { + return "unknown"; + } +} + +static int +vg_sock_fd_read(int sock, void *buf, ssize_t buflen) +{ + int ret; + + do { + ret =3D read(sock, buf, buflen); + } while (ret < 0 && (errno =3D=3D EINTR || errno =3D=3D EAGAIN)); + + g_warn_if_fail(ret =3D=3D buflen); + return ret; +} + +static void +vg_sock_fd_close(VuGpu *g) +{ + if (g->sock_fd >=3D 0) { + close(g->sock_fd); + g->sock_fd =3D -1; + } +} + +static gboolean +source_wait_cb(gint fd, GIOCondition condition, gpointer user_data) +{ + VuGpu *g =3D user_data; + + if (!vg_recv_msg(g, VHOST_USER_GPU_DMABUF_UPDATE, 0, NULL)) { + return G_SOURCE_CONTINUE; + } + + /* resume */ + g->wait_ok =3D 0; + vg_handle_ctrl(&g->dev.parent, 0); + + return G_SOURCE_REMOVE; +} + +void +vg_wait_ok(VuGpu *g) +{ + assert(g->wait_ok =3D=3D 0); + g->wait_ok =3D g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, + source_wait_cb, g); +} + +static int +vg_sock_fd_write(int sock, const void *buf, ssize_t buflen, int fd) +{ + ssize_t ret; + struct msghdr msg; + struct iovec iov; + union { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(sizeof(int))]; + } cmsgu; + struct cmsghdr *cmsg; + + iov.iov_base =3D (void *)buf; + iov.iov_len =3D buflen; + + msg.msg_name =3D NULL; + msg.msg_namelen =3D 0; + msg.msg_iov =3D &iov; + msg.msg_iovlen =3D 1; + + if (fd !=3D -1) { + msg.msg_control =3D cmsgu.control; + msg.msg_controllen =3D sizeof(cmsgu.control); + + cmsg =3D CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len =3D CMSG_LEN(sizeof(int)); + cmsg->cmsg_level =3D SOL_SOCKET; + cmsg->cmsg_type =3D SCM_RIGHTS; + + *((int *)CMSG_DATA(cmsg)) =3D fd; + } else { + msg.msg_control =3D NULL; + msg.msg_controllen =3D 0; + } + + do { + ret =3D sendmsg(sock, &msg, 0); + } while (ret =3D=3D -1 && (errno =3D=3D EINTR || errno =3D=3D EAGAIN)); + + g_warn_if_fail(ret =3D=3D buflen); + return ret; +} + +void +vg_send_msg(VuGpu *vg, const VhostUserGpuMsg *msg, int fd) +{ + if (vg_sock_fd_write(vg->sock_fd, msg, + VHOST_USER_GPU_HDR_SIZE + msg->size, fd) < 0) { + vg_sock_fd_close(vg); + } +} + +bool +vg_recv_msg(VuGpu *g, uint32_t expect_req, uint32_t expect_size, + gpointer payload) +{ + uint32_t req, flags, size; + + if (vg_sock_fd_read(g->sock_fd, &req, sizeof(req)) < 0 || + vg_sock_fd_read(g->sock_fd, &flags, sizeof(flags)) < 0 || + vg_sock_fd_read(g->sock_fd, &size, sizeof(size)) < 0) { + goto err; + } + + g_return_val_if_fail(req =3D=3D expect_req, false); + g_return_val_if_fail(flags & VHOST_USER_GPU_MSG_FLAG_REPLY, false); + g_return_val_if_fail(size =3D=3D expect_size, false); + + if (size && vg_sock_fd_read(g->sock_fd, payload, size) !=3D size) { + goto err; + } + + return true; + +err: + vg_sock_fd_close(g); + return false; +} + +static struct virtio_gpu_simple_resource * +virtio_gpu_find_resource(VuGpu *g, uint32_t resource_id) +{ + struct virtio_gpu_simple_resource *res; + + QTAILQ_FOREACH(res, &g->reslist, next) { + if (res->resource_id =3D=3D resource_id) { + return res; + } + } + return NULL; +} + +void +vg_ctrl_response(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd, + struct virtio_gpu_ctrl_hdr *resp, + size_t resp_len) +{ + size_t s; + + if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE) { + resp->flags |=3D VIRTIO_GPU_FLAG_FENCE; + resp->fence_id =3D cmd->cmd_hdr.fence_id; + resp->ctx_id =3D cmd->cmd_hdr.ctx_id; + } + virtio_gpu_ctrl_hdr_bswap(resp); + s =3D iov_from_buf(cmd->elem.in_sg, cmd->elem.in_num, 0, resp, resp_le= n); + if (s !=3D resp_len) { + g_critical("%s: response size incorrect %zu vs %zu", + __func__, s, resp_len); + } + vu_queue_push(&g->dev.parent, cmd->vq, &cmd->elem, s); + vu_queue_notify(&g->dev.parent, cmd->vq); + cmd->finished =3D true; +} + +void +vg_ctrl_response_nodata(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd, + enum virtio_gpu_ctrl_type type) +{ + struct virtio_gpu_ctrl_hdr resp =3D { + .type =3D type, + }; + + vg_ctrl_response(g, cmd, &resp, sizeof(resp)); +} + +void +vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resp_display_info dpy_info =3D { {} }; + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_GET_DISPLAY_INFO, + .size =3D 0, + }; + + assert(vg->wait_ok =3D=3D 0); + + vg_send_msg(vg, &msg, -1); + if (!vg_recv_msg(vg, msg.request, sizeof(dpy_info), &dpy_info)) { + return; + } + + vg_ctrl_response(vg, cmd, &dpy_info.hdr, sizeof(dpy_info)); +} + +static void +vg_resource_create_2d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + pixman_format_code_t pformat; + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_create_2d c2d; + + VUGPU_FILL_CMD(c2d); + virtio_gpu_bswap_32(&c2d, sizeof(c2d)); + + if (c2d.resource_id =3D=3D 0) { + g_critical("%s: resource id 0 is not allowed", __func__); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + res =3D virtio_gpu_find_resource(g, c2d.resource_id); + if (res) { + g_critical("%s: resource already exists %d", __func__, c2d.resourc= e_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + res =3D g_new0(struct virtio_gpu_simple_resource, 1); + res->width =3D c2d.width; + res->height =3D c2d.height; + res->format =3D c2d.format; + res->resource_id =3D c2d.resource_id; + + pformat =3D virtio_gpu_get_pixman_format(c2d.format); + if (!pformat) { + g_critical("%s: host couldn't handle guest format %d", + __func__, c2d.format); + g_free(res); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height); + res->image =3D pixman_image_create_bits(pformat, + c2d.width, + c2d.height, + (uint32_t *)res->buffer.mmap, + res->buffer.stride); + if (!res->image) { + g_critical("%s: resource creation failed %d %d %d", + __func__, c2d.resource_id, c2d.width, c2d.height); + g_free(res); + cmd->error =3D VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } + + QTAILQ_INSERT_HEAD(&g->reslist, res, next); +} + +static void +vg_disable_scanout(VuGpu *g, int scanout_id) +{ + struct virtio_gpu_scanout *scanout =3D &g->scanout[scanout_id]; + struct virtio_gpu_simple_resource *res; + + if (scanout->resource_id =3D=3D 0) { + return; + } + + res =3D virtio_gpu_find_resource(g, scanout->resource_id); + if (res) { + res->scanout_bitmask &=3D ~(1 << scanout_id); + } + + scanout->width =3D 0; + scanout->height =3D 0; + + { + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_SCANOUT, + .size =3D sizeof(VhostUserGpuScanout), + .payload.scanout.scanout_id =3D scanout_id, + }; + vg_send_msg(g, &msg, -1); + } +} + +static void +vg_resource_destroy(VuGpu *g, + struct virtio_gpu_simple_resource *res) +{ + int i; + + if (res->scanout_bitmask) { + for (i =3D 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { + if (res->scanout_bitmask & (1 << i)) { + vg_disable_scanout(g, i); + } + } + } + + vugbm_buffer_destroy(&res->buffer); + pixman_image_unref(res->image); + QTAILQ_REMOVE(&g->reslist, res, next); + g_free(res); +} + +static void +vg_resource_unref(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_unref unref; + + VUGPU_FILL_CMD(unref); + virtio_gpu_bswap_32(&unref, sizeof(unref)); + + res =3D virtio_gpu_find_resource(g, unref.resource_id); + if (!res) { + g_critical("%s: illegal resource specified %d", + __func__, unref.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + vg_resource_destroy(g, res); +} + +int +vg_create_mapping_iov(VuGpu *g, + struct virtio_gpu_resource_attach_backing *ab, + struct virtio_gpu_ctrl_command *cmd, + struct iovec **iov) +{ + struct virtio_gpu_mem_entry *ents; + size_t esize, s; + int i; + + if (ab->nr_entries > 16384) { + g_critical("%s: nr_entries is too big (%d > 16384)", + __func__, ab->nr_entries); + return -1; + } + + esize =3D sizeof(*ents) * ab->nr_entries; + ents =3D g_malloc(esize); + s =3D iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, + sizeof(*ab), ents, esize); + if (s !=3D esize) { + g_critical("%s: command data size incorrect %zu vs %zu", + __func__, s, esize); + g_free(ents); + return -1; + } + + *iov =3D g_malloc0(sizeof(struct iovec) * ab->nr_entries); + for (i =3D 0; i < ab->nr_entries; i++) { + uint64_t len =3D ents[i].length; + (*iov)[i].iov_len =3D ents[i].length; + (*iov)[i].iov_base =3D vu_gpa_to_va(&g->dev.parent, &len, ents[i].= addr); + if (!(*iov)[i].iov_base || len !=3D ents[i].length) { + g_critical("%s: resource %d element %d", + __func__, ab->resource_id, i); + g_free(*iov); + g_free(ents); + *iov =3D NULL; + return -1; + } + } + g_free(ents); + return 0; +} + +static void +vg_resource_attach_backing(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_attach_backing ab; + int ret; + + VUGPU_FILL_CMD(ab); + virtio_gpu_bswap_32(&ab, sizeof(ab)); + + res =3D virtio_gpu_find_resource(g, ab.resource_id); + if (!res) { + g_critical("%s: illegal resource specified %d", + __func__, ab.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + ret =3D vg_create_mapping_iov(g, &ab, cmd, &res->iov); + if (ret !=3D 0) { + cmd->error =3D VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + + res->iov_cnt =3D ab.nr_entries; +} + +static void +vg_resource_detach_backing(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_detach_backing detach; + + VUGPU_FILL_CMD(detach); + virtio_gpu_bswap_32(&detach, sizeof(detach)); + + res =3D virtio_gpu_find_resource(g, detach.resource_id); + if (!res || !res->iov) { + g_critical("%s: illegal resource specified %d", + __func__, detach.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + g_free(res->iov); + res->iov =3D NULL; + res->iov_cnt =3D 0; +} + +static void +vg_transfer_to_host_2d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + int h; + uint32_t src_offset, dst_offset, stride; + int bpp; + pixman_format_code_t format; + struct virtio_gpu_transfer_to_host_2d t2d; + + VUGPU_FILL_CMD(t2d); + virtio_gpu_t2d_bswap(&t2d); + + res =3D virtio_gpu_find_resource(g, t2d.resource_id); + if (!res || !res->iov) { + g_critical("%s: illegal resource specified %d", + __func__, t2d.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + if (t2d.r.x > res->width || + t2d.r.y > res->height || + t2d.r.width > res->width || + t2d.r.height > res->height || + t2d.r.x + t2d.r.width > res->width || + t2d.r.y + t2d.r.height > res->height) { + g_critical("%s: transfer bounds outside resource" + " bounds for resource %d: %d %d %d %d vs %d %d", + __func__, t2d.resource_id, t2d.r.x, t2d.r.y, + t2d.r.width, t2d.r.height, res->width, res->height); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + format =3D pixman_image_get_format(res->image); + bpp =3D (PIXMAN_FORMAT_BPP(format) + 7) / 8; + stride =3D pixman_image_get_stride(res->image); + + if (t2d.offset || t2d.r.x || t2d.r.y || + t2d.r.width !=3D pixman_image_get_width(res->image)) { + void *img_data =3D pixman_image_get_data(res->image); + for (h =3D 0; h < t2d.r.height; h++) { + src_offset =3D t2d.offset + stride * h; + dst_offset =3D (t2d.r.y + h) * stride + (t2d.r.x * bpp); + + iov_to_buf(res->iov, res->iov_cnt, src_offset, + img_data + + dst_offset, t2d.r.width * bpp); + } + } else { + iov_to_buf(res->iov, res->iov_cnt, 0, + pixman_image_get_data(res->image), + pixman_image_get_stride(res->image) + * pixman_image_get_height(res->image)); + } +} + +static void +vg_set_scanout(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res, *ores; + struct virtio_gpu_scanout *scanout; + struct virtio_gpu_set_scanout ss; + int fd; + + VUGPU_FILL_CMD(ss); + virtio_gpu_bswap_32(&ss, sizeof(ss)); + + if (ss.scanout_id >=3D VIRTIO_GPU_MAX_SCANOUTS) { + g_critical("%s: illegal scanout id specified %d", + __func__, ss.scanout_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; + return; + } + + if (ss.resource_id =3D=3D 0) { + vg_disable_scanout(g, ss.scanout_id); + return; + } + + /* create a surface for this scanout */ + res =3D virtio_gpu_find_resource(g, ss.resource_id); + if (!res) { + g_critical("%s: illegal resource specified %d", + __func__, ss.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + if (ss.r.x > res->width || + ss.r.y > res->height || + ss.r.width > res->width || + ss.r.height > res->height || + ss.r.x + ss.r.width > res->width || + ss.r.y + ss.r.height > res->height) { + g_critical("%s: illegal scanout %d bounds for" + " resource %d, (%d,%d)+%d,%d vs %d %d", + __func__, ss.scanout_id, ss.resource_id, ss.r.x, ss.r.y, + ss.r.width, ss.r.height, res->width, res->height); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + scanout =3D &g->scanout[ss.scanout_id]; + + ores =3D virtio_gpu_find_resource(g, scanout->resource_id); + if (ores) { + ores->scanout_bitmask &=3D ~(1 << ss.scanout_id); + } + + res->scanout_bitmask |=3D (1 << ss.scanout_id); + scanout->resource_id =3D ss.resource_id; + scanout->x =3D ss.r.x; + scanout->y =3D ss.r.y; + scanout->width =3D ss.r.width; + scanout->height =3D ss.r.height; + + struct vugbm_buffer *buffer =3D &res->buffer; + + if (vugbm_buffer_can_get_dmabuf_fd(buffer)) { + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_DMABUF_SCANOUT, + .size =3D sizeof(VhostUserGpuDMABUFScanout), + .payload.dmabuf_scanout =3D (VhostUserGpuDMABUFScanout) { + .scanout_id =3D ss.scanout_id, + .x =3D ss.r.x, + .y =3D ss.r.y, + .width =3D ss.r.width, + .height =3D ss.r.height, + .fd_width =3D buffer->width, + .fd_height =3D buffer->height, + .fd_stride =3D buffer->stride, + .fd_drm_fourcc =3D buffer->format + } + }; + + if (vugbm_buffer_get_dmabuf_fd(buffer, &fd)) { + vg_send_msg(g, &msg, fd); + close(fd); + } + } else { + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_SCANOUT, + .size =3D sizeof(VhostUserGpuScanout), + .payload.scanout =3D (VhostUserGpuScanout) { + .scanout_id =3D ss.scanout_id, + .width =3D scanout->width, + .height =3D scanout->height + } + }; + vg_send_msg(g, &msg, -1); + } +} + +static void +vg_resource_flush(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_flush rf; + pixman_region16_t flush_region; + int i; + + VUGPU_FILL_CMD(rf); + virtio_gpu_bswap_32(&rf, sizeof(rf)); + + res =3D virtio_gpu_find_resource(g, rf.resource_id); + if (!res) { + g_critical("%s: illegal resource specified %d\n", + __func__, rf.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + if (rf.r.x > res->width || + rf.r.y > res->height || + rf.r.width > res->width || + rf.r.height > res->height || + rf.r.x + rf.r.width > res->width || + rf.r.y + rf.r.height > res->height) { + g_critical("%s: flush bounds outside resource" + " bounds for resource %d: %d %d %d %d vs %d %d\n", + __func__, rf.resource_id, rf.r.x, rf.r.y, + rf.r.width, rf.r.height, res->width, res->height); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + pixman_region_init_rect(&flush_region, + rf.r.x, rf.r.y, rf.r.width, rf.r.height); + for (i =3D 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { + struct virtio_gpu_scanout *scanout; + pixman_region16_t region, finalregion; + pixman_box16_t *extents; + + if (!(res->scanout_bitmask & (1 << i))) { + continue; + } + scanout =3D &g->scanout[i]; + + pixman_region_init(&finalregion); + pixman_region_init_rect(®ion, scanout->x, scanout->y, + scanout->width, scanout->height); + + pixman_region_intersect(&finalregion, &flush_region, ®ion); + + extents =3D pixman_region_extents(&finalregion); + size_t width =3D extents->x2 - extents->x1; + size_t height =3D extents->y2 - extents->y1; + + if (vugbm_buffer_can_get_dmabuf_fd(&res->buffer)) { + VhostUserGpuMsg vmsg =3D { + .request =3D VHOST_USER_GPU_DMABUF_UPDATE, + .size =3D sizeof(VhostUserGpuUpdate), + .payload.update =3D (VhostUserGpuUpdate) { + .scanout_id =3D i, + .x =3D extents->x1, + .y =3D extents->y1, + .width =3D width, + .height =3D height, + } + }; + vg_send_msg(g, &vmsg, -1); + vg_wait_ok(g); + } else { + size_t bpp =3D + PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8; + size_t size =3D width * height * bpp; + + void *p =3D g_malloc(VHOST_USER_GPU_HDR_SIZE + + sizeof(VhostUserGpuUpdate) + size); + VhostUserGpuMsg *msg =3D p; + msg->request =3D VHOST_USER_GPU_UPDATE; + msg->size =3D sizeof(VhostUserGpuUpdate) + size; + msg->payload.update =3D (VhostUserGpuUpdate) { + .scanout_id =3D i, + .x =3D extents->x1, + .y =3D extents->y1, + .width =3D width, + .height =3D height, + }; + pixman_image_t *i =3D + pixman_image_create_bits(pixman_image_get_format(res->imag= e), + msg->payload.update.width, + msg->payload.update.height, + p + offsetof(VhostUserGpuMsg, + payload.update.data), + width * bpp); + pixman_image_composite(PIXMAN_OP_SRC, + res->image, NULL, i, + extents->x1, extents->y1, + 0, 0, 0, 0, + width, height); + pixman_image_unref(i); + vg_send_msg(g, msg, -1); + g_free(msg); + } + pixman_region_fini(®ion); + pixman_region_fini(&finalregion); + } + pixman_region_fini(&flush_region); +} + +static void +vg_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) +{ + switch (cmd->cmd_hdr.type) { + case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: + vg_get_display_info(vg, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: + vg_resource_create_2d(vg, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_UNREF: + vg_resource_unref(vg, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_FLUSH: + vg_resource_flush(vg, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: + vg_transfer_to_host_2d(vg, cmd); + break; + case VIRTIO_GPU_CMD_SET_SCANOUT: + vg_set_scanout(vg, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: + vg_resource_attach_backing(vg, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: + vg_resource_detach_backing(vg, cmd); + break; + /* case VIRTIO_GPU_CMD_GET_EDID: */ + /* break */ + default: + g_warning("TODO handle ctrl %x\n", cmd->cmd_hdr.type); + cmd->error =3D VIRTIO_GPU_RESP_ERR_UNSPEC; + break; + } + if (!cmd->finished) { + vg_ctrl_response_nodata(vg, cmd, cmd->error ? cmd->error : + VIRTIO_GPU_RESP_OK_NODATA); + } +} + +static void +vg_handle_ctrl(VuDev *dev, int qidx) +{ + VuGpu *vg =3D container_of(dev, VuGpu, dev.parent); + VuVirtq *vq =3D vu_get_queue(dev, qidx); + struct virtio_gpu_ctrl_command *cmd =3D NULL; + size_t len; + + for (;;) { + if (vg->wait_ok !=3D 0) { + return; + } + + cmd =3D vu_queue_pop(dev, vq, sizeof(struct virtio_gpu_ctrl_comman= d)); + if (!cmd) { + break; + } + cmd->vq =3D vq; + cmd->error =3D 0; + cmd->finished =3D false; + + len =3D iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, + 0, &cmd->cmd_hdr, sizeof(cmd->cmd_hdr)); + if (len !=3D sizeof(cmd->cmd_hdr)) { + g_warning("%s: command size incorrect %zu vs %zu\n", + __func__, len, sizeof(cmd->cmd_hdr)); + } + + virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); + g_debug("%d %s\n", cmd->cmd_hdr.type, + vg_cmd_to_string(cmd->cmd_hdr.type)); + + if (vg->virgl) { + vg_virgl_process_cmd(vg, cmd); + } else { + vg_process_cmd(vg, cmd); + } + + if (!cmd->finished) { + QTAILQ_INSERT_TAIL(&vg->fenceq, cmd, next); + vg->inflight++; + } else { + g_free(cmd); + } + } +} + +static void +update_cursor_data_simple(VuGpu *g, uint32_t resource_id, gpointer data) +{ + struct virtio_gpu_simple_resource *res; + + res =3D virtio_gpu_find_resource(g, resource_id); + g_return_if_fail(res !=3D NULL); + g_return_if_fail(pixman_image_get_width(res->image) =3D=3D 64); + g_return_if_fail(pixman_image_get_height(res->image) =3D=3D 64); + g_return_if_fail( + PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) =3D=3D 32); + + memcpy(data, pixman_image_get_data(res->image), 64 * 64 * sizeof(uint3= 2_t)); +} + +static void +vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor) +{ + bool move =3D cursor->hdr.type !=3D VIRTIO_GPU_CMD_MOVE_CURSOR; + + g_debug("%s move:%d\n", G_STRFUNC, move); + + if (move) { + VhostUserGpuMsg msg =3D { + .request =3D cursor->resource_id ? + VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE, + .size =3D sizeof(VhostUserGpuCursorPos), + .payload.cursor_pos =3D { + .scanout_id =3D cursor->pos.scanout_id, + .x =3D cursor->pos.x, + .y =3D cursor->pos.y, + } + }; + vg_send_msg(g, &msg, -1); + } else { + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_CURSOR_UPDATE, + .size =3D sizeof(VhostUserGpuCursorUpdate), + .payload.cursor_update =3D { + .pos =3D { + .scanout_id =3D cursor->pos.scanout_id, + .x =3D cursor->pos.x, + .y =3D cursor->pos.y, + }, + .hot_x =3D cursor->hot_x, + .hot_y =3D cursor->hot_y, + } + }; + if (g->virgl) { + vg_virgl_update_cursor_data(g, cursor->resource_id, + msg.payload.cursor_update.data); + } else { + update_cursor_data_simple(g, cursor->resource_id, + msg.payload.cursor_update.data); + } + vg_send_msg(g, &msg, -1); + } +} + +static void +vg_handle_cursor(VuDev *dev, int qidx) +{ + VuGpu *g =3D container_of(dev, VuGpu, dev.parent); + VuVirtq *vq =3D vu_get_queue(dev, qidx); + VuVirtqElement *elem; + size_t len; + struct virtio_gpu_update_cursor cursor; + + for (;;) { + elem =3D vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); + if (!elem) { + break; + } + g_debug("cursor out:%d in:%d\n", elem->out_num, elem->in_num); + + len =3D iov_to_buf(elem->out_sg, elem->out_num, + 0, &cursor, sizeof(cursor)); + if (len !=3D sizeof(cursor)) { + g_warning("%s: cursor size incorrect %zu vs %zu\n", + __func__, len, sizeof(cursor)); + } else { + virtio_gpu_bswap_32(&cursor, sizeof(cursor)); + vg_process_cursor_cmd(g, &cursor); + } + vu_queue_push(dev, vq, elem, 0); + vu_queue_notify(dev, vq); + g_free(elem); + } +} + +static void +vg_panic(VuDev *dev, const char *msg) +{ + g_critical("%s\n", msg); + exit(1); +} + +static void +vg_queue_set_started(VuDev *dev, int qidx, bool started) +{ + VuVirtq *vq =3D vu_get_queue(dev, qidx); + + g_debug("queue started %d:%d\n", qidx, started); + + switch (qidx) { + case 0: + vu_set_queue_handler(dev, vq, started ? vg_handle_ctrl : NULL); + break; + case 1: + vu_set_queue_handler(dev, vq, started ? vg_handle_cursor : NULL); + break; + default: + break; + } +} + +static void +set_gpu_protocol_features(VuGpu *g) +{ + uint64_t u64; + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_GET_PROTOCOL_FEATURES + }; + + assert(g->wait_ok =3D=3D 0); + vg_send_msg(g, &msg, -1); + if (!vg_recv_msg(g, msg.request, sizeof(u64), &u64)) { + return; + } + + msg =3D (VhostUserGpuMsg) { + .request =3D VHOST_USER_GPU_SET_PROTOCOL_FEATURES, + .size =3D sizeof(uint64_t), + .payload.u64 =3D 0 + }; + vg_send_msg(g, &msg, -1); +} + +static int +vg_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply) +{ + VuGpu *g =3D container_of(dev, VuGpu, dev.parent); + + switch (msg->request) { + case VHOST_USER_GPU_SET_SOCKET: { + g_return_val_if_fail(msg->fd_num =3D=3D 1, 1); + g_return_val_if_fail(g->sock_fd =3D=3D -1, 1); + g->sock_fd =3D msg->fds[0]; + set_gpu_protocol_features(g); + return 1; + } + default: + return 0; + } + + return 0; +} + +static uint64_t +vg_get_features(VuDev *dev) +{ + uint64_t features =3D 0; + + if (opt_virgl) { + features |=3D 1 << VIRTIO_GPU_F_VIRGL; + } + + return features; +} + +static void +vg_set_features(VuDev *dev, uint64_t features) +{ + VuGpu *g =3D container_of(dev, VuGpu, dev.parent); + bool virgl =3D features & (1 << VIRTIO_GPU_F_VIRGL); + + if (virgl && !g->virgl_inited) { + if (!vg_virgl_init(g)) { + vg_panic(dev, "Failed to initialize virgl"); + } + g->virgl_inited =3D true; + } + + g->virgl =3D virgl; +} + +static int +vg_get_config(VuDev *dev, uint8_t *config, uint32_t len) +{ + VuGpu *g =3D container_of(dev, VuGpu, dev.parent); + + g_return_val_if_fail(len <=3D sizeof(struct virtio_gpu_config), -1); + + if (opt_virgl) { + g->virtio_config.num_capsets =3D vg_virgl_get_num_capsets(); + } + + memcpy(config, &g->virtio_config, len); + + return 0; +} + +static int +vg_set_config(VuDev *dev, const uint8_t *data, + uint32_t offset, uint32_t size, + uint32_t flags) +{ + VuGpu *g =3D container_of(dev, VuGpu, dev.parent); + struct virtio_gpu_config *config =3D (struct virtio_gpu_config *)data; + + if (config->events_clear) { + g->virtio_config.events_read &=3D ~config->events_clear; + } + + return 0; +} + +static const VuDevIface vuiface =3D { + .set_features =3D vg_set_features, + .get_features =3D vg_get_features, + .queue_set_started =3D vg_queue_set_started, + .process_msg =3D vg_process_msg, + .get_config =3D vg_get_config, + .set_config =3D vg_set_config, +}; + +static void +vg_destroy(VuGpu *g) +{ + struct virtio_gpu_simple_resource *res, *tmp; + + vug_deinit(&g->dev); + + vg_sock_fd_close(g); + + QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { + vg_resource_destroy(g, res); + } + + vugbm_device_destroy(&g->gdev); +} + +static GOptionEntry entries[] =3D { + { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps, + "Print capabilities", NULL }, + { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum, + "Use inherited fd socket", "FDNUM" }, + { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path, + "Use UNIX socket path", "PATH" }, + { "render-node", 'r', 0, G_OPTION_ARG_FILENAME, &opt_render_node, + "Specify DRM render node", "PATH" }, + { "virgl", 'v', 0, G_OPTION_ARG_NONE, &opt_virgl, + "Turn virgl rendering on", NULL }, + { NULL, } +}; + +int +main(int argc, char *argv[]) +{ + GOptionContext *context; + GError *error =3D NULL; + GMainLoop *loop =3D NULL; + int fd; + VuGpu g =3D { .sock_fd =3D -1, .drm_rnode_fd =3D -1 }; + + QTAILQ_INIT(&g.reslist); + QTAILQ_INIT(&g.fenceq); + + context =3D g_option_context_new("QEMU vhost-user-gpu"); + g_option_context_add_main_entries(context, entries, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_printerr("Option parsing failed: %s\n", error->message); + exit(EXIT_FAILURE); + } + g_option_context_free(context); + + if (opt_print_caps) { + g_print("{\n"); + g_print(" \"type\": \"gpu\",\n"); + g_print(" \"features\": [\n"); + g_print(" \"render-node\",\n"); + g_print(" \"virgl\"\n"); + g_print(" ]\n"); + g_print("}\n"); + exit(EXIT_SUCCESS); + } + + g.drm_rnode_fd =3D qemu_drm_rendernode_open(opt_render_node); + if (opt_render_node && g.drm_rnode_fd =3D=3D -1) { + g_printerr("Failed to open DRM rendernode.\n"); + exit(EXIT_FAILURE); + } + + if (g.drm_rnode_fd >=3D 0) { + if (!vugbm_device_init(&g.gdev, g.drm_rnode_fd)) { + g_warning("Failed to init DRM device, using fallback path"); + } + } + + if ((!!opt_socket_path + (opt_fdnum !=3D -1)) !=3D 1) { + g_printerr("Please specify either --fd or --socket-path\n"); + exit(EXIT_FAILURE); + } + + if (opt_socket_path) { + int lsock =3D unix_listen(opt_socket_path, &error_fatal); + fd =3D accept(lsock, NULL, NULL); + close(lsock); + } else { + fd =3D opt_fdnum; + } + if (fd =3D=3D -1) { + g_printerr("Invalid socket"); + exit(EXIT_FAILURE); + } + + vug_init(&g.dev, fd, vg_panic, &vuiface); + + loop =3D g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + vg_destroy(&g); + if (g.drm_rnode_fd >=3D 0) { + close(g.drm_rnode_fd); + } + + return 0; +} diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c new file mode 100644 index 0000000000..43413e29df --- /dev/null +++ b/contrib/vhost-user-gpu/virgl.c @@ -0,0 +1,579 @@ +/* + * Virtio vhost-user GPU Device + * + * Copyright Red Hat, Inc. 2013-2018 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include +#include "virgl.h" + +void +vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id, + gpointer data) +{ + uint32_t width, height; + uint32_t *cursor; + + cursor =3D virgl_renderer_get_cursor_data(resource_id, &width, &height= ); + g_return_if_fail(cursor !=3D NULL); + g_return_if_fail(width =3D=3D 64); + g_return_if_fail(height =3D=3D 64); + + memcpy(data, cursor, 64 * 64 * sizeof(uint32_t)); + free(cursor); +} + +static void +virgl_cmd_context_create(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_create cc; + + VUGPU_FILL_CMD(cc); + + virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen, + cc.debug_name); +} + +static void +virgl_cmd_context_destroy(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_destroy cd; + + VUGPU_FILL_CMD(cd); + + virgl_renderer_context_destroy(cd.hdr.ctx_id); +} + +static void +virgl_cmd_create_resource_2d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_create_2d c2d; + struct virgl_renderer_resource_create_args args; + + VUGPU_FILL_CMD(c2d); + + args.handle =3D c2d.resource_id; + args.target =3D 2; + args.format =3D c2d.format; + args.bind =3D (1 << 1); + args.width =3D c2d.width; + args.height =3D c2d.height; + args.depth =3D 1; + args.array_size =3D 1; + args.last_level =3D 0; + args.nr_samples =3D 0; + args.flags =3D VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP; + virgl_renderer_resource_create(&args, NULL, 0); +} + +static void +virgl_cmd_create_resource_3d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_create_3d c3d; + struct virgl_renderer_resource_create_args args; + + VUGPU_FILL_CMD(c3d); + + args.handle =3D c3d.resource_id; + args.target =3D c3d.target; + args.format =3D c3d.format; + args.bind =3D c3d.bind; + args.width =3D c3d.width; + args.height =3D c3d.height; + args.depth =3D c3d.depth; + args.array_size =3D c3d.array_size; + args.last_level =3D c3d.last_level; + args.nr_samples =3D c3d.nr_samples; + args.flags =3D c3d.flags; + virgl_renderer_resource_create(&args, NULL, 0); +} + +static void +virgl_cmd_resource_unref(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_unref unref; + + VUGPU_FILL_CMD(unref); + + virgl_renderer_resource_unref(unref.resource_id); +} + +/* Not yet(?) defined in standard-headers, remove when possible */ +#ifndef VIRTIO_GPU_CAPSET_VIRGL2 +#define VIRTIO_GPU_CAPSET_VIRGL2 2 +#endif + +static void +virgl_cmd_get_capset_info(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_get_capset_info info; + struct virtio_gpu_resp_capset_info resp; + + VUGPU_FILL_CMD(info); + + if (info.capset_index =3D=3D 0) { + resp.capset_id =3D VIRTIO_GPU_CAPSET_VIRGL; + virgl_renderer_get_cap_set(resp.capset_id, + &resp.capset_max_version, + &resp.capset_max_size); + } else if (info.capset_index =3D=3D 1) { + resp.capset_id =3D VIRTIO_GPU_CAPSET_VIRGL2; + virgl_renderer_get_cap_set(resp.capset_id, + &resp.capset_max_version, + &resp.capset_max_size); + } else { + resp.capset_max_version =3D 0; + resp.capset_max_size =3D 0; + } + resp.hdr.type =3D VIRTIO_GPU_RESP_OK_CAPSET_INFO; + vg_ctrl_response(g, cmd, &resp.hdr, sizeof(resp)); +} + +uint32_t +vg_virgl_get_num_capsets(void) +{ + uint32_t capset2_max_ver, capset2_max_size; + virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VIRGL2, + &capset2_max_ver, + &capset2_max_size); + + return capset2_max_ver ? 2 : 1; +} + +static void +virgl_cmd_get_capset(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_get_capset gc; + struct virtio_gpu_resp_capset *resp; + uint32_t max_ver, max_size; + + VUGPU_FILL_CMD(gc); + + virgl_renderer_get_cap_set(gc.capset_id, &max_ver, + &max_size); + resp =3D g_malloc0(sizeof(*resp) + max_size); + + resp->hdr.type =3D VIRTIO_GPU_RESP_OK_CAPSET; + virgl_renderer_fill_caps(gc.capset_id, + gc.capset_version, + (void *)resp->capset_data); + vg_ctrl_response(g, cmd, &resp->hdr, sizeof(*resp) + max_size); + g_free(resp); +} + +static void +virgl_cmd_submit_3d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_cmd_submit cs; + void *buf; + size_t s; + + VUGPU_FILL_CMD(cs); + + buf =3D g_malloc(cs.size); + s =3D iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, + sizeof(cs), buf, cs.size); + if (s !=3D cs.size) { + g_critical("%s: size mismatch (%zd/%d)", __func__, s, cs.size); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + goto out; + } + + virgl_renderer_submit_cmd(buf, cs.hdr.ctx_id, cs.size / 4); + +out: + g_free(buf); +} + +static void +virgl_cmd_transfer_to_host_2d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_transfer_to_host_2d t2d; + struct virtio_gpu_box box; + + VUGPU_FILL_CMD(t2d); + + box.x =3D t2d.r.x; + box.y =3D t2d.r.y; + box.z =3D 0; + box.w =3D t2d.r.width; + box.h =3D t2d.r.height; + box.d =3D 1; + + virgl_renderer_transfer_write_iov(t2d.resource_id, + 0, + 0, + 0, + 0, + (struct virgl_box *)&box, + t2d.offset, NULL, 0); +} + +static void +virgl_cmd_transfer_to_host_3d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_transfer_host_3d t3d; + + VUGPU_FILL_CMD(t3d); + + virgl_renderer_transfer_write_iov(t3d.resource_id, + t3d.hdr.ctx_id, + t3d.level, + t3d.stride, + t3d.layer_stride, + (struct virgl_box *)&t3d.box, + t3d.offset, NULL, 0); +} + +static void +virgl_cmd_transfer_from_host_3d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_transfer_host_3d tf3d; + + VUGPU_FILL_CMD(tf3d); + + virgl_renderer_transfer_read_iov(tf3d.resource_id, + tf3d.hdr.ctx_id, + tf3d.level, + tf3d.stride, + tf3d.layer_stride, + (struct virgl_box *)&tf3d.box, + tf3d.offset, NULL, 0); +} + +static void +virgl_resource_attach_backing(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_attach_backing att_rb; + struct iovec *res_iovs; + int ret; + + VUGPU_FILL_CMD(att_rb); + + ret =3D vg_create_mapping_iov(g, &att_rb, cmd, &res_iovs); + if (ret !=3D 0) { + cmd->error =3D VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + + virgl_renderer_resource_attach_iov(att_rb.resource_id, + res_iovs, att_rb.nr_entries); +} + +static void +virgl_resource_detach_backing(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_detach_backing detach_rb; + struct iovec *res_iovs =3D NULL; + int num_iovs =3D 0; + + VUGPU_FILL_CMD(detach_rb); + + virgl_renderer_resource_detach_iov(detach_rb.resource_id, + &res_iovs, + &num_iovs); + if (res_iovs =3D=3D NULL || num_iovs =3D=3D 0) { + return; + } + g_free(res_iovs); +} + +static void +virgl_cmd_set_scanout(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_set_scanout ss; + struct virgl_renderer_resource_info info; + int ret; + + VUGPU_FILL_CMD(ss); + + if (ss.scanout_id >=3D VIRTIO_GPU_MAX_SCANOUTS) { + g_critical("%s: illegal scanout id specified %d", + __func__, ss.scanout_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; + return; + } + + memset(&info, 0, sizeof(info)); + + if (ss.resource_id && ss.r.width && ss.r.height) { + ret =3D virgl_renderer_resource_get_info(ss.resource_id, &info); + if (ret =3D=3D -1) { + g_critical("%s: illegal resource specified %d\n", + __func__, ss.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + int fd =3D -1; + if (virgl_renderer_get_fd_for_texture(info.tex_id, &fd) < 0) { + g_critical("%s: failed to get fd for texture\n", __func__); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + assert(fd >=3D 0); + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_DMABUF_SCANOUT, + .size =3D sizeof(VhostUserGpuDMABUFScanout), + .payload.dmabuf_scanout.scanout_id =3D ss.scanout_id, + .payload.dmabuf_scanout.x =3D ss.r.x, + .payload.dmabuf_scanout.y =3D ss.r.y, + .payload.dmabuf_scanout.width =3D ss.r.width, + .payload.dmabuf_scanout.height =3D ss.r.height, + .payload.dmabuf_scanout.fd_width =3D info.width, + .payload.dmabuf_scanout.fd_height =3D info.height, + .payload.dmabuf_scanout.fd_stride =3D info.stride, + .payload.dmabuf_scanout.fd_flags =3D info.flags, + .payload.dmabuf_scanout.fd_drm_fourcc =3D info.drm_fourcc + }; + vg_send_msg(g, &msg, fd); + close(fd); + } else { + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_DMABUF_SCANOUT, + .size =3D sizeof(VhostUserGpuDMABUFScanout), + .payload.dmabuf_scanout.scanout_id =3D ss.scanout_id, + }; + g_debug("disable scanout"); + vg_send_msg(g, &msg, -1); + } + g->scanout[ss.scanout_id].resource_id =3D ss.resource_id; +} + +static void +virgl_cmd_resource_flush(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_flush rf; + int i; + + VUGPU_FILL_CMD(rf); + + if (!rf.resource_id) { + g_debug("bad resource id for flush..?"); + return; + } + for (i =3D 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { + if (g->scanout[i].resource_id !=3D rf.resource_id) { + continue; + } + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_DMABUF_UPDATE, + .size =3D sizeof(VhostUserGpuUpdate), + .payload.update.scanout_id =3D i, + .payload.update.x =3D rf.r.x, + .payload.update.y =3D rf.r.y, + .payload.update.width =3D rf.r.width, + .payload.update.height =3D rf.r.height + }; + vg_send_msg(g, &msg, -1); + vg_wait_ok(g); + } +} + +static void +virgl_cmd_ctx_attach_resource(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_resource att_res; + + VUGPU_FILL_CMD(att_res); + + virgl_renderer_ctx_attach_resource(att_res.hdr.ctx_id, att_res.resourc= e_id); +} + +static void +virgl_cmd_ctx_detach_resource(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_resource det_res; + + VUGPU_FILL_CMD(det_res); + + virgl_renderer_ctx_detach_resource(det_res.hdr.ctx_id, det_res.resourc= e_id); +} + +void vg_virgl_process_cmd(VuGpu *g, struct virtio_gpu_ctrl_command *cmd) +{ + virgl_renderer_force_ctx_0(); + switch (cmd->cmd_hdr.type) { + case VIRTIO_GPU_CMD_CTX_CREATE: + virgl_cmd_context_create(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_DESTROY: + virgl_cmd_context_destroy(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: + virgl_cmd_create_resource_2d(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D: + virgl_cmd_create_resource_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_SUBMIT_3D: + virgl_cmd_submit_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: + virgl_cmd_transfer_to_host_2d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D: + virgl_cmd_transfer_to_host_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: + virgl_cmd_transfer_from_host_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: + virgl_resource_attach_backing(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: + virgl_resource_detach_backing(g, cmd); + break; + case VIRTIO_GPU_CMD_SET_SCANOUT: + virgl_cmd_set_scanout(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_FLUSH: + virgl_cmd_resource_flush(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_UNREF: + virgl_cmd_resource_unref(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: + /* TODO add security */ + virgl_cmd_ctx_attach_resource(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE: + /* TODO add security */ + virgl_cmd_ctx_detach_resource(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_CAPSET_INFO: + virgl_cmd_get_capset_info(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_CAPSET: + virgl_cmd_get_capset(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: + vg_get_display_info(g, cmd); + break; + default: + g_debug("TODO handle ctrl %x\n", cmd->cmd_hdr.type); + cmd->error =3D VIRTIO_GPU_RESP_ERR_UNSPEC; + break; + } + + if (cmd->finished) { + return; + } + + if (cmd->error) { + g_warning("%s: ctrl 0x%x, error 0x%x\n", __func__, + cmd->cmd_hdr.type, cmd->error); + vg_ctrl_response_nodata(g, cmd, cmd->error); + return; + } + + if (!(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) { + vg_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA); + return; + } + + g_debug("Creating fence id:%" PRId64 " type:%d", + cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type); + virgl_renderer_create_fence(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type); +} + +static void +virgl_write_fence(void *opaque, uint32_t fence) +{ + VuGpu *g =3D opaque; + struct virtio_gpu_ctrl_command *cmd, *tmp; + + QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) { + /* + * the guest can end up emitting fences out of order + * so we should check all fenced cmds not just the first one. + */ + if (cmd->cmd_hdr.fence_id > fence) { + continue; + } + g_debug("FENCE %" PRIu64, cmd->cmd_hdr.fence_id); + vg_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA); + QTAILQ_REMOVE(&g->fenceq, cmd, next); + g_free(cmd); + g->inflight--; + } +} + +#if defined(VIRGL_RENDERER_CALLBACKS_VERSION) && \ + VIRGL_RENDERER_CALLBACKS_VERSION >=3D 2 +static int +virgl_get_drm_fd(void *opaque) +{ + VuGpu *g =3D opaque; + + return g->drm_rnode_fd; +} +#endif + +static struct virgl_renderer_callbacks virgl_cbs =3D { +#if defined(VIRGL_RENDERER_CALLBACKS_VERSION) && \ + VIRGL_RENDERER_CALLBACKS_VERSION >=3D 2 + .get_drm_fd =3D virgl_get_drm_fd, + .version =3D 2, +#else + .version =3D 1, +#endif + .write_fence =3D virgl_write_fence, +}; + +static void +vg_virgl_poll(VuDev *dev, int condition, void *data) +{ + virgl_renderer_poll(); +} + +bool +vg_virgl_init(VuGpu *g) +{ + int ret; + + if (g->drm_rnode_fd && virgl_cbs.version =3D=3D 1) { + g_warning("virgl will use the default rendernode"); + } + + ret =3D virgl_renderer_init(g, + VIRGL_RENDERER_USE_EGL | + VIRGL_RENDERER_THREAD_SYNC, + &virgl_cbs); + if (ret !=3D 0) { + return false; + } + + ret =3D virgl_renderer_get_poll_fd(); + if (ret !=3D -1) { + g->renderer_source =3D + vug_source_new(&g->dev, ret, G_IO_IN, vg_virgl_poll, g); + } + + return true; +} diff --git a/contrib/vhost-user-gpu/vugbm.c b/contrib/vhost-user-gpu/vugbm.c new file mode 100644 index 0000000000..d3bb82ff0e --- /dev/null +++ b/contrib/vhost-user-gpu/vugbm.c @@ -0,0 +1,328 @@ +/* + * Virtio vhost-user GPU Device + * + * DRM helpers + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "vugbm.h" + +static bool +mem_alloc_bo(struct vugbm_buffer *buf) +{ + buf->mmap =3D g_malloc(buf->width * buf->height * 4); + buf->stride =3D buf->width * 4; + return true; +} + +static void +mem_free_bo(struct vugbm_buffer *buf) +{ + g_free(buf->mmap); +} + +static bool +mem_map_bo(struct vugbm_buffer *buf) +{ + return buf->mmap !=3D NULL; +} + +static void +mem_unmap_bo(struct vugbm_buffer *buf) +{ +} + +static void +mem_device_destroy(struct vugbm_device *dev) +{ +} + +#ifdef CONFIG_MEMFD +struct udmabuf_create { + uint32_t memfd; + uint32_t flags; + uint64_t offset; + uint64_t size; +}; + +#define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create) + +static size_t +udmabuf_get_size(struct vugbm_buffer *buf) +{ + return ROUND_UP(buf->width * buf->height * 4, getpagesize()); +} + +static bool +udmabuf_alloc_bo(struct vugbm_buffer *buf) +{ + int ret; + + buf->memfd =3D memfd_create("udmabuf-bo", MFD_ALLOW_SEALING); + if (buf->memfd < 0) { + return false; + } + + ret =3D ftruncate(buf->memfd, udmabuf_get_size(buf)); + if (ret < 0) { + close(buf->memfd); + return false; + } + + ret =3D fcntl(buf->memfd, F_ADD_SEALS, F_SEAL_SHRINK); + if (ret < 0) { + close(buf->memfd); + return false; + } + + buf->stride =3D buf->width * 4; + + return true; +} + +static void +udmabuf_free_bo(struct vugbm_buffer *buf) +{ + close(buf->memfd); +} + +static bool +udmabuf_map_bo(struct vugbm_buffer *buf) +{ + buf->mmap =3D mmap(NULL, udmabuf_get_size(buf), + PROT_READ | PROT_WRITE, MAP_SHARED, buf->memfd, 0); + if (buf->mmap =3D=3D MAP_FAILED) { + return false; + } + + return true; +} + +static bool +udmabuf_get_fd(struct vugbm_buffer *buf, int *fd) +{ + struct udmabuf_create create =3D { + .memfd =3D buf->memfd, + .offset =3D 0, + .size =3D udmabuf_get_size(buf), + }; + + *fd =3D ioctl(buf->dev->fd, UDMABUF_CREATE, &create); + + return *fd >=3D 0; +} + +static void +udmabuf_unmap_bo(struct vugbm_buffer *buf) +{ + munmap(buf->mmap, udmabuf_get_size(buf)); +} + +static void +udmabuf_device_destroy(struct vugbm_device *dev) +{ + close(dev->fd); +} +#endif + +#ifdef CONFIG_GBM +static bool +alloc_bo(struct vugbm_buffer *buf) +{ + struct gbm_device *dev =3D buf->dev->dev; + + assert(!buf->bo); + + buf->bo =3D gbm_bo_create(dev, buf->width, buf->height, + buf->format, + GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR); + + if (buf->bo) { + buf->stride =3D gbm_bo_get_stride(buf->bo); + return true; + } + + return false; +} + +static void +free_bo(struct vugbm_buffer *buf) +{ + gbm_bo_destroy(buf->bo); +} + +static bool +map_bo(struct vugbm_buffer *buf) +{ + uint32_t stride; + + buf->mmap =3D gbm_bo_map(buf->bo, 0, 0, buf->width, buf->height, + GBM_BO_TRANSFER_READ_WRITE, &stride, + &buf->mmap_data); + + assert(stride =3D=3D buf->stride); + + return buf->mmap !=3D NULL; +} + +static void +unmap_bo(struct vugbm_buffer *buf) +{ + gbm_bo_unmap(buf->bo, buf->mmap_data); +} + +static bool +get_fd(struct vugbm_buffer *buf, int *fd) +{ + *fd =3D gbm_bo_get_fd(buf->bo); + + return *fd >=3D 0; +} + +static void +device_destroy(struct vugbm_device *dev) +{ + gbm_device_destroy(dev->dev); +} +#endif + +void +vugbm_device_destroy(struct vugbm_device *dev) +{ + if (!dev->inited) { + return; + } + + dev->device_destroy(dev); +} + +bool +vugbm_device_init(struct vugbm_device *dev, int fd) +{ + dev->fd =3D fd; + +#ifdef CONFIG_GBM + dev->dev =3D gbm_create_device(fd); +#endif + + if (0) { + /* nothing */ + } +#ifdef CONFIG_GBM + else if (dev->dev !=3D NULL) { + dev->alloc_bo =3D alloc_bo; + dev->free_bo =3D free_bo; + dev->get_fd =3D get_fd; + dev->map_bo =3D map_bo; + dev->unmap_bo =3D unmap_bo; + dev->device_destroy =3D device_destroy; + } +#endif +#ifdef CONFIG_MEMFD + else if (g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) { + dev->fd =3D open("/dev/udmabuf", O_RDWR); + if (dev->fd < 0) { + return false; + } + g_debug("Using experimental udmabuf backend"); + dev->alloc_bo =3D udmabuf_alloc_bo; + dev->free_bo =3D udmabuf_free_bo; + dev->get_fd =3D udmabuf_get_fd; + dev->map_bo =3D udmabuf_map_bo; + dev->unmap_bo =3D udmabuf_unmap_bo; + dev->device_destroy =3D udmabuf_device_destroy; + } +#endif + else { + g_debug("Using mem fallback"); + dev->alloc_bo =3D mem_alloc_bo; + dev->free_bo =3D mem_free_bo; + dev->map_bo =3D mem_map_bo; + dev->unmap_bo =3D mem_unmap_bo; + dev->device_destroy =3D mem_device_destroy; + return false; + } + + dev->inited =3D true; + return true; +} + +static bool +vugbm_buffer_map(struct vugbm_buffer *buf) +{ + struct vugbm_device *dev =3D buf->dev; + + return dev->map_bo(buf); +} + +static void +vugbm_buffer_unmap(struct vugbm_buffer *buf) +{ + struct vugbm_device *dev =3D buf->dev; + + dev->unmap_bo(buf); +} + +bool +vugbm_buffer_can_get_dmabuf_fd(struct vugbm_buffer *buffer) +{ + if (!buffer->dev->get_fd) { + return false; + } + + return true; +} + +bool +vugbm_buffer_get_dmabuf_fd(struct vugbm_buffer *buffer, int *fd) +{ + if (!vugbm_buffer_can_get_dmabuf_fd(buffer) || + !buffer->dev->get_fd(buffer, fd)) { + g_warning("Failed to get dmabuf"); + return false; + } + + if (*fd < 0) { + g_warning("error: dmabuf_fd < 0"); + return false; + } + + return true; +} + +bool +vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev, + uint32_t width, uint32_t height) +{ + buffer->dev =3D dev; + buffer->width =3D width; + buffer->height =3D height; + buffer->format =3D GBM_FORMAT_XRGB8888; + buffer->stride =3D 0; /* modified during alloc */ + if (!dev->alloc_bo(buffer)) { + g_warning("alloc_bo failed"); + return false; + } + + if (!vugbm_buffer_map(buffer)) { + g_warning("map_bo failed"); + goto err; + } + + return true; + +err: + dev->free_bo(buffer); + return false; +} + +void +vugbm_buffer_destroy(struct vugbm_buffer *buffer) +{ + struct vugbm_device *dev =3D buffer->dev; + + vugbm_buffer_unmap(buffer); + dev->free_bo(buffer); +} diff --git a/MAINTAINERS b/MAINTAINERS index e04ae7817c..093b9cb49b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1681,6 +1681,8 @@ M: Marc-Andr=C3=A9 Lureau M: Gerd Hoffmann S: Maintained F: docs/interop/vhost-user-gpu.rst +F: contrib/vhost-user-gpu +F: hw/display/vhost-user-* =20 Cirrus VGA M: Gerd Hoffmann diff --git a/Makefile b/Makefile index e02b88bcb1..68c2e9f0c6 100644 --- a/Makefile +++ b/Makefile @@ -314,8 +314,20 @@ $(call set-vpath, $(SRC_PATH)) =20 LIBS+=3D-lz $(LIBS_TOOLS) =20 +vhost-user-json-y =3D +HELPERS-y =3D + HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) =3D qemu-bridge-hel= per$(EXESUF) =20 +ifdef CONFIG_LINUX +ifdef CONFIG_VIRGL +ifdef CONFIG_GBM +HELPERS-y +=3D vhost-user-gpu$(EXESUF) +vhost-user-json-y +=3D contrib/vhost-user-gpu/50-qemu-gpu.json +endif +endif +endif + ifdef BUILD_DOCS DOCS=3Dqemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 DOCS+=3Ddocs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/= interop/qemu-qmp-ref.7 @@ -409,6 +421,7 @@ dummy :=3D $(call unnest-vars,, \ vhost-user-scsi-obj-y \ vhost-user-blk-obj-y \ vhost-user-input-obj-y \ + vhost-user-gpu-obj-y \ qga-vss-dll-obj-y \ block-obj-y \ block-obj-m \ @@ -426,7 +439,7 @@ dummy :=3D $(call unnest-vars,, \ =20 include $(SRC_PATH)/tests/Makefile.include =20 -all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-= all modules +all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-= all modules $(vhost-user-json-y) =20 qemu-version.h: FORCE $(call quiet-command, \ @@ -619,6 +632,9 @@ rdmacm-mux$(EXESUF): LIBS +=3D "-libumad" rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) $(call LINK, $^) =20 +vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) li= bqemuutil.a libqemustub.a + $(call LINK, $^) + ifdef CONFIG_VHOST_USER_INPUT ifdef CONFIG_LINUX vhost-user-input$(EXESUF): $(vhost-user-input-obj-y) libvhost-user.a libqe= muutil.a @@ -827,6 +843,12 @@ endif ifneq ($(HELPERS-y),) $(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) endif +ifneq ($(vhost-user-json-y),) + $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/vhost-user/" + for x in $(vhost-user-json-y); do \ + $(INSTALL_DATA) $$x "$(DESTDIR)$(qemu_datadir)/vhost-user/"; \ + done +endif ifdef CONFIG_TRACE_SYSTEMTAP $(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir) endif diff --git a/Makefile.objs b/Makefile.objs index d7491413c1..be7ad2ffe9 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -123,6 +123,7 @@ vhost-user-scsi-obj-y =3D contrib/vhost-user-scsi/ vhost-user-blk-obj-y =3D contrib/vhost-user-blk/ rdmacm-mux-obj-y =3D contrib/rdmacm-mux/ vhost-user-input-obj-y =3D contrib/vhost-user-input/ +vhost-user-gpu-obj-y =3D contrib/vhost-user-gpu/ =20 ###################################################################### trace-events-subdirs =3D diff --git a/contrib/vhost-user-gpu/50-qemu-gpu.json.in b/contrib/vhost-use= r-gpu/50-qemu-gpu.json.in new file mode 100644 index 0000000000..658b545864 --- /dev/null +++ b/contrib/vhost-user-gpu/50-qemu-gpu.json.in @@ -0,0 +1,5 @@ +{ + "description": "QEMU vhost-user-gpu", + "type": "gpu", + "binary": "@libexecdir@/vhost-user-gpu", +} diff --git a/contrib/vhost-user-gpu/Makefile.objs b/contrib/vhost-user-gpu/= Makefile.objs new file mode 100644 index 0000000000..6170c919e4 --- /dev/null +++ b/contrib/vhost-user-gpu/Makefile.objs @@ -0,0 +1,10 @@ +vhost-user-gpu-obj-y =3D main.o virgl.o vugbm.o + +main.o-cflags :=3D $(PIXMAN_CFLAGS) $(GBM_CFLAGS) +main.o-libs :=3D $(PIXMAN_LIBS) + +virgl.o-cflags :=3D $(VIRGL_CFLAGS) $(GBM_CFLAGS) +virgl.o-libs :=3D $(VIRGL_LIBS) + +vugbm.o-cflags :=3D $(GBM_CFLAGS) +vugbm.o-libs :=3D $(GBM_LIBS) diff --git a/rules.mak b/rules.mak index df45bcffb4..967295dd2b 100644 --- a/rules.mak +++ b/rules.mak @@ -144,7 +144,7 @@ cc-option =3D $(if $(shell $(CC) $1 $2 -S -o /dev/null = -xc /dev/null \ cc-c-option =3D $(if $(shell $(CC) $1 $2 -c -o /dev/null -xc /dev/null \ >/dev/null 2>&1 && echo OK), $2, $3) =20 -VPATH_SUFFIXES =3D %.c %.h %.S %.cc %.cpp %.m %.mak %.texi %.sh %.rc Kconf= ig% +VPATH_SUFFIXES =3D %.c %.h %.S %.cc %.cpp %.m %.mak %.texi %.sh %.rc Kconf= ig% %.json.in set-vpath =3D $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(P= ATTERN) $1))) =20 # install-prog list, dir @@ -392,3 +392,10 @@ TEXI2MAN =3D $(call quiet-command, \ $(call TEXI2MAN) %.8: $(call TEXI2MAN) + +GEN_SUBST =3D $(call quiet-command, \ + sed -e "s!@libexecdir@!$(libexecdir)!g" < $< > $@, \ + "GEN","$@") + +%.json: %.json.in + $(call GEN_SUBST) --=20 2.22.0.rc1.1.g079e7d2849.dirty From nobody Fri Apr 19 02:12:17 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1558704148; cv=none; d=zoho.com; s=zohoarc; b=SHbAIOtlbaZ8sXaO83qiCBA01/eWfx67JL4rdWgOFnEVKZGm43iMUS6opZ412tMyptmII3nviY+qXK02WCrEvMIkKFv8eYDO7UbbUJV9MoI89lblrFnKqOcTmYfHsHN25uA2IwcunuwesI/SJKdKDbnbhT5IueLr+zzwAaMS5mc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1558704148; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=VCAtceY/SslT1MNQ6D6R1zd3LhA+n1anvK27Pgtzq9E=; b=JBRot6wK4chuNn1FzI+HagCcMYrX1di6rnZW/LRPkG4qIANveTZylnQpeGRGzaSMEDgYbDgfP93zfy1bVKRjtuU4pwbt+a0h+Gf9nidpx/U7V92XsNiqx6jnbGwrKnwh7AzoEzPeDevWCVZu+4Lxb3xFjTAuFuW/sOjkQHfYecA= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1558704148507267.5014684398568; Fri, 24 May 2019 06:22:28 -0700 (PDT) Received: from localhost ([127.0.0.1]:54575 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA9N-0005t9-J1 for importer@patchew.org; Fri, 24 May 2019 09:22:21 -0400 Received: from eggs.gnu.org ([209.51.188.92]:58521) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA18-0007aL-T1 for qemu-devel@nongnu.org; Fri, 24 May 2019 09:13:51 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hU9yQ-0001bG-30 for qemu-devel@nongnu.org; Fri, 24 May 2019 09:11:03 -0400 Received: from mx1.redhat.com ([209.132.183.28]:27858) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hU9yP-0001ax-Tj for qemu-devel@nongnu.org; Fri, 24 May 2019 09:11:02 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3133E59450 for ; Fri, 24 May 2019 13:10:56 +0000 (UTC) Received: from localhost (ovpn-112-23.ams2.redhat.com [10.36.112.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id AC9C07848B; Fri, 24 May 2019 13:10:55 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 24 May 2019 15:09:43 +0200 Message-Id: <20190524130946.31736-7-marcandre.lureau@redhat.com> In-Reply-To: <20190524130946.31736-1-marcandre.lureau@redhat.com> References: <20190524130946.31736-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Fri, 24 May 2019 13:10:56 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v9 6/9] spice-app: fix running when !CONFIG_OPENGL X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Do not set 'gl' parameter, fixes: qemu-system-x86_64: Invalid parameter 'gl' Signed-off-by: Marc-Andr=C3=A9 Lureau --- ui/spice-app.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/spice-app.c b/ui/spice-app.c index 925b27b708..30541b1022 100644 --- a/ui/spice-app.c +++ b/ui/spice-app.c @@ -157,9 +157,10 @@ static void spice_app_display_early_init(DisplayOption= s *opts) qemu_opt_set(qopts, "addr", sock_path, &error_abort); qemu_opt_set(qopts, "image-compression", "off", &error_abort); qemu_opt_set(qopts, "streaming-video", "off", &error_abort); +#ifdef CONFIG_OPENGL qemu_opt_set(qopts, "gl", opts->has_gl ? "on" : "off", &error_abort); display_opengl =3D opts->has_gl; - +#endif be->u.spiceport.data->fqdn =3D g_strdup("org.qemu.monitor.qmp.0"); qemu_chardev_new("org.qemu.monitor.qmp", TYPE_CHARDEV_SPICEPORT, be, NULL, &error_abort); --=20 2.22.0.rc1.1.g079e7d2849.dirty From nobody Fri Apr 19 02:12:17 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1558704371; cv=none; d=zoho.com; s=zohoarc; b=fWoMxNmP1noySdzWUEphI0WgRx0gTrXt79oWeJmL4j8UkQaYeeoIoPwy3waM3UQt4LnHPrCWuz997czYYXZnThh3WvXeWPeRbLHhoZo2tf4CvcFsgHLu27y/Q7k3PVrNLhiM1NGUxr7e8GS36YlTKpl8jNTFy0w47BQr0X8XUs8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1558704371; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=YVEU/mfOyVCDafBxISf382f+2iJuqF6GxXZpk6TXoUQ=; b=TUqE7gaD9hHH1Fpgs1gy0gQXCAn2hoiGlifQ/mpBfqGc6kRdvRDjiXBCCJZtKzIiV4Qg46IGksJMEuTyB68nRsiMfEbFuuAD1W2PdWGZ9D27iKY5B/jXLT41xzZPBhVJ134Ahx92WoQCQXbewig6qKEj8DVyVvc4pmx+BjxNQq0= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1558704371150103.24923344969602; Fri, 24 May 2019 06:26:11 -0700 (PDT) Received: from localhost ([127.0.0.1]:54624 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUACs-0007ml-JR for importer@patchew.org; Fri, 24 May 2019 09:25:58 -0400 Received: from eggs.gnu.org ([209.51.188.92]:58688) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA18-0007mH-Kj for qemu-devel@nongnu.org; Fri, 24 May 2019 09:13:56 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hU9yZ-0001fb-PW for qemu-devel@nongnu.org; Fri, 24 May 2019 09:11:15 -0400 Received: from mx1.redhat.com ([209.132.183.28]:1371) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hU9yZ-0001fJ-Dl for qemu-devel@nongnu.org; Fri, 24 May 2019 09:11:11 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id BB50E88E62 for ; Fri, 24 May 2019 13:11:05 +0000 (UTC) Received: from localhost (ovpn-112-23.ams2.redhat.com [10.36.112.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id 053945DA2E; Fri, 24 May 2019 13:10:57 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 24 May 2019 15:09:44 +0200 Message-Id: <20190524130946.31736-8-marcandre.lureau@redhat.com> In-Reply-To: <20190524130946.31736-1-marcandre.lureau@redhat.com> References: <20190524130946.31736-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Fri, 24 May 2019 13:11:05 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v9 7/9] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a base class that is common to virtio-gpu and vhost-user-gpu devices. The VirtIOGPUBase base class provides common functionalities necessary for both virtio-gpu and vhost-user-gpu: - common configuration (max-outputs, initial resolution, flags) - virtio device initialization, including queue setup - device pre-conditions checks (iommu) - migration blocker - virtio device callbacks - hooking up to qemu display subsystem - a few common helper functions to reset the device, retrieve display informations - a class callback to unblock the rendering (for GL updates) What is left to the virtio-gpu subdevice to take care of, in short, are all the virtio queues handling, command processing and migration. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/hw/virtio/virtio-gpu.h | 73 +++++-- hw/display/virtio-gpu-3d.c | 49 +++-- hw/display/virtio-gpu-base.c | 268 +++++++++++++++++++++++ hw/display/virtio-gpu-pci.c | 2 +- hw/display/virtio-gpu.c | 376 +++++++++------------------------ hw/display/virtio-vga.c | 11 +- hw/display/Makefile.objs | 2 +- 7 files changed, 460 insertions(+), 321 deletions(-) create mode 100644 hw/display/virtio-gpu-base.c diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 60425c5d58..d0d8d7b246 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -22,6 +22,14 @@ =20 #include "standard-headers/linux/virtio_gpu.h" =20 +#define TYPE_VIRTIO_GPU_BASE "virtio-gpu-base" +#define VIRTIO_GPU_BASE(obj) = \ + OBJECT_CHECK(VirtIOGPUBase, (obj), TYPE_VIRTIO_GPU_BASE) +#define VIRTIO_GPU_BASE_GET_CLASS(obj) = \ + OBJECT_GET_CLASS(VirtIOGPUBaseClass, obj, TYPE_VIRTIO_GPU_BASE) +#define VIRTIO_GPU_BASE_CLASS(klass) = \ + OBJECT_CLASS_CHECK(VirtIOGPUBaseClass, klass, TYPE_VIRTIO_GPU_BASE) + #define TYPE_VIRTIO_GPU "virtio-gpu-device" #define VIRTIO_GPU(obj) \ OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU) @@ -58,7 +66,7 @@ struct virtio_gpu_requested_state { int x, y; }; =20 -enum virtio_gpu_conf_flags { +enum virtio_gpu_base_conf_flags { VIRTIO_GPU_FLAG_VIRGL_ENABLED =3D 1, VIRTIO_GPU_FLAG_STATS_ENABLED, VIRTIO_GPU_FLAG_EDID_ENABLED, @@ -71,8 +79,7 @@ enum virtio_gpu_conf_flags { #define virtio_gpu_edid_enabled(_cfg) \ (_cfg.flags & (1 << VIRTIO_GPU_FLAG_EDID_ENABLED)) =20 -struct virtio_gpu_conf { - uint64_t max_hostmem; +struct virtio_gpu_base_conf { uint32_t max_outputs; uint32_t flags; uint32_t xres; @@ -88,31 +95,55 @@ struct virtio_gpu_ctrl_command { QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; }; =20 -typedef struct VirtIOGPU { +typedef struct VirtIOGPUBase { VirtIODevice parent_obj; =20 - QEMUBH *ctrl_bh; - QEMUBH *cursor_bh; + Error *migration_blocker; + + struct virtio_gpu_base_conf conf; + struct virtio_gpu_config virtio_config; + + bool use_virgl_renderer; + int renderer_blocked; + int enable; + + struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; + + int enabled_output_bitmask; + struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUTS]; +} VirtIOGPUBase; + +typedef struct VirtIOGPUBaseClass { + VirtioDeviceClass parent; + + void (*gl_unblock)(VirtIOGPUBase *g); +} VirtIOGPUBaseClass; + +#define VIRTIO_GPU_BASE_PROPERTIES(_state, _conf) \ + DEFINE_PROP_UINT32("max_outputs", _state, _conf.max_outputs, 1), \ + DEFINE_PROP_BIT("edid", _state, _conf.flags, \ + VIRTIO_GPU_FLAG_EDID_ENABLED, false), \ + DEFINE_PROP_UINT32("xres", _state, _conf.xres, 1024), \ + DEFINE_PROP_UINT32("yres", _state, _conf.yres, 768) + +typedef struct VirtIOGPU { + VirtIOGPUBase parent_obj; + + uint64_t conf_max_hostmem; + VirtQueue *ctrl_vq; VirtQueue *cursor_vq; =20 - int enable; + QEMUBH *ctrl_bh; + QEMUBH *cursor_bh; =20 QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq; QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; =20 - struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; - struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUTS]; - - struct virtio_gpu_conf conf; uint64_t hostmem; - int enabled_output_bitmask; - struct virtio_gpu_config virtio_config; =20 - bool use_virgl_renderer; bool renderer_inited; - int renderer_blocked; bool renderer_reset; QEMUTimer *fence_poll; QEMUTimer *print_stats; @@ -124,8 +155,6 @@ typedef struct VirtIOGPU { uint32_t req_3d; uint32_t bytes_3d; } stats; - - Error *migration_blocker; } VirtIOGPU; =20 extern const GraphicHwOps virtio_gpu_ops; @@ -148,6 +177,15 @@ extern const GraphicHwOps virtio_gpu_ops; } \ } while (0) =20 +/* virtio-gpu-base.c */ +bool virtio_gpu_base_device_realize(DeviceState *qdev, + VirtIOHandleOutput ctrl_cb, + VirtIOHandleOutput cursor_cb, + Error **errp); +void virtio_gpu_base_reset(VirtIOGPUBase *g); +void virtio_gpu_base_fill_display_info(VirtIOGPUBase *g, + struct virtio_gpu_resp_display_info *dpy_info); + /* virtio-gpu.c */ void virtio_gpu_ctrl_response(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd, @@ -175,4 +213,5 @@ void virtio_gpu_virgl_fence_poll(VirtIOGPU *g); void virtio_gpu_virgl_reset(VirtIOGPU *g); int virtio_gpu_virgl_init(VirtIOGPU *g); int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g); + #endif diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c index 2d302526ab..b918167aec 100644 --- a/hw/display/virtio-gpu-3d.c +++ b/hw/display/virtio-gpu-3d.c @@ -118,11 +118,11 @@ static void virgl_cmd_context_destroy(VirtIOGPU *g, static void virtio_gpu_rect_update(VirtIOGPU *g, int idx, int x, int y, int width, int height) { - if (!g->scanout[idx].con) { + if (!g->parent_obj.scanout[idx].con) { return; } =20 - dpy_gl_update(g->scanout[idx].con, x, y, width, height); + dpy_gl_update(g->parent_obj.scanout[idx].con, x, y, width, height); } =20 static void virgl_cmd_resource_flush(VirtIOGPU *g, @@ -135,8 +135,8 @@ static void virgl_cmd_resource_flush(VirtIOGPU *g, trace_virtio_gpu_cmd_res_flush(rf.resource_id, rf.r.width, rf.r.height, rf.r.x, rf.r.y= ); =20 - for (i =3D 0; i < g->conf.max_outputs; i++) { - if (g->scanout[i].resource_id !=3D rf.resource_id) { + for (i =3D 0; i < g->parent_obj.conf.max_outputs; i++) { + if (g->parent_obj.scanout[i].resource_id !=3D rf.resource_id) { continue; } virtio_gpu_rect_update(g, i, rf.r.x, rf.r.y, rf.r.width, rf.r.heig= ht); @@ -154,13 +154,13 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, ss.r.width, ss.r.height, ss.r.x, ss.r= .y); =20 - if (ss.scanout_id >=3D g->conf.max_outputs) { + if (ss.scanout_id >=3D g->parent_obj.conf.max_outputs) { qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %= d", __func__, ss.scanout_id); cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; return; } - g->enable =3D 1; + g->parent_obj.enable =3D 1; =20 memset(&info, 0, sizeof(info)); =20 @@ -173,20 +173,22 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } - qemu_console_resize(g->scanout[ss.scanout_id].con, + qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con, ss.r.width, ss.r.height); virgl_renderer_force_ctx_0(); - dpy_gl_scanout_texture(g->scanout[ss.scanout_id].con, info.tex_id, - info.flags & 1 /* FIXME: Y_0_TOP */, - info.width, info.height, - ss.r.x, ss.r.y, ss.r.width, ss.r.height); + dpy_gl_scanout_texture( + g->parent_obj.scanout[ss.scanout_id].con, info.tex_id, + info.flags & 1 /* FIXME: Y_0_TOP */, + info.width, info.height, + ss.r.x, ss.r.y, ss.r.width, ss.r.height); } else { if (ss.scanout_id !=3D 0) { - dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); + dpy_gfx_replace_surface( + g->parent_obj.scanout[ss.scanout_id].con, NULL); } - dpy_gl_scanout_disable(g->scanout[ss.scanout_id].con); + dpy_gl_scanout_disable(g->parent_obj.scanout[ss.scanout_id].con); } - g->scanout[ss.scanout_id].resource_id =3D ss.resource_id; + g->parent_obj.scanout[ss.scanout_id].resource_id =3D ss.resource_id; } =20 static void virgl_cmd_submit_3d(VirtIOGPU *g, @@ -209,7 +211,7 @@ static void virgl_cmd_submit_3d(VirtIOGPU *g, goto out; } =20 - if (virtio_gpu_stats_enabled(g->conf)) { + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { g->stats.req_3d++; g->stats.bytes_3d +=3D cs.size; } @@ -507,7 +509,7 @@ static void virgl_write_fence(void *opaque, uint32_t fe= nce) QTAILQ_REMOVE(&g->fenceq, cmd, next); g_free(cmd); g->inflight--; - if (virtio_gpu_stats_enabled(g->conf)) { + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { fprintf(stderr, "inflight: %3d (-)\r", g->inflight); } } @@ -524,7 +526,7 @@ virgl_create_context(void *opaque, int scanout_idx, qparams.major_ver =3D params->major_ver; qparams.minor_ver =3D params->minor_ver; =20 - ctx =3D dpy_gl_ctx_create(g->scanout[scanout_idx].con, &qparams); + ctx =3D dpy_gl_ctx_create(g->parent_obj.scanout[scanout_idx].con, &qpa= rams); return (virgl_renderer_gl_context)ctx; } =20 @@ -533,7 +535,7 @@ static void virgl_destroy_context(void *opaque, virgl_r= enderer_gl_context ctx) VirtIOGPU *g =3D opaque; QEMUGLContext qctx =3D (QEMUGLContext)ctx; =20 - dpy_gl_ctx_destroy(g->scanout[0].con, qctx); + dpy_gl_ctx_destroy(g->parent_obj.scanout[0].con, qctx); } =20 static int virgl_make_context_current(void *opaque, int scanout_idx, @@ -542,7 +544,8 @@ static int virgl_make_context_current(void *opaque, int= scanout_idx, VirtIOGPU *g =3D opaque; QEMUGLContext qctx =3D (QEMUGLContext)ctx; =20 - return dpy_gl_ctx_make_current(g->scanout[scanout_idx].con, qctx); + return dpy_gl_ctx_make_current(g->parent_obj.scanout[scanout_idx].con, + qctx); } =20 static struct virgl_renderer_callbacks virtio_gpu_3d_cbs =3D { @@ -594,11 +597,11 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g) int i; =20 /* virgl_renderer_reset() ??? */ - for (i =3D 0; i < g->conf.max_outputs; i++) { + for (i =3D 0; i < g->parent_obj.conf.max_outputs; i++) { if (i !=3D 0) { - dpy_gfx_replace_surface(g->scanout[i].con, NULL); + dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL); } - dpy_gl_scanout_disable(g->scanout[i].con); + dpy_gl_scanout_disable(g->parent_obj.scanout[i].con); } } =20 @@ -614,7 +617,7 @@ int virtio_gpu_virgl_init(VirtIOGPU *g) g->fence_poll =3D timer_new_ms(QEMU_CLOCK_VIRTUAL, virtio_gpu_fence_poll, g); =20 - if (virtio_gpu_stats_enabled(g->conf)) { + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { g->print_stats =3D timer_new_ms(QEMU_CLOCK_VIRTUAL, virtio_gpu_print_stats, g); timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + = 1000); diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c new file mode 100644 index 0000000000..55e07995fe --- /dev/null +++ b/hw/display/virtio-gpu-base.c @@ -0,0 +1,268 @@ +/* + * Virtio GPU Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "hw/virtio/virtio-gpu.h" +#include "migration/blocker.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "trace.h" + +void +virtio_gpu_base_reset(VirtIOGPUBase *g) +{ + int i; + + g->enable =3D 0; + g->use_virgl_renderer =3D false; + + for (i =3D 0; i < g->conf.max_outputs; i++) { + g->scanout[i].resource_id =3D 0; + g->scanout[i].width =3D 0; + g->scanout[i].height =3D 0; + g->scanout[i].x =3D 0; + g->scanout[i].y =3D 0; + g->scanout[i].ds =3D NULL; + } +} + +void +virtio_gpu_base_fill_display_info(VirtIOGPUBase *g, + struct virtio_gpu_resp_display_info *dpy= _info) +{ + int i; + + for (i =3D 0; i < g->conf.max_outputs; i++) { + if (g->enabled_output_bitmask & (1 << i)) { + dpy_info->pmodes[i].enabled =3D 1; + dpy_info->pmodes[i].r.width =3D cpu_to_le32(g->req_state[i].wi= dth); + dpy_info->pmodes[i].r.height =3D cpu_to_le32(g->req_state[i].h= eight); + } + } +} + +static void virtio_gpu_invalidate_display(void *opaque) +{ +} + +static void virtio_gpu_update_display(void *opaque) +{ +} + +static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata) +{ +} + +static void virtio_gpu_notify_event(VirtIOGPUBase *g, uint32_t event_type) +{ + g->virtio_config.events_read |=3D event_type; + virtio_notify_config(&g->parent_obj); +} + +static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) +{ + VirtIOGPUBase *g =3D opaque; + + if (idx >=3D g->conf.max_outputs) { + return -1; + } + + g->req_state[idx].x =3D info->xoff; + g->req_state[idx].y =3D info->yoff; + g->req_state[idx].width =3D info->width; + g->req_state[idx].height =3D info->height; + + if (info->width && info->height) { + g->enabled_output_bitmask |=3D (1 << idx); + } else { + g->enabled_output_bitmask &=3D ~(1 << idx); + } + + /* send event to guest */ + virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY); + return 0; +} + +static void +virtio_gpu_gl_block(void *opaque, bool block) +{ + VirtIOGPUBase *g =3D opaque; + VirtIOGPUBaseClass *vgc =3D VIRTIO_GPU_BASE_GET_CLASS(g); + + if (block) { + g->renderer_blocked++; + } else { + g->renderer_blocked--; + } + assert(g->renderer_blocked >=3D 0); + + if (g->renderer_blocked =3D=3D 0) { + vgc->gl_unblock(g); + } +} + +const GraphicHwOps virtio_gpu_ops =3D { + .invalidate =3D virtio_gpu_invalidate_display, + .gfx_update =3D virtio_gpu_update_display, + .text_update =3D virtio_gpu_text_update, + .ui_info =3D virtio_gpu_ui_info, + .gl_block =3D virtio_gpu_gl_block, +}; + +bool +virtio_gpu_base_device_realize(DeviceState *qdev, + VirtIOHandleOutput ctrl_cb, + VirtIOHandleOutput cursor_cb, + Error **errp) +{ + VirtIODevice *vdev =3D VIRTIO_DEVICE(qdev); + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(qdev); + Error *local_err =3D NULL; + int i; + + if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) { + error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOU= TS); + return false; + } + + g->use_virgl_renderer =3D false; + if (virtio_gpu_virgl_enabled(g->conf)) { + error_setg(&g->migration_blocker, "virgl is not yet migratable"); + migrate_add_blocker(g->migration_blocker, &local_err); + if (local_err) { + error_propagate(errp, local_err); + error_free(g->migration_blocker); + return false; + } + } + + g->virtio_config.num_scanouts =3D cpu_to_le32(g->conf.max_outputs); + virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, + sizeof(struct virtio_gpu_config)); + + if (virtio_gpu_virgl_enabled(g->conf)) { + /* use larger control queue in 3d mode */ + virtio_add_queue(vdev, 256, ctrl_cb); + virtio_add_queue(vdev, 16, cursor_cb); + } else { + virtio_add_queue(vdev, 64, ctrl_cb); + virtio_add_queue(vdev, 16, cursor_cb); + } + + g->enabled_output_bitmask =3D 1; + + g->req_state[0].width =3D g->conf.xres; + g->req_state[0].height =3D g->conf.yres; + + for (i =3D 0; i < g->conf.max_outputs; i++) { + g->scanout[i].con =3D + graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g); + if (i > 0) { + dpy_gfx_replace_surface(g->scanout[i].con, NULL); + } + } + + return true; +} + +static uint64_t +virtio_gpu_base_get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(vdev); + + if (virtio_gpu_virgl_enabled(g->conf)) { + features |=3D (1 << VIRTIO_GPU_F_VIRGL); + } + if (virtio_gpu_edid_enabled(g->conf)) { + features |=3D (1 << VIRTIO_GPU_F_EDID); + } + + return features; +} + +static void +virtio_gpu_base_set_features(VirtIODevice *vdev, uint64_t features) +{ + static const uint32_t virgl =3D (1 << VIRTIO_GPU_F_VIRGL); + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(vdev); + + g->use_virgl_renderer =3D ((features & virgl) =3D=3D virgl); + trace_virtio_gpu_features(g->use_virgl_renderer); +} + +static void +virtio_gpu_base_device_unrealize(DeviceState *qdev, Error **errp) +{ + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(qdev); + + if (g->migration_blocker) { + migrate_del_blocker(g->migration_blocker); + error_free(g->migration_blocker); + } +} + +static void +virtio_gpu_base_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + VirtioDeviceClass *vdc =3D VIRTIO_DEVICE_CLASS(klass); + + vdc->unrealize =3D virtio_gpu_base_device_unrealize; + vdc->get_features =3D virtio_gpu_base_get_features; + vdc->set_features =3D virtio_gpu_base_set_features; + + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + dc->hotpluggable =3D false; +} + +static const TypeInfo virtio_gpu_base_info =3D { + .name =3D TYPE_VIRTIO_GPU_BASE, + .parent =3D TYPE_VIRTIO_DEVICE, + .instance_size =3D sizeof(VirtIOGPUBase), + .class_size =3D sizeof(VirtIOGPUBaseClass), + .class_init =3D virtio_gpu_base_class_init, + .abstract =3D true +}; + +static void +virtio_register_types(void) +{ + type_register_static(&virtio_gpu_base_info); +} + +type_init(virtio_register_types) + +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr) !=3D 2= 4); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor) !=3D 5= 6); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d) !=3D 4= 0); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout) !=3D 4= 8); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush) !=3D 4= 8); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d) !=3D 5= 6); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry) !=3D 1= 6); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info) !=3D 4= 08); + +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d) !=3D 7= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d) !=3D 7= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create) !=3D 9= 6); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy) !=3D 2= 4); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info) !=3D 4= 0); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset) !=3D 2= 4); diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index 0bc4d9d424..87f61e857c 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -41,7 +41,7 @@ static Property virtio_gpu_pci_properties[] =3D { static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOGPUPCI *vgpu =3D VIRTIO_GPU_PCI(vpci_dev); - VirtIOGPU *g =3D &vgpu->vdev; + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vgpu->vdev); DeviceState *vdev =3D DEVICE(&vgpu->vdev); int i; Error *local_error =3D NULL; diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 3262f7f1f1..8cd33653b2 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -24,9 +24,9 @@ #include "hw/virtio/virtio-gpu-pixman.h" #include "hw/virtio/virtio-bus.h" #include "hw/display/edid.h" -#include "migration/blocker.h" #include "qemu/log.h" #include "qapi/error.h" +#include "qemu/error-report.h" =20 #define VIRTIO_GPU_VM_VERSION 1 =20 @@ -40,7 +40,7 @@ static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, #include #define VIRGL(_g, _virgl, _simple, ...) \ do { \ - if (_g->use_virgl_renderer) { \ + if (_g->parent_obj.use_virgl_renderer) { \ _virgl(__VA_ARGS__); \ } else { \ _simple(__VA_ARGS__); \ @@ -108,10 +108,10 @@ static void update_cursor(VirtIOGPU *g, struct virtio= _gpu_update_cursor *cursor) struct virtio_gpu_scanout *s; bool move =3D cursor->hdr.type =3D=3D VIRTIO_GPU_CMD_MOVE_CURSOR; =20 - if (cursor->pos.scanout_id >=3D g->conf.max_outputs) { + if (cursor->pos.scanout_id >=3D g->parent_obj.conf.max_outputs) { return; } - s =3D &g->scanout[cursor->pos.scanout_id]; + s =3D &g->parent_obj.scanout[cursor->pos.scanout_id]; =20 trace_virtio_gpu_update_cursor(cursor->pos.scanout_id, cursor->pos.x, @@ -142,53 +142,6 @@ static void update_cursor(VirtIOGPU *g, struct virtio_= gpu_update_cursor *cursor) cursor->resource_id ? 1 : 0); } =20 -static void virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config) -{ - VirtIOGPU *g =3D VIRTIO_GPU(vdev); - memcpy(config, &g->virtio_config, sizeof(g->virtio_config)); -} - -static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *confi= g) -{ - VirtIOGPU *g =3D VIRTIO_GPU(vdev); - struct virtio_gpu_config vgconfig; - - memcpy(&vgconfig, config, sizeof(g->virtio_config)); - - if (vgconfig.events_clear) { - g->virtio_config.events_read &=3D ~vgconfig.events_clear; - } -} - -static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t featu= res, - Error **errp) -{ - VirtIOGPU *g =3D VIRTIO_GPU(vdev); - - if (virtio_gpu_virgl_enabled(g->conf)) { - features |=3D (1 << VIRTIO_GPU_F_VIRGL); - } - if (virtio_gpu_edid_enabled(g->conf)) { - features |=3D (1 << VIRTIO_GPU_F_EDID); - } - return features; -} - -static void virtio_gpu_set_features(VirtIODevice *vdev, uint64_t features) -{ - static const uint32_t virgl =3D (1 << VIRTIO_GPU_F_VIRGL); - VirtIOGPU *g =3D VIRTIO_GPU(vdev); - - g->use_virgl_renderer =3D ((features & virgl) =3D=3D virgl); - trace_virtio_gpu_features(g->use_virgl_renderer); -} - -static void virtio_gpu_notify_event(VirtIOGPU *g, uint32_t event_type) -{ - g->virtio_config.events_read |=3D event_type; - virtio_notify_config(&g->parent_obj); -} - static struct virtio_gpu_simple_resource * virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id) { @@ -237,21 +190,6 @@ void virtio_gpu_ctrl_response_nodata(VirtIOGPU *g, virtio_gpu_ctrl_response(g, cmd, &resp, sizeof(resp)); } =20 -static void -virtio_gpu_fill_display_info(VirtIOGPU *g, - struct virtio_gpu_resp_display_info *dpy_info) -{ - int i; - - for (i =3D 0; i < g->conf.max_outputs; i++) { - if (g->enabled_output_bitmask & (1 << i)) { - dpy_info->pmodes[i].enabled =3D 1; - dpy_info->pmodes[i].r.width =3D cpu_to_le32(g->req_state[i].wi= dth); - dpy_info->pmodes[i].r.height =3D cpu_to_le32(g->req_state[i].h= eight); - } - } -} - void virtio_gpu_get_display_info(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { @@ -260,7 +198,7 @@ void virtio_gpu_get_display_info(VirtIOGPU *g, trace_virtio_gpu_cmd_get_display_info(); memset(&display_info, 0, sizeof(display_info)); display_info.hdr.type =3D VIRTIO_GPU_RESP_OK_DISPLAY_INFO; - virtio_gpu_fill_display_info(g, &display_info); + virtio_gpu_base_fill_display_info(VIRTIO_GPU_BASE(g), &display_info); virtio_gpu_ctrl_response(g, cmd, &display_info.hdr, sizeof(display_info)); } @@ -269,9 +207,10 @@ static void virtio_gpu_generate_edid(VirtIOGPU *g, int scanout, struct virtio_gpu_resp_edid *edid) { + VirtIOGPUBase *b =3D VIRTIO_GPU_BASE(g); qemu_edid_info info =3D { - .prefx =3D g->req_state[scanout].width, - .prefy =3D g->req_state[scanout].height, + .prefx =3D b->req_state[scanout].width, + .prefy =3D b->req_state[scanout].height, }; =20 edid->size =3D cpu_to_le32(sizeof(edid->edid)); @@ -283,11 +222,12 @@ void virtio_gpu_get_edid(VirtIOGPU *g, { struct virtio_gpu_resp_edid edid; struct virtio_gpu_cmd_get_edid get_edid; + VirtIOGPUBase *b =3D VIRTIO_GPU_BASE(g); =20 VIRTIO_GPU_FILL_CMD(get_edid); virtio_gpu_bswap_32(&get_edid, sizeof(get_edid)); =20 - if (get_edid.scanout >=3D g->conf.max_outputs) { + if (get_edid.scanout >=3D b->conf.max_outputs) { cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; } @@ -356,7 +296,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, } =20 res->hostmem =3D calc_image_hostmem(pformat, c2d.width, c2d.height); - if (res->hostmem + g->hostmem < g->conf.max_hostmem) { + if (res->hostmem + g->hostmem < g->conf_max_hostmem) { res->image =3D pixman_image_create_bits(pformat, c2d.width, c2d.height, @@ -378,7 +318,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, =20 static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) { - struct virtio_gpu_scanout *scanout =3D &g->scanout[scanout_id]; + struct virtio_gpu_scanout *scanout =3D &g->parent_obj.scanout[scanout_= id]; struct virtio_gpu_simple_resource *res; DisplaySurface *ds =3D NULL; =20 @@ -410,7 +350,7 @@ static void virtio_gpu_resource_destroy(VirtIOGPU *g, int i; =20 if (res->scanout_bitmask) { - for (i =3D 0; i < g->conf.max_outputs; i++) { + for (i =3D 0; i < g->parent_obj.conf.max_outputs; i++) { if (res->scanout_bitmask & (1 << i)) { virtio_gpu_disable_scanout(g, i); } @@ -540,7 +480,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, =20 pixman_region_init_rect(&flush_region, rf.r.x, rf.r.y, rf.r.width, rf.r.height); - for (i =3D 0; i < g->conf.max_outputs; i++) { + for (i =3D 0; i < g->parent_obj.conf.max_outputs; i++) { struct virtio_gpu_scanout *scanout; pixman_region16_t region, finalregion; pixman_box16_t *extents; @@ -548,7 +488,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, if (!(res->scanout_bitmask & (1 << i))) { continue; } - scanout =3D &g->scanout[i]; + scanout =3D &g->parent_obj.scanout[i]; =20 pixman_region_init(&finalregion); pixman_region_init_rect(®ion, scanout->x, scanout->y, @@ -558,7 +498,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, pixman_region_translate(&finalregion, -scanout->x, -scanout->y); extents =3D pixman_region_extents(&finalregion); /* work out the area we need to update for each console */ - dpy_gfx_update(g->scanout[i].con, + dpy_gfx_update(g->parent_obj.scanout[i].con, extents->x1, extents->y1, extents->x2 - extents->x1, extents->y2 - extents->y1); @@ -589,14 +529,14 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, ss.r.width, ss.r.height, ss.r.x, ss.r= .y); =20 - if (ss.scanout_id >=3D g->conf.max_outputs) { + if (ss.scanout_id >=3D g->parent_obj.conf.max_outputs) { qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %= d", __func__, ss.scanout_id); cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; return; } =20 - g->enable =3D 1; + g->parent_obj.enable =3D 1; if (ss.resource_id =3D=3D 0) { virtio_gpu_disable_scanout(g, ss.scanout_id); return; @@ -625,7 +565,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, return; } =20 - scanout =3D &g->scanout[ss.scanout_id]; + scanout =3D &g->parent_obj.scanout[ss.scanout_id]; =20 format =3D pixman_image_get_format(res->image); bpp =3D DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); @@ -648,7 +588,8 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, return; } pixman_image_unref(rect); - dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds= ); + dpy_gfx_replace_surface(g->parent_obj.scanout[ss.scanout_id].con, + scanout->ds); } =20 ores =3D virtio_gpu_find_resource(g, scanout->resource_id); @@ -866,7 +807,7 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) while (!QTAILQ_EMPTY(&g->cmdq)) { cmd =3D QTAILQ_FIRST(&g->cmdq); =20 - if (g->renderer_blocked) { + if (g->parent_obj.renderer_blocked) { break; } =20 @@ -875,14 +816,14 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) g, cmd); =20 QTAILQ_REMOVE(&g->cmdq, cmd, next); - if (virtio_gpu_stats_enabled(g->conf)) { + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { g->stats.requests++; } =20 if (!cmd->finished) { QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next); g->inflight++; - if (virtio_gpu_stats_enabled(g->conf)) { + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { if (g->stats.max_inflight < g->inflight) { g->stats.max_inflight =3D g->inflight; } @@ -894,6 +835,19 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) } } =20 +static void virtio_gpu_gl_unblock(VirtIOGPUBase *b) +{ + VirtIOGPU *g =3D VIRTIO_GPU(b); + +#ifdef CONFIG_VIRGL + if (g->renderer_reset) { + g->renderer_reset =3D false; + virtio_gpu_virgl_reset(g); + } +#endif + virtio_gpu_process_cmdq(g); +} + static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) { VirtIOGPU *g =3D VIRTIO_GPU(vdev); @@ -904,7 +858,7 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, = VirtQueue *vq) } =20 #ifdef CONFIG_VIRGL - if (!g->renderer_inited && g->use_virgl_renderer) { + if (!g->renderer_inited && g->parent_obj.use_virgl_renderer) { virtio_gpu_virgl_init(g); g->renderer_inited =3D true; } @@ -922,7 +876,7 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, = VirtQueue *vq) virtio_gpu_process_cmdq(g); =20 #ifdef CONFIG_VIRGL - if (g->use_virgl_renderer) { + if (g->parent_obj.use_virgl_renderer) { virtio_gpu_virgl_fence_poll(g); } #endif @@ -931,7 +885,7 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, = VirtQueue *vq) static void virtio_gpu_ctrl_bh(void *opaque) { VirtIOGPU *g =3D opaque; - virtio_gpu_handle_ctrl(&g->parent_obj, g->ctrl_vq); + virtio_gpu_handle_ctrl(&g->parent_obj.parent_obj, g->ctrl_vq); } =20 static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq) @@ -969,75 +923,9 @@ static void virtio_gpu_handle_cursor(VirtIODevice *vde= v, VirtQueue *vq) static void virtio_gpu_cursor_bh(void *opaque) { VirtIOGPU *g =3D opaque; - virtio_gpu_handle_cursor(&g->parent_obj, g->cursor_vq); -} - -static void virtio_gpu_invalidate_display(void *opaque) -{ -} - -static void virtio_gpu_update_display(void *opaque) -{ -} - -static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata) -{ -} - -static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) -{ - VirtIOGPU *g =3D opaque; - - if (idx >=3D g->conf.max_outputs) { - return -1; - } - - g->req_state[idx].x =3D info->xoff; - g->req_state[idx].y =3D info->yoff; - g->req_state[idx].width =3D info->width; - g->req_state[idx].height =3D info->height; - - if (info->width && info->height) { - g->enabled_output_bitmask |=3D (1 << idx); - } else { - g->enabled_output_bitmask &=3D ~(1 << idx); - } - - /* send event to guest */ - virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY); - return 0; + virtio_gpu_handle_cursor(&g->parent_obj.parent_obj, g->cursor_vq); } =20 -static void virtio_gpu_gl_block(void *opaque, bool block) -{ - VirtIOGPU *g =3D opaque; - - if (block) { - g->renderer_blocked++; - } else { - g->renderer_blocked--; - } - assert(g->renderer_blocked >=3D 0); - - if (g->renderer_blocked =3D=3D 0) { -#ifdef CONFIG_VIRGL - if (g->renderer_reset) { - g->renderer_reset =3D false; - virtio_gpu_virgl_reset(g); - } -#endif - virtio_gpu_process_cmdq(g); - } -} - -const GraphicHwOps virtio_gpu_ops =3D { - .invalidate =3D virtio_gpu_invalidate_display, - .gfx_update =3D virtio_gpu_update_display, - .text_update =3D virtio_gpu_text_update, - .ui_info =3D virtio_gpu_ui_info, - .gl_block =3D virtio_gpu_gl_block, -}; - static const VMStateDescription vmstate_virtio_gpu_scanout =3D { .name =3D "virtio-gpu-one-scanout", .version_id =3D 1, @@ -1060,10 +948,11 @@ static const VMStateDescription vmstate_virtio_gpu_s= canouts =3D { .name =3D "virtio-gpu-scanouts", .version_id =3D 1, .fields =3D (VMStateField[]) { - VMSTATE_INT32(enable, struct VirtIOGPU), - VMSTATE_UINT32_EQUAL(conf.max_outputs, struct VirtIOGPU, NULL), - VMSTATE_STRUCT_VARRAY_UINT32(scanout, struct VirtIOGPU, - conf.max_outputs, 1, + VMSTATE_INT32(parent_obj.enable, struct VirtIOGPU), + VMSTATE_UINT32_EQUAL(parent_obj.conf.max_outputs, + struct VirtIOGPU, NULL), + VMSTATE_STRUCT_VARRAY_UINT32(parent_obj.scanout, struct VirtIOGPU, + parent_obj.conf.max_outputs, 1, vmstate_virtio_gpu_scanout, struct virtio_gpu_scanout), VMSTATE_END_OF_LIST() @@ -1178,8 +1067,8 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque,= size_t size, =20 /* load & apply scanout state */ vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1); - for (i =3D 0; i < g->conf.max_outputs; i++) { - scanout =3D &g->scanout[i]; + for (i =3D 0; i < g->parent_obj.conf.max_outputs; i++) { + scanout =3D &g->parent_obj.scanout[i]; if (!scanout->resource_id) { continue; } @@ -1208,84 +1097,35 @@ static void virtio_gpu_device_realize(DeviceState *= qdev, Error **errp) VirtIODevice *vdev =3D VIRTIO_DEVICE(qdev); VirtIOGPU *g =3D VIRTIO_GPU(qdev); bool have_virgl; - Error *local_err =3D NULL; - int i; =20 - if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) { - error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOU= TS); - return; - } - - g->use_virgl_renderer =3D false; #if !defined(CONFIG_VIRGL) || defined(HOST_WORDS_BIGENDIAN) have_virgl =3D false; #else have_virgl =3D display_opengl; #endif if (!have_virgl) { - g->conf.flags &=3D ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED); - } - - if (virtio_gpu_virgl_enabled(g->conf)) { - error_setg(&g->migration_blocker, "virgl is not yet migratable"); - migrate_add_blocker(g->migration_blocker, &local_err); - if (local_err) { - error_propagate(errp, local_err); - error_free(g->migration_blocker); - return; - } - } - - g->virtio_config.num_scanouts =3D cpu_to_le32(g->conf.max_outputs); - virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, - sizeof(struct virtio_gpu_config)); - - g->req_state[0].width =3D g->conf.xres; - g->req_state[0].height =3D g->conf.yres; - - if (virtio_gpu_virgl_enabled(g->conf)) { - /* use larger control queue in 3d mode */ - g->ctrl_vq =3D virtio_add_queue(vdev, 256, virtio_gpu_handle_ctr= l_cb); - g->cursor_vq =3D virtio_add_queue(vdev, 16, virtio_gpu_handle_curs= or_cb); - + g->parent_obj.conf.flags &=3D ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED= ); + } else { #if defined(CONFIG_VIRGL) - g->virtio_config.num_capsets =3D virtio_gpu_virgl_get_num_capsets(= g); -#else - g->virtio_config.num_capsets =3D 0; + VIRTIO_GPU_BASE(g)->virtio_config.num_capsets =3D + virtio_gpu_virgl_get_num_capsets(g); #endif - } else { - g->ctrl_vq =3D virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl= _cb); - g->cursor_vq =3D virtio_add_queue(vdev, 16, virtio_gpu_handle_curs= or_cb); } =20 + if (!virtio_gpu_base_device_realize(qdev, + virtio_gpu_handle_ctrl_cb, + virtio_gpu_handle_cursor_cb, + errp)) { + return; + } + + g->ctrl_vq =3D virtio_get_queue(vdev, 0); + g->cursor_vq =3D virtio_get_queue(vdev, 1); g->ctrl_bh =3D qemu_bh_new(virtio_gpu_ctrl_bh, g); g->cursor_bh =3D qemu_bh_new(virtio_gpu_cursor_bh, g); QTAILQ_INIT(&g->reslist); QTAILQ_INIT(&g->cmdq); QTAILQ_INIT(&g->fenceq); - - g->enabled_output_bitmask =3D 1; - - for (i =3D 0; i < g->conf.max_outputs; i++) { - g->scanout[i].con =3D - graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g); - if (i > 0) { - dpy_gfx_replace_surface(g->scanout[i].con, NULL); - } - } -} - -static void virtio_gpu_device_unrealize(DeviceState *qdev, Error **errp) -{ - VirtIOGPU *g =3D VIRTIO_GPU(qdev); - if (g->migration_blocker) { - migrate_del_blocker(g->migration_blocker); - error_free(g->migration_blocker); - } -} - -static void virtio_gpu_instance_init(Object *obj) -{ } =20 static void virtio_gpu_reset(VirtIODevice *vdev) @@ -1293,21 +1133,16 @@ static void virtio_gpu_reset(VirtIODevice *vdev) VirtIOGPU *g =3D VIRTIO_GPU(vdev); struct virtio_gpu_simple_resource *res, *tmp; struct virtio_gpu_ctrl_command *cmd; - int i; =20 - g->enable =3D 0; +#ifdef CONFIG_VIRGL + if (g->parent_obj.use_virgl_renderer) { + virtio_gpu_virgl_reset(g); + } +#endif =20 QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { virtio_gpu_resource_destroy(g, res); } - for (i =3D 0; i < g->conf.max_outputs; i++) { - g->scanout[i].resource_id =3D 0; - g->scanout[i].width =3D 0; - g->scanout[i].height =3D 0; - g->scanout[i].x =3D 0; - g->scanout[i].y =3D 0; - g->scanout[i].ds =3D NULL; - } =20 while (!QTAILQ_EMPTY(&g->cmdq)) { cmd =3D QTAILQ_FIRST(&g->cmdq); @@ -1323,15 +1158,37 @@ static void virtio_gpu_reset(VirtIODevice *vdev) } =20 #ifdef CONFIG_VIRGL - if (g->use_virgl_renderer) { - if (g->renderer_blocked) { + if (g->parent_obj.use_virgl_renderer) { + if (g->parent_obj.renderer_blocked) { g->renderer_reset =3D true; } else { virtio_gpu_virgl_reset(g); } - g->use_virgl_renderer =3D 0; + g->parent_obj.use_virgl_renderer =3D false; } #endif + + virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev)); +} + +static void +virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(vdev); + + memcpy(config, &g->virtio_config, sizeof(g->virtio_config)); +} + +static void +virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config) +{ + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(vdev); + const struct virtio_gpu_config *vgconfig =3D + (const struct virtio_gpu_config *)config; + + if (vgconfig->events_clear) { + g->virtio_config.events_read &=3D ~vgconfig->events_clear; + } } =20 /* @@ -1362,18 +1219,15 @@ static const VMStateDescription vmstate_virtio_gpu = =3D { }; =20 static Property virtio_gpu_properties[] =3D { - DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1), - DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf.max_hostmem, 256 * MiB= ), + VIRTIO_GPU_BASE_PROPERTIES(VirtIOGPU, parent_obj.conf), + DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf_max_hostmem, + 256 * MiB), #ifdef CONFIG_VIRGL - DEFINE_PROP_BIT("virgl", VirtIOGPU, conf.flags, + DEFINE_PROP_BIT("virgl", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_VIRGL_ENABLED, true), - DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags, + DEFINE_PROP_BIT("stats", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_STATS_ENABLED, false), #endif - DEFINE_PROP_BIT("edid", VirtIOGPU, conf.flags, - VIRTIO_GPU_FLAG_EDID_ENABLED, false), - DEFINE_PROP_UINT32("xres", VirtIOGPU, conf.xres, 1024), - DEFINE_PROP_UINT32("yres", VirtIOGPU, conf.yres, 768), DEFINE_PROP_END_OF_LIST(), }; =20 @@ -1381,27 +1235,22 @@ static void virtio_gpu_class_init(ObjectClass *klas= s, void *data) { DeviceClass *dc =3D DEVICE_CLASS(klass); VirtioDeviceClass *vdc =3D VIRTIO_DEVICE_CLASS(klass); + VirtIOGPUBaseClass *vgc =3D VIRTIO_GPU_BASE_CLASS(klass); =20 + vgc->gl_unblock =3D virtio_gpu_gl_unblock; vdc->realize =3D virtio_gpu_device_realize; - vdc->unrealize =3D virtio_gpu_device_unrealize; + vdc->reset =3D virtio_gpu_reset; vdc->get_config =3D virtio_gpu_get_config; vdc->set_config =3D virtio_gpu_set_config; - vdc->get_features =3D virtio_gpu_get_features; - vdc->set_features =3D virtio_gpu_set_features; - - vdc->reset =3D virtio_gpu_reset; =20 - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->props =3D virtio_gpu_properties; dc->vmsd =3D &vmstate_virtio_gpu; - dc->hotpluggable =3D false; + dc->props =3D virtio_gpu_properties; } =20 static const TypeInfo virtio_gpu_info =3D { .name =3D TYPE_VIRTIO_GPU, - .parent =3D TYPE_VIRTIO_DEVICE, + .parent =3D TYPE_VIRTIO_GPU_BASE, .instance_size =3D sizeof(VirtIOGPU), - .instance_init =3D virtio_gpu_instance_init, .class_init =3D virtio_gpu_class_init, }; =20 @@ -1411,26 +1260,3 @@ static void virtio_register_types(void) } =20 type_init(virtio_register_types) - -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr) !=3D 2= 4); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor) !=3D 5= 6); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d) !=3D 4= 0); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout) !=3D 4= 8); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush) !=3D 4= 8); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d) !=3D 5= 6); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry) !=3D 1= 6); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info) !=3D 4= 08); - -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d) !=3D 7= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d) !=3D 7= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create) !=3D 9= 6); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy) !=3D 2= 4); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info) !=3D 4= 0); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset) !=3D 2= 4); diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 5d57bf5b0c..a8138c0432 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -32,8 +32,9 @@ typedef struct VirtIOVGAClass { static void virtio_vga_invalidate_display(void *opaque) { VirtIOVGA *vvga =3D opaque; + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); =20 - if (vvga->vdev.enable) { + if (g->enable) { virtio_gpu_ops.invalidate(&vvga->vdev); } else { vvga->vga.hw_ops->invalidate(&vvga->vga); @@ -43,8 +44,9 @@ static void virtio_vga_invalidate_display(void *opaque) static void virtio_vga_update_display(void *opaque) { VirtIOVGA *vvga =3D opaque; + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); =20 - if (vvga->vdev.enable) { + if (g->enable) { virtio_gpu_ops.gfx_update(&vvga->vdev); } else { vvga->vga.hw_ops->gfx_update(&vvga->vga); @@ -54,8 +56,9 @@ static void virtio_vga_update_display(void *opaque) static void virtio_vga_text_update(void *opaque, console_ch_t *chardata) { VirtIOVGA *vvga =3D opaque; + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); =20 - if (vvga->vdev.enable) { + if (g->enable) { if (virtio_gpu_ops.text_update) { virtio_gpu_ops.text_update(&vvga->vdev, chardata); } @@ -108,7 +111,7 @@ static const VMStateDescription vmstate_virtio_vga =3D { static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOVGA *vvga =3D VIRTIO_VGA(vpci_dev); - VirtIOGPU *g =3D &vvga->vdev; + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); VGACommonState *vga =3D &vvga->vga; Error *err =3D NULL; uint32_t offset; diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 650031f725..22e0374df2 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -43,7 +43,7 @@ obj-$(CONFIG_VGA) +=3D vga.o =20 common-obj-$(CONFIG_QXL) +=3D qxl.o qxl-logger.o qxl-render.o =20 -obj-$(CONFIG_VIRTIO_GPU) +=3D virtio-gpu.o virtio-gpu-3d.o +obj-$(CONFIG_VIRTIO_GPU) +=3D virtio-gpu-base.o virtio-gpu.o virtio-gpu-3d= .o obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) +=3D virtio-gpu= -pci.o obj-$(CONFIG_VIRTIO_VGA) +=3D virtio-vga.o virtio-gpu.o-cflags :=3D $(VIRGL_CFLAGS) --=20 2.22.0.rc1.1.g079e7d2849.dirty From nobody Fri Apr 19 02:12:17 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1558703938; cv=none; d=zoho.com; s=zohoarc; b=ID0hQEpbOy1KZHl68I97bo4jRF5245TRKkO76E/8z6HyyZjsGfI3nEcb/tkX/sGg2p/rR2iOWXz8pryAroAzWM5beIw2i2CzXMYmRYbSkL1UzzZ8aW/dzLmUSrHI2LSuQS+KWHba4df7REoci3r1+yxq2qFLYlhBIqIjN+7aiMc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1558703938; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=TGT3PBK+7S0GINt/M69bZd7vX8Y2hCAP0ih8whad1Y0=; b=SikUcvAJcctHEN8tBedLvB85AJQwMeW59LgZoPJFXQpf3FYypwXdgtgV9hxzphQbTB51F4AtQTpjYZgjocJE/H7Te3uzBYs/yXt4zXOGMg9MJEfr9U3IT+uHicj1qrXf4GmpKlBK+n2AFwljjHup27xTNcmqOHPcKrED18uQi+w= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1558703938421637.9410700769265; Fri, 24 May 2019 06:18:58 -0700 (PDT) Received: from localhost ([127.0.0.1]:54502 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA5z-00036W-B1 for importer@patchew.org; Fri, 24 May 2019 09:18:51 -0400 Received: from eggs.gnu.org ([209.51.188.92]:58537) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA12-0007cP-8u for qemu-devel@nongnu.org; Fri, 24 May 2019 09:13:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hU9yj-0001pG-SX for qemu-devel@nongnu.org; Fri, 24 May 2019 09:11:24 -0400 Received: from mx1.redhat.com ([209.132.183.28]:60510) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hU9yj-0001o0-Iy for qemu-devel@nongnu.org; Fri, 24 May 2019 09:11:21 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C24D73007401 for ; Fri, 24 May 2019 13:11:13 +0000 (UTC) Received: from localhost (ovpn-112-23.ams2.redhat.com [10.36.112.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8ADA919748; Fri, 24 May 2019 13:11:07 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 24 May 2019 15:09:45 +0200 Message-Id: <20190524130946.31736-9-marcandre.lureau@redhat.com> In-Reply-To: <20190524130946.31736-1-marcandre.lureau@redhat.com> References: <20190524130946.31736-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.42]); Fri, 24 May 2019 13:11:15 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v9 8/9] virtio-gpu: split virtio-gpu-pci & virtio-vga X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add base classes that are common to vhost-user-gpu-pci and vhost-user-vga. Signed-off-by: Marc-Andr=C3=A9 Lureau --- hw/display/virtio-vga.h | 32 +++++++++ hw/display/virtio-gpu-pci.c | 52 +++++++++----- hw/display/virtio-vga.c | 135 ++++++++++++++++++------------------ MAINTAINERS | 2 +- 4 files changed, 137 insertions(+), 84 deletions(-) create mode 100644 hw/display/virtio-vga.h diff --git a/hw/display/virtio-vga.h b/hw/display/virtio-vga.h new file mode 100644 index 0000000000..f03e1ba619 --- /dev/null +++ b/hw/display/virtio-vga.h @@ -0,0 +1,32 @@ +#ifndef VIRTIO_VGA_H_ +#define VIRTIO_VGA_H_ + +#include "hw/virtio/virtio-pci.h" +#include "vga_int.h" + +/* + * virtio-vga-base: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_VGA_BASE "virtio-vga-base" +#define VIRTIO_VGA_BASE(obj) \ + OBJECT_CHECK(VirtIOVGABase, (obj), TYPE_VIRTIO_VGA_BASE) +#define VIRTIO_VGA_BASE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtIOVGABaseClass, obj, TYPE_VIRTIO_VGA_BASE) +#define VIRTIO_VGA_BASE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtIOVGABaseClass, klass, TYPE_VIRTIO_VGA_BASE) + +typedef struct VirtIOVGABase { + VirtIOPCIProxy parent_obj; + + VirtIOGPUBase *vgpu; + VGACommonState vga; + MemoryRegion vga_mrs[3]; +} VirtIOVGABase; + +typedef struct VirtIOVGABaseClass { + VirtioPCIClass parent_class; + + DeviceReset parent_reset; +} VirtIOVGABaseClass; + +#endif /* VIRTIO_VGA_H_ */ diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index 87f61e857c..d03cadd236 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -19,30 +19,30 @@ #include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-gpu.h" =20 -typedef struct VirtIOGPUPCI VirtIOGPUPCI; +typedef struct VirtIOGPUPCIBase VirtIOGPUPCIBase; =20 /* - * virtio-gpu-pci: This extends VirtioPCIProxy. + * virtio-gpu-pci-base: This extends VirtioPCIProxy. */ -#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci" -#define VIRTIO_GPU_PCI(obj) \ - OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI) +#define TYPE_VIRTIO_GPU_PCI_BASE "virtio-gpu-pci-base" +#define VIRTIO_GPU_PCI_BASE(obj) \ + OBJECT_CHECK(VirtIOGPUPCIBase, (obj), TYPE_VIRTIO_GPU_PCI_BASE) =20 -struct VirtIOGPUPCI { +struct VirtIOGPUPCIBase { VirtIOPCIProxy parent_obj; - VirtIOGPU vdev; + VirtIOGPUBase *vgpu; }; =20 -static Property virtio_gpu_pci_properties[] =3D { +static Property virtio_gpu_pci_base_properties[] =3D { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), DEFINE_PROP_END_OF_LIST(), }; =20 -static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +static void virtio_gpu_pci_base_realize(VirtIOPCIProxy *vpci_dev, Error **= errp) { - VirtIOGPUPCI *vgpu =3D VIRTIO_GPU_PCI(vpci_dev); - VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vgpu->vdev); - DeviceState *vdev =3D DEVICE(&vgpu->vdev); + VirtIOGPUPCIBase *vgpu =3D VIRTIO_GPU_PCI_BASE(vpci_dev); + VirtIOGPUBase *g =3D vgpu->vgpu; + DeviceState *vdev =3D DEVICE(g); int i; Error *local_error =3D NULL; =20 @@ -64,36 +64,56 @@ static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci= _dev, Error **errp) } } =20 -static void virtio_gpu_pci_class_init(ObjectClass *klass, void *data) +static void virtio_gpu_pci_base_class_init(ObjectClass *klass, void *data) { DeviceClass *dc =3D DEVICE_CLASS(klass); VirtioPCIClass *k =3D VIRTIO_PCI_CLASS(klass); PCIDeviceClass *pcidev_k =3D PCI_DEVICE_CLASS(klass); =20 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->props =3D virtio_gpu_pci_properties; + dc->props =3D virtio_gpu_pci_base_properties; dc->hotpluggable =3D false; - k->realize =3D virtio_gpu_pci_realize; + k->realize =3D virtio_gpu_pci_base_realize; pcidev_k->class_id =3D PCI_CLASS_DISPLAY_OTHER; } =20 +static const TypeInfo virtio_gpu_pci_base_info =3D { + .name =3D TYPE_VIRTIO_GPU_PCI_BASE, + .parent =3D TYPE_VIRTIO_PCI, + .instance_size =3D sizeof(VirtIOGPUPCIBase), + .class_init =3D virtio_gpu_pci_base_class_init, + .abstract =3D true +}; + +#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci" +#define VIRTIO_GPU_PCI(obj) \ + OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI) + +typedef struct VirtIOGPUPCI { + VirtIOGPUPCIBase parent_obj; + VirtIOGPU vdev; +} VirtIOGPUPCI; + static void virtio_gpu_initfn(Object *obj) { VirtIOGPUPCI *dev =3D VIRTIO_GPU_PCI(obj); =20 virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU); + VIRTIO_GPU_PCI_BASE(obj)->vgpu =3D VIRTIO_GPU_BASE(&dev->vdev); } =20 static const VirtioPCIDeviceTypeInfo virtio_gpu_pci_info =3D { .generic_name =3D TYPE_VIRTIO_GPU_PCI, + .parent =3D TYPE_VIRTIO_GPU_PCI_BASE, .instance_size =3D sizeof(VirtIOGPUPCI), .instance_init =3D virtio_gpu_initfn, - .class_init =3D virtio_gpu_pci_class_init, }; =20 static void virtio_gpu_pci_register_types(void) { + type_register_static(&virtio_gpu_pci_base_info); virtio_pci_types_register(&virtio_gpu_pci_info); } + type_init(virtio_gpu_pci_register_types) diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index a8138c0432..67e34935c2 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -1,66 +1,42 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" -#include "vga_int.h" -#include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-gpu.h" #include "qapi/error.h" +#include "virtio-vga.h" =20 -/* - * virtio-vga: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_VGA "virtio-vga" -#define VIRTIO_VGA(obj) \ - OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA) -#define VIRTIO_VGA_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtIOVGAClass, obj, TYPE_VIRTIO_VGA) -#define VIRTIO_VGA_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtIOVGAClass, klass, TYPE_VIRTIO_VGA) - -typedef struct VirtIOVGA { - VirtIOPCIProxy parent_obj; - VirtIOGPU vdev; - VGACommonState vga; - MemoryRegion vga_mrs[3]; -} VirtIOVGA; - -typedef struct VirtIOVGAClass { - VirtioPCIClass parent_class; - DeviceReset parent_reset; -} VirtIOVGAClass; - -static void virtio_vga_invalidate_display(void *opaque) +static void virtio_vga_base_invalidate_display(void *opaque) { - VirtIOVGA *vvga =3D opaque; - VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); + VirtIOVGABase *vvga =3D opaque; + VirtIOGPUBase *g =3D vvga->vgpu; =20 if (g->enable) { - virtio_gpu_ops.invalidate(&vvga->vdev); + virtio_gpu_ops.invalidate(g); } else { vvga->vga.hw_ops->invalidate(&vvga->vga); } } =20 -static void virtio_vga_update_display(void *opaque) +static void virtio_vga_base_update_display(void *opaque) { - VirtIOVGA *vvga =3D opaque; - VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); + VirtIOVGABase *vvga =3D opaque; + VirtIOGPUBase *g =3D vvga->vgpu; =20 if (g->enable) { - virtio_gpu_ops.gfx_update(&vvga->vdev); + virtio_gpu_ops.gfx_update(g); } else { vvga->vga.hw_ops->gfx_update(&vvga->vga); } } =20 -static void virtio_vga_text_update(void *opaque, console_ch_t *chardata) +static void virtio_vga_base_text_update(void *opaque, console_ch_t *charda= ta) { - VirtIOVGA *vvga =3D opaque; - VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); + VirtIOVGABase *vvga =3D opaque; + VirtIOGPUBase *g =3D vvga->vgpu; =20 if (g->enable) { if (virtio_gpu_ops.text_update) { - virtio_gpu_ops.text_update(&vvga->vdev, chardata); + virtio_gpu_ops.text_update(g, chardata); } } else { if (vvga->vga.hw_ops->text_update) { @@ -69,49 +45,52 @@ static void virtio_vga_text_update(void *opaque, consol= e_ch_t *chardata) } } =20 -static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) +static int virtio_vga_base_ui_info(void *opaque, uint32_t idx, QemuUIInfo = *info) { - VirtIOVGA *vvga =3D opaque; + VirtIOVGABase *vvga =3D opaque; + VirtIOGPUBase *g =3D vvga->vgpu; =20 if (virtio_gpu_ops.ui_info) { - return virtio_gpu_ops.ui_info(&vvga->vdev, idx, info); + return virtio_gpu_ops.ui_info(g, idx, info); } return -1; } =20 -static void virtio_vga_gl_block(void *opaque, bool block) +static void virtio_vga_base_gl_block(void *opaque, bool block) { - VirtIOVGA *vvga =3D opaque; + VirtIOVGABase *vvga =3D opaque; + VirtIOGPUBase *g =3D vvga->vgpu; =20 if (virtio_gpu_ops.gl_block) { - virtio_gpu_ops.gl_block(&vvga->vdev, block); + virtio_gpu_ops.gl_block(g, block); } } =20 -static const GraphicHwOps virtio_vga_ops =3D { - .invalidate =3D virtio_vga_invalidate_display, - .gfx_update =3D virtio_vga_update_display, - .text_update =3D virtio_vga_text_update, - .ui_info =3D virtio_vga_ui_info, - .gl_block =3D virtio_vga_gl_block, +static const GraphicHwOps virtio_vga_base_ops =3D { + .invalidate =3D virtio_vga_base_invalidate_display, + .gfx_update =3D virtio_vga_base_update_display, + .text_update =3D virtio_vga_base_text_update, + .ui_info =3D virtio_vga_base_ui_info, + .gl_block =3D virtio_vga_base_gl_block, }; =20 -static const VMStateDescription vmstate_virtio_vga =3D { +static const VMStateDescription vmstate_virtio_vga_base =3D { .name =3D "virtio-vga", .version_id =3D 2, .minimum_version_id =3D 2, .fields =3D (VMStateField[]) { /* no pci stuff here, saving the virtio device will handle that */ - VMSTATE_STRUCT(vga, VirtIOVGA, 0, vmstate_vga_common, VGACommonSta= te), + VMSTATE_STRUCT(vga, VirtIOVGABase, 0, + vmstate_vga_common, VGACommonState), VMSTATE_END_OF_LIST() } }; =20 /* VGA device wrapper around PCI device around virtio GPU */ -static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { - VirtIOVGA *vvga =3D VIRTIO_VGA(vpci_dev); - VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); + VirtIOVGABase *vvga =3D VIRTIO_VGA_BASE(vpci_dev); + VirtIOGPUBase *g =3D vvga->vgpu; VGACommonState *vga =3D &vvga->vga; Error *err =3D NULL; uint32_t offset; @@ -171,7 +150,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev= , Error **errp) vvga->vga_mrs, true, false); =20 vga->con =3D g->scanout[0].con; - graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga); + graphic_console_set_hwops(vga->con, &virtio_vga_base_ops, vvga); =20 for (i =3D 0; i < g->conf.max_outputs; i++) { object_property_set_link(OBJECT(g->scanout[i].con), @@ -180,10 +159,10 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_d= ev, Error **errp) } } =20 -static void virtio_vga_reset(DeviceState *dev) +static void virtio_vga_base_reset(DeviceState *dev) { - VirtIOVGAClass *klass =3D VIRTIO_VGA_GET_CLASS(dev); - VirtIOVGA *vvga =3D VIRTIO_VGA(dev); + VirtIOVGABaseClass *klass =3D VIRTIO_VGA_BASE_GET_CLASS(dev); + VirtIOVGABase *vvga =3D VIRTIO_VGA_BASE(dev); =20 /* reset virtio-gpu */ klass->parent_reset(dev); @@ -193,48 +172,70 @@ static void virtio_vga_reset(DeviceState *dev) vga_dirty_log_start(&vvga->vga); } =20 -static Property virtio_vga_properties[] =3D { +static Property virtio_vga_base_properties[] =3D { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), DEFINE_PROP_END_OF_LIST(), }; =20 -static void virtio_vga_class_init(ObjectClass *klass, void *data) +static void virtio_vga_base_class_init(ObjectClass *klass, void *data) { DeviceClass *dc =3D DEVICE_CLASS(klass); VirtioPCIClass *k =3D VIRTIO_PCI_CLASS(klass); - VirtIOVGAClass *v =3D VIRTIO_VGA_CLASS(klass); + VirtIOVGABaseClass *v =3D VIRTIO_VGA_BASE_CLASS(klass); PCIDeviceClass *pcidev_k =3D PCI_DEVICE_CLASS(klass); =20 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->props =3D virtio_vga_properties; - dc->vmsd =3D &vmstate_virtio_vga; + dc->props =3D virtio_vga_base_properties; + dc->vmsd =3D &vmstate_virtio_vga_base; dc->hotpluggable =3D false; - device_class_set_parent_reset(dc, virtio_vga_reset, + device_class_set_parent_reset(dc, virtio_vga_base_reset, &v->parent_reset); =20 - k->realize =3D virtio_vga_realize; + k->realize =3D virtio_vga_base_realize; pcidev_k->romfile =3D "vgabios-virtio.bin"; pcidev_k->class_id =3D PCI_CLASS_DISPLAY_VGA; } =20 +static TypeInfo virtio_vga_base_info =3D { + .name =3D TYPE_VIRTIO_VGA_BASE, + .parent =3D TYPE_VIRTIO_PCI, + .instance_size =3D sizeof(struct VirtIOVGABase), + .class_size =3D sizeof(struct VirtIOVGABaseClass), + .class_init =3D virtio_vga_base_class_init, + .abstract =3D true, +}; + +#define TYPE_VIRTIO_VGA "virtio-vga" + +#define VIRTIO_VGA(obj) \ + OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA) + +typedef struct VirtIOVGA { + VirtIOVGABase parent_obj; + + VirtIOGPU vdev; +} VirtIOVGA; + static void virtio_vga_inst_initfn(Object *obj) { VirtIOVGA *dev =3D VIRTIO_VGA(obj); =20 virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU); + VIRTIO_VGA_BASE(dev)->vgpu =3D VIRTIO_GPU_BASE(&dev->vdev); } =20 + static VirtioPCIDeviceTypeInfo virtio_vga_info =3D { .generic_name =3D TYPE_VIRTIO_VGA, + .parent =3D TYPE_VIRTIO_VGA_BASE, .instance_size =3D sizeof(struct VirtIOVGA), .instance_init =3D virtio_vga_inst_initfn, - .class_size =3D sizeof(struct VirtIOVGAClass), - .class_init =3D virtio_vga_class_init, }; =20 static void virtio_vga_register_types(void) { + type_register_static(&virtio_vga_base_info); virtio_pci_types_register(&virtio_vga_info); } =20 diff --git a/MAINTAINERS b/MAINTAINERS index 093b9cb49b..0ff2e2b38f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1673,7 +1673,7 @@ virtio-gpu M: Gerd Hoffmann S: Maintained F: hw/display/virtio-gpu* -F: hw/display/virtio-vga.c +F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h =20 vhost-user-gpu --=20 2.22.0.rc1.1.g079e7d2849.dirty From nobody Fri Apr 19 02:12:17 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1558704068; cv=none; d=zoho.com; s=zohoarc; b=V9ZeemAevueERIek3/H0EPuVt3+Hpz1jKmmJV28axFc7JUts/o2dmYq23dwMbR6tHjB25O2zmFxp1DlVSePw8bIBRN/MD6lj+03ljMniDkDYy0SPv3Q+gcmn/oTzB/FIRt3PGvBFC26AunbaLeZtjJqQjKYZkB+LwDyBkzMJ6r0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1558704068; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=dmQgVzTOvmwEPyoDiZTNc23Qd8b3EtsPxbrgTXnVVD0=; b=SG0obbPUb4Vbx14ck0HtHSQdH9EbjiId258RE77R5kz4v+fy9ct7plbFPhFD4d+f3scjCMRuNb4GcN1zIPpHJcPfI0MH7coKaLGTqnDj412P/HDQ1kmAC9KdnjAndUkRMPDq2rGpHxFZ5iszIuBvYssmeCQfkHRPG+gtuPJTfcM= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (209.51.188.17 [209.51.188.17]) by mx.zohomail.com with SMTPS id 1558704068901328.84006511884377; Fri, 24 May 2019 06:21:08 -0700 (PDT) Received: from localhost ([127.0.0.1]:54558 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA83-0004ZG-KQ for importer@patchew.org; Fri, 24 May 2019 09:20:59 -0400 Received: from eggs.gnu.org ([209.51.188.92]:58529) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hUA14-0007aO-Ed for qemu-devel@nongnu.org; Fri, 24 May 2019 09:13:50 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hU9yf-0001jG-Kc for qemu-devel@nongnu.org; Fri, 24 May 2019 09:11:20 -0400 Received: from mx1.redhat.com ([209.132.183.28]:63772) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hU9yf-0001iP-Br for qemu-devel@nongnu.org; Fri, 24 May 2019 09:11:17 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id A4F84C0AD2BC for ; Fri, 24 May 2019 13:11:16 +0000 (UTC) Received: from localhost (ovpn-112-23.ams2.redhat.com [10.36.112.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id 96B9D5B2EC; Fri, 24 May 2019 13:11:15 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 24 May 2019 15:09:46 +0200 Message-Id: <20190524130946.31736-10-marcandre.lureau@redhat.com> In-Reply-To: <20190524130946.31736-1-marcandre.lureau@redhat.com> References: <20190524130946.31736-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Fri, 24 May 2019 13:11:16 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v9 9/9] hw/display: add vhost-user-vga & gpu-pci X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add new virtio-gpu devices with a "vhost-user" property. The associated vhost-user backend is used to handle the virtio rings and provide rendering results thanks to the vhost-user-gpu protocol. Example usage: -object vhost-user-backend,id=3Dvug,cmd=3D"./vhost-user-gpu" -device vhost-user-vga,vhost-user=3Dvug Signed-off-by: Marc-Andr=C3=A9 Lureau --- hw/display/virtio-vga.h | 2 +- include/hw/virtio/virtio-gpu-pci.h | 40 ++ include/hw/virtio/virtio-gpu.h | 19 +- hw/display/vhost-user-gpu-pci.c | 51 +++ hw/display/vhost-user-gpu.c | 607 +++++++++++++++++++++++++++++ hw/display/vhost-user-vga.c | 52 +++ hw/display/virtio-gpu-pci.c | 17 +- vl.c | 1 + hw/display/Kconfig | 10 + hw/display/Makefile.objs | 3 + 10 files changed, 779 insertions(+), 23 deletions(-) create mode 100644 include/hw/virtio/virtio-gpu-pci.h create mode 100644 hw/display/vhost-user-gpu-pci.c create mode 100644 hw/display/vhost-user-gpu.c create mode 100644 hw/display/vhost-user-vga.c diff --git a/hw/display/virtio-vga.h b/hw/display/virtio-vga.h index f03e1ba619..c10bf390aa 100644 --- a/hw/display/virtio-vga.h +++ b/hw/display/virtio-vga.h @@ -1,7 +1,7 @@ #ifndef VIRTIO_VGA_H_ #define VIRTIO_VGA_H_ =20 -#include "hw/virtio/virtio-pci.h" +#include "hw/virtio/virtio-gpu-pci.h" #include "vga_int.h" =20 /* diff --git a/include/hw/virtio/virtio-gpu-pci.h b/include/hw/virtio/virtio-= gpu-pci.h new file mode 100644 index 0000000000..2f69b5a9cc --- /dev/null +++ b/include/hw/virtio/virtio-gpu-pci.h @@ -0,0 +1,40 @@ +/* + * Virtio GPU PCI Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_VIRTIO_GPU_PCI_H +#define HW_VIRTIO_GPU_PCI_H + +#include "hw/virtio/virtio-pci.h" +#include "hw/virtio/virtio-gpu.h" + +typedef struct VirtIOGPUPCIBase VirtIOGPUPCIBase; + +/* + * virtio-gpu-pci-base: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_GPU_PCI_BASE "virtio-gpu-pci-base" +#define VIRTIO_GPU_PCI_BASE(obj) \ + OBJECT_CHECK(VirtIOGPUPCIBase, (obj), TYPE_VIRTIO_GPU_PCI_BASE) + +struct VirtIOGPUPCIBase { + VirtIOPCIProxy parent_obj; + VirtIOGPUBase *vgpu; +}; + +/* to share between PCI and VGA */ +#define DEFINE_VIRTIO_GPU_PCI_PROPERTIES(_state) \ + DEFINE_PROP_BIT("ioeventfd", _state, flags, \ + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), \ + DEFINE_PROP_UINT32("vectors", _state, nvectors, 3) + +#endif /* HW_VIRTIO_GPU_PCI_H */ diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index d0d8d7b246..8ecac1987a 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -19,6 +19,7 @@ #include "ui/console.h" #include "hw/virtio/virtio.h" #include "qemu/log.h" +#include "sysemu/vhost-user-backend.h" =20 #include "standard-headers/linux/virtio_gpu.h" =20 @@ -34,6 +35,8 @@ #define VIRTIO_GPU(obj) \ OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU) =20 +#define TYPE_VHOST_USER_GPU "vhost-user-gpu" + #define VIRTIO_ID_GPU 16 =20 struct virtio_gpu_simple_resource { @@ -157,13 +160,17 @@ typedef struct VirtIOGPU { } stats; } VirtIOGPU; =20 -extern const GraphicHwOps virtio_gpu_ops; +typedef struct VhostUserGPU { + VirtIOGPUBase parent_obj; =20 -/* to share between PCI and VGA */ -#define DEFINE_VIRTIO_GPU_PCI_PROPERTIES(_state) \ - DEFINE_PROP_BIT("ioeventfd", _state, flags, \ - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), \ - DEFINE_PROP_UINT32("vectors", _state, nvectors, 3) + VhostUserBackend *vhost; + int vhost_gpu_fd; /* closed by the chardev */ + CharBackend vhost_chr; + QemuDmaBuf dmabuf[VIRTIO_GPU_MAX_SCANOUTS]; + bool backend_blocked; +} VhostUserGPU; + +extern const GraphicHwOps virtio_gpu_ops; =20 #define VIRTIO_GPU_FILL_CMD(out) do { \ size_t s; \ diff --git a/hw/display/vhost-user-gpu-pci.c b/hw/display/vhost-user-gpu-pc= i.c new file mode 100644 index 0000000000..7d9b1f5a8c --- /dev/null +++ b/hw/display/vhost-user-gpu-pci.c @@ -0,0 +1,51 @@ +/* + * vhost-user GPU PCI device + * + * Copyright Red Hat, Inc. 2018 + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/virtio/virtio-gpu-pci.h" + +#define TYPE_VHOST_USER_GPU_PCI "vhost-user-gpu-pci" +#define VHOST_USER_GPU_PCI(obj) \ + OBJECT_CHECK(VhostUserGPUPCI, (obj), TYPE_VHOST_USER_GPU_PCI) + +typedef struct VhostUserGPUPCI { + VirtIOGPUPCIBase parent_obj; + + VhostUserGPU vdev; +} VhostUserGPUPCI; + +static void vhost_user_gpu_pci_initfn(Object *obj) +{ + VhostUserGPUPCI *dev =3D VHOST_USER_GPU_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_GPU); + + VIRTIO_GPU_PCI_BASE(obj)->vgpu =3D VIRTIO_GPU_BASE(&dev->vdev); + + object_property_add_alias(obj, "chardev", + OBJECT(&dev->vdev), "chardev", + &error_abort); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_gpu_pci_info =3D { + .generic_name =3D TYPE_VHOST_USER_GPU_PCI, + .parent =3D TYPE_VIRTIO_GPU_PCI_BASE, + .instance_size =3D sizeof(VhostUserGPUPCI), + .instance_init =3D vhost_user_gpu_pci_initfn, +}; + +static void vhost_user_gpu_pci_register_types(void) +{ + virtio_pci_types_register(&vhost_user_gpu_pci_info); +} + +type_init(vhost_user_gpu_pci_register_types) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c new file mode 100644 index 0000000000..7181d9cdba --- /dev/null +++ b/hw/display/vhost-user-gpu.c @@ -0,0 +1,607 @@ +/* + * vhost-user GPU Device + * + * Copyright Red Hat, Inc. 2018 + * + * Authors: + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio-gpu.h" +#include "chardev/char-fe.h" +#include "qapi/error.h" +#include "migration/blocker.h" + +#define VHOST_USER_GPU(obj) \ + OBJECT_CHECK(VhostUserGPU, (obj), TYPE_VHOST_USER_GPU) + +typedef enum VhostUserGpuRequest { + VHOST_USER_GPU_NONE =3D 0, + VHOST_USER_GPU_GET_PROTOCOL_FEATURES, + VHOST_USER_GPU_SET_PROTOCOL_FEATURES, + VHOST_USER_GPU_GET_DISPLAY_INFO, + VHOST_USER_GPU_CURSOR_POS, + VHOST_USER_GPU_CURSOR_POS_HIDE, + VHOST_USER_GPU_CURSOR_UPDATE, + VHOST_USER_GPU_SCANOUT, + VHOST_USER_GPU_UPDATE, + VHOST_USER_GPU_DMABUF_SCANOUT, + VHOST_USER_GPU_DMABUF_UPDATE, +} VhostUserGpuRequest; + +typedef struct VhostUserGpuDisplayInfoReply { + struct virtio_gpu_resp_display_info info; +} VhostUserGpuDisplayInfoReply; + +typedef struct VhostUserGpuCursorPos { + uint32_t scanout_id; + uint32_t x; + uint32_t y; +} QEMU_PACKED VhostUserGpuCursorPos; + +typedef struct VhostUserGpuCursorUpdate { + VhostUserGpuCursorPos pos; + uint32_t hot_x; + uint32_t hot_y; + uint32_t data[64 * 64]; +} QEMU_PACKED VhostUserGpuCursorUpdate; + +typedef struct VhostUserGpuScanout { + uint32_t scanout_id; + uint32_t width; + uint32_t height; +} QEMU_PACKED VhostUserGpuScanout; + +typedef struct VhostUserGpuUpdate { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint8_t data[]; +} QEMU_PACKED VhostUserGpuUpdate; + +typedef struct VhostUserGpuDMABUFScanout { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint32_t fd_width; + uint32_t fd_height; + uint32_t fd_stride; + uint32_t fd_flags; + int fd_drm_fourcc; +} QEMU_PACKED VhostUserGpuDMABUFScanout; + +typedef struct VhostUserGpuMsg { + uint32_t request; /* VhostUserGpuRequest */ + uint32_t flags; + uint32_t size; /* the following payload size */ + union { + VhostUserGpuCursorPos cursor_pos; + VhostUserGpuCursorUpdate cursor_update; + VhostUserGpuScanout scanout; + VhostUserGpuUpdate update; + VhostUserGpuDMABUFScanout dmabuf_scanout; + struct virtio_gpu_resp_display_info display_info; + uint64_t u64; + } payload; +} QEMU_PACKED VhostUserGpuMsg; + +static VhostUserGpuMsg m __attribute__ ((unused)); +#define VHOST_USER_GPU_HDR_SIZE \ + (sizeof(m.request) + sizeof(m.size) + sizeof(m.flags)) + +#define VHOST_USER_GPU_MSG_FLAG_REPLY 0x4 + +static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked); + +static void +vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg) +{ + VhostUserGpuCursorPos *pos =3D &msg->payload.cursor_pos; + struct virtio_gpu_scanout *s; + + if (pos->scanout_id >=3D g->parent_obj.conf.max_outputs) { + return; + } + s =3D &g->parent_obj.scanout[pos->scanout_id]; + + if (msg->request =3D=3D VHOST_USER_GPU_CURSOR_UPDATE) { + VhostUserGpuCursorUpdate *up =3D &msg->payload.cursor_update; + if (!s->current_cursor) { + s->current_cursor =3D cursor_alloc(64, 64); + } + + s->current_cursor->hot_x =3D up->hot_x; + s->current_cursor->hot_y =3D up->hot_y; + + memcpy(s->current_cursor->data, up->data, + 64 * 64 * sizeof(uint32_t)); + + dpy_cursor_define(s->con, s->current_cursor); + } + + dpy_mouse_set(s->con, pos->x, pos->y, + msg->request !=3D VHOST_USER_GPU_CURSOR_POS_HIDE); +} + +static void +vhost_user_gpu_send_msg(VhostUserGPU *g, const VhostUserGpuMsg *msg) +{ + qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)msg, + VHOST_USER_GPU_HDR_SIZE + msg->size); +} + +static void +vhost_user_gpu_unblock(VhostUserGPU *g) +{ + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_DMABUF_UPDATE, + .flags =3D VHOST_USER_GPU_MSG_FLAG_REPLY, + }; + + vhost_user_gpu_send_msg(g, &msg); +} + +static void +vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) +{ + QemuConsole *con =3D NULL; + struct virtio_gpu_scanout *s; + + switch (msg->request) { + case VHOST_USER_GPU_GET_PROTOCOL_FEATURES: { + VhostUserGpuMsg reply =3D { + .request =3D msg->request, + .flags =3D VHOST_USER_GPU_MSG_FLAG_REPLY, + .size =3D sizeof(uint64_t), + }; + + vhost_user_gpu_send_msg(g, &reply); + break; + } + case VHOST_USER_GPU_SET_PROTOCOL_FEATURES: { + break; + } + case VHOST_USER_GPU_GET_DISPLAY_INFO: { + struct virtio_gpu_resp_display_info display_info =3D { {} }; + VhostUserGpuMsg reply =3D { + .request =3D msg->request, + .flags =3D VHOST_USER_GPU_MSG_FLAG_REPLY, + .size =3D sizeof(struct virtio_gpu_resp_display_info), + }; + + display_info.hdr.type =3D VIRTIO_GPU_RESP_OK_DISPLAY_INFO; + virtio_gpu_base_fill_display_info(VIRTIO_GPU_BASE(g), &display_inf= o); + memcpy(&reply.payload.display_info, &display_info, + sizeof(display_info)); + vhost_user_gpu_send_msg(g, &reply); + break; + } + case VHOST_USER_GPU_SCANOUT: { + VhostUserGpuScanout *m =3D &msg->payload.scanout; + + if (m->scanout_id >=3D g->parent_obj.conf.max_outputs) { + return; + } + + g->parent_obj.enable =3D 1; + s =3D &g->parent_obj.scanout[m->scanout_id]; + con =3D s->con; + + if (m->scanout_id =3D=3D 0 && m->width =3D=3D 0) { + s->ds =3D qemu_create_message_surface(640, 480, + "Guest disabled display."); + dpy_gfx_replace_surface(con, s->ds); + } else { + s->ds =3D qemu_create_displaysurface(m->width, m->height); + /* replace surface on next update */ + } + + break; + } + case VHOST_USER_GPU_DMABUF_SCANOUT: { + VhostUserGpuDMABUFScanout *m =3D &msg->payload.dmabuf_scanout; + int fd =3D qemu_chr_fe_get_msgfd(&g->vhost_chr); + QemuDmaBuf *dmabuf; + + if (m->scanout_id >=3D g->parent_obj.conf.max_outputs) { + error_report("invalid scanout: %d", m->scanout_id); + if (fd >=3D 0) { + close(fd); + } + break; + } + + g->parent_obj.enable =3D 1; + con =3D g->parent_obj.scanout[m->scanout_id].con; + dmabuf =3D &g->dmabuf[m->scanout_id]; + if (dmabuf->fd >=3D 0) { + close(dmabuf->fd); + dmabuf->fd =3D -1; + } + if (!console_has_gl_dmabuf(con)) { + /* it would be nice to report that error earlier */ + error_report("console doesn't support dmabuf!"); + break; + } + dpy_gl_release_dmabuf(con, dmabuf); + if (fd =3D=3D -1) { + dpy_gl_scanout_disable(con); + break; + } + *dmabuf =3D (QemuDmaBuf) { + .fd =3D fd, + .width =3D m->fd_width, + .height =3D m->fd_height, + .stride =3D m->fd_stride, + .fourcc =3D m->fd_drm_fourcc, + .y0_top =3D m->fd_flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP, + }; + dpy_gl_scanout_dmabuf(con, dmabuf); + break; + } + case VHOST_USER_GPU_DMABUF_UPDATE: { + VhostUserGpuUpdate *m =3D &msg->payload.update; + + if (m->scanout_id >=3D g->parent_obj.conf.max_outputs || + !g->parent_obj.scanout[m->scanout_id].con) { + error_report("invalid scanout update: %d", m->scanout_id); + vhost_user_gpu_unblock(g); + break; + } + + con =3D g->parent_obj.scanout[m->scanout_id].con; + if (!console_has_gl(con)) { + error_report("console doesn't support GL!"); + vhost_user_gpu_unblock(g); + break; + } + dpy_gl_update(con, m->x, m->y, m->width, m->height); + g->backend_blocked =3D true; + break; + } + case VHOST_USER_GPU_UPDATE: { + VhostUserGpuUpdate *m =3D &msg->payload.update; + + if (m->scanout_id >=3D g->parent_obj.conf.max_outputs) { + break; + } + s =3D &g->parent_obj.scanout[m->scanout_id]; + con =3D s->con; + pixman_image_t *image =3D + pixman_image_create_bits(PIXMAN_x8r8g8b8, + m->width, + m->height, + (uint32_t *)m->data, + m->width * 4); + + pixman_image_composite(PIXMAN_OP_SRC, + image, NULL, s->ds->image, + 0, 0, 0, 0, m->x, m->y, m->width, m->height= ); + + pixman_image_unref(image); + if (qemu_console_surface(con) !=3D s->ds) { + dpy_gfx_replace_surface(con, s->ds); + } else { + dpy_gfx_update(con, m->x, m->y, m->width, m->height); + } + break; + } + default: + g_warning("unhandled message %d %d", msg->request, msg->size); + } + + if (con && qemu_console_is_gl_blocked(con)) { + vhost_user_gpu_update_blocked(g, true); + } +} + +static void +vhost_user_gpu_chr_read(void *opaque) +{ + VhostUserGPU *g =3D opaque; + VhostUserGpuMsg *msg =3D NULL; + VhostUserGpuRequest request; + uint32_t size, flags; + int r; + + r =3D qemu_chr_fe_read_all(&g->vhost_chr, + (uint8_t *)&request, sizeof(uint32_t)); + if (r !=3D sizeof(uint32_t)) { + error_report("failed to read msg header: %d, %d", r, errno); + goto end; + } + + r =3D qemu_chr_fe_read_all(&g->vhost_chr, + (uint8_t *)&flags, sizeof(uint32_t)); + if (r !=3D sizeof(uint32_t)) { + error_report("failed to read msg flags"); + goto end; + } + + r =3D qemu_chr_fe_read_all(&g->vhost_chr, + (uint8_t *)&size, sizeof(uint32_t)); + if (r !=3D sizeof(uint32_t)) { + error_report("failed to read msg size"); + goto end; + } + + msg =3D g_malloc(VHOST_USER_GPU_HDR_SIZE + size); + g_return_if_fail(msg !=3D NULL); + + r =3D qemu_chr_fe_read_all(&g->vhost_chr, + (uint8_t *)&msg->payload, size); + if (r !=3D size) { + error_report("failed to read msg payload %d !=3D %d", r, size); + goto end; + } + + msg->request =3D request; + msg->flags =3D size; + msg->size =3D size; + + if (request =3D=3D VHOST_USER_GPU_CURSOR_UPDATE || + request =3D=3D VHOST_USER_GPU_CURSOR_POS || + request =3D=3D VHOST_USER_GPU_CURSOR_POS_HIDE) { + vhost_user_gpu_handle_cursor(g, msg); + } else { + vhost_user_gpu_handle_display(g, msg); + } + +end: + g_free(msg); +} + +static void +vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked) +{ + qemu_set_fd_handler(g->vhost_gpu_fd, + blocked ? NULL : vhost_user_gpu_chr_read, NULL, g); +} + +static void +vhost_user_gpu_gl_unblock(VirtIOGPUBase *b) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(b); + + if (g->backend_blocked) { + vhost_user_gpu_unblock(VHOST_USER_GPU(g)); + g->backend_blocked =3D false; + } + + vhost_user_gpu_update_blocked(VHOST_USER_GPU(g), false); +} + +static bool +vhost_user_gpu_do_set_socket(VhostUserGPU *g, Error **errp) +{ + Chardev *chr; + int sv[2]; + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) =3D=3D -1) { + error_setg_errno(errp, errno, "socketpair() failed"); + return false; + } + + chr =3D CHARDEV(object_new(TYPE_CHARDEV_SOCKET)); + if (!chr || qemu_chr_add_client(chr, sv[0]) =3D=3D -1) { + error_setg(errp, "Failed to make socket chardev"); + goto err; + } + if (!qemu_chr_fe_init(&g->vhost_chr, chr, errp)) { + goto err; + } + if (vhost_user_gpu_set_socket(&g->vhost->dev, sv[1]) < 0) { + error_setg(errp, "Failed to set vhost-user-gpu socket"); + qemu_chr_fe_deinit(&g->vhost_chr, false); + goto err; + } + + g->vhost_gpu_fd =3D sv[0]; + vhost_user_gpu_update_blocked(g, false); + close(sv[1]); + return true; + +err: + close(sv[0]); + close(sv[1]); + if (chr) { + object_unref(OBJECT(chr)); + } + return false; +} + +static void +vhost_user_gpu_get_config(VirtIODevice *vdev, uint8_t *config_data) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + VirtIOGPUBase *b =3D VIRTIO_GPU_BASE(vdev); + struct virtio_gpu_config *vgconfig =3D + (struct virtio_gpu_config *)config_data; + int ret; + + memset(config_data, 0, sizeof(struct virtio_gpu_config)); + + ret =3D vhost_dev_get_config(&g->vhost->dev, + config_data, sizeof(struct virtio_gpu_confi= g)); + if (ret) { + error_report("vhost-user-gpu: get device config space failed"); + return; + } + + /* those fields are managed by qemu */ + vgconfig->num_scanouts =3D b->virtio_config.num_scanouts; + vgconfig->events_read =3D b->virtio_config.events_read; + vgconfig->events_clear =3D b->virtio_config.events_clear; +} + +static void +vhost_user_gpu_set_config(VirtIODevice *vdev, + const uint8_t *config_data) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + VirtIOGPUBase *b =3D VIRTIO_GPU_BASE(vdev); + const struct virtio_gpu_config *vgconfig =3D + (const struct virtio_gpu_config *)config_data; + int ret; + + if (vgconfig->events_clear) { + b->virtio_config.events_read &=3D ~vgconfig->events_clear; + } + + ret =3D vhost_dev_set_config(&g->vhost->dev, config_data, + 0, sizeof(struct virtio_gpu_config), + VHOST_SET_CONFIG_TYPE_MASTER); + if (ret) { + error_report("vhost-user-gpu: set device config space failed"); + return; + } +} + +static void +vhost_user_gpu_set_status(VirtIODevice *vdev, uint8_t val) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + Error *err =3D NULL; + + if (val & VIRTIO_CONFIG_S_DRIVER_OK && vdev->vm_running) { + if (!vhost_user_gpu_do_set_socket(g, &err)) { + error_report_err(err); + return; + } + vhost_user_backend_start(g->vhost); + } else { + /* unblock any wait and stop processing */ + if (g->vhost_gpu_fd !=3D -1) { + vhost_user_gpu_update_blocked(g, true); + qemu_chr_fe_deinit(&g->vhost_chr, true); + g->vhost_gpu_fd =3D -1; + } + vhost_user_backend_stop(g->vhost); + } +} + +static bool +vhost_user_gpu_guest_notifier_pending(VirtIODevice *vdev, int idx) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + + return vhost_virtqueue_pending(&g->vhost->dev, idx); +} + +static void +vhost_user_gpu_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + + vhost_virtqueue_mask(&g->vhost->dev, vdev, idx, mask); +} + +static void +vhost_user_gpu_instance_init(Object *obj) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(obj); + + g->vhost =3D VHOST_USER_BACKEND(object_new(TYPE_VHOST_USER_BACKEND)); + object_property_add_alias(obj, "chardev", + OBJECT(g->vhost), "chardev", &error_abort); +} + +static void +vhost_user_gpu_instance_finalize(Object *obj) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(obj); + + object_unref(OBJECT(g->vhost)); +} + +static void +vhost_user_gpu_reset(VirtIODevice *vdev) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + + virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev)); + + vhost_user_backend_stop(g->vhost); +} + +static int +vhost_user_gpu_config_change(struct vhost_dev *dev) +{ + error_report("vhost-user-gpu: unhandled backend config change"); + return -1; +} + +static const VhostDevConfigOps config_ops =3D { + .vhost_dev_config_notifier =3D vhost_user_gpu_config_change, +}; + +static void +vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(qdev); + VirtIODevice *vdev =3D VIRTIO_DEVICE(g); + + vhost_dev_set_config_notifier(&g->vhost->dev, &config_ops); + if (vhost_user_backend_dev_init(g->vhost, vdev, 2, errp) < 0) { + return; + } + + if (virtio_has_feature(g->vhost->dev.features, VIRTIO_GPU_F_VIRGL)) { + g->parent_obj.conf.flags |=3D 1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED; + } + + if (!virtio_gpu_base_device_realize(qdev, NULL, NULL, errp)) { + return; + } + + g->vhost_gpu_fd =3D -1; +} + +static Property vhost_user_gpu_properties[] =3D { + VIRTIO_GPU_BASE_PROPERTIES(VhostUserGPU, parent_obj.conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void +vhost_user_gpu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + VirtioDeviceClass *vdc =3D VIRTIO_DEVICE_CLASS(klass); + VirtIOGPUBaseClass *vgc =3D VIRTIO_GPU_BASE_CLASS(klass); + + vgc->gl_unblock =3D vhost_user_gpu_gl_unblock; + + vdc->realize =3D vhost_user_gpu_device_realize; + vdc->reset =3D vhost_user_gpu_reset; + vdc->set_status =3D vhost_user_gpu_set_status; + vdc->guest_notifier_mask =3D vhost_user_gpu_guest_notifier_mask; + vdc->guest_notifier_pending =3D vhost_user_gpu_guest_notifier_pending; + vdc->get_config =3D vhost_user_gpu_get_config; + vdc->set_config =3D vhost_user_gpu_set_config; + + dc->props =3D vhost_user_gpu_properties; +} + +static const TypeInfo vhost_user_gpu_info =3D { + .name =3D TYPE_VHOST_USER_GPU, + .parent =3D TYPE_VIRTIO_GPU_BASE, + .instance_size =3D sizeof(VhostUserGPU), + .instance_init =3D vhost_user_gpu_instance_init, + .instance_finalize =3D vhost_user_gpu_instance_finalize, + .class_init =3D vhost_user_gpu_class_init, +}; + +static void vhost_user_gpu_register_types(void) +{ + type_register_static(&vhost_user_gpu_info); +} + +type_init(vhost_user_gpu_register_types) diff --git a/hw/display/vhost-user-vga.c b/hw/display/vhost-user-vga.c new file mode 100644 index 0000000000..a7195276d9 --- /dev/null +++ b/hw/display/vhost-user-vga.c @@ -0,0 +1,52 @@ +/* + * vhost-user VGA device + * + * Copyright Red Hat, Inc. 2018 + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "virtio-vga.h" + +#define TYPE_VHOST_USER_VGA "vhost-user-vga" + +#define VHOST_USER_VGA(obj) \ + OBJECT_CHECK(VhostUserVGA, (obj), TYPE_VHOST_USER_VGA) + +typedef struct VhostUserVGA { + VirtIOVGABase parent_obj; + + VhostUserGPU vdev; +} VhostUserVGA; + +static void vhost_user_vga_inst_initfn(Object *obj) +{ + VhostUserVGA *dev =3D VHOST_USER_VGA(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_GPU); + + VIRTIO_VGA_BASE(dev)->vgpu =3D VIRTIO_GPU_BASE(&dev->vdev); + + object_property_add_alias(obj, "chardev", + OBJECT(&dev->vdev), "chardev", + &error_abort); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_vga_info =3D { + .generic_name =3D TYPE_VHOST_USER_VGA, + .parent =3D TYPE_VIRTIO_VGA_BASE, + .instance_size =3D sizeof(struct VhostUserVGA), + .instance_init =3D vhost_user_vga_inst_initfn, +}; + +static void vhost_user_vga_register_types(void) +{ + virtio_pci_types_register(&vhost_user_vga_info); +} + +type_init(vhost_user_vga_register_types) diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index d03cadd236..206870cd4c 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -16,22 +16,7 @@ #include "hw/pci/pci.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-pci.h" -#include "hw/virtio/virtio-gpu.h" - -typedef struct VirtIOGPUPCIBase VirtIOGPUPCIBase; - -/* - * virtio-gpu-pci-base: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_GPU_PCI_BASE "virtio-gpu-pci-base" -#define VIRTIO_GPU_PCI_BASE(obj) \ - OBJECT_CHECK(VirtIOGPUPCIBase, (obj), TYPE_VIRTIO_GPU_PCI_BASE) - -struct VirtIOGPUPCIBase { - VirtIOPCIProxy parent_obj; - VirtIOGPUBase *vgpu; -}; +#include "hw/virtio/virtio-gpu-pci.h" =20 static Property virtio_gpu_pci_base_properties[] =3D { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), diff --git a/vl.c b/vl.c index 5550bd7693..37618ee8a4 100644 --- a/vl.c +++ b/vl.c @@ -239,6 +239,7 @@ static struct { { .driver =3D "qxl-vga", .flag =3D &default_vga }, { .driver =3D "virtio-vga", .flag =3D &default_vga }, { .driver =3D "ati-vga", .flag =3D &default_vga }, + { .driver =3D "vhost-user-vga", .flag =3D &default_vga }, }; =20 static QemuOptsList qemu_rtc_opts =3D { diff --git a/hw/display/Kconfig b/hw/display/Kconfig index dc1f113df2..910dccb2f7 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -111,6 +111,16 @@ config VIRTIO_VGA depends on VIRTIO_PCI select VGA =20 +config VHOST_USER_GPU + bool + default y + depends on VIRTIO_GPU && VHOST_USER + +config VHOST_USER_VGA + bool + default y + depends on VIRTIO_VGA && VHOST_USER_GPU + config DPCD bool select AUX diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 22e0374df2..a64998fc7b 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -44,8 +44,11 @@ obj-$(CONFIG_VGA) +=3D vga.o common-obj-$(CONFIG_QXL) +=3D qxl.o qxl-logger.o qxl-render.o =20 obj-$(CONFIG_VIRTIO_GPU) +=3D virtio-gpu-base.o virtio-gpu.o virtio-gpu-3d= .o +obj-$(CONFIG_VHOST_USER_GPU) +=3D vhost-user-gpu.o obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) +=3D virtio-gpu= -pci.o +obj-$(call land,$(CONFIG_VHOST_USER_GPU),$(CONFIG_VIRTIO_PCI)) +=3D vhost-= user-gpu-pci.o obj-$(CONFIG_VIRTIO_VGA) +=3D virtio-vga.o +obj-$(CONFIG_VHOST_USER_VGA) +=3D vhost-user-vga.o virtio-gpu.o-cflags :=3D $(VIRGL_CFLAGS) virtio-gpu.o-libs +=3D $(VIRGL_LIBS) virtio-gpu-3d.o-cflags :=3D $(VIRGL_CFLAGS) --=20 2.22.0.rc1.1.g079e7d2849.dirty