diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-09 19:27:48 -0500 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-09 19:27:48 -0500 |
| commit | a478f0b418bd3dd5fbcc6260979865160ca25cf0 (patch) | |
| tree | e29fb7ec56741b9be1d8e747c108cb661b89d6c2 /kernel/gsp_emulation.c | |
| parent | 4a2cf8292ad37767de22a26e7e35d0a8a06edad3 (diff) | |
| download | open-iov-a478f0b418bd3dd5fbcc6260979865160ca25cf0.tar.gz open-iov-a478f0b418bd3dd5fbcc6260979865160ca25cf0.zip | |
vgsp: raise the GSP engine event interrupt so guest ISR drains events and wakes poll() waiters
The emulated GSP only signalled the MSI-X eventfd when it queued a status
record. The guest's primary ISR (nvidia_isr) decides pending from the
CPU_INTR leaf registers, which we never set for the GSP stall vector, so it
returned IRQ_NONE: IRQ 33 counted thousands of interrupts while the nvidia
event thread sat idle and a client blocked in poll() on /dev/nvidia0 (a GLX
swap) waited forever even though the event was already in the status queue.
Mirror what a real GSP-RM does when it queues an event:
- program RISC-V IRQMASK/IRQDEST for SWGEN0 (read-only to the guest; the
GSP firmware owns them),
- set SWGEN0 in the falcon IRQSTAT,
- latch GSP stall vector 155 in the CPU interrupt tree (LEAF[4] bit 27,
UVM_SHARED subtree 2 -> TOP[0] bit 2),
- signal MSI-X.
Raise it only for POST_EVENT records (replies are polled by the client).
Verified on r1680: the guest now writes IRQSCLR SWGEN0, kgspService_TU102
drains events, and glxgears renders (was a hard hang).
Diffstat (limited to 'kernel/gsp_emulation.c')
| -rw-r--r-- | kernel/gsp_emulation.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/kernel/gsp_emulation.c b/kernel/gsp_emulation.c index 47c8151..38f6284 100644 --- a/kernel/gsp_emulation.c +++ b/kernel/gsp_emulation.c @@ -644,6 +644,63 @@ 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 * ============================================================ */ |
