summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_emulation.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gsp_emulation.c')
-rw-r--r--kernel/gsp_emulation.c92
1 files changed, 92 insertions, 0 deletions
diff --git a/kernel/gsp_emulation.c b/kernel/gsp_emulation.c
index 47c8151..b82bab1 100644
--- a/kernel/gsp_emulation.c
+++ b/kernel/gsp_emulation.c
@@ -644,10 +644,99 @@ void gsp_emu_inject_completion_interrupt(struct gsp_emu_context *ctx, u32 seq_id
eventfd_signal(trigger);
}
+/*
+ * Raise the GSP engine event interrupt.
+ *
+ * Mirrors what a real GSP-RM does when it queues an event for the host:
+ * - program the RISC-V interrupt mask/destination so SWGEN0 is routed to
+ * the CPU (read-only to the guest; the GSP firmware owns them),
+ * - set SWGEN0 in the falcon IRQSTAT,
+ * - latch the GSP stall vector (155) in the host CPU interrupt tree:
+ * LEAF[4] bit 27 and TOP[0] bit 2 (UVM_SHARED subtree 2),
+ * - signal MSI-X.
+ *
+ * Without the leaf bit the guest's primary ISR returns IRQ_NONE and the
+ * threaded handler (kgspService_TU102 -> kgspRpcRecvEvents -> nv_post_event)
+ * never runs, so a client blocked in poll() on /dev/nvidia0 (a GLX swap)
+ * waits forever even though the event is sitting in the status queue.
+ */
+void gsp_emu_raise_gsp_event_irq(struct gsp_emu_context *ctx)
+{
+ u32 reg, val;
+ u32 leaf_idx = GSP_EMU_GSP_EVENT_VECTOR / 32;
+ u32 leaf_bit = GSP_EMU_GSP_EVENT_VECTOR % 32;
+ u32 subtree = GSP_EMU_GSP_EVENT_VECTOR / 64;
+ u32 top_idx = subtree / 32;
+ u32 top_bit = subtree % 32;
+
+ if (!ctx || !ctx->bar0_vaddr)
+ return;
+
+ /* IRQMASK / IRQDEST: route SWGEN0 to the CPU (idempotent). */
+ reg = GSP_EMU_REG_RISCV_IRQMASK;
+ val = gsp_emu_shadow_read32(ctx, reg);
+ if (!(val & GSP_EMU_FALCON_IRQSTAT_SWGEN0))
+ gsp_emu_shadow_write32(ctx, reg,
+ val | GSP_EMU_FALCON_IRQSTAT_SWGEN0);
+ reg = GSP_EMU_REG_RISCV_IRQDEST;
+ val = gsp_emu_shadow_read32(ctx, reg);
+ if (!(val & GSP_EMU_FALCON_IRQSTAT_SWGEN0))
+ gsp_emu_shadow_write32(ctx, reg,
+ val | GSP_EMU_FALCON_IRQSTAT_SWGEN0);
+
+ /* Pending source: SWGEN0. */
+ reg = GSP_EMU_REG_FALCON_IRQSTAT;
+ val = gsp_emu_shadow_read32(ctx, reg);
+ gsp_emu_shadow_write32(ctx, reg,
+ val | GSP_EMU_FALCON_IRQSTAT_SWGEN0);
+
+ /* CPU interrupt tree: LEAF[leaf_idx] |= bit, TOP[top_idx] |= bit. */
+ reg = GSP_EMU_REG_CPU_INTR_LEAF_BASE + leaf_idx * 4;
+ val = gsp_emu_shadow_read32(ctx, reg);
+ gsp_emu_shadow_write32(ctx, reg, val | (1U << leaf_bit));
+ reg = GSP_EMU_REG_CPU_INTR_TOP_BASE + top_idx * 4;
+ val = gsp_emu_shadow_read32(ctx, reg);
+ gsp_emu_shadow_write32(ctx, reg, val | (1U << top_bit));
+
+ gsp_emu_inject_completion_interrupt(ctx, 0);
+}
+
/* ============================================================
* Emulation Thread
* ============================================================ */
+/* Advance the emulated GPU PTIMER (NV_PTIMER_TIME_0/1 at BAR0 0x9400/0x9410).
+ *
+ * The guest's nvidia.ko RM uses this register as its time source for timed
+ * waits (e.g. _nv015102rm -> _nv018147rm -> _nv018351rm). BAR0 is a plain
+ * host shadow mapped to the guest, so nothing advances these registers by
+ * default: every RM deadline becomes unreachable and the DDX/GL client
+ * spins forever inside the driver (observed as an Xorg hang at GDI/window
+ * creation). Seed from the existing value to keep time continuous, then
+ * advance by the host monotonic clock.
+ */
+static void gsp_emu_tick_ptimer(struct gsp_emu_context *ctx)
+{
+ u64 now, val;
+
+ if (!ctx->bar0_vaddr)
+ return;
+
+ now = ktime_get_ns();
+ if (!ctx->ptimer_seeded) {
+ u32 lo = gsp_emu_shadow_read32(ctx, 0x9400);
+ u32 hi = gsp_emu_shadow_read32(ctx, 0x9410);
+
+ ctx->ptimer_base = ((u64)hi << 32) | lo;
+ ctx->ptimer_t0 = now;
+ ctx->ptimer_seeded = true;
+ }
+
+ val = ctx->ptimer_base + (now - ctx->ptimer_t0);
+ gsp_emu_shadow_write32(ctx, 0x9400, (u32)val);
+ gsp_emu_shadow_write32(ctx, 0x9410, (u32)(val >> 32));
+}
+
static int gsp_emu_thread_fn(void *data)
{
struct gsp_emu_context *ctx = data;
@@ -659,6 +748,9 @@ static int gsp_emu_thread_fn(void *data)
nv_vgpu_info(ctx->vgpu, "GSP emulation thread started");
while (!kthread_should_stop()) {
+ /* Keep the guest-visible GPU timer advancing (RM timed waits) */
+ gsp_emu_tick_ptimer(ctx);
+
/* Read doorbell */
doorbell_val = gsp_emu_read_doorbell(ctx);