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 /tools/test/cuda_drv_test.c | |
| 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 'tools/test/cuda_drv_test.c')
| -rw-r--r-- | tools/test/cuda_drv_test.c | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/tools/test/cuda_drv_test.c b/tools/test/cuda_drv_test.c new file mode 100644 index 0000000..6868a2e --- /dev/null +++ b/tools/test/cuda_drv_test.c @@ -0,0 +1,87 @@ +/* cuda_drv_test.c - minimal CUDA driver-API smoke test (dlopen, no toolkit). + * + * Run inside the guest. Exercises the paths CUDA needs beyond NVML: + * cuInit -> cuDeviceGetCount/Get -> cuCtxCreate -> cuMemAlloc -> + * cuMemcpyHtoD/DtoH -> cuMemFree -> cuCtxDestroy + * + * Expected output on success: "ALL OK". + * + * Status (2026-09-23): cuInit returns 3 (CUDA_ERROR_NOT_INITIALIZED) on the + * emulated GPU. strace shows every nvidia.ko ioctl succeeding (RM status 0), + * the driver completing RmInitAdapter and then closing the device, and + * libcuda opening /dev/nvidia-uvm before /dev/nvidia0 - i.e. the next blocker + * is the UVM driver (nvidia-uvm.ko), which needs the GPU MMU/VAS model that + * the GSP-RM emulation does not provide yet. Build in the guest with: + * gcc -O1 -o /tmp/cdt cuda_drv_test.c -ldl + */ +#include <stdio.h> +#include <string.h> +#include <dlfcn.h> +#include <stdint.h> + +typedef int CUresult; +typedef int CUdevice; +typedef void *CUcontext; +typedef uint64_t CUdeviceptr; + +#define CHECK(expr) do { \ + CUresult r = (expr); \ + if (r) { printf("%-24s FAILED rc=%d\n", #expr, r); return 1; } \ + printf("%-24s OK\n", #expr); \ +} while (0) + +int main(void) +{ + void *h = dlopen("libcuda.so.1", RTLD_NOW); + if (!h) { printf("dlopen libcuda.so.1 failed: %s\n", dlerror()); return 1; } + + CUresult (*cuInit)(unsigned int) = dlsym(h, "cuInit"); + CUresult (*cuDeviceGetCount)(int *) = dlsym(h, "cuDeviceGetCount"); + CUresult (*cuDeviceGet)(CUdevice *, int) = dlsym(h, "cuDeviceGet"); + CUresult (*cuCtxCreate)(CUcontext *, unsigned int, CUdevice) = dlsym(h, "cuCtxCreate_v2"); + CUresult (*cuMemAlloc)(CUdeviceptr *, size_t) = dlsym(h, "cuMemAlloc_v2"); + CUresult (*cuMemcpyHtoD)(CUdeviceptr, const void *, size_t) = dlsym(h, "cuMemcpyHtoD_v2"); + CUresult (*cuMemcpyDtoH)(void *, CUdeviceptr, size_t) = dlsym(h, "cuMemcpyDtoH_v2"); + CUresult (*cuMemFree)(CUdeviceptr) = dlsym(h, "cuMemFree_v2"); + CUresult (*cuCtxDestroy)(CUcontext) = dlsym(h, "cuCtxDestroy_v2"); + + if (!cuInit || !cuDeviceGetCount || !cuDeviceGet || !cuCtxCreate || + !cuMemAlloc || !cuMemcpyHtoD || !cuMemcpyDtoH || !cuMemFree || !cuCtxDestroy) { + printf("dlsym failed\n"); + return 1; + } + + int count = 0; + CUdevice dev; + CUcontext ctx = NULL; + CUdeviceptr dptr = 0; + unsigned int buf[64]; + unsigned int back[64]; + size_t i; + + CHECK(cuInit(0)); + CHECK(cuDeviceGetCount(&count)); + printf("device count = %d\n", count); + if (count < 1) return 1; + + CHECK(cuDeviceGet(&dev, 0)); + printf("device 0 = %d\n", dev); + + CHECK(cuCtxCreate(&ctx, 0, dev)); + printf("context = %p\n", ctx); + + CHECK(cuMemAlloc(&dptr, sizeof(buf))); + printf("device ptr = 0x%llx (%zu bytes)\n", (unsigned long long)dptr, sizeof(buf)); + + for (i = 0; i < 64; i++) buf[i] = (unsigned int)(i * 3 + 1); + CHECK(cuMemcpyHtoD(dptr, buf, sizeof(buf))); + memset(back, 0, sizeof(back)); + CHECK(cuMemcpyDtoH(back, dptr, sizeof(buf))); + printf("roundtrip %s (back[7]=%u expected 22)\n", + memcmp(buf, back, sizeof(buf)) ? "MISMATCH" : "match", back[7]); + + CHECK(cuMemFree(dptr)); + CHECK(cuCtxDestroy(ctx)); + printf("ALL OK\n"); + return 0; +} |
