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 | 68cdd7728a7cc6715bbbff7decfe2b11c3bdf558 (patch) | |
| tree | 3dc8ce2e6af313a97ec6852f5c663a9a93665d2e /tools/guest-diag/ptrace_ioctl.c | |
| download | open-iov-68cdd7728a7cc6715bbbff7decfe2b11c3bdf558.tar.gz open-iov-68cdd7728a7cc6715bbbff7decfe2b11c3bdf558.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/guest-diag/ptrace_ioctl.c')
| -rw-r--r-- | tools/guest-diag/ptrace_ioctl.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/tools/guest-diag/ptrace_ioctl.c b/tools/guest-diag/ptrace_ioctl.c new file mode 100644 index 0000000..5f96b31 --- /dev/null +++ b/tools/guest-diag/ptrace_ioctl.c @@ -0,0 +1,236 @@ +/* ptrace_ioctl.c - follow a child with PTRACE_SYSCALL and dump the buffer + * results of selected UVM ioctls. Needed because libcuda issues raw syscall + * instructions (LD_PRELOAD/ltrace never see these). + * + * Build (guest): gcc -O1 -w -o /tmp/ptio ptrace_ioctl.c + * Run: /tmp/ptio /tmp/cdt + */ +#define _GNU_SOURCE +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#define SYS_IOCTL_X86_64 16 + +struct syscall_info { + unsigned char op; + unsigned char pad[3]; + unsigned int arch; + unsigned long long instruction_pointer; + unsigned long long stack_pointer; + union { + struct { + unsigned long long nr; + unsigned long long args[6]; + } entry; + struct { + long long rval; + unsigned char is_error; + } exit; + } u; +}; + +static int interesting(unsigned long req) +{ + switch (req) { + case 0x1: /* UVM_RESERVE_VA */ + case 0x2: /* UVM_RELEASE_VA */ + case 0x25: /* UVM_REGISTER_GPU */ + case 0x27: /* UVM_PAGEABLE_MEM_ACCESS */ + case 0x17: /* UVM_EVENT_CTRL */ + case 0x19: /* UVM_REGISTER_MPS_CLIENT */ + return 1; + default: + return 0; + } +} + +static void dump_words(pid_t pid, unsigned long addr, int nwords) +{ + int i; + + fprintf(stderr, " buffer @%#lx:", addr); + for (i = 0; i < nwords; i++) { + errno = 0; + long v = ptrace(PTRACE_PEEKDATA, pid, addr + i * 8, NULL); + if (errno) + break; + fprintf(stderr, " %016lx", (unsigned long)v); + } + fprintf(stderr, "\n"); +} + +struct saved { + pid_t pid; + int used; + unsigned long req, arg; +} saved[64]; + +static struct saved *slot(pid_t pid) +{ + int i, free_i = -1; + + for (i = 0; i < 64; i++) { + if (saved[i].used && saved[i].pid == pid) + return &saved[i]; + if (!saved[i].used && free_i < 0) + free_i = i; + } + if (free_i >= 0) { + saved[free_i].used = 1; + saved[free_i].pid = pid; + saved[free_i].req = 0; + return &saved[free_i]; + } + return NULL; +} + +int main(int argc, char **argv) +{ + pid_t pid; + int nprint = 0; + + if (argc < 2) { + fprintf(stderr, "usage: %s prog [args...]\n", argv[0]); + return 2; + } + + pid = fork(); + if (pid == 0) { + ptrace(PTRACE_TRACEME, 0, 0, 0); + raise(SIGSTOP); + execvp(argv[1], &argv[1]); + perror("execvp"); + _exit(127); + } + + waitpid(pid, NULL, WUNTRACED); + ptrace(PTRACE_SETOPTIONS, pid, 0, + (void *)(PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE | + PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_EXITKILL)); + ptrace(PTRACE_SYSCALL, pid, 0, 0); + + fprintf(stderr, "[ptio] attached to pid %d\n", pid); + for (;;) { + int status; + pid_t p = waitpid(-1, &status, __WALL); + + if (p < 0) { + if (errno == EINTR) + continue; + break; + } + if (WIFEXITED(status) || WIFSIGNALED(status)) + continue; + if (!WIFSTOPPED(status)) + continue; + + { + int sig = WSTOPSIG(status); + struct syscall_info si; + + memset(&si, 0, sizeof(si)); + if ((sig == (SIGTRAP | 0x80)) && + ptrace(PTRACE_GET_SYSCALL_INFO, p, sizeof(si), &si) > 0) { + struct saved *sv = slot(p); + + if (si.op == 1 /* entry */) { + if (sv && si.u.entry.nr == SYS_IOCTL_X86_64) { + sv->req = si.u.entry.args[1]; + sv->arg = si.u.entry.args[2]; + if (sv->req == 0x25) { + fprintf(stderr, "[ptio] ENTER UVM_REGISTER_GPU pid=%d\n", p); + dump_words(p, sv->arg, 6); + fprintf(stderr, " ^ IN: uuid(2q) | numaEnabled numaNodeId | rmCtrlFd hClient | hSmcPartRef rmStatus\n"); + } + } else if (sv) { + sv->req = 0; + } + } else if (si.op == 2 /* exit */ && sv && + ((sv->req & 0xff) == 0x4629ul || + ((sv->req & 0xffff00fful) == 0x0046002bul) || + ((sv->req & 0xffff00fful) == 0x0046002aul))) { + unsigned int own = (unsigned int)(sv->req & 0xff); + long q0 = 0, q1 = 0, q2 = 0, q3 = 0; + unsigned long st = 0; + + errno = 0; q0 = ptrace(PTRACE_PEEKDATA, p, sv->arg, NULL); + errno = 0; q1 = ptrace(PTRACE_PEEKDATA, p, sv->arg + 8, NULL); + errno = 0; q2 = ptrace(PTRACE_PEEKDATA, p, sv->arg + 16, NULL); + errno = 0; q3 = ptrace(PTRACE_PEEKDATA, p, sv->arg + 24, NULL); + if (own == 0x29) + st = (unsigned long)((q1 >> 32) & 0xfffffffful); + else + st = (unsigned long)((q3 >> 32) & 0xfffffffful); + if (st != 0) + fprintf(stderr, + "[rmio] pid=%d nr=0x%x status=0x%lx hClient=0x%lx hObj=0x%lx cmd/class=0x%lx size=%lu\n", + p, own, + st, + (unsigned long)(q0 & 0xfffffffful), + (unsigned long)((q0 >> 32) & 0xfffffffful), + (unsigned long)(q1 & 0xfffffffful), + (unsigned long)((q3 >> 32) & 0xfffffffful)); + } else if (si.op == 2 /* exit */ && sv && + sv->req == 0xc020462aul) { + long q0, q1, q2, q3; + errno = 0; + q1 = ptrace(PTRACE_PEEKDATA, p, sv->arg + 8, NULL); + errno = 0; + q2 = ptrace(PTRACE_PEEKDATA, p, sv->arg + 16, NULL); + errno = 0; + q3 = ptrace(PTRACE_PEEKDATA, p, sv->arg + 24, NULL); + q0 = ptrace(PTRACE_PEEKDATA, p, sv->arg, NULL); + (void)q0; + fprintf(stderr, + "[rmctl] pid=%d cmd=0x%lx hClient=0x%lx params=%#lx size=%lu status=0x%lx\n", + p, (unsigned long)(q1 & 0xfffffffful), + (unsigned long)(q0 & 0xfffffffful), + (unsigned long)(q2 & 0xfffffffffffful), + (unsigned long)((q2 >> 32) & 0xfffffffful), + (unsigned long)((q3 >> 32) & 0xfffffffful)); + if (((q3 >> 32) & 0xfffffffful) != 0 && + (q2 & 0xfffffffffffful) > 0x10000) { + fprintf(stderr, " params:"); + { + int i2; + for (i2 = 0; i2 < 6; i2++) { + errno = 0; + long v = ptrace(PTRACE_PEEKDATA, p, + (unsigned long)(q2 & 0xfffffffffffful) + i2 * 8, NULL); + if (errno) + break; + fprintf(stderr, " %016lx", + (unsigned long)v); + } + } + fprintf(stderr, "\n"); + } + } else if (si.op == 2 /* exit */ && sv && + interesting(sv->req)) { + fprintf(stderr, + "[ptio] pid=%d ioctl req=0x%lx arg=%#lx ret=%ld%s\n", + p, sv->req, sv->arg, + si.u.exit.rval, + interesting(sv->req) ? " *" : ""); + if (sv->req == 0x25) { + dump_words(p, sv->arg, 6); + fprintf(stderr, + " ^ UVM_REGISTER_GPU: uuid(2q) | numaEnabled numaNodeId rmCtrlFd hClient | hSmcPartRef rmStatus\n"); + } else { + dump_words(p, sv->arg, 4); + } + } + } + ptrace(PTRACE_SYSCALL, p, 0, sig == (SIGTRAP | 0x80) ? 0 : + (sig == SIGTRAP ? 0 : sig)); + } + } + return 0; +} |
