* [PATCH v3 1/6] pci: Add device ID for CnuasNIC
2026-09-29 20:23 [PATCH v3 0/6] hw: Add Cnuas virtual devices Weqaar Janjua
@ 2026-09-29 20:23 ` Weqaar Janjua
2026-09-30 7:00 ` Gerd Hoffmann
2026-09-29 20:23 ` [PATCH v3 2/6] hw/net: Add Cnuas virtual network adapter Weqaar Janjua
` (5 subsequent siblings)
6 siblings, 1 reply; 15+ messages in thread
From: Weqaar Janjua @ 2026-09-29 20:23 UTC (permalink / raw)
To: qemu-devel
Cc: kraxel, peter.maydell, mst, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier
Reserve device ID 0017 for the Cnuas virtual network adapter.
Signed-off-by: Weqaar Janjua <weqaar.janjua@packetfive.com>
---
docs/specs/pci-ids.rst | 2 ++
include/hw/pci/pci.h | 1 +
2 files changed, 3 insertions(+)
diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst
index 261b0f359f..271f61bb81 100644
--- a/docs/specs/pci-ids.rst
+++ b/docs/specs/pci-ids.rst
@@ -100,6 +100,8 @@ PCI devices (other than virtio):
PCI UFS device (``-device ufs``)
1b36:0014
PCI RISC-V IOMMU device
+1b36:0017
+ Cnuas virtual RDMA network adapter
All these devices are documented in :doc:`index`.
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index f2448e941a..99a31856d0 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -121,6 +121,7 @@ extern bool pci_available;
#define PCI_DEVICE_ID_REDHAT_ACPI_ERST 0x0012
#define PCI_DEVICE_ID_REDHAT_UFS 0x0013
#define PCI_DEVICE_ID_REDHAT_RISCV_IOMMU 0x0014
+#define PCI_DEVICE_ID_REDHAT_CNUASNIC 0x0017
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
#define FMT_PCIBUS PRIx64
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v3 1/6] pci: Add device ID for CnuasNIC
2026-09-29 20:23 ` [PATCH v3 1/6] pci: Add device ID for CnuasNIC Weqaar Janjua
@ 2026-09-30 7:00 ` Gerd Hoffmann
0 siblings, 0 replies; 15+ messages in thread
From: Gerd Hoffmann @ 2026-09-30 7:00 UTC (permalink / raw)
To: Weqaar Janjua
Cc: qemu-devel, peter.maydell, mst, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier
On Tue, Sep 29, 2026 at 08:23:52PM +0000, Weqaar Janjua wrote:
> Reserve device ID 0017 for the Cnuas virtual network adapter.
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 2/6] hw/net: Add Cnuas virtual network adapter
2026-09-29 20:23 [PATCH v3 0/6] hw: Add Cnuas virtual devices Weqaar Janjua
2026-09-29 20:23 ` [PATCH v3 1/6] pci: Add device ID for CnuasNIC Weqaar Janjua
@ 2026-09-29 20:23 ` Weqaar Janjua
2026-09-29 20:23 ` [PATCH v3 3/6] tests/qtest: Add Cnuas virtual network adapter tests Weqaar Janjua
` (4 subsequent siblings)
6 siblings, 0 replies; 15+ messages in thread
From: Weqaar Janjua @ 2026-09-29 20:23 UTC (permalink / raw)
To: qemu-devel
Cc: kraxel, peter.maydell, mst, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier
Add guest DMA and packet transport while retaining the existing MIT license.
Signed-off-by: Weqaar Janjua <weqaar.janjua@packetfive.com>
---
MAINTAINERS | 6 +
docs/specs/cnuas-vnic.rst | 78 ++++++
docs/specs/index.rst | 1 +
docs/specs/pci-ids.rst | 2 +-
hw/net/Kconfig | 5 +
hw/net/cnuas_vnic.c | 508 ++++++++++++++++++++++++++++++++++++++
hw/net/meson.build | 1 +
7 files changed, 600 insertions(+), 1 deletion(-)
create mode 100644 docs/specs/cnuas-vnic.rst
create mode 100644 hw/net/cnuas_vnic.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 1b66c65f68..efcb15c52d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2773,6 +2773,12 @@ F: net/eth.c
F: hw/net/net_rx_pkt*
F: hw/net/net_tx_pkt*
+Cnuas virtual devices
+M: Weqaar Janjua <weqaar.janjua@packetfive.com>
+S: Maintained
+F: docs/specs/cnuas-vnic.rst
+F: hw/net/cnuas_vnic.c
+
VMware
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
S: Maintained
diff --git a/docs/specs/cnuas-vnic.rst b/docs/specs/cnuas-vnic.rst
new file mode 100644
index 0000000000..c20f05feea
--- /dev/null
+++ b/docs/specs/cnuas-vnic.rst
@@ -0,0 +1,78 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+=============================
+Cnuas virtual network adapter
+=============================
+
+``cnuas-vnic`` is a PCIe network device for Cnuas RDMA and Ethernet emulation.
+Its wire side is an ``AF_UNIX`` ``SOCK_SEQPACKET`` socket,
+allowing multiple guests to connect to a separate virtual switch process.
+The socket transport currently requires a Linux host.
+
+The device intentionally uses a single transmit operation and one receive
+slot instead of descriptor rings. This keeps the programming model small
+while retaining guest DMA, interrupts, link state, and frame transport.
+
+PCI interface
+-------------
+
+===================== ==============================
+Vendor ID 1b36
+Device ID 0017
+Revision 1
+Class 0280, other network controller
+===================== ==============================
+
+BAR 0 is a 4 KiB MMIO region. The device supports one MSI vector and INTx.
+
+Properties
+----------
+
+``socket_path``
+ Path of the switch's ``SOCK_SEQPACKET`` socket. Without this property, or
+ when the connection fails, the device still enumerates with its link down.
+
+``mac``
+ MAC address exposed through ``MAC_LO`` and ``MAC_HI``. If omitted, QEMU
+ derives a locally administered address from the PCI function number.
+
+``x-speed``, ``x-width``
+ PCIe link speed and width reported through PCIe configuration space.
+
+Register map
+------------
+
+All registers require 32-bit little-endian accesses.
+
+====== =========== ===============================================
+Offset Name Meaning
+====== =========== ===============================================
+0x00 TX_ADDR_LO Guest physical transmit address, low 32 bits
+0x04 TX_ADDR_HI Guest physical transmit address, high 32 bits
+0x08 TX_LEN Frame length from 1 through 9216 bytes
+0x0c TX_DOORBELL Write to transmit
+0x10 RX_ADDR_LO Guest physical receive address, low 32 bits
+0x14 RX_ADDR_HI Guest physical receive address, high 32 bits
+0x18 RX_LEN Length of the received frame, read only
+0x1c RX_STATUS Bit 0 marks a frame ready; write zero to release
+0x20 IRQ_STATUS Interrupt status; write one bits to clear
+0x24 IRQ_MASK Interrupt enable mask
+0x28 LINK_STATUS Bit 0 marks the socket link up, read only
+0x2c MAC_LO MAC bytes 0 through 3, read only
+0x30 MAC_HI MAC bytes 4 and 5, read only
+====== =========== ===============================================
+
+Interrupt bits are bit 0 for receive completion, bit 1 for transmit
+completion, and bit 2 for a link-state change.
+
+Transport
+---------
+
+Writing ``TX_DOORBELL`` reads ``TX_LEN`` bytes from guest memory and sends
+one socket message. Receive messages are copied to the address in
+``RX_ADDR_LO`` and ``RX_ADDR_HI``. The device stops reading its socket while
+``RX_STATUS`` is set, preventing a later frame from overwriting the active
+receive slot.
+
+The socket transports complete Ethernet frames without an additional QEMU
+header. ``SOCK_SEQPACKET`` preserves frame boundaries.
diff --git a/docs/specs/index.rst b/docs/specs/index.rst
index 44e108db02..0fd5da2aa8 100644
--- a/docs/specs/index.rst
+++ b/docs/specs/index.rst
@@ -26,6 +26,7 @@ guest hardware that is specific to QEMU.
fw_cfg
fsi
vmw_pvscsi-spec
+ cnuas-vnic
edu
ivshmem-spec
pvpanic
diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst
index 271f61bb81..dd7d62d8ac 100644
--- a/docs/specs/pci-ids.rst
+++ b/docs/specs/pci-ids.rst
@@ -101,7 +101,7 @@ PCI devices (other than virtio):
1b36:0014
PCI RISC-V IOMMU device
1b36:0017
- Cnuas virtual RDMA network adapter
+ Cnuas virtual RDMA network adapter (:doc:`cnuas-vnic`)
All these devices are documented in :doc:`index`.
diff --git a/hw/net/Kconfig b/hw/net/Kconfig
index b56a173eed..0721efc8b1 100644
--- a/hw/net/Kconfig
+++ b/hw/net/Kconfig
@@ -49,6 +49,11 @@ config IGB_PCI_EXPRESS
default y if PCI_DEVICES || PCIE_DEVICES
depends on PCI_EXPRESS && MSI_NONBROKEN
+config CNUAS_VNIC
+ bool
+ default y if PCI_DEVICES || PCIE_DEVICES
+ depends on LINUX && PCI_EXPRESS && MSI_NONBROKEN
+
config RTL8139_PCI
bool
default y if PCI_DEVICES
diff --git a/hw/net/cnuas_vnic.c b/hw/net/cnuas_vnic.c
new file mode 100644
index 0000000000..506d917e39
--- /dev/null
+++ b/hw/net/cnuas_vnic.c
@@ -0,0 +1,508 @@
+/*
+ * Cnuas virtual RDMA network adapter
+ *
+ * Copyright (c) 2026 PacketFive
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/main-loop.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev-properties-system.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pcie.h"
+#include "migration/vmstate.h"
+#include "net/net.h"
+#include "qapi/error.h"
+#include "qom/object.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define TYPE_CNUAS_VNIC "cnuas-vnic"
+OBJECT_DECLARE_SIMPLE_TYPE(CnuasVnicState, CNUAS_VNIC)
+
+#define CNUAS_VNIC_MAX_FRAME 9216
+#define CNUAS_VNIC_MMIO_SIZE 4096
+
+#define CNUAS_VNIC_REG_TX_ADDR_LO 0x00
+#define CNUAS_VNIC_REG_TX_ADDR_HI 0x04
+#define CNUAS_VNIC_REG_TX_LEN 0x08
+#define CNUAS_VNIC_REG_TX_DOORBELL 0x0c
+#define CNUAS_VNIC_REG_RX_ADDR_LO 0x10
+#define CNUAS_VNIC_REG_RX_ADDR_HI 0x14
+#define CNUAS_VNIC_REG_RX_LEN 0x18
+#define CNUAS_VNIC_REG_RX_STATUS 0x1c
+#define CNUAS_VNIC_REG_IRQ_STATUS 0x20
+#define CNUAS_VNIC_REG_IRQ_MASK 0x24
+#define CNUAS_VNIC_REG_LINK_STATUS 0x28
+#define CNUAS_VNIC_REG_MAC_LO 0x2c
+#define CNUAS_VNIC_REG_MAC_HI 0x30
+
+#define CNUAS_VNIC_IRQ_RX_COMPLETE BIT(0)
+#define CNUAS_VNIC_IRQ_TX_COMPLETE BIT(1)
+#define CNUAS_VNIC_IRQ_LINK_CHANGE BIT(2)
+
+#define CNUAS_VNIC_LINK_UP BIT(0)
+#define CNUAS_VNIC_RX_READY BIT(0)
+
+struct CnuasVnicState {
+ PCIDevice parent_obj;
+ MemoryRegion mmio;
+
+ char *socket_path;
+ int socket_fd;
+
+ MACAddr conf_mac;
+ uint8_t mac[6];
+
+ PCIExpLinkSpeed pcie_speed;
+ PCIExpLinkWidth pcie_width;
+
+ uint32_t tx_addr_lo;
+ uint32_t tx_addr_hi;
+ uint32_t tx_len;
+ uint32_t rx_addr_lo;
+ uint32_t rx_addr_hi;
+ uint32_t rx_len;
+ uint32_t rx_status;
+ uint32_t irq_status;
+ uint32_t irq_mask;
+ uint32_t link_status;
+};
+
+static void cnuas_vnic_update_irq(CnuasVnicState *s)
+{
+ bool pending = s->irq_status & s->irq_mask;
+
+ if (msi_enabled(&s->parent_obj)) {
+ if (pending) {
+ msi_notify(&s->parent_obj, 0);
+ }
+ } else {
+ pci_set_irq(&s->parent_obj, pending);
+ }
+}
+
+static void cnuas_vnic_set_link(CnuasVnicState *s, bool up)
+{
+ uint32_t new_status = up ? CNUAS_VNIC_LINK_UP : 0;
+
+ if (s->link_status == new_status) {
+ return;
+ }
+
+ s->link_status = new_status;
+ s->irq_status |= CNUAS_VNIC_IRQ_LINK_CHANGE;
+ cnuas_vnic_update_irq(s);
+}
+
+static void cnuas_vnic_disconnect(CnuasVnicState *s)
+{
+ if (s->socket_fd >= 0) {
+ qemu_set_fd_handler(s->socket_fd, NULL, NULL, NULL);
+ close(s->socket_fd);
+ s->socket_fd = -1;
+ }
+ cnuas_vnic_set_link(s, false);
+}
+
+static void cnuas_vnic_transmit(CnuasVnicState *s)
+{
+ uint8_t frame[CNUAS_VNIC_MAX_FRAME];
+ uint64_t address;
+ ssize_t sent;
+
+ if (!s->tx_len || s->tx_len > sizeof(frame)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: invalid TX length %u\n", s->tx_len);
+ return;
+ }
+
+ address = ((uint64_t)s->tx_addr_hi << 32) | s->tx_addr_lo;
+ if (pci_dma_read(&s->parent_obj, address, frame, s->tx_len) != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: DMA read failed at 0x%" PRIx64 "\n",
+ address);
+ return;
+ }
+
+ if (s->socket_fd >= 0 && s->link_status == CNUAS_VNIC_LINK_UP) {
+ sent = send(s->socket_fd, frame, s->tx_len, MSG_NOSIGNAL);
+ if (sent < 0) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: TX failed: %s\n", strerror(errno));
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ cnuas_vnic_disconnect(s);
+ }
+ } else if (sent != s->tx_len) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: short TX (%zd of %u bytes)\n",
+ sent, s->tx_len);
+ }
+ }
+
+ s->irq_status |= CNUAS_VNIC_IRQ_TX_COMPLETE;
+ cnuas_vnic_update_irq(s);
+}
+
+static bool cnuas_vnic_receive(CnuasVnicState *s)
+{
+ uint8_t frame[CNUAS_VNIC_MAX_FRAME];
+ uint64_t address;
+ ssize_t length;
+
+ if (s->socket_fd < 0 || s->rx_status == CNUAS_VNIC_RX_READY) {
+ return false;
+ }
+
+ length = recv(s->socket_fd, frame, sizeof(frame), MSG_DONTWAIT);
+ if (length == 0) {
+ cnuas_vnic_disconnect(s);
+ return false;
+ }
+ if (length < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: RX failed: %s\n", strerror(errno));
+ cnuas_vnic_disconnect(s);
+ }
+ return false;
+ }
+
+ address = ((uint64_t)s->rx_addr_hi << 32) | s->rx_addr_lo;
+ if (!address) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: dropped RX frame without a guest buffer\n");
+ return true;
+ }
+
+ if (pci_dma_write(&s->parent_obj, address, frame, length) != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: DMA write failed at 0x%" PRIx64 "\n",
+ address);
+ return true;
+ }
+
+ s->rx_len = length;
+ s->rx_status = CNUAS_VNIC_RX_READY;
+ s->irq_status |= CNUAS_VNIC_IRQ_RX_COMPLETE;
+ cnuas_vnic_update_irq(s);
+
+ /*
+ * The model has one receive slot. Stop monitoring the level-triggered
+ * socket until the guest releases that slot to avoid a busy loop.
+ */
+ qemu_set_fd_handler(s->socket_fd, NULL, NULL, NULL);
+ return true;
+}
+
+static void cnuas_vnic_receive_ready(void *opaque)
+{
+ cnuas_vnic_receive(opaque);
+}
+
+static uint64_t cnuas_vnic_mmio_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ CnuasVnicState *s = opaque;
+
+ switch (addr) {
+ case CNUAS_VNIC_REG_TX_ADDR_LO:
+ return s->tx_addr_lo;
+ case CNUAS_VNIC_REG_TX_ADDR_HI:
+ return s->tx_addr_hi;
+ case CNUAS_VNIC_REG_TX_LEN:
+ return s->tx_len;
+ case CNUAS_VNIC_REG_RX_ADDR_LO:
+ return s->rx_addr_lo;
+ case CNUAS_VNIC_REG_RX_ADDR_HI:
+ return s->rx_addr_hi;
+ case CNUAS_VNIC_REG_RX_LEN:
+ return s->rx_len;
+ case CNUAS_VNIC_REG_RX_STATUS:
+ return s->rx_status;
+ case CNUAS_VNIC_REG_IRQ_STATUS:
+ return s->irq_status;
+ case CNUAS_VNIC_REG_IRQ_MASK:
+ return s->irq_mask;
+ case CNUAS_VNIC_REG_LINK_STATUS:
+ return s->link_status;
+ case CNUAS_VNIC_REG_MAC_LO:
+ return s->mac[0] | s->mac[1] << 8 | s->mac[2] << 16 |
+ s->mac[3] << 24;
+ case CNUAS_VNIC_REG_MAC_HI:
+ return s->mac[4] | s->mac[5] << 8;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: read from unknown register 0x%"
+ HWADDR_PRIx "\n", addr);
+ return 0;
+ }
+}
+
+static void cnuas_vnic_mmio_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ CnuasVnicState *s = opaque;
+
+ switch (addr) {
+ case CNUAS_VNIC_REG_TX_ADDR_LO:
+ s->tx_addr_lo = value;
+ break;
+ case CNUAS_VNIC_REG_TX_ADDR_HI:
+ s->tx_addr_hi = value;
+ break;
+ case CNUAS_VNIC_REG_TX_LEN:
+ s->tx_len = value;
+ break;
+ case CNUAS_VNIC_REG_TX_DOORBELL:
+ cnuas_vnic_transmit(s);
+ break;
+ case CNUAS_VNIC_REG_RX_ADDR_LO:
+ s->rx_addr_lo = value;
+ break;
+ case CNUAS_VNIC_REG_RX_ADDR_HI:
+ s->rx_addr_hi = value;
+ break;
+ case CNUAS_VNIC_REG_RX_STATUS:
+ if (!value && s->rx_status == CNUAS_VNIC_RX_READY) {
+ s->rx_status = 0;
+ s->rx_len = 0;
+ if (s->socket_fd >= 0) {
+ qemu_set_fd_handler(s->socket_fd,
+ cnuas_vnic_receive_ready, NULL, s);
+ cnuas_vnic_receive(s);
+ }
+ }
+ break;
+ case CNUAS_VNIC_REG_IRQ_STATUS:
+ s->irq_status &= ~value;
+ cnuas_vnic_update_irq(s);
+ break;
+ case CNUAS_VNIC_REG_IRQ_MASK:
+ s->irq_mask = value;
+ cnuas_vnic_update_irq(s);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: write to unknown register 0x%"
+ HWADDR_PRIx "\n", addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps cnuas_vnic_mmio_ops = {
+ .read = cnuas_vnic_mmio_read,
+ .write = cnuas_vnic_mmio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void cnuas_vnic_connect(CnuasVnicState *s)
+{
+ struct sockaddr_un address = { .sun_family = AF_UNIX };
+ int flags;
+ int fd;
+ int buffer_size = 8 * MiB;
+
+ if (!s->socket_path || !s->socket_path[0]) {
+ return;
+ }
+ if (strlen(s->socket_path) >= sizeof(address.sun_path)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: socket path is too long\n");
+ return;
+ }
+
+ fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ if (fd < 0) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: socket creation failed: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ g_strlcpy(address.sun_path, s->socket_path, sizeof(address.sun_path));
+ if (connect(fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
+ qemu_log_mask(LOG_UNIMP,
+ "cnuas-vnic: cannot connect to %s: %s\n",
+ s->socket_path, strerror(errno));
+ close(fd);
+ return;
+ }
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuas-vnic: cannot make socket nonblocking: %s\n",
+ strerror(errno));
+ close(fd);
+ return;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof(buffer_size));
+ setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof(buffer_size));
+
+ s->socket_fd = fd;
+ cnuas_vnic_set_link(s, true);
+ qemu_set_fd_handler(fd, cnuas_vnic_receive_ready, NULL, s);
+}
+
+static void cnuas_vnic_reset_hold(Object *obj, ResetType type)
+{
+ CnuasVnicState *s = CNUAS_VNIC(obj);
+
+ s->tx_addr_lo = 0;
+ s->tx_addr_hi = 0;
+ s->tx_len = 0;
+ s->rx_addr_lo = 0;
+ s->rx_addr_hi = 0;
+ s->rx_len = 0;
+ s->rx_status = 0;
+ s->irq_status = 0;
+ s->irq_mask = 0;
+ pci_set_irq(&s->parent_obj, 0);
+
+ if (s->socket_fd >= 0) {
+ qemu_set_fd_handler(s->socket_fd, cnuas_vnic_receive_ready, NULL, s);
+ }
+}
+
+static int cnuas_vnic_post_load(void *opaque, int version_id)
+{
+ CnuasVnicState *s = opaque;
+
+ if (s->socket_fd >= 0) {
+ qemu_set_fd_handler(s->socket_fd,
+ s->rx_status ? NULL : cnuas_vnic_receive_ready,
+ NULL, s);
+ }
+ cnuas_vnic_update_irq(s);
+ return 0;
+}
+
+static const VMStateDescription vmstate_cnuas_vnic = {
+ .name = TYPE_CNUAS_VNIC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = cnuas_vnic_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, CnuasVnicState),
+ VMSTATE_UINT32(tx_addr_lo, CnuasVnicState),
+ VMSTATE_UINT32(tx_addr_hi, CnuasVnicState),
+ VMSTATE_UINT32(tx_len, CnuasVnicState),
+ VMSTATE_UINT32(rx_addr_lo, CnuasVnicState),
+ VMSTATE_UINT32(rx_addr_hi, CnuasVnicState),
+ VMSTATE_UINT32(rx_len, CnuasVnicState),
+ VMSTATE_UINT32(rx_status, CnuasVnicState),
+ VMSTATE_UINT32(irq_status, CnuasVnicState),
+ VMSTATE_UINT32(irq_mask, CnuasVnicState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void cnuas_vnic_realize(PCIDevice *pdev, Error **errp)
+{
+ CnuasVnicState *s = CNUAS_VNIC(pdev);
+ static const uint8_t zero_mac[6];
+
+ pci_config_set_interrupt_pin(pdev->config, 1);
+ if (msi_init(pdev, 0, 1, true, false, errp)) {
+ return;
+ }
+
+ if (pcie_endpoint_cap_init(pdev, 0x80) < 0) {
+ error_setg(errp, "cnuas-vnic: cannot initialize PCIe capability");
+ msi_uninit(pdev);
+ return;
+ }
+ pcie_cap_fill_link_ep_usp(pdev, s->pcie_width, s->pcie_speed, false);
+
+ memory_region_init_io(&s->mmio, OBJECT(s), &cnuas_vnic_mmio_ops, s,
+ "cnuas-vnic-mmio", CNUAS_VNIC_MMIO_SIZE);
+ pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
+
+ if (!memcmp(s->conf_mac.a, zero_mac, sizeof(zero_mac))) {
+ s->mac[0] = 0x02;
+ s->mac[1] = 0x48;
+ s->mac[2] = 0x43;
+ s->mac[3] = 0x41;
+ s->mac[4] = 0x49;
+ s->mac[5] = pdev->devfn;
+ } else {
+ memcpy(s->mac, s->conf_mac.a, sizeof(s->mac));
+ }
+
+ s->socket_fd = -1;
+ cnuas_vnic_connect(s);
+}
+
+static void cnuas_vnic_exit(PCIDevice *pdev)
+{
+ CnuasVnicState *s = CNUAS_VNIC(pdev);
+
+ cnuas_vnic_disconnect(s);
+ pcie_cap_exit(pdev);
+ msi_uninit(pdev);
+}
+
+static const Property cnuas_vnic_properties[] = {
+ DEFINE_PROP_STRING("socket_path", CnuasVnicState, socket_path),
+ DEFINE_PROP_MACADDR("mac", CnuasVnicState, conf_mac),
+ DEFINE_PROP_PCIE_LINK_SPEED("x-speed", CnuasVnicState, pcie_speed,
+ PCIE_LINK_SPEED_32),
+ DEFINE_PROP_PCIE_LINK_WIDTH("x-width", CnuasVnicState, pcie_width,
+ PCIE_LINK_WIDTH_16),
+};
+
+static void cnuas_vnic_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ pc->realize = cnuas_vnic_realize;
+ pc->exit = cnuas_vnic_exit;
+ pc->vendor_id = PCI_VENDOR_ID_REDHAT;
+ pc->device_id = PCI_DEVICE_ID_REDHAT_CNUASNIC;
+ pc->class_id = PCI_CLASS_NETWORK_OTHER;
+ pc->revision = 1;
+
+ rc->phases.hold = cnuas_vnic_reset_hold;
+ dc->desc = "Cnuas virtual RDMA network adapter";
+ dc->vmsd = &vmstate_cnuas_vnic;
+ device_class_set_props(dc, cnuas_vnic_properties);
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+}
+
+static const TypeInfo cnuas_vnic_info = {
+ .name = TYPE_CNUAS_VNIC,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(CnuasVnicState),
+ .class_init = cnuas_vnic_class_init,
+ .interfaces = (const InterfaceInfo[]) {
+ { INTERFACE_PCIE_DEVICE },
+ { },
+ },
+};
+
+static void cnuas_vnic_register_types(void)
+{
+ type_register_static(&cnuas_vnic_info);
+}
+
+type_init(cnuas_vnic_register_types)
diff --git a/hw/net/meson.build b/hw/net/meson.build
index 63b0e96245..1b942bd415 100644
--- a/hw/net/meson.build
+++ b/hw/net/meson.build
@@ -12,6 +12,7 @@ system_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('net_tx_pkt.c',
system_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('e1000e.c', 'e1000e_core.c', 'e1000x_common.c'))
system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c'))
system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('igb.c', 'igbvf.c', 'igb_core.c'))
+system_ss.add(when: 'CONFIG_CNUAS_VNIC', if_true: files('cnuas_vnic.c'))
system_ss.add(when: 'CONFIG_RTL8139_PCI', if_true: files('rtl8139.c'))
system_ss.add(when: 'CONFIG_TULIP', if_true: files('tulip.c'))
system_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c'))
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v3 3/6] tests/qtest: Add Cnuas virtual network adapter tests
2026-09-29 20:23 [PATCH v3 0/6] hw: Add Cnuas virtual devices Weqaar Janjua
2026-09-29 20:23 ` [PATCH v3 1/6] pci: Add device ID for CnuasNIC Weqaar Janjua
2026-09-29 20:23 ` [PATCH v3 2/6] hw/net: Add Cnuas virtual network adapter Weqaar Janjua
@ 2026-09-29 20:23 ` Weqaar Janjua
2026-09-29 20:23 ` [PATCH v3 4/6] pci: Add device ID for CnuasGPU Weqaar Janjua
` (3 subsequent siblings)
6 siblings, 0 replies; 15+ messages in thread
From: Weqaar Janjua @ 2026-09-29 20:23 UTC (permalink / raw)
To: qemu-devel
Cc: kraxel, peter.maydell, mst, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier
Cover PCI identity, register reset, DMA, and packet transport.
Signed-off-by: Weqaar Janjua <weqaar.janjua@packetfive.com>
---
MAINTAINERS | 1 +
tests/qtest/cnuas-vnic-test.c | 233 ++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 1 +
3 files changed, 235 insertions(+)
create mode 100644 tests/qtest/cnuas-vnic-test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index efcb15c52d..7be5622f23 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2778,6 +2778,7 @@ M: Weqaar Janjua <weqaar.janjua@packetfive.com>
S: Maintained
F: docs/specs/cnuas-vnic.rst
F: hw/net/cnuas_vnic.c
+F: tests/qtest/cnuas-vnic-test.c
VMware
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
diff --git a/tests/qtest/cnuas-vnic-test.c b/tests/qtest/cnuas-vnic-test.c
new file mode 100644
index 0000000000..fc27b7fa8e
--- /dev/null
+++ b/tests/qtest/cnuas-vnic-test.c
@@ -0,0 +1,233 @@
+/*
+ * QTest for the Cnuas virtual network adapter
+ *
+ * Copyright (c) 2026 PacketFive
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_regs.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "libqtest.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define CNUAS_VNIC_DEVFN QPCI_DEVFN(4, 0)
+#define CNUAS_VNIC_BAR_SIZE 4096
+#define CNUAS_VNIC_REG_TX_ADDR_LO 0x00
+#define CNUAS_VNIC_REG_TX_ADDR_HI 0x04
+#define CNUAS_VNIC_REG_TX_LEN 0x08
+#define CNUAS_VNIC_REG_TX_DOORBELL 0x0c
+#define CNUAS_VNIC_REG_RX_ADDR_LO 0x10
+#define CNUAS_VNIC_REG_RX_ADDR_HI 0x14
+#define CNUAS_VNIC_REG_RX_LEN 0x18
+#define CNUAS_VNIC_REG_RX_STATUS 0x1c
+#define CNUAS_VNIC_REG_IRQ_STATUS 0x20
+#define CNUAS_VNIC_REG_IRQ_MASK 0x24
+#define CNUAS_VNIC_REG_LINK_STATUS 0x28
+#define CNUAS_VNIC_REG_MAC_LO 0x2c
+#define CNUAS_VNIC_REG_MAC_HI 0x30
+
+typedef struct CnuasVnicTest {
+ QTestState *qts;
+ QPCIBus *bus;
+ QPCIDevice *dev;
+ QPCIBar bar;
+} CnuasVnicTest;
+
+static void cnuas_vnic_test_start_with_args(CnuasVnicTest *test,
+ const char *args)
+{
+ uint64_t bar_size;
+
+ test->qts = qtest_initf("-machine q35 "
+ "-device cnuas-vnic,addr=04.0,"
+ "mac=02:11:22:33:44:55%s", args);
+ test->bus = qpci_new_pc(test->qts, NULL);
+ test->dev = qpci_device_find(test->bus, CNUAS_VNIC_DEVFN);
+ g_assert_nonnull(test->dev);
+ qpci_device_enable(test->dev);
+ test->bar = qpci_iomap(test->dev, 0, &bar_size);
+ g_assert_cmpuint(bar_size, ==, CNUAS_VNIC_BAR_SIZE);
+}
+
+static void cnuas_vnic_test_start(CnuasVnicTest *test)
+{
+ cnuas_vnic_test_start_with_args(test, "");
+}
+
+static void cnuas_vnic_test_stop(CnuasVnicTest *test)
+{
+ g_free(test->dev);
+ qpci_free_pc(test->bus);
+ qtest_quit(test->qts);
+}
+
+static void cnuas_vnic_test_pci_config(void)
+{
+ CnuasVnicTest test;
+
+ cnuas_vnic_test_start(&test);
+
+ g_assert_cmphex(qpci_config_readw(test.dev, PCI_VENDOR_ID), ==,
+ PCI_VENDOR_ID_REDHAT);
+ g_assert_cmphex(qpci_config_readw(test.dev, PCI_DEVICE_ID), ==,
+ PCI_DEVICE_ID_REDHAT_CNUASNIC);
+ g_assert_cmphex(qpci_config_readw(test.dev, PCI_CLASS_DEVICE), ==,
+ PCI_CLASS_NETWORK_OTHER);
+ g_assert_cmphex(qpci_config_readb(test.dev, PCI_REVISION_ID), ==, 1);
+ g_assert_cmphex(qpci_find_capability(test.dev, PCI_CAP_ID_MSI, 0), !=, 0);
+
+ cnuas_vnic_test_stop(&test);
+}
+
+static void cnuas_vnic_test_registers(void)
+{
+ CnuasVnicTest test;
+
+ cnuas_vnic_test_start(&test);
+
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_LINK_STATUS), ==, 0);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_IRQ_STATUS), ==, 0);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_MAC_LO), ==, 0x33221102);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_MAC_HI), ==, 0x5544);
+
+ qpci_io_writel(test.dev, test.bar, CNUAS_VNIC_REG_TX_ADDR_LO,
+ 0x89abcdef);
+ qpci_io_writel(test.dev, test.bar, CNUAS_VNIC_REG_TX_ADDR_HI,
+ 0x01234567);
+ qpci_io_writel(test.dev, test.bar, CNUAS_VNIC_REG_TX_LEN, 4096);
+ qpci_io_writel(test.dev, test.bar, CNUAS_VNIC_REG_IRQ_MASK, 7);
+
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_TX_ADDR_LO), ==,
+ 0x89abcdef);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_TX_ADDR_HI), ==,
+ 0x01234567);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_TX_LEN), ==, 4096);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_IRQ_MASK), ==, 7);
+
+ qtest_system_reset(test.qts);
+ qpci_device_enable(test.dev);
+
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_TX_ADDR_LO), ==, 0);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_TX_ADDR_HI), ==, 0);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_TX_LEN), ==, 0);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_RX_STATUS), ==, 0);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_IRQ_MASK), ==, 0);
+
+ cnuas_vnic_test_stop(&test);
+}
+
+static int cnuas_vnic_test_listen(char **path)
+{
+ struct sockaddr_un address = { .sun_family = AF_UNIX };
+ int fd;
+
+ *path = g_strdup_printf("/tmp/cnuas-vnic-test-%u.sock", getpid());
+ unlink(*path);
+ g_strlcpy(address.sun_path, *path, sizeof(address.sun_path));
+
+ fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ g_assert_cmpint(fd, >=, 0);
+ g_assert_cmpint(bind(fd, (struct sockaddr *)&address, sizeof(address)),
+ ==, 0);
+ g_assert_cmpint(listen(fd, 1), ==, 0);
+ return fd;
+}
+
+static void cnuas_vnic_test_transport(void)
+{
+ static const uint8_t tx_frame[] = {
+ 0x02, 0, 0, 0, 0, 2, 0x02, 0, 0, 0, 0, 1, 0x08, 0,
+ 'C', 'n', 'u', 'a', 's',
+ };
+ static const uint8_t rx_frame[] = {
+ 0x02, 0, 0, 0, 0, 1, 0x02, 0, 0, 0, 0, 2, 0x08, 0,
+ 'R', 'D', 'M', 'A',
+ };
+ uint8_t result[64] = { 0 };
+ const uint64_t tx_address = 0x100000;
+ const uint64_t rx_address = 0x101000;
+ g_autofree char *path = NULL;
+ g_autofree char *args = NULL;
+ CnuasVnicTest test;
+ int connection;
+ int listener;
+ unsigned int i;
+
+ listener = cnuas_vnic_test_listen(&path);
+ args = g_strdup_printf(",socket_path=%s", path);
+ cnuas_vnic_test_start_with_args(&test, args);
+ connection = accept(listener, NULL, NULL);
+ g_assert_cmpint(connection, >=, 0);
+
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_LINK_STATUS), ==, 1);
+
+ qtest_memwrite(test.qts, tx_address, tx_frame, sizeof(tx_frame));
+ qpci_io_writel(test.dev, test.bar, CNUAS_VNIC_REG_TX_ADDR_LO,
+ tx_address);
+ qpci_io_writel(test.dev, test.bar, CNUAS_VNIC_REG_TX_ADDR_HI,
+ tx_address >> 32);
+ qpci_io_writel(test.dev, test.bar, CNUAS_VNIC_REG_TX_LEN,
+ sizeof(tx_frame));
+ qpci_io_writel(test.dev, test.bar, CNUAS_VNIC_REG_TX_DOORBELL, 1);
+
+ g_assert_cmpint(recv(connection, result, sizeof(result), 0), ==,
+ sizeof(tx_frame));
+ g_assert_cmpmem(result, sizeof(tx_frame), tx_frame, sizeof(tx_frame));
+
+ qpci_io_writel(test.dev, test.bar, CNUAS_VNIC_REG_RX_ADDR_LO,
+ rx_address);
+ qpci_io_writel(test.dev, test.bar, CNUAS_VNIC_REG_RX_ADDR_HI,
+ rx_address >> 32);
+ g_assert_cmpint(send(connection, rx_frame, sizeof(rx_frame), 0), ==,
+ sizeof(rx_frame));
+
+ for (i = 0; i < 1000; i++) {
+ if (qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_RX_STATUS) == 1) {
+ break;
+ }
+ g_usleep(1000);
+ }
+ g_assert_cmpuint(i, <, 1000);
+ g_assert_cmpuint(qpci_io_readl(test.dev, test.bar,
+ CNUAS_VNIC_REG_RX_LEN), ==,
+ sizeof(rx_frame));
+
+ memset(result, 0, sizeof(result));
+ qtest_memread(test.qts, rx_address, result, sizeof(rx_frame));
+ g_assert_cmpmem(result, sizeof(rx_frame), rx_frame, sizeof(rx_frame));
+
+ close(connection);
+ close(listener);
+ unlink(path);
+ cnuas_vnic_test_stop(&test);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ qtest_add_func("/cnuas-vnic/pci-config", cnuas_vnic_test_pci_config);
+ qtest_add_func("/cnuas-vnic/registers", cnuas_vnic_test_registers);
+ qtest_add_func("/cnuas-vnic/transport", cnuas_vnic_test_transport);
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 33f58b0e95..84d2fa702a 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -59,6 +59,7 @@ qtests_filter = \
qtests_i386 = \
(slirp.found() ? ['pxe-test'] : []) + \
qtests_filter + \
+ (config_all_devices.has_key('CONFIG_CNUAS_VNIC') ? ['cnuas-vnic-test'] : []) + \
(config_all_devices.has_key('CONFIG_ACPI_VMGENID') ? ['vmgenid-test'] : []) + \
(config_all_devices.has_key('CONFIG_AHCI_ICH9') and have_tools ? ['ahci-test'] : []) + \
(config_all_devices.has_key('CONFIG_AHCI_ICH9') ? ['tco-test'] : []) + \
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v3 4/6] pci: Add device ID for CnuasGPU
2026-09-29 20:23 [PATCH v3 0/6] hw: Add Cnuas virtual devices Weqaar Janjua
` (2 preceding siblings ...)
2026-09-29 20:23 ` [PATCH v3 3/6] tests/qtest: Add Cnuas virtual network adapter tests Weqaar Janjua
@ 2026-09-29 20:23 ` Weqaar Janjua
2026-09-30 7:00 ` Gerd Hoffmann
2026-09-29 20:23 ` [PATCH v3 5/6] hw/misc: Add CnuasGPU virtual accelerator Weqaar Janjua
` (2 subsequent siblings)
6 siblings, 1 reply; 15+ messages in thread
From: Weqaar Janjua @ 2026-09-29 20:23 UTC (permalink / raw)
To: qemu-devel
Cc: kraxel, peter.maydell, mst, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier
Reserve device ID 0018 for the CnuasGPU virtual accelerator.
Signed-off-by: Weqaar Janjua <weqaar.janjua@packetfive.com>
---
docs/specs/pci-ids.rst | 2 ++
include/hw/pci/pci.h | 1 +
2 files changed, 3 insertions(+)
diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst
index dd7d62d8ac..74a35b8f09 100644
--- a/docs/specs/pci-ids.rst
+++ b/docs/specs/pci-ids.rst
@@ -102,6 +102,8 @@ PCI devices (other than virtio):
PCI RISC-V IOMMU device
1b36:0017
Cnuas virtual RDMA network adapter (:doc:`cnuas-vnic`)
+1b36:0018
+ CnuasGPU virtual accelerator
All these devices are documented in :doc:`index`.
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 99a31856d0..ab064a347f 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -122,6 +122,7 @@ extern bool pci_available;
#define PCI_DEVICE_ID_REDHAT_UFS 0x0013
#define PCI_DEVICE_ID_REDHAT_RISCV_IOMMU 0x0014
#define PCI_DEVICE_ID_REDHAT_CNUASNIC 0x0017
+#define PCI_DEVICE_ID_REDHAT_CNUASGPU 0x0018
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
#define FMT_PCIBUS PRIx64
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v3 4/6] pci: Add device ID for CnuasGPU
2026-09-29 20:23 ` [PATCH v3 4/6] pci: Add device ID for CnuasGPU Weqaar Janjua
@ 2026-09-30 7:00 ` Gerd Hoffmann
0 siblings, 0 replies; 15+ messages in thread
From: Gerd Hoffmann @ 2026-09-30 7:00 UTC (permalink / raw)
To: Weqaar Janjua
Cc: qemu-devel, peter.maydell, mst, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier
On Tue, Sep 29, 2026 at 08:23:55PM +0000, Weqaar Janjua wrote:
> Reserve device ID 0018 for the CnuasGPU virtual accelerator.
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 5/6] hw/misc: Add CnuasGPU virtual accelerator
2026-09-29 20:23 [PATCH v3 0/6] hw: Add Cnuas virtual devices Weqaar Janjua
` (3 preceding siblings ...)
2026-09-29 20:23 ` [PATCH v3 4/6] pci: Add device ID for CnuasGPU Weqaar Janjua
@ 2026-09-29 20:23 ` Weqaar Janjua
2026-09-29 20:23 ` [PATCH v3 6/6] tests/qtest: Add CnuasGPU tests Weqaar Janjua
2026-10-16 15:32 ` [PATCH v3 0/6] hw: Add Cnuas virtual devices Weqaar Janjua
6 siblings, 0 replies; 15+ messages in thread
From: Weqaar Janjua @ 2026-09-29 20:23 UTC (permalink / raw)
To: qemu-devel
Cc: kraxel, peter.maydell, mst, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier
Add device memory, interrupt state, and link transport.
Signed-off-by: Weqaar Janjua <weqaar.janjua@packetfive.com>
---
MAINTAINERS | 2 +
docs/specs/cnuasgpu.rst | 91 +++++++
docs/specs/index.rst | 1 +
docs/specs/pci-ids.rst | 2 +-
hw/misc/Kconfig | 5 +
hw/misc/cnuasgpu.c | 557 ++++++++++++++++++++++++++++++++++++++++
hw/misc/meson.build | 1 +
7 files changed, 658 insertions(+), 1 deletion(-)
create mode 100644 docs/specs/cnuasgpu.rst
create mode 100644 hw/misc/cnuasgpu.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 7be5622f23..7b2173d832 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2777,6 +2777,8 @@ Cnuas virtual devices
M: Weqaar Janjua <weqaar.janjua@packetfive.com>
S: Maintained
F: docs/specs/cnuas-vnic.rst
+F: docs/specs/cnuasgpu.rst
+F: hw/misc/cnuasgpu.c
F: hw/net/cnuas_vnic.c
F: tests/qtest/cnuas-vnic-test.c
diff --git a/docs/specs/cnuasgpu.rst b/docs/specs/cnuasgpu.rst
new file mode 100644
index 0000000000..b364f6b04d
--- /dev/null
+++ b/docs/specs/cnuasgpu.rst
@@ -0,0 +1,91 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+============================
+CnuasGPU virtual accelerator
+============================
+
+``cnuasgpu`` is the PCIe device model for the Cnuas virtual AI/ML
+accelerator. It emulates the guest-visible control, device-memory, interrupt,
+and peer-fabric interfaces. Compute kernels are executed by Cnuas runtime
+backends rather than by a cycle-accurate GPU pipeline inside QEMU. Its
+optional ``SOCK_SEQPACKET`` transport currently requires a Linux host.
+
+PCI interface
+-------------
+
+===================== ==================
+Vendor ID 1b36
+Device ID 0018
+Revision 1
+Class 0b40, co-processor
+===================== ==================
+
+====== ======================================================
+BAR Contents
+====== ======================================================
+0 64 KiB MMIO register region
+1 64-bit prefetchable device memory, 256 MiB by default
+====== ======================================================
+
+The device supports one MSI vector and INTx.
+
+Properties
+----------
+
+``gpu_id``
+ Guest-visible accelerator identifier.
+
+``sm_count``, ``lanes_per_sm``, ``tensor_size``
+ Guest-visible geometry values. They do not control a compute engine.
+
+``devmem_size``
+ BAR 1 size. It must be a power of two from 16 MiB through 1 TiB.
+
+``cnuaslink_socket``
+ Optional ``AF_UNIX`` ``SOCK_SEQPACKET`` endpoint for CnuasLink frames.
+
+``x-speed``, ``x-width``
+ PCIe link speed and width reported through PCIe configuration space.
+
+Register map
+------------
+
+All registers require 32-bit little-endian accesses.
+
+====== ================== =========================================
+Offset Name Meaning
+====== ================== =========================================
+0x000 VENDOR_ID 0x1b36, read only
+0x004 DEVICE_ID 0x0018, read only
+0x008 REVISION 0x01, read only
+0x00c FW_VERSION 0x00010000, read only
+0x010 GPU_ID ``gpu_id`` property, read only
+0x014 SM_COUNT ``sm_count`` property, read only
+0x018 LANES_PER_SM ``lanes_per_sm`` property, read only
+0x01c TENSOR_SIZE ``tensor_size`` property, read only
+0x020 DEVMEM_SIZE_LO BAR 1 size, low 32 bits, read only
+0x024 DEVMEM_SIZE_HI BAR 1 size, high 32 bits, read only
+0x100 IRQ_STATUS Interrupt status; write one bits to clear
+0x104 IRQ_MASK Interrupt enable mask
+0x200 LINK_STATUS Bit 0 link up, bit 1 receive ready
+0x204 TX_OFFSET_LO BAR 1 transmit offset, low 32 bits
+0x208 TX_OFFSET_HI BAR 1 transmit offset, high 32 bits
+0x20c TX_LEN Transmit length from 1 through 65536 bytes
+0x210 TX_DOORBELL Write nonzero to transmit
+0x214 RX_OFFSET_LO BAR 1 receive offset, low 32 bits
+0x218 RX_OFFSET_HI BAR 1 receive offset, high 32 bits
+0x21c RX_BUF_SIZE Maximum accepted receive length
+0x220 RX_LEN Last receive length, read only
+0x224 RX_CONSUME Write nonzero to release the receive buffer
+====== ================== =========================================
+
+Interrupt bit 0 reports transmit completion and bit 1 reports an available
+receive frame.
+
+CnuasLink
+---------
+
+The optional socket transports one frame per message. A transmit reads from
+BAR 1. A receive writes to BAR 1 and pauses socket monitoring until the guest
+writes ``RX_CONSUME``. Offsets and lengths are checked against BAR 1 before
+the model accesses device memory.
diff --git a/docs/specs/index.rst b/docs/specs/index.rst
index 0fd5da2aa8..e674d76da6 100644
--- a/docs/specs/index.rst
+++ b/docs/specs/index.rst
@@ -27,6 +27,7 @@ guest hardware that is specific to QEMU.
fsi
vmw_pvscsi-spec
cnuas-vnic
+ cnuasgpu
edu
ivshmem-spec
pvpanic
diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst
index 74a35b8f09..bacd005862 100644
--- a/docs/specs/pci-ids.rst
+++ b/docs/specs/pci-ids.rst
@@ -103,7 +103,7 @@ PCI devices (other than virtio):
1b36:0017
Cnuas virtual RDMA network adapter (:doc:`cnuas-vnic`)
1b36:0018
- CnuasGPU virtual accelerator
+ CnuasGPU virtual accelerator (:doc:`cnuasgpu`)
All these devices are documented in :doc:`index`.
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 46e3c03cc8..39d52674e0 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -35,6 +35,11 @@ config EDU
default y if TEST_DEVICES
depends on PCI && MSI_NONBROKEN
+config CNUAS_GPU
+ bool
+ default y if PCI_DEVICES || PCIE_DEVICES
+ depends on LINUX && PCI_EXPRESS && MSI_NONBROKEN
+
config I2C_ECHO
bool
default y if TEST_DEVICES
diff --git a/hw/misc/cnuasgpu.c b/hw/misc/cnuasgpu.c
new file mode 100644
index 0000000000..38ac1bdb42
--- /dev/null
+++ b/hw/misc/cnuasgpu.c
@@ -0,0 +1,557 @@
+/*
+ * CnuasGPU virtual accelerator
+ *
+ * Copyright (c) 2026 PacketFive
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/main-loop.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev-properties-system.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pcie.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qom/object.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define TYPE_CNUAS_GPU "cnuasgpu"
+OBJECT_DECLARE_SIMPLE_TYPE(CnuasGpuState, CNUAS_GPU)
+
+#define CNUAS_GPU_BAR0_SIZE (64 * KiB)
+#define CNUAS_GPU_DEVMEM_DEFAULT (256 * MiB)
+#define CNUAS_GPU_DEVMEM_MIN (16 * MiB)
+#define CNUAS_GPU_DEVMEM_MAX (1024ULL * GiB)
+#define CNUAS_GPU_LINK_MAX_FRAME (64 * KiB)
+
+#define CNUAS_GPU_REG_VENDOR_ID 0x000
+#define CNUAS_GPU_REG_DEVICE_ID 0x004
+#define CNUAS_GPU_REG_REVISION 0x008
+#define CNUAS_GPU_REG_FW_VERSION 0x00c
+#define CNUAS_GPU_REG_GPU_ID 0x010
+#define CNUAS_GPU_REG_SM_COUNT 0x014
+#define CNUAS_GPU_REG_LANES_PER_SM 0x018
+#define CNUAS_GPU_REG_TENSOR_SIZE 0x01c
+#define CNUAS_GPU_REG_DEVMEM_SIZE_LO 0x020
+#define CNUAS_GPU_REG_DEVMEM_SIZE_HI 0x024
+#define CNUAS_GPU_REG_IRQ_STATUS 0x100
+#define CNUAS_GPU_REG_IRQ_MASK 0x104
+#define CNUAS_GPU_REG_LINK_STATUS 0x200
+#define CNUAS_GPU_REG_TX_OFFSET_LO 0x204
+#define CNUAS_GPU_REG_TX_OFFSET_HI 0x208
+#define CNUAS_GPU_REG_TX_LEN 0x20c
+#define CNUAS_GPU_REG_TX_DOORBELL 0x210
+#define CNUAS_GPU_REG_RX_OFFSET_LO 0x214
+#define CNUAS_GPU_REG_RX_OFFSET_HI 0x218
+#define CNUAS_GPU_REG_RX_BUF_SIZE 0x21c
+#define CNUAS_GPU_REG_RX_LEN 0x220
+#define CNUAS_GPU_REG_RX_CONSUME 0x224
+
+#define CNUAS_GPU_FW_VERSION 0x00010000
+#define CNUAS_GPU_REVISION 1
+
+#define CNUAS_GPU_IRQ_TX_DONE BIT(0)
+#define CNUAS_GPU_IRQ_RX_AVAILABLE BIT(1)
+
+#define CNUAS_GPU_LINK_UP BIT(0)
+#define CNUAS_GPU_LINK_RX_READY BIT(1)
+
+struct CnuasGpuState {
+ PCIDevice parent_obj;
+ MemoryRegion bar0;
+ MemoryRegion bar1;
+
+ uint32_t gpu_id;
+ uint32_t sm_count;
+ uint32_t lanes_per_sm;
+ uint32_t tensor_size;
+ uint64_t devmem_size;
+ PCIExpLinkSpeed pcie_speed;
+ PCIExpLinkWidth pcie_width;
+
+ uint32_t irq_status;
+ uint32_t irq_mask;
+
+ char *link_socket;
+ int socket_fd;
+ bool link_up;
+ uint32_t tx_offset_lo;
+ uint32_t tx_offset_hi;
+ uint32_t tx_len;
+ uint32_t rx_offset_lo;
+ uint32_t rx_offset_hi;
+ uint32_t rx_buf_size;
+ uint32_t rx_len;
+ bool rx_ready;
+};
+
+static void cnuas_gpu_update_irq(CnuasGpuState *s)
+{
+ bool pending = s->irq_status & s->irq_mask;
+
+ if (msi_enabled(&s->parent_obj)) {
+ if (pending) {
+ msi_notify(&s->parent_obj, 0);
+ }
+ } else {
+ pci_set_irq(&s->parent_obj, pending);
+ }
+}
+
+static void *cnuas_gpu_devmem_ptr(CnuasGpuState *s, uint64_t offset,
+ size_t length)
+{
+ if (offset > s->devmem_size || length > s->devmem_size - offset) {
+ return NULL;
+ }
+
+ return memory_region_get_ram_ptr(&s->bar1) + offset;
+}
+
+static void cnuas_gpu_disconnect(CnuasGpuState *s)
+{
+ if (s->socket_fd >= 0) {
+ qemu_set_fd_handler(s->socket_fd, NULL, NULL, NULL);
+ close(s->socket_fd);
+ s->socket_fd = -1;
+ }
+ s->link_up = false;
+ s->rx_ready = false;
+ s->rx_len = 0;
+}
+
+static void cnuas_gpu_receive_ready(void *opaque)
+{
+ CnuasGpuState *s = opaque;
+ uint8_t frame[CNUAS_GPU_LINK_MAX_FRAME];
+ uint64_t offset;
+ void *destination;
+ ssize_t length;
+
+ if (s->rx_ready) {
+ qemu_set_fd_handler(s->socket_fd, NULL, NULL, NULL);
+ return;
+ }
+
+ length = recv(s->socket_fd, frame, sizeof(frame), MSG_DONTWAIT);
+ if (length == 0) {
+ cnuas_gpu_disconnect(s);
+ return;
+ }
+ if (length < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: link receive failed: %s\n",
+ strerror(errno));
+ cnuas_gpu_disconnect(s);
+ }
+ return;
+ }
+
+ if (!s->rx_buf_size || length > s->rx_buf_size) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: RX frame does not fit guest buffer\n");
+ return;
+ }
+
+ offset = ((uint64_t)s->rx_offset_hi << 32) | s->rx_offset_lo;
+ destination = cnuas_gpu_devmem_ptr(s, offset, length);
+ if (!destination) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: RX range exceeds device memory\n");
+ return;
+ }
+
+ memcpy(destination, frame, length);
+ s->rx_len = length;
+ s->rx_ready = true;
+ s->irq_status |= CNUAS_GPU_IRQ_RX_AVAILABLE;
+ cnuas_gpu_update_irq(s);
+ qemu_set_fd_handler(s->socket_fd, NULL, NULL, NULL);
+}
+
+static void cnuas_gpu_connect(CnuasGpuState *s)
+{
+ struct sockaddr_un address = { .sun_family = AF_UNIX };
+ int flags;
+ int fd;
+
+ if (!s->link_socket || !s->link_socket[0]) {
+ return;
+ }
+ if (strlen(s->link_socket) >= sizeof(address.sun_path)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: link socket path is too long\n");
+ return;
+ }
+
+ fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ if (fd < 0) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: link socket creation failed: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ g_strlcpy(address.sun_path, s->link_socket, sizeof(address.sun_path));
+ if (connect(fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
+ qemu_log_mask(LOG_UNIMP,
+ "cnuasgpu: cannot connect to %s: %s\n",
+ s->link_socket, strerror(errno));
+ close(fd);
+ return;
+ }
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: cannot make socket nonblocking: %s\n",
+ strerror(errno));
+ close(fd);
+ return;
+ }
+
+ s->socket_fd = fd;
+ s->link_up = true;
+ qemu_set_fd_handler(fd, cnuas_gpu_receive_ready, NULL, s);
+}
+
+static void cnuas_gpu_transmit(CnuasGpuState *s)
+{
+ uint64_t offset;
+ void *source;
+ ssize_t sent;
+
+ if (!s->link_up || s->socket_fd < 0) {
+ return;
+ }
+ if (!s->tx_len || s->tx_len > CNUAS_GPU_LINK_MAX_FRAME) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: invalid TX length %u\n", s->tx_len);
+ return;
+ }
+
+ offset = ((uint64_t)s->tx_offset_hi << 32) | s->tx_offset_lo;
+ source = cnuas_gpu_devmem_ptr(s, offset, s->tx_len);
+ if (!source) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: TX range exceeds device memory\n");
+ return;
+ }
+
+ sent = send(s->socket_fd, source, s->tx_len, MSG_NOSIGNAL);
+ if (sent < 0) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: link transmit failed: %s\n",
+ strerror(errno));
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ cnuas_gpu_disconnect(s);
+ }
+ return;
+ }
+ if (sent != s->tx_len) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: short TX (%zd of %u bytes)\n",
+ sent, s->tx_len);
+ return;
+ }
+
+ s->irq_status |= CNUAS_GPU_IRQ_TX_DONE;
+ cnuas_gpu_update_irq(s);
+}
+
+static uint64_t cnuas_gpu_bar0_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ CnuasGpuState *s = opaque;
+
+ switch (addr) {
+ case CNUAS_GPU_REG_VENDOR_ID:
+ return PCI_VENDOR_ID_REDHAT;
+ case CNUAS_GPU_REG_DEVICE_ID:
+ return PCI_DEVICE_ID_REDHAT_CNUASGPU;
+ case CNUAS_GPU_REG_REVISION:
+ return CNUAS_GPU_REVISION;
+ case CNUAS_GPU_REG_FW_VERSION:
+ return CNUAS_GPU_FW_VERSION;
+ case CNUAS_GPU_REG_GPU_ID:
+ return s->gpu_id;
+ case CNUAS_GPU_REG_SM_COUNT:
+ return s->sm_count;
+ case CNUAS_GPU_REG_LANES_PER_SM:
+ return s->lanes_per_sm;
+ case CNUAS_GPU_REG_TENSOR_SIZE:
+ return s->tensor_size;
+ case CNUAS_GPU_REG_DEVMEM_SIZE_LO:
+ return s->devmem_size;
+ case CNUAS_GPU_REG_DEVMEM_SIZE_HI:
+ return s->devmem_size >> 32;
+ case CNUAS_GPU_REG_IRQ_STATUS:
+ return s->irq_status;
+ case CNUAS_GPU_REG_IRQ_MASK:
+ return s->irq_mask;
+ case CNUAS_GPU_REG_LINK_STATUS:
+ return (s->link_up ? CNUAS_GPU_LINK_UP : 0) |
+ (s->rx_ready ? CNUAS_GPU_LINK_RX_READY : 0);
+ case CNUAS_GPU_REG_TX_OFFSET_LO:
+ return s->tx_offset_lo;
+ case CNUAS_GPU_REG_TX_OFFSET_HI:
+ return s->tx_offset_hi;
+ case CNUAS_GPU_REG_TX_LEN:
+ return s->tx_len;
+ case CNUAS_GPU_REG_RX_OFFSET_LO:
+ return s->rx_offset_lo;
+ case CNUAS_GPU_REG_RX_OFFSET_HI:
+ return s->rx_offset_hi;
+ case CNUAS_GPU_REG_RX_BUF_SIZE:
+ return s->rx_buf_size;
+ case CNUAS_GPU_REG_RX_LEN:
+ return s->rx_len;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: read from unknown register 0x%"
+ HWADDR_PRIx "\n", addr);
+ return 0;
+ }
+}
+
+static void cnuas_gpu_bar0_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ CnuasGpuState *s = opaque;
+
+ switch (addr) {
+ case CNUAS_GPU_REG_IRQ_STATUS:
+ s->irq_status &= ~value;
+ cnuas_gpu_update_irq(s);
+ break;
+ case CNUAS_GPU_REG_IRQ_MASK:
+ s->irq_mask = value;
+ cnuas_gpu_update_irq(s);
+ break;
+ case CNUAS_GPU_REG_TX_OFFSET_LO:
+ s->tx_offset_lo = value;
+ break;
+ case CNUAS_GPU_REG_TX_OFFSET_HI:
+ s->tx_offset_hi = value;
+ break;
+ case CNUAS_GPU_REG_TX_LEN:
+ s->tx_len = value;
+ break;
+ case CNUAS_GPU_REG_TX_DOORBELL:
+ if (value) {
+ cnuas_gpu_transmit(s);
+ }
+ break;
+ case CNUAS_GPU_REG_RX_OFFSET_LO:
+ s->rx_offset_lo = value;
+ break;
+ case CNUAS_GPU_REG_RX_OFFSET_HI:
+ s->rx_offset_hi = value;
+ break;
+ case CNUAS_GPU_REG_RX_BUF_SIZE:
+ s->rx_buf_size = value;
+ break;
+ case CNUAS_GPU_REG_RX_CONSUME:
+ if (value && s->rx_ready) {
+ s->rx_ready = false;
+ s->rx_len = 0;
+ if (s->socket_fd >= 0) {
+ qemu_set_fd_handler(s->socket_fd,
+ cnuas_gpu_receive_ready, NULL, s);
+ }
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "cnuasgpu: write to unknown register 0x%"
+ HWADDR_PRIx "\n", addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps cnuas_gpu_bar0_ops = {
+ .read = cnuas_gpu_bar0_read,
+ .write = cnuas_gpu_bar0_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void cnuas_gpu_reset_hold(Object *obj, ResetType type)
+{
+ CnuasGpuState *s = CNUAS_GPU(obj);
+
+ s->irq_status = 0;
+ s->irq_mask = 0;
+ s->tx_offset_lo = 0;
+ s->tx_offset_hi = 0;
+ s->tx_len = 0;
+ s->rx_offset_lo = 0;
+ s->rx_offset_hi = 0;
+ s->rx_buf_size = 0;
+ s->rx_len = 0;
+ s->rx_ready = false;
+ pci_set_irq(&s->parent_obj, 0);
+
+ if (s->socket_fd >= 0) {
+ qemu_set_fd_handler(s->socket_fd, cnuas_gpu_receive_ready, NULL, s);
+ }
+}
+
+static int cnuas_gpu_post_load(void *opaque, int version_id)
+{
+ CnuasGpuState *s = opaque;
+
+ if (s->socket_fd >= 0) {
+ qemu_set_fd_handler(s->socket_fd,
+ s->rx_ready ? NULL : cnuas_gpu_receive_ready,
+ NULL, s);
+ }
+ cnuas_gpu_update_irq(s);
+ return 0;
+}
+
+static const VMStateDescription vmstate_cnuas_gpu = {
+ .name = TYPE_CNUAS_GPU,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = cnuas_gpu_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, CnuasGpuState),
+ VMSTATE_UINT32(irq_status, CnuasGpuState),
+ VMSTATE_UINT32(irq_mask, CnuasGpuState),
+ VMSTATE_UINT32(tx_offset_lo, CnuasGpuState),
+ VMSTATE_UINT32(tx_offset_hi, CnuasGpuState),
+ VMSTATE_UINT32(tx_len, CnuasGpuState),
+ VMSTATE_UINT32(rx_offset_lo, CnuasGpuState),
+ VMSTATE_UINT32(rx_offset_hi, CnuasGpuState),
+ VMSTATE_UINT32(rx_buf_size, CnuasGpuState),
+ VMSTATE_UINT32(rx_len, CnuasGpuState),
+ VMSTATE_BOOL(rx_ready, CnuasGpuState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void cnuas_gpu_realize(PCIDevice *pdev, Error **errp)
+{
+ CnuasGpuState *s = CNUAS_GPU(pdev);
+
+ if (s->devmem_size < CNUAS_GPU_DEVMEM_MIN ||
+ s->devmem_size > CNUAS_GPU_DEVMEM_MAX ||
+ !is_power_of_2(s->devmem_size)) {
+ error_setg(errp,
+ "cnuasgpu: devmem_size must be a power of two from "
+ "%llu to %llu bytes",
+ (unsigned long long)CNUAS_GPU_DEVMEM_MIN,
+ (unsigned long long)CNUAS_GPU_DEVMEM_MAX);
+ return;
+ }
+
+ pci_config_set_interrupt_pin(pdev->config, 1);
+ if (msi_init(pdev, 0, 1, true, false, errp)) {
+ return;
+ }
+ if (pcie_endpoint_cap_init(pdev, 0x80) < 0) {
+ error_setg(errp, "cnuasgpu: cannot initialize PCIe capability");
+ msi_uninit(pdev);
+ return;
+ }
+ pcie_cap_fill_link_ep_usp(pdev, s->pcie_width, s->pcie_speed, false);
+
+ memory_region_init_io(&s->bar0, OBJECT(s), &cnuas_gpu_bar0_ops, s,
+ "cnuasgpu-bar0", CNUAS_GPU_BAR0_SIZE);
+ pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0);
+
+ memory_region_init_ram(&s->bar1, OBJECT(s), "cnuasgpu-devmem",
+ s->devmem_size, errp);
+ if (*errp) {
+ pcie_cap_exit(pdev);
+ msi_uninit(pdev);
+ return;
+ }
+ pci_register_bar(pdev, 1,
+ PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH |
+ PCI_BASE_ADDRESS_MEM_TYPE_64,
+ &s->bar1);
+
+ s->socket_fd = -1;
+ cnuas_gpu_connect(s);
+}
+
+static void cnuas_gpu_exit(PCIDevice *pdev)
+{
+ CnuasGpuState *s = CNUAS_GPU(pdev);
+
+ cnuas_gpu_disconnect(s);
+ pcie_cap_exit(pdev);
+ msi_uninit(pdev);
+}
+
+static const Property cnuas_gpu_properties[] = {
+ DEFINE_PROP_UINT32("gpu_id", CnuasGpuState, gpu_id, 0),
+ DEFINE_PROP_UINT32("sm_count", CnuasGpuState, sm_count, 16),
+ DEFINE_PROP_UINT32("lanes_per_sm", CnuasGpuState, lanes_per_sm, 32),
+ DEFINE_PROP_UINT32("tensor_size", CnuasGpuState, tensor_size, 16),
+ DEFINE_PROP_SIZE("devmem_size", CnuasGpuState, devmem_size,
+ CNUAS_GPU_DEVMEM_DEFAULT),
+ DEFINE_PROP_STRING("cnuaslink_socket", CnuasGpuState, link_socket),
+ DEFINE_PROP_PCIE_LINK_SPEED("x-speed", CnuasGpuState, pcie_speed,
+ PCIE_LINK_SPEED_32),
+ DEFINE_PROP_PCIE_LINK_WIDTH("x-width", CnuasGpuState, pcie_width,
+ PCIE_LINK_WIDTH_16),
+};
+
+static void cnuas_gpu_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ pc->realize = cnuas_gpu_realize;
+ pc->exit = cnuas_gpu_exit;
+ pc->vendor_id = PCI_VENDOR_ID_REDHAT;
+ pc->device_id = PCI_DEVICE_ID_REDHAT_CNUASGPU;
+ pc->class_id = PCI_CLASS_PROCESSOR_CO;
+ pc->revision = CNUAS_GPU_REVISION;
+
+ rc->phases.hold = cnuas_gpu_reset_hold;
+ dc->desc = "CnuasGPU virtual accelerator";
+ dc->vmsd = &vmstate_cnuas_gpu;
+ device_class_set_props(dc, cnuas_gpu_properties);
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo cnuas_gpu_info = {
+ .name = TYPE_CNUAS_GPU,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(CnuasGpuState),
+ .class_init = cnuas_gpu_class_init,
+ .interfaces = (const InterfaceInfo[]) {
+ { INTERFACE_PCIE_DEVICE },
+ { },
+ },
+};
+
+static void cnuas_gpu_register_types(void)
+{
+ type_register_static(&cnuas_gpu_info);
+}
+
+type_init(cnuas_gpu_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 9790afa104..beb3a678a5 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -1,6 +1,7 @@
system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c'))
system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c'))
+system_ss.add(when: 'CONFIG_CNUAS_GPU', if_true: files('cnuasgpu.c'))
system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c'))
system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v3 6/6] tests/qtest: Add CnuasGPU tests
2026-09-29 20:23 [PATCH v3 0/6] hw: Add Cnuas virtual devices Weqaar Janjua
` (4 preceding siblings ...)
2026-09-29 20:23 ` [PATCH v3 5/6] hw/misc: Add CnuasGPU virtual accelerator Weqaar Janjua
@ 2026-09-29 20:23 ` Weqaar Janjua
2026-10-16 15:32 ` [PATCH v3 0/6] hw: Add Cnuas virtual devices Weqaar Janjua
6 siblings, 0 replies; 15+ messages in thread
From: Weqaar Janjua @ 2026-09-29 20:23 UTC (permalink / raw)
To: qemu-devel
Cc: kraxel, peter.maydell, mst, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier
Cover PCI identity, registers, device memory, reset, and link transport.
Signed-off-by: Weqaar Janjua <weqaar.janjua@packetfive.com>
---
MAINTAINERS | 1 +
tests/qtest/cnuasgpu-test.c | 274 ++++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 1 +
3 files changed, 276 insertions(+)
create mode 100644 tests/qtest/cnuasgpu-test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 7b2173d832..d05d3db674 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2781,6 +2781,7 @@ F: docs/specs/cnuasgpu.rst
F: hw/misc/cnuasgpu.c
F: hw/net/cnuas_vnic.c
F: tests/qtest/cnuas-vnic-test.c
+F: tests/qtest/cnuasgpu-test.c
VMware
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
diff --git a/tests/qtest/cnuasgpu-test.c b/tests/qtest/cnuasgpu-test.c
new file mode 100644
index 0000000000..3ac3b20b32
--- /dev/null
+++ b/tests/qtest/cnuasgpu-test.c
@@ -0,0 +1,274 @@
+/*
+ * QTest for the CnuasGPU virtual accelerator
+ *
+ * Copyright (c) 2026 PacketFive
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_regs.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "libqtest.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define CNUAS_GPU_DEVFN QPCI_DEVFN(4, 0)
+#define CNUAS_GPU_BAR0_SIZE (64 * KiB)
+#define CNUAS_GPU_BAR1_SIZE (16 * MiB)
+#define CNUAS_GPU_REG_VENDOR_ID 0x000
+#define CNUAS_GPU_REG_DEVICE_ID 0x004
+#define CNUAS_GPU_REG_REVISION 0x008
+#define CNUAS_GPU_REG_FW_VERSION 0x00c
+#define CNUAS_GPU_REG_GPU_ID 0x010
+#define CNUAS_GPU_REG_SM_COUNT 0x014
+#define CNUAS_GPU_REG_LANES_PER_SM 0x018
+#define CNUAS_GPU_REG_TENSOR_SIZE 0x01c
+#define CNUAS_GPU_REG_DEVMEM_SIZE_LO 0x020
+#define CNUAS_GPU_REG_DEVMEM_SIZE_HI 0x024
+#define CNUAS_GPU_REG_IRQ_STATUS 0x100
+#define CNUAS_GPU_REG_IRQ_MASK 0x104
+#define CNUAS_GPU_REG_LINK_STATUS 0x200
+#define CNUAS_GPU_REG_TX_OFFSET_LO 0x204
+#define CNUAS_GPU_REG_TX_OFFSET_HI 0x208
+#define CNUAS_GPU_REG_TX_LEN 0x20c
+#define CNUAS_GPU_REG_TX_DOORBELL 0x210
+#define CNUAS_GPU_REG_RX_OFFSET_LO 0x214
+#define CNUAS_GPU_REG_RX_OFFSET_HI 0x218
+#define CNUAS_GPU_REG_RX_BUF_SIZE 0x21c
+#define CNUAS_GPU_REG_RX_LEN 0x220
+#define CNUAS_GPU_REG_RX_CONSUME 0x224
+
+typedef struct CnuasGpuTest {
+ QTestState *qts;
+ QPCIBus *bus;
+ QPCIDevice *dev;
+ QPCIBar bar0;
+ QPCIBar bar1;
+} CnuasGpuTest;
+
+static void cnuas_gpu_test_start_with_args(CnuasGpuTest *test,
+ const char *args)
+{
+ uint64_t bar_size;
+
+ test->qts = qtest_initf("-machine q35 "
+ "-device cnuasgpu,addr=04.0,devmem_size=16M,"
+ "gpu_id=7,sm_count=24,lanes_per_sm=64,"
+ "tensor_size=32%s", args);
+ test->bus = qpci_new_pc(test->qts, NULL);
+ test->dev = qpci_device_find(test->bus, CNUAS_GPU_DEVFN);
+ g_assert_nonnull(test->dev);
+ qpci_device_enable(test->dev);
+
+ test->bar0 = qpci_iomap(test->dev, 0, &bar_size);
+ g_assert_cmpuint(bar_size, ==, CNUAS_GPU_BAR0_SIZE);
+ test->bar1 = qpci_iomap(test->dev, 1, &bar_size);
+ g_assert_cmpuint(bar_size, ==, CNUAS_GPU_BAR1_SIZE);
+}
+
+static void cnuas_gpu_test_start(CnuasGpuTest *test)
+{
+ cnuas_gpu_test_start_with_args(test, "");
+}
+
+static void cnuas_gpu_test_stop(CnuasGpuTest *test)
+{
+ g_free(test->dev);
+ qpci_free_pc(test->bus);
+ qtest_quit(test->qts);
+}
+
+static void cnuas_gpu_test_pci_config(void)
+{
+ CnuasGpuTest test;
+
+ cnuas_gpu_test_start(&test);
+
+ g_assert_cmphex(qpci_config_readw(test.dev, PCI_VENDOR_ID), ==,
+ PCI_VENDOR_ID_REDHAT);
+ g_assert_cmphex(qpci_config_readw(test.dev, PCI_DEVICE_ID), ==,
+ PCI_DEVICE_ID_REDHAT_CNUASGPU);
+ g_assert_cmphex(qpci_config_readw(test.dev, PCI_CLASS_DEVICE), ==,
+ PCI_CLASS_PROCESSOR_CO);
+ g_assert_cmphex(qpci_config_readb(test.dev, PCI_REVISION_ID), ==, 1);
+ g_assert_cmphex(qpci_find_capability(test.dev, PCI_CAP_ID_MSI, 0), !=, 0);
+
+ cnuas_gpu_test_stop(&test);
+}
+
+static void cnuas_gpu_test_registers(void)
+{
+ CnuasGpuTest test;
+
+ cnuas_gpu_test_start(&test);
+
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_VENDOR_ID), ==,
+ PCI_VENDOR_ID_REDHAT);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_DEVICE_ID), ==,
+ PCI_DEVICE_ID_REDHAT_CNUASGPU);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_REVISION), ==, 1);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_FW_VERSION), ==, 0x00010000);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_GPU_ID), ==, 7);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_SM_COUNT), ==, 24);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_LANES_PER_SM), ==, 64);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_TENSOR_SIZE), ==, 32);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_DEVMEM_SIZE_LO), ==,
+ CNUAS_GPU_BAR1_SIZE);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_DEVMEM_SIZE_HI), ==, 0);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_LINK_STATUS), ==, 0);
+
+ cnuas_gpu_test_stop(&test);
+}
+
+static void cnuas_gpu_test_memory_and_reset(void)
+{
+ static const uint8_t pattern[] = {
+ 0x43, 0x6e, 0x75, 0x61, 0x73, 0x47, 0x50, 0x55,
+ };
+ uint8_t result[sizeof(pattern)] = { 0 };
+ CnuasGpuTest test;
+
+ cnuas_gpu_test_start(&test);
+
+ qpci_memwrite(test.dev, test.bar1, 0x1000, pattern, sizeof(pattern));
+ qpci_memread(test.dev, test.bar1, 0x1000, result, sizeof(result));
+ g_assert_cmpmem(result, sizeof(result), pattern, sizeof(pattern));
+
+ qpci_io_writel(test.dev, test.bar0, CNUAS_GPU_REG_IRQ_MASK, 3);
+ qpci_io_writel(test.dev, test.bar0, CNUAS_GPU_REG_TX_OFFSET_LO,
+ 0x12345678);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_IRQ_MASK), ==, 3);
+
+ qtest_system_reset(test.qts);
+ qpci_device_enable(test.dev);
+
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_IRQ_STATUS), ==, 0);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_IRQ_MASK), ==, 0);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_TX_OFFSET_LO), ==, 0);
+
+ cnuas_gpu_test_stop(&test);
+}
+
+static int cnuas_gpu_test_listen(char **path)
+{
+ struct sockaddr_un address = { .sun_family = AF_UNIX };
+ int fd;
+
+ *path = g_strdup_printf("/tmp/cnuasgpu-test-%u.sock", getpid());
+ unlink(*path);
+ g_strlcpy(address.sun_path, *path, sizeof(address.sun_path));
+
+ fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ g_assert_cmpint(fd, >=, 0);
+ g_assert_cmpint(bind(fd, (struct sockaddr *)&address, sizeof(address)),
+ ==, 0);
+ g_assert_cmpint(listen(fd, 1), ==, 0);
+ return fd;
+}
+
+static void cnuas_gpu_test_transport(void)
+{
+ static const uint8_t tx_frame[] = {
+ 0x43, 0x6e, 0x75, 0x61, 0x73, 0x4c, 0x69, 0x6e, 0x6b,
+ };
+ static const uint8_t rx_frame[] = {
+ 0x70, 0x65, 0x65, 0x72, 0x2d, 0x66, 0x72, 0x61, 0x6d, 0x65,
+ };
+ uint8_t result[64] = { 0 };
+ const uint64_t tx_offset = 0x1000;
+ const uint64_t rx_offset = 0x2000;
+ g_autofree char *path = NULL;
+ g_autofree char *args = NULL;
+ CnuasGpuTest test;
+ int connection;
+ int listener;
+ unsigned int i;
+
+ listener = cnuas_gpu_test_listen(&path);
+ args = g_strdup_printf(",cnuaslink_socket=%s", path);
+ cnuas_gpu_test_start_with_args(&test, args);
+ connection = accept(listener, NULL, NULL);
+ g_assert_cmpint(connection, >=, 0);
+
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_LINK_STATUS), ==, 1);
+
+ qpci_memwrite(test.dev, test.bar1, tx_offset, tx_frame,
+ sizeof(tx_frame));
+ qpci_io_writel(test.dev, test.bar0, CNUAS_GPU_REG_TX_OFFSET_LO,
+ tx_offset);
+ qpci_io_writel(test.dev, test.bar0, CNUAS_GPU_REG_TX_OFFSET_HI,
+ tx_offset >> 32);
+ qpci_io_writel(test.dev, test.bar0, CNUAS_GPU_REG_TX_LEN,
+ sizeof(tx_frame));
+ qpci_io_writel(test.dev, test.bar0, CNUAS_GPU_REG_TX_DOORBELL, 1);
+
+ g_assert_cmpint(recv(connection, result, sizeof(result), 0), ==,
+ sizeof(tx_frame));
+ g_assert_cmpmem(result, sizeof(tx_frame), tx_frame, sizeof(tx_frame));
+
+ qpci_io_writel(test.dev, test.bar0, CNUAS_GPU_REG_RX_OFFSET_LO,
+ rx_offset);
+ qpci_io_writel(test.dev, test.bar0, CNUAS_GPU_REG_RX_OFFSET_HI,
+ rx_offset >> 32);
+ qpci_io_writel(test.dev, test.bar0, CNUAS_GPU_REG_RX_BUF_SIZE,
+ sizeof(rx_frame));
+ g_assert_cmpint(send(connection, rx_frame, sizeof(rx_frame), 0), ==,
+ sizeof(rx_frame));
+
+ for (i = 0; i < 1000; i++) {
+ if (qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_LINK_STATUS) == 3) {
+ break;
+ }
+ g_usleep(1000);
+ }
+ g_assert_cmpuint(i, <, 1000);
+ g_assert_cmpuint(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_RX_LEN), ==,
+ sizeof(rx_frame));
+
+ memset(result, 0, sizeof(result));
+ qpci_memread(test.dev, test.bar1, rx_offset, result, sizeof(rx_frame));
+ g_assert_cmpmem(result, sizeof(rx_frame), rx_frame, sizeof(rx_frame));
+
+ qpci_io_writel(test.dev, test.bar0, CNUAS_GPU_REG_RX_CONSUME, 1);
+ g_assert_cmphex(qpci_io_readl(test.dev, test.bar0,
+ CNUAS_GPU_REG_LINK_STATUS), ==, 1);
+
+ close(connection);
+ close(listener);
+ unlink(path);
+ cnuas_gpu_test_stop(&test);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ qtest_add_func("/cnuasgpu/pci-config", cnuas_gpu_test_pci_config);
+ qtest_add_func("/cnuasgpu/registers", cnuas_gpu_test_registers);
+ qtest_add_func("/cnuasgpu/memory-and-reset",
+ cnuas_gpu_test_memory_and_reset);
+ qtest_add_func("/cnuasgpu/transport", cnuas_gpu_test_transport);
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 84d2fa702a..719bb107ca 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -60,6 +60,7 @@ qtests_i386 = \
(slirp.found() ? ['pxe-test'] : []) + \
qtests_filter + \
(config_all_devices.has_key('CONFIG_CNUAS_VNIC') ? ['cnuas-vnic-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_CNUAS_GPU') ? ['cnuasgpu-test'] : []) + \
(config_all_devices.has_key('CONFIG_ACPI_VMGENID') ? ['vmgenid-test'] : []) + \
(config_all_devices.has_key('CONFIG_AHCI_ICH9') and have_tools ? ['ahci-test'] : []) + \
(config_all_devices.has_key('CONFIG_AHCI_ICH9') ? ['tco-test'] : []) + \
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v3 0/6] hw: Add Cnuas virtual devices
2026-09-29 20:23 [PATCH v3 0/6] hw: Add Cnuas virtual devices Weqaar Janjua
` (5 preceding siblings ...)
2026-09-29 20:23 ` [PATCH v3 6/6] tests/qtest: Add CnuasGPU tests Weqaar Janjua
@ 2026-10-16 15:32 ` Weqaar Janjua
2026-10-27 8:07 ` Weqaar Janjua
6 siblings, 1 reply; 15+ messages in thread
From: Weqaar Janjua @ 2026-10-16 15:32 UTC (permalink / raw)
To: qemu-devel
Cc: kraxel, peter.maydell, mst, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier
Hi,
A gentle ping on the series, patches 1 and 4 have Gerd's Reviewed-by;
patches 2, 3, 5 and 6 are still awaiting review. Could someone please have a look?
https://lore.kernel.org/qemu-devel/20260929202358.725310-1-weqaar.janjua@packetfive.com/
Thanks, appreciated!
Weqaar
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v3 0/6] hw: Add Cnuas virtual devices
2026-10-16 15:32 ` [PATCH v3 0/6] hw: Add Cnuas virtual devices Weqaar Janjua
@ 2026-10-27 8:07 ` Weqaar Janjua
2026-10-27 10:46 ` Michael S. Tsirkin
0 siblings, 1 reply; 15+ messages in thread
From: Weqaar Janjua @ 2026-10-27 8:07 UTC (permalink / raw)
To: qemu-devel
Cc: Weqaar Janjua, kraxel, peter.maydell, mst, pierrick.bouvier,
jasowangio, pbonzini, farosas, lvivier, marcel.apfelbaum
Hi,
Ping.
Patches 1 and 4 (PCI IDs) have Gerd's Reviewed-by. Patches 2, 3, 5
and 6 (CnuasNIC/CnuasGPU/dev models/qtests) are pending review.
https://lore.kernel.org/qemu-devel/20260929202358.725310-1-weqaar.janjua@packetfive.com/
@Michael, @Jason:
Would one of you please be willing to take this series through your
tree once it is reviewed, or could you suggest who should? I am happy
to rework anything needed, and I understand this may now target the
next release given the upcoming soft freeze.
Thanks, appreciated!
Weqaar
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3 0/6] hw: Add Cnuas virtual devices
2026-10-27 8:07 ` Weqaar Janjua
@ 2026-10-27 10:46 ` Michael S. Tsirkin
2026-10-29 11:57 ` Weqaar Janjua
0 siblings, 1 reply; 15+ messages in thread
From: Michael S. Tsirkin @ 2026-10-27 10:46 UTC (permalink / raw)
To: Weqaar Janjua
Cc: qemu-devel, kraxel, peter.maydell, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier, marcel.apfelbaum
On Tue, Oct 27, 2026 at 08:07:44AM +0000, Weqaar Janjua wrote:
> Hi,
>
> Ping.
>
> Patches 1 and 4 (PCI IDs) have Gerd's Reviewed-by. Patches 2, 3, 5
> and 6 (CnuasNIC/CnuasGPU/dev models/qtests) are pending review.
>
> https://lore.kernel.org/qemu-devel/20260929202358.725310-1-weqaar.janjua@packetfive.com/
>
> @Michael, @Jason:
> Would one of you please be willing to take this series through your
> tree once it is reviewed, or could you suggest who should? I am happy
> to rework anything needed, and I understand this may now target the
> next release given the upcoming soft freeze.
>
> Thanks, appreciated!
>
> Weqaar
Thanks for the patches!
Sorry I can merge if someone reviews but I just can't take on reviewing
a new device right now, I'm buried under AI security reports as it is.
I appreciate that you listed yourself as the sole maintainer but you are
a first time contributor.
Maybe Jason can review. Try poking other net device maintainers.
Maybe rdma migration guys? if this is useful to test
qemu rdma migration then suddenly you have users within the community.
Indeed, next release.
Thanks!
--
MST
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3 0/6] hw: Add Cnuas virtual devices
2026-10-27 10:46 ` Michael S. Tsirkin
@ 2026-10-29 11:57 ` Weqaar Janjua
2026-10-29 17:50 ` Michael S. Tsirkin
0 siblings, 1 reply; 15+ messages in thread
From: Weqaar Janjua @ 2026-10-29 11:57 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Weqaar Janjua, qemu-devel, kraxel, peter.maydell,
pierrick.bouvier, jasowangio, pbonzini, farosas, lvivier,
marcel.apfelbaum, peterx, lizhijian, odaki
On Tue, Oct 27, 2026 at 06:46:49AM -0400, Michael S. Tsirkin wrote:
> Sorry I can merge if someone reviews but I just can't take on reviewing
> a new device right now, I'm buried under AI security reports as it is.
Thanks Michael, understood, and thank you for offering to merge once
it is reviewed.
> I appreciate that you listed yourself as the sole maintainer but you are
> a first time contributor.
Fair point. I am happy to be listed as a reviewer instead, or alongside
an existing maintainer if someone in the net or migration area is
willing. I will keep supporting the code either way.
> Maybe Jason can review. Try poking other net device maintainers.
> Maybe rdma migration guys? if this is useful to test
> qemu rdma migration then suddenly you have users within the community.
Thanks for the suggestion, I tried it. Setup: two Linux guests on one
host, each with a CnuasNIC (1b36:0017 from this series) attached to a
shared user-space switch, using the in-kernel RDMA stack over RoCEv2.
- rping over RDMA CM, 1000 iterations per size, data verified with -V
(rping -s/-c -a <ip> -C 1000 -S <size> -V), both sides exit 0:
size wall time per iteration RDMA READ+WRITE data
64 B 1.218 s 1218 us 0.8 Mb/s
4096 B 1.172 s 1172 us 55.9 Mb/s
32768 B 1.857 s 1857 us 282.4 Mb/s
Wall time is measured on the client and includes connect and
disconnect.
- perftest over RDMA CM (ib_send_bw -R, ib_read_lat -R,
ib_write_lat -R) passes.
- QEMU RDMA migration of a nested TCG guest with 512 MiB of RAM filled
with random data, source paused, migrate rdma:<ip>:<port>. With QEMU
master (29b1dc631d) it completed in 6.4 s at about 675 Mbps with
269 ms downtime, and a pmemsave of guest RAM is identical on source
and destination.
So CnuasNIC gives another way to exercise migration/rdma.c on a single
host without RDMA hardware. The guest driver changes needed for RDMA CM
will be part of our Linux driver submission (blocked on PCI ID patches 1
and 4), and I can share the full setup if anyone wants to reproduce it.
Jason, Peter, Zhijian, Akihiko: would one of you be able to review
patches 2 and 3 (the CnuasNIC device model and its qtest)?
> Indeed, next release.
Agreed for the device models.
One request on the PCI IDs: patches 1 and 4 only reserve 1b36:0017 and
1b36:0018, and both have Gerd's Reviewed-by. Our Linux driver and
rdma-core provider submissions depend on these IDs, so the device models
waiting for the next release also holds up that work. Would it be
acceptable to merge patches 1 and 4 ahead of the device models, for
11.2 if there is still time before the soft freeze? If it is easier, I
can send them as a separate two-patch series rebased on master.
Thanks,
Weqaar
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v3 0/6] hw: Add Cnuas virtual devices
2026-10-29 11:57 ` Weqaar Janjua
@ 2026-10-29 17:50 ` Michael S. Tsirkin
2026-10-29 19:42 ` Weqaar Janjua
0 siblings, 1 reply; 15+ messages in thread
From: Michael S. Tsirkin @ 2026-10-29 17:50 UTC (permalink / raw)
To: Weqaar Janjua
Cc: qemu-devel, kraxel, peter.maydell, pierrick.bouvier, jasowangio,
pbonzini, farosas, lvivier, marcel.apfelbaum, peterx, lizhijian,
odaki
On Thu, Oct 29, 2026 at 11:57:06AM +0000, Weqaar Janjua wrote:
> On Tue, Oct 27, 2026 at 06:46:49AM -0400, Michael S. Tsirkin wrote:
> > Sorry I can merge if someone reviews but I just can't take on reviewing
> > a new device right now, I'm buried under AI security reports as it is.
>
> Thanks Michael, understood, and thank you for offering to merge once
> it is reviewed.
>
> > I appreciate that you listed yourself as the sole maintainer but you are
> > a first time contributor.
>
> Fair point. I am happy to be listed as a reviewer instead, or alongside
> an existing maintainer if someone in the net or migration area is
> willing. I will keep supporting the code either way.
>
> > Maybe Jason can review. Try poking other net device maintainers.
> > Maybe rdma migration guys? if this is useful to test
> > qemu rdma migration then suddenly you have users within the community.
>
> Thanks for the suggestion, I tried it. Setup: two Linux guests on one
> host, each with a CnuasNIC (1b36:0017 from this series) attached to a
> shared user-space switch, using the in-kernel RDMA stack over RoCEv2.
>
> - rping over RDMA CM, 1000 iterations per size, data verified with -V
> (rping -s/-c -a <ip> -C 1000 -S <size> -V), both sides exit 0:
>
> size wall time per iteration RDMA READ+WRITE data
> 64 B 1.218 s 1218 us 0.8 Mb/s
> 4096 B 1.172 s 1172 us 55.9 Mb/s
> 32768 B 1.857 s 1857 us 282.4 Mb/s
>
> Wall time is measured on the client and includes connect and
> disconnect.
> - perftest over RDMA CM (ib_send_bw -R, ib_read_lat -R,
> ib_write_lat -R) passes.
> - QEMU RDMA migration of a nested TCG guest with 512 MiB of RAM filled
> with random data, source paused, migrate rdma:<ip>:<port>. With QEMU
> master (29b1dc631d) it completed in 6.4 s at about 675 Mbps with
> 269 ms downtime, and a pmemsave of guest RAM is identical on source
> and destination.
>
> So CnuasNIC gives another way to exercise migration/rdma.c on a single
> host without RDMA hardware. The guest driver changes needed for RDMA CM
> will be part of our Linux driver submission (blocked on PCI ID patches 1
> and 4), and I can share the full setup if anyone wants to reproduce it.
>
> Jason, Peter, Zhijian, Akihiko: would one of you be able to review
> patches 2 and 3 (the CnuasNIC device model and its qtest)?
>
> > Indeed, next release.
>
> Agreed for the device models.
>
> One request on the PCI IDs: patches 1 and 4 only reserve 1b36:0017 and
> 1b36:0018, and both have Gerd's Reviewed-by. Our Linux driver and
> rdma-core provider submissions depend on these IDs, so the device models
> waiting for the next release also holds up that work. Would it be
> acceptable to merge patches 1 and 4 ahead of the device models, for
> 11.2 if there is still time before the soft freeze? If it is easier, I
> can send them as a separate two-patch series rebased on master.
>
> Thanks,
>
> Weqaar
Hmm wait a second. I don't understand. Isn't it a physical device you
are emulating? Why is it using the Red Hat IDs?
--
MST
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3 0/6] hw: Add Cnuas virtual devices
2026-10-29 17:50 ` Michael S. Tsirkin
@ 2026-10-29 19:42 ` Weqaar Janjua
0 siblings, 0 replies; 15+ messages in thread
From: Weqaar Janjua @ 2026-10-29 19:42 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Weqaar Janjua, qemu-devel, kraxel, peter.maydell,
pierrick.bouvier, jasowangio, pbonzini, farosas, lvivier,
marcel.apfelbaum, peterx, lizhijian, odaki
On Thu, Oct 29, 2026 at 01:50:11PM -0400, Michael S. Tsirkin wrote:
> Hmm wait a second. I don't understand. Isn't it a physical device you
> are emulating? Why is it using the Red Hat IDs?
No, it is not a physical device. There is no CnuasNIC or CnuasGPU
hardware; both exist only as virtual devices, the QEMU device models
in this series. Think of CnuasNIC as veth or rxe for RDMA, but
presented to the guest as a PCI device so the full driver, verbs and
RDMA CM path runs unmodified. The Cnuas project (cnuas.io) emulates
the rest of the RDMA subsystem around it, including a RoCEv2 and
InfiniBand switch. CnuasGPU is the same idea for an accelerator.
The register interfaces are defined by the models themselves and
documented in docs/specs/cnuas-vnic.rst (patch 2) and
docs/specs/cnuasgpu.rst (patch 5), with more detail in the
datasheets at:
https://cnuas.io/cnuas/docs/datasheets/CnuasNIC_Datasheet/
https://cnuas.io/cnuas/docs/datasheets/CnuasGPU_Datasheet/
So this is the same situation as other QEMU-specific devices in the
1b36 range, for example pvpanic-pci (1b36:0011) and the emulated UFS
host controller (1b36:0013), which also have upstream Linux drivers.
That is why I asked Gerd for IDs from the QEMU range. If we were to
build hardware, it would use our own PCI vendor ID, not 1b36.
The "RDMA network adapter" wording may have suggested otherwise. It is
an RoCEv2 adapter in the sense that a Linux guest sees a normal RDMA
device, but the wire side is a socket to a user-space switch process,
not a real NIC. I can make "virtual" more explicit in the commit
messages and docs if that helps.
Thanks,
Weqaar
^ permalink raw reply [flat|nested] 15+ messages in thread