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/guest_engines.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/guest_engines.c')
| -rw-r--r-- | tools/guest-diag/guest_engines.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/tools/guest-diag/guest_engines.c b/tools/guest-diag/guest_engines.c new file mode 100644 index 0000000..10209e2 --- /dev/null +++ b/tools/guest-diag/guest_engines.c @@ -0,0 +1,192 @@ +/* guest_engines.c - query the guest client RM for the data UVM's register + * path depends on: virtualization mode, subdevice count, ENGINE LIST, + * CLASS LIST, CE caps and CE->PCE masks. Uses the NVOS64 serialized alloc + * path (same as tools/rmq/rmq.c) so device allocs succeed. + * + * Build in guest: gcc -O1 -o /tmp/ge guest_engines.c ; run /tmp/ge + */ +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +typedef uint32_t NvU32; +typedef uint32_t NvHandle; +typedef uint64_t NvP64; + +#define NV_IOCTL_MAGIC 'F' +#define NV_ESC_RM_ALLOC 0x2B +#define NV_ESC_RM_CONTROL 0x2A +#define NV01_ROOT 0x0 +#define NV01_ROOT_CLIENT 0x41 +#define NV01_DEVICE_0 0x80 +#define NV20_SUBDEVICE_0 0x2080 + +typedef struct { + NvHandle hRoot, hObjectParent, hObjectNew; + NvU32 hClass; + NvP64 pAllocParms; + NvU32 paramsSize, status; +} NVOS21_PARAMETERS; + +typedef struct { + NvHandle hRoot, hObjectParent, hObjectNew; + NvU32 hClass; + NvP64 pAllocParms; + NvP64 pRightsRequested; + NvU32 paramsSize, flags, status; +} NVOS64_PARAMETERS; + +typedef struct { + NvHandle hClient, hObject; + NvU32 cmd, flags; + NvP64 params; + NvU32 paramsSize, status; +} NVOS54_PARAMETERS; + +static int fd; + +static NvU32 rm_alloc(NvHandle hRoot, NvHandle hParent, NvU32 hClass, + NvHandle *pOut) +{ + NVOS21_PARAMETERS p; + memset(&p, 0, sizeof(p)); + p.hRoot = hRoot; p.hObjectParent = hParent; p.hClass = hClass; + if (ioctl(fd, _IOWR(NV_IOCTL_MAGIC, NV_ESC_RM_ALLOC, NVOS21_PARAMETERS), &p) < 0) { + perror("alloc ioctl"); return 0xffffffff; + } + *pOut = p.hObjectNew; + return p.status; +} + +static NvU32 rm_alloc64(NvHandle hRoot, NvHandle hParent, NvU32 hClass, + NvHandle *pOut) +{ + NVOS64_PARAMETERS p; + memset(&p, 0, sizeof(p)); + p.hRoot = hRoot; p.hObjectParent = hParent; p.hClass = hClass; + p.flags = 1; /* FINN_SERIALIZED */ + if (ioctl(fd, _IOWR(NV_IOCTL_MAGIC, NV_ESC_RM_ALLOC, NVOS64_PARAMETERS), &p) < 0) { + perror("alloc64 ioctl"); return 0xffffffff; + } + *pOut = p.hObjectNew; + return p.status; +} + +static NvU32 rm_control(NvHandle hClient, NvHandle hObject, NvU32 cmd, + void *params, NvU32 size) +{ + NVOS54_PARAMETERS p; + memset(&p, 0, sizeof(p)); + p.hClient = hClient; p.hObject = hObject; p.cmd = cmd; + p.params = (NvP64)params; p.paramsSize = size; + if (ioctl(fd, _IOWR(NV_IOCTL_MAGIC, NV_ESC_RM_CONTROL, NVOS54_PARAMETERS), &p) < 0) { + perror("control ioctl"); return 0xffffffff; + } + return p.status; +} + +#define MAX_CLASSES 256 + +int main(void) +{ + NvHandle hClient = 0, hDevice = 0, hSubdevice = 0; + NvU32 st; + + fd = open("/dev/nvidiactl", O_RDWR | O_CLOEXEC); + if (fd < 0) { perror("open nvidiactl"); return 1; } + + st = rm_alloc(0, 0, getenv("RC") ? NV01_ROOT_CLIENT : NV01_ROOT, &hClient); + printf("alloc client: st=0x%x hClient=0x%x\n", st, hClient); + + st = rm_alloc64(hClient, hClient, NV01_DEVICE_0, &hDevice); + printf("alloc device: st=0x%x hDevice=0x%x\n", st, hDevice); + if (st) return 2; + + st = rm_alloc64(hClient, hDevice, NV20_SUBDEVICE_0, &hSubdevice); + printf("alloc subdevice: st=0x%x hSub=0x%x\n", st, hSubdevice); + + { + struct { NvU32 mode; NvU32 isGridBuild; } v = {0xdead, 0xdead}; + st = rm_control(hClient, hDevice, 0x800289, &v, sizeof(v)); + printf("GET_VIRTUALIZATION_MODE: st=0x%x mode=%u (0=NONE,1=NMOS,2=VGX) gridBuild=%u\n", + st, v.mode, v.isGridBuild); + } + { + struct { NvU32 n; } s = {0xdead}; + st = rm_control(hClient, hDevice, 0x800280, &s, sizeof(s)); + printf("GET_NUM_SUBDEVICES: st=0x%x n=%u\n", st, s.n); + } + { + struct { NvU32 numClasses; NvU32 classList[MAX_CLASSES]; } c; + int i; + memset(&c, 0, sizeof(c)); + st = rm_control(hClient, hDevice, 0x800292, &c, sizeof(c)); + printf("GET_CLASSLIST_V2: st=0x%x numClasses=%u\n", st, c.numClasses); + if (!st && c.numClasses) { + printf(" classes:"); + for (i = 0; i < (int)c.numClasses && i < MAX_CLASSES; i++) + printf(" 0x%x", c.classList[i]); + printf("\n"); + } + } + if (hSubdevice) { + struct { NvU32 engineCount; NvU32 engineList[0x54]; } e; + int i; + memset(&e, 0, sizeof(e)); + st = rm_control(hClient, hSubdevice, 0x20800170, &e, sizeof(e)); + printf("GPU_GET_ENGINES_V2: st=0x%x engineCount=%u\n", st, e.engineCount); + if (!st && e.engineCount) { + printf(" engines:"); + for (i = 0; i < (int)e.engineCount && i < 0x54; i++) + printf(" 0x%x", e.engineList[i]); + printf("\n"); + } + for (i = 0; i < (int)e.engineCount && i < 0x54; i++) { + struct { NvU32 t, mask; } m; + struct { NvU32 t; unsigned char caps[2]; } cc; + NvU32 t = e.engineList[i]; + memset(&m, 0, sizeof(m)); + st = rm_control(hClient, hSubdevice, 0x20802a02, &m, sizeof(m)); + printf(" CE_GET_CE_PCE_MASK(type=0x%x): st=0x%x mask=0x%x\n", t, st, m.mask); + memset(&cc, 0, sizeof(cc)); + cc.t = t; + st = rm_control(hClient, hSubdevice, 0x20802a03, &cc, sizeof(cc)); + printf(" CE_GET_CAPS_V2(type=0x%x): st=0x%x caps=%02x %02x\n", t, st, cc.caps[0], cc.caps[1]); + } + } + if (hSubdevice) { + struct { + NvU32 engineData[16]; + NvU32 pbdmaIds[2]; + NvU32 pbdmaFaultIds[2]; + NvU32 numPbdmas; + char engineName[16]; + } e[32]; + struct { + NvU32 baseIndex, numEntries, bMore; + typeof(e) entries; + } t; + int i, j; + NvU32 base = 0; + memset(&t, 0, sizeof(t)); + do { + t.baseIndex = base; + st = rm_control(hClient, hSubdevice, 0x20801112, &t, sizeof(t)); + if (st) { printf("FIFO_GET_DEVICE_INFO_TABLE: st=0x%x\n", st); break; } + for (i = 0; i < (int)t.numEntries; i++) { + printf(" [%2u] %-8s numPbdmas=%u pbdmaIds=[%u,%u] engineData=[", + base + i, t.entries[i].engineName, t.entries[i].numPbdmas, + t.entries[i].pbdmaIds[0], t.entries[i].pbdmaIds[1]); + for (j = 0; j < 16; j++) + printf("%s%u", j ? "," : "", t.entries[i].engineData[j]); + printf("]\n"); + } + base += t.numEntries; + } while (t.bMore); + } + return 0; +} |
