diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-13 18:36:21 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-13 18:36:21 -0400 |
| commit | 00e54c13c7c9030c4d5f7151467f2d85a3730e29 (patch) | |
| tree | 17a45239df8d2e4912ff3697ed06e85a10b969de /userspace/tests/test_vgpu_userspace.c | |
| download | open-iov-00e54c13c7c9030c4d5f7151467f2d85a3730e29.tar.gz open-iov-00e54c13c7c9030c4d5f7151467f2d85a3730e29.zip | |
Initial commit: Open-IOV vGPU-IO complete stack
- Firmware decompilation: GSP 570.144W (28.5MB ELF, RV64IMAC), 132 decompiled C files
- Kernel modules: nvidia.ko (34MB, OpenRM), open-iov-vfio.ko (2.8MB, VFIO mdev), gsp-vgpu.ko (1.8MB, guest driver)
- Userspace: libiov.so/libvgpu.so (76KB), example_vgpu (28KB), rpc_emulator.c
- PCI config space emulation (256B with PM, MSI, MSI-X, PCIe caps)
- 12 RPC types fully mapped from firmware decompilation
- BAR trap, WPR, scheduling, notification, migration emulation
- Helper scripts: load_vgpu_stack, create_mdev, launch_qemu_vgpu, test_vgpu_stack, setup_guest_driver, unload_vgpu_stack
- Architecture documentation
- Wiki: 10 technical documents
- Git LFS for large binaries (.ko, .bin, .iso, .img, .o, ghidra repos)
- nvidia-open-gpu-kernel-modules as submodule
Diffstat (limited to 'userspace/tests/test_vgpu_userspace.c')
| -rw-r--r-- | userspace/tests/test_vgpu_userspace.c | 283 |
1 files changed, 283 insertions, 0 deletions
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 |
