summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_emulation.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-22 22:40:26 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-22 22:40:26 -0400
commitf6d450a8be075fdc19a874826e7649ae0149b099 (patch)
tree9f68bdb6ce1f120fb80531895ad4f265c001d65a /kernel/gsp_emulation.c
parentfcd603db457c0fb035df882abc55397bc854a1cd (diff)
downloadopen-iov-f6d450a8be075fdc19a874826e7649ae0149b099.tar.gz
open-iov-f6d450a8be075fdc19a874826e7649ae0149b099.zip
kernel: guest probe completes - nvidia-smi enumerates the emulated TU104
Root cause of the golden-image NV_ERR_NO_MEMORY: STATIC_KGR_GET_CONTEXT_ BUFFERS_INFO (0x20800a32) fell through to the generic zeroed reply, so kgraphicsInitializeDeferredStaticData() stored a 0-byte main ctx buffer size and kgrctxAllocMainCtxBuffer() did a 0-page allocation (mem_desc.c:1353). Serve the TU104 per-engine ctx buffer table (26 engine IDs x 8 GR engines; NV_U32_MAX = unsupported, which the client skips). Second blocker after that: L2 cache-op and TLB-invalidate completion registers. Turing reaches them through the virtual-register window (GPU_VREG_* = BAR0 + sriovState.virtualRegPhysOffset + offset, 0xB80000 for a non-SRIOV virtual GPU): NV_VIRTUAL_FUNCTION_PRIV_MMU_INVALIDATE (0xB830B0, TRIGGER bit31) and the L2 invalidate/dirty-flush set (0x70000-0x70014 and 0xB80F00/0xB80F04). The driver soft-locked spinning in osDevReadReg032 on 0xB830B4 (kgmmuCheckPendingInvalidates_TU102) and timed out in kmemsysDoCacheOp_GM107; auto-clear all of them (both the plain and windowed aliases). Third: re-probe failed with GspStatusQueueInit msgqRxLink timeout because gsp_rm_emu_reset() was not called before a fresh boot, leaving rm->parsed set so the new libos args were never parsed. Reset GSP-RM state on the fresh FWSEC kick. Verified: guest nvidia-smi and nvidia-smi -L both exit 0 (full unload/reload cycle), GPU enumerated as "NVIDIA GeForce RTX 2080 SUPER" with 256MB, no NO_MEMORY / timeouts / soft lockups in the guest. Also add tools/rmq/grctx_oracle.c documenting that the host RM does not expose the ctx-buffer table to userspace (the real values are probed from the ctxsw ucode by GSP-RM), so the emulation serves synthesized sizes.
Diffstat (limited to 'kernel/gsp_emulation.c')
-rw-r--r--kernel/gsp_emulation.c72
1 files changed, 64 insertions, 8 deletions
diff --git a/kernel/gsp_emulation.c b/kernel/gsp_emulation.c
index 44facc1..0b6bba0 100644
--- a/kernel/gsp_emulation.c
+++ b/kernel/gsp_emulation.c
@@ -195,7 +195,6 @@ static void gsp_emu_poll_boot(struct gsp_emu_context *ctx)
u32 gsp_cpuctl = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_GSP_CPUCTL);
u32 sec2_cpuctl = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_SEC2_CPUCTL);
u32 engine = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_GSP_ENGINE);
- u32 mmu_inv = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_MMU_INVALIDATE);
u32 sw_vector;
/* Software-interrupt trigger: the driver writes a vector to
@@ -231,13 +230,63 @@ static void gsp_emu_poll_boot(struct gsp_emu_context *ctx)
}
/* TLB invalidate auto-complete: the driver sets TRIGGER then spins
- * polling for it to clear (kgmmuCheckPendingInvalidates). Report
- * instant completion or the driver wedges the open/init path. */
- if (mmu_inv & GSP_EMU_MMU_INVALIDATE_TRIGGER) {
- gsp_emu_shadow_write32(ctx, GSP_EMU_REG_MMU_INVALIDATE,
- mmu_inv & ~GSP_EMU_MMU_INVALIDATE_TRIGGER);
- pr_info_ratelimited("GSP-BOOT: MMU_INVALIDATE trigger auto-cleared (val=0x%x)\n",
- mmu_inv);
+ * polling for it to clear (kgmmuCheckPendingInvalidates_TU102 reads
+ * GPU_VREG_RD32(NV_VIRTUAL_FUNCTION_PRIV_MMU_INVALIDATE) =
+ * BAR0 + 0xB80000 + 0x30B0; the physical path uses the plain alias).
+ * Report instant completion or the driver soft-locks spinning inside
+ * osDevReadReg032. */
+ {
+ static const u32 tlb_inv_regs[] = {
+ GSP_EMU_REG_MMU_INVALIDATE,
+ GSP_EMU_REG_MMU_INVALIDATE_WIN,
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(tlb_inv_regs); i++) {
+ u32 val = gsp_emu_shadow_read32(ctx, tlb_inv_regs[i]);
+
+ if (!(val & GSP_EMU_MMU_INVALIDATE_TRIGGER))
+ continue;
+ gsp_emu_shadow_write32(ctx, tlb_inv_regs[i],
+ val & ~GSP_EMU_MMU_INVALIDATE_TRIGGER);
+ pr_info_ratelimited("GSP-BOOT: MMU_INVALIDATE[0x%x] trigger auto-cleared (val=0x%x)\n",
+ tlb_inv_regs[i], val);
+ }
+ }
+
+ /* L2 cache-op auto-complete: kmemsysDoCacheOp_GM107 writes PENDING
+ * (bit 0) into the NV_UFLUSH_* / VF L2 invalidate registers and spins
+ * until the polled bits read back 0 (FB flush, L2 sysmem/peermem
+ * invalidate, L2 clean, L2 flush-dirty, L2 wait-for-sys-reads). On
+ * Turing the sysmem/peermem invalidate registers go through the
+ * GPU_VREG_* window (BAR0 + 0xB80000 + NV_VIRTUAL_FUNCTION_PRIV_L2_*),
+ * so cover both the plain and windowed aliases. The emulated L2 has
+ * nothing to do, so clear them as soon as they are observed. */
+ {
+ static const u32 uflush_regs[] = {
+ GSP_EMU_REG_UFLUSH_FB_FLUSH,
+ GSP_EMU_REG_UFLUSH_L2_SYSMEM_INV,
+ GSP_EMU_REG_UFLUSH_L2_PEERMEM_INV,
+ GSP_EMU_REG_UFLUSH_L2_CLEAN_COMPTAGS,
+ GSP_EMU_REG_UFLUSH_L2_FLUSH_DIRTY,
+ GSP_EMU_REG_UFLUSH_L2_WAIT_SYS_READS,
+ GSP_EMU_REG_VF_L2_SYSMEM_INV,
+ GSP_EMU_REG_VF_L2_PEERMEM_INV,
+ GSP_EMU_REG_VF_L2_SYSMEM_INV_WIN,
+ GSP_EMU_REG_VF_L2_PEERMEM_INV_WIN,
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(uflush_regs); i++) {
+ u32 val = gsp_emu_shadow_read32(ctx,
+ uflush_regs[i]);
+
+ if (!val)
+ continue;
+ gsp_emu_shadow_write32(ctx, uflush_regs[i], 0);
+ pr_info_ratelimited("GSP-BOOT: UFLUSH[0x%x] 0x%x auto-cleared\n",
+ uflush_regs[i], val);
+ }
}
/* CrashCat wayfinder suppression: if the GSP falcon's DEBUGINFO
@@ -312,6 +361,13 @@ static void gsp_emu_poll_boot(struct gsp_emu_context *ctx)
*/
if ((gsp_cpuctl & GSP_EMU_CPUCTL_STARTCPU) &&
ctx->boot_stage == GSP_BOOT_STAGE_IDLE) {
+ /* A fresh FWSEC kick means a new GSP boot: drop any GSP-RM
+ * queue state left from the previous probe (the driver has
+ * already torn down its old queue). Without this the next
+ * Booter completion would skip the chain re-parse
+ * (rm->parsed still set) and GspStatusQueueInit would time
+ * out on re-probe. */
+ gsp_rm_emu_reset(ctx);
gsp_emu_shadow_write32(ctx, GSP_EMU_REG_VBIOS_SCRATCH_0E, 0);
gsp_emu_shadow_write32(ctx, GSP_EMU_REG_WPR2_ADDR_HI, 0x100000);
gsp_emu_shadow_write32(ctx, GSP_EMU_REG_WPR2_ADDR_LO,