From nobody Sat Apr 20 03:45:11 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=1555083563; cv=none; d=zoho.com; s=zohoarc; b=IrV8Y7KS4yP3zf7kfq4HZACIi0z+yspDOFFzmISvusVoCma6BDoCWjVjTRWsJJY97m4RYcTjfmh+qRAe+7qQP1h4EI6DzRQuHlWwRYDRJhPeOum9cC+HDAZpBv4RfQEuABXArJqsk0qfpvwl97gAQCmluPUzUdoQbzWaqfcAlgQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555083563; h=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=t96REWOdmurh5kJXSLvrWZEa0cus0uuFqb4FpM0GJ4M=; b=L51nCnQiiK0bPiosoivrWUlyJyy/zWcbPtnhLfhsQrIr4wi+OpihvnZ/PNOEqhjMZ4P97RrBDXOWk86Kx0Rqhs3DjNoaL629trxmkipt6PKCbFUS4/2lQlPBqcwScamRRAMLJ73VvuJu2XKOY9xyguqL3OWAS79ODtpPV/BGVSs= 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 1555083563336809.6115051198424; Fri, 12 Apr 2019 08:39:23 -0700 (PDT) Received: from localhost ([127.0.0.1]:38806 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyGt-0001F7-60 for importer@patchew.org; Fri, 12 Apr 2019 11:39:19 -0400 Received: from eggs.gnu.org ([209.51.188.92]:36704) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyF0-0000Dj-GD for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyEv-0001RO-1L for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:19 -0400 Received: from mx1.redhat.com ([209.132.183.28]:54354) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyEn-0001Nw-03; Fri, 12 Apr 2019 11:37:11 -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 A4FAA308FC20; Fri, 12 Apr 2019 15:37:04 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id C2641608BB; Fri, 12 Apr 2019 15:36:57 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:35 +0200 Message-Id: <20190412153647.19027-2-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-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.43]); Fri, 12 Apr 2019 15:37:04 +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 v5 01/13] Update version for v4.0.0-rc3 release 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: Peter Maydell , "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" From: Peter Maydell Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 5b9105c989..859ede923f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.1.92 +3.1.93 --=20 2.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555084005; cv=none; d=zoho.com; s=zohoarc; b=CViiWz7ODrwYVDDrIPrFQNOFLTEaFRrPkUS53VQVKroxYecAVrsD5sTjcZoDb+pK/Uw+m5ObRBoomflKEJkANxgXaTNBWkQnwdNjkKSA+shyeumccKZHkIlNAfH9AqAGPm8EhlRCnXPA8g9O+lhdTNaMzhu3Wot2M56fsjQvu+8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555084005; h=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=RjXMemKILClK0NCOUnGEB1uEW53q6QPyGkKS7a2+y9o=; b=CRLYSod/r3WNScyJg0R2ifZAjEyjp4eNd6VTU6eVZg7LKAkkDd98lhEHQj2evYeJTkecl1sX9q5EEn1ZNCuY09ZH03YlqQT18pBQr5wdIN3Md1VcqFK0ZU0JDFPYa/XmKDU+Me5Yx/PSvgQi+/SPgZEyxMtUWiBkOXGgiFy29bk= 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 1555084005067875.7596844451655; Fri, 12 Apr 2019 08:46:45 -0700 (PDT) Received: from localhost ([127.0.0.1]:38951 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyNz-0000JA-VN for importer@patchew.org; Fri, 12 Apr 2019 11:46:39 -0400 Received: from eggs.gnu.org ([209.51.188.92]:36768) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyF7-0000J5-4f for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:32 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyF5-0001Wh-DO for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:29 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34052) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyF2-0001Qp-6s; Fri, 12 Apr 2019 11:37:25 -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 3CDB2308621C; Fri, 12 Apr 2019 15:37:15 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id B35AD19738; Fri, 12 Apr 2019 15:37:06 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:36 +0200 Message-Id: <20190412153647.19027-3-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-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, 12 Apr 2019 15:37: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 v5 02/13] spapr_pci: Fix broken naming of PCI bus 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: "Michael S. Tsirkin" , Greg Kurz , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" From: Greg Kurz Recent commit 5cf0d326a0fe fixed a regression which was preventing the guest to access the extended config space of a PCIe device. This was done by introducing a new PCI bus subtype for PAPR. The original fix was causing PCI busses to be named "spapr-pci-host-bridge-root-bus.N" instead of "pci.N", which was making upper layers unhappy of course. This got worked around by hardcoding the PCI bus name to "pci.0", but this only works for the default PHB. And we're now hitting: # qemu-system-ppc64 \ -device spapr-pci-host-bridge,index=3D1 \ -device e1000e,bus=3Dpci.0 \ -device e1000e,bus=3Dpci.1 qemu-system-ppc64: -device e1000e,bus=3Dpci.1: Bus 'pci.1' not found David already posted some patches [1] to control PCI extended config space accesses with a new flag in the base PCI bus class instead of subtyping. These patches are a bit more intrusive though, and are targetted for 4.1. When no name is passed to pci_register_bus(), the core device code generates a lowercase name based on the QOM typename. The typename for the base PCI bus class is "PCI", hence the "pci.0", "pci.1" bus names. Rename the type of the PAPR PCI bus to "pci", so that the QOM code can generate proper names. This is a hack but it is enough to fix the regression. And all this will be reworked properly in 4.1. [1] https://patchwork.ozlabs.org/project/qemu-devel/list/?series=3D100486 Fixes: 5cf0d326a0fe Signed-off-by: Greg Kurz Message-Id: <155500034416.646888.1307366522340665522.stgit@bahia.lab.toulou= se-stg.fr.ibm.com> Signed-off-by: David Gibson --- hw/ppc/spapr_pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index f0b6b23afc..f62e6833b8 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1652,7 +1652,7 @@ static void spapr_phb_root_bus_class_init(ObjectClass= *klass, void *data) pbc->allows_extended_config_space =3D spapr_phb_allows_extended_config= _space; } =20 -#define TYPE_SPAPR_PHB_ROOT_BUS "spapr-pci-host-bridge-root-bus" +#define TYPE_SPAPR_PHB_ROOT_BUS "pci" =20 static const TypeInfo spapr_phb_root_bus_info =3D { .name =3D TYPE_SPAPR_PHB_ROOT_BUS, @@ -1761,7 +1761,7 @@ static void spapr_phb_realize(DeviceState *dev, Error= **errp) memory_region_add_subregion(get_system_memory(), sphb->io_win_addr, &sphb->iowindow); =20 - bus =3D pci_register_root_bus(dev, "pci.0", + bus =3D pci_register_root_bus(dev, NULL, pci_spapr_set_irq, pci_spapr_map_irq, sphb, &sphb->memspace, &sphb->iospace, PCI_DEVFN(0, 0), PCI_NUM_PINS, --=20 2.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555083700; cv=none; d=zoho.com; s=zohoarc; b=L88Hd8lRINKRI7SuvEAkxn/nHm3xOmgtw1S7uraSMnVSwZEVVSKz8pCBqGvJcyTpz/KvQwljCUexhQvgJLwT7VBtXpiylaF14uYJZYtSOnJMJ0CpSGLR6oejBsQcwOAXJwzXFM7MduuxVhcMa+jR5zb6mGN2AiLKctilUebQ3Kk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555083700; 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=pbyv3tF5E3KgqffWHmae/vYw0ZuPIYcA8wCjhT0Seec=; b=mBwfk/nx/VnwGfRRvcWDYJTCsw9a+HKGP358yBRbZjLQxjGqdxigbim2+Nq73+nv/y4Fk5JPuQzZHV022p8HRy/dQqlUPmR9FTSeKmYzmT5EAf5PNyuubzUmIuVlWbOj3xgqmnDVBpbMwM6G6wv30dmomaxM1jLdYfxtPfLox/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 1555083700193527.9710954340061; Fri, 12 Apr 2019 08:41:40 -0700 (PDT) Received: from localhost ([127.0.0.1]:38863 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyJ6-0003c6-4T for importer@patchew.org; Fri, 12 Apr 2019 11:41:36 -0400 Received: from eggs.gnu.org ([209.51.188.92]:36761) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyF6-0000Ic-R6 for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyF5-0001XJ-Pn for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:28 -0400 Received: from mx1.redhat.com ([209.132.183.28]:53656) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyF5-0001VU-E0; Fri, 12 Apr 2019 11:37:27 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 34499308219E; Fri, 12 Apr 2019 15:37:24 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id CE1DC5C206; Fri, 12 Apr 2019 15:37:16 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:37 +0200 Message-Id: <20190412153647.19027-4-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.47]); Fri, 12 Apr 2019 15:37:24 +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 v5 03/13] curses: fix wchar_t printf warning 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: Peter Maydell , "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" From: Gerd Hoffmann On some systems wchar_t is "long int", on others just "int". So go cast to "long int" and adjust the printf format accordingly. Reported-by: Mark Cave-Ayland Signed-off-by: Gerd Hoffmann Reviewed-by: Eric Blake Reviewed-by: Philippe Mathieu-Daud=C3=A9 Message-id: 20190402073018.17747-1-kraxel@redhat.com Signed-off-by: Peter Maydell --- ui/curses.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/curses.c b/ui/curses.c index cc6d6da684..fb63945188 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -453,8 +453,8 @@ static uint16_t get_ucs(wchar_t wch, iconv_t conv) swch =3D sizeof(wch); =20 if (iconv(conv, &pwch, &swch, &pch, &sch) =3D=3D (size_t) -1) { - fprintf(stderr, "Could not convert 0x%02x from WCHAR_T to UCS-2: %= s\n", - wch, strerror(errno)); + fprintf(stderr, "Could not convert 0x%02lx from WCHAR_T to UCS-2: = %s\n", + (unsigned long)wch, strerror(errno)); return 0xFFFD; } =20 --=20 2.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555083864; cv=none; d=zoho.com; s=zohoarc; b=kOr9vEptj2vFvn3jQq0aP0GUdpVGmf5ESzpaBs8lTScegApa7ha9sqKmsICQjaGvmYodCAlwcpEUxCHqYemAaNoXKr2PMom7cLhMBFX+jiTsRDdvQjObTAr8FvUmmmTKWcmyX459FLXFInnM2hd8M2VGMSWrjN5trDPAeGhLKZg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555083864; 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=R3VLPxV1RQ1itHFHrrc4FjlLxiwLRdpPpgkASz20ck0=; b=d2H8LBzNzf/LCaAKK+O4YdMjuHrj4V2rmDCeUxHXomPaNeVt+fuPyVFq0J9V01eAe27NBDYiTa/xJbeuwPAY5lAI1ehGnVHKYEHAsGC31BrWQOojxRu0+XWGgbkDNXeGeMkKeSL0O0xKeEj5pOl/XREBZtqFVNEU/NvK+Ot1tC0= 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 1555083864895839.8988272478276; Fri, 12 Apr 2019 08:44:24 -0700 (PDT) Received: from localhost ([127.0.0.1]:38887 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyLe-00068M-J3 for importer@patchew.org; Fri, 12 Apr 2019 11:44:14 -0400 Received: from eggs.gnu.org ([209.51.188.92]:36777) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyF7-0000JR-Iz for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:32 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyF5-0001XQ-RW for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:29 -0400 Received: from mx1.redhat.com ([209.132.183.28]:54474) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyF5-0001WI-Ee; Fri, 12 Apr 2019 11:37:27 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3A4B1308FBB1; Fri, 12 Apr 2019 15:37:26 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id A6D665C206; Fri, 12 Apr 2019 15:37:25 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:38 +0200 Message-Id: <20190412153647.19027-5-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.43]); Fri, 12 Apr 2019 15:37:26 +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 v5 04/13] Add vhost-user-backend 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: "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Create a vhost-user-backend object that holds a connection to a vhost-user backend (or "slave" process) and can be referenced from virtio devices that support it. See later patches for input & gpu usage. Note: a previous iteration of this object made it user-creatable, and allowed managed sub-process spawning, but that has been dropped for now. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/sysemu/vhost-user-backend.h | 57 ++++++++ backends/vhost-user.c | 209 ++++++++++++++++++++++++++++ MAINTAINERS | 2 + backends/Makefile.objs | 2 + 4 files changed, 270 insertions(+) create mode 100644 include/sysemu/vhost-user-backend.h create mode 100644 backends/vhost-user.c diff --git a/include/sysemu/vhost-user-backend.h b/include/sysemu/vhost-use= r-backend.h new file mode 100644 index 0000000000..9abf8f06a1 --- /dev/null +++ b/include/sysemu/vhost-user-backend.h @@ -0,0 +1,57 @@ +/* + * QEMU vhost-user backend + * + * Copyright (C) 2018 Red Hat Inc + * + * 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. + */ +#ifndef QEMU_VHOST_USER_BACKEND_H +#define QEMU_VHOST_USER_BACKEND_H + +#include "qom/object.h" +#include "exec/memory.h" +#include "qemu/option.h" +#include "qemu/bitmap.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "chardev/char-fe.h" +#include "io/channel.h" + +#define TYPE_VHOST_USER_BACKEND "vhost-user-backend" +#define VHOST_USER_BACKEND(obj) \ + OBJECT_CHECK(VhostUserBackend, (obj), TYPE_VHOST_USER_BACKEND) +#define VHOST_USER_BACKEND_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VhostUserBackendClass, (obj), TYPE_VHOST_USER_BACKEND) +#define VHOST_USER_BACKEND_CLASS(klass) \ + OBJECT_CLASS_CHECK(VhostUserBackendClass, (klass), TYPE_VHOST_USER_BAC= KEND) + +typedef struct VhostUserBackend VhostUserBackend; +typedef struct VhostUserBackendClass VhostUserBackendClass; + +struct VhostUserBackendClass { + ObjectClass parent_class; +}; + +struct VhostUserBackend { + /* private */ + Object parent; + + char *chr_name; + CharBackend chr; + VhostUserState vhost_user; + struct vhost_dev dev; + VirtIODevice *vdev; + bool started; + bool completed; +}; + +int vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev, + unsigned nvqs, Error **errp); +void vhost_user_backend_start(VhostUserBackend *b); +void vhost_user_backend_stop(VhostUserBackend *b); + +#endif diff --git a/backends/vhost-user.c b/backends/vhost-user.c new file mode 100644 index 0000000000..2b055544a7 --- /dev/null +++ b/backends/vhost-user.c @@ -0,0 +1,209 @@ +/* + * QEMU vhost-user backend + * + * Copyright (C) 2018 Red Hat Inc + * + * 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/qdev.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" +#include "qom/object_interfaces.h" +#include "sysemu/vhost-user-backend.h" +#include "sysemu/kvm.h" +#include "io/channel-command.h" +#include "hw/virtio/virtio-bus.h" + +static bool +ioeventfd_enabled(void) +{ + return kvm_enabled() && kvm_eventfds_enabled(); +} + +int +vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev, + unsigned nvqs, Error **errp) +{ + int ret; + + assert(!b->vdev && vdev); + + if (!ioeventfd_enabled()) { + error_setg(errp, "vhost initialization failed: requires kvm"); + return -1; + } + + if (!vhost_user_init(&b->vhost_user, &b->chr, errp)) { + return -1; + } + + b->vdev =3D vdev; + b->dev.nvqs =3D nvqs; + b->dev.vqs =3D g_new(struct vhost_virtqueue, nvqs); + + ret =3D vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USE= R, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, "vhost initialization failed"); + return -1; + } + + return 0; +} + +void +vhost_user_backend_start(VhostUserBackend *b) +{ + BusState *qbus =3D BUS(qdev_get_parent_bus(DEVICE(b->vdev))); + VirtioBusClass *k =3D VIRTIO_BUS_GET_CLASS(qbus); + int ret, i ; + + if (b->started) { + return; + } + + if (!k->set_guest_notifiers) { + error_report("binding does not support guest notifiers"); + return; + } + + ret =3D vhost_dev_enable_notifiers(&b->dev, b->vdev); + if (ret < 0) { + return; + } + + ret =3D k->set_guest_notifiers(qbus->parent, b->dev.nvqs, true); + if (ret < 0) { + error_report("Error binding guest notifier"); + goto err_host_notifiers; + } + + b->dev.acked_features =3D b->vdev->guest_features; + ret =3D vhost_dev_start(&b->dev, b->vdev); + if (ret < 0) { + error_report("Error start vhost dev"); + goto err_guest_notifiers; + } + + /* guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i =3D 0; i < b->dev.nvqs; i++) { + vhost_virtqueue_mask(&b->dev, b->vdev, + b->dev.vq_index + i, false); + } + + b->started =3D true; + return; + +err_guest_notifiers: + k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false); +err_host_notifiers: + vhost_dev_disable_notifiers(&b->dev, b->vdev); +} + +void +vhost_user_backend_stop(VhostUserBackend *b) +{ + BusState *qbus =3D BUS(qdev_get_parent_bus(DEVICE(b->vdev))); + VirtioBusClass *k =3D VIRTIO_BUS_GET_CLASS(qbus); + int ret =3D 0; + + if (!b->started) { + return; + } + + vhost_dev_stop(&b->dev, b->vdev); + + if (k->set_guest_notifiers) { + ret =3D k->set_guest_notifiers(qbus->parent, + b->dev.nvqs, false); + if (ret < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + } + } + assert(ret >=3D 0); + + vhost_dev_disable_notifiers(&b->dev, b->vdev); + b->started =3D false; +} + +static void set_chardev(Object *obj, const char *value, Error **errp) +{ + VhostUserBackend *b =3D VHOST_USER_BACKEND(obj); + Chardev *chr; + + if (b->completed) { + error_setg(errp, QERR_PERMISSION_DENIED); + return; + } + + g_free(b->chr_name); + b->chr_name =3D g_strdup(value); + + chr =3D qemu_chr_find(b->chr_name); + if (chr =3D=3D NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Chardev '%s' not found", b->chr_name); + return; + } + + if (!qemu_chr_fe_init(&b->chr, chr, errp)) { + return; + } + + b->completed =3D true; + /* could call vhost_dev_init() so early message can be exchanged */ +} + +static char *get_chardev(Object *obj, Error **errp) +{ + VhostUserBackend *b =3D VHOST_USER_BACKEND(obj); + Chardev *chr =3D qemu_chr_fe_get_driver(&b->chr); + + if (chr && chr->label) { + return g_strdup(chr->label); + } + + return NULL; +} + +static void vhost_user_backend_init(Object *obj) +{ + object_property_add_str(obj, "chardev", get_chardev, set_chardev, NULL= ); +} + +static void vhost_user_backend_finalize(Object *obj) +{ + VhostUserBackend *b =3D VHOST_USER_BACKEND(obj); + + g_free(b->dev.vqs); + g_free(b->chr_name); + + vhost_user_cleanup(&b->vhost_user); + qemu_chr_fe_deinit(&b->chr, true); +} + +static const TypeInfo vhost_user_backend_info =3D { + .name =3D TYPE_VHOST_USER_BACKEND, + .parent =3D TYPE_OBJECT, + .instance_size =3D sizeof(VhostUserBackend), + .instance_init =3D vhost_user_backend_init, + .instance_finalize =3D vhost_user_backend_finalize, + .class_size =3D sizeof(VhostUserBackendClass), +}; + +static void register_types(void) +{ + type_register_static(&vhost_user_backend_info); +} + +type_init(register_types); diff --git a/MAINTAINERS b/MAINTAINERS index 56139ac8ab..e0f3f9c4bd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1469,6 +1469,8 @@ F: hw/*/*vhost* F: docs/interop/vhost-user.json F: docs/interop/vhost-user.txt F: contrib/vhost-user-*/ +F: backends/vhost-user.c +F: include/sysemu/vhost-user-backend.h =20 virtio M: Michael S. Tsirkin diff --git a/backends/Makefile.objs b/backends/Makefile.objs index ff619d31b4..981e8e122f 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -14,4 +14,6 @@ common-obj-y +=3D cryptodev-vhost.o common-obj-$(CONFIG_VHOST_CRYPTO) +=3D cryptodev-vhost-user.o endif =20 +common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) +=3D vhost-u= ser.o + common-obj-$(CONFIG_LINUX) +=3D hostmem-memfd.o --=20 2.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555083590; cv=none; d=zoho.com; s=zohoarc; b=iIKz5rvCU/8tHmGNUVJi7YoGezDjU6ziH049ePMsSsrQFKhZWXCG8xOmYfXfuKEL66e5cWlHSz5uADK1gYU9ab03osOgr1MRd0Bi5OAFUHCs5Nudqjquzv/70A2oviOnhDVE1KaD/Mf9rZiJo5+npU8MK9PHNHYMACNKY0I0zK8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555083590; 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=CLOQJdPkkPUOD5giOIouj9ZUYq7yu9ABlG7i2v0jLlI=; b=ZVxWAQ+FqQl7Vdrebk++GgD5n6qOwTzAWOcGCh5fQHk13A1VAa5B6C0bcOsIrBVpaebzBmMAgeXg34FBlegv3DAhAuL2tzGEq0c1rSo3I1CNqkZRnTzdGJ9Z0m6f1b28xbo36HznMBuHgHTr7iJOuXcSeKnK+JbxGK4qiWLSI8s= 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 1555083590989803.9110080946816; Fri, 12 Apr 2019 08:39:50 -0700 (PDT) Received: from localhost ([127.0.0.1]:38808 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyHL-0001gI-TP for importer@patchew.org; Fri, 12 Apr 2019 11:39:47 -0400 Received: from eggs.gnu.org ([209.51.188.92]:36895) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyFH-0000R4-4O for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:40 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyFF-0001cW-Fs for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:39 -0400 Received: from mx1.redhat.com ([209.132.183.28]:44926) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyFF-0001c8-5x; Fri, 12 Apr 2019 11:37:37 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 75E4319D2BD; Fri, 12 Apr 2019 15:37:36 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id F31926013D; Fri, 12 Apr 2019 15:37:27 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:39 +0200 Message-Id: <20190412153647.19027-6-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.29]); Fri, 12 Apr 2019 15:37: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 v5 05/13] Add vhost-user-input-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: "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a new virtio-input device, which connects to a vhost-user backend. Instead of reading configuration directly from an input device / evdev (like virtio-input-host), it reads it over vhost-user protocol with {SET,GET}_CONFIG messages. The vhost-user-backend handles the queues & events setup. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/hw/virtio/virtio-input.h | 14 ++++ hw/input/vhost-user-input.c | 129 +++++++++++++++++++++++++++++++ hw/virtio/vhost-user-input-pci.c | 53 +++++++++++++ MAINTAINERS | 1 + hw/input/Kconfig | 5 ++ hw/input/Makefile.objs | 1 + hw/virtio/Makefile.objs | 1 + 7 files changed, 204 insertions(+) create mode 100644 hw/input/vhost-user-input.c create mode 100644 hw/virtio/vhost-user-input-pci.c diff --git a/include/hw/virtio/virtio-input.h b/include/hw/virtio/virtio-in= put.h index 054c38836f..4fca03e796 100644 --- a/include/hw/virtio/virtio-input.h +++ b/include/hw/virtio/virtio-input.h @@ -2,6 +2,7 @@ #define QEMU_VIRTIO_INPUT_H =20 #include "ui/input.h" +#include "sysemu/vhost-user-backend.h" =20 /* ----------------------------------------------------------------- */ /* virtio input protocol */ @@ -42,11 +43,18 @@ typedef struct virtio_input_event virtio_input_event; #define VIRTIO_INPUT_HOST_GET_PARENT_CLASS(obj) \ OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_INPUT_HOST) =20 +#define TYPE_VHOST_USER_INPUT "vhost-user-input" +#define VHOST_USER_INPUT(obj) \ + OBJECT_CHECK(VHostUserInput, (obj), TYPE_VHOST_USER_INPUT) +#define VHOST_USER_INPUT_GET_PARENT_CLASS(obj) \ + OBJECT_GET_PARENT_CLASS(obj, TYPE_VHOST_USER_INPUT) + typedef struct VirtIOInput VirtIOInput; typedef struct VirtIOInputClass VirtIOInputClass; typedef struct VirtIOInputConfig VirtIOInputConfig; typedef struct VirtIOInputHID VirtIOInputHID; typedef struct VirtIOInputHost VirtIOInputHost; +typedef struct VHostUserInput VHostUserInput; =20 struct VirtIOInputConfig { virtio_input_config config; @@ -98,6 +106,12 @@ struct VirtIOInputHost { int fd; }; =20 +struct VHostUserInput { + VirtIOInput parent_obj; + + VhostUserBackend *vhost; +}; + void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event); void virtio_input_init_config(VirtIOInput *vinput, virtio_input_config *config); diff --git a/hw/input/vhost-user-input.c b/hw/input/vhost-user-input.c new file mode 100644 index 0000000000..6da497b1a8 --- /dev/null +++ b/hw/input/vhost-user-input.c @@ -0,0 +1,129 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qemu-common.h" + +#include "hw/qdev.h" +#include "hw/virtio/virtio-input.h" + +static int vhost_input_config_change(struct vhost_dev *dev) +{ + error_report("vhost-user-input: unhandled backend config change"); + return -1; +} + +static const VhostDevConfigOps config_ops =3D { + .vhost_dev_config_notifier =3D vhost_input_config_change, +}; + +static void vhost_input_realize(DeviceState *dev, Error **errp) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(dev); + VirtIOInput *vinput =3D VIRTIO_INPUT(dev); + VirtIODevice *vdev =3D VIRTIO_DEVICE(dev); + + vhost_dev_set_config_notifier(&vhi->vhost->dev, &config_ops); + vinput->cfg_size =3D sizeof_field(virtio_input_config, u); + if (vhost_user_backend_dev_init(vhi->vhost, vdev, 2, errp) =3D=3D -1) { + return; + } +} + +static void vhost_input_change_active(VirtIOInput *vinput) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(vinput); + + if (vinput->active) { + vhost_user_backend_start(vhi->vhost); + } else { + vhost_user_backend_stop(vhi->vhost); + } +} + +static void vhost_input_get_config(VirtIODevice *vdev, uint8_t *config_dat= a) +{ + VirtIOInput *vinput =3D VIRTIO_INPUT(vdev); + VHostUserInput *vhi =3D VHOST_USER_INPUT(vdev); + int ret; + + memset(config_data, 0, vinput->cfg_size); + + ret =3D vhost_dev_get_config(&vhi->vhost->dev, config_data, vinput->cf= g_size); + if (ret) { + error_report("vhost-user-input: get device config space failed"); + return; + } +} + +static void vhost_input_set_config(VirtIODevice *vdev, + const uint8_t *config_data) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(vdev); + int ret; + + ret =3D vhost_dev_set_config(&vhi->vhost->dev, config_data, + 0, sizeof(virtio_input_config), + VHOST_SET_CONFIG_TYPE_MASTER); + if (ret) { + error_report("vhost-user-input: set device config space failed"); + return; + } + + virtio_notify_config(vdev); +} + +static const VMStateDescription vmstate_vhost_input =3D { + .name =3D "vhost-user-input", + .unmigratable =3D 1, +}; + +static void vhost_input_class_init(ObjectClass *klass, void *data) +{ + VirtIOInputClass *vic =3D VIRTIO_INPUT_CLASS(klass); + VirtioDeviceClass *vdc =3D VIRTIO_DEVICE_CLASS(klass); + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->vmsd =3D &vmstate_vhost_input; + vdc->get_config =3D vhost_input_get_config; + vdc->set_config =3D vhost_input_set_config; + vic->realize =3D vhost_input_realize; + vic->change_active =3D vhost_input_change_active; +} + +static void vhost_input_init(Object *obj) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(obj); + + vhi->vhost =3D VHOST_USER_BACKEND(object_new(TYPE_VHOST_USER_BACKEND)); + object_property_add_alias(obj, "chardev", + OBJECT(vhi->vhost), "chardev", &error_abort); +} + +static void vhost_input_finalize(Object *obj) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(obj); + + object_unref(OBJECT(vhi->vhost)); +} + +static const TypeInfo vhost_input_info =3D { + .name =3D TYPE_VHOST_USER_INPUT, + .parent =3D TYPE_VIRTIO_INPUT, + .instance_size =3D sizeof(VHostUserInput), + .instance_init =3D vhost_input_init, + .instance_finalize =3D vhost_input_finalize, + .class_init =3D vhost_input_class_init, +}; + +static void vhost_input_register_types(void) +{ + type_register_static(&vhost_input_info); +} + +type_init(vhost_input_register_types) diff --git a/hw/virtio/vhost-user-input-pci.c b/hw/virtio/vhost-user-input-= pci.c new file mode 100644 index 0000000000..3d1b7a85fd --- /dev/null +++ b/hw/virtio/vhost-user-input-pci.c @@ -0,0 +1,53 @@ +/* + * This work is licensed under the terms of the GNU LGPL, version 2 or + * later. See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-input.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "virtio-pci.h" + +typedef struct VHostUserInputPCI VHostUserInputPCI; + +#define TYPE_VHOST_USER_INPUT_PCI "vhost-user-input-pci-base" + +#define VHOST_USER_INPUT_PCI(obj) \ + OBJECT_CHECK(VHostUserInputPCI, (obj), TYPE_VHOST_USER_INPUT_PCI) + +struct VHostUserInputPCI { + VirtIOPCIProxy parent_obj; + VHostUserInput vhi; +}; + +static void vhost_user_input_pci_instance_init(Object *obj) +{ + VHostUserInputPCI *dev =3D VHOST_USER_INPUT_PCI(obj); + + virtio_instance_init_common(obj, &dev->vhi, sizeof(dev->vhi), + TYPE_VHOST_USER_INPUT); + + object_property_add_alias(obj, "chardev", + OBJECT(&dev->vhi), "chardev", + &error_abort); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_input_pci_info =3D { + .base_name =3D TYPE_VHOST_USER_INPUT_PCI, + .generic_name =3D "vhost-user-input-pci", + .transitional_name =3D "vhost-user-input-pci-transitional", + .non_transitional_name =3D "vhost-user-input-pci-non-transitional", + .parent =3D TYPE_VIRTIO_INPUT_PCI, + .instance_size =3D sizeof(VHostUserInputPCI), + .instance_init =3D vhost_user_input_pci_instance_init, +}; + +static void vhost_user_input_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_input_pci_info); +} + +type_init(vhost_user_input_pci_register) diff --git a/MAINTAINERS b/MAINTAINERS index e0f3f9c4bd..b6e855ccf9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1512,6 +1512,7 @@ L: qemu-s390x@nongnu.org virtio-input M: Gerd Hoffmann S: Maintained +F: hw/input/vhost-user-input.c F: hw/input/virtio-input*.c F: include/hw/virtio/virtio-input.h =20 diff --git a/hw/input/Kconfig b/hw/input/Kconfig index e2e66f0858..50e55e3538 100644 --- a/hw/input/Kconfig +++ b/hw/input/Kconfig @@ -29,5 +29,10 @@ config VIRTIO_INPUT_HOST default y depends on VIRTIO && LINUX =20 +config VHOST_USER_INPUT + bool + default y + depends on VIRTIO_INPUT && VHOST_USER + config TSC210X bool diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs index c8b00f71ec..9511e3102f 100644 --- a/hw/input/Makefile.objs +++ b/hw/input/Makefile.objs @@ -11,6 +11,7 @@ common-obj-$(CONFIG_VIRTIO_INPUT) +=3D virtio-input.o common-obj-$(CONFIG_VIRTIO_INPUT) +=3D virtio-input-hid.o ifeq ($(CONFIG_LINUX),y) common-obj-$(CONFIG_VIRTIO_INPUT) +=3D virtio-input-host.o +common-obj-$(CONFIG_VHOST_USER_INPUT) +=3D vhost-user-input.o endif =20 obj-$(CONFIG_MILKYMIST) +=3D milkymist-softusb.o diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index f2ab667a21..5570ea8df8 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -17,6 +17,7 @@ obj-$(CONFIG_VHOST_VSOCK) +=3D vhost-vsock.o ifeq ($(CONFIG_VIRTIO_PCI),y) obj-$(CONFIG_VHOST_VSOCK) +=3D vhost-vsock-pci.o obj-$(CONFIG_VHOST_USER_BLK) +=3D vhost-user-blk-pci.o +obj-$(CONFIG_VHOST_USER_INPUT) +=3D vhost-user-input-pci.o obj-$(CONFIG_VHOST_USER_SCSI) +=3D vhost-user-scsi-pci.o obj-$(CONFIG_VHOST_SCSI) +=3D vhost-scsi-pci.o obj-$(CONFIG_VIRTIO_INPUT_HOST) +=3D virtio-input-host-pci.o --=20 2.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555083745; cv=none; d=zoho.com; s=zohoarc; b=irEYt4uGTUlSmT/r5nRL1pTen2Worr4Irsc6uJt/H1sSXPqVTemOYep3N5vvQb+095TlfrsalnhTMSaozGm/P7W9JiC3vdmbCGlrV7PcxKG9Ckz/WKs9q/UkzoKgA+PGqxv4VoWZkxtgTvaLLVIcqLQJ2O3QKZ+ejNDF23UCzsU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555083745; 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=laww6EXFodW5Ro2GtTrTfm5QrFvRD/zW1yyohirz0OY=; b=fqDihIAlYNsoPipknJzLURAnseEtG+jNpIode4Jr6mNjBSACYmo2/Z3j64u05paHbmMSijXouZZgwKKUwlpmPkFb7uDq5zM4gZ2vUa0YSKSfGw3lIZtMQqN+Vj3OKuHG7c3ZZYIM2bNn7U9HI9zqqnNGvKXaW3mjjuOjx1elYRE= 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 1555083745325808.8343984676981; Fri, 12 Apr 2019 08:42:25 -0700 (PDT) Received: from localhost ([127.0.0.1]:38869 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyJq-0004TM-BN for importer@patchew.org; Fri, 12 Apr 2019 11:42:22 -0400 Received: from eggs.gnu.org ([209.51.188.92]:36942) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyFR-0000ad-21 for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:49 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyFQ-0001iC-3E for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:37:49 -0400 Received: from mx1.redhat.com ([209.132.183.28]:45036) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyFP-0001i0-SU; Fri, 12 Apr 2019 11:37:48 -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 1064120261; Fri, 12 Apr 2019 15:37:47 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id EA64C5D6A9; Fri, 12 Apr 2019 15:37:37 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:40 +0200 Message-Id: <20190412153647.19027-7-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-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.29]); Fri, 12 Apr 2019 15:37:47 +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 v5 06/13] libvhost-user: add PROTOCOL_F_CONFIG if {set, get}_config 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: "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add the config protocol feature bit if the set_config & get_config callbacks are implemented. Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/libvhost-user/libvhost-user.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/= libvhost-user.c index e08d6c7b97..e738f6ce88 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -1157,6 +1157,10 @@ vu_get_protocol_features_exec(VuDev *dev, VhostUserM= sg *vmsg) features |=3D 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT; } =20 + if (dev->iface->get_config && dev->iface->set_config) { + features |=3D 1ULL << VHOST_USER_PROTOCOL_F_CONFIG; + } + if (dev->iface->get_protocol_features) { features |=3D dev->iface->get_protocol_features(dev); } --=20 2.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555083780; cv=none; d=zoho.com; s=zohoarc; b=U43f4aSiEf1Y8VLpTUjuDt/Xkyk1UhZEfY57awhMEKmCGG8T+TZjZwqc63Hveb5U+gC7SqBiGepWdgfIq+prtjCwa3XLz3acq4rIOgpr+VRW7Pg4sijCfsdnOef7seoyLuK373C6dcPhQdcFqXmU83tg2iVSYW9ZIyQMGwVSSh0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555083780; 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=5rHkfL8y5Fm0kcNHlrKK9s4DnIHRgpZeLWdEa7Y/V6w=; b=Epq/DwfZwwkrgTTm13xLVrF/4y3XbgJOszNjvlnGV9yniSdSLToqnyuGsV/hLm7b5grHBN7n9YSSrKpkyveuVRHUBP+mBCwqCWC1+uJfPErhauokwr0EdsscDpKkCg8bFi3o4pFre82GPl1krp7MZHmNTV0NN73K3nx3B2Xr2SM= 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 1555083780320531.579745764988; Fri, 12 Apr 2019 08:43:00 -0700 (PDT) Received: from localhost ([127.0.0.1]:38875 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyKP-0004yU-7g for importer@patchew.org; Fri, 12 Apr 2019 11:42:57 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37010) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyFd-0000im-3Q for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:02 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyFb-0001rJ-14 for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:01 -0400 Received: from mx1.redhat.com ([209.132.183.28]:50430) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyFa-0001qQ-M9; Fri, 12 Apr 2019 11:37:58 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id F25B23082E5F; Fri, 12 Apr 2019 15:37:57 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id C23225C206; Fri, 12 Apr 2019 15:37:48 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:41 +0200 Message-Id: <20190412153647.19027-8-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.46]); Fri, 12 Apr 2019 15:37:58 +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 v5 07/13] contrib: add vhost-user-input 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: "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a vhost-user input backend example, based on virtio-input-host device. It takes an evdev path as argument, and can be associated with a vhost-user-input device via a UNIX socket: $ vhost-user-input -p /dev/input/eventX -s /tmp/vui.sock $ qemu ... -chardev socket,id=3Dvuic,path=3D/tmp/vui.sock -device vhost-user-input-pci,chardev=3Dvuic Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/vhost-user-input/main.c | 427 +++++++++++++++++++++++++ MAINTAINERS | 1 + Makefile | 3 + Makefile.objs | 1 + configure | 3 + contrib/vhost-user-input/Makefile.objs | 1 + 6 files changed, 436 insertions(+) create mode 100644 contrib/vhost-user-input/main.c create mode 100644 contrib/vhost-user-input/Makefile.objs diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/mai= n.c new file mode 100644 index 0000000000..425056cd69 --- /dev/null +++ b/contrib/vhost-user-input/main.c @@ -0,0 +1,427 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" + +#include +#include + +#include "qemu/iov.h" +#include "qemu/bswap.h" +#include "contrib/libvhost-user/libvhost-user.h" +#include "contrib/libvhost-user/libvhost-user-glib.h" +#include "standard-headers/linux/virtio_input.h" +#include "qapi/error.h" + +typedef struct virtio_input_event virtio_input_event; +typedef struct virtio_input_config virtio_input_config; + +typedef struct VuInput { + VugDev dev; + GSource *evsrc; + int evdevfd; + GArray *config; + virtio_input_config *sel_config; + struct { + virtio_input_event event; + VuVirtqElement *elem; + } *queue; + uint32_t qindex, qsize; +} VuInput; + +static void vi_input_send(VuInput *vi, struct virtio_input_event *event) +{ + VuDev *dev =3D &vi->dev.parent; + VuVirtq *vq =3D vu_get_queue(dev, 0); + VuVirtqElement *elem; + int i, len; + + /* queue up events ... */ + if (vi->qindex =3D=3D vi->qsize) { + vi->qsize++; + vi->queue =3D g_realloc_n(vi->queue, vi->qsize, sizeof(vi->queue[0= ])); + } + vi->queue[vi->qindex++].event =3D *event; + + /* ... until we see a report sync ... */ + if (event->type !=3D htole16(EV_SYN) || + event->code !=3D htole16(SYN_REPORT)) { + return; + } + + /* ... then check available space ... */ + for (i =3D 0; i < vi->qindex; i++) { + elem =3D vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); + if (!elem) { + while (--i >=3D 0) { + vu_queue_unpop(dev, vq, vi->queue[i].elem, 0); + } + vi->qindex =3D 0; + g_warning("virtio-input queue full"); + return; + } + vi->queue[i].elem =3D elem; + } + + /* ... and finally pass them to the guest */ + for (i =3D 0; i < vi->qindex; i++) { + elem =3D vi->queue[i].elem; + len =3D iov_from_buf(elem->in_sg, elem->in_num, + 0, &vi->queue[i].event, sizeof(virtio_input_eve= nt)); + vu_queue_push(dev, vq, elem, len); + g_free(elem); + } + + vu_queue_notify(&vi->dev.parent, vq); + vi->qindex =3D 0; +} + +static void +vi_evdev_watch(VuDev *dev, int condition, void *data) +{ + VuInput *vi =3D data; + int fd =3D vi->evdevfd; + + g_debug("Got evdev condition %x", condition); + + struct virtio_input_event virtio; + struct input_event evdev; + int rc; + + for (;;) { + rc =3D read(fd, &evdev, sizeof(evdev)); + if (rc !=3D sizeof(evdev)) { + break; + } + + g_debug("input %d %d %d", evdev.type, evdev.code, evdev.value); + + virtio.type =3D htole16(evdev.type); + virtio.code =3D htole16(evdev.code); + virtio.value =3D htole32(evdev.value); + vi_input_send(vi, &virtio); + } +} + + +static void vi_handle_status(VuInput *vi, virtio_input_event *event) +{ + struct input_event evdev; + int rc; + + if (gettimeofday(&evdev.time, NULL)) { + perror("vi_handle_status: gettimeofday"); + return; + } + + evdev.type =3D le16toh(event->type); + evdev.code =3D le16toh(event->code); + evdev.value =3D le32toh(event->value); + + rc =3D write(vi->evdevfd, &evdev, sizeof(evdev)); + if (rc =3D=3D -1) { + perror("vi_host_handle_status: write"); + } +} + +static void vi_handle_sts(VuDev *dev, int qidx) +{ + VuInput *vi =3D container_of(dev, VuInput, dev.parent); + VuVirtq *vq =3D vu_get_queue(dev, qidx); + virtio_input_event event; + VuVirtqElement *elem; + int len; + + g_debug("%s", G_STRFUNC); + + for (;;) { + elem =3D vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); + if (!elem) { + break; + } + + memset(&event, 0, sizeof(event)); + len =3D iov_to_buf(elem->out_sg, elem->out_num, + 0, &event, sizeof(event)); + vi_handle_status(vi, &event); + vu_queue_push(dev, vq, elem, len); + g_free(elem); + } + + vu_queue_notify(&vi->dev.parent, vq); +} + +static void +vi_panic(VuDev *dev, const char *msg) +{ + g_critical("%s\n", msg); + exit(EXIT_FAILURE); +} + +static void +vi_queue_set_started(VuDev *dev, int qidx, bool started) +{ + VuInput *vi =3D container_of(dev, VuInput, dev.parent); + VuVirtq *vq =3D vu_get_queue(dev, qidx); + + g_debug("queue started %d:%d", qidx, started); + + if (qidx =3D=3D 1) { + vu_set_queue_handler(dev, vq, started ? vi_handle_sts : NULL); + } + + started =3D vu_queue_started(dev, vu_get_queue(dev, 0)) && + vu_queue_started(dev, vu_get_queue(dev, 1)); + + if (started && !vi->evsrc) { + vi->evsrc =3D vug_source_new(&vi->dev, vi->evdevfd, + G_IO_IN, vi_evdev_watch, vi); + } + + if (!started && vi->evsrc) { + g_source_destroy(vi->evsrc); + vi->evsrc =3D NULL; + } +} + +static virtio_input_config * +vi_find_config(VuInput *vi, uint8_t select, uint8_t subsel) +{ + virtio_input_config *cfg; + int i; + + for (i =3D 0; i < vi->config->len; i++) { + cfg =3D &g_array_index(vi->config, virtio_input_config, i); + if (select =3D=3D cfg->select && subsel =3D=3D cfg->subsel) { + return cfg; + } + } + + return NULL; +} + +static int vi_get_config(VuDev *dev, uint8_t *config, uint32_t len) +{ + VuInput *vi =3D container_of(dev, VuInput, dev.parent); + + g_return_val_if_fail(len <=3D sizeof(*vi->sel_config), -1); + + if (vi->sel_config) { + memcpy(config, vi->sel_config, len); + } else { + memset(config, 0, len); + } + + return 0; +} + +static int vi_set_config(VuDev *dev, const uint8_t *data, + uint32_t offset, uint32_t size, + uint32_t flags) +{ + VuInput *vi =3D container_of(dev, VuInput, dev.parent); + virtio_input_config *config =3D (virtio_input_config *)data; + + vi->sel_config =3D vi_find_config(vi, config->select, config->subsel); + + return 0; +} + +static const VuDevIface vuiface =3D { + .queue_set_started =3D vi_queue_set_started, + .get_config =3D vi_get_config, + .set_config =3D vi_set_config, +}; + +static void +vi_bits_config(VuInput *vi, int type, int count) +{ + virtio_input_config bits; + int rc, i, size =3D 0; + + memset(&bits, 0, sizeof(bits)); + rc =3D ioctl(vi->evdevfd, EVIOCGBIT(type, count / 8), bits.u.bitmap); + if (rc < 0) { + return; + } + + for (i =3D 0; i < count / 8; i++) { + if (bits.u.bitmap[i]) { + size =3D i + 1; + } + } + if (size =3D=3D 0) { + return; + } + + bits.select =3D VIRTIO_INPUT_CFG_EV_BITS; + bits.subsel =3D type; + bits.size =3D size; + g_array_append_val(vi->config, bits); +} + +static int unix_sock_new(char *path) +{ + int sock; + struct sockaddr_un un; + size_t len; + + sock =3D socket(AF_UNIX, SOCK_STREAM, 0); + if (sock <=3D 0) { + perror("socket"); + return -1; + } + + un.sun_family =3D AF_UNIX; + snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); + len =3D sizeof(un.sun_family) + strlen(un.sun_path); + + unlink(path); + if (bind(sock, (struct sockaddr *)&un, len) < 0) { + perror("bind"); + goto fail; + } + + if (listen(sock, 1) < 0) { + perror("listen"); + goto fail; + } + + return sock; + +fail: + close(sock); + + return -1; +} + +static char *opt_evdev; +static int opt_fdnum =3D -1; +static char *opt_socket_path; +static gboolean opt_nograb; +static gboolean opt_print_caps; + +static GOptionEntry entries[] =3D { + { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps, + "Print capabilities", NULL }, + { "no-grab", 'n', 0, G_OPTION_ARG_NONE, &opt_nograb, + "Don't grab device", 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" }, + { "evdev-path", 'p', 0, G_OPTION_ARG_FILENAME, &opt_evdev, + "evdev input device path", "PATH" }, + { NULL, } +}; + +int +main(int argc, char *argv[]) +{ + GMainLoop *loop =3D NULL; + VuInput vi =3D { 0, }; + int rc, ver, fd; + virtio_input_config id; + struct input_id ids; + GError *error =3D NULL; + GOptionContext *context; + + context =3D g_option_context_new(NULL); + 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); + } + if (opt_print_caps) { + g_print("{\n"); + g_print(" \"type\": \"input\",\n"); + g_print(" \"features\": [\n"); + g_print(" \"evdev-path\",\n"); + g_print(" \"no-grab\"\n"); + g_print(" ]\n"); + g_print("}\n"); + exit(EXIT_SUCCESS); + } + if (!opt_evdev) { + g_printerr("Please specify an evdev path\n"); + exit(EXIT_FAILURE); + } + if ((!!opt_socket_path + (opt_fdnum !=3D -1)) !=3D 1) { + g_printerr("Please specify either --fd or --socket-path\n"); + exit(EXIT_FAILURE); + } + + vi.evdevfd =3D open(opt_evdev, O_RDWR); + if (vi.evdevfd < 0) { + g_printerr("Failed to open evdev: %s\n", g_strerror(errno)); + exit(EXIT_FAILURE); + } + + rc =3D ioctl(vi.evdevfd, EVIOCGVERSION, &ver); + if (rc < 0) { + g_printerr("%s: is not an evdev device\n", argv[1]); + exit(EXIT_FAILURE); + } + + if (!opt_nograb) { + rc =3D ioctl(vi.evdevfd, EVIOCGRAB, 1); + if (rc < 0) { + g_printerr("Failed to grab device\n"); + exit(EXIT_FAILURE); + } + } + + vi.config =3D g_array_new(false, false, sizeof(virtio_input_config)); + memset(&id, 0, sizeof(id)); + ioctl(vi.evdevfd, EVIOCGNAME(sizeof(id.u.string) - 1), id.u.string); + id.select =3D VIRTIO_INPUT_CFG_ID_NAME; + id.size =3D strlen(id.u.string); + g_array_append_val(vi.config, id); + + if (ioctl(vi.evdevfd, EVIOCGID, &ids) =3D=3D 0) { + memset(&id, 0, sizeof(id)); + id.select =3D VIRTIO_INPUT_CFG_ID_DEVIDS; + id.size =3D sizeof(struct virtio_input_devids); + id.u.ids.bustype =3D cpu_to_le16(ids.bustype); + id.u.ids.vendor =3D cpu_to_le16(ids.vendor); + id.u.ids.product =3D cpu_to_le16(ids.product); + id.u.ids.version =3D cpu_to_le16(ids.version); + g_array_append_val(vi.config, id); + } + + vi_bits_config(&vi, EV_KEY, KEY_CNT); + vi_bits_config(&vi, EV_REL, REL_CNT); + vi_bits_config(&vi, EV_ABS, ABS_CNT); + vi_bits_config(&vi, EV_MSC, MSC_CNT); + vi_bits_config(&vi, EV_SW, SW_CNT); + g_debug("config length: %u", vi.config->len); + + if (opt_socket_path) { + int lsock =3D unix_sock_new(opt_socket_path); + 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(&vi.dev, fd, vi_panic, &vuiface); + + loop =3D g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + vug_deinit(&vi.dev); + + if (vi.evsrc) { + g_source_unref(vi.evsrc); + } + g_array_free(vi.config, TRUE); + g_free(vi.queue); + return 0; +} diff --git a/MAINTAINERS b/MAINTAINERS index b6e855ccf9..7e727299de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1515,6 +1515,7 @@ S: Maintained F: hw/input/vhost-user-input.c F: hw/input/virtio-input*.c F: include/hw/virtio/virtio-input.h +F: contrib/vhost-user-input/* =20 virtio-serial M: Amit Shah diff --git a/Makefile b/Makefile index 04a0d45050..5fd26cfc97 100644 --- a/Makefile +++ b/Makefile @@ -402,6 +402,7 @@ dummy :=3D $(call unnest-vars,, \ libvhost-user-obj-y \ vhost-user-scsi-obj-y \ vhost-user-blk-obj-y \ + vhost-user-input-obj-y \ qga-vss-dll-obj-y \ block-obj-y \ block-obj-m \ @@ -609,6 +610,8 @@ vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvho= st-user.a rdmacm-mux$(EXESUF): LIBS +=3D "-libumad" rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) $(call LINK, $^) +vhost-user-input$(EXESUF): $(vhost-user-input-obj-y) libvhost-user.a libqe= muutil.a + $(call LINK, $^) =20 module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak $(call quiet-command,$(PYTHON) $< $@ \ diff --git a/Makefile.objs b/Makefile.objs index cf065de5ed..27502b7df1 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -122,6 +122,7 @@ vhost-user-scsi.o-libs :=3D $(LIBISCSI_LIBS) 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/ =20 ###################################################################### trace-events-subdirs =3D diff --git a/configure b/configure index 1c563a7027..140a294938 100755 --- a/configure +++ b/configure @@ -6044,6 +6044,9 @@ if test "$want_tools" =3D "yes" ; then if [ "$curl" =3D "yes" ]; then tools=3D"elf2dmp\$(EXESUF) $tools" fi + if [ "$vhost_user" =3D "yes" ] && [ "$linux" =3D "yes" ] ; then + tools=3D"vhost-user-input\$(EXESUF) $tools" + fi fi if test "$softmmu" =3D yes ; then if test "$linux" =3D yes; then diff --git a/contrib/vhost-user-input/Makefile.objs b/contrib/vhost-user-in= put/Makefile.objs new file mode 100644 index 0000000000..b1fad90d51 --- /dev/null +++ b/contrib/vhost-user-input/Makefile.objs @@ -0,0 +1 @@ +vhost-user-input-obj-y =3D main.o --=20 2.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555084179; cv=none; d=zoho.com; s=zohoarc; b=lCthrOWIoyE7SsJoGhKHVvnqgBX5O2teVgK3DFEd8dcU9ozASC4mbnI/dx82R9BG0Q04brGvuqmqIE32bv499NjFDPrwEFRW8Wv2muGcM5giEFKw5xAeSg95I5EFVB9dADxn9I5ek94AqA/abnd6MTJUQuqHinpzaKYTssnVZfY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555084179; 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=9JZsi2FmVAwhc6LW8PKK6TIog3KdB3Nqo6MwCs1KYt4=; b=lWUMpCXxG0ds6eYEA84b/s+vn778rPylf+XgvmiRLVZ1gOMXTSDAjK1WtDgftrkZOS8jnYpP4QrmG0HEah8Is+Ilj4GMFeX4uTZCV/opaWZmdN2ZD9vcyo5BBgU53UgX+qWg2FgOUbaQY7BsDdgcMD7c+F5e8ihbxt90F4sAGlk= 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 155508417996643.008431955165634; Fri, 12 Apr 2019 08:49:39 -0700 (PDT) Received: from localhost ([127.0.0.1]:38977 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyQp-0002Hd-Q1 for importer@patchew.org; Fri, 12 Apr 2019 11:49:35 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37072) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyFi-0000nK-3v for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:07 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyFg-0001tc-DR for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:06 -0400 Received: from mx1.redhat.com ([209.132.183.28]:45114) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyFg-0001tN-2n; Fri, 12 Apr 2019 11:38:04 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 5F1FC3D38; Fri, 12 Apr 2019 15:38:03 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id 158A15C206; Fri, 12 Apr 2019 15:37:59 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:42 +0200 Message-Id: <20190412153647.19027-9-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.29]); Fri, 12 Apr 2019 15:38:03 +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 v5 08/13] 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: "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson 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 | 2 +- docs/interop/vhost-user-gpu.rst | 238 ++++++++++++++++++++++++++ docs/interop/vhost-user.txt | 9 + 8 files changed, 269 insertions(+), 1 deletion(-) 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 414ceb0a2f..eac5c120de 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 e738f6ce88..c9823a1354 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 7e727299de..337eca10a3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1659,6 +1659,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 2df977dd52..28ead07466 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -15,4 +15,4 @@ Contents: bitmaps live-block-operations pr-helper - + 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..9cc1106b02 --- /dev/null +++ b/docs/interop/vhost-user-gpu.rst @@ -0,0 +1,238 @@ +=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 request consists of 2 header fields and a payload. + ++---------+------+---------+ +| request | size | payload | ++---------+------+---------+ + +Header +------ + +:request: ``u32``, type of the request + +:size: ``u32``, size of the payload + +A reply consists only of a payload, whose content depends on the request. + +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 + +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: RGBA data (the size is computed based on the region size, and + the request type) + +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 size; /* the following payload size */ + union { + VhostUserGpuCursorPos cursor_pos; + VhostUserGpuCursorUpdate cursor_update; + VhostUserGpuScanout scanout; + VhostUserGpuUpdate update; + VhostUserGpuDMABUFScanout dmabuf_scanout; + 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: ``u32`` + + 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.txt b/docs/interop/vhost-user.txt index 4dbd530cb9..1be2c74cc7 100644 --- a/docs/interop/vhost-user.txt +++ b/docs/interop/vhost-user.txt @@ -1051,6 +1051,15 @@ Master message types 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 + Master payload: N/A + Slave 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.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555084346; cv=none; d=zoho.com; s=zohoarc; b=V4KDlQ8FX4ruDigsqsGoY6p+5Jh7FaGaERVvlbcd66Pkar0zKKbikQ/sGlotSUEQbtHyuYv5u66ZuEIKRhbQ42X+2eruquGh0BnvlYw7a+CqnAGd510MW9cZsGVyclnkPa1IzTcr52YWQbYZKVI/CUz/I5VN2uFS/lvW/TYrhJU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555084346; 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=YWcuhHmtR8W99iOYLujYJvWfqYIzOZCvtYxIdEjsTSs=; b=h52RAsqHvNXE9UUz+YJWoMYVhQntv/feGpMA+yLclRnYuhkYdhy/iUb8+kKlTecfpW2px/wt/4JtQ3Bb6BZV/YJENaD0y8kya79Gt3EFp5e7scJJDEiGu87uTLQF00JMqYZWkS44JbNvTn86syi+hcjOMh14m7PxE/CgjExx0GY= 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 1555084346140875.3269787278028; Fri, 12 Apr 2019 08:52:26 -0700 (PDT) Received: from localhost ([127.0.0.1]:39027 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyTR-0004e7-51 for importer@patchew.org; Fri, 12 Apr 2019 11:52:21 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37120) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyFq-0000sa-0l for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:15 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyFo-0001wr-UI for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:13 -0400 Received: from mx1.redhat.com ([209.132.183.28]:43370) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyFo-0001wd-Jb; Fri, 12 Apr 2019 11:38:12 -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 E2C45307D85A; Fri, 12 Apr 2019 15:38:11 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id D3C1760BFC; Fri, 12 Apr 2019 15:38:04 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:43 +0200 Message-Id: <20190412153647.19027-10-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-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.48]); Fri, 12 Apr 2019 15:38:11 +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 v5 09/13] virtio: add virtio-gpu 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: "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson 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.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555083648; cv=none; d=zoho.com; s=zohoarc; b=nV8wTCQ9giigXQHMcW7vE2ToTRnfP39ed2pr88peFpys2ZQcE1PebCu9HqhJIEfIg4aXL23JpKLe9F5BLEYU1LLdknoDwWRcxfk7+dCd5MxWte479rwFCGonwyn8a518sklxm/uDtxSQM4o2ACKqX/wOM2jO6VMYutRDOyHu6jc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555083648; 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=fJj2lAEAI7YNWoPBAr25fsazoYm380gpsKLXFwWcs30=; b=J/+7sDGHXLDFd+PSUzqbW6k2slhMsxLERjt4xiVF5jCDojEHAY9e60znB7x2xQnm8JtErYJBXffVI0HcHvsk1/TnAmlUx5W8/Jv2yco+onEupDQrNeMkR/nqgeI+6GZlVxWIqqHP8W0MOInJvpnrUhyhF9HLGGY0wcfFDC1koiw= 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 1555083648054337.12817013655274; Fri, 12 Apr 2019 08:40:48 -0700 (PDT) Received: from localhost ([127.0.0.1]:38836 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyIG-0002RH-LP for importer@patchew.org; Fri, 12 Apr 2019 11:40:44 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37227) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyG3-00014G-FA for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:33 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyFx-0001zY-CX for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:27 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34532) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyFw-0001yq-2w; Fri, 12 Apr 2019 11:38:20 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 634F63086221; Fri, 12 Apr 2019 15:38:19 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id DE1885C219; Fri, 12 Apr 2019 15:38:13 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:44 +0200 Message-Id: <20190412153647.19027-11-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.42]); Fri, 12 Apr 2019 15:38:19 +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 v5 10/13] 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: "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson 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 example, based on virtio-gpu/3d device. It is to be 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 - more drm devices support for 2d surfaces - edid support Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/vhost-user-gpu/drm.h | 72 ++ contrib/vhost-user-gpu/virgl.h | 25 + contrib/vhost-user-gpu/vugpu.h | 170 ++++ contrib/vhost-user-gpu/drm.c | 341 +++++++ contrib/vhost-user-gpu/main.c | 1235 +++++++++++++++++++++++ contrib/vhost-user-gpu/virgl.c | 579 +++++++++++ MAINTAINERS | 2 + Makefile | 3 + Makefile.objs | 1 + configure | 32 + contrib/vhost-user-gpu/50-qemu-gpu.json | 5 + contrib/vhost-user-gpu/Makefile.objs | 10 + 12 files changed, 2475 insertions(+) create mode 100644 contrib/vhost-user-gpu/drm.h create mode 100644 contrib/vhost-user-gpu/virgl.h create mode 100644 contrib/vhost-user-gpu/vugpu.h create mode 100644 contrib/vhost-user-gpu/drm.c 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/50-qemu-gpu.json create mode 100644 contrib/vhost-user-gpu/Makefile.objs diff --git a/contrib/vhost-user-gpu/drm.h b/contrib/vhost-user-gpu/drm.h new file mode 100644 index 0000000000..1c3e7b2c51 --- /dev/null +++ b/contrib/vhost-user-gpu/drm.h @@ -0,0 +1,72 @@ +/* + * 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. + */ +#ifndef VHOST_USER_GPU_DRM_H +#define VHOST_USER_GPU_DRM_H + +#include "qemu/osdep.h" + +#ifdef CONFIG_LIBDRM_INTEL +#include +#include +#endif + +#ifdef CONFIG_MEMFD +#include +#include +#endif + +struct drm_buffer; + +struct drm_device { + bool inited; + int fd; + char *name; +#ifdef CONFIG_LIBDRM_INTEL + drm_intel_bufmgr *bufmgr; +#endif + + bool (*alloc_bo)(struct drm_buffer *buf); + void (*free_bo)(struct drm_buffer *buf); + bool (*export_bo_to_prime)(struct drm_buffer *buf, int *fd); + bool (*map_bo)(struct drm_buffer *buf); + void (*unmap_bo)(struct drm_buffer *buf); + void (*device_destroy)(struct drm_device *dev); +}; + +struct drm_buffer { + struct drm_device *dev; + +#ifdef CONFIG_LIBDRM_INTEL + drm_intel_bo *intel_bo; +#endif /* HAVE_LIBDRM_INTEL */ +#ifdef CONFIG_MEMFD + int memfd; +#endif + + uint32_t gem_handle; + int dmabuf_fd; + uint8_t *mmap; + + int width; + int height; + int bpp; + unsigned long stride; + int format; +}; + +bool drm_device_init(struct drm_device *dev, int fd); +void drm_device_destroy(struct drm_device *dev); + +bool drm_buffer_create(struct drm_buffer *buffer, struct drm_device *dev, + int width, int height); +bool drm_buffer_can_get_dmabuf_fd(struct drm_buffer *buffer); +bool drm_buffer_get_dmabuf_fd(struct drm_buffer *buffer, int *fd); +void drm_buffer_destroy(struct drm_buffer *buffer); + +#endif 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/vugpu.h b/contrib/vhost-user-gpu/vugpu.h new file mode 100644 index 0000000000..fbee95e7b9 --- /dev/null +++ b/contrib/vhost-user-gpu/vugpu.h @@ -0,0 +1,170 @@ +/* + * 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 "drm.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 size; /* the following payload size */ + union { + VhostUserGpuCursorPos cursor_pos; + VhostUserGpuCursorUpdate cursor_update; + VhostUserGpuScanout scanout; + VhostUserGpuUpdate update; + VhostUserGpuDMABUFScanout dmabuf_scanout; + uint64_t u64; + } payload; +} QEMU_PACKED VhostUserGpuMsg; + +static VhostUserGpuMsg m __attribute__ ((unused)); +#define VHOST_USER_GPU_HDR_SIZE (sizeof(m.request) + sizeof(m.size)) + +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 drm_device drm_dev; + 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); + +int vg_sock_fd_read(int sock, void *buf, ssize_t buflen); + +#endif diff --git a/contrib/vhost-user-gpu/drm.c b/contrib/vhost-user-gpu/drm.c new file mode 100644 index 0000000000..70177918a9 --- /dev/null +++ b/contrib/vhost-user-gpu/drm.c @@ -0,0 +1,341 @@ +/* + * 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 /* not much to do with xf86 */ +#include + +#include "drm.h" + +static bool +mem_alloc_bo(struct drm_buffer *buf) +{ + buf->mmap =3D g_malloc(buf->width * buf->height * buf->bpp / 8); + buf->stride =3D buf->width * buf->bpp / 8; + return true; +} + +static void +mem_free_bo(struct drm_buffer *buf) +{ + g_free(buf->mmap); +} + +static bool +mem_map_bo(struct drm_buffer *buf) +{ + return buf->mmap !=3D NULL; +} + +static void +mem_unmap_bo(struct drm_buffer *buf) +{ +} + +static void +mem_device_destroy(struct drm_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 drm_buffer *buf) +{ + return ROUND_UP(buf->width * buf->height * buf->bpp / 8, getpagesize()= ); +} + +static bool +udmabuf_alloc_bo(struct drm_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 * buf->bpp / 8; + + return true; +} + +static void +udmabuf_free_bo(struct drm_buffer *buf) +{ + close(buf->memfd); +} + +static bool +udmabuf_map_bo(struct drm_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_export_bo_to_prime(struct drm_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 drm_buffer *buf) +{ + munmap(buf->mmap, udmabuf_get_size(buf)); +} + +static void +udmabuf_device_destroy(struct drm_device *dev) +{ + close(dev->fd); +} +#endif + +#ifdef CONFIG_LIBDRM_INTEL +static bool +intel_alloc_bo(struct drm_buffer *buf) +{ + uint32_t tiling =3D I915_TILING_NONE; + + buf->intel_bo =3D drm_intel_bo_alloc_tiled(buf->dev->bufmgr, "vhost-us= er-gpu", + buf->width, buf->height, + (buf->bpp / 8), &tiling, + &buf->stride, 0); + + if (!buf->intel_bo) { + return false; + } + + if (tiling !=3D I915_TILING_NONE) { + drm_intel_bo_unreference(buf->intel_bo); + return false; + } + + return true; +} + +static void +intel_free_bo(struct drm_buffer *buf) +{ + drm_intel_bo_unreference(buf->intel_bo); +} + +static bool +intel_map_bo(struct drm_buffer *buf) +{ + if (drm_intel_gem_bo_map_gtt(buf->intel_bo) !=3D 0) { + return false; + } + + buf->mmap =3D buf->intel_bo->virtual; + + return true; +} + +static bool +intel_export_bo_to_prime(struct drm_buffer *buffer, int *fd) +{ + if (drm_intel_bo_gem_export_to_prime(buffer->intel_bo, fd) < 0) { + return false; + } + + return true; +} + +static void +intel_unmap_bo(struct drm_buffer *buf) +{ + drm_intel_gem_bo_unmap_gtt(buf->intel_bo); +} + +static void +intel_device_destroy(struct drm_device *dev) +{ + drm_intel_bufmgr_destroy(dev->bufmgr); +} + +#endif /* CONFIG_LIBDRM_INTEL */ + +void +drm_device_destroy(struct drm_device *dev) +{ + if (!dev->inited) { + return; + } + + dev->device_destroy(dev); + g_free(dev->name); +} + +bool +drm_device_init(struct drm_device *dev, int fd) +{ + drmVersionPtr version =3D drmGetVersion(fd); + + dev->fd =3D fd; + dev->name =3D g_strdup(version->name); + drmFreeVersion(version); + + if (0) { + /* nothing */ + } +#ifdef CONFIG_LIBDRM_INTEL + else if (g_str_equal(dev->name, "i915")) { + dev->bufmgr =3D drm_intel_bufmgr_gem_init(fd, 32); + if (!dev->bufmgr) { + return false; + } + dev->alloc_bo =3D intel_alloc_bo; + dev->free_bo =3D intel_free_bo; + dev->export_bo_to_prime =3D intel_export_bo_to_prime; + dev->map_bo =3D intel_map_bo; + dev->unmap_bo =3D intel_unmap_bo; + dev->device_destroy =3D intel_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->export_bo_to_prime =3D udmabuf_export_bo_to_prime; + 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 +drm_buffer_map(struct drm_buffer *buf) +{ + struct drm_device *dev =3D buf->dev; + + return dev->map_bo(buf); +} + +static void +drm_buffer_unmap(struct drm_buffer *buf) +{ + struct drm_device *dev =3D buf->dev; + + dev->unmap_bo(buf); +} + +bool +drm_buffer_can_get_dmabuf_fd(struct drm_buffer *buffer) +{ + if (!buffer->dev->export_bo_to_prime) { + return false; + } + + return true; +} + +bool +drm_buffer_get_dmabuf_fd(struct drm_buffer *buffer, int *fd) +{ + if (!drm_buffer_can_get_dmabuf_fd(buffer) || + !buffer->dev->export_bo_to_prime(buffer, fd)) { + g_warning("Failed to get dmabuf"); + return false; + } + + if (*fd < 0) { + g_warning("error: dmabuf_fd < 0"); + return false; + } + + return true; +} + +bool +drm_buffer_create(struct drm_buffer *buffer, struct drm_device *dev, + int width, int height) +{ + buffer->dev =3D dev; + buffer->width =3D width; + buffer->height =3D height; + buffer->stride =3D 0; /* modified during alloc */ + buffer->bpp =3D 32; + buffer->format =3D DRM_FORMAT_XRGB8888; + if (!dev->alloc_bo(buffer)) { + g_warning("alloc_bo failed"); + return false; + } + + if (!drm_buffer_map(buffer)) { + g_warning("map_bo failed"); + goto err; + } + + return true; + +err: + dev->free_bo(buffer); + return false; +} + +void +drm_buffer_destroy(struct drm_buffer *buffer) +{ + struct drm_device *drm_dev =3D buffer->dev; + + drm_buffer_unmap(buffer); + drm_dev->free_bo(buffer); +} diff --git a/contrib/vhost-user-gpu/main.c b/contrib/vhost-user-gpu/main.c new file mode 100644 index 0000000000..ddf1a8a298 --- /dev/null +++ b/contrib/vhost-user-gpu/main.c @@ -0,0 +1,1235 @@ +/* + * 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 +#include + +#include "vugpu.h" +#include "hw/virtio/virtio-gpu-bswap.h" +#include "virgl.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 drm_buffer drm_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"; + } +} + +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; + uint32_t ok; + + if (vg_sock_fd_read(g->sock_fd, &ok, sizeof(ok)) < 0) { + vg_sock_fd_close(g); + } + + /* 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); + } +} + +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 { 0 ,}; + 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_sock_fd_read(vg->sock_fd, &dpy_info, sizeof(dpy_info)) < 0) { + vg_sock_fd_close(vg); + return; + } + + vg_ctrl_response(vg, cmd, &dpy_info.hdr, sizeof(dpy_info)); +} + +static pixman_format_code_t +get_pixman_format(uint32_t virtio_gpu_format) +{ + switch (virtio_gpu_format) { +#ifdef HOST_WORDS_BIGENDIAN + case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: + return PIXMAN_b8g8r8x8; + case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: + return PIXMAN_b8g8r8a8; + case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: + return PIXMAN_x8r8g8b8; + case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: + return PIXMAN_a8r8g8b8; + case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: + return PIXMAN_r8g8b8x8; + case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: + return PIXMAN_r8g8b8a8; + case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: + return PIXMAN_x8b8g8r8; + case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: + return PIXMAN_a8b8g8r8; +#else + case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: + return PIXMAN_x8r8g8b8; + case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: + return PIXMAN_a8r8g8b8; + case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: + return PIXMAN_b8g8r8x8; + case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: + return PIXMAN_b8g8r8a8; + case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: + return PIXMAN_x8b8g8r8; + case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: + return PIXMAN_a8b8g8r8; + case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: + return PIXMAN_r8g8b8x8; + case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: + return PIXMAN_r8g8b8a8; +#endif + default: + return 0; + } +} + +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 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; + } + drm_buffer_create(&res->drm_buffer, &g->drm_dev, c2d.width, c2d.height= ); + res->image =3D pixman_image_create_bits(pformat, + c2d.width, + c2d.height, + (uint32_t *)res->drm_buffer.mmap, + res->drm_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); + } + } + } + + drm_buffer_destroy(&res->drm_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 drm_buffer *buffer =3D &res->drm_buffer; + + if (drm_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 (drm_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 (drm_buffer_can_get_dmabuf_fd(&res->drm_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_sock_fd_read(g->sock_fd, &u64, sizeof(u64)) < 0) { + vg_sock_fd_close(g); + 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); + } + + drm_device_destroy(&g->drm_dev); +} + +static int unix_sock_new(char *path) +{ + int sock; + struct sockaddr_un un; + size_t len; + + sock =3D socket(AF_UNIX, SOCK_STREAM, 0); + if (sock <=3D 0) { + perror("socket"); + return -1; + } + + un.sun_family =3D AF_UNIX; + snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); + len =3D sizeof(un.sun_family) + strlen(un.sun_path); + + unlink(path); + if (bind(sock, (struct sockaddr *)&un, len) < 0) { + perror("bind"); + goto fail; + } + + if (listen(sock, 1) < 0) { + perror("listen"); + goto fail; + } + + return sock; + +fail: + close(sock); + + return -1; +} + +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 (!drm_device_init(&g.drm_dev, 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_sock_new(opt_socket_path); + 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/MAINTAINERS b/MAINTAINERS index 337eca10a3..476fe4c49c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1664,6 +1664,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 5fd26cfc97..5fd1be6bfb 100644 --- a/Makefile +++ b/Makefile @@ -403,6 +403,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 \ @@ -612,6 +613,8 @@ rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADD= S) $(call LINK, $^) vhost-user-input$(EXESUF): $(vhost-user-input-obj-y) libvhost-user.a libqe= muutil.a $(call LINK, $^) +vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) li= bqemuutil.a libqemustub.a + $(call LINK, $^) =20 module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak $(call quiet-command,$(PYTHON) $< $@ \ diff --git a/Makefile.objs b/Makefile.objs index 27502b7df1..89ed0b7ad4 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/configure b/configure index 140a294938..689ab688a5 100755 --- a/configure +++ b/configure @@ -4095,6 +4095,20 @@ libs_softmmu=3D"$libs_softmmu $fdt_libs" ########################################## # opengl probe (for sdl2, gtk, milkymist-tmu2) =20 +libdrm=3D"no" +if $pkg_config libdrm; then + libdrm_cflags=3D"$($pkg_config --cflags libdrm)" + libdrm_libs=3D"$($pkg_config --libs libdrm)" + libdrm=3D"yes" +fi + +libdrm_intel=3D"no" +if $pkg_config libdrm_intel; then + libdrm_intel_cflags=3D"$($pkg_config --cflags libdrm_intel)" + libdrm_intel_libs=3D"$($pkg_config --libs libdrm_intel)" + libdrm_intel=3D"yes" +fi + if test "$opengl" !=3D "no" ; then opengl_pkgs=3D"epoxy gbm" if $pkg_config $opengl_pkgs; then @@ -6047,6 +6061,9 @@ if test "$want_tools" =3D "yes" ; then if [ "$vhost_user" =3D "yes" ] && [ "$linux" =3D "yes" ] ; then tools=3D"vhost-user-input\$(EXESUF) $tools" fi + if [ "$linux" =3D "yes" -a "$virglrenderer" =3D "yes" -a "$libdrm" =3D "= yes" ] ; then + tools=3D"vhost-user-gpu\$(EXESUF) $tools" + fi fi if test "$softmmu" =3D yes ; then if test "$linux" =3D yes; then @@ -6942,6 +6959,18 @@ if test "$opengl" =3D "yes" ; then fi fi =20 +if test "$libdrm" =3D "yes" ; then + echo "CONFIG_LIBDRM=3Dy" >> $config_host_mak + echo "LIBDRM_LIBS=3D$libdrm_libs" >> $config_host_mak + echo "LIBDRM_CFLAGS=3D$libdrm_cflags" >> $config_host_mak +fi + +if test "$libdrm_intel" =3D "yes" ; then + echo "CONFIG_LIBDRM_INTEL=3Dy" >> $config_host_mak + echo "LIBDRM_INTEL_LIBS=3D$libdrm_intel_libs" >> $config_host_mak + echo "LIBDRM_INTEL_CFLAGS=3D$libdrm_intel_cflags" >> $config_host_mak +fi + if test "$malloc_trim" =3D "yes" ; then echo "CONFIG_MALLOC_TRIM=3Dy" >> $config_host_mak fi @@ -7835,6 +7864,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/50-qemu-gpu.json b/contrib/vhost-user-g= pu/50-qemu-gpu.json new file mode 100644 index 0000000000..99188a13f8 --- /dev/null +++ b/contrib/vhost-user-gpu/50-qemu-gpu.json @@ -0,0 +1,5 @@ +{ + "description": "QEMU vhost-user-gpu", + "type": "gpu", + "binary": "/usr/libexec/qemu/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..4da8c09cbb --- /dev/null +++ b/contrib/vhost-user-gpu/Makefile.objs @@ -0,0 +1,10 @@ +vhost-user-gpu-obj-y =3D main.o virgl.o drm.o + +main.o-cflags :=3D $(PIXMAN_CFLAGS) +main.o-libs :=3D $(PIXMAN_LIBS) + +virgl.o-cflags :=3D $(VIRGL_CFLAGS) +virgl.o-libs :=3D $(VIRGL_LIBS) + +drm.o-cflags :=3D $(LIBDRM_CFLAGS) $(LIBDRM_INTEL_CFLAGS) +drm.o-libs :=3D $(LIBDRM_LIBS) $(LIBDRM_INTEL_LIBS) --=20 2.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555083963; cv=none; d=zoho.com; s=zohoarc; b=NAE3ayJ2BXkcsqzhgHsQOAoWyJIWY37CZe2H2X/CBnzjKgNA+cRRHxJMHeECC1hSJKS+/Z07tg1ReoYw7tnMWZeRrNkB6Nu4jSCGilZ2hQqKAGgdeckNoz/+hsg8DimtGeG6Xl4llq0Exx7GX0Jh06ZQWr/rCwFqrHZQXUemmvY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555083963; 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=hF2D6IaPphwVmIPP63RzAG4vuBjVoT12pTfIfFcdBCM=; b=Ja63w2CApeGeouMVFJqBF13po4z1MDw2h9rEJC4hAcFG5q9rnQZPAkKET8ysL1FfiyNmXeSQNEBECk7flL8bmqMLt6W5xgmGyeMoacdNztoV1pD2KnJ5AQxkhfZnu035DY70AW02odKOjHcHF+xLburMQ2eGZN+cmW5xl6LIDmQ= 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 1555083963593242.84048331190445; Fri, 12 Apr 2019 08:46:03 -0700 (PDT) Received: from localhost ([127.0.0.1]:38949 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyNE-0007bi-29 for importer@patchew.org; Fri, 12 Apr 2019 11:45:52 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37261) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyGE-0001CR-Is for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:42 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyG5-00023p-I2 for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:34 -0400 Received: from mx1.redhat.com ([209.132.183.28]:40176) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyG5-00023M-0v; Fri, 12 Apr 2019 11:38:29 -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 4BB5E316B78E; Fri, 12 Apr 2019 15:38:28 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id ECE115D9C5; Fri, 12 Apr 2019 15:38:20 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:45 +0200 Message-Id: <20190412153647.19027-12-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-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.41]); Fri, 12 Apr 2019 15:38:28 +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 v5 11/13] 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: "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson 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 | 372 +++++++++------------------------ hw/display/virtio-vga.c | 11 +- hw/display/Makefile.objs | 2 +- 7 files changed, 458 insertions(+), 319 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 bdcd33c925..305ea5fff3 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 c35025b6fb..b0a71537b9 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -23,9 +23,9 @@ #include "hw/virtio/virtio-gpu-bswap.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 @@ -39,7 +39,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__); \ @@ -107,10 +107,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, @@ -141,53 +141,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) { @@ -236,21 +189,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) { @@ -259,7 +197,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)); } @@ -268,9 +206,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)); @@ -282,11 +221,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; } @@ -379,7 +319,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, @@ -401,7 +341,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 @@ -433,7 +373,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); } @@ -563,7 +503,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; @@ -571,7 +511,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, @@ -581,7 +521,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); @@ -612,14 +552,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; @@ -648,7 +588,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); @@ -671,7 +611,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); @@ -889,7 +830,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 @@ -898,14 +839,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; } @@ -917,6 +858,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); @@ -927,7 +881,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; } @@ -945,7 +899,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 @@ -954,7 +908,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) @@ -992,75 +946,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) -{ + virtio_gpu_handle_cursor(&g->parent_obj.parent_obj, g->cursor_vq); } =20 -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; -} - -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, @@ -1083,10 +971,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() @@ -1201,8 +1090,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; } @@ -1231,84 +1120,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->parent_obj.conf.flags &=3D ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED= ); } =20 - 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); - #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); + + if (!virtio_gpu_base_device_realize(qdev, + virtio_gpu_handle_ctrl_cb, + virtio_gpu_handle_cursor_cb, + errp)) { + return; } =20 + 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) @@ -1316,21 +1156,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); @@ -1346,15 +1181,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 /* @@ -1385,18 +1242,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 @@ -1404,27 +1258,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 @@ -1434,26 +1283,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 a2b803b75f..33af54d44f 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 dbd453ab1b..f1c46b8e3e 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -42,7 +42,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.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555084168; cv=none; d=zoho.com; s=zohoarc; b=gEcS9OaMjceSjwQzqOWOi/Hfg6i2EnnKVHg9eYgLzC2NJIi0iC4Nsuqr7Yf2PRSiGHQM0rqd3SbX5mK3tv738EbSfmo2Sw4232lNAvRjAf/N1SU1LR277VpwKJDdlV7GYFQEqsoV2g+i4C7H+GQ8YM/TJcFxyyAtIcUGIHIIAGA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555084168; 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=wOUJ2cDRQkfLFaEWkWmdCPcrRKyzCwtQzMy0NlqWXpQ=; b=nLaXvKhlfK1a25qOwXEDuWT6TWsXw9SozIE9jRksP3aWacR1TDDiOToOGXnZfUIDeD43ecUcbqbstJPNTaLIVujcEZahm8J1DHHCpWLCuopmMFgPsQEWAedE/lz9ltPW1fmQ8iKdayjC0+hXmqSkEgndmbm6WKxFjrLAGHJGEZY= 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 1555084168255568.6301078688142; Fri, 12 Apr 2019 08:49:28 -0700 (PDT) Received: from localhost ([127.0.0.1]:38975 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyQf-0002CT-6S for importer@patchew.org; Fri, 12 Apr 2019 11:49:25 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37340) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyGR-0001Oq-N5 for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:53 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyGP-0002BF-P1 for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:51 -0400 Received: from mx1.redhat.com ([209.132.183.28]:37674) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyGJ-00026V-VL; Fri, 12 Apr 2019 11:38:47 -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 F36A130BB340; Fri, 12 Apr 2019 15:38:38 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id D5FEC5D6A9; Fri, 12 Apr 2019 15:38:29 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:46 +0200 Message-Id: <20190412153647.19027-13-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-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, 12 Apr 2019 15:38:39 +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 v5 12/13] 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: "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson 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 305ea5fff3..3b88bf4d1f 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 @@ -62,36 +62,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 33af54d44f..caa3e138b0 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; @@ -169,7 +148,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), @@ -178,10 +157,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); @@ -191,48 +170,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 476fe4c49c..ef25553169 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1656,7 +1656,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.21.0.313.ge35b8cb8e2 From nobody Sat Apr 20 03:45:11 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=1555083837; cv=none; d=zoho.com; s=zohoarc; b=UMcw5kGjU1gVXHSUHWVaahjPuM5nijOrKlGFFmJ/ByuMJq5m9ChlrWCJ9bMUmdvLT4VxLOqDu7hXTR6eT7etWEfUm6PWipODb8YaFdBjfVAb5sL65Gl+as8YpG+2ZWvw+x0+cPb/1j1T0WnSsbWRhjKotXD0Nir358+25BNmg+0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555083837; 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=1EeT6YoY6N4P/MPIFpMVSUiKS+bgsBCB7NWI3KKdrTg=; b=GIXDMBHK4pCR7Wg1aLAwtJEw7OpuruWX520uia8Ue6CAVp5t1bfQpxkpDbA+8x99cgHDCV4XAh25xAz2Nnssj3hLO723O4dBzqb/EzB02FgpiGm2ddmts5CDKUhcJR7fntlZ9lTm4Ny+v0CZTE8vZcRvByEWqOPQYAK4jkgepdo= 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 1555083837106274.76099085698365; Fri, 12 Apr 2019 08:43:57 -0700 (PDT) Received: from localhost ([127.0.0.1]:38883 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyLJ-0005pk-Qn for importer@patchew.org; Fri, 12 Apr 2019 11:43:53 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37374) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEyGU-0001Rg-Jj for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:57 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEyGR-0002C0-Mg for qemu-devel@nongnu.org; Fri, 12 Apr 2019 11:38:54 -0400 Received: from mx1.redhat.com ([209.132.183.28]:57628) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEyGP-0002B6-OU; Fri, 12 Apr 2019 11:38:50 -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 A5A6AC7CA9; Fri, 12 Apr 2019 15:38:48 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2722319738; Fri, 12 Apr 2019 15:38:40 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 12 Apr 2019 17:36:47 +0200 Message-Id: <20190412153647.19027-14-marcandre.lureau@redhat.com> In-Reply-To: <20190412153647.19027-1-marcandre.lureau@redhat.com> References: <20190412153647.19027-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.39]); Fri, 12 Apr 2019 15:38:48 +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 v5 13/13] 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: "Michael S. Tsirkin" , qemu-ppc@nongnu.org, Gerd Hoffmann , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , David Gibson 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 | 572 +++++++++++++++++++++++++++++ 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, 744 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..3509d2b45a --- /dev/null +++ b/hw/display/vhost-user-gpu.c @@ -0,0 +1,572 @@ +/* + * 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 size; /* the following payload size */ + union { + VhostUserGpuCursorPos cursor_pos; + VhostUserGpuCursorUpdate cursor_update; + VhostUserGpuScanout scanout; + VhostUserGpuUpdate update; + VhostUserGpuDMABUFScanout dmabuf_scanout; + uint64_t u64; + } payload; +} QEMU_PACKED VhostUserGpuMsg; + +static VhostUserGpuMsg m __attribute__ ((unused)); +#define VHOST_USER_GPU_HDR_SIZE (sizeof(m.request) + sizeof(m.size)) + +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_unblock(VhostUserGPU *g) +{ + uint32_t ok; + + qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)&ok, sizeof(ok)); + +} +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: { + uint64_t u64 =3D 0; + qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)&u64, sizeof(u64)); + 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 { 0, }; + display_info.hdr.type =3D VIRTIO_GPU_RESP_OK_DISPLAY_INFO; + virtio_gpu_base_fill_display_info(VIRTIO_GPU_BASE(g), &display_inf= o); + qemu_chr_fe_write(&g->vhost_chr, + (uint8_t *)&display_info, sizeof(display_info)); + 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; + 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 *)&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->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-input: 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 3b88bf4d1f..ef8d6ecb49 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 c696ad2a13..b06458070f 100644 --- a/vl.c +++ b/vl.c @@ -238,6 +238,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 72be57a403..492ea35104 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -104,6 +104,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 =20 diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index f1c46b8e3e..9c342eef41 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -43,8 +43,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.21.0.313.ge35b8cb8e2