All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/6] hw: Add Cnuas virtual devices
@ 2026-09-29 20:23 Weqaar Janjua
  2026-09-29 20:23 ` [PATCH v3 1/6] pci: Add device ID for CnuasNIC Weqaar Janjua
                   ` (6 more replies)
  0 siblings, 7 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

This series adds PCI IDs and QEMU models for the Cnuas virtual network
adapter and CnuasGPU AI/ML accelerator.

CnuasNIC provides guest DMA and Linux SOCK_SEQPACKET transport. CnuasGPU
provides MMIO control, device memory, and optional link transport. Both
models include reset and migration state, MSI and INTx support,
documentation, and functional qtests.

Changes since v2:

- use 1b36:0017 (CnuasNIC) and 1b36:0018 (CnuasGPU) to avoid PCI ID
  conflicts
- add PCI-ID documentation links along with their target document additions

The complete QEMU device implementation is included in this patch series.

Regards,

Weqaar Janjua
Packet Five Networks Ltd.
Dublin, Ireland
https://www.packetfive.com

Weqaar Janjua (6):
  pci: Add device ID for CnuasNIC
  hw/net: Add Cnuas virtual network adapter
  tests/qtest: Add Cnuas virtual network adapter tests
  pci: Add device ID for CnuasGPU
  hw/misc: Add CnuasGPU virtual accelerator
  tests/qtest: Add CnuasGPU tests

 MAINTAINERS                   |  10 +
 docs/specs/cnuas-vnic.rst     |  78 +++++
 docs/specs/cnuasgpu.rst       |  91 ++++++
 docs/specs/index.rst          |   2 +
 docs/specs/pci-ids.rst        |   4 +
 hw/misc/Kconfig               |   5 +
 hw/misc/cnuasgpu.c            | 557 ++++++++++++++++++++++++++++++++++
 hw/misc/meson.build           |   1 +
 hw/net/Kconfig                |   5 +
 hw/net/cnuas_vnic.c           | 508 +++++++++++++++++++++++++++++++
 hw/net/meson.build            |   1 +
 include/hw/pci/pci.h          |   2 +
 tests/qtest/cnuas-vnic-test.c | 233 ++++++++++++++
 tests/qtest/cnuasgpu-test.c   | 274 +++++++++++++++++
 tests/qtest/meson.build       |   2 +
 15 files changed, 1773 insertions(+)
 create mode 100644 docs/specs/cnuas-vnic.rst
 create mode 100644 docs/specs/cnuasgpu.rst
 create mode 100644 hw/misc/cnuasgpu.c
 create mode 100644 hw/net/cnuas_vnic.c
 create mode 100644 tests/qtest/cnuas-vnic-test.c
 create mode 100644 tests/qtest/cnuasgpu-test.c

--
2.43.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [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

* [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

* [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 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

* 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

* 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

end of thread, other threads:[~2026-10-29 19:43 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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-30  7:00   ` Gerd Hoffmann
2026-09-29 20:23 ` [PATCH v3 2/6] hw/net: Add Cnuas virtual network adapter Weqaar Janjua
2026-09-29 20:23 ` [PATCH v3 3/6] tests/qtest: Add Cnuas virtual network adapter tests Weqaar Janjua
2026-09-29 20:23 ` [PATCH v3 4/6] pci: Add device ID for CnuasGPU 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
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
2026-10-27  8:07   ` Weqaar Janjua
2026-10-27 10:46     ` Michael S. Tsirkin
2026-10-29 11:57       ` Weqaar Janjua
2026-10-29 17:50         ` Michael S. Tsirkin
2026-10-29 19:42           ` Weqaar Janjua

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.