diff options
| author | Open-IOV <dev@open-iov.org> | 2026-09-23 05:44:08 -0400 |
|---|---|---|
| committer | Open-IOV <dev@open-iov.org> | 2026-09-23 05:46:43 -0400 |
| commit | 2302fc09080bad51c73f293294e19fd4db464eb0 (patch) | |
| tree | c722d186347fdef5a34131e9e143b1549b1daf8e /userspace/tests | |
| download | open-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/Makefile | 22 | ||||
| -rw-r--r-- | userspace/tests/test_guest_init.c | 176 | ||||
| -rw-r--r-- | userspace/tests/test_host_init.c | 68 | ||||
| -rw-r--r-- | userspace/tests/test_vgpu_userspace.c | 283 |
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 |
