summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-21 18:01:55 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-21 18:01:55 -0400
commit6aadb593750736f239fd54a0ccef4b3834c08fc4 (patch)
tree5b3d39d82a09cdfd3ca8022cd9c0f83040f23229 /tools
parentc8249854284c3e8181587b96e3c5b4177d705e0c (diff)
downloadopen-iov-6aadb593750736f239fd54a0ccef4b3834c08fc4.tar.gz
open-iov-6aadb593750736f239fd54a0ccef4b3834c08fc4.zip
kernel: GSP-RM msgq emulation + TU104 hardware tables (GET_GSP_STATIC_INFO, GSP_RM_CONTROL)
Implements the GSP-RM reply content layer for the msgq RPC channel: - kernel/gsp_rm_emulation.c/h: GSP-RM msgq emulation engine - Queue chain discovery (libos args → RMARGS → GSP_ARGUMENTS_CACHED) - Status queue link + GSP_INIT_DONE emission - Command queue polling + RPC dispatch - SET_GUEST_SYSTEM_INFO version negotiation (VGX 41.12) - GET_GSP_STATIC_INFO(65) with 1656-byte GspStaticConfigInfo - GSP_RM_CONTROL(76) sub-command dispatch: - FIFO_GET_DEVICE_INFO_TABLE (0x20801112) - INTERNAL_INTR_GET_KERNEL_TABLE (0x20800a5c) - GPU_GET_CONSTRUCTED_FALCON_INFO (0x208001b0) - INTERNAL_GET_DEVICE_INFO_TABLE (0x20800a40) - kernel/gsp_rm_tu104.h: TU104 hardware tables - 12-entry FIFO engine table (GR0, CE0-4, NVDEC0-1, NVENC0, SEC2, NVJPG, SW) - 8-entry interrupt table (from intr_phys_tu102.c) - 1-entry constructed falcon table (FBFLCN) - 11-entry internal device info table - GspStaticConfigInfo layout (1656 bytes, ABI-verified offsets) - Data from NVIDIA driver material (ext-tree) + hwref/turing/tu104 - tools/rmq/: Userspace RM API client (oracle for ground-truth tables) KNOWN ISSUE: gsp_rm_send crashes (fortify_panic) on multi-element messages when reply data exceeds one 4096-byte queue element. Affects cmd 0x20800a41 (paramsSize=8204). Multi-element splitting is the next fix.
Diffstat (limited to 'tools')
-rw-r--r--tools/rmq/Makefile19
-rw-r--r--tools/rmq/kmod/Makefile7
-rw-r--r--tools/rmq/kmod/wpr2_reset_kmod.c90
-rw-r--r--tools/rmq/rmq.c154
-rwxr-xr-xtools/rmq/wpr2_resetbin0 -> 16360 bytes
-rw-r--r--tools/rmq/wpr2_reset.c50
6 files changed, 320 insertions, 0 deletions
diff --git a/tools/rmq/Makefile b/tools/rmq/Makefile
new file mode 100644
index 0000000..f58e4d9
--- /dev/null
+++ b/tools/rmq/Makefile
@@ -0,0 +1,19 @@
+# rmq - RM query tool (userspace RM API client)
+#
+# Queries the host NVIDIA Resource Manager via /dev/nvidia0 ioctls.
+# Used as an "oracle" to extract ground-truth static GPU data (device info
+# tables, static config) from the host's real GSP-RM for the GSP-RM emulation
+# layer (kernel/gsp_rm_emulation.c).
+
+CC ?= gcc
+CFLAGS ?= -O2 -Wall
+
+all: rmq
+
+rmq: rmq.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+clean:
+ rm -f rmq
+
+.PHONY: all clean
diff --git a/tools/rmq/kmod/Makefile b/tools/rmq/kmod/Makefile
new file mode 100644
index 0000000..2fde4ef
--- /dev/null
+++ b/tools/rmq/kmod/Makefile
@@ -0,0 +1,7 @@
+obj-m += wpr2_reset_kmod.o
+
+all:
+ make -C /lib/modules/$(shell uname -r)/build M=$(CURDIR) modules
+
+clean:
+ make -C /lib/modules/$(shell uname -r)/build M=$(CURDIR) clean
diff --git a/tools/rmq/kmod/wpr2_reset_kmod.c b/tools/rmq/kmod/wpr2_reset_kmod.c
new file mode 100644
index 0000000..523fac7
--- /dev/null
+++ b/tools/rmq/kmod/wpr2_reset_kmod.c
@@ -0,0 +1,90 @@
+/* wpr2_reset_kmod - kernel module that clears GPU WPR2 registers via BAR0 ioremap.
+ *
+ * When nvidia.ko is unloaded without a clean GSP teardown, GSP firmware stays
+ * resident in WPR2 (a write-protected FB region described by
+ * PFB_PRI_MMU_WPR2_ADDR_LO/HI). Every subsequent RmInitAdapter then fails with
+ * "unexpected WPR2 already up" and FLR does not help (GSP survives warm resets).
+ *
+ * This module ioremaps GPU BAR0 and pokes WPR2_ADDR_LO/HI to zero so the next
+ * driver load takes the normal (clean) GSP boot path. Equivalent to the effect
+ * of FWSEC-SB + Booter Unload teardown, minus running the ucodes.
+ *
+ * Usage: sudo insmod wpr2_reset_kmod.ko [pci_bdf=0000:01:00.0]
+ * result printed via pr_info (check dmesg)
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+
+#define NV_PFB_PRI_MMU_WPR2_ADDR_LO 0x1fa824
+#define NV_PFB_PRI_MMU_WPR2_ADDR_HI 0x1fa828
+
+static char *pci_bdf = "0000:01:00.0";
+module_param(pci_bdf, charp, 0444);
+MODULE_PARM_DESC(pci_bdf, "PCI BDF of the GPU (default 0000:01:00.0)");
+
+static int __init wpr2_reset_init(void)
+{
+ struct pci_dev *pdev = NULL;
+ unsigned int bus, dev, fn;
+ void __iomem *bar0;
+ u32 lo_before, hi_before, lo_after, hi_after;
+
+ if (sscanf(pci_bdf, "0000:%x:%x.%u", &bus, &dev, &fn) != 3) {
+ pr_err("wpr2_reset: bad pci_bdf %s\n", pci_bdf);
+ return -EINVAL;
+ }
+ pdev = pci_get_domain_bus_and_slot(0, bus, PCI_DEVFN(dev, fn));
+ if (!pdev) {
+ pr_err("wpr2_reset: device %s not found\n", pci_bdf);
+ return -ENODEV;
+ }
+ if (!pci_resource_start(pdev, 0)) {
+ pr_err("wpr2_reset: BAR0 not assigned\n");
+ pci_dev_put(pdev);
+ return -ENODEV;
+ }
+
+ if (pci_enable_device(pdev)) {
+ pr_err("wpr2_reset: pci_enable_device failed\n");
+ pci_dev_put(pdev);
+ return -EIO;
+ }
+ pci_set_master(pdev);
+ bar0 = ioremap_wc(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+ if (!bar0) {
+ pr_err("wpr2_reset: ioremap failed\n");
+ pci_dev_put(pdev);
+ return -ENOMEM;
+ }
+
+ lo_before = readl(bar0 + NV_PFB_PRI_MMU_WPR2_ADDR_LO);
+ hi_before = readl(bar0 + NV_PFB_PRI_MMU_WPR2_ADDR_HI);
+ pr_info("wpr2_reset: before LO=0x%08x HI=0x%08x\n", lo_before, hi_before);
+
+ writel(0, bar0 + NV_PFB_PRI_MMU_WPR2_ADDR_LO);
+ writel(0, bar0 + NV_PFB_PRI_MMU_WPR2_ADDR_HI);
+
+ lo_after = readl(bar0 + NV_PFB_PRI_MMU_WPR2_ADDR_LO);
+ hi_after = readl(bar0 + NV_PFB_PRI_MMU_WPR2_ADDR_HI);
+ pr_info("wpr2_reset: after LO=0x%08x HI=0x%08x\n", lo_after, hi_after);
+
+ if (hi_after == 0)
+ pr_info("wpr2_reset: WPR2 cleared, clean GSP boot now possible\n");
+ else
+ pr_info("wpr2_reset: write dropped (PLM?) - WPR2 still up\n");
+
+ iounmap(bar0);
+ pci_dev_put(pdev);
+ return hi_after ? -EINVAL : 0;
+}
+
+static void __exit wpr2_reset_exit(void)
+{
+ pr_info("wpr2_reset: unloaded\n");
+}
+
+module_init(wpr2_reset_init);
+module_exit(wpr2_reset_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Clear GPU WPR2 registers to unstick GSP boot");
diff --git a/tools/rmq/rmq.c b/tools/rmq/rmq.c
new file mode 100644
index 0000000..97d1bed
--- /dev/null
+++ b/tools/rmq/rmq.c
@@ -0,0 +1,154 @@
+/* rmq - query host NVIDIA RM via /dev/nvidia0 ioctls (oracle for TU104 static data) */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <stdint.h>
+
+typedef uint32_t NvU32;
+typedef uint16_t NvU16;
+typedef uint8_t NvU8;
+typedef uint64_t NvU64;
+typedef uint32_t NvHandle;
+typedef uint32_t NvV32;
+typedef uint64_t NvP64;
+
+#define NV_ESC_RM_ALLOC 0x2B
+#define NV_ESC_RM_CONTROL 0x2A
+#define NV01_ROOT 0x0
+#define NV01_DEVICE_0 0x80
+#define NV20_SUBDEVICE_0 0x2080
+
+typedef struct {
+ NvHandle hRoot;
+ NvHandle hObjectParent;
+ NvHandle hObjectNew;
+ NvV32 hClass;
+ NvP64 pAllocParms __attribute__((aligned(8)));
+ NvU32 paramsSize;
+ NvV32 status;
+} NVOS21_PARAMETERS;
+
+typedef struct {
+ NvHandle hClient;
+ NvHandle hObject;
+ NvV32 cmd;
+ NvU32 flags;
+ NvP64 params __attribute__((aligned(8)));
+ NvU32 paramsSize;
+ NvV32 status;
+} NVOS54_PARAMETERS;
+
+static int fd;
+
+static NvU32 rm_alloc(NvHandle hRoot, NvHandle hParent, NvU32 hClass, NvHandle *pOut)
+{
+ NVOS21_PARAMETERS p = {0};
+ p.hRoot = hRoot; p.hObjectParent = hParent; p.hClass = hClass;
+ if (ioctl(fd, NV_ESC_RM_ALLOC, &p) < 0) { perror("alloc ioctl"); return 1; }
+ if (p.status) { fprintf(stderr, "alloc class 0x%x failed: 0x%x\n", hClass, p.status); return p.status; }
+ *pOut = p.hObjectNew;
+ return 0;
+}
+
+static NvU32 rm_control(NvHandle hClient, NvHandle hObject, NvU32 cmd, void *params, NvU32 size)
+{
+ NVOS54_PARAMETERS p = {0};
+ p.hClient = hClient; p.hObject = hObject; p.cmd = cmd;
+ p.params = (NvP64)params; p.paramsSize = size;
+ if (ioctl(fd, NV_ESC_RM_CONTROL, &p) < 0) { perror("control ioctl"); return 1; }
+ return p.status;
+}
+
+/* NV2080_CTRL_FIFO_DEVICE_ENTRY (16 u32 engineData + 2 pbdmaIds + 2 pbdmaFaultIds + numPbdmas + name[16]) */
+typedef struct {
+ NvU32 engineData[16];
+ NvU32 pbdmaIds[2];
+ NvU32 pbdmaFaultIds[2];
+ NvU32 numPbdmas;
+ char engineName[16];
+} FIFO_DEVICE_ENTRY;
+
+typedef struct {
+ NvU32 baseIndex;
+ NvU32 numEntries;
+ NvU32 bMore;
+ FIFO_DEVICE_ENTRY entries[32];
+} FIFO_GET_DEVICE_INFO_TABLE_PARAMS;
+
+typedef struct {
+ NvU32 faultId, instanceId, typeEnum, resetId, devicePriBase, isEngine;
+ NvU32 rlEngId, runlistPriBase, groupId, ginTargetId, deviceBroadcastPriBase, groupLocalInstanceId;
+} INTERNAL_DEVICE_INFO;
+
+typedef struct {
+ NvU32 numEntries;
+ INTERNAL_DEVICE_INFO deviceInfoTable[512];
+} INTERNAL_GET_DEVICE_INFO_TABLE_PARAMS;
+
+int main(int argc, char **argv)
+{
+ NvHandle hClient = 0, hDevice = 0, hSubdevice = 0;
+ NvU32 st;
+
+ fd = open("/dev/nvidia0", O_RDWR);
+ if (fd < 0) { perror("open /dev/nvidia0"); return 1; }
+
+ if ((st = rm_alloc(0, 0, NV01_ROOT, &hClient))) return 1;
+ if ((st = rm_alloc(hClient, hClient, NV01_DEVICE_0, &hDevice))) return 1;
+ if ((st = rm_alloc(hClient, hDevice, NV20_SUBDEVICE_0, &hSubdevice))) return 1;
+ printf("hClient=0x%x hDevice=0x%x hSubdevice=0x%x\n", hClient, hDevice, hSubdevice);
+
+ /* FIFO_GET_DEVICE_INFO_TABLE (0x20801112) */
+ {
+ FIFO_GET_DEVICE_INFO_TABLE_PARAMS *t = calloc(1, sizeof(*t));
+ NvU32 base = 0;
+ printf("\n=== FIFO_GET_DEVICE_INFO_TABLE (0x20801112) ===\n");
+ do {
+ memset(t, 0, sizeof(*t));
+ t->baseIndex = base;
+ st = rm_control(hClient, hSubdevice, 0x20801112, t, sizeof(*t));
+ if (st) { printf("control failed: 0x%x\n", st); break; }
+ NvU32 i;
+ for (i = 0; i < t->numEntries; i++) {
+ FIFO_DEVICE_ENTRY *e = &t->entries[i];
+ printf("[%3u] name=%-16s numPbdmas=%u pbdmaIds=[%u,%u] faultIds=[%u,%u] engineData=[",
+ base + i, e->engineName, e->numPbdmas,
+ e->pbdmaIds[0], e->pbdmaIds[1],
+ e->pbdmaFaultIds[0], e->pbdmaFaultIds[1]);
+ int j;
+ for (j = 0; j < 16; j++) printf("%s%u", j ? "," : "", e->engineData[j]);
+ printf("]\n");
+ }
+ base += t->numEntries;
+ if (!t->bMore) break;
+ } while (1);
+ free(t);
+ }
+
+ /* INTERNAL_GET_DEVICE_INFO_TABLE (0x20800a40) */
+ {
+ INTERNAL_GET_DEVICE_INFO_TABLE_PARAMS *t = calloc(1, sizeof(*t));
+ printf("\n=== INTERNAL_GET_DEVICE_INFO_TABLE (0x20800a40) ===\n");
+ st = rm_control(hClient, hSubdevice, 0x20800a40, t, sizeof(*t));
+ if (st) {
+ printf("control failed: 0x%x (expected from userspace if internal-only)\n", st);
+ } else {
+ NvU32 i;
+ printf("numEntries=%u\n", t->numEntries);
+ for (i = 0; i < t->numEntries; i++) {
+ INTERNAL_DEVICE_INFO *e = &t->deviceInfoTable[i];
+ printf("[%3u] faultId=%u instanceId=%u typeEnum=%u resetId=%u devicePriBase=0x%x isEngine=%u rlEngId=%u runlistPriBase=0x%x groupId=%u ginTargetId=%u deviceBroadcastPriBase=0x%x groupLocalInstanceId=%u\n",
+ i, e->faultId, e->instanceId, e->typeEnum, e->resetId,
+ e->devicePriBase, e->isEngine, e->rlEngId, e->runlistPriBase,
+ e->groupId, e->ginTargetId, e->deviceBroadcastPriBase,
+ e->groupLocalInstanceId);
+ }
+ }
+ free(t);
+ }
+
+ return 0;
+}
diff --git a/tools/rmq/wpr2_reset b/tools/rmq/wpr2_reset
new file mode 100755
index 0000000..08d4f55
--- /dev/null
+++ b/tools/rmq/wpr2_reset
Binary files differ
diff --git a/tools/rmq/wpr2_reset.c b/tools/rmq/wpr2_reset.c
new file mode 100644
index 0000000..4435843
--- /dev/null
+++ b/tools/rmq/wpr2_reset.c
@@ -0,0 +1,50 @@
+/* wpr2_reset - clear GPU WPR2 registers via BAR0 MMIO to unstick GSP boot.
+ *
+ * When nvidia.ko is unloaded, GSP firmware can remain resident in WPR2
+ * (write-protected region 2 in FB). The next RmInitAdapter then fails with
+ * "unexpected WPR2 already up". FLR does not clear WPR2; only a cold (power)
+ * reset does. This tool pokes WPR2_ADDR_LO/HI to zero directly through
+ * /sys/bus/pci/devices/<dev>/resource0 while no driver is bound.
+ *
+ * Usage: sudo ./wpr2_reset [pci.bdf] (default 0000:01:00.0)
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#define NV_PFB_PRI_MMU_WPR2_ADDR_LO 0x1fa824
+#define NV_PFB_PRI_MMU_WPR2_ADDR_HI 0x1fa828
+
+int main(int argc, char **argv)
+{
+ const char *bdf = argc > 1 ? argv[1] : "0000:01:00.0";
+ char path[256];
+ snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/resource0", bdf);
+
+ int fd = open(path, O_RDWR | O_SYNC);
+ if (fd < 0) { perror(path); return 1; }
+ void *bar = mmap(NULL, 0x200000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (bar == MAP_FAILED) { perror("mmap"); return 1; }
+
+ volatile uint32_t *lo = (volatile uint32_t *)((char *)bar + NV_PFB_PRI_MMU_WPR2_ADDR_LO);
+ volatile uint32_t *hi = (volatile uint32_t *)((char *)bar + NV_PFB_PRI_MMU_WPR2_ADDR_HI);
+
+ printf("before: WPR2_ADDR_LO=0x%08x WPR2_ADDR_HI=0x%08x\n", *lo, *hi);
+ *lo = 0;
+ *hi = 0;
+ /* read back */
+ printf("after : WPR2_ADDR_LO=0x%08x WPR2_ADDR_HI=0x%08x\n", *lo, *hi);
+
+ if (*hi == 0)
+ printf("WPR2 cleared - driver load should now boot GSP cleanly\n");
+ else
+ printf("write dropped (PLM?) - WPR2 still up\n");
+
+ munmap(bar, 0x200000);
+ close(fd);
+ return *hi ? 1 : 0;
+}