summaryrefslogtreecommitdiffstats
path: root/userspace/tests
diff options
context:
space:
mode:
authorOpen-IOV <dev@open-iov.org>2026-09-23 05:44:08 -0400
committerOpen-IOV <dev@open-iov.org>2026-09-23 05:46:43 -0400
commit2302fc09080bad51c73f293294e19fd4db464eb0 (patch)
treec722d186347fdef5a34131e9e143b1549b1daf8e /userspace/tests
downloadopen-iov-2302fc09080bad51c73f293294e19fd4db464eb0.tar.gz
open-iov-2302fc09080bad51c73f293294e19fd4db464eb0.zip
Open-IOV: initial public release
Open-IOV virtualizes NVIDIA GPUs so that unmodified guest drivers run with real GPU functionality - CUDA first, 3D and video to follow - on unmodified hosts, without a proprietary vGPU license and without patching any driver, firmware or hypervisor. Core principle: emulate the interface, pass through the work. kernel/ open-iov-vfio.ko - host VFIO mdev driver: virtual GPU device (PCI config space, BAR shadows, MSI-X), GSP boot emulation, GSP-RM message-queue/RPC emulation, memory model (GPU-VA to guest-PA discovery) and the semaphore-completion service that advances guest UVM progress. userspace/ libiov.so management API and examples. tools/ guest diagnostics, firmware analysis wrappers, RM probes. scripts/ stack manager, VM harness, build/load/test automation. config/ hardware/version profiles (the portability surface). docs/ documentation tree - start at docs/README.md. wiki/ long-form firmware and protocol deep dives. Status: initialization works end to end for a stock 570.144 Linux guest on Turing (TU104): driver probe, nvidia-smi, the full GSP-RM control battery, object creation and the early CUDA/UVM start-up path. GPU work forwarding - the step that makes CUDA kernels execute on real hardware - is the active work item; see docs/04-implementation/01-known-knowns-and-unknowns.md. License: BSD-3-Clause for the project; GPL-2.0 for kernel modules that link GPL-only kernel interfaces. See LICENSE and docs/09-reference/legal-and-licensing.md.
Diffstat (limited to 'userspace/tests')
-rw-r--r--userspace/tests/Makefile22
-rw-r--r--userspace/tests/test_guest_init.c176
-rw-r--r--userspace/tests/test_host_init.c68
-rw-r--r--userspace/tests/test_vgpu_userspace.c283
4 files changed, 549 insertions, 0 deletions
diff --git a/userspace/tests/Makefile b/userspace/tests/Makefile
new file mode 100644
index 0000000..3be451d
--- /dev/null
+++ b/userspace/tests/Makefile
@@ -0,0 +1,22 @@
+# Userspace Test Makefile
+
+CC = gcc
+CFLAGS = -Wall -Wextra -O2 -std=gnu11 -I../../kernel/include
+LDFLAGS =
+
+TARGET = test_vgpu_userspace
+SRC = test_vgpu_userspace.c
+
+all: $(TARGET)
+
+$(TARGET): $(SRC)
+ $(CC) $(CFLAGS) -o $(TARGET) $(SRC) $(LDFLAGS)
+
+clean:
+ rm -f $(TARGET)
+
+test: $(TARGET)
+ @echo "Running userspace test (requires driver loaded)..."
+ sudo ./$(TARGET)
+
+.PHONY: all clean test \ No newline at end of file
diff --git a/userspace/tests/test_guest_init.c b/userspace/tests/test_guest_init.c
new file mode 100644
index 0000000..135c16f
--- /dev/null
+++ b/userspace/tests/test_guest_init.c
@@ -0,0 +1,176 @@
+/*
+ * Test for guest vGPU driver initialization
+ * This test verifies that the guest vGPU driver can initialize properly
+ * when passed through to a VM via VFIO/mdev
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <linux/vfio.h>
+#include <linux/mdev.h>
+#include <errno.h>
+
+#define NV_VGPU_IOCTL_MAGIC 'N'
+#define NV_VGPU_IOCTL_GET_INFO \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x01, struct nv_vgpu_ioctl_get_info)
+#define NV_VGPU_IOCTL_MAP_BAR \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x02, struct nv_vgpu_ioctl_map_bar)
+#define NV_VGPU_IOCTL_GSP_RPC \
+ _IOWR(NV_VGPU_IOCTL_MAGIC, 0x06, struct nv_vgpu_ioctl_gsp_rpc)
+
+struct nv_vgpu_ioctl_get_info {
+ uint32_t vgpu_type;
+ uint32_t vgpu_id;
+ uint32_t gpu_instance_id;
+ uint32_t compute_instance_id;
+ uint32_t state;
+ uint64_t fb_base;
+ uint64_t fb_size;
+ uint64_t bar0_config_base;
+ uint32_t num_bars;
+ struct {
+ uint32_t index;
+ uint64_t gpa;
+ uint64_t size;
+ uint64_t iova;
+ bool mapped;
+ bool trapped;
+ } bars[8];
+};
+
+struct nv_vgpu_ioctl_map_bar {
+ uint32_t bar_index;
+ uint64_t gpa;
+ uint64_t size;
+ uint64_t iova;
+ uint32_t flags;
+};
+
+struct nv_vgpu_ioctl_gsp_rpc {
+ uint32_t type;
+ uint32_t flags;
+ uint64_t data_ptr;
+ uint32_t data_size;
+ uint32_t result;
+};
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ int vfio_fd = -1;
+ int mdev_fd = -1;
+ char *mdev_path = NULL;
+ struct nv_vgpu_ioctl_get_info info = {0};
+ struct nv_vgpu_ioctl_map_bar map_bar = {0};
+ struct vfio_device_info dev_info = {0};
+
+ printf("Testing guest vGPU driver initialization...\n");
+
+ if (argc < 2) {
+ printf("Usage: %s <mdev_uuid>\n", argv[0]);
+ printf("Example: %s 00000000-0000-0000-0000-000000000000\n", argv[0]);
+ return 1;
+ }
+
+ mdev_path = argv[1];
+
+ vfio_fd = open("/dev/vfio/vfio", O_RDWR);
+ if (vfio_fd < 0) {
+ printf("FAIL: Cannot open /dev/vfio/vfio: %s\n", strerror(errno));
+ return 1;
+ }
+
+ dev_info.argsz = sizeof(dev_info);
+ if (ioctl(vfio_fd, VFIO_GET_API_VERSION, &dev_info) < 0) {
+ printf("FAIL: VFIO_GET_API_VERSION failed: %s\n", strerror(errno));
+ ret = 1;
+ goto cleanup;
+ }
+
+ if (dev_info.version != VFIO_API_VERSION) {
+ printf("FAIL: VFIO API version mismatch\n");
+ ret = 1;
+ goto cleanup;
+ }
+
+ printf("PASS: VFIO API version %d\n", dev_info.version);
+
+ char mdev_dev_path[256];
+ snprintf(mdev_dev_path, sizeof(mdev_dev_path), "/dev/vfio/%s", mdev_path);
+
+ mdev_fd = open(mdev_dev_path, O_RDWR);
+ if (mdev_fd < 0) {
+ printf("FAIL: Cannot open mdev device %s: %s\n", mdev_dev_path, strerror(errno));
+ ret = 1;
+ goto cleanup;
+ }
+
+ printf("PASS: Opened mdev device %s\n", mdev_dev_path);
+
+ dev_info.argsz = sizeof(dev_info);
+ if (ioctl(mdev_fd, VFIO_DEVICE_GET_INFO, &dev_info) < 0) {
+ printf("FAIL: VFIO_DEVICE_GET_INFO failed: %s\n", strerror(errno));
+ ret = 1;
+ goto cleanup;
+ }
+
+ printf("PASS: Device info - flags: 0x%x, num_regions: %d, num_irqs: %d\n",
+ dev_info.flags, dev_info.num_regions, dev_info.num_irqs);
+
+ if (ioctl(mdev_fd, NV_VGPU_IOCTL_GET_INFO, &info) < 0) {
+ printf("FAIL: NV_VGPU_IOCTL_GET_INFO failed: %s\n", strerror(errno));
+ ret = 1;
+ goto cleanup;
+ }
+
+ printf("PASS: vGPU info - type: %d, id: %d, state: %d\n",
+ info.vgpu_type, info.vgpu_id, info.state);
+ printf(" fb_base: 0x%lx, fb_size: 0x%lx\n", info.fb_base, info.fb_size);
+ printf(" bar0_config_base: 0x%lx\n", info.bar0_config_base);
+ printf(" num_bars: %d\n", info.num_bars);
+
+ for (int i = 0; i < info.num_bars; i++) {
+ printf(" BAR%d: gpa=0x%lx, size=0x%lx, iova=0x%lx, mapped=%d, trapped=%d\n",
+ info.bars[i].index, info.bars[i].gpa, info.bars[i].size,
+ info.bars[i].iova, info.bars[i].mapped, info.bars[i].trapped);
+ }
+
+ if (info.num_bars > 0) {
+ map_bar.bar_index = 0;
+ map_bar.gpa = info.bars[0].gpa;
+ map_bar.size = info.bars[0].size;
+ map_bar.flags = 0x1;
+
+ if (ioctl(mdev_fd, NV_VGPU_IOCTL_MAP_BAR, &map_bar) < 0) {
+ printf("FAIL: NV_VGPU_IOCTL_MAP_BAR failed: %s\n", strerror(errno));
+ ret = 1;
+ goto cleanup;
+ }
+
+ printf("PASS: Mapped BAR0 at IOVA 0x%lx\n", map_bar.iova);
+ }
+
+ struct nv_vgpu_ioctl_gsp_rpc rpc = {0};
+ rpc.type = 0x01;
+ rpc.data_size = 0;
+
+ if (ioctl(mdev_fd, NV_VGPU_IOCTL_GSP_RPC, &rpc) < 0) {
+ printf("WARN: NV_VGPU_IOCTL_GSP_RPC failed: %s (may not be implemented yet)\n", strerror(errno));
+ } else {
+ printf("PASS: GSP RPC test completed\n");
+ }
+
+ printf("\nAll guest initialization tests PASSED\n");
+
+cleanup:
+ if (mdev_fd >= 0)
+ close(mdev_fd);
+ if (vfio_fd >= 0)
+ close(vfio_fd);
+
+ return ret;
+} \ No newline at end of file
diff --git a/userspace/tests/test_host_init.c b/userspace/tests/test_host_init.c
new file mode 100644
index 0000000..a08fe33
--- /dev/null
+++ b/userspace/tests/test_host_init.c
@@ -0,0 +1,68 @@
+/*
+ * Test for host vGPU initialization
+ */
+#include <gvm/apis.h>
+#include <gvm/nvidia/manager.h>
+#include <gvm/nvidia/creator.h>
+#include <gvm/nvidia/open/signature.h>
+#include <utils/configs.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ enum SupportedGpus selected_gpu = Nvidia;
+ const struct ManagerAPI *api = MANAGER_APIS[selected_gpu];
+ struct NvManager *mgr = NULL;
+
+ printf("Testing host vGPU initialization...\n");
+
+ mgr = calloc(1, sizeof(struct NvManager));
+ if (!mgr) {
+ printf("Failed to allocate manager\n");
+ return 1;
+ }
+
+ mgr->ignore_version_check = 1;
+ mgr->create_signature = nvidia_open_create_signature;
+ mgr->use_vmiop = 1;
+ mgr->vmiop_plugin = "/usr/lib/x86_64-linux-gnu/libnvidia-vgpu.so";
+
+ if (api->init(mgr)) {
+ printf("FAIL: Manager initialization failed\n");
+ ret = 1;
+ goto cleanup;
+ }
+
+ printf("PASS: Manager initialized successfully\n");
+
+ if (!api->running(mgr)) {
+ printf("FAIL: Manager not running\n");
+ ret = 1;
+ goto cleanup;
+ }
+
+ printf("PASS: Manager is running\n");
+
+ if (mgr->vmiop) {
+ printf("PASS: VMIOP initialized\n");
+ } else {
+ printf("FAIL: VMIOP not initialized\n");
+ ret = 1;
+ }
+
+cleanup:
+ if (mgr) {
+ api->stop(mgr);
+ free(mgr);
+ }
+
+ if (ret == 0)
+ printf("\nAll host initialization tests PASSED\n");
+ else
+ printf("\nSome host initialization tests FAILED\n");
+
+ return ret;
+} \ No newline at end of file
diff --git a/userspace/tests/test_vgpu_userspace.c b/userspace/tests/test_vgpu_userspace.c
new file mode 100644
index 0000000..cd22111
--- /dev/null
+++ b/userspace/tests/test_vgpu_userspace.c
@@ -0,0 +1,283 @@
+/*
+ * GSP vGPU-IO Userspace Test
+ * Tests VFIO interface and driver functionality from userspace
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <linux/vfio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#define GSP_VGPU_DEVICE "/dev/vfio/gsp-vgpu"
+
+#define TEST_PASS 0
+#define TEST_FAIL 1
+
+static int test_count = 0;
+static int pass_count = 0;
+static int fail_count = 0;
+
+#define TEST_ASSERT(cond, msg) \
+ do { \
+ test_count++; \
+ if (cond) { \
+ pass_count++; \
+ printf("PASS: %s\n", msg); \
+ } else { \
+ fail_count++; \
+ fprintf(stderr, "FAIL: %s\n", msg); \
+ } \
+ } while (0)
+
+#define TEST_ASSERT_EQ(a, b, msg) \
+ TEST_ASSERT((a) == (b), msg " (expected " #b ", got " #a ")")
+
+/* IOCTL definitions matching kernel driver */
+#define GSP_IOCTL_MAGIC 'G'
+#define GSP_IOCTL_CREATE_VF _IOW(GSP_IOCTL_MAGIC, 0x01, struct gsp_vf_config)
+#define GSP_IOCTL_DESTROY_VF _IOW(GSP_IOCTL_MAGIC, 0x02, uint32_t)
+#define GSP_IOCTL_ALLOC _IOWR(GSP_IOCTL_MAGIC, 0x10, struct gsp_alloc_args)
+#define GSP_IOCTL_FREE _IOW(GSP_IOCTL_MAGIC, 0x11, struct gsp_free_args)
+#define GSP_IOCTL_MAP _IOWR(GSP_IOCTL_MAGIC, 0x12, struct gsp_map_args)
+#define GSP_IOCTL_UNMAP _IOW(GSP_IOCTL_MAGIC, 0x13, struct gsp_unmap_args)
+#define GSP_IOCTL_PIN _IOWR(GSP_IOCTL_MAGIC, 0x14, struct gsp_pin_args)
+#define GSP_IOCTL_UNPIN _IOW(GSP_IOCTL_MAGIC, 0x15, struct gsp_unpin_args)
+#define GSP_IOCTL_BAR_TRAP _IOWR(GSP_IOCTL_MAGIC, 0x16, struct gsp_bar_trap_args)
+#define GSP_IOCTL_MIGRATE _IOWR(GSP_IOCTL_MAGIC, 0x17, struct gsp_migrate_args)
+#define GSP_IOCTL_WPR_PROTECT _IOWR(GSP_IOCTL_MAGIC, 0x18, struct gsp_wpr_args)
+#define GSP_IOCTL_WPR_UNPROTECT _IOW(GSP_IOCTL_MAGIC, 0x19, struct gsp_wpr_args)
+
+struct gsp_vf_config {
+ uint32_t id;
+ uint64_t bar1_offset;
+ uint64_t bar1_size;
+ uint64_t pt_base;
+ uint64_t pt_size;
+ uint64_t cmd_base;
+ uint64_t cmd_size;
+ uint32_t max_memory_mb;
+ uint32_t num_engines;
+};
+
+struct gsp_alloc_args {
+ uint32_t vf_id;
+ uint64_t size;
+ uint32_t type;
+ uint64_t alignment;
+ uint64_t va_hint;
+ uint32_t flags;
+ uint32_t pid;
+ uint64_t handle;
+ uint64_t va;
+ uint64_t actual_size;
+};
+
+struct gsp_free_args {
+ uint32_t vf_id;
+ uint64_t handle;
+};
+
+struct gsp_map_args {
+ uint32_t vf_id;
+ uint64_t handle;
+ uint64_t gpu_va;
+ uint64_t size;
+ uint32_t prot;
+ uint32_t flags;
+ uint64_t offset;
+ uint64_t mapped_va;
+};
+
+struct gsp_unmap_args {
+ uint32_t vf_id;
+ uint64_t handle;
+ uint64_t gpu_va;
+ uint64_t size;
+};
+
+struct gsp_pin_args {
+ uint32_t vf_id;
+ uint64_t handle;
+ uint64_t offset;
+ uint64_t size;
+ uint32_t flags;
+ uint64_t dma_addr;
+ uint32_t num_pages;
+};
+
+struct gsp_unpin_args {
+ uint32_t vf_id;
+ uint64_t handle;
+ uint64_t offset;
+ uint64_t size;
+};
+
+struct gsp_bar_trap_args {
+ uint32_t vf_id;
+ uint32_t bar_index;
+ uint32_t trap_type;
+ uint64_t offset;
+ uint64_t value;
+ uint32_t size;
+ uint64_t ret_value;
+};
+
+struct gsp_migrate_args {
+ uint32_t vf_id;
+ uint32_t phase;
+ uint32_t subcmd;
+ uint64_t src_vf;
+ uint64_t dst_vf;
+ uint64_t data_ptr;
+ uint64_t data_size;
+ uint32_t flags;
+ uint64_t token;
+};
+
+struct gsp_wpr_args {
+ uint32_t vf_id;
+ uint64_t base;
+ uint64_t size;
+ uint32_t flags;
+};
+
+int main(int argc, char *argv[])
+{
+ int fd, ret;
+ uint32_t vf_id = 1;
+
+ printf("========================================\n");
+ printf("GSP vGPU-IO Userspace Test Suite\n");
+ printf("========================================\n");
+
+ /* Open device */
+ fd = open(GSP_VGPU_DEVICE, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open %s: %s\n", GSP_VGPU_DEVICE, strerror(errno));
+ fprintf(stderr, "Make sure driver is loaded and VFIO device created\n");
+ return TEST_FAIL;
+ }
+ printf("Opened device: %s (fd=%d)\n", GSP_VGPU_DEVICE, fd);
+
+ /* Test 1: Create VF */
+ {
+ struct gsp_vf_config config = {
+ .id = vf_id,
+ .bar1_offset = 0x10000000,
+ .bar1_size = 0x100000,
+ .pt_base = 0x20000000,
+ .pt_size = 0x100000,
+ .cmd_base = 0x30000000,
+ .cmd_size = 0x10000,
+ .max_memory_mb = 1024,
+ .num_engines = 4,
+ };
+
+ ret = ioctl(fd, GSP_IOCTL_CREATE_VF, &config);
+ TEST_ASSERT(ret == 0, "Create VF");
+ if (ret == 0)
+ printf(" VF %u created successfully\n", vf_id);
+ }
+
+ /* Test 2: Allocate memory */
+ {
+ struct gsp_alloc_args args = {
+ .vf_id = vf_id,
+ .size = 0x10000, // 64KB
+ .type = 0, // VRAM
+ .alignment = 0x1000,
+ .va_hint = 0,
+ .flags = 0,
+ .pid = getpid(),
+ };
+
+ ret = ioctl(fd, GSP_IOCTL_ALLOC, &args);
+ TEST_ASSERT(ret == 0, "Allocate memory");
+ if (ret == 0) {
+ printf(" Allocated: handle=0x%" PRIx64 ", va=0x%" PRIx64 ", size=0x%" PRIx64 "\n",
+ args.handle, args.va, args.actual_size);
+ }
+ }
+
+ /* Test 3: Pin memory */
+ {
+ struct gsp_pin_args args = {
+ .vf_id = vf_id,
+ .handle = 0, // Would use handle from alloc
+ .offset = 0,
+ .size = 0x1000,
+ .flags = 3, // READ | WRITE
+ };
+
+ ret = ioctl(fd, GSP_IOCTL_PIN, &args);
+ TEST_ASSERT(ret == 0 || errno == ENODEV, "Pin memory (may fail without real allocation)");
+ }
+
+ /* Test 4: BAR Trap */
+ {
+ struct gsp_bar_trap_args args = {
+ .vf_id = vf_id,
+ .bar_index = 1,
+ .trap_type = 1, // WRITE
+ .offset = 0x1000,
+ .value = 0xDEADBEEF,
+ .size = 4,
+ };
+
+ ret = ioctl(fd, GSP_IOCTL_BAR_TRAP, &args);
+ TEST_ASSERT(ret == 0 || errno == ENODEV, "BAR trap (may fail without real HW)");
+ }
+
+ /* Test 5: WPR Protect */
+ {
+ struct gsp_wpr_args args = {
+ .vf_id = vf_id,
+ .base = 0x10000000,
+ .size = 0x10000,
+ .flags = 3, // WRITE | READ
+ };
+
+ ret = ioctl(fd, GSP_IOCTL_WPR_PROTECT, &args);
+ TEST_ASSERT(ret == 0 || errno == ENODEV, "WPR protect (may fail without real HW)");
+ }
+
+ /* Test 6: Migration */
+ {
+ struct gsp_migrate_args args = {
+ .vf_id = vf_id,
+ .phase = 1, // PREPARE
+ .subcmd = 3,
+ .src_vf = vf_id,
+ .dst_vf = 2,
+ .flags = 1, // LIVE
+ };
+
+ ret = ioctl(fd, GSP_IOCTL_MIGRATE, &args);
+ TEST_ASSERT(ret == 0 || errno == ENODEV, "Migration (may fail without real HW)");
+ }
+
+ /* Test 7: Destroy VF */
+ {
+ ret = ioctl(fd, GSP_IOCTL_DESTROY_VF, &vf_id);
+ TEST_ASSERT(ret == 0, "Destroy VF");
+ if (ret == 0)
+ printf(" VF %u destroyed successfully\n", vf_id);
+ }
+
+ close(fd);
+
+ printf("========================================\n");
+ printf("Userspace Test Results: %d total, %d passed, %d failed\n",
+ test_count, pass_count, fail_count);
+ printf("========================================\n");
+
+ return (fail_count > 0) ? TEST_FAIL : TEST_PASS;
+} \ No newline at end of file