summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_emulation.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-21 14:10:43 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-21 14:11:45 -0400
commitc8249854284c3e8181587b96e3c5b4177d705e0c (patch)
treef0f1c16b055c88b059d159db715407d2f7406090 /kernel/gsp_emulation.c
parent85d888e4763837a5b2f5843c771bf6b6d6e6bfe3 (diff)
downloadopen-iov-c8249854284c3e8181587b96e3c5b4177d705e0c.tar.gz
open-iov-c8249854284c3e8181587b96e3c5b4177d705e0c.zip
kernel: Present physical TU104 to guest + emulate GSP boot (FWSEC/Booter) + VBIOS serving
Guest driver probe fixes (unmodified 570.144 open driver on physical path): - kernel/main.c: add show_description to the mdev driver. Userspace mdev management tools parse 'framebuffer=<n>M' from every mdev type's description sysfs file before creating an mdev; the missing file made such tools fail to create the vGPU. Also fix create_mdev.sh for the real type dir name pci-nvidia-vgpu-* (mdev core names dirs dev_driver_string(parent)-<sysfs_name>; the parent registers before nvidia.ko binds so dev_driver_string == 'pci'). - kernel/bar_emulation.c: seed BAR0 shadow so guest sees a REAL, PHYSICAL, non-virtual TU104 (user directive: no PV/VF vGPU paths, unmodified driver): * PMC_BOOT_0/1/42 = physical TU104 values captured from host hardware (0x164000a1 / 0x0 / 0x164a1000). Without these, halmgrGetHalForGpu() fails and probe aborts with 'does not include the required GSP'. * Serve a valid VBIOS at the PROM window (BAR0+0x300000) loaded from /lib/firmware/tu104_vbios.bin (dump of host TU104 ROM). Guest's kgspExtractVbiosFromRom_TU102 needs it for the mandatory FWSEC ucode (kgspGetFrtsSize is hardwired to 1MB on Turing). * Pre-set GSP secure-boot (GFW) completion state: PGC6 PLM (0x118128)=1, GFW_BOOT progress (0x118234)=0xFF, GSP falcon CPUCTL HALTED (0x110100). The driver's post-FLR GFW wait only *waits* (never kicks), so the pre-set state satisfies it immediately. - kernel/gsp_emulation.c/h: physical GSP boot emulation. * Falcon boot-watch: poll GSP falcon/PGC6/WPR2/scratch registers and log changes (guest BAR0 is mmap-based, otherwise invisible to host). * gsp_emu_poll_boot: STARTCPU auto-complete state machine. Guest kicks FWSEC then Booter Load via GSP falcon CPUCTL; detect each kick and complete the stage (HALTED + FRTS err NONE + WPR2 + mailbox + RISCV-active) so the guest believes its ucodes ran. Diagnosis that led here (documented in Handoff-vGPU-IO.md): - Open nvidia.ko can never be an SR-IOV VF vGPU guest: os_is_grid_supported() is a stub returning FALSE in the open build (disassembled). PV path also rejected per user directive. Only the physical path with a fully emulated local GSP boot remains.
Diffstat (limited to 'kernel/gsp_emulation.c')
-rw-r--r--kernel/gsp_emulation.c173
1 files changed, 173 insertions, 0 deletions
diff --git a/kernel/gsp_emulation.c b/kernel/gsp_emulation.c
index bfd8be7..81c319f 100644
--- a/kernel/gsp_emulation.c
+++ b/kernel/gsp_emulation.c
@@ -66,6 +66,173 @@ static inline void gsp_emu_copy_response_to_shared(struct gsp_emu_context *ctx,
}
/* ============================================================
+ * GSP Falcon Boot-Watch (observability for physical GSP boot)
+ * ============================================================ */
+
+/*
+ * The guest's physical GSP boot (FWSEC -> Booter -> RISCV) pokes the GSP
+ * falcon block via the mmap'd shadow, which the host cannot trap. Poll these
+ * BAR0 offsets and log changes so the boot sequence is visible in host dmesg.
+ */
+static const struct {
+ u32 offset;
+ const char *name;
+} gsp_emu_falcon_watch[GSP_EMU_FALCON_WATCH_COUNT] = {
+ { 0x110040, "GSP_MAILBOX0" },
+ { 0x110044, "GSP_MAILBOX1" },
+ { 0x110080, "GSP_FALCON_OS" },
+ { 0x110100, "GSP_CPUCTL" },
+ { 0x110180, "GSP_IMEMC" },
+ { 0x110184, "GSP_IMEMD" },
+ { 0x1101c0, "GSP_DMEMC" },
+ { 0x1101c4, "GSP_DMEMD" },
+ { 0x1103c0, "GSP_ENGINE" },
+ { 0x110c04, "GSP_QUEUE_HEAD1" },
+ { 0x110c08, "GSP_QUEUE_TAIL0" },
+ { 0x110c0c, "GSP_QUEUE_TAIL1" },
+ { 0x118128, "PGC6_PLM_GFW" },
+ { 0x118234, "PGC6_GFW_BOOT" },
+ { 0x1fa824, "WPR2_ADDR_LO" },
+ { 0x1fa828, "WPR2_ADDR_HI" },
+ { 0x001438, "VBIOS_SCRATCH_0E" },
+};
+
+static void gsp_emu_poll_falcon_watch(struct gsp_emu_context *ctx)
+{
+ int i;
+
+ for (i = 0; i < GSP_EMU_FALCON_WATCH_COUNT; i++) {
+ u32 val = readl(ctx->bar0_vaddr + gsp_emu_falcon_watch[i].offset);
+
+ if (!ctx->falcon_watch_valid) {
+ ctx->falcon_watch_last[i] = val;
+ continue;
+ }
+
+ if (val != ctx->falcon_watch_last[i]) {
+ nv_vgpu_info(ctx->vgpu, "FALCON %s (0x%x): 0x%08x -> 0x%08x",
+ gsp_emu_falcon_watch[i].name,
+ gsp_emu_falcon_watch[i].offset,
+ ctx->falcon_watch_last[i], val);
+ ctx->falcon_watch_last[i] = val;
+ }
+ }
+ ctx->falcon_watch_valid = true;
+}
+
+/* ============================================================
+ * Physical GSP Boot Emulation (FWSEC / Booter falcon auto-complete)
+ * ============================================================ */
+
+/* BAR0 shadow offsets used by the guest's physical GSP boot */
+#define GSP_EMU_REG_GSP_CPUCTL 0x110100 /* bit1 STARTCPU, bit4 HALTED */
+#define GSP_EMU_REG_GSP_ENGINE 0x1103c0 /* bit0 RESET */
+#define GSP_EMU_REG_GSP_MAILBOX0 0x110040
+#define GSP_EMU_REG_VBIOS_SCRATCH_0E 0x001438 /* FRTS err code (0=NONE) */
+#define GSP_EMU_REG_WPR2_ADDR_LO 0x1fa824
+#define GSP_EMU_REG_WPR2_ADDR_HI 0x1fa828
+#define GSP_EMU_REG_RISCV_STATUS 0x110240 /* bit0 ACTIVE (NV_PRISCV_RISCV
+ CORE_SWITCH_RISCV_STATUS at
+ GSP riscv base 0x110000) */
+
+#define GSP_EMU_CPUCTL_STARTCPU 0x2
+#define GSP_EMU_CPUCTL_HALTED 0x10
+
+enum gsp_emu_boot_stage {
+ GSP_BOOT_STAGE_IDLE = 0, /* waiting for first kick (FWSEC) */
+ GSP_BOOT_STAGE_FWSEC_DONE, /* FWSEC FRTS completed */
+ GSP_BOOT_STAGE_BOOTER_DONE, /* Booter Load completed */
+};
+
+static inline void gsp_emu_shadow_write32(struct gsp_emu_context *ctx,
+ u32 offset, u32 val)
+{
+ writel(val, ctx->bar0_vaddr + offset);
+}
+
+static inline u32 gsp_emu_shadow_read32(struct gsp_emu_context *ctx, u32 offset)
+{
+ return readl(ctx->bar0_vaddr + offset);
+}
+
+/*
+ * gsp_emu_poll_boot - drive the guest's physical GSP boot to completion.
+ *
+ * The guest driver kicks falcon ucodes (FWSEC for FRTS, then Booter Load for
+ * GSP-RM) by writing STARTCPU to the GSP falcon CPUCTL, then polls for HALTED
+ * plus stage-specific result registers. The auto-boot waits (GFW) are already
+ * satisfied by the state pre-set in nv_vgpu_bar_emul_init. Here we watch for
+ * STARTCPU kicks and complete each stage so the guest believes its falcon
+ * ucodes ran successfully.
+ */
+static void gsp_emu_poll_boot(struct gsp_emu_context *ctx)
+{
+ u32 cpuctl = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_GSP_CPUCTL);
+ u32 engine = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_GSP_ENGINE);
+
+ /* ENGINE reset asserted -> falcon rebooted, restart stage tracking */
+ if ((engine & 0x1) && ctx->boot_stage != GSP_BOOT_STAGE_IDLE) {
+ ctx->boot_stage = GSP_BOOT_STAGE_IDLE;
+ ctx->flcn_kick_count = 0;
+ nv_vgpu_info(ctx->vgpu, "GSP-BOOT: falcon reset, back to IDLE");
+ return;
+ }
+
+ /* Nothing to do unless the guest kicked the falcon */
+ if (!(cpuctl & GSP_EMU_CPUCTL_STARTCPU))
+ return;
+
+ ctx->flcn_kick_count++;
+
+ switch (ctx->boot_stage) {
+ case GSP_BOOT_STAGE_IDLE:
+ /*
+ * First kick after reset: FWSEC (FRTS setup). Complete it:
+ * - CPUCTL: clear STARTCPU, set HALTED
+ * - VBIOS_SCRATCH[0x0E] FRTS_ERR_CODE = NONE (0)
+ * - WPR2_ADDR_HI != 0, WPR2_ADDR_LO = frtsOffset >> 0xc
+ * frtsOffset is learned from the driver's own error message if
+ * our value mismatches (deterministic for fixed FB + fw image).
+ */
+ gsp_emu_shadow_write32(ctx, GSP_EMU_REG_VBIOS_SCRATCH_0E, 0);
+ gsp_emu_shadow_write32(ctx, GSP_EMU_REG_WPR2_ADDR_HI, 0x1);
+ gsp_emu_shadow_write32(ctx, GSP_EMU_REG_WPR2_ADDR_LO,
+ ctx->wpr2_addr_lo);
+ gsp_emu_shadow_write32(ctx, GSP_EMU_REG_GSP_CPUCTL,
+ GSP_EMU_CPUCTL_HALTED);
+ ctx->boot_stage = GSP_BOOT_STAGE_FWSEC_DONE;
+ nv_vgpu_info(ctx->vgpu, "GSP-BOOT: FWSEC kick completed "
+ "(WPR2_LO=0x%x)", ctx->wpr2_addr_lo);
+ break;
+
+ case GSP_BOOT_STAGE_FWSEC_DONE:
+ /*
+ * Second kick: Booter Load (loads/authenticates GSP-RM image,
+ * starts the RISCV). Complete it:
+ * - CPUCTL: clear STARTCPU, set HALTED
+ * - MAILBOX0 = 0 (booter success status)
+ * - RISCV_STATUS = ACTIVE (kflcnIsRiscvActive passes)
+ */
+ gsp_emu_shadow_write32(ctx, GSP_EMU_REG_GSP_MAILBOX0, 0);
+ gsp_emu_shadow_write32(ctx, GSP_EMU_REG_RISCV_STATUS, 0x1);
+ gsp_emu_shadow_write32(ctx, GSP_EMU_REG_GSP_CPUCTL,
+ GSP_EMU_CPUCTL_HALTED);
+ ctx->boot_stage = GSP_BOOT_STAGE_BOOTER_DONE;
+ nv_vgpu_info(ctx->vgpu, "GSP-BOOT: Booter kick completed, "
+ "RISCV active");
+ break;
+
+ default:
+ /* Later kicks (e.g. resume/unload): just complete them */
+ gsp_emu_shadow_write32(ctx, GSP_EMU_REG_GSP_CPUCTL,
+ GSP_EMU_CPUCTL_HALTED);
+ nv_vgpu_info(ctx->vgpu, "GSP-BOOT: extra kick #%u completed",
+ ctx->flcn_kick_count);
+ break;
+ }
+}
+
+/* ============================================================
* RPC Processing
* ============================================================ */
@@ -276,6 +443,12 @@ static int gsp_emu_thread_fn(void *data)
/* Read doorbell */
doorbell_val = gsp_emu_read_doorbell(ctx);
+ /* Watch GSP falcon boot registers (physical GSP boot tracing) */
+ gsp_emu_poll_falcon_watch(ctx);
+
+ /* Drive the guest's physical GSP boot to completion */
+ gsp_emu_poll_boot(ctx);
+
if (doorbell_val != 0) {
/* ACK doorbell immediately */
gsp_emu_write_doorbell(ctx, 0);