Cover PCI identity, registers, device memory, reset, and link transport. Signed-off-by: Weqaar Janjua <[email protected]> --- 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 d11ea29ff9..bb38cd1b4a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2780,6 +2780,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 <[email protected]> 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
