From nobody Tue Apr 7 23:08:15 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1771948669; cv=none; d=zohomail.com; s=zohoarc; b=Z2P1ik8LZOJ6ihbtIop3GZktBWPiXxGFElQFHh17bg5JxbRnsI6MxtJv+6iqpUHHT4X/7AWygjsYyQh2xCOpQZ0ToZzQ9xGX9yT0C6QR5qy1AWsrBA+jsIA2u3JOYY38nfJpcNbbgzVrK+eFfs1Cnju5z3j0IyL0lSJm7m6cHjU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1771948669; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=v9+lW+DSDlpSHt3UBv5+MmnqIWtMkhzwTy2QDZ17jkM=; b=jNAb5kUk6B7QgbrBLs6C4OGnH8Wx08WLveLf86vEROBIEhja7EvkmuoHYMhINFrbUOHO+nDelNbMzW8KRBxig4G+OqrR0T8PS+p8Prv8+66a1ejvCFZaGsHwhd81yi9Ok010X+fliwBfwEjyAqJ9usu3bT9dPgxVGN8XL7wy3Ok= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1771948669264501.1570005997678; Tue, 24 Feb 2026 07:57:49 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vuun4-0006Uw-AK; Tue, 24 Feb 2026 10:57:38 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vuun1-0006UC-E7 for qemu-devel@nongnu.org; Tue, 24 Feb 2026 10:57:35 -0500 Received: from mail-vk1-xa29.google.com ([2607:f8b0:4864:20::a29]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vuumx-0000oN-FY for qemu-devel@nongnu.org; Tue, 24 Feb 2026 10:57:34 -0500 Received: by mail-vk1-xa29.google.com with SMTP id 71dfb90a1353d-567543b8989so2714963e0c.2 for ; Tue, 24 Feb 2026 07:57:30 -0800 (PST) Received: from gmail.com (ip190-5-140-142.intercom.com.sv. [190.5.140.142]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-94da8a05060sm9902928241.6.2026.02.24.07.57.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 07:57:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771948649; x=1772553449; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=v9+lW+DSDlpSHt3UBv5+MmnqIWtMkhzwTy2QDZ17jkM=; b=Kv3vkqyEmbvCkPgeZbzcPiBZXt5Z9Hbi48+iFyp8AdWZbIrZmygnRqdalCJ0T04HKo PmWqNvoQ/y3jyBzGn7agPt7RfYAn+MfR3wxw+LaOpRrkSt/tvqWUBM4aK1GyDHd4MYNZ 5Gr8bRU67aWfmFNAOoxDypNIJvuSiVUoD/FHq5iqjdb6iANDx6BP9UyUS2HXBzREHDm+ u72Y6p8mUNu98Re0a/CrxI1kMZHOR7PxDCII2bCiOXusZoT2RgtyEz1WTeOlUWX4yF7U 6XiUT/XWshOydiI/8nqwjVxsybCFQkLqjC5RZmwv9BhcQOJOrofr2+FsBCe1szGDNgO8 JsFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771948649; x=1772553449; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=v9+lW+DSDlpSHt3UBv5+MmnqIWtMkhzwTy2QDZ17jkM=; b=qwKSfc9adVPMUQ1dsNIrd0V3n499dmNE7fpwwOJgr7DJgbbnM76CTs8+MKKzkD1ILj E5oAgZZF2nronREvoYL0ujnOWI8NxQLGWWoFTmRETqVtIS384cBRk3ak4vwQBddP/LQo CVZytCQ+LmmEfxRO36PadzieJyFS8Z+C5rKt8QYqazXsRnwV7klVnOU83RFlHQXBDzYe 9CfgjJYhimcS9Oh/ruaqKPsQUWVhyAR7VGMA53VacxHAarzT+CMBmAkk91/iIP9Cdo+u sqk/zoP+62UXSkltIi0Gtv1jYLIZsjJstITwuGSXcLW+fCIVrFMpqvwfxIPRVVV63SHO IyuQ== X-Gm-Message-State: AOJu0YwznB1v8j9qExpuXjFtyiyA4fpYf6cV8eusSELko+ctlgjvxgrd BH/+k9I5PDC/T+sYPEueqr104ArSjV7TwN9Kzdj91LZ6pt+2RwCYlYJqTnFm92Hx X-Gm-Gg: ATEYQzxAAGdTART0iTr9lh4DqqCi0A402mNPzwUD8slIsClKMoY6iLdMGzbURLvsq1x ly1ICHWW6XW35Ls1GLWf3DFxe48vfg2QQD1ybg2NEfpOvWbv/FLB/pB0ou45XsxFKc0/Saf66qE NELBVACeTCnGro7EIN03BkcTCFtcnSkOqImvMtQgVKtC1to1+X9/duANFUUHEGTkR3vX8qpQbA8 pnhh+2cjEZP1EEjaPrGvhp89meXe4s9mqwWgFt0PEDaPj57IpKLlGVfpPMlu6OyamsdKACIXDRr mUlq7EXi5DbanAvdR/XqCihEmJgOEm/mqd4n6rBsBN/QaRVcTtqHQa6fOSxbcNvRKCgzWNbPWsx cDLes5MIHAzegoLLa9bVWtiNg+b4QiUCX39uMGCugQtW56cLMLJasGG0rBat6aX9tGdYUb6RYPp E+S0fvGtoBtATO19acoHlslCurHz0+8Bz9l7Wlv6ojCGso62D9FJYOyKoVYA== X-Received: by 2002:a05:6102:4192:b0:5fd:7140:e38b with SMTP id ada2fe7eead31-5feb310c798mr4329570137.35.1771948649433; Tue, 24 Feb 2026 07:57:29 -0800 (PST) From: "Edgar E. Iglesias" To: qemu-devel@nongnu.org, "Michael S. Tsirkin" Cc: alex.bennee@linaro.org, bill.mills@linaro.org, edgar.iglesias@amd.com Subject: [PATCH v2 1/4] virtio: Add virtio_queue_get_rings Date: Tue, 24 Feb 2026 16:57:18 +0100 Message-ID: <20260224155721.612314-2-edgar.iglesias@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260224155721.612314-1-edgar.iglesias@gmail.com> References: <20260224155721.612314-1-edgar.iglesias@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.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; Received-SPF: pass client-ip=2607:f8b0:4864:20::a29; envelope-from=edgar.iglesias@gmail.com; helo=mail-vk1-xa29.google.com X-Spam_score_int: 12 X-Spam_score: 1.2 X-Spam_bar: + X-Spam_report: (1.2 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_SBL_CSS=3.335, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1771948671627158501 Content-Type: text/plain; charset="utf-8" From: "Edgar E. Iglesias" Signed-off-by: Edgar E. Iglesias --- hw/virtio/virtio.c | 16 ++++++++++++++++ include/hw/virtio/virtio.h | 14 ++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index e9d5532952..00d387ba05 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2402,6 +2402,22 @@ void virtio_queue_set_rings(VirtIODevice *vdev, int = n, hwaddr desc, virtio_init_region_cache(vdev, n); } =20 +void virtio_queue_get_rings(VirtIODevice *vdev, int n, hwaddr *desc, + hwaddr *avail, hwaddr *used) +{ + assert(vdev->vq[n].vring.num); + + if (desc) { + *desc =3D vdev->vq[n].vring.desc; + } + if (avail) { + *avail =3D vdev->vq[n].vring.avail; + } + if (used) { + *used =3D vdev->vq[n].vring.used; + } +} + void virtio_queue_set_num(VirtIODevice *vdev, int n, int num) { /* Don't allow guest to flip queue between existent and diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 9dd93cf965..3a24d109fb 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -361,6 +361,20 @@ int virtio_queue_get_max_num(VirtIODevice *vdev, int n= ); int virtio_get_num_queues(VirtIODevice *vdev); void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc, hwaddr avail, hwaddr used); +/** + * virtio_queue_get_rings() - read ring addresses for a virtqueue + * @vdev: the virtio device + * @n: queue index + * @desc: optional pointer for the descriptor ring address + * @avail: optional pointer for the available ring address + * @used: optional pointer for the used ring address + * + * Returns the ring addresses configured for queue @n. The queue must + * be initialized (non-zero size); passing NULL for any output argument + * skips that address. + */ +void virtio_queue_get_rings(VirtIODevice *vdev, int n, hwaddr *desc, + hwaddr *avail, hwaddr *used); void virtio_queue_update_rings(VirtIODevice *vdev, int n); void virtio_init_region_cache(VirtIODevice *vdev, int n); void virtio_queue_set_align(VirtIODevice *vdev, int n, int align); --=20 2.43.0 From nobody Tue Apr 7 23:08:15 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1771948679; cv=none; d=zohomail.com; s=zohoarc; b=UgFnW8/vRT5u8ht60IswzODm8K+GJLWZxq7ivSytIzN5p0d5Ujt+dTpuE7yHieKykG5HKK5DoVpGz8er0wVhvkjgCVaOPDwVPVUIiESB3jlSMjbAShPSY8CHZ8L3vhudBLiDSPpfNQioz6VL5ZLYEtbTMJ1m/PA7iDjy965GzRU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1771948679; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=AGhTD5gZ5s5Ozo3gWFMv0h8te1YKb3tGqQFzCyeo4ZQ=; b=i8rkicmGVdg6Sch2VJW3kYDl6rZcYmYnOyUOolIROW7qpz9YsGSYW58T70645TtYlBVYrJVyx8uEzDLoNLpCLOItPA5lCbjMz2i4C+11vfQ80IkSIkr7LRMaTqcbt2zwxIrBQdh3gfX36faiOxKszvD+Y8EZhBeaqK27Zy/y+RA= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1771948679110658.2739400422051; Tue, 24 Feb 2026 07:57:59 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vuun8-0006Vj-Bq; Tue, 24 Feb 2026 10:57:43 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vuun6-0006VD-1M for qemu-devel@nongnu.org; Tue, 24 Feb 2026 10:57:40 -0500 Received: from mail-vk1-xa30.google.com ([2607:f8b0:4864:20::a30]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vuun1-0000oq-59 for qemu-devel@nongnu.org; Tue, 24 Feb 2026 10:57:39 -0500 Received: by mail-vk1-xa30.google.com with SMTP id 71dfb90a1353d-5674689e507so2397828e0c.3 for ; Tue, 24 Feb 2026 07:57:33 -0800 (PST) Received: from gmail.com (ip190-5-140-142.intercom.com.sv. [190.5.140.142]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-568e579c2ccsm12990983e0c.2.2026.02.24.07.57.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 07:57:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771948652; x=1772553452; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=AGhTD5gZ5s5Ozo3gWFMv0h8te1YKb3tGqQFzCyeo4ZQ=; b=Jv9AHj1tJMrl567yEUCURoRcn9sYT3HcM4oO61iTdF0YO88Nl4Kj84RQueQZ1ZWaKn DeCntbVLjRdSAZCqSovJXPCtBSusQS2gQFD/Je3bmfVbwOtzTGBL9Loj0stGyr3D7wH7 mNbhvhWzl+2jYy0Hf6LsZv0iwGYeWVzvx341yzPm+lo812dwR9ViQZCJ4/q5RZdkhRKD l+bpMJ2uosnZEWx5CqZJvlpidFP8n93WPPvjpIBIo3956KRFUTXYyB2u9ox0IXLunmKE 4FMVUNRfPwO2krLysMx4mFrGVlJaCKThYoRNHn6aGjCtpyNSDGa+m/jEa0MVXEXZ7lry Pq3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771948652; x=1772553452; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=AGhTD5gZ5s5Ozo3gWFMv0h8te1YKb3tGqQFzCyeo4ZQ=; b=UHVAvNbPwA+ki/GsY/w0IDI1IgiIz7g20H3R8ulPr3PHzbC/4U1KBKJPJfcBoKNMeE fyhpcp4hcvsJ3RkxxJzm513TTnC0DNrUrbYHmFCGDUGwg/BlKlBL9MpcabaiRys5/o6p apNoqqCpIjtIyj6mcURG/GDJF00sZotqaCZAI/mP7kvVWE3/jIXhv14b4Cizy3wbuAYb VVuNF+yvKSYstQ5dkyI4BYBXTPBRymgxlcD6a3fQxOuzS22555qbGeMNcS6QmVzGIIw6 dqEg7LntsIIjMU3UhKXO5+O+dr2yvAuw3VFr7VmzttfXI8V43Sw18UulzC7FlF2JmEc/ hGug== X-Gm-Message-State: AOJu0Yxfn9zI31vysHj+Clg7FlUFKUVzuwi3r/an8PG9H9IHmOl4DqHU pLGES6J5qpWAgPLeQvlnMTez1IRP9BlzBFfJNPQhsEMlYa91qeZ6Mfw3kbzLsMHe X-Gm-Gg: ATEYQzyN51NMUczwq1t1kWE2SJRWy9dMU78xVuI6+bFSYHLV8Jm21jdvHLVT6G7LxML 3id8jv6WDGY1AF3OZ9wv70bTrkh7KJ8UG5Z53VbXcoJ2NI4B4Gv6YisBUBVje5xl/Dqie8xZ0CR Qs+8B0ktRy0UqRLfJltkPWoWjyE0hiYFUGWHrCqGlFtMfzz71eLw0kjgeV6zRK9Jfl3Sq1e8IT8 KETOTZjnouAXgDFwa2irvJCYt58hsSavO64MHZ7mLCi4KFAcdWd2EagXokhGbW6T8imy0zJAX1T h8cI4LmUiOuaAwmuDAHam9kymFrAtstv/PgFjZUfs7/1N75JSLazMyUIYfZU0yT7b8T8oeodtuF nbi7A6fi2MRIE7ZsktcI7lJEIXwj+dc4sVeu3G+kbgQozEXs1Sj/UzRdk/3C8YVsXcGmhfqCX05 E0xFno5E1DW+jGSi0HMJEQMcCxswV2SnppNUI/UqF3xzjW659vnSUnTSo88Q== X-Received: by 2002:a05:6122:1809:b0:567:47fc:3a9b with SMTP id 71dfb90a1353d-568e47a64e8mr4487728e0c.5.1771948651743; Tue, 24 Feb 2026 07:57:31 -0800 (PST) From: "Edgar E. Iglesias" To: qemu-devel@nongnu.org, Paolo Bonzini , "Michael S. Tsirkin" Cc: alex.bennee@linaro.org, bill.mills@linaro.org, edgar.iglesias@amd.com Subject: [PATCH v2 2/4] virtio: Add the virtio-msg transport Date: Tue, 24 Feb 2026 16:57:19 +0100 Message-ID: <20260224155721.612314-3-edgar.iglesias@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260224155721.612314-1-edgar.iglesias@gmail.com> References: <20260224155721.612314-1-edgar.iglesias@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.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; Received-SPF: pass client-ip=2607:f8b0:4864:20::a30; envelope-from=edgar.iglesias@gmail.com; helo=mail-vk1-xa30.google.com X-Spam_score_int: 12 X-Spam_score: 1.2 X-Spam_bar: + X-Spam_report: (1.2 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_SBL_CSS=3.335, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1771948679794158500 Content-Type: text/plain; charset="utf-8" From: "Edgar E. Iglesias" Add the new virtio-msg transport together with a framework for virtio-msg buses. Signed-off-by: Edgar E. Iglesias --- hw/virtio/Kconfig | 4 + hw/virtio/meson.build | 5 + hw/virtio/virtio-msg-bus.c | 89 +++ hw/virtio/virtio-msg.c | 986 ++++++++++++++++++++++++++++ include/hw/virtio/virtio-msg-bus.h | 95 +++ include/hw/virtio/virtio-msg-prot.h | 949 ++++++++++++++++++++++++++ include/hw/virtio/virtio-msg.h | 56 ++ 7 files changed, 2184 insertions(+) create mode 100644 hw/virtio/virtio-msg-bus.c create mode 100644 hw/virtio/virtio-msg.c create mode 100644 include/hw/virtio/virtio-msg-bus.h create mode 100644 include/hw/virtio/virtio-msg-prot.h create mode 100644 include/hw/virtio/virtio-msg.h diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig index 8895682c61..f362c50c59 100644 --- a/hw/virtio/Kconfig +++ b/hw/virtio/Kconfig @@ -26,6 +26,10 @@ config VIRTIO_MMIO bool select VIRTIO =20 +config VIRTIO_MSG + bool + select VIRTIO + config VIRTIO_CCW bool select VIRTIO diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index 6675b63ce6..a5b9234df1 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -13,6 +13,11 @@ specific_virtio_ss =3D ss.source_set() specific_virtio_ss.add(files('virtio.c')) specific_virtio_ss.add(files('virtio-qmp.c')) =20 +specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MSG', if_true: files( + 'virtio-msg.c', + 'virtio-msg-bus.c', +)) + if have_vhost system_virtio_ss.add(files('vhost.c')) system_virtio_ss.add(files('vhost-backend.c', 'vhost-iova-tree.c')) diff --git a/hw/virtio/virtio-msg-bus.c b/hw/virtio/virtio-msg-bus.c new file mode 100644 index 0000000000..e907fd64ec --- /dev/null +++ b/hw/virtio/virtio-msg-bus.c @@ -0,0 +1,89 @@ +/* + * VirtIO MSG bus. + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * Written by Edgar E. Iglesias + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/virtio/virtio-msg-bus.h" + +bool virtio_msg_bus_connect(BusState *bus, + const VirtIOMSGBusPort *port, + void *opaque) +{ + VirtIOMSGBusDevice *bd =3D virtio_msg_bus_get_device(bus); + + if (!bd) { + /* Nothing connected to this virtio-msg device. Ignore. */ + return false; + } + + bd->peer =3D port; + bd->opaque =3D opaque; + return true; +} + +void virtio_msg_bus_process(VirtIOMSGBusDevice *bd) +{ + VirtIOMSGBusDeviceClass *bdc; + bdc =3D VIRTIO_MSG_BUS_DEVICE_CLASS(object_get_class(OBJECT(bd))); + + bdc->process(bd); +} + +int virtio_msg_bus_send(BusState *bus, VirtIOMSG *msg_req) +{ + VirtIOMSGBusDevice *bd =3D virtio_msg_bus_get_device(bus); + VirtIOMSGBusDeviceClass *bdc; + int r =3D VIRTIO_MSG_NO_ERROR; + + bdc =3D VIRTIO_MSG_BUS_DEVICE_CLASS(object_get_class(OBJECT(bd))); + + if (bdc->send) { + r =3D bdc->send(bd, msg_req); + } + return r; +} + +static void virtio_msg_bus_class_init(ObjectClass *klass, const void *data) +{ + BusClass *bc =3D BUS_CLASS(klass); + + bc->max_dev =3D 1; +} + +static const TypeInfo virtio_msg_bus_info =3D { + .name =3D TYPE_VIRTIO_MSG_BUS, + .parent =3D TYPE_BUS, + .instance_size =3D sizeof(BusState), + .class_init =3D virtio_msg_bus_class_init, +}; + +static void virtio_msg_bus_device_class_init(ObjectClass *klass, + const void *data) +{ + DeviceClass *k =3D DEVICE_CLASS(klass); + + k->bus_type =3D TYPE_VIRTIO_MSG_BUS; +} + +static const TypeInfo virtio_msg_bus_device_type_info =3D { + .name =3D TYPE_VIRTIO_MSG_BUS_DEVICE, + .parent =3D TYPE_DEVICE, + .instance_size =3D sizeof(VirtIOMSGBusDevice), + .abstract =3D true, + .class_size =3D sizeof(VirtIOMSGBusDeviceClass), + .class_init =3D virtio_msg_bus_device_class_init, +}; + +static void virtio_msg_bus_register_types(void) +{ + type_register_static(&virtio_msg_bus_info); + type_register_static(&virtio_msg_bus_device_type_info); +} + +type_init(virtio_msg_bus_register_types) diff --git a/hw/virtio/virtio-msg.c b/hw/virtio/virtio-msg.c new file mode 100644 index 0000000000..e77bd47e0f --- /dev/null +++ b/hw/virtio/virtio-msg.c @@ -0,0 +1,986 @@ +/* + * Virtio MSG bindings + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * Written by Edgar E. Iglesias . + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "hw/core/irq.h" +#include "hw/core/qdev-properties.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-msg-bus.h" +#include "hw/virtio/virtio-msg.h" +#include "migration/qemu-file.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "trace.h" + +#define VIRTIO_MSG_VENDOR_ID 0x554D4551 /* 'QEMU' */ + +static bool virtio_msg_bad(VirtIOMSGProxy *s, VirtIOMSG *msg) +{ + bool drop =3D false; + size_t min_size; + unsigned int n; + + min_size =3D virtio_msg_header_size(); + switch (msg->msg_id) { + case VIRTIO_MSG_GET_DEVICE_STATUS: + case VIRTIO_MSG_DEVICE_INFO: + break; + case VIRTIO_MSG_GET_FEATURES: + min_size +=3D sizeof msg->get_features; + break; + case VIRTIO_MSG_SET_FEATURES: + n =3D msg->set_features.num; + + /* We expect at least one feature block. */ + if (n =3D=3D 0 || n > VIRTIO_MSG_MAX_FEATURE_NUM) { + drop =3D true; + break; + } + + min_size +=3D sizeof msg->set_features + n * 4; + break; + case VIRTIO_MSG_GET_CONFIG: + min_size +=3D sizeof msg->get_config; + break; + case VIRTIO_MSG_SET_CONFIG: + if (msg->set_config.size > VIRTIO_MSG_MAX_CONFIG_BYTES) { + drop =3D true; + break; + } + + min_size +=3D sizeof msg->set_config + msg->set_config.size; + break; + case VIRTIO_MSG_SET_DEVICE_STATUS: + min_size +=3D sizeof msg->set_device_status; + break; + case VIRTIO_MSG_GET_VQUEUE: + min_size +=3D sizeof msg->get_vqueue; + break; + case VIRTIO_MSG_SET_VQUEUE: + min_size +=3D sizeof msg->set_vqueue; + break; + case VIRTIO_MSG_RESET_VQUEUE: + min_size +=3D sizeof msg->reset_vqueue; + break; + case VIRTIO_MSG_EVENT_AVAIL: + min_size +=3D sizeof msg->event_avail; + break; + default: + /* Unexpected message. */ + drop =3D true; + break; + } + + /* Accept large messages allowing future backwards compatible extensio= ns. */ + if (drop || + msg->msg_size < min_size || msg->msg_size > VIRTIO_MSG_MAX_SIZE) { + return true; + } + + if (msg->dev_num >=3D ARRAY_SIZE(s->devs)) { + return true; + } + + return false; +} + +static VirtIODevice *virtio_msg_vdev(VirtIOMSGProxy *s, uint16_t dev_num) +{ + VirtIODevice *vdev; + + vdev =3D virtio_bus_get_device(&s->devs[dev_num].bus); + return vdev; +} + +static VirtIODevice *virtio_msg_lookup_vdev(VirtIOMSGProxy *s, uint16_t de= v_num, + const char *what) +{ + VirtIODevice *vdev =3D virtio_msg_vdev(s, dev_num); + + if (!vdev) { + error_report("%s: No virtio device on bus %s!", + what, BUS(&s->devs[dev_num].bus)->name); + } + + return vdev; +} + +static void virtio_msg_bus_get_devices(VirtIOMSGProxy *s, + VirtIOMSG *msg) +{ + VirtIOMSG msg_resp; + uint8_t data[VIRTIO_MSG_MAX_DEVS / 8] =3D {0}; + uint16_t req_offset =3D msg->bus_get_devices.offset; + uint16_t offset =3D MIN(req_offset, VIRTIO_MSG_MAX_DEVS); + uint16_t max_window =3D VIRTIO_MSG_MAX_DEVS - offset; + uint16_t num =3D MIN(msg->bus_get_devices.num, max_window); + uint16_t next_offset =3D offset + num; + int i; + + for (i =3D 0; i < num; i++) { + uint16_t dev_idx =3D offset + i; + VirtIODevice *vdev =3D virtio_msg_vdev(s, dev_idx); + + if (vdev) { + data[i / 8] |=3D 1U << (i & 7); + } + } + + virtio_msg_pack_bus_get_devices_resp(&msg_resp, + offset, num, next_offset, + data); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_bus_ping(VirtIOMSGProxy *s, VirtIOMSG *msg) +{ + VirtIOMSG msg_resp; + + virtio_msg_pack_bus_ping_resp(&msg_resp, msg->bus_ping.data); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_device_info(VirtIOMSGProxy *s, + VirtIOMSG *msg) +{ + VirtIODevice *vdev =3D virtio_msg_vdev(s, msg->dev_num); + uint32_t config_len =3D 0; + uint32_t device_id =3D 0; + uint32_t max_vqs =3D 0; + VirtIOMSG msg_resp; + + if (vdev) { + device_id =3D vdev->device_id; + config_len =3D vdev->config_len; + max_vqs =3D virtio_get_num_queues(vdev); + } else { + error_report("%s: No virtio device on bus %s!", + __func__, BUS(&s->devs[msg->dev_num].bus)->name); + } + + virtio_msg_pack_get_device_info_resp(&msg_resp, msg->dev_num, msg->tok= en, + device_id, + VIRTIO_MSG_VENDOR_ID, + /* Feature bits */ + 64, + config_len, + max_vqs, + 0, 0); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_get_features(VirtIOMSGProxy *s, + VirtIOMSG *msg) +{ + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, msg->dev_num, __func_= _); + VirtIOMSG msg_resp; + uint32_t index =3D msg->get_features.index; + uint32_t f[VIRTIO_MSG_MAX_FEATURE_NUM] =3D { 0 }; + uint32_t num =3D MIN(msg->get_features.num, VIRTIO_MSG_MAX_FEATURE_NUM= ); + uint64_t features =3D 0; + + if (vdev) { + VirtioDeviceClass *vdc =3D VIRTIO_DEVICE_GET_CLASS(vdev); + + features =3D vdev->host_features & ~vdc->legacy_features; + } + + /* We only have 64 feature bits. If driver asks for more, return zeros= */ + if (index < 2) { + features >>=3D index * 32; + f[0] =3D features; + f[1] =3D features >> 32; + } + + /* If index is out of bounds, we respond with num=3D0, f=3D0. */ + virtio_msg_pack_get_features_resp(&msg_resp, msg->dev_num, msg->token, + index, num, f); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_set_features(VirtIOMSGProxy *s, + VirtIOMSG *msg) +{ + VirtIOMSG msg_resp; + unsigned int i; + uint64_t f; + + f =3D s->devs[msg->dev_num].guest_features; + + for (i =3D 0; i < msg->set_features.num; i++) { + unsigned int feature_index =3D i + msg->set_features.index; + + /* We only support up to 64bits */ + if (feature_index >=3D 2) { + break; + } + + f =3D deposit64(f, feature_index * 32, 32, msg->set_features.b32[i= ]); + } + + s->devs[msg->dev_num].guest_features =3D f; + + virtio_msg_pack_set_features_resp(&msg_resp, msg->dev_num, msg->token); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_soft_reset(VirtIOMSGProxy *s, uint16_t dev_num) +{ + assert(dev_num < ARRAY_SIZE(s->devs)); + + virtio_bus_reset(&s->devs[dev_num].bus); + s->devs[dev_num].guest_features =3D 0; +} + +static void virtio_msg_set_device_status(VirtIOMSGProxy *s, + VirtIOMSG *msg) +{ + VirtIODevice *vdev =3D virtio_msg_vdev(s, msg->dev_num); + uint32_t status =3D msg->set_device_status.status; + VirtIOMSG msg_resp; + + if (!vdev) { + return; + } + + if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { + virtio_bus_stop_ioeventfd(&s->devs[msg->dev_num].bus); + } + + if (status & VIRTIO_CONFIG_S_FEATURES_OK) { + virtio_set_features(vdev, s->devs[msg->dev_num].guest_features); + } + + virtio_set_status(vdev, status); + assert(vdev->status =3D=3D status); + + if (status & VIRTIO_CONFIG_S_DRIVER_OK) { + virtio_bus_start_ioeventfd(&s->devs[msg->dev_num].bus); + } + + if (status =3D=3D 0) { + virtio_msg_soft_reset(s, msg->dev_num); + } + + virtio_msg_pack_set_device_status_resp(&msg_resp, msg->dev_num, msg->t= oken, + vdev->status); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_get_device_status(VirtIOMSGProxy *s, + VirtIOMSG *msg) +{ + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, msg->dev_num, __func_= _); + VirtIOMSG msg_resp; + uint32_t status =3D vdev ? vdev->status : 0; + + virtio_msg_pack_get_device_status_resp(&msg_resp, msg->dev_num, msg->t= oken, + status); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_get_config(VirtIOMSGProxy *s, + VirtIOMSG *msg) +{ + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, msg->dev_num, __func_= _); + uint32_t size =3D msg->get_config.size; + uint32_t offset =3D msg->get_config.offset; + uint8_t data[VIRTIO_MSG_MAX_CONFIG_BYTES]; + VirtIOMSG msg_resp; + uint32_t generation =3D 0; + unsigned int i; + + if (size > VIRTIO_MSG_MAX_CONFIG_BYTES) { + return; + } + + memset(data, 0, size); + + if (vdev) { + for (i =3D 0; i < size; i++) { + data[i] =3D virtio_config_modern_readb(vdev, offset + i); + } + generation =3D vdev->generation; + } + + virtio_msg_pack_get_config_resp(&msg_resp, msg->dev_num, msg->token, + size, offset, + generation, data); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_set_config(VirtIOMSGProxy *s, + VirtIOMSG *msg) +{ + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, msg->dev_num, __func_= _); + uint32_t offset =3D msg->set_config.offset; + uint32_t size =3D msg->set_config.size; + uint8_t *data =3D msg->set_config.data; + VirtIOMSG msg_resp; + uint32_t generation =3D 0; + unsigned int i; + + if (vdev) { + for (i =3D 0; i < size; i++) { + virtio_config_modern_writeb(vdev, offset + i, data[i]); + } + generation =3D vdev->generation; + } + + virtio_msg_pack_set_config_resp(&msg_resp, msg->dev_num, msg->token, + size, offset, + generation, data); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_get_vqueue(VirtIOMSGProxy *s, + VirtIOMSG *msg) +{ + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, msg->dev_num, __func_= _); + uint32_t max_size =3D 0; + uint32_t index =3D msg->get_vqueue.index; + hwaddr desc =3D 0, avail =3D 0, used =3D 0; + VirtIOMSG msg_resp; + uint32_t size =3D 0; + + if (index < VIRTIO_QUEUE_MAX && vdev) { + max_size =3D virtio_queue_get_max_num(vdev, index); + size =3D virtio_queue_get_num(vdev, index); + if (size) { + virtio_queue_get_rings(vdev, index, &desc, &avail, &used); + } + virtio_msg_pack_get_vqueue_resp(&msg_resp, msg->dev_num, msg->toke= n, + index, max_size, size, + desc, avail, used); + } else { + /* OOB index or missing device, respond with all zeroes. */ + virtio_msg_pack_get_vqueue_resp(&msg_resp, msg->dev_num, msg->toke= n, + index, 0, 0, 0, 0, 0); + } + + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_set_vqueue(VirtIOMSGProxy *s, VirtIOMSG *msg) +{ + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, msg->dev_num, __func_= _); + uint32_t index =3D msg->set_vqueue.index; + VirtIOMSG msg_resp; + + if (!vdev || index >=3D VIRTIO_QUEUE_MAX) { + /* Missing device or OOB index, ignore. */ + return; + } + + virtio_queue_set_vector(vdev, index, index); + virtio_queue_set_num(vdev, index, msg->set_vqueue.size); + virtio_queue_set_rings(vdev, index, + msg->set_vqueue.descriptor_addr, + msg->set_vqueue.driver_addr, + msg->set_vqueue.device_addr); + virtio_queue_enable(vdev, index); + + virtio_msg_pack_set_vqueue_resp(&msg_resp, msg->dev_num, msg->token); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_reset_vqueue(VirtIOMSGProxy *s, VirtIOMSG *msg) +{ + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, msg->dev_num, __func_= _); + VirtIOMSG msg_resp; + + if (!vdev) { + return; + } + + virtio_queue_reset(vdev, msg->reset_vqueue.index); + + virtio_msg_pack_reset_vqueue_resp(&msg_resp, msg->dev_num, msg->token); + virtio_msg_bus_send(&s->msg_bus, &msg_resp); +} + +static void virtio_msg_event_avail(VirtIOMSGProxy *s, + VirtIOMSG *msg) +{ + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, msg->dev_num, __func_= _); + uint16_t vq_idx =3D msg->event_avail.index; + VirtQueue *vq; + + if (!vdev) { + return; + } + + if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { + VirtIOMSG msg_ev; + + virtio_error(vdev, "Notification while driver not OK?"); + virtio_msg_pack_event_config(&msg_ev, msg->dev_num, + vdev->status, vdev->generation, + 0, 0, NULL); + virtio_msg_bus_send(&s->msg_bus, &msg_ev); + return; + } + + if (vq_idx >=3D VIRTIO_QUEUE_MAX) { + virtio_error(vdev, "Notification to bad VQ!"); + return; + } + + if (!virtio_queue_get_num(vdev, vq_idx)) { + virtio_error(vdev, "Notification to unconfigured VQ!"); + return; + } + + vq =3D virtio_get_queue(vdev, vq_idx); + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { + uint32_t next_offset_wrap =3D msg->event_avail.next_offset_wrap; + uint16_t qsize =3D virtio_queue_get_num(vdev, vq_idx); + uint32_t offset =3D next_offset_wrap & 0x7fffffff; + bool wrap =3D next_offset_wrap & 0x80000000; + uint16_t data; + + if (offset > 0x7fff || offset >=3D qsize) { + virtio_error(vdev, "Next offset to large!"); + /* Bail out without notification??? */ + return; + } + + data =3D wrap << 15; + data |=3D offset & 0x7fff; + + virtio_queue_set_shadow_avail_idx(vq, data); + } + virtio_queue_notify(vdev, msg->event_avail.index); +} + +typedef void (*VirtIOMSGHandler)(VirtIOMSGProxy *s, + VirtIOMSG *msg); + +static const VirtIOMSGHandler msg_handlers[] =3D { + [VIRTIO_MSG_DEVICE_INFO] =3D virtio_msg_device_info, + [VIRTIO_MSG_GET_FEATURES] =3D virtio_msg_get_features, + [VIRTIO_MSG_SET_FEATURES] =3D virtio_msg_set_features, + [VIRTIO_MSG_GET_DEVICE_STATUS] =3D virtio_msg_get_device_status, + [VIRTIO_MSG_SET_DEVICE_STATUS] =3D virtio_msg_set_device_status, + [VIRTIO_MSG_GET_CONFIG] =3D virtio_msg_get_config, + [VIRTIO_MSG_SET_CONFIG] =3D virtio_msg_set_config, + [VIRTIO_MSG_GET_VQUEUE] =3D virtio_msg_get_vqueue, + [VIRTIO_MSG_SET_VQUEUE] =3D virtio_msg_set_vqueue, + [VIRTIO_MSG_RESET_VQUEUE] =3D virtio_msg_reset_vqueue, + [VIRTIO_MSG_EVENT_AVAIL] =3D virtio_msg_event_avail, +}; + +static int virtio_msg_receive_msg(VirtIOMSGBusDevice *bd, VirtIOMSG *msg) +{ + VirtIOMSGProxy *s =3D VIRTIO_MSG(bd->opaque); + VirtIOMSGHandler handler; + + /* virtio_msg_print(msg); */ + + /* We handle some generic bus messages. */ + if (msg->type & VIRTIO_MSG_TYPE_BUS) { + if (msg->msg_id =3D=3D VIRTIO_MSG_BUS_GET_DEVICES) { + virtio_msg_bus_get_devices(s, msg); + } + if (msg->msg_id =3D=3D VIRTIO_MSG_BUS_PING) { + virtio_msg_bus_ping(s, msg); + } + return VIRTIO_MSG_NO_ERROR; + } + + if (msg->msg_id >=3D ARRAY_SIZE(msg_handlers)) { + return VIRTIO_MSG_ERROR_UNSUPPORTED_MESSAGE_ID; + } + + handler =3D msg_handlers[msg->msg_id]; + + /* We don't expect responses. */ + if ((msg->type & VIRTIO_MSG_TYPE_RESPONSE) || virtio_msg_bad(s, msg)) { + /* Drop bad messages. */ + return VIRTIO_MSG_ERROR_BAD_MESSAGE; + } + + if (handler) { + handler(s, msg); + } + + return VIRTIO_MSG_NO_ERROR; +} + +static const VirtIOMSGBusPort virtio_msg_port =3D { + .receive =3D virtio_msg_receive_msg, + .is_driver =3D false +}; + +static void virtio_msg_notify(DeviceState *opaque, uint16_t vector) +{ + VirtIOMSGDev *mdev =3D VIRTIO_MSG_DEV(opaque); + VirtIOMSGProxy *s =3D VIRTIO_MSG(mdev->proxy); + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, mdev->dev_num, __func= __); + VirtIOMSG msg; + + if (!vdev || !virtio_msg_bus_connected(&s->msg_bus)) { + return; + } + + if (vector < VIRTIO_QUEUE_MAX) { + virtio_msg_pack_event_used(&msg, mdev->dev_num, vector); + virtio_msg_bus_send(&s->msg_bus, &msg); + return; + } + + if (vector < VIRTIO_NO_VECTOR) { + virtio_msg_pack_event_config(&msg, mdev->dev_num, + vdev->status, vdev->generation, + 0, 0, NULL); + virtio_msg_bus_send(&s->msg_bus, &msg); + return; + } +} + +static void virtio_msg_save_queue(DeviceState *opaque, int n, QEMUFile *f) +{ + VirtIOMSGDev *mdev =3D VIRTIO_MSG_DEV(opaque); + VirtIOMSGProxy *s =3D VIRTIO_MSG(mdev->proxy); + VirtIODevice *vdev =3D virtio_msg_vdev(s, mdev->dev_num); + uint16_t vector =3D VIRTIO_NO_VECTOR; + + if (vdev) { + vector =3D virtio_queue_vector(vdev, n); + } + + /* Preserve per-queue MSI-X vector so notifications keep working. */ + qemu_put_be16(f, vector); +} + +static int virtio_msg_load_queue(DeviceState *opaque, int n, QEMUFile *f) +{ + VirtIOMSGDev *mdev =3D VIRTIO_MSG_DEV(opaque); + VirtIOMSGProxy *s =3D VIRTIO_MSG(mdev->proxy); + VirtIODevice *vdev =3D virtio_msg_vdev(s, mdev->dev_num); + uint16_t vector; + + /* Restore the MSI-X vector saved by virtio_msg_save_queue(). */ + qemu_get_be16s(f, &vector); + + if (!vdev) { + return -ENODEV; + } + + if (vector !=3D VIRTIO_NO_VECTOR && vector >=3D VIRTIO_QUEUE_MAX) { + return -EINVAL; + } + + virtio_queue_set_vector(vdev, n, vector); + return 0; +} + +static bool virtio_msg_has_vdevs(VirtIOMSGProxy *s) +{ + int i; + + for (i =3D 0; i < ARRAY_SIZE(s->devs); i++) { + if (virtio_msg_vdev(s, i)) { + return true; + } + } + + return false; +} + +static void virtio_msg_connect_bus(VirtIOMSGProxy *s, bool has_vdevs) +{ + if (!has_vdevs) { + return; + } + + if (!virtio_msg_bus_connect(&s->msg_bus, &virtio_msg_port, s)) { + /* This is a user error, forgetting to setup a msg-bus. */ + error_report("%s: No bus connected!", + object_get_canonical_path(OBJECT(s))); + exit(EXIT_FAILURE); + } +} + +static int virtio_msg_post_load(void *opaque, int version_id) +{ + VirtIOMSGProxy *s =3D VIRTIO_MSG(opaque); + + virtio_msg_connect_bus(s, virtio_msg_has_vdevs(s)); + return 0; +} + +static void virtio_msg_vmstate_change(DeviceState *d, bool running) +{ + VirtIOMSGDev *mdev =3D VIRTIO_MSG_DEV(d); + VirtIOMSGProxy *s =3D VIRTIO_MSG(mdev->proxy); + VirtIOMSGBusDevice *bd; + + if (!running) { + return; + } + + if (!virtio_msg_bus_connected(&s->msg_bus)) { + return; + } + + bd =3D virtio_msg_bus_get_device(&s->msg_bus); + if (!bd) { + return; + } + + /* Resume path: ensure any pending bus work is processed post-migratio= n. */ + virtio_msg_bus_process(bd); +} + +static const VMStateDescription vmstate_virtio_msg_dev =3D { + .name =3D "virtio_msg/dev", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (VMStateField[]) { + VMSTATE_UINT64(guest_features, VirtIOMSGDev), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_virtio_msg_state_sub =3D { + .name =3D "virtio_msg_proxy_backend/state", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(devs, VirtIOMSGProxy, VIRTIO_MSG_MAX_DEVS, 0, + vmstate_virtio_msg_dev, VirtIOMSGDev), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_virtio_msg =3D { + .name =3D "virtio_msg_proxy_backend", + .version_id =3D 1, + .minimum_version_id =3D 1, + .post_load =3D virtio_msg_post_load, + .fields =3D (const VMStateField[]) { + VMSTATE_END_OF_LIST() + }, + .subsections =3D (const VMStateDescription * []) { + &vmstate_virtio_msg_state_sub, + NULL + } +}; + +static void virtio_msg_save_extra_state(DeviceState *opaque, QEMUFile *f) +{ + VirtIOMSGDev *mdev =3D VIRTIO_MSG_DEV(opaque); + VirtIOMSGProxy *s =3D VIRTIO_MSG(mdev->proxy); + + vmstate_save_state(f, &vmstate_virtio_msg, s, NULL, &error_fatal); +} + +static int virtio_msg_load_extra_state(DeviceState *opaque, QEMUFile *f) +{ + VirtIOMSGDev *mdev =3D VIRTIO_MSG_DEV(opaque); + VirtIOMSGProxy *s =3D VIRTIO_MSG(mdev->proxy); + + return vmstate_load_state(f, &vmstate_virtio_msg, s, 1, &error_fatal); +} + +static bool virtio_msg_has_extra_state(DeviceState *opaque) +{ + return true; +} + +static void virtio_msg_reset_hold(Object *obj, ResetType type) +{ + VirtIOMSGProxy *s =3D VIRTIO_MSG(obj); + VirtIODevice *vdev; + bool found_a_vdev =3D false; + int i; + + for (i =3D 0; i < ARRAY_SIZE(s->devs); i++) { + virtio_msg_soft_reset(s, i); + + vdev =3D virtio_msg_vdev(s, i); + if (vdev) { + found_a_vdev =3D true; + } + } + + /* Only connect transports with virtio-devs. */ + virtio_msg_connect_bus(s, found_a_vdev); +} + +static bool virtio_msg_ioeventfd_enabled(DeviceState *d) +{ + /* We don't have any MMIO/PIO regs directly mapped to eventfds. */ + return false; +} + +static int virtio_msg_ioeventfd_assign(DeviceState *d, + EventNotifier *notifier, + int n, bool assign) +{ + /* + * virtio-msg has no MMIO/PIO notify register to bind an ioeventfd to. + * Host kicks arrive via EVENT_AVAIL messages, and we explicitly signal + * the per-queue host notifier in virtio_msg_event_avail(). + * Nothing to map here; return success so vhost can proceed. + */ + return 0; +} + +static int virtio_msg_set_guest_notifier(DeviceState *d, int n, bool assig= n, + bool with_irqfd) +{ + VirtIOMSGDev *mdev =3D VIRTIO_MSG_DEV(d); + VirtIOMSGProxy *s =3D VIRTIO_MSG(mdev->proxy); + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, mdev->dev_num, __func= __); + VirtQueue *vq; + EventNotifier *notifier; + VirtioDeviceClass *vdc; + + if (!vdev) { + return -ENODEV; + } + + vdc =3D VIRTIO_DEVICE_GET_CLASS(vdev); + vq =3D virtio_get_queue(vdev, n); + notifier =3D virtio_queue_get_guest_notifier(vq); + + if (assign) { + int r =3D event_notifier_init(notifier, 0); + if (r < 0) { + return r; + } + virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); + } else { + virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); + event_notifier_cleanup(notifier); + } + + if (vdc->guest_notifier_mask && vdev->use_guest_notifier_mask) { + vdc->guest_notifier_mask(vdev, n, !assign); + } + + return 0; +} + +static int virtio_msg_set_config_guest_notifier(DeviceState *d, bool assig= n, + bool with_irqfd) +{ + VirtIOMSGDev *mdev =3D VIRTIO_MSG_DEV(d); + VirtIOMSGProxy *s =3D VIRTIO_MSG(mdev->proxy); + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, mdev->dev_num, __func= __); + EventNotifier *notifier; + VirtioDeviceClass *vdc; + int r =3D 0; + + if (!vdev) { + return -ENODEV; + } + + vdc =3D VIRTIO_DEVICE_GET_CLASS(vdev); + notifier =3D virtio_config_get_guest_notifier(vdev); + + if (assign) { + r =3D event_notifier_init(notifier, 0); + if (r < 0) { + return r; + } + virtio_config_set_guest_notifier_fd_handler(vdev, assign, with_irq= fd); + } else { + virtio_config_set_guest_notifier_fd_handler(vdev, assign, with_irq= fd); + event_notifier_cleanup(notifier); + } + if (vdc->guest_notifier_mask && vdev->use_guest_notifier_mask) { + vdc->guest_notifier_mask(vdev, VIRTIO_CONFIG_IRQ_IDX, !assign); + } + return r; +} + +static int virtio_msg_set_guest_notifiers(DeviceState *d, int nvqs, + bool assign) +{ + VirtIOMSGDev *mdev =3D VIRTIO_MSG_DEV(d); + VirtIOMSGProxy *s =3D VIRTIO_MSG(mdev->proxy); + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, mdev->dev_num, __func= __); + /* Mirror virtio-mmio: use eventfd handlers and skip irqfd for now. */ + bool with_irqfd =3D false; + int r, n; + + if (!vdev) { + return -ENODEV; + } + + nvqs =3D MIN(nvqs, VIRTIO_QUEUE_MAX); + + for (n =3D 0; n < nvqs; n++) { + if (!virtio_queue_get_num(vdev, n)) { + break; + } + + r =3D virtio_msg_set_guest_notifier(d, n, assign, with_irqfd); + if (r < 0) { + goto assign_error; + } + } + r =3D virtio_msg_set_config_guest_notifier(d, assign, with_irqfd); + if (r < 0) { + goto assign_error; + } + + return 0; + +assign_error: + /* We get here on assignment failure. Recover by undoing for VQs 0 .. = n. */ + assert(assign); + while (--n >=3D 0) { + virtio_msg_set_guest_notifier(d, n, !assign, false); + } + return r; +} + +static void virtio_msg_pre_plugged(DeviceState *d, Error **errp) +{ + VirtIOMSGDev *mdev =3D VIRTIO_MSG_DEV(d); + VirtIOMSGProxy *s =3D VIRTIO_MSG(mdev->proxy); + VirtIODevice *vdev =3D virtio_msg_lookup_vdev(s, mdev->dev_num, __func= __); + + if (!vdev) { + return; + } + + virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1); +} + +static AddressSpace *virtio_msg_get_dma_as(DeviceState *d) +{ + VirtIOMSGProxy *s =3D VIRTIO_MSG(d); + AddressSpace *as; + + as =3D virtio_msg_bus_get_remote_as(&s->msg_bus); + return as; +} + +static int virtio_msg_query_nvectors(DeviceState *d) +{ + return VIRTIO_QUEUE_MAX; +} + +static void virtio_msg_realize(DeviceState *d, Error **errp) +{ + VirtIOMSGProxy *s =3D VIRTIO_MSG(d); + Object *o =3D OBJECT(d); + int i; + + for (i =3D 0; i < ARRAY_SIZE(s->devs); i++) { + g_autofree char *outer_bus_name =3D g_strdup_printf("bus%d", i); + + qbus_init(&s->devs_bus[i], sizeof(s->devs_bus[i]), + TYPE_VIRTIO_MSG_OUTER_BUS, d, outer_bus_name); + + object_initialize_child(o, "dev[*]", &s->devs[i], TYPE_VIRTIO_MSG_= DEV); + s->devs[i].proxy =3D s; + s->devs[i].dev_num =3D i; + qdev_realize(DEVICE(&s->devs[i]), BUS(&s->devs_bus[i]), &error_fat= al); + + qbus_init(&s->devs[i].bus, sizeof(s->devs[i].bus), + TYPE_VIRTIO_MSG_PROXY_BUS, DEVICE(&s->devs[i]), "bus"); + } + + qbus_init(&s->msg_bus, sizeof(s->msg_bus), + TYPE_VIRTIO_MSG_BUS, d, "msg-bus"); +} + +static void virtio_msg_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + dc->realize =3D virtio_msg_realize; + dc->bus_type =3D TYPE_VIRTIO_MSG_OUTER_BUS; + dc->user_creatable =3D true; + rc->phases.hold =3D virtio_msg_reset_hold; +} + +static void virtio_msg_dev_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->bus_type =3D TYPE_VIRTIO_MSG_OUTER_BUS; +} + +static const TypeInfo virtio_msg_types[] =3D { + { + .name =3D TYPE_VIRTIO_MSG, + .parent =3D TYPE_DEVICE, + .instance_size =3D sizeof(VirtIOMSGProxy), + .class_init =3D virtio_msg_class_init, + }, + { + .name =3D TYPE_VIRTIO_MSG_DEV, + .parent =3D TYPE_DEVICE, + .instance_size =3D sizeof(VirtIOMSGDev), + .class_init =3D virtio_msg_dev_class_init, + }, +}; +DEFINE_TYPES(virtio_msg_types); + +static char *virtio_msg_bus_get_dev_path(DeviceState *dev) +{ + BusState *bus =3D qdev_get_parent_bus(dev); + return strdup(object_get_canonical_path(OBJECT(bus->parent))); +} + +static void virtio_msg_bus_class_init(ObjectClass *klass, const void *data) +{ + BusClass *bus_class =3D BUS_CLASS(klass); + VirtioBusClass *k =3D VIRTIO_BUS_CLASS(klass); + + k->notify =3D virtio_msg_notify; + k->save_queue =3D virtio_msg_save_queue; + k->load_queue =3D virtio_msg_load_queue; + k->save_extra_state =3D virtio_msg_save_extra_state; + k->load_extra_state =3D virtio_msg_load_extra_state; + k->has_extra_state =3D virtio_msg_has_extra_state; + k->pre_plugged =3D virtio_msg_pre_plugged; + k->has_variable_vring_alignment =3D true; + k->get_dma_as =3D virtio_msg_get_dma_as; + k->query_nvectors =3D virtio_msg_query_nvectors; + + k->set_guest_notifiers =3D virtio_msg_set_guest_notifiers; + k->ioeventfd_enabled =3D virtio_msg_ioeventfd_enabled; + k->ioeventfd_assign =3D virtio_msg_ioeventfd_assign; + k->vmstate_change =3D virtio_msg_vmstate_change; + + /* Needed for multiple devs of the same kind (virtio-net). */ + bus_class->get_dev_path =3D virtio_msg_bus_get_dev_path; +} + +static const TypeInfo virtio_msg_bus_types[] =3D { + { + /* Specialized virtio-bus with our custom callbacks. */ + .name =3D TYPE_VIRTIO_MSG_PROXY_BUS, + .parent =3D TYPE_VIRTIO_BUS, + .instance_size =3D sizeof(VirtioBusState), + .class_init =3D virtio_msg_bus_class_init, + }, + { + /* + * Outer bus to hold virtio-msg objects making them visible to the + * qom-tree. + */ + .name =3D TYPE_VIRTIO_MSG_OUTER_BUS, + .parent =3D TYPE_BUS, + .instance_size =3D sizeof(BusState), + }, +}; + +DEFINE_TYPES(virtio_msg_bus_types); diff --git a/include/hw/virtio/virtio-msg-bus.h b/include/hw/virtio/virtio-= msg-bus.h new file mode 100644 index 0000000000..c01681824c --- /dev/null +++ b/include/hw/virtio/virtio-msg-bus.h @@ -0,0 +1,95 @@ +/* + * VirtIO MSG bus. + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * Written by Edgar E. Iglesias + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_VIRTIO_MSG_BUS_H +#define QEMU_VIRTIO_MSG_BUS_H + +#include "qom/object.h" +#include "system/dma.h" +#include "hw/core/qdev.h" +#include "hw/virtio/virtio-msg-prot.h" + +#define TYPE_VIRTIO_MSG_BUS "virtio-msg-bus" +DECLARE_INSTANCE_CHECKER(BusState, VIRTIO_MSG_BUS, + TYPE_VIRTIO_MSG_BUS) + + +#define TYPE_VIRTIO_MSG_BUS_DEVICE "virtio-msg-bus-device" +OBJECT_DECLARE_TYPE(VirtIOMSGBusDevice, VirtIOMSGBusDeviceClass, + VIRTIO_MSG_BUS_DEVICE) + +typedef struct VirtIOMSGBusPort { + int (*receive)(VirtIOMSGBusDevice *bd, VirtIOMSG *msg); + bool is_driver; +} VirtIOMSGBusPort; + +struct VirtIOMSGBusDeviceClass { + /*< private >*/ + DeviceClass parent_class; + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; + + /* + * Ask the bus to receive and process all messages that + * are readily available. The bus will call the registered + * VirtIOMSGBusPort.receive() function for each message. + * + * Will return immediately if no messages are available. + */ + void (*process)(VirtIOMSGBusDevice *bd); + + /* + * Called by the transport to send a message. + */ + int (*send)(VirtIOMSGBusDevice *bd, VirtIOMSG *msg_req); +}; + +typedef struct VirtIOMSGBusDevice { + DeviceState parent; + + const VirtIOMSGBusPort *peer; + void *opaque; +} VirtIOMSGBusDevice; + +static inline VirtIOMSGBusDevice *virtio_msg_bus_get_device(BusState *qbus) +{ + BusChild *kid =3D QTAILQ_FIRST(&qbus->children); + DeviceState *qdev =3D kid ? kid->child : NULL; + + return (VirtIOMSGBusDevice *)qdev; +} + +static inline bool virtio_msg_bus_connected(BusState *bus) +{ + VirtIOMSGBusDevice *bd =3D virtio_msg_bus_get_device(bus); + + return bd && bd->peer !=3D NULL; +} + +void virtio_msg_bus_process(VirtIOMSGBusDevice *bd); + +bool virtio_msg_bus_connect(BusState *bus, + const VirtIOMSGBusPort *port, + void *opaque); + +static inline void +virtio_msg_bus_receive(VirtIOMSGBusDevice *bd, VirtIOMSG *msg) +{ + virtio_msg_unpack(msg); + bd->peer->receive(bd, msg); +} + +int virtio_msg_bus_send(BusState *bus, VirtIOMSG *msg_req); + +static inline AddressSpace *virtio_msg_bus_get_remote_as(BusState *bus) +{ + return &address_space_memory; +} +#endif diff --git a/include/hw/virtio/virtio-msg-prot.h b/include/hw/virtio/virtio= -msg-prot.h new file mode 100644 index 0000000000..a501564082 --- /dev/null +++ b/include/hw/virtio/virtio-msg-prot.h @@ -0,0 +1,949 @@ +/* + * Virtio MSG - Message packing/unpacking functions. + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * Written by Edgar E. Iglesias . + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_VIRTIO_MSG_H +#define QEMU_VIRTIO_MSG_H + +#include +#include "standard-headers/linux/virtio_config.h" + +enum { + VIRTIO_MSG_NO_ERROR =3D 0, + VIRTIO_MSG_ERROR_RETRY, + VIRTIO_MSG_ERROR_TIMEOUT, + VIRTIO_MSG_ERROR_UNSUPPORTED_MESSAGE_ID, + VIRTIO_MSG_ERROR_BAD_MESSAGE, + VIRTIO_MSG_ERROR_MEMORY, /* General memory error. */ +}; + +enum { + VIRTIO_MSG_DEVICE_INFO =3D 0x02, + VIRTIO_MSG_GET_FEATURES =3D 0x03, + VIRTIO_MSG_SET_FEATURES =3D 0x04, + VIRTIO_MSG_GET_CONFIG =3D 0x05, + VIRTIO_MSG_SET_CONFIG =3D 0x06, + VIRTIO_MSG_GET_DEVICE_STATUS =3D 0x07, + VIRTIO_MSG_SET_DEVICE_STATUS =3D 0x08, + VIRTIO_MSG_GET_VQUEUE =3D 0x09, + VIRTIO_MSG_SET_VQUEUE =3D 0x0a, + VIRTIO_MSG_RESET_VQUEUE =3D 0x0b, + VIRTIO_MSG_GET_SHM =3D 0x0c, /* Not yet supported */ + VIRTIO_MSG_EVENT_CONFIG =3D 0x40, + VIRTIO_MSG_EVENT_AVAIL =3D 0x41, + VIRTIO_MSG_EVENT_USED =3D 0x42, + + /* Generic bus messages. */ + VIRTIO_MSG_BUS_GET_DEVICES =3D 0x02, + VIRTIO_MSG_BUS_PING =3D 0x03, + VIRTIO_MSG_BUS_EVENT_DEVICE =3D 0x40, + + VIRTIO_MSG_MAX =3D VIRTIO_MSG_EVENT_USED, +}; + +#define VIRTIO_MSG_MAX_SIZE 48 + +#define VIRTIO_MSG_TYPE_RESPONSE (1 << 0) +#define VIRTIO_MSG_TYPE_BUS (1 << 1) + +typedef struct VirtIOMSG { + uint8_t type; + uint8_t msg_id; + uint16_t dev_num; + uint16_t token; + uint16_t msg_size; + + union { + uint8_t payload_u8[40]; + + struct { + uint32_t device_id; + uint32_t vendor_id; + uint32_t num_feature_bits; + uint32_t config_size; + uint32_t max_vqs; + uint16_t admin_vq_idx; + uint16_t admin_vq_count; + } QEMU_PACKED get_device_info_resp; + struct { + uint32_t index; + uint32_t num; + } QEMU_PACKED get_features; + struct { + uint32_t index; + uint32_t num; + uint32_t b32[]; + } QEMU_PACKED get_features_resp; + struct { + uint32_t index; + uint32_t num; + uint32_t b32[]; + } QEMU_PACKED set_features; + struct { + uint32_t offset; + uint32_t size; + } QEMU_PACKED get_config; + struct { + uint32_t generation; + uint32_t offset; + uint32_t size; + uint8_t data[]; + } QEMU_PACKED get_config_resp; + struct { + uint32_t generation; + uint32_t offset; + uint32_t size; + uint8_t data[]; + } QEMU_PACKED set_config; + struct { + uint32_t generation; + uint32_t offset; + uint32_t size; + uint8_t data[]; + } QEMU_PACKED set_config_resp; + struct { + uint32_t status; + } QEMU_PACKED get_device_status_resp; + struct { + uint32_t status; + } QEMU_PACKED set_device_status; + struct { + uint32_t status; + } QEMU_PACKED set_device_status_resp; + struct { + uint32_t index; + } QEMU_PACKED get_vqueue; + struct { + uint32_t index; + uint32_t max_size; + uint32_t size; + uint32_t reserved; + uint64_t descriptor_addr; + uint64_t driver_addr; + uint64_t device_addr; + } QEMU_PACKED get_vqueue_resp; + struct { + uint32_t index; + uint32_t unused; + uint32_t size; + uint32_t reserved; + uint64_t descriptor_addr; + uint64_t driver_addr; + uint64_t device_addr; + } QEMU_PACKED set_vqueue; + struct { + uint32_t index; + } QEMU_PACKED reset_vqueue; + struct { + uint32_t index; + } QEMU_PACKED get_shm; + struct { + uint32_t index; + uint32_t length; + uint32_t address; + } QEMU_PACKED get_shm_resp; + struct { + uint32_t status; + uint32_t generation; + uint32_t offset; + uint32_t size; + uint8_t config_value[]; + } QEMU_PACKED event_config; + struct { + uint32_t index; + uint32_t next_offset_wrap; + } QEMU_PACKED event_avail; + struct { + uint32_t index; + } QEMU_PACKED event_used; + + /* Generic bus messages. */ + struct { + uint16_t offset; + uint16_t num; + } QEMU_PACKED bus_get_devices; + struct { + uint16_t offset; + uint16_t num; + uint16_t next_offset; + uint8_t data[]; + } QEMU_PACKED bus_get_devices_resp; + struct { + uint32_t data; + } QEMU_PACKED bus_ping; + struct { + uint32_t data; + } QEMU_PACKED bus_ping_resp; + struct { + uint16_t dev_num; + +#define VIRTIO_MSG_BUS_EVENT_DEVICE_STATE_READY 0x1 +#define VIRTIO_MSG_BUS_EVENT_DEVICE_STATE_REMOVED 0x2 + uint16_t dev_state; + } QEMU_PACKED bus_event_device; + }; +} QEMU_PACKED VirtIOMSG; + +/* Maximum number of 32b feature-blocks in a single message. */ +#define VIRTIO_MSG_MAX_FEATURE_NUM \ + ((VIRTIO_MSG_MAX_SIZE - offsetof(VirtIOMSG, get_features_resp.b32)) / = 4) + +/* Maximum amount of config-data in a single message, in bytes. */ +#define VIRTIO_MSG_MAX_CONFIG_BYTES \ + (VIRTIO_MSG_MAX_SIZE - offsetof(VirtIOMSG, set_config.data)) + +#define LE_TO_CPU(v) \ +{ \ + if (sizeof(v) =3D=3D 2) { \ + v =3D le16_to_cpu(v); \ + } else if (sizeof(v) =3D=3D 4) { \ + v =3D le32_to_cpu(v); \ + } else if (sizeof(v) =3D=3D 8) { \ + v =3D le64_to_cpu(v); \ + } else { \ + g_assert_not_reached(); \ + } \ +} + +static inline void virtio_msg_unpack_bus(VirtIOMSG *msg) +{ + switch (msg->msg_id) { + case VIRTIO_MSG_BUS_PING: + /* Both req and resp have the same layout. */ + LE_TO_CPU(msg->bus_ping.data); + break; + case VIRTIO_MSG_BUS_GET_DEVICES: + LE_TO_CPU(msg->bus_get_devices.offset); + LE_TO_CPU(msg->bus_get_devices.num); + if (msg->type & VIRTIO_MSG_TYPE_RESPONSE) { + LE_TO_CPU(msg->bus_get_devices_resp.next_offset); + } + break; + case VIRTIO_MSG_BUS_EVENT_DEVICE: + LE_TO_CPU(msg->bus_event_device.dev_num); + LE_TO_CPU(msg->bus_event_device.dev_state); + break; + default: + break; + } +} + +/** + * virtio_msg_unpack_resp: Unpacks a wire virtio message responses into + * a host version + * @msg: the virtio message to unpack + * + * See virtio_msg_unpack(). + */ +static inline void virtio_msg_unpack_resp(VirtIOMSG *msg) +{ + int i; + + switch (msg->msg_id) { + case VIRTIO_MSG_DEVICE_INFO: + LE_TO_CPU(msg->get_device_info_resp.device_id); + LE_TO_CPU(msg->get_device_info_resp.vendor_id); + LE_TO_CPU(msg->get_device_info_resp.num_feature_bits); + LE_TO_CPU(msg->get_device_info_resp.config_size); + LE_TO_CPU(msg->get_device_info_resp.max_vqs); + LE_TO_CPU(msg->get_device_info_resp.admin_vq_idx); + LE_TO_CPU(msg->get_device_info_resp.admin_vq_count); + break; + case VIRTIO_MSG_GET_FEATURES: + LE_TO_CPU(msg->get_features_resp.index); + LE_TO_CPU(msg->get_features_resp.num); + for (i =3D 0; i < VIRTIO_MSG_MAX_FEATURE_NUM && + i < msg->get_features_resp.num; i++) { + LE_TO_CPU(msg->get_features_resp.b32[i]); + } + break; + case VIRTIO_MSG_GET_DEVICE_STATUS: + LE_TO_CPU(msg->get_device_status_resp.status); + break; + case VIRTIO_MSG_GET_CONFIG: + LE_TO_CPU(msg->get_config_resp.generation); + LE_TO_CPU(msg->get_config_resp.offset); + LE_TO_CPU(msg->get_config_resp.size); + break; + case VIRTIO_MSG_GET_SHM: + LE_TO_CPU(msg->get_shm_resp.index); + LE_TO_CPU(msg->get_shm_resp.length); + LE_TO_CPU(msg->get_shm_resp.address); + break; + case VIRTIO_MSG_SET_CONFIG: + LE_TO_CPU(msg->set_config_resp.generation); + LE_TO_CPU(msg->set_config_resp.offset); + LE_TO_CPU(msg->set_config_resp.size); + break; + case VIRTIO_MSG_GET_VQUEUE: + LE_TO_CPU(msg->get_vqueue_resp.index); + LE_TO_CPU(msg->get_vqueue_resp.max_size); + LE_TO_CPU(msg->get_vqueue_resp.size); + LE_TO_CPU(msg->get_vqueue_resp.descriptor_addr); + LE_TO_CPU(msg->get_vqueue_resp.driver_addr); + LE_TO_CPU(msg->get_vqueue_resp.device_addr); + break; + default: + break; + } +} + +/** + * virtio_msg_unpack: Unpacks a wire virtio message into a host version + * @msg: the virtio message to unpack + * + * Virtio messages arriving on the virtio message bus have a specific + * format (little-endian, packet encoding, etc). To simplify for the + * the rest of the implementation, we have packers and unpackers that + * convert the wire messages into host versions. This includes endianess + * conversion and potentially future decoding and expansion. + * + * At the moment, we only do endian conversion, virtio_msg_unpack() should + * get completely eliminated by the compiler when targetting little-endian + * hosts. + */ +static inline void virtio_msg_unpack(VirtIOMSG *msg) +{ + int i; + + LE_TO_CPU(msg->dev_num); + LE_TO_CPU(msg->token); + LE_TO_CPU(msg->msg_size); + + if (msg->type & VIRTIO_MSG_TYPE_BUS) { + virtio_msg_unpack_bus(msg); + return; + } + + if (msg->type & VIRTIO_MSG_TYPE_RESPONSE) { + virtio_msg_unpack_resp(msg); + return; + } + + switch (msg->msg_id) { + case VIRTIO_MSG_GET_FEATURES: + LE_TO_CPU(msg->get_features.index); + LE_TO_CPU(msg->get_features.num); + break; + case VIRTIO_MSG_SET_FEATURES: + LE_TO_CPU(msg->set_features.index); + LE_TO_CPU(msg->set_features.num); + for (i =3D 0; i < VIRTIO_MSG_MAX_FEATURE_NUM && + i < msg->set_features.num; i++) { + LE_TO_CPU(msg->set_features.b32[i]); + } + break; + case VIRTIO_MSG_SET_DEVICE_STATUS: + LE_TO_CPU(msg->set_device_status.status); + break; + case VIRTIO_MSG_GET_CONFIG: + LE_TO_CPU(msg->get_config.offset); + LE_TO_CPU(msg->get_config.size); + break; + case VIRTIO_MSG_SET_CONFIG: + LE_TO_CPU(msg->set_config.generation); + LE_TO_CPU(msg->set_config.offset); + LE_TO_CPU(msg->set_config.size); + break; + case VIRTIO_MSG_GET_SHM: + LE_TO_CPU(msg->get_shm.index); + break; + case VIRTIO_MSG_GET_VQUEUE: + LE_TO_CPU(msg->get_vqueue.index); + break; + case VIRTIO_MSG_SET_VQUEUE: + LE_TO_CPU(msg->set_vqueue.index); + LE_TO_CPU(msg->set_vqueue.size); + LE_TO_CPU(msg->set_vqueue.descriptor_addr); + LE_TO_CPU(msg->set_vqueue.driver_addr); + LE_TO_CPU(msg->set_vqueue.device_addr); + break; + case VIRTIO_MSG_RESET_VQUEUE: + LE_TO_CPU(msg->reset_vqueue.index); + break; + case VIRTIO_MSG_EVENT_CONFIG: + LE_TO_CPU(msg->event_config.status); + LE_TO_CPU(msg->event_config.generation); + LE_TO_CPU(msg->event_config.offset); + LE_TO_CPU(msg->event_config.size); + break; + case VIRTIO_MSG_EVENT_AVAIL: + LE_TO_CPU(msg->event_avail.index); + LE_TO_CPU(msg->event_avail.next_offset_wrap); + break; + case VIRTIO_MSG_EVENT_USED: + LE_TO_CPU(msg->event_used.index); + break; + default: + break; + } +} + +static inline size_t virtio_msg_header_size(void) +{ + return offsetof(VirtIOMSG, payload_u8); +} + +static inline void virtio_msg_pack_header(VirtIOMSG *msg, + uint8_t msg_id, + uint8_t type, + uint16_t dev_num, + uint16_t token, + uint16_t payload_size) +{ + uint16_t msg_size =3D virtio_msg_header_size() + payload_size; + + msg->type =3D type; + msg->msg_id =3D msg_id; + msg->dev_num =3D cpu_to_le16(dev_num); + msg->token =3D cpu_to_le16(token); + msg->msg_size =3D cpu_to_le16(msg_size); + + /* Keep things predictable. */ + memset(msg->payload_u8, 0, sizeof msg->payload_u8); +} + +static inline void virtio_msg_pack_get_device_info(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_DEVICE_INFO, 0, dev_num, token,= 0); +} + +static inline void virtio_msg_pack_get_device_info_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t device_id, + uint32_t vendor_id, + uint32_t num_feature_bi= ts, + uint32_t config_size, + uint32_t max_vqs, + uint16_t admin_vq_idx, + uint16_t admin_vq_count) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_DEVICE_INFO, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, + sizeof msg->get_device_info_resp); + + msg->get_device_info_resp.device_id =3D cpu_to_le32(device_id); + msg->get_device_info_resp.vendor_id =3D cpu_to_le32(vendor_id); + msg->get_device_info_resp.num_feature_bits =3D cpu_to_le32(num_feature= _bits); + msg->get_device_info_resp.config_size =3D cpu_to_le32(config_size); + msg->get_device_info_resp.max_vqs =3D cpu_to_le32(max_vqs); + msg->get_device_info_resp.admin_vq_idx =3D cpu_to_le16(admin_vq_idx); + msg->get_device_info_resp.admin_vq_count =3D cpu_to_le16(admin_vq_coun= t); +} + +static inline void virtio_msg_pack_get_features(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t index, + uint32_t num) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_GET_FEATURES, 0, dev_num, token, + sizeof msg->get_features); + + msg->get_features.index =3D cpu_to_le32(index); + msg->get_features.num =3D cpu_to_le32(num); +} + +static inline void virtio_msg_pack_get_features_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t index, + uint32_t num, + uint32_t *f) +{ + unsigned int i; + + virtio_msg_pack_header(msg, VIRTIO_MSG_GET_FEATURES, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, + sizeof msg->get_features_resp + num * sizeof(*f= )); + + msg->get_features_resp.index =3D cpu_to_le32(index); + msg->get_features_resp.num =3D cpu_to_le32(num); + + assert(num <=3D VIRTIO_MSG_MAX_FEATURE_NUM); + + for (i =3D 0; i < num && i < VIRTIO_MSG_MAX_FEATURE_NUM; i++) { + msg->get_features_resp.b32[i] =3D cpu_to_le32(f[i]); + } +} + +static inline void virtio_msg_pack_set_features(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t index, + uint32_t num, + uint32_t *f) +{ + unsigned int i; + + virtio_msg_pack_header(msg, VIRTIO_MSG_SET_FEATURES, 0, dev_num, token, + sizeof msg->set_features + num * sizeof(*f)); + + msg->set_features.index =3D cpu_to_le32(index); + msg->set_features.num =3D cpu_to_le32(num); + + assert(num <=3D VIRTIO_MSG_MAX_FEATURE_NUM); + + for (i =3D 0; i < num && i < VIRTIO_MSG_MAX_FEATURE_NUM; i++) { + msg->set_features.b32[i] =3D cpu_to_le32(f[i]); + } +} + +static inline void virtio_msg_pack_set_features_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_SET_FEATURES, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, 0); +} + +static inline void virtio_msg_pack_set_device_status(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t status) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_SET_DEVICE_STATUS, 0, dev_num, + token, sizeof msg->set_device_status); + + msg->set_device_status.status =3D cpu_to_le32(status); +} + +static inline void virtio_msg_pack_set_device_status_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t status) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_SET_DEVICE_STATUS, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, + sizeof msg->set_device_status_resp); + + msg->set_device_status_resp.status =3D cpu_to_le32(status); +} + +static inline void virtio_msg_pack_get_device_status(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_GET_DEVICE_STATUS, 0, + dev_num, token, 0); +} + +static inline void virtio_msg_pack_get_device_status_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t status) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_GET_DEVICE_STATUS, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, + sizeof msg->get_device_status_resp); + + msg->get_device_status_resp.status =3D cpu_to_le32(status); +} + +static inline void virtio_msg_pack_get_config(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t size, + uint32_t offset) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_GET_CONFIG, 0, dev_num, token, + sizeof msg->get_config); + + msg->get_config.offset =3D cpu_to_le32(offset); + msg->get_config.size =3D cpu_to_le32(size); +} + +static inline void virtio_msg_pack_get_config_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t size, + uint32_t offset, + uint32_t generation, + uint8_t data[]) +{ + assert(size <=3D VIRTIO_MSG_MAX_CONFIG_BYTES); + + virtio_msg_pack_header(msg, VIRTIO_MSG_GET_CONFIG, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, + sizeof msg->get_config_resp + size); + + msg->get_config_resp.generation =3D cpu_to_le32(generation); + msg->get_config_resp.offset =3D cpu_to_le32(offset); + msg->get_config_resp.size =3D cpu_to_le32(size); + + memcpy(&msg->get_config_resp.data, data, size); +} + +static inline void virtio_msg_pack_set_config(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t size, + uint32_t offset, + uint32_t generation, + uint8_t data[]) +{ + assert(size <=3D VIRTIO_MSG_MAX_CONFIG_BYTES); + + virtio_msg_pack_header(msg, VIRTIO_MSG_SET_CONFIG, 0, dev_num, token, + sizeof msg->set_config + size); + + msg->set_config.offset =3D cpu_to_le32(offset); + msg->set_config.size =3D cpu_to_le32(size); + msg->set_config.generation =3D cpu_to_le32(generation); + + memcpy(&msg->set_config.data, data, size); +} + +static inline void virtio_msg_pack_set_config_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t size, + uint32_t offset, + uint32_t generation, + uint8_t data[]) +{ + assert(size <=3D VIRTIO_MSG_MAX_CONFIG_BYTES); + + virtio_msg_pack_header(msg, VIRTIO_MSG_SET_CONFIG, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, + sizeof msg->set_config_resp + size); + + msg->set_config_resp.offset =3D cpu_to_le32(offset); + msg->set_config_resp.size =3D cpu_to_le32(size); + msg->set_config_resp.generation =3D cpu_to_le32(generation); + + memcpy(&msg->set_config_resp.data, data, size); +} + +static inline void virtio_msg_pack_get_vqueue(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t index) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_GET_VQUEUE, 0, dev_num, token, + sizeof msg->get_vqueue); + + msg->get_vqueue.index =3D cpu_to_le32(index); +} + +static inline void virtio_msg_pack_get_vqueue_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t index, + uint32_t max_size, + uint32_t size, + uint64_t descriptor_add= r, + uint64_t driver_addr, + uint64_t device_addr) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_GET_VQUEUE, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, + sizeof msg->get_vqueue_resp); + + msg->get_vqueue_resp.index =3D cpu_to_le32(index); + msg->get_vqueue_resp.max_size =3D cpu_to_le32(max_size); + msg->get_vqueue_resp.size =3D cpu_to_le32(size); + msg->get_vqueue_resp.descriptor_addr =3D cpu_to_le64(descriptor_addr); + msg->get_vqueue_resp.driver_addr =3D cpu_to_le64(driver_addr); + msg->get_vqueue_resp.device_addr =3D cpu_to_le64(device_addr); +} + +static inline void virtio_msg_pack_reset_vqueue(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t index) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_RESET_VQUEUE, 0, dev_num, token, + sizeof msg->reset_vqueue); + + msg->reset_vqueue.index =3D cpu_to_le32(index); +} + +static inline void virtio_msg_pack_reset_vqueue_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_RESET_VQUEUE, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, 0); +} + +static inline void virtio_msg_pack_get_shm(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t index) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_GET_SHM, 0, dev_num, token, + sizeof msg->get_shm); + + msg->get_shm.index =3D cpu_to_le32(index); +} + +static inline void virtio_msg_pack_get_shm_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t index, + uint32_t length, + uint32_t address) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_GET_SHM, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, + sizeof msg->get_shm_resp); + + msg->get_shm_resp.index =3D cpu_to_le32(index); + msg->get_shm_resp.length =3D cpu_to_le32(length); + msg->get_shm_resp.address =3D cpu_to_le32(address); +} + +static inline void virtio_msg_pack_set_vqueue(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token, + uint32_t index, + uint32_t size, + uint64_t descriptor_addr, + uint64_t driver_addr, + uint64_t device_addr) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_SET_VQUEUE, 0, dev_num, token, + sizeof msg->set_vqueue); + + msg->set_vqueue.index =3D cpu_to_le32(index); + msg->set_vqueue.unused =3D 0; + msg->set_vqueue.size =3D cpu_to_le32(size); + msg->set_vqueue.descriptor_addr =3D cpu_to_le64(descriptor_addr); + msg->set_vqueue.driver_addr =3D cpu_to_le64(driver_addr); + msg->set_vqueue.device_addr =3D cpu_to_le64(device_addr); +} + +static inline void virtio_msg_pack_set_vqueue_resp(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t token) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_SET_VQUEUE, + VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, 0); +} + +static inline void virtio_msg_pack_event_avail(VirtIOMSG *msg, + uint16_t dev_num, + uint32_t index, + uint32_t next_offset, + bool next_wrap) +{ + uint32_t next_ow; + + virtio_msg_pack_header(msg, VIRTIO_MSG_EVENT_AVAIL, 0, dev_num, 0, + sizeof msg->event_avail); + + /* next_offset is 31b wide. */ + assert((next_offset & 0x80000000U) =3D=3D 0); + + /* Pack the next_offset_wrap field. */ + next_ow =3D next_wrap << 31; + next_ow |=3D next_offset; + + msg->event_avail.index =3D cpu_to_le32(index); + msg->event_avail.next_offset_wrap =3D cpu_to_le32(next_ow); +} + +static inline void virtio_msg_pack_event_used(VirtIOMSG *msg, + uint16_t dev_num, + uint32_t index) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_EVENT_USED, 0, dev_num, 0, + sizeof msg->event_used); + + msg->event_used.index =3D cpu_to_le32(index); +} + +static inline void virtio_msg_pack_event_config(VirtIOMSG *msg, + uint16_t dev_num, + uint32_t status, + uint32_t generation, + uint32_t offset, + uint32_t size, + uint8_t *value) +{ + unsigned int max_size; + + max_size =3D VIRTIO_MSG_MAX_SIZE; + max_size -=3D offsetof(VirtIOMSG, event_config.config_value); + assert(size <=3D max_size); + + virtio_msg_pack_header(msg, VIRTIO_MSG_EVENT_CONFIG, 0, dev_num, 0, + sizeof msg->event_config + size); + + msg->event_config.status =3D cpu_to_le32(status); + msg->event_config.generation =3D cpu_to_le32(generation); + msg->event_config.offset =3D cpu_to_le32(offset); + msg->event_config.size =3D cpu_to_le32(size); + + if (size > 0 && size <=3D max_size) { + memcpy(&msg->event_config.config_value[0], value, size); + } +} + +static inline void virtio_msg_pack_bus_get_devices(VirtIOMSG *msg, + uint16_t offset, + uint16_t num) +{ + virtio_msg_pack_header(msg, + VIRTIO_MSG_BUS_GET_DEVICES, VIRTIO_MSG_TYPE_BUS, + 0, 0, sizeof msg->bus_get_devices); + + msg->bus_get_devices.offset =3D cpu_to_le16(offset); + msg->bus_get_devices.num =3D cpu_to_le16(num); +} + +static inline void virtio_msg_pack_bus_get_devices_resp(VirtIOMSG *msg, + uint16_t offset, + uint16_t num, + uint16_t next_offs= et, + uint8_t *data) +{ + size_t byte_len =3D (num + 7) / 8; + + virtio_msg_pack_header(msg, + VIRTIO_MSG_BUS_GET_DEVICES, + VIRTIO_MSG_TYPE_BUS | VIRTIO_MSG_TYPE_RESPONSE, + 0, 0, sizeof msg->bus_get_devices_resp + byte_l= en); + + msg->bus_get_devices_resp.offset =3D cpu_to_le16(offset); + msg->bus_get_devices_resp.num =3D cpu_to_le16(num); + msg->bus_get_devices_resp.next_offset =3D cpu_to_le16(next_offset); + + if (byte_len > 0) { + memcpy(msg->bus_get_devices_resp.data, data, byte_len); + } +} + +static inline void virtio_msg_pack_bus_ping_resp(VirtIOMSG *msg, + uint32_t data) +{ + virtio_msg_pack_header(msg, + VIRTIO_MSG_BUS_PING, + VIRTIO_MSG_TYPE_BUS | VIRTIO_MSG_TYPE_RESPONSE, + 0, 0, sizeof msg->bus_ping_resp); + + msg->bus_ping_resp.data =3D cpu_to_le32(data); +} + +static inline void virtio_msg_pack_bus_event_device(VirtIOMSG *msg, + uint16_t dev_num, + uint16_t dev_state) +{ + virtio_msg_pack_header(msg, VIRTIO_MSG_BUS_EVENT_DEVICE, + VIRTIO_MSG_TYPE_BUS, 0, 0, + sizeof msg->bus_event_device); + + msg->bus_event_device.dev_num =3D cpu_to_le16(dev_num); + msg->bus_event_device.dev_state =3D cpu_to_le16(dev_state); +} + +static inline const char *virtio_msg_id_to_str(uint8_t type, uint8_t msg_i= d) +{ +#define VIRTIO_MSG_TYPE2STR(x) [VIRTIO_MSG_ ## x] =3D stringify(x) + static const char *type2str[VIRTIO_MSG_MAX + 1] =3D { + VIRTIO_MSG_TYPE2STR(DEVICE_INFO), + VIRTIO_MSG_TYPE2STR(GET_FEATURES), + VIRTIO_MSG_TYPE2STR(SET_FEATURES), + VIRTIO_MSG_TYPE2STR(GET_CONFIG), + VIRTIO_MSG_TYPE2STR(SET_CONFIG), + VIRTIO_MSG_TYPE2STR(GET_DEVICE_STATUS), + VIRTIO_MSG_TYPE2STR(SET_DEVICE_STATUS), + VIRTIO_MSG_TYPE2STR(GET_VQUEUE), + VIRTIO_MSG_TYPE2STR(SET_VQUEUE), + VIRTIO_MSG_TYPE2STR(RESET_VQUEUE), + VIRTIO_MSG_TYPE2STR(EVENT_CONFIG), + VIRTIO_MSG_TYPE2STR(EVENT_AVAIL), + VIRTIO_MSG_TYPE2STR(EVENT_USED), + }; + static const char *bus_type2str[VIRTIO_MSG_MAX + 1] =3D { + VIRTIO_MSG_TYPE2STR(BUS_GET_DEVICES), + }; + + if (type & VIRTIO_MSG_TYPE_BUS) { + return bus_type2str[msg_id]; + } else { + return type2str[msg_id]; + } +} + +static inline void virtio_msg_print_status(uint32_t status) +{ + printf("status %x", status); + + if (status & VIRTIO_CONFIG_S_ACKNOWLEDGE) { + printf(" ACKNOWLEDGE"); + } + if (status & VIRTIO_CONFIG_S_DRIVER) { + printf(" DRIVER"); + } + if (status & VIRTIO_CONFIG_S_DRIVER_OK) { + printf(" DRIVER_OK"); + } + if (status & VIRTIO_CONFIG_S_FEATURES_OK) { + printf(" FEATURES_OK"); + } + if (status & VIRTIO_CONFIG_S_NEEDS_RESET) { + printf(" NEEDS_RESET"); + } + if (status & VIRTIO_CONFIG_S_FAILED) { + printf(" FAILED"); + } + + printf("\n"); +} + +static inline void virtio_msg_print(VirtIOMSG *msg) +{ + bool resp =3D msg->type & VIRTIO_MSG_TYPE_RESPONSE; + size_t payload_size; + int i; + + assert(msg); + printf("virtio-msg: id %s 0x%x type 0x%x dev_num 0x%x msg_size 0x%x\n", + virtio_msg_id_to_str(msg->type, msg->msg_id), msg->msg_id, + msg->type, msg->dev_num, msg->msg_size); + + payload_size =3D msg->msg_size - offsetof(VirtIOMSG, payload_u8); + if (payload_size > ARRAY_SIZE(msg->payload_u8)) { + printf("Size overflow! %zu > %zu\n", + payload_size, ARRAY_SIZE(msg->payload_u8)); + payload_size =3D ARRAY_SIZE(msg->payload_u8); + } + + for (i =3D 0; i < payload_size; i++) { + printf("%2.2x ", msg->payload_u8[i]); + if (((i + 1) % 16) =3D=3D 0) { + printf("\n"); + } + } + + switch (msg->msg_id) { + case VIRTIO_MSG_GET_DEVICE_STATUS: + if (resp) { + virtio_msg_print_status(msg->get_device_status_resp.status); + } + break; + case VIRTIO_MSG_SET_DEVICE_STATUS: + virtio_msg_print_status(msg->set_device_status.status); + break; + case VIRTIO_MSG_SET_VQUEUE: + printf("set-vqueue: index=3D%d size=3D%d desc-addr=3D%lx " + "driver-addr=3D%lx device-addr=3D%lx\n", + msg->set_vqueue.index, msg->set_vqueue.size, + msg->set_vqueue.descriptor_addr, + msg->set_vqueue.driver_addr, + msg->set_vqueue.device_addr); + break; + } + printf("\n"); +} +#endif diff --git a/include/hw/virtio/virtio-msg.h b/include/hw/virtio/virtio-msg.h new file mode 100644 index 0000000000..c25195b1bc --- /dev/null +++ b/include/hw/virtio/virtio-msg.h @@ -0,0 +1,56 @@ +/* + * Virtio MSG bindings + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * Written by Edgar E. Iglesias . + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VIRTIO_MSG_H +#define HW_VIRTIO_MSG_H + +#include "hw/core/sysbus.h" +#include "hw/virtio/virtio-bus.h" + +#define TYPE_VIRTIO_MSG_PROXY_BUS "virtio-msg-proxy-bus" +/* This is reusing the VirtioBusState typedef from TYPE_VIRTIO_BUS */ +DECLARE_OBJ_CHECKERS(VirtioBusState, VirtioBusClass, + VIRTIO_MSG_PROXY_BUS, TYPE_VIRTIO_MSG_PROXY_BUS) + +#define TYPE_VIRTIO_MSG_OUTER_BUS "virtio-msg-outer-bus" +OBJECT_DECLARE_SIMPLE_TYPE(BusState, VIRTIO_MSG_OUTER_BUS) + +#define TYPE_VIRTIO_MSG_DEV "virtio-msg-dev" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOMSGDev, VIRTIO_MSG_DEV) + +#define TYPE_VIRTIO_MSG "virtio-msg" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOMSGProxy, VIRTIO_MSG) + +struct VirtIOMSGDev { + DeviceState parent_obj; + + /* virtio-bus */ + VirtioBusState bus; + + VirtIOMSGProxy *proxy; + uint16_t dev_num; + uint64_t guest_features; +}; + +#define VIRTIO_MSG_MAX_DEVS 32 +struct VirtIOMSGProxy { + DeviceState parent_obj; + + AddressSpace dma_as; + AddressSpace *bus_as; + IOMMUMemoryRegion mr_iommu; + MemoryRegion *mr_bus; + + BusState devs_bus[VIRTIO_MSG_MAX_DEVS]; + VirtIOMSGDev devs[VIRTIO_MSG_MAX_DEVS]; + + /* virtio-msg-bus. */ + BusState msg_bus; +}; +#endif --=20 2.43.0 From nobody Tue Apr 7 23:08:15 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1771948694; cv=none; d=zohomail.com; s=zohoarc; b=NN0ax2eVSI0GyzrLrbMkjGG/USY95tQk6u+9mI7sK+b9Tgy/bN/HltLHxdEH7DHvMLsvw8hBwyZfj0FOXotGVWo447iTp3cxaa361VCKW9RSOX3SuSVSOEAotakrB7073fUg9fHERuYMlbaNV7ItxpDM7ywFti8ZwASBhE5y/Tw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1771948694; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=ZzGE6CUWLA5ueYduHjeibn6N465oB1K2VLZVfOZlWao=; b=iBKT4ODMFwwY7bmuKqOn2aXWzCCp4BIJdd2jfA1MuYEiZJOHghH401BkOCIm4C3Iq5XxoL05gMnuV+jrM9P6NKNZlwwP4kBtvWDeAFb4mgq6FQur8IS0wANJ/fKUwpNjqz+pQkbEPXQ7eCRK4LVXt3me7mgbZAOkMiJjooXopUs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1771948694410734.7460997314661; Tue, 24 Feb 2026 07:58:14 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vuunA-0006Vl-EV; Tue, 24 Feb 2026 10:57:45 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vuun6-0006VC-0y for qemu-devel@nongnu.org; Tue, 24 Feb 2026 10:57:40 -0500 Received: from mail-vk1-xa2e.google.com ([2607:f8b0:4864:20::a2e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vuun1-0000oy-QG for qemu-devel@nongnu.org; Tue, 24 Feb 2026 10:57:39 -0500 Received: by mail-vk1-xa2e.google.com with SMTP id 71dfb90a1353d-56637565faaso4622573e0c.1 for ; Tue, 24 Feb 2026 07:57:35 -0800 (PST) Received: from gmail.com (ip190-5-140-142.intercom.com.sv. [190.5.140.142]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-568e57a0009sm12904238e0c.3.2026.02.24.07.57.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 07:57:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771948654; x=1772553454; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ZzGE6CUWLA5ueYduHjeibn6N465oB1K2VLZVfOZlWao=; b=lRNLWo5R3iMFhC5A3+Jeyn4Xn0jMC3fYmrPiO/FVDGv7PKDeMI38yTn5LM3sHKjXv9 LCEAT9VyfC4gArdkPhsA56c0zZjoa+SGGvoC4tBOR/FZrAskvnZLVxVYLoDFT9HFh+MI gG22PPIp1ERLy/6EobW0BHzmQxrZ33VRMXuAYJ8agNCaZU0DDWXvouwXA3+uyS0xiTAa tFTW8Lgd+FshYu+EL+rz/evTRHvnInmiNlv+AvtKMZsVvCrgjGDENkOzlWcDVS71d2R4 pxHuhfF7pnB1AJqHSNUjLAeVF5Ra0Zs7q6Nu7mjYoBL66XmO1q8FAGRJdy+MCxk5x3te LzhQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771948654; x=1772553454; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ZzGE6CUWLA5ueYduHjeibn6N465oB1K2VLZVfOZlWao=; b=HUU0al7fudRCjqp2aIcnZDwToW20rnsOcHTxUsctoVLDR0nijk6COK0W6vzQ1PAw18 B6GtZ7FDmm8gwtc/BS6U4xmpczuAusO3B4DiJ0MY9srdnhSy+Iin6IuROE9usM9MfChN o0pCMPwoeFPNGUURNDrw2v6g0Qkdbwch2yc/uuB1wqsqXVYRD+a/Qnh5sxPZ/2SE4MY+ OYORG03AmAEjOt3OUyfcPSc/AUMG7Lc1crHZjyHkn5NkpK/Sae6tBDbRTbMe6p/8iGxj khbm1ANblZouxUiyscM0Fty3jIVHzFZP0JyTxNlFeX71v8nIcTxDzxEoYh9v5ZOzqYvY y/Gw== X-Gm-Message-State: AOJu0Yz44YI4E96BUBGc6h1rWN31/HO8jNIGgWSoN9QedWVIBdGyU4GK hihMuvSDvNbNJc5P0L0PtUQP99naT46BxlV5FqjI0iuB70dk1Y3lqFSlAcRH08Ux X-Gm-Gg: ATEYQzwWOTpC4s2PPZ+9Mm7KGDsNw3eGty/HROGdwTJYQXVTYns5WEbhqHiA6YOBmcX 0E3eTPa0rA+wcaw4dnLdwgqphNOfskikJzp8UI1fRwMxjovf6wdKJah7gYLPXMohL2TVkjV08Wk I6epGb+3prZG9/HH5S762nPrOnTnlXVYasWgC7xbux6M2HBwSUDFO/ja1hrHOadk56O7ROvfu/x j2MhIn627tewFZ1AeKVUdGNTJpFsPcDJCrl4EjUQo0yLQm69+1t2kdMUCmixNCwVI1syFGCvPnS LG3m8wd6Mf5/20Xmceeep0YNuf5+D+Hu3VG+HuH5IVneAKeR6ZNM7XXeJNH+DO3YiGagyD0XfSN TfJUyMRtLlGiQwlF6cJkwODQR2XbYVldRjWVdkyWwTAkR59JLfvhmlOKiSiBoX0wgS2FH4S4ink qkcR4PyaBLsCuzYPj6ufV7UFmGwS4gUfWcU8kn8KbDGOcPmMawyib0iEr1rQ== X-Received: by 2002:a05:6122:4701:b0:566:4689:46eb with SMTP id 71dfb90a1353d-568e455a597mr4636687e0c.0.1771948654106; Tue, 24 Feb 2026 07:57:34 -0800 (PST) From: "Edgar E. Iglesias" To: qemu-devel@nongnu.org, Paolo Bonzini , "Michael S. Tsirkin" Cc: alex.bennee@linaro.org, bill.mills@linaro.org, edgar.iglesias@amd.com Subject: [PATCH v2 3/4] hw/misc: Add generic virtio-msg AMP PCI device Date: Tue, 24 Feb 2026 16:57:20 +0100 Message-ID: <20260224155721.612314-4-edgar.iglesias@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260224155721.612314-1-edgar.iglesias@gmail.com> References: <20260224155721.612314-1-edgar.iglesias@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.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; Received-SPF: pass client-ip=2607:f8b0:4864:20::a2e; envelope-from=edgar.iglesias@gmail.com; helo=mail-vk1-xa2e.google.com X-Spam_score_int: 12 X-Spam_score: 1.2 X-Spam_bar: + X-Spam_report: (1.2 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_SBL_CSS=3.335, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1771948696527158500 Content-Type: text/plain; charset="utf-8" From: "Edgar E. Iglesias" Signed-off-by: Edgar E. Iglesias --- hw/misc/Kconfig | 7 + hw/misc/meson.build | 1 + hw/misc/virtio-msg-amp-pci.c | 361 +++++++++++++++++++++++++++++++++ include/hw/virtio/spsc_queue.h | 213 +++++++++++++++++++ 4 files changed, 582 insertions(+) create mode 100644 hw/misc/virtio-msg-amp-pci.c create mode 100644 include/hw/virtio/spsc_queue.h diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index f4d49248c0..a8dea757ed 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -30,6 +30,13 @@ config IOMMU_TESTDEV default y if TEST_DEVICES depends on PCI =20 +config VIRTIO_MSG_AMP_PCI + bool + default y if PCI_DEVICES + depends on PCI + select VIRTIO + select VIRTIO_MSG + config EDU bool default y if TEST_DEVICES diff --git a/hw/misc/meson.build b/hw/misc/meson.build index d304a98498..cfca758fdb 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -9,6 +9,7 @@ system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c= ')) system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c')) system_ss.add(when: 'CONFIG_LED', if_true: files('led.c')) system_ss.add(when: 'CONFIG_PVPANIC_COMMON', if_true: files('pvpanic.c')) +system_ss.add(when: 'CONFIG_VIRTIO_MSG_AMP_PCI', if_true: files('virtio-ms= g-amp-pci.c')) =20 # ARM devices system_ss.add(when: 'CONFIG_PL310', if_true: files('arm_l2x0.c')) diff --git a/hw/misc/virtio-msg-amp-pci.c b/hw/misc/virtio-msg-amp-pci.c new file mode 100644 index 0000000000..192e4cf146 --- /dev/null +++ b/hw/misc/virtio-msg-amp-pci.c @@ -0,0 +1,361 @@ +/* + * Model of a virtio-msg AMP capable PCI device. + * + * Copyright (C) 2025 Advanced Micro Devices, Inc. + * Written by Edgar E. Iglesias + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/log.h" + +#include "system/memory.h" +#include "migration/vmstate.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/sysbus.h" +#include "hw/core/register.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/msix.h" + +#include "hw/virtio/virtio-msg.h" +#include "hw/virtio/virtio-msg-bus.h" +#include "hw/virtio/spsc_queue.h" + +#define TYPE_VMSG_AMP_PCI "virtio-msg-amp-pci" +OBJECT_DECLARE_SIMPLE_TYPE(VmsgAmpPciState, VMSG_AMP_PCI) + +#define TYPE_VMSG_BUS_AMP_PCI "virtio-msg-bus-amp-pci" +OBJECT_DECLARE_SIMPLE_TYPE(VmsgBusAmpPciState, VMSG_BUS_AMP_PCI) +#define VMSG_BUS_AMP_PCI_GET_PARENT_CLASS(obj) \ + OBJECT_GET_PARENT_CLASS(obj, TYPE_VMSG_BUS_AMP_PCI) + +REG32(VERSION, 0x00) +REG32(FEATURES, 0x04) +REG32(NOTIFY, 0x20) + +#define MAX_FIFOS 8 + +typedef struct VmsgBusAmpPciState { + VirtIOMSGBusDevice parent; + PCIDevice *pcidev; + unsigned int queue_index; + + struct { + void *va; + spsc_queue driver; + spsc_queue device; + unsigned int mapcount; + } shm; +} VmsgBusAmpPciState; + +typedef struct VmsgAmpPciState { + PCIDevice dev; + MemoryRegion mr_mmio; + MemoryRegion mr_ram; + + struct fifo_bus { + VmsgBusAmpPciState dev; + VirtIOMSGProxy proxy; + BusState bus; + } fifo[MAX_FIFOS]; + + struct { + uint32_t num_fifos; + } cfg; +} VmsgAmpPciState; + +static void vmsg_bus_amp_pci_process(VirtIOMSGBusDevice *bd); + +static uint64_t vmsg_read(void *opaque, hwaddr addr, unsigned int size) +{ + uint64_t r =3D 0; + + assert(size =3D=3D 4); + + switch (addr) { + case A_VERSION: + /* v0.1 */ + r =3D 0x0001; + break; + case A_FEATURES: + /* No features bit yet. */ + break; + default: + break; + } + + return r; +} + +static void vmsg_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + VmsgAmpPciState *s =3D VMSG_AMP_PCI(opaque); + unsigned int q; + + assert(size =3D=3D 4); + + if (addr >=3D A_NOTIFY) { + q =3D (addr - A_NOTIFY) / 4; + if (q >=3D s->cfg.num_fifos) { + /* Fifo doesn't exist. */ + return; + } + + vmsg_bus_amp_pci_process(VIRTIO_MSG_BUS_DEVICE(&s->fifo[q].dev)); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to read-only reg 0x%" HWADDR_PRIx "\n", + __func__, addr); + } +} + +static const MemoryRegionOps vmsg_pci_ops =3D { + .read =3D vmsg_read, + .write =3D vmsg_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, +}; + +static void vmsg_create_bus(VmsgAmpPciState *s, unsigned int i) +{ + DeviceState *dev =3D DEVICE(s); + Object *o =3D OBJECT(s); + struct fifo_bus *fifo =3D &s->fifo[i]; + g_autofree char *fifo_name =3D g_strdup_printf("fifo%d", i); + + qbus_init(&fifo->bus, sizeof(fifo->bus), TYPE_VIRTIO_MSG_OUTER_BUS, + dev, fifo_name); + + /* Create the proxy. */ + object_initialize_child(o, "proxy[*]", &fifo->proxy, TYPE_VIRTIO_MSG); + qdev_realize(DEVICE(&fifo->proxy), BUS(&fifo->bus), &error_fatal); + + object_initialize_child(o, "vmsg[*]", &fifo->dev, + TYPE_VMSG_BUS_AMP_PCI); + qdev_realize(DEVICE(&fifo->dev), &fifo->proxy.msg_bus, &error_fatal); + + msix_vector_use(PCI_DEVICE(s), i); + + /* Caches for quick lookup. */ + fifo->dev.queue_index =3D i; + fifo->dev.pcidev =3D PCI_DEVICE(s); +} + +static void vmsg_amp_pci_realizefn(PCIDevice *dev, Error **errp) +{ + VmsgAmpPciState *s =3D VMSG_AMP_PCI(dev); + int i; + + if (!s->cfg.num_fifos || s->cfg.num_fifos > MAX_FIFOS) { + error_setg(errp, "Unsupported number of FIFOs (%u)", s->cfg.num_fi= fos); + return; + } + + memory_region_init_io(&s->mr_mmio, OBJECT(s), &vmsg_pci_ops, s, + TYPE_VMSG_AMP_PCI, 16 * KiB); + + /* 16KB per FIFO. */ + memory_region_init_ram(&s->mr_ram, OBJECT(s), "ram", + s->cfg.num_fifos * 16 * KiB, &error_fatal); + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mr_mmio); + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH, + &s->mr_ram); + + msix_init_exclusive_bar(PCI_DEVICE(s), s->cfg.num_fifos, 2, &error_fat= al); + for (i =3D 0; i < s->cfg.num_fifos; i++) { + vmsg_create_bus(s, i); + } +} + +static void vmsg_amp_pci_exit(PCIDevice *dev) +{ + VmsgAmpPciState *s =3D VMSG_AMP_PCI(dev); + + vmstate_unregister_ram(&s->mr_ram, DEVICE(dev)); + msix_uninit_exclusive_bar(dev); +} + +static int vmsg_amp_pci_post_load(void *opaque, int version_id) +{ + VmsgAmpPciState *s =3D VMSG_AMP_PCI(opaque); + int i; + + for (i =3D 0; i < s->cfg.num_fifos; i++) { + VmsgBusAmpPciState *bus =3D &s->fifo[i].dev; + + memset(&bus->shm, 0, sizeof(bus->shm)); + } + + return 0; +} + +static const Property vmsg_properties[] =3D { + DEFINE_PROP_UINT32("num-fifos", VmsgAmpPciState, cfg.num_fifos, 1), +}; + +static const VMStateDescription vmstate_vmsg_pci =3D { + .name =3D TYPE_VMSG_AMP_PCI, + .version_id =3D 1, + .minimum_version_id =3D 1, + .post_load =3D vmsg_amp_pci_post_load, + .fields =3D (const VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, VmsgAmpPciState), + VMSTATE_MSIX(dev, VmsgAmpPciState), + VMSTATE_END_OF_LIST() + } +}; + +static void vmsg_amp_pci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + PCIDeviceClass *pc =3D PCI_DEVICE_CLASS(klass); + + device_class_set_props(dc, vmsg_properties); + + pc->realize =3D vmsg_amp_pci_realizefn; + pc->exit =3D vmsg_amp_pci_exit; + pc->vendor_id =3D PCI_VENDOR_ID_XILINX; + pc->device_id =3D 0x9039; + pc->revision =3D 1; + pc->class_id =3D PCI_CLASS_SYSTEM_OTHER; + dc->vmsd =3D &vmstate_vmsg_pci; + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static bool vmsg_bus_amp_pci_map_fifo(VmsgBusAmpPciState *s) +{ + VmsgAmpPciState *pci_s =3D VMSG_AMP_PCI(s->pcidev); + void *va; + size_t fifo_offset; + + if (s->shm.mapcount) { + s->shm.mapcount++; + return true; + } + + va =3D memory_region_get_ram_ptr(&pci_s->mr_ram); + if (!va) { + return false; + } + + fifo_offset =3D s->queue_index * 16 * KiB; + va =3D (uint8_t *)va + fifo_offset; + + if (!s->shm.driver.shm) { + int capacity =3D spsc_capacity(4 * KiB); + + /* + * Layout: + * 0 - 4KB Reserved + * 4KB - 8KB Driver queue + * 8KB - 12KB Device queue + */ + spsc_init(&s->shm.driver, "driver", capacity, va + 4 * KiB); + spsc_init(&s->shm.device, "device", capacity, va + 8 * KiB); + } + + /* Map queues. */ + s->shm.va =3D va; + s->shm.mapcount++; + return true; +} + +static void vmsg_bus_amp_pci_unmap_fifo(VmsgBusAmpPciState *s) +{ + assert(s->shm.mapcount); + if (--s->shm.mapcount) { + return; + } + + /* TODO: Actually unmap. */ +} + +static void vmsg_bus_amp_pci_process(VirtIOMSGBusDevice *bd) +{ + VmsgBusAmpPciState *s =3D VMSG_BUS_AMP_PCI(bd); + spsc_queue *q; + VirtIOMSG msg; + bool r; + + if (!vmsg_bus_amp_pci_map_fifo(s)) { + return; + } + + /* + * We process the opposite queue, i.e, a driver will want to receive + * messages on the backend queue (and send messages on the driver queu= e). + */ + q =3D bd->peer->is_driver ? &s->shm.device : &s->shm.driver; + do { + r =3D spsc_recv(q, &msg, sizeof msg); + if (r) { + virtio_msg_bus_receive(bd, &msg); + } + } while (r); + vmsg_bus_amp_pci_unmap_fifo(s); +} + +static int vmsg_bus_amp_pci_send(VirtIOMSGBusDevice *bd, VirtIOMSG *msg_re= q) +{ + VmsgAmpPciState *pci_s =3D VMSG_AMP_PCI(OBJECT(bd)->parent); + VmsgBusAmpPciState *s =3D VMSG_BUS_AMP_PCI(bd); + bool sent; + + if (!vmsg_bus_amp_pci_map_fifo(s)) { + return VIRTIO_MSG_ERROR_MEMORY; + } + + sent =3D spsc_send(&s->shm.device, msg_req, sizeof *msg_req); + if (!sent) { + vmsg_bus_amp_pci_unmap_fifo(s); + return VIRTIO_MSG_ERROR_RETRY; + } + + /* Notify. */ + msix_notify(PCI_DEVICE(pci_s), s->queue_index); + + vmsg_bus_amp_pci_unmap_fifo(s); + return VIRTIO_MSG_NO_ERROR; +} + +static void vmsg_bus_amp_pci_class_init(ObjectClass *klass, + const void *data) +{ + VirtIOMSGBusDeviceClass *bdc =3D VIRTIO_MSG_BUS_DEVICE_CLASS(klass); + + bdc->process =3D vmsg_bus_amp_pci_process; + bdc->send =3D vmsg_bus_amp_pci_send; +} + +static const TypeInfo vmsg_pci_info[] =3D { + { + .name =3D TYPE_VMSG_AMP_PCI, + .parent =3D TYPE_PCI_DEVICE, + .instance_size =3D sizeof(VmsgAmpPciState), + .class_init =3D vmsg_amp_pci_class_init, + .interfaces =3D (const InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + }, + }, { + .name =3D TYPE_VMSG_BUS_AMP_PCI, + .parent =3D TYPE_VIRTIO_MSG_BUS_DEVICE, + .instance_size =3D sizeof(VmsgBusAmpPciState), + .class_init =3D vmsg_bus_amp_pci_class_init, + }, +}; + +static void vmsg_pci_register_types(void) +{ + type_register_static_array(vmsg_pci_info, ARRAY_SIZE(vmsg_pci_info)); +} + +type_init(vmsg_pci_register_types); diff --git a/include/hw/virtio/spsc_queue.h b/include/hw/virtio/spsc_queue.h new file mode 100644 index 0000000000..3d88baab55 --- /dev/null +++ b/include/hw/virtio/spsc_queue.h @@ -0,0 +1,213 @@ +/* + * Hardened and lockless Single Producer Single Consumer Queue implemented + * over shared-memory. + * + * The queue implementation does not look at packet contents, it's up to u= pper + * layers to make sure data is produced and parsed safely. All data is cop= ied + * in/out from/to local private buffers so the peer cannot mess with them = while + * upper layers parse. + * + * The queue is split into a private and a shared part. + * The private part contains cached and sanitized versions of the indexes = that + * indicate our position in the ring-buffer. Peers can corrupt the shared = area + * but have no access to the private area. So whenever we copy from the sh= ared + * area into the private one, we need to sanitize indexes and make sure th= ey + * are within bounds. + * + * A malicious peer can send corrupt data, it can stop receiving or flood = the + * queue causing a sort of denial of service but it can NOT cause our side + * to copy data in or out of buffers outside of the shared memory area. + * + * This implementation expects the SHM area to be cache-coherent or uncach= ed. + * The shared area can be mapped in different ways and our peer may be any= thing + * from another thread on our same OS to an FPGA implementation on a PCI c= ard. + * So local CPU cache-lines sizes, or spin-locks and things that work on a + * single CPU cluster are not used. Instead the implementation sticks to a= tomic + * load/stores of 32b values and to using memory-barriers to guarantee ord= ering. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SPSC_QUEUE_H__ +#define SPSC_QUEUE_H__ + +#include +#include "qemu/atomic.h" + +#define BUG_ON(x) assert(!(x)) + +#define SPSC_QUEUE_MAX_PACKET_SIZE 64 +/* + * This cache-line size is used to align fields in the hope of + * avoiding cache-line ping-pong:ing. Since the queue layout is + * used across heterogenous CPU clusters and across FPGA/HW implementation= s, + * a fixed size must be used, i.e not the local CPU's cache-line size. + */ +#define SPSC_QUEUE_CACHE_LINE_SIZE 64 + +typedef struct spsc_queue_shared { + uint32_t head __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE))); + uint32_t tail __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE))); + uint32_t packets[][SPSC_QUEUE_MAX_PACKET_SIZE / 4] + __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE))); +} spsc_queue_shared; + +typedef struct spsc_queue { + uint32_t cached_tail; + uint32_t cached_head; + spsc_queue_shared *shm; + const char *name; + unsigned int capacity; +} spsc_queue; + +/* Atomically load and sanitize an index from the SHM area. */ +static inline uint32_t spsc_atomic_load(spsc_queue *q, uint32_t *ptr) +{ + uint32_t val; + + val =3D qatomic_read(ptr); + /* Make sure packet reads are done after reading the index. */ + smp_mb_acquire(); + + /* Bounds check that index is within queue size. */ + if (val >=3D q->capacity) { + val =3D val % q->capacity; + } + + return val; +} + +static inline void spsc_atomic_store(spsc_queue *q, uint32_t *ptr, uint32_= t v) +{ + /* Make sure packet-data gets written before updating the index. */ + smp_mb_release(); + qatomic_set(ptr, v); +} + +/* Returns the capacity of a queue given a specific mapsize. */ +static inline unsigned int spsc_capacity(size_t mapsize) +{ + unsigned int capacity; + spsc_queue *q =3D NULL; + + if (mapsize < sizeof(*q->shm)) { + return 0; + } + + /* Start with the size of the shared area. */ + mapsize -=3D sizeof(*q->shm); + capacity =3D mapsize / sizeof(q->shm->packets[0]); + + if (capacity < 2) { + /* Capacities of less than 2 are invalid. */ + return 0; + } + + return capacity; +} + +static inline size_t spsc_mapsize(unsigned int capacity) +{ + spsc_queue *q =3D NULL; + size_t mapsize; + + BUG_ON(capacity < 2); + + mapsize =3D sizeof(*q->shm); + mapsize +=3D sizeof(q->shm->packets[0]) * capacity; + + return mapsize; +} + +static inline void spsc_init(spsc_queue *q, const char *name, size_t capac= ity, + void *mem) +{ + BUG_ON(!mem); + + /* Initialize private queue area to all zeroes */ + memset(q, 0, sizeof *q); + + q->shm =3D (spsc_queue_shared *) mem; + q->name =3D name; + q->capacity =3D capacity; + + /* In case we're opening a pre-existing queue, pick up where we left o= ff. */ + q->cached_tail =3D spsc_atomic_load(q, &q->shm->tail); + q->cached_head =3D spsc_atomic_load(q, &q->shm->head); +} + +static inline bool spsc_queue_is_full(spsc_queue *q) +{ + uint32_t next_head; + uint32_t head; + + head =3D spsc_atomic_load(q, &q->shm->head); + + next_head =3D head + 1; + if (next_head >=3D q->capacity) { + next_head =3D 0; + } + + if (next_head =3D=3D q->cached_tail) { + q->cached_tail =3D spsc_atomic_load(q, &q->shm->tail); + if (next_head =3D=3D q->cached_tail) { + return true; + } + } + return false; +} + +static inline bool spsc_send(spsc_queue *q, void *buf, size_t size) +{ + uint32_t next_head; + uint32_t head; + + BUG_ON(size > sizeof q->shm->packets[0]); + BUG_ON(size =3D=3D 0); + + /* Is the queue full? */ + if (spsc_queue_is_full(q)) { + return false; + } + + head =3D spsc_atomic_load(q, &q->shm->head); + next_head =3D head + 1; + if (next_head >=3D q->capacity) { + next_head =3D 0; + } + + memcpy(q->shm->packets[head], buf, size); + + spsc_atomic_store(q, &q->shm->head, next_head); + return true; +} + +static inline bool spsc_recv(spsc_queue *q, void *buf, size_t size) +{ + uint32_t tail; + + BUG_ON(size > sizeof q->shm->packets[0]); + BUG_ON(size =3D=3D 0); + + tail =3D spsc_atomic_load(q, &q->shm->tail); + + /* Is the queue empty? */ + if (tail =3D=3D q->cached_head) { + q->cached_head =3D spsc_atomic_load(q, &q->shm->head); + if (tail =3D=3D q->cached_head) { + return false; + } + } + + memcpy(buf, q->shm->packets[tail], size); + + /* Update the read pointer. */ + tail++; + if (tail >=3D q->capacity) { + tail =3D 0; + } + + spsc_atomic_store(q, &q->shm->tail, tail); + return true; +} +#endif /* SPSC_QUEUE_H__ */ --=20 2.43.0 From nobody Tue Apr 7 23:08:15 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1771948704; cv=none; d=zohomail.com; s=zohoarc; b=mwW6JqibGkm9sOf93mWz5YHG01F+GPUX8p9ngjR7DVQwcyn29743xvjFYI6U0rLUR+kE4TcYfyRA0rlieuW79IuWnqzFc8XLZM22uxlKc3BjtiUwkzqCd6Pu4sm5CB20gMlyRRevZ/WxZa0ZTLnrPtYcNS1dhUbZRC+RYf/NxEk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1771948704; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=XGDY0x1qV7scWCj/d8xZ05T1lEHN+YAZifpPNfg7lYc=; b=TLRQrTglMdbRgPCExZ4PRjup/3y5te7eyUZrPYvOseNLIjOeYsCWy2wuuvz9uKjA4Z/jifFMb78s6BoLU5n9Dl3J9+HtJ0V8Hz04nQyOovsLRPm1pDlmjLpo+/owmaCliab4NZO3Khrn2AsAZ64P44ol0kBSAlyY9nZ9kz3kmHI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1771948704832970.5867549205523; Tue, 24 Feb 2026 07:58:24 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vuunB-0006Vv-FI; Tue, 24 Feb 2026 10:57:46 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vuun6-0006VE-1d for qemu-devel@nongnu.org; Tue, 24 Feb 2026 10:57:40 -0500 Received: from mail-vk1-xa35.google.com ([2607:f8b0:4864:20::a35]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vuun3-0000pw-Uw for qemu-devel@nongnu.org; Tue, 24 Feb 2026 10:57:39 -0500 Received: by mail-vk1-xa35.google.com with SMTP id 71dfb90a1353d-56750cb34c8so4713482e0c.0 for ; Tue, 24 Feb 2026 07:57:37 -0800 (PST) Received: from gmail.com (ip190-5-140-142.intercom.com.sv. [190.5.140.142]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-568e595979dsm13204878e0c.20.2026.02.24.07.57.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Feb 2026 07:57:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771948656; x=1772553456; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=XGDY0x1qV7scWCj/d8xZ05T1lEHN+YAZifpPNfg7lYc=; b=eYG8fqpN32mM7zPbUACydWcB7D+/962iqp7UpStLN1BmjxrGIGHRlzbFXndJKz7nxB weILaJtKi+8I2UbU+VZiSREsgHdA3FdgwWm32Yj0b7fU6Dxu0JY020ZwZLrv+UEIO4eb /Dioq+DCbVmergS56T0UnRccArQhTxw6s8gdpJ5d4xNgjXdsECiLHaxMr49Kmtw4iJFb 4PWeMrWxmJM9j6tVTq6hwfL/xaKuOsPfGpjBV97GmYQBzPS7I2ZYAjk1PM5JFccPQ4au +dLqNGuJ1ThQaDyXsZ8eq6o6478NVjjXHnq8odUsTrZNuGHG8CupIj+Mw0peO0FAxiH0 pSRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771948656; x=1772553456; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=XGDY0x1qV7scWCj/d8xZ05T1lEHN+YAZifpPNfg7lYc=; b=AJpYb4Orm7lUqGMoj00ixzZ98R2eImhMqyjZuMHJb/wLtCJdI6mKnp9eL86RLh8DGE tnke7JXE621IzJCQWOzOYZaiJQVETo5o21eGHH5weHOYb0cvfbHy1VgIJGTkUIbD7g/I kipArNU2fXeBeectaJBX+UhjnDtKdXrchksE7axjzZn49CWdB11nGlxVkB14VIyobSBu Zp3TOcwyIuq142AFbHwfkET7nndpMffFSF9MocWnAU/WS6RwT/m91C9VPaPVGEsxnpkr 5MDRVeJ2/at70hxwd9t9tfQozDoRdzwrhMmOEStSWEXP+w6+WwmW1eJ18hdou4OM7DVG bySQ== X-Gm-Message-State: AOJu0YxUQFFFoSV3luUN+sfmlE50Nv5SOOq6JLSCeD5CbB9BUDOC0o1l mWqbHhXYJgvhxP3mUvyPH3cV/9ZhguC8+1hSCei/Q3OMJobv8vJ8LscSht32nCQ/ X-Gm-Gg: ATEYQzy7Q+zkvqGdCG33qa02V0mwB97cSTZDyIHNeezhjiaqezIbwDgBO7ZLcevAPII d/HZYY/ttkZByBlMyNgmqAWJE9BYkbIHdwA0fQN84zzfttSL4zneI4+ciyGQQXZEilZCZH0L9a6 29lLHtgkGBwOFE75nFsMuYaks73TcajO7Ohp4LShrvlwkGReJXvk87p/ccXxeWrPFXi08xiNmJG z/mB/GL2w9/NEM9emV40JtBHHvAAVBa9vUNqQ7fW0VrnslvCT6m5zuqOpzPcAsibFl+azVQcATV MAHIIMjONFU0Dy/ofM44JLZQaH5csdfDbnl0g3xDtUHW2pmMTdP5+S6BdqCQ24GjqJ8IZSqMuGy sAlX3YrRLsc3r7AhCLiUUUSVixn+Ph0GDn4gWprL7VNTWn+cZanPE0GVQBStQ4Jpk7qbUUCHuYh rZuyzyQbgC5pdk6IfD3pu8UT46oLEOnqIqg2SLyJab5ppNtkVpucmAI90G5+yf0IVV7Flc X-Received: by 2002:a05:6122:3a15:b0:567:4576:534e with SMTP id 71dfb90a1353d-568e470ad22mr6287463e0c.1.1771948655905; Tue, 24 Feb 2026 07:57:35 -0800 (PST) From: "Edgar E. Iglesias" To: qemu-devel@nongnu.org, Pierrick Bouvier Cc: mst@redhat.com, alex.bennee@linaro.org, bill.mills@linaro.org, edgar.iglesias@amd.com Subject: [PATCH v2 4/4] docs: Describe virtio-msg-amp-pci Date: Tue, 24 Feb 2026 16:57:21 +0100 Message-ID: <20260224155721.612314-5-edgar.iglesias@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260224155721.612314-1-edgar.iglesias@gmail.com> References: <20260224155721.612314-1-edgar.iglesias@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.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; Received-SPF: pass client-ip=2607:f8b0:4864:20::a35; envelope-from=edgar.iglesias@gmail.com; helo=mail-vk1-xa35.google.com X-Spam_score_int: 12 X-Spam_score: 1.2 X-Spam_bar: + X-Spam_report: (1.2 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_SBL_CSS=3.335, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1771948705719158500 Content-Type: text/plain; charset="utf-8" From: "Edgar E. Iglesias" Signed-off-by: Edgar E. Iglesias --- docs/system/devices/virtio/index.rst | 1 + .../devices/virtio/virtio-msg-amp-pci.rst | 92 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 docs/system/devices/virtio/virtio-msg-amp-pci.rst diff --git a/docs/system/devices/virtio/index.rst b/docs/system/devices/vir= tio/index.rst index c292101ade..f2d8c24353 100644 --- a/docs/system/devices/virtio/index.rst +++ b/docs/system/devices/virtio/index.rst @@ -21,6 +21,7 @@ can also be off-loaded to an external process via :ref:`v= host user :maxdepth: 1 =20 virtio-gpu.rst + virtio-msg-amp-pci.rst virtio-pmem.rst virtio-snd.rst vhost-user.rst diff --git a/docs/system/devices/virtio/virtio-msg-amp-pci.rst b/docs/syste= m/devices/virtio/virtio-msg-amp-pci.rst new file mode 100644 index 0000000000..3290a75eec --- /dev/null +++ b/docs/system/devices/virtio/virtio-msg-amp-pci.rst @@ -0,0 +1,92 @@ +Virtio-msg AMP PCI +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +This document explains the setup and usage of the virtio-msg-amp-pci devic= e. +The virtio-msg-amp-pci is an emulated PCI device that provides a small +set of features to enable virtio-msg over shared-memory queues. + +Overview +-------- + +Virtio-msg is a message-based virtio transport: instead of MMIO/PIO access= es +to discover queues and kick the device, the guest driver exchanges small +messages with QEMU. The virtio-msg-amp-pci device provides a PCI wrapper f= or +an AMP-style shared-memory transport. Each FIFO is a point-to-point message +channel backed by RAM and a doorbell register for notifications. + +QEMU exposes a virtio-msg bus per FIFO. Devices are attached under the +``virtio-msg-amp-pci`` object and exchange virtio-msg protocol messages ov= er +the FIFO. MSI-X interrupts are used to notify the guest when QEMU enqueues +messages for the driver. + +Use case +-------- + +Virtio-msg is a virtio transport where driver and device communicate over +messages rather than using memory accesses that get trapped and emulated. +Virtio-msg depends on a lower level virtio-msg-bus responsible for deliver= ing +these messages. In this case, we're using the virtio-msg AMP bus which mov= es +messages back and forth using a FIFO on top of shared-memory and interrupt= s. + +The virtio-msg-amp-pci device exposes a BAR with RAM and doorbell registers +so guests can implement the shared-memory FIFO protocol and QEMU implements +the backend side of it. + +Virtio-msg-amp-pci PCI device +----------------------------- + +The virtio-msg-amp-pci device has the following layout: + +- BAR 0: Registers (Version, features and notification/doorbell registers) +- BAR 1: RAM for FIFOs +- BAR 2: MSI-X table and PBA (created automatically) + +Each FIFO gets an MSI-X interrupt reserved for it and a dedicated doorbell +register:: + + REG32(VERSION, 0x00) + REG32(FEATURES, 0x04) + REG32(NOTIFY0, 0x20) + REG32(NOTIFY1, 0x24) + REG32(NOTIFY2, 0x28) + And so on. + +Each FIFO uses a 16 KiB window in BAR 1 with the following layout: + +- 0 - 4 KiB: Reserved +- 4 - 8 KiB: Driver queue +- 8 - 12 KiB: Device queue +- 12 - 16 KiB: Reserved + +The guest driver writes to the doorbell register to notify QEMU to process +the driver queue. QEMU posts responses on the device queue and raises the +FIFO's MSI-X vector to notify the guest. + +How Does virtio-msg-amp-pci Compare to virtio-pci Emulation? +------------------------------------------------------------ + +Both virtio-msg-amp-pci and virtio-pci emulate PCI devices and allow users +to plug virtio devices behind them. The main difference is in how the +guest uses virtio-msg vs virtio-pci to discover and configure the virtio d= ev. + +Virtio-msg-amp-pci Usage +------------------------ + +A virtio-msg-amp-pci can be created by adding the following to the QEMU +command-line:: + + -device virtio-msg-amp-pci + +Virtio devices can then be attached to the virtio-msg bus with for example +the following:: + + -device virtio-rng-device,bus=3D/gpex-pcihost/pcie.0/virtio-msg-amp-pc= i/fifo0/virtio-msg/bus0/virtio-msg-dev + +Multiple virtio devices can be connected by using bus1, bus2 and so on. + +Device properties +----------------- + +The virtio-msg-amp-pci device can be configured with the following propert= ies: + + * ``num-fifos`` number of FIFOs (default 1, max 8). --=20 2.43.0