diff options
Diffstat (limited to 'kernel/gsp_emulation.c')
| -rw-r--r-- | kernel/gsp_emulation.c | 92 |
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); |
