summaryrefslogtreecommitdiffstats
path: root/tools/test/cuda_drv_test.c
blob: 6868a2e6f3229eee1cea978321cc7652a8999ce1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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;
}