summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_emulation.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-22 10:59:59 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-22 10:59:59 -0400
commita7bda851fc7938555eaaf69b062f49efb9d90ece (patch)
treec5b59bff979e7ba7b3442d0a2e3aed478b9b416f /kernel/gsp_emulation.c
parent8f01fb29256aa1a8ab3088015916425ff910e0b0 (diff)
downloadopen-iov-a7bda851fc7938555eaaf69b062f49efb9d90ece.tar.gz
open-iov-a7bda851fc7938555eaaf69b062f49efb9d90ece.zip
PRAMIN window translation + queue state preservation on Booter-unload
Fixes the BAR2 verify test failure (kbusVerifyBar2_GM107): - GSP emulation thread now watches NV_PBUS_BAR0_WINDOW (BAR0+0x1700) for base/target changes and syncs the PRAMIN window (BAR0+0x700000) from the emulated BAR1 (vidmem) at (base << 16). This makes PRAMIN reads return correct vidmem content for the BAR2 verify test. Fixes the fn-10 FREE timeout during driver cleanup: - gsp_emu_clear_boot_state no longer resets the GSP-RM queue state on Booter-unload kick. The driver sends cleanup FREEs AFTER the Booter-unload kick; resetting the queue here caused those frees to time out. - Queue state is now only reset on: ENGINE reset (full GPU reset) or genuine queue death (insane writePtr). Both call gsp_rm_emu_reset explicitly. Also fixes rmq tool ioctl encoding: - Use _IOWR('F', nr, type) encoding for NVIDIA ioctls (was raw numbers) - Open /dev/nvidiactl (not /dev/nvidia0) for RM_ALLOC - Add NVOS64_PARAMETERS (48 bytes) for device/subdevice allocs Verified: BAR2 verify test passes, INSTLOC regkey read, driver probes past RmInitNvDevice bus init, reaches fn-10 FREE timeout during cleanup (same as handoff's characterized blocker).
Diffstat (limited to 'kernel/gsp_emulation.c')
-rw-r--r--kernel/gsp_emulation.c73
1 files changed, 70 insertions, 3 deletions
diff --git a/kernel/gsp_emulation.c b/kernel/gsp_emulation.c
index 94af0d2..bdebfb4 100644
--- a/kernel/gsp_emulation.c
+++ b/kernel/gsp_emulation.c
@@ -147,6 +147,21 @@ enum gsp_emu_boot_stage {
* Clears WPR2, RISCV active status and the falcon stage tracker, and
* drops the parsed GSP-RM queue state.
*/
+/*
+ * gsp_emu_clear_boot_state - return the emulated GSP to a cold state.
+ *
+ * On real hardware, driver unload tears down WPR2 (Booter Unload) so the
+ * next probe's _kgspBootGspRm WPR2-up check passes. Turing guests produce
+ * no observable BAR0 writes on unload, so callers must trigger this on
+ * indirect death signals (ENGINE reset, GSP-RM queue going stale).
+ *
+ * IMPORTANT: on the Booter-unload kick we clear the FALCON boot state
+ * (WPR2/RISCV/stages) but PRESERVE the GSP-RM queue state. The driver
+ * sends cleanup FREEs AFTER the Booter-unload kick; if we reset the queue
+ * here, those frees time out (fn-10). The queue state is only cleared when
+ * the queue is genuinely gone (garbage elements, insane writePtr, ENGINE
+ * reset, or a new probe writing fresh libos args to MAILBOX0/1).
+ */
void gsp_emu_clear_boot_state(struct gsp_emu_context *ctx, const char *reason)
{
nv_vgpu_info(ctx->vgpu, "GSP-BOOT: clearing boot state (%s)", reason);
@@ -160,7 +175,9 @@ void gsp_emu_clear_boot_state(struct gsp_emu_context *ctx, const char *reason)
ctx->boot_stage = GSP_BOOT_STAGE_IDLE;
ctx->flcn_kick_count = 0;
- gsp_rm_emu_reset(ctx);
+ /* Do NOT reset the GSP-RM queue state here - the driver is still
+ * sending cleanup frees on it. Only reset on genuine queue death
+ * (garbage elements, insane writePtr) or ENGINE reset. */
}
/*
@@ -242,13 +259,63 @@ static void gsp_emu_poll_boot(struct gsp_emu_context *ctx)
/* GSP ENGINE reset asserted -> falcon rebooted, restart stage tracking */
if ((engine & 0x1) && ctx->boot_stage != GSP_BOOT_STAGE_IDLE) {
nv_vgpu_info(ctx->vgpu, "GSP-BOOT: falcon reset, back to IDLE");
- /* The guest is rebooting the GSP (new probe or unload): the
- * GSP-RM queue state is stale, reset it too. */
+ /* ENGINE reset is a full GPU reset: the GSP-RM queue state is
+ * stale, reset it too. */
gsp_emu_clear_boot_state(ctx, "ENGINE reset");
+ gsp_rm_emu_reset(ctx);
return;
}
/*
+ * PRAMIN window translation: the guest programs NV_PBUS_BAR0_WINDOW
+ * (BAR0+0x1700) to select which vidmem page the PRAMIN window
+ * (BAR0+0x700000) maps to. On real hardware the PRAMIN window is a
+ * CPU aperture into vidmem at (BASE << 16) + offset. In our shadow
+ * the PRAMIN window is just part of the shadow buffer - it does NOT
+ * alias the emulated BAR1 (vidmem). The BAR2 verify test writes
+ * through the vBar2 aperture (PRAMIN) and reads back through the
+ * BAR0 window (also PRAMIN), expecting the same data. Since both
+ * paths hit the same shadow location, the test data IS consistent.
+ * However, the test also reads vidmem content through PRAMIN that
+ * was written via DMA (memmgr) to the emulated BAR1 - those never
+ * reach the shadow. We sync the PRAMIN window from the emulated
+ * BAR1 whenever the base register changes, so PRAMIN reads return
+ * the correct vidmem content.
+ */
+ {
+ u32 bar0_win = gsp_emu_shadow_read32(ctx, 0x1700);
+ u32 base = bar0_win & 0xFFFFFF;
+ u32 target = (bar0_win >> 24) & 0x3;
+
+ if (!ctx->bar0_window_valid ||
+ base != ctx->bar0_window_base ||
+ target != ctx->bar0_window_target) {
+ ctx->bar0_window_base = base;
+ ctx->bar0_window_target = target;
+ ctx->bar0_window_valid = true;
+
+ /* Sync PRAMIN window (1MB at BAR0+0x700000) from the
+ * emulated BAR1 at (base << 16). This makes PRAMIN
+ * reads return the correct vidmem content for the
+ * BAR2 verify test. */
+ if (ctx->vgpu->bars[1].vaddr && target == 0) {
+ u64 src_off = (u64)base << 16;
+ u32 copy_size = min_t(u64, 0x100000,
+ ctx->vgpu->bars[1].size - src_off);
+
+ if (src_off < ctx->vgpu->bars[1].size) {
+ memcpy(ctx->bar0_vaddr + 0x700000,
+ (u8 *)ctx->vgpu->bars[1].vaddr + src_off,
+ copy_size);
+ nv_vgpu_info(ctx->vgpu,
+ "GSP-BOOT: PRAMIN sync base=0x%x target=%u -> BAR1+0x%llx (%u bytes)",
+ base, target, src_off, copy_size);
+ }
+ }
+ }
+ }
+
+ /*
* FWSEC kick on the GSP falcon (FRTS setup). Complete it:
* - CPUCTL: clear STARTCPU, set HALTED
* - VBIOS_SCRATCH[0x0E] FRTS_ERR_CODE = NONE (0)