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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
/* 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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdint.h>
typedef int CUresult;
typedef int CUdevice;
typedef void *CUcontext;
typedef uint64_t CUdeviceptr;
/* On failure: print cuGetErrorName/String and, when CDT_HOLD=<seconds> is
* set, keep the process alive so the live state (trackers, RM error record)
* can be inspected with gdb after cuCtxCreate has given up. */
static void fail_info(CUresult r)
{
const char *(*errname)(CUresult, const char **) = dlsym(RTLD_DEFAULT, "cuGetErrorName");
const char *(*errstr)(CUresult, const char **) = dlsym(RTLD_DEFAULT, "cuGetErrorString");
const char *s = NULL;
const char *hold = getenv("CDT_HOLD");
if (errname && errname(r, &s) == 0 && s)
printf("cuGetErrorName: %s\n", s);
if (errstr && errstr(r, &s) == 0 && s)
printf("cuGetErrorString: %s\n", s);
if (hold && atoi(hold) > 0) {
printf("CDT_HOLD: sleeping %d s for inspection\n", atoi(hold));
fflush(stdout);
sleep((unsigned)atoi(hold));
}
}
#define CHECK(expr) do { \
CUresult r = (expr); \
if (r) { printf("%-24s FAILED rc=%d\n", #expr, r); fail_info(r); return 1; } \
printf("%-24s OK\n", #expr); \
} while (0)
/* Print before entering libcuda so a crash identifies the call. */
#define MARK(name) do { printf("-> %s\n", name); } while (0)
int main(void)
{
setvbuf(stdout, NULL, _IONBF, 0);
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;
MARK("cuInit");
CHECK(cuInit(0));
MARK("cuDeviceGetCount");
CHECK(cuDeviceGetCount(&count));
printf("device count = %d\n", count);
if (count < 1) return 1;
MARK("cuDeviceGet");
CHECK(cuDeviceGet(&dev, 0));
printf("device 0 = %d\n", dev);
MARK("cuCtxCreate");
CHECK(cuCtxCreate(&ctx, 0, dev));
printf("context = %p\n", ctx);
MARK("cuMemAlloc");
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);
MARK("cuMemcpyHtoD");
CHECK(cuMemcpyHtoD(dptr, buf, sizeof(buf)));
memset(back, 0, sizeof(back));
MARK("cuMemcpyDtoH");
CHECK(cuMemcpyDtoH(back, dptr, sizeof(buf)));
printf("roundtrip %s (back[7]=%u expected 22)\n",
memcmp(buf, back, sizeof(buf)) ? "MISMATCH" : "match", back[7]);
MARK("cuMemFree");
CHECK(cuMemFree(dptr));
MARK("cuCtxDestroy");
CHECK(cuCtxDestroy(ctx));
printf("ALL OK\n");
return 0;
}
|