From 6aadb593750736f239fd54a0ccef4b3834c08fc4 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Mon, 21 Sep 2026 18:01:55 -0400 Subject: kernel: GSP-RM msgq emulation + TU104 hardware tables (GET_GSP_STATIC_INFO, GSP_RM_CONTROL) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements the GSP-RM reply content layer for the msgq RPC channel: - kernel/gsp_rm_emulation.c/h: GSP-RM msgq emulation engine - Queue chain discovery (libos args → RMARGS → GSP_ARGUMENTS_CACHED) - Status queue link + GSP_INIT_DONE emission - Command queue polling + RPC dispatch - SET_GUEST_SYSTEM_INFO version negotiation (VGX 41.12) - GET_GSP_STATIC_INFO(65) with 1656-byte GspStaticConfigInfo - GSP_RM_CONTROL(76) sub-command dispatch: - FIFO_GET_DEVICE_INFO_TABLE (0x20801112) - INTERNAL_INTR_GET_KERNEL_TABLE (0x20800a5c) - GPU_GET_CONSTRUCTED_FALCON_INFO (0x208001b0) - INTERNAL_GET_DEVICE_INFO_TABLE (0x20800a40) - kernel/gsp_rm_tu104.h: TU104 hardware tables - 12-entry FIFO engine table (GR0, CE0-4, NVDEC0-1, NVENC0, SEC2, NVJPG, SW) - 8-entry interrupt table (from intr_phys_tu102.c) - 1-entry constructed falcon table (FBFLCN) - 11-entry internal device info table - GspStaticConfigInfo layout (1656 bytes, ABI-verified offsets) - Data from NVIDIA driver material (ext-tree) + hwref/turing/tu104 - tools/rmq/: Userspace RM API client (oracle for ground-truth tables) KNOWN ISSUE: gsp_rm_send crashes (fortify_panic) on multi-element messages when reply data exceeds one 4096-byte queue element. Affects cmd 0x20800a41 (paramsSize=8204). Multi-element splitting is the next fix. --- kernel/Makefile | 1 + kernel/bar_emulation.c | 27 ++ kernel/gsp_emulation.c | 135 +++--- kernel/gsp_emulation.h | 37 +- kernel/gsp_rm_emulation.c | 1022 +++++++++++++++++++++++++++++++++++++++++++++ kernel/gsp_rm_emulation.h | 151 +++++++ kernel/gsp_rm_tu104.h | 694 ++++++++++++++++++++++++++++++ 7 files changed, 2004 insertions(+), 63 deletions(-) create mode 100644 kernel/gsp_rm_emulation.c create mode 100644 kernel/gsp_rm_emulation.h create mode 100644 kernel/gsp_rm_tu104.h (limited to 'kernel') diff --git a/kernel/Makefile b/kernel/Makefile index 98adfd4..83eab9b 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -6,6 +6,7 @@ open-iov-vfio-y := \ bar_emulation.o \ gsp_comm.o \ gsp_emulation.o \ + gsp_rm_emulation.o \ gsp_rpc_client.o \ gsp_rpc_integration.o \ memory.o \ diff --git a/kernel/bar_emulation.c b/kernel/bar_emulation.c index e120b3c..eaa006c 100644 --- a/kernel/bar_emulation.c +++ b/kernel/bar_emulation.c @@ -252,6 +252,33 @@ int nv_vgpu_bar_emul_init(struct nv_vgpu_device *vgpu) *(u32 *)((u8 *)ctx->bar0_shadow + 0x118234) = 0x000000ff; *(u32 *)((u8 *)ctx->bar0_shadow + 0x110100) = 0x00000010; + /* + * More post-GFW-boot state the driver validates before/while booting + * GSP-RM: + * 0x1100f4 GSP falcon HWCFG2 bit10 RISCV = ENABLE. Checked by + * kflcnIsRiscvCpuEnabled in kgspPrepareForBootstrap; + * without it probe dies with "RISC-V core is not enabled". + * 0x009430 NV_PTIMER_TIME_PRIV_LEVEL_MASK bit4 + * WRITE_PROTECTION_LEVEL0 = ENABLE. FWSEC lowers PLMs + * after GFW boot; driver asserts "Write to PTIMER + * attempted even though Level 0 PLM is disabled" when + * setting the GPU clock otherwise. + */ + *(u32 *)((u8 *)ctx->bar0_shadow + 0x1100f4) = 0x00000400; + *(u32 *)((u8 *)ctx->bar0_shadow + 0x009430) = 0x00000010; + + /* + * 0x100ce0 NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE - usable FB size. + * Encoded as fbSize = LOWER_MAG << (LOWER_SCALE + 20). Set to + * 256MB (matches our BAR1): mag=1, scale=8 -> 1 << 28. On real + * hardware VBIOS devinit programs this after GFW boot; the guest + * driver's kgspPopulateWprMeta reads it via kmemsysGetUsableFbSize + * and uses it to compute the FRTS offset (fbSize - reservedFromEnd). + * With it 0, the FRTS offset underflows and FWSEC verification + * fails with "WPR2 initialized at an unexpected location". + */ + *(u32 *)((u8 *)ctx->bar0_shadow + 0x100ce0) = 0x00000018; + /* * Serve a valid VBIOS at the PROM window (BAR0+0x300000). * diff --git a/kernel/gsp_emulation.c b/kernel/gsp_emulation.c index 81c319f..f96e521 100644 --- a/kernel/gsp_emulation.c +++ b/kernel/gsp_emulation.c @@ -21,6 +21,7 @@ #include "nvidia-vgpu-vfio.h" #include "gsp_emulation.h" +#include "gsp_rm_emulation.h" #include "gsp_rpc_client.h" #include "bar_emulation.h" #include "irq.h" @@ -95,6 +96,9 @@ static const struct { { 0x1fa824, "WPR2_ADDR_LO" }, { 0x1fa828, "WPR2_ADDR_HI" }, { 0x001438, "VBIOS_SCRATCH_0E" }, + { 0x840040, "SEC2_MAILBOX0" }, + { 0x840100, "SEC2_CPUCTL" }, + { 0x8403c0, "SEC2_ENGINE" }, }; static void gsp_emu_poll_falcon_watch(struct gsp_emu_context *ctx) @@ -124,19 +128,8 @@ static void gsp_emu_poll_falcon_watch(struct gsp_emu_context *ctx) * 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 +/* Register offsets + CPUCTL bits live in gsp_emulation.h (shared with + * gsp_rm_emulation.c), as are gsp_emu_shadow_read32/write32. */ enum gsp_emu_boot_stage { GSP_BOOT_STAGE_IDLE = 0, /* waiting for first kick (FWSEC) */ @@ -144,17 +137,6 @@ enum gsp_emu_boot_stage { 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. * @@ -167,69 +149,74 @@ static inline u32 gsp_emu_shadow_read32(struct gsp_emu_context *ctx, u32 offset) */ static void gsp_emu_poll_boot(struct gsp_emu_context *ctx) { - u32 cpuctl = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_GSP_CPUCTL); + u32 gsp_cpuctl = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_GSP_CPUCTL); + u32 sec2_cpuctl = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_SEC2_CPUCTL); u32 engine = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_GSP_ENGINE); - /* ENGINE reset asserted -> falcon rebooted, restart stage tracking */ + /* GSP 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"); + /* The guest is rebooting the GSP (new probe or unload): the + * GSP-RM queue state is stale, reset it too. */ + gsp_rm_emu_reset(ctx); 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). - */ + /* + * FWSEC kick on the GSP falcon (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 (in _VAL bits31:4) + */ + if ((gsp_cpuctl & GSP_EMU_CPUCTL_STARTCPU) && + ctx->boot_stage == GSP_BOOT_STAGE_IDLE) { 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_HI, 0x100000); 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; + ctx->flcn_kick_count++; 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); + return; + } + + /* + * Booter Load kick on the SEC2 falcon (loads/authenticates the GSP-RM + * image and starts the GSP RISCV). Complete it: + * - SEC2 CPUCTL: clear STARTCPU, set HALTED + * - SEC2 MAILBOX0 = 0 (booter error code, 0 = success) + * - GSP RISCV_STATUS = ACTIVE (Booter started the RISCV; + * kflcnIsRiscvActive passes) + */ + if ((sec2_cpuctl & GSP_EMU_CPUCTL_STARTCPU) && + ctx->boot_stage == GSP_BOOT_STAGE_FWSEC_DONE) { + gsp_emu_shadow_write32(ctx, GSP_EMU_REG_SEC2_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_shadow_write32(ctx, GSP_EMU_REG_SEC2_CPUCTL, GSP_EMU_CPUCTL_HALTED); ctx->boot_stage = GSP_BOOT_STAGE_BOOTER_DONE; + ctx->flcn_kick_count++; nv_vgpu_info(ctx->vgpu, "GSP-BOOT: Booter kick completed, " "RISCV active"); - break; + /* The driver has programmed the libos init args address into + * GSP MAILBOX0/1 by now; parse the queue chain, link the + * status queue and emit GSP_INIT_DONE. */ + gsp_rm_emu_on_booter_done(ctx); + return; + } - default: - /* Later kicks (e.g. resume/unload): just complete them */ + /* Any other STARTCPU kick (resume/unload/etc.): just complete it */ + if (gsp_cpuctl & GSP_EMU_CPUCTL_STARTCPU) 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; - } + if (sec2_cpuctl & GSP_EMU_CPUCTL_STARTCPU) + gsp_emu_shadow_write32(ctx, GSP_EMU_REG_SEC2_CPUCTL, + GSP_EMU_CPUCTL_HALTED); } /* ============================================================ @@ -449,6 +436,9 @@ static int gsp_emu_thread_fn(void *data) /* Drive the guest's physical GSP boot to completion */ gsp_emu_poll_boot(ctx); + /* GSP-RM msgq emulation (physical path: sysmem cmd/status queues) */ + gsp_rm_emu_poll(ctx); + if (doorbell_val != 0) { /* ACK doorbell immediately */ gsp_emu_write_doorbell(ctx, 0); @@ -518,6 +508,17 @@ int gsp_emu_init(struct nv_vgpu_device *vgpu) init_completion(&ctx->thread_stopped); spin_lock_init(&ctx->lock); + /* Physical GSP boot emulation state. wpr2_addr_lo matches what the + * guest driver expects WPR2_ADDR_LO to read after FWSEC FRTS: + * frtsOffset = fbSize - reservedFromEnd. With fbSize = 256MB + * (LOCAL_MEMORY_RANGE) and the 570.144 image's 2MB reservation, + * frtsOffset = 0x10000000 - 0x200000 = 0xfe00000; the register's + * _VAL field (bits31:4) must equal frtsOffset >> 12 = 0xfe00, so the + * raw register value is 0xfe000. */ + ctx->boot_stage = 0; /* GSP_BOOT_STAGE_IDLE */ + ctx->flcn_kick_count = 0; + ctx->wpr2_addr_lo = 0x000fe000; + /* The guest's BAR0 is the emulation shadow (vzalloc'd by * nv_vgpu_bar_emul_init, mapped to the guest via VFIO mmap) - NOT the * host physical BAR0 (vgpu->bars[0].vaddr, which belongs to the real GPU @@ -556,6 +557,13 @@ int gsp_emu_init(struct nv_vgpu_device *vgpu) /* Enable emulation mode in RPC client */ gsp_rpc_emulation_set(true); + /* Initialize GSP-RM msgq emulation (physical path) */ + ret = gsp_rm_emu_init(vgpu, ctx); + if (ret) { + nv_vgpu_err(vgpu, "Failed to init GSP-RM emulation: %d", ret); + goto err_fini_client; + } + /* NOTE: MSI-X eventfd triggers are NOT copied here - QEMU wires them * via VFIO_DEVICE_SET_IRQS later, so gsp_emu_inject_completion_interrupt * looks them up live from vgpu->irqs[]. */ @@ -605,6 +613,9 @@ void gsp_emu_cleanup(struct nv_vgpu_device *vgpu) } ctx->running = false; + /* Teardown GSP-RM msgq emulation */ + gsp_rm_emu_fini(ctx); + /* Disable emulation mode */ gsp_rpc_emulation_set(false); diff --git a/kernel/gsp_emulation.h b/kernel/gsp_emulation.h index 53560e9..668d567 100644 --- a/kernel/gsp_emulation.h +++ b/kernel/gsp_emulation.h @@ -38,7 +38,27 @@ * physical GSP boot sequence is visible on the host (guest BAR0 access is * mmap-based and otherwise invisible). Offsets are BAR0-relative, GSP falcon * block at 0x110000. */ -#define GSP_EMU_FALCON_WATCH_COUNT 17 +#define GSP_EMU_FALCON_WATCH_COUNT 20 + +/* 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 0x111240 /* bit0 ACTIVE (NV_PRISCV_RISCV + CORE_SWITCH_RISCV_STATUS=0x240 at + GSP riscvRegisterBase = + NV_FALCON2_GSP_BASE=0x111000) */ + +/* SEC2 falcon (Booter Load/Unload runs here, not on the GSP falcon) */ +#define GSP_EMU_REG_SEC2_CPUCTL 0x840100 +#define GSP_EMU_REG_SEC2_MAILBOX0 0x840040 +#define GSP_EMU_REG_SEC2_ENGINE 0x8403c0 + +#define GSP_EMU_CPUCTL_STARTCPU 0x2 +#define GSP_EMU_CPUCTL_HALTED 0x10 /* ============================================================ * GSP Emulation Context @@ -78,8 +98,23 @@ struct gsp_emu_context { u32 boot_stage; /* enum gsp_emu_boot_stage */ u32 flcn_kick_count; /* STARTCPU kicks seen since last ENGINE reset */ u32 wpr2_addr_lo; /* WPR2_ADDR_LO value to report after FWSEC */ + + /* GSP-RM msgq emulation (physical path, guest-sysmem queues) */ + struct gsp_rm_emu *rm_emu; }; +/* Shadow accessors shared with gsp_rm_emulation.c */ +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); +} + /* ============================================================ * Public API * ============================================================ */ diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c new file mode 100644 index 0000000..d3e4548 --- /dev/null +++ b/kernel/gsp_rm_emulation.c @@ -0,0 +1,1022 @@ +/* + * GSP-RM Emulation (physical-path) for Host-Side vGPU + * + * Emulates the GSP-RM msgq RPC channel in guest system memory. + * See gsp_rm_emulation.h for the design overview. + */ + +#include +#include +#include +#include + +#include "nvidia-vgpu-vfio.h" +#include "gsp_emulation.h" +#include "gsp_rm_emulation.h" +#include "gsp_rm_tu104.h" + +/* ============================================================ + * Guest-memory access ("virtual DMA" on behalf of the device) + * ============================================================ */ + +static int gsp_rm_read(struct gsp_emu_context *ctx, u64 gpa, void *buf, + size_t len) +{ + struct nv_vgpu_device *vgpu = ctx->vgpu; + + if (atomic_read(&vgpu->vfio_dev_open_count) <= 0) + return -ENODEV; + + return vfio_dma_rw(&vgpu->vfio_dev, gpa, buf, len, false); +} + +static int gsp_rm_write(struct gsp_emu_context *ctx, u64 gpa, void *buf, + size_t len) +{ + struct nv_vgpu_device *vgpu = ctx->vgpu; + + if (atomic_read(&vgpu->vfio_dev_open_count) <= 0) + return -ENODEV; + + return vfio_dma_rw(&vgpu->vfio_dev, gpa, buf, len, true); +} + +/* Translate a shared-buffer offset to a GPA via the buffer's page table */ +static u64 gsp_rm_off_to_gpa(struct gsp_rm_emu *rm, u64 off) +{ + u64 idx = off >> 12; + + if (idx >= rm->mq.pageTableEntryCount) + return 0; + return rm->ptes[idx] + (off & 0xfff); +} + +/* ============================================================ + * Status queue element construction + * ============================================================ */ + +/* XOR-fold checksum over u64s (message_queue_priv.h _checkSum32) */ +static u32 gsp_rm_checksum32(const void *data, u32 len) +{ + const u64 *p = data; + const u64 *end = (const u64 *)((const u8 *)data + len); + u64 sum = 0; + + while (p < end) + sum ^= *p++; + + return (u32)(sum >> 32) ^ (u32)sum; +} + +/* + * 570.144 GSP_MSG_QUEUE_ELEMENT wire layout (guest driver is 570.144 stock): + * 0x00 authTagBuffer[16] (zero - no CC) + * 0x10 aadBuffer[16] (zero - no CC) + * 0x20 checkSum (XOR-fold over msgLen, makes total 0) + * 0x24 seqNum (per-record sequence) + * 0x28 elemCount (elements this record spans) + * 0x30 rpc_message_header_v (32 bytes) + payload + * GSP_MSG_QUEUE_ELEMENT_HDR_SIZE = offsetof(rpc) = 0x30. + */ +#define GSPRM_ELEM_HDR_SIZE 0x30 + +/* + * Build one 4KB queue element carrying an rpc_message_header_v + payload. + * Returns message length (element header + rpc length). + */ +static u32 gsp_rm_build_element(struct gsp_rm_emu *rm, u8 *elem, + u32 function, u32 sequence, u32 result, + const void *data, u32 data_len) +{ + struct gsprm_rpc_header *hdr = + (struct gsprm_rpc_header *)(elem + GSPRM_ELEM_HDR_SIZE); + u32 rpc_len = sizeof(*hdr) + data_len; + u32 msg_len = GSPRM_ELEM_HDR_SIZE + rpc_len; + u32 aligned_len; + u32 elem_count = 1; + + memset(elem, 0, GSPRM_QUEUE_ELEMENT_SIZE); + /* elem+0x20: checksum, filled below */ + memcpy(elem + 0x24, &rm->stat_seq, 4); + memcpy(elem + 0x28, &elem_count, 4); + + hdr->header_version = GSPRM_RPC_HEADER_VERSION; + hdr->signature = GSPRM_RPC_SIGNATURE; + hdr->length = rpc_len; + hdr->function = function; + hdr->rpc_result = result; + hdr->rpc_result_private = result; + hdr->sequence = sequence; + hdr->spare = 0; + if (data && data_len) + memcpy((u8 *)(hdr + 1), data, data_len); + + /* checksum covers msg_len zero-padded to 8 (buffer already zeroed) */ + aligned_len = (msg_len + 7) & ~7U; + *(u32 *)(elem + 0x20) = gsp_rm_checksum32(elem, aligned_len); + + return aligned_len; +} + +/* + * Write one status queue element and advance the status TX writePtr. + */ +static int gsp_rm_send(struct gsp_emu_context *ctx, u32 function, + u32 sequence, u32 result, const void *data, u32 data_len) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + u8 *elem; + u64 elem_gpa; + u32 slot; + u32 new_wp; + int ret; + + elem = kzalloc(GSPRM_QUEUE_ELEMENT_SIZE, GFP_KERNEL); + if (!elem) + return -ENOMEM; + + gsp_rm_build_element(rm, elem, function, sequence, result, data, + data_len); + + slot = rm->stat_write % rm->stat_msgcount; + elem_gpa = gsp_rm_off_to_gpa(rm, rm->stat_off + rm->entry_off + + (u64)slot * GSPRM_QUEUE_ELEMENT_SIZE); + + ret = gsp_rm_write(ctx, elem_gpa, elem, GSPRM_QUEUE_ELEMENT_SIZE); + if (ret) { + nv_vgpu_err(ctx->vgpu, "GSPRM: status element write failed gpa=0x%llx ret=%d", + elem_gpa, ret); + kfree(elem); + return ret; + } + kfree(elem); + + rm->stat_write++; + rm->stat_seq++; + + /* bump status TX header writePtr (offset 0x10) */ + new_wp = rm->stat_write; + ret = gsp_rm_write(ctx, gsp_rm_off_to_gpa(rm, rm->stat_off) + 0x10, + &new_wp, sizeof(new_wp)); + if (ret) { + nv_vgpu_err(ctx->vgpu, "GSPRM: status writePtr update failed ret=%d", ret); + return ret; + } + + /* Notify the guest (MSI-X vector 0). Boot path polls anyway; this + * covers the driver's interrupt-driven drain later on. */ + gsp_emu_inject_completion_interrupt(ctx, rm->stat_seq); + + return 0; +} + +/* ============================================================ + * Queue-chain discovery (libos args -> RMARGS -> GSP_ARGUMENTS_CACHED) + * ============================================================ */ + +static int gsp_rm_parse_queue_chain(struct gsp_emu_context *ctx) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + struct gsprm_libos_region *regions; + int ret, i; + u64 gsp_args_gpa = 0; + u32 total_size; + + regions = kmalloc_array(GSPRM_MAX_LIBOS_REGIONS, sizeof(*regions), + GFP_KERNEL); + if (!regions) + return -ENOMEM; + + /* 1. Read the libos init args array */ + ret = gsp_rm_read(ctx, rm->libos_args_gpa, regions, + GSPRM_MAX_LIBOS_REGIONS * sizeof(*regions)); + if (ret) { + nv_vgpu_err(ctx->vgpu, "GSPRM: libos args read failed gpa=0x%llx ret=%d", + rm->libos_args_gpa, ret); + goto out; + } + + /* 2. Find the RMARGS region -> GSP_ARGUMENTS_CACHED */ + for (i = 0; i < GSPRM_MAX_LIBOS_REGIONS; i++) { + if (regions[i].id8 == 0) + break; + nv_vgpu_info(ctx->vgpu, "GSPRM: libos region[%d] id8=0x%llx pa=0x%llx size=0x%llx kind=%u loc=%u", + i, regions[i].id8, regions[i].pa, regions[i].size, + regions[i].kind, regions[i].loc); + if (regions[i].id8 == GSPRM_LIBOS_ID_RMARGS) + gsp_args_gpa = regions[i].pa; + } + if (!gsp_args_gpa) { + nv_vgpu_err(ctx->vgpu, "GSPRM: RMARGS region not found"); + ret = -ENOENT; + goto out; + } + rm->gsp_args_gpa = gsp_args_gpa; + + /* 3. Read GSP_ARGUMENTS_CACHED (MESSAGE_QUEUE_INIT_ARGUMENTS first) */ + ret = gsp_rm_read(ctx, gsp_args_gpa, &rm->mq, sizeof(rm->mq)); + if (ret) { + nv_vgpu_err(ctx->vgpu, "GSPRM: GSP_ARGUMENTS_CACHED read failed ret=%d", ret); + goto out; + } + + nv_vgpu_info(ctx->vgpu, "GSPRM: queue args sharedPA=0x%llx ptes=%u cmdOff=0x%llx statOff=0x%llx hdrSize=0x%llx elemMin=0x%llx elemMax=0x%llx hdrAlign=%u elemAlign=%u", + rm->mq.sharedMemPhysAddr, rm->mq.pageTableEntryCount, + rm->mq.cmdQueueOffset, rm->mq.statQueueOffset, + rm->mq.queueElementHdrSize, rm->mq.queueElementSizeMin, + rm->mq.queueElementSizeMax, rm->mq.queueHeaderAlign, + rm->mq.queueElementAlign); + print_hex_dump(KERN_INFO, "GSPRM args raw: ", DUMP_PREFIX_OFFSET, + 16, 8, &rm->mq, sizeof(rm->mq), false); + + if (!rm->mq.sharedMemPhysAddr || !rm->mq.pageTableEntryCount || + rm->mq.pageTableEntryCount > 1024) { + nv_vgpu_err(ctx->vgpu, "GSPRM: invalid queue args"); + ret = -EINVAL; + goto out; + } + + /* 4. Read the page table (RmPhysAddr per 4K page of the shared buffer) */ + rm->ptes = kmalloc_array(rm->mq.pageTableEntryCount, sizeof(u64), + GFP_KERNEL); + if (!rm->ptes) { + ret = -ENOMEM; + goto out; + } + ret = gsp_rm_read(ctx, rm->mq.sharedMemPhysAddr, rm->ptes, + rm->mq.pageTableEntryCount * sizeof(u64)); + if (ret) { + nv_vgpu_err(ctx->vgpu, "GSPRM: page table read failed ret=%d", ret); + goto out; + } + + /* 5. Compute queue geometry. NOTE: the shared buffer is + * NV_MEMORY_NONCONTIGUOUS - only queue-offsets are linear, GPAs are + * not. Every access must translate through rm->ptes (gsp_rm_off_to_gpa). */ + rm->cmd_off = rm->mq.cmdQueueOffset; + rm->stat_off = rm->mq.statQueueOffset; + rm->cmd_gpa = gsp_rm_off_to_gpa(rm, rm->cmd_off); + rm->stat_gpa = gsp_rm_off_to_gpa(rm, rm->stat_off); + rm->cmd_size = (u32)(rm->mq.statQueueOffset - rm->mq.cmdQueueOffset); + total_size = rm->mq.pageTableEntryCount << 12; + rm->stat_size = total_size - (u32)rm->mq.statQueueOffset; + rm->entry_off = 0x1000; /* ALIGN_UP(0x20 + 4, 1 << elemAlign(12)) */ + rm->cmd_msgcount = (rm->cmd_size - rm->entry_off) / GSPRM_QUEUE_ELEMENT_SIZE; + rm->stat_msgcount = (rm->stat_size - rm->entry_off) / GSPRM_QUEUE_ELEMENT_SIZE; + + if (!rm->cmd_gpa || !rm->stat_gpa || !rm->cmd_msgcount || !rm->stat_msgcount) { + nv_vgpu_err(ctx->vgpu, "GSPRM: bad queue geometry cmd_gpa=0x%llx stat_gpa=0x%llx cmd_mc=%u stat_mc=%u", + rm->cmd_gpa, rm->stat_gpa, rm->cmd_msgcount, + rm->stat_msgcount); + ret = -EINVAL; + goto out; + } + + nv_vgpu_info(ctx->vgpu, "GSPRM: cmd queue gpa=0x%llx size=0x%x mc=%u; status queue gpa=0x%llx size=0x%x mc=%u", + rm->cmd_gpa, rm->cmd_size, rm->cmd_msgcount, + rm->stat_gpa, rm->stat_size, rm->stat_msgcount); + + ret = 0; +out: + kfree(regions); + return ret; +} + +/* + * Write the status queue TX header (what GSP-RM's msgqTxCreate produces). + */ +static int gsp_rm_link_status_queue(struct gsp_emu_context *ctx) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + struct gsprm_msgq_tx_header tx; + u32 zero = 0; + int ret; + + memset(&tx, 0, sizeof(tx)); + tx.version = GSPRM_MSGQ_VERSION; + tx.size = rm->stat_size; + tx.msgSize = GSPRM_QUEUE_ELEMENT_SIZE; + tx.msgCount = rm->stat_msgcount; + tx.writePtr = 0; + tx.flags = GSPRM_MSGQ_FLAGS_SWAP_RX; + tx.rxHdrOff = 0x20; /* ALIGN_UP(sizeof(msgqTxHeader)=32, 1<entry_off; + + ret = gsp_rm_write(ctx, gsp_rm_off_to_gpa(rm, rm->stat_off), &tx, sizeof(tx)); + if (ret) { + nv_vgpu_err(ctx->vgpu, "GSPRM: status TX header write failed ret=%d", ret); + return ret; + } + + /* status queue RX header (our command-queue read position, swap mode) */ + ret = gsp_rm_write(ctx, gsp_rm_off_to_gpa(rm, rm->stat_off) + tx.rxHdrOff, + &zero, sizeof(zero)); + if (ret) { + nv_vgpu_err(ctx->vgpu, "GSPRM: status RX header write failed ret=%d", ret); + return ret; + } + + nv_vgpu_info(ctx->vgpu, "GSPRM: status queue linked (size=0x%x msgCount=%u)", + tx.size, tx.msgCount); + return 0; +} + +/* ============================================================ + * Command processing + * ============================================================ */ + +static const char *gsp_rm_rpc_name(u32 function) +{ + switch (function) { + case GSPRM_RPC_FUNC_SET_GUEST_SYSTEM_INFO: return "SET_GUEST_SYSTEM_INFO"; + case GSPRM_RPC_FUNC_ALLOC_ROOT: return "ALLOC_ROOT"; + case GSPRM_RPC_FUNC_GET_GSP_STATIC_INFO: return "GET_GSP_STATIC_INFO"; + case GSPRM_RPC_FUNC_GSP_SET_SYSTEM_INFO: return "GSP_SET_SYSTEM_INFO"; + case GSPRM_RPC_FUNC_SET_REGISTRY: return "SET_REGISTRY"; + case GSPRM_RPC_FUNC_CONTINUATION_RECORD: return "CONTINUATION_RECORD"; + case GSPRM_RPC_FUNC_GSP_RM_CONTROL: return "GSP_RM_CONTROL"; + default: return "UNKNOWN"; + } +} + +/* + * SET_GUEST_SYSTEM_INFO: the driver negotiates the RPC interface version + * from the reply's vgxVersionMajorNum/MinorNum fields. Without them the + * version-gated HALs stay stubs and every subsequent RPC fails with + * NV_VGPU_MSG_RESULT_RPC_UNKNOWN_FUNCTION (0xff100001). Echo the driver's + * own version back (no host-guest version skew). + */ +static void gsp_rm_handle_set_guest_system_info(struct gsp_emu_context *ctx, + const u8 *elem, + u32 sequence) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + const u8 *req_data = elem + GSPRM_ELEM_HDR_SIZE + + sizeof(struct gsprm_rpc_header); + u8 *reply; + u32 major, minor; + + memcpy(&major, req_data + 0, 4); + memcpy(&minor, req_data + 4, 4); + if (!major) + major = GSPRM_VGX_VERSION_MAJOR; + if (!minor && major != GSPRM_VGX_VERSION_MAJOR) + minor = GSPRM_VGX_VERSION_MINOR; + + reply = kzalloc(GSPRM_SET_GUEST_INFO_SIZE, GFP_KERNEL); + if (!reply) { + nv_vgpu_err(ctx->vgpu, "GSPRM: SET_GUEST_SYSTEM_INFO reply alloc failed"); + return; + } + memcpy(reply + 0, &major, 4); + memcpy(reply + 4, &minor, 4); + + nv_vgpu_info(ctx->vgpu, "GSPRM: SET_GUEST_SYSTEM_INFO seq=%u -> VGX %u.%u", + sequence, major, minor); + + if (gsp_rm_send(ctx, GSPRM_RPC_FUNC_SET_GUEST_SYSTEM_INFO, sequence, 0, + reply, GSPRM_SET_GUEST_INFO_SIZE)) + nv_vgpu_err(ctx->vgpu, "GSPRM: SET_GUEST_SYSTEM_INFO reply send failed"); + else + rm->rpcs_replied++; + + kfree(reply); +} + +/* ============================================================ + * GET_GSP_STATIC_INFO (function 65) + * + * Builds the 1656-byte GspStaticConfigInfo for TU104. + * ============================================================ */ + +static void gsp_rm_build_static_info(u8 *buf) +{ + u64 val64; + u32 val32; + u8 *p; + + memset(buf, 0, TU104_GSCI_SIZE); + + /* grCapsBits (offset 0, 23 bytes) - basic GR caps */ + buf[0] = 0x01; /* GR present */ + + /* gidInfo (offset 24, 268 bytes): index(4) + flags(4) + length(4) + data(256) */ + p = buf + TU104_GSCI_OFF_gidInfo; + val32 = 0; memcpy(p + 0, &val32, 4); /* index */ + val32 = 0x80000000; memcpy(p + 4, &val32, 4); /* flags: valid */ + val32 = 16; memcpy(p + 8, &val32, 4); /* length = 16 (UUID) */ + /* GPU UUID - deterministic fake for TU104 */ + memcpy(p + 12, "GPU-9da5e221-18", 15); + + /* SKUInfo (offset 292, 48 bytes) */ + p = buf + TU104_GSCI_OFF_SKUInfo; + memcpy(p + 4, "TU104", 5); /* chipSKU */ + memcpy(p + 13, "400", 3); /* chipSKUMod */ + memcpy(p + 20, "N18E", 4); /* project */ + memcpy(p + 24, "G3", 2); /* projectSKU */ + + /* fbRegionInfoParams (offset 344, 776 bytes) */ + p = buf + TU104_GSCI_OFF_fbRegionInfoParams; + val32 = 1; memcpy(p, &val32, 4); /* numFBRegions = 1 */ + /* Region 0: base=0, limit=256MB-1, reserved=0 */ + { + struct tu104_fb_region *region = (void *)(p + 8); /* 8-aligned */ + val64 = 0; memcpy(®ion->base, &val64, 8); + val64 = 0x0FFFFFFF; memcpy(®ion->limit, &val64, 8); + val64 = 0; memcpy(®ion->reserved, &val64, 8); + val32 = 0; memcpy(®ion->performance, &val32, 4); + region->supportCompressed = 0; + region->supportISO = 0; + region->bProtected = 0; + memset(region->blackList, 0, sizeof(region->blackList)); + } + + /* sriovCaps (offset 1120, 80 bytes) - all zeros (not SR-IOV) */ + + /* sriovMaxGfid (offset 1200, 4 bytes) */ + val32 = 0; + memcpy(buf + TU104_GSCI_OFF_sriovMaxGfid, &val32, 4); + + /* engineCaps (offset 1204, 12 bytes) */ + val32 = 0x60003E02; /* GR0|CE0-4|NVDEC0|NVDEC1 */ + memcpy(buf + TU104_GSCI_OFF_engineCaps, &val32, 4); + val32 = 0x00362020; /* NVENC0|SW|SEC2|HOST|PMU|FBFLCN */ + memcpy(buf + TU104_GSCI_OFF_engineCaps + 4, &val32, 4); + val32 = 0; + memcpy(buf + TU104_GSCI_OFF_engineCaps + 8, &val32, 4); + + /* poisonFuseEnabled (offset 1216, 1 byte) */ + buf[TU104_GSCI_OFF_poisonFuseEnabled] = 0; + + /* fb_length (offset 1224, 8 bytes) - 256MB */ + val64 = 0x10000000ULL; + memcpy(buf + TU104_GSCI_OFF_fb_length, &val64, 8); + + /* fbio_mask (offset 1232, 8 bytes) - 8 FBIOs for 256-bit bus */ + val64 = 0xFF; + memcpy(buf + TU104_GSCI_OFF_fbio_mask, &val64, 8); + + /* fb_bus_width (offset 1240, 4 bytes) - 256-bit */ + val32 = 256; + memcpy(buf + TU104_GSCI_OFF_fb_bus_width, &val32, 4); + + /* fb_ram_type (offset 1244, 4 bytes) - GDDR6 = 6 */ + val32 = 6; + memcpy(buf + TU104_GSCI_OFF_fb_ram_type, &val32, 4); + + /* fbp_mask (offset 1248, 8 bytes) - 6 FBPs for 6 GPCs */ + val64 = 0x3F; + memcpy(buf + TU104_GSCI_OFF_fbp_mask, &val64, 8); + + /* l2_cache_size (offset 1256, 4 bytes) - 4MB */ + val32 = 0x400000; + memcpy(buf + TU104_GSCI_OFF_l2_cache_size, &val32, 4); + + /* gpuNameString (offset 1260, 64 bytes) */ + strncpy((char *)(buf + TU104_GSCI_OFF_gpuNameString), + "NVIDIA GeForce RTX 2080 SUPER", 64); + + /* gpuShortNameString (offset 1324, 64 bytes) */ + strncpy((char *)(buf + TU104_GSCI_OFF_gpuShortNameString), + "TU104", 64); + + /* gpuNameString_Unicode (offset 1388, 128 bytes) - skip for now */ + + /* Branding booleans */ + buf[TU104_GSCI_OFF_bIsMobile] = 1; /* Mobile/Max-Q */ + + /* bar1PdeBase (offset 1536, 8 bytes) */ + val64 = 0; + memcpy(buf + TU104_GSCI_OFF_bar1PdeBase, &val64, 8); + + /* bar2PdeBase (offset 1544, 8 bytes) */ + val64 = 0; + memcpy(buf + TU104_GSCI_OFF_bar2PdeBase, &val64, 8); + + /* bVbiosValid (offset 1552, 1 byte) */ + buf[TU104_GSCI_OFF_bVbiosValid] = 1; + + /* vbiosSubVendor (offset 1556, 4 bytes) - ASUS */ + val32 = 0x1043; + memcpy(buf + TU104_GSCI_OFF_vbiosSubVendor, &val32, 4); + + /* vbiosSubDevice (offset 1560, 4 bytes) */ + val32 = 0x1F01; + memcpy(buf + TU104_GSCI_OFF_vbiosSubDevice, &val32, 4); + + /* hInternalClient/Device/Subdevice */ + val32 = TU104_H_INTERNAL_CLIENT; + memcpy(buf + TU104_GSCI_OFF_hInternalClient, &val32, 4); + val32 = TU104_H_INTERNAL_DEVICE; + memcpy(buf + TU104_GSCI_OFF_hInternalDevice, &val32, 4); + val32 = TU104_H_INTERNAL_SUBDEVICE; + memcpy(buf + TU104_GSCI_OFF_hInternalSubdevice, &val32, 4); + + /* ecidInfo (offset 1616, 24 bytes) - leave zeroed */ + + /* fwWprLayoutOffset (offset 1640, 16 bytes): nonWprHeapOffset, frtsOffset */ + val64 = 0; + memcpy(buf + TU104_GSCI_OFF_fwWprLayoutOffset, &val64, 8); + val64 = 0xFE00000; /* frtsOffset = fbSize - 2MB */ + memcpy(buf + TU104_GSCI_OFF_fwWprLayoutOffset + 8, &val64, 8); +} + +static void gsp_rm_handle_get_static_info(struct gsp_emu_context *ctx, + u32 sequence) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + u8 *reply; + + reply = kzalloc(TU104_GSCI_SIZE, GFP_KERNEL); + if (!reply) { + nv_vgpu_err(ctx->vgpu, "GSPRM: GET_GSP_STATIC_INFO reply alloc failed"); + return; + } + + gsp_rm_build_static_info(reply); + + nv_vgpu_info(ctx->vgpu, "GSPRM: GET_GSP_STATIC_INFO seq=%u -> %u bytes (fb=%lluMB, engines=0x%x/0x%x)", + sequence, TU104_GSCI_SIZE, + 0x10000000ULL >> 20, 0x60003E02, 0x362020); + + if (gsp_rm_send(ctx, GSPRM_RPC_FUNC_GET_GSP_STATIC_INFO, sequence, 0, + reply, TU104_GSCI_SIZE)) + nv_vgpu_err(ctx->vgpu, "GSPRM: GET_GSP_STATIC_INFO reply send failed"); + else + rm->rpcs_replied++; + + kfree(reply); +} + +/* ============================================================ + * GSP_RM_CONTROL (function 76) - sub-command dispatch + * + * rpc_gsp_rm_control_v03_00: hClient(4) + hObject(4) + cmd(4) + status(4) + * + paramsSize(4) + flags(4) + params[] + * ============================================================ */ + +struct gsp_rm_control_req { + u32 hClient; + u32 hObject; + u32 cmd; + u32 status; + u32 paramsSize; + u32 flags; + /* params[] follows */ +}; + +#define GSP_RM_CTRL_HDR_SIZE 24 + +static void gsp_rm_handle_fifo_device_table(struct gsp_emu_context *ctx, + u32 sequence, + struct gsp_rm_control_req *req, + u32 *out_status, + u8 *out_params, u32 *out_params_size) +{ + struct tu104_fifo_get_device_info_params *fifo_params; + u32 base, count, i, copy_count; + + fifo_params = (struct tu104_fifo_get_device_info_params *)out_params; + base = 0; + if (req->paramsSize >= 4) + memcpy(&base, (u8 *)(req + 1), 4); + + /* How many entries to return in this page */ + count = TU104_ENGINE_TABLE_SIZE - base; + if (count > TU104_FIFO_TABLE_MAX_ENTRIES) + count = TU104_FIFO_TABLE_MAX_ENTRIES; + + copy_count = count; + if (base >= TU104_ENGINE_TABLE_SIZE) + copy_count = 0; + + fifo_params->baseIndex = base; + fifo_params->numEntries = copy_count; + fifo_params->bMore = (base + copy_count) < TU104_ENGINE_TABLE_SIZE; + + for (i = 0; i < copy_count; i++) + memcpy(&fifo_params->entries[i], &tu104_engine_table[base + i], + sizeof(struct tu104_fifo_device_entry)); + + *out_params_size = offsetof(struct tu104_fifo_get_device_info_params, + entries) + + copy_count * sizeof(struct tu104_fifo_device_entry); + *out_status = 0; /* NV_OK */ + + nv_vgpu_info(ctx->vgpu, "GSPRM: FIFO_GET_DEVICE_INFO_TABLE base=%u count=%u more=%u", + base, copy_count, fifo_params->bMore); +} + +static void gsp_rm_handle_intr_kernel_table(struct gsp_emu_context *ctx, + u32 sequence, + u32 *out_status, + u8 *out_params, u32 *out_params_size) +{ + struct tu104_intr_get_kernel_table_params *intr_params; + u32 copy_size; + + intr_params = (struct tu104_intr_get_kernel_table_params *)out_params; + + intr_params->tableLen = TU104_INTR_KERNEL_TABLE_SIZE; + copy_size = TU104_INTR_KERNEL_TABLE_SIZE * + sizeof(struct tu104_intr_table_entry); + memcpy(intr_params->table, tu104_intr_kernel_table, copy_size); + + /* Subtree map: all entries in DEFAULT category (0) */ + memset(intr_params->subtreeMap, 0, sizeof(intr_params->subtreeMap)); + + *out_params_size = offsetof(struct tu104_intr_get_kernel_table_params, + table) + + copy_size + + sizeof(intr_params->subtreeMap); + *out_status = 0; + + nv_vgpu_info(ctx->vgpu, "GSPRM: INTR_GET_KERNEL_TABLE tableLen=%u", + TU104_INTR_KERNEL_TABLE_SIZE); +} + +static void gsp_rm_handle_constructed_falcon_info(struct gsp_emu_context *ctx, + u32 sequence, + u32 *out_status, + u8 *out_params, + u32 *out_params_size) +{ + struct tu104_get_constructed_falcon_info_params *falcon_params; + + falcon_params = (struct tu104_get_constructed_falcon_info_params *)out_params; + + falcon_params->numConstructedFalcons = TU104_FALCON_TABLE_SIZE; + memcpy(falcon_params->constructedFalconsTable, tu104_falcon_table, + TU104_FALCON_TABLE_SIZE * + sizeof(struct tu104_constructed_falcon_info)); + + *out_params_size = offsetof(struct tu104_get_constructed_falcon_info_params, + constructedFalconsTable) + + TU104_FALCON_TABLE_SIZE * + sizeof(struct tu104_constructed_falcon_info); + *out_status = 0; + + nv_vgpu_info(ctx->vgpu, "GSPRM: GET_CONSTRUCTED_FALCON_INFO numFalcons=%u", + TU104_FALCON_TABLE_SIZE); +} + +static void gsp_rm_handle_internal_device_table(struct gsp_emu_context *ctx, + u32 sequence, + u32 *out_status, + u8 *out_params, + u32 *out_params_size) +{ + struct tu104_internal_get_device_info_params *dev_params; + u32 copy_size; + + dev_params = (struct tu104_internal_get_device_info_params *)out_params; + + dev_params->numEntries = TU104_INTERNAL_DEVICE_TABLE_SIZE; + copy_size = TU104_INTERNAL_DEVICE_TABLE_SIZE * + sizeof(struct tu104_internal_device_info); + memcpy(dev_params->deviceInfoTable, tu104_internal_device_table, + copy_size); + + *out_params_size = offsetof(struct tu104_internal_get_device_info_params, + deviceInfoTable) + copy_size; + *out_status = 0; + + nv_vgpu_info(ctx->vgpu, "GSPRM: INTERNAL_GET_DEVICE_INFO_TABLE numEntries=%u", + TU104_INTERNAL_DEVICE_TABLE_SIZE); +} + +static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx, + const u8 *elem, u32 sequence) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + const u8 *req_data = elem + GSPRM_ELEM_HDR_SIZE + + sizeof(struct gsprm_rpc_header); + struct gsp_rm_control_req req; + u32 reply_data_len; + u8 *reply; + u32 out_status = 0; + u32 out_params_size = 0; + + memcpy(&req, req_data, GSP_RM_CTRL_HDR_SIZE); + + nv_vgpu_info(ctx->vgpu, "GSPRM: GSP_RM_CONTROL cmd=0x%x hClient=0x%x hObject=0x%x paramsSize=%u flags=0x%x", + req.cmd, req.hClient, req.hObject, req.paramsSize, + req.flags); + + /* Allocate reply: control header + max possible params */ + reply_data_len = GSP_RM_CTRL_HDR_SIZE + max(req.paramsSize, (u32)4096); + reply = kzalloc(reply_data_len, GFP_KERNEL); + if (!reply) { + nv_vgpu_err(ctx->vgpu, "GSPRM: RM_CONTROL reply alloc failed"); + return; + } + + switch (req.cmd) { + case TU104_CTRL_FIFO_GET_DEVICE_INFO_TABLE: + gsp_rm_handle_fifo_device_table(ctx, sequence, &req, + &out_status, + reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size); + break; + + case TU104_CTRL_INTERNAL_INTR_GET_KERNEL_TABLE: + gsp_rm_handle_intr_kernel_table(ctx, sequence, + &out_status, + reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size); + break; + + case TU104_CTRL_GPU_GET_CONSTRUCTED_FALCON_INFO: + gsp_rm_handle_constructed_falcon_info(ctx, sequence, + &out_status, + reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size); + break; + + case TU104_CTRL_INTERNAL_GET_DEVICE_INFO_TABLE: + gsp_rm_handle_internal_device_table(ctx, sequence, + &out_status, + reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size); + break; + + default: + /* Unknown control command - return success with zeroed params */ + nv_vgpu_info(ctx->vgpu, "GSPRM: RM_CONTROL unknown cmd=0x%x -> generic SUCCESS", + req.cmd); + out_status = 0; + out_params_size = req.paramsSize; + break; + } + + /* Build the reply header */ + memcpy(reply + 0, &req.hClient, 4); + memcpy(reply + 4, &req.hObject, 4); + memcpy(reply + 8, &req.cmd, 4); + memcpy(reply + 12, &out_status, 4); + memcpy(reply + 16, &out_params_size, 4); + memcpy(reply + 20, &req.flags, 4); + + if (gsp_rm_send(ctx, GSPRM_RPC_FUNC_GSP_RM_CONTROL, sequence, 0, + reply, GSP_RM_CTRL_HDR_SIZE + out_params_size)) + nv_vgpu_err(ctx->vgpu, "GSPRM: RM_CONTROL reply send failed cmd=0x%x", + req.cmd); + else + rm->rpcs_replied++; + + kfree(reply); +} + +/* + * Handle one command-queue record (first element). For async init RPCs we + * just consume. Synchronous RPCs get a reply; per-function handlers are + * implemented iteratively (unknown ones get a generic success reply). + */ +static void gsp_rm_handle_element(struct gsp_emu_context *ctx, const u8 *elem) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + const struct gsprm_rpc_header *hdr = + (const struct gsprm_rpc_header *)(elem + GSPRM_ELEM_HDR_SIZE); + u32 function = hdr->function; + u32 sequence = hdr->sequence; + + rm->rpcs_seen++; + + switch (function) { + case GSPRM_RPC_FUNC_GSP_SET_SYSTEM_INFO: + case GSPRM_RPC_FUNC_SET_REGISTRY: + case GSPRM_RPC_FUNC_CONTINUATION_RECORD: + /* Async init RPCs - fire and forget, just consume. */ + nv_vgpu_info(ctx->vgpu, "GSPRM: async RPC %s(%u) seq=%u len=%u consumed", + gsp_rm_rpc_name(function), function, sequence, + hdr->length); + return; + + case GSPRM_RPC_FUNC_SET_GUEST_SYSTEM_INFO: + gsp_rm_handle_set_guest_system_info(ctx, elem, sequence); + return; + + case GSPRM_RPC_FUNC_GET_GSP_STATIC_INFO: + gsp_rm_handle_get_static_info(ctx, sequence); + return; + + case GSPRM_RPC_FUNC_GSP_RM_CONTROL: + gsp_rm_handle_rm_control(ctx, elem, sequence); + return; + + default: + /* Synchronous RPC: echo function+sequence with success so the + * driver's rpcRecvPoll completes. */ + rm->unknown_rpcs++; + nv_vgpu_info(ctx->vgpu, "GSPRM: RPC %s(%u) seq=%u len=%u -> generic SUCCESS reply (unknown #%llu)", + gsp_rm_rpc_name(function), function, sequence, + hdr->length, rm->unknown_rpcs); + if (gsp_rm_send(ctx, function, sequence, 0, NULL, 0)) + nv_vgpu_err(ctx->vgpu, "GSPRM: reply send failed fn=%u", function); + else + rm->rpcs_replied++; + return; + } +} + +/* + * Poll the command queue TX writePtr; consume and handle new records. + * A record spans elemCount consecutive queue elements. + */ +static void gsp_rm_poll_commands(struct gsp_emu_context *ctx) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + struct gsprm_msgq_tx_header tx; + int ret; + + ret = gsp_rm_read(ctx, gsp_rm_off_to_gpa(rm, rm->cmd_off), &tx, sizeof(tx)); + if (ret) { + /* VM may be gone or device closed; stay quiet, retry later */ + return; + } + + if (tx.writePtr == rm->cmd_read) + return; + + if (tx.writePtr < rm->cmd_read || + (tx.writePtr - rm->cmd_read) > rm->cmd_msgcount) { + nv_vgpu_warn(ctx->vgpu, "GSPRM: cmd writePtr jumped %u -> %u, resyncing", + rm->cmd_read, tx.writePtr); + rm->cmd_read = tx.writePtr; + return; + } + + while (rm->cmd_read < tx.writePtr) { + u8 *elem; + u64 elem_gpa = gsp_rm_off_to_gpa(rm, rm->cmd_off + rm->entry_off + + (u64)(rm->cmd_read % rm->cmd_msgcount) * GSPRM_QUEUE_ELEMENT_SIZE); + u32 signature, elem_count; + const struct gsprm_rpc_header *hdr; + + elem = kmalloc(GSPRM_QUEUE_ELEMENT_SIZE, GFP_KERNEL); + if (!elem) + return; + ret = gsp_rm_read(ctx, elem_gpa, elem, GSPRM_QUEUE_ELEMENT_SIZE); + if (ret) { + kfree(elem); + return; + } + + hdr = (const struct gsprm_rpc_header *)(elem + GSPRM_ELEM_HDR_SIZE); + signature = hdr->signature; + memcpy(&elem_count, elem + 0x28, 4); + + if (signature != GSPRM_RPC_SIGNATURE || elem_count == 0 || + elem_count > rm->cmd_msgcount) { + nv_vgpu_warn(ctx->vgpu, "GSPRM: bad element idx=%u sig=0x%x elemCount=%u fn=%u, skipping", + rm->cmd_read, signature, elem_count, + hdr->function); + rm->cmd_read++; + kfree(elem); + continue; + } + + gsp_rm_handle_element(ctx, elem); + kfree(elem); + + /* consume the whole record (all its elements) */ + rm->cmd_read += elem_count; + } + + /* Publish our command-queue read position into the status queue RX + * header (MSGQ_FLAGS_SWAP_RX: read pointers live in the peer's queue). */ + gsp_rm_write(ctx, gsp_rm_off_to_gpa(rm, rm->stat_off) + 0x20, + &rm->cmd_read, sizeof(rm->cmd_read)); +} + +/* ============================================================ + * Public interface + * ============================================================ */ + +int gsp_rm_emu_init(struct nv_vgpu_device *vgpu, struct gsp_emu_context *ctx) +{ + struct gsp_rm_emu *rm; + + rm = kzalloc(sizeof(*rm), GFP_KERNEL); + if (!rm) + return -ENOMEM; + + rm->vgpu = vgpu; + ctx->rm_emu = rm; + return 0; +} + +void gsp_rm_emu_fini(struct gsp_emu_context *ctx) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + + if (!rm) + return; + kfree(rm->ptes); + kfree(rm); + ctx->rm_emu = NULL; +} + +/* + * Reset the GSP-RM emulation state on GSP ENGINE reset (guest driver reboots + * the GSP on every probe/unload). The queue buffer GPAs from the previous + * boot are stale (freed by the guest) - drop all parsed state so the next + * Booter completion re-parses the chain. + */ +void gsp_rm_emu_reset(struct gsp_emu_context *ctx) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + + if (!rm || !rm->parsed) + return; + + nv_vgpu_info(ctx->vgpu, "GSPRM: reset (rpcs=%llu replied=%llu unknown=%llu)", + rm->rpcs_seen, rm->rpcs_replied, rm->unknown_rpcs); + + kfree(rm->ptes); + rm->ptes = NULL; + rm->parsed = false; + rm->active = false; + rm->init_done_sent = false; + rm->libos_args_gpa = 0; + rm->gsp_args_gpa = 0; + rm->cmd_read = 0; + rm->stat_write = 0; + rm->stat_seq = 0; + memset(&rm->mq, 0, sizeof(rm->mq)); + rm->cmd_gpa = 0; + rm->stat_gpa = 0; + rm->cmd_size = 0; + rm->stat_size = 0; + rm->cmd_msgcount = 0; + rm->stat_msgcount = 0; +} + +/* + * Called when the guest's Booter Load completes. The GSP MAILBOX0/1 now hold + * the libos init args GPA; parse the queue chain, link the status queue and + * emit GSP_INIT_DONE so the driver's kgspWaitForRmInitDone completes. + */ +void gsp_rm_emu_on_booter_done(struct gsp_emu_context *ctx) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + u32 mb_lo, mb_hi; + u32 b_is_d3hot = 0; + int ret; + + if (!rm || rm->parsed) + return; + + mb_lo = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_GSP_MAILBOX0); + mb_hi = gsp_emu_shadow_read32(ctx, GSP_EMU_REG_GSP_MAILBOX0 + 4); + rm->libos_args_gpa = ((u64)mb_hi << 32) | mb_lo; + + nv_vgpu_info(ctx->vgpu, "GSPRM: libos args at gpa=0x%llx (mailbox 0x%x:0x%x)", + rm->libos_args_gpa, mb_hi, mb_lo); + + if (!rm->libos_args_gpa) { + nv_vgpu_warn(ctx->vgpu, "GSPRM: libos args GPA is 0, will retry"); + return; + } + + ret = gsp_rm_parse_queue_chain(ctx); + if (ret) { + nv_vgpu_err(ctx->vgpu, "GSPRM: queue chain parse failed: %d", ret); + return; + } + + ret = gsp_rm_link_status_queue(ctx); + if (ret) + return; + + rm->parsed = true; + rm->active = true; + + /* Emit GSP_INIT_DONE (function 0x1001, sequence 0, init_done_v17_00) */ + ret = gsp_rm_send(ctx, GSPRM_RPC_EVENT_GSP_INIT_DONE, 0, 0, + &b_is_d3hot, sizeof(b_is_d3hot)); + if (ret) { + nv_vgpu_err(ctx->vgpu, "GSPRM: GSP_INIT_DONE send failed: %d", ret); + return; + } + rm->init_done_sent = true; + + nv_vgpu_info(ctx->vgpu, "GSPRM: GSP_INIT_DONE emitted, RPC loop active"); +} + +void gsp_rm_emu_poll(struct gsp_emu_context *ctx) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + + if (!rm) + return; + + /* Retry chain parsing if the first attempt at BOOTER_DONE failed + * (e.g. mailbox not yet visible). Boot stage 2 == BOOTER_DONE. */ + if (!rm->parsed) { + if (ctx->boot_stage == 2) + gsp_rm_emu_on_booter_done(ctx); + return; + } + + if (!rm->active) + return; + + gsp_rm_poll_commands(ctx); +} diff --git a/kernel/gsp_rm_emulation.h b/kernel/gsp_rm_emulation.h new file mode 100644 index 0000000..b1a7259 --- /dev/null +++ b/kernel/gsp_rm_emulation.h @@ -0,0 +1,151 @@ +/* + * GSP-RM Emulation (physical-path) for Host-Side vGPU + * + * Emulates the GSP-RM firmware's msgq-based RPC channel for a guest driver + * that believes it is driving a real physical GPU. The guest driver: + * - boots the "GSP" (falcon stages emulated in gsp_emulation.c) + * - programs libos init args address into GSP MAILBOX0/1 + * - expects GSP-RM to msgqInit the status queue in *guest system memory* + * (shared command/status queue buffer described by GSP_ARGUMENTS_CACHED) + * - polls the status queue for NV_VGPU_MSG_EVENT_GSP_INIT_DONE and then + * issues the RM RPC sequence on the command queue. + * + * Since there is no real GSP engine, this module performs the "virtual DMA" + * on behalf of the device using vfio_dma_rw() (CPU copies through the VFIO + * container's IOMMU mappings, IOVA == guest physical address). + */ + +#ifndef _GSP_RM_EMULATION_H_ +#define _GSP_RM_EMULATION_H_ + +#include + +#include "nvidia-vgpu-vfio.h" + +struct gsp_emu_context; + +/* Wire constants (from NVIDIA OpenRM 570.144 msgq/rpc headers - the guest + * driver is the 570.144 stock runfile, whose GSP_MSG_QUEUE_ELEMENT has NO + * MCTP/NVDM transport headers: authTag[16]+aad[16]+checkSum+seqNum+elemCount + * +rpc@0x30). */ +#define GSPRM_MSGQ_VERSION 0 +#define GSPRM_MSGQ_FLAGS_SWAP_RX 1 +#define GSPRM_RPC_SIGNATURE 0x43505256 /* NV_VGPU_MSG_SIGNATURE_VALID */ +#define GSPRM_RPC_HEADER_VERSION 0x03000000 /* MAJOR_TOT(3)<<24|MINOR_TOT(0)<<16 */ + +/* RPC function/event numbers (rpc_global_enums.h) */ +#define GSPRM_RPC_FUNC_SET_GUEST_SYSTEM_INFO 1 +#define GSPRM_RPC_FUNC_ALLOC_ROOT 2 +#define GSPRM_RPC_FUNC_GET_GSP_STATIC_INFO 65 +#define GSPRM_RPC_FUNC_GSP_SET_SYSTEM_INFO 72 +#define GSPRM_RPC_FUNC_SET_REGISTRY 73 +#define GSPRM_RPC_FUNC_CONTINUATION_RECORD 71 +#define GSPRM_RPC_FUNC_GSP_RM_CONTROL 76 +#define GSPRM_RPC_EVENT_GSP_INIT_DONE 0x1001 + +/* rpc_set_guest_system_info_v03_00: version fields the driver reads back + * from the reply to negotiate the RPC interface version (ipVersion). */ +#define GSPRM_SET_GUEST_INFO_SIZE 0x324 +#define GSPRM_VGX_VERSION_MAJOR 0x29 +#define GSPRM_VGX_VERSION_MINOR 0x0C + +/* "RMARGS" packed big-endian into u64 by _kgspGenerateInitArgId() */ +#define GSPRM_LIBOS_ID_RMARGS 0x524D41524753ULL + +#define GSPRM_MAX_LIBOS_REGIONS 32 +#define GSPRM_QUEUE_ELEMENT_SIZE 0x1000 +#define GSPRM_STATUS_QUEUE_SIZE 0x40000 + +/* msgqTxHeader (msgq_priv.h) */ +struct gsprm_msgq_tx_header { + u32 version; + u32 size; + u32 msgSize; + u32 msgCount; + u32 writePtr; + u32 flags; + u32 rxHdrOff; + u32 entryOff; +}; + +/* rpc_message_header_v03_00 (g_rpc-message-header.h) */ +struct gsprm_rpc_header { + u32 header_version; + u32 signature; + u32 length; + u32 function; + u32 rpc_result; + u32 rpc_result_private; + u32 sequence; + u32 spare; /* rpc_message_rpc_union_field_v (spare/cpuRmGfid) */ +}; + +/* LibosMemoryRegionInitArgument (libos_init_args.h) - natural alignment, 32B */ +struct gsprm_libos_region { + u64 id8; + u64 pa; + u64 size; + u8 kind; + u8 loc; + u8 pad[6]; +}; + +/* MESSAGE_QUEUE_INIT_ARGUMENTS (gsp_init_args.h), x86_64 NvLength=u64 */ +struct gsprm_mq_init_args { + u64 sharedMemPhysAddr; + u32 pageTableEntryCount; + u32 pad0; + u64 cmdQueueOffset; + u64 statQueueOffset; + u64 queueElementHdrSize; + u64 queueElementSizeMin; + u64 queueElementSizeMax; + u32 queueHeaderAlign; + u32 queueElementAlign; +}; + +struct gsp_rm_emu { + struct nv_vgpu_device *vgpu; + + bool parsed; /* queue chain parsed + status queue linked */ + bool active; + + u64 libos_args_gpa; /* from GSP MAILBOX0/1 */ + u64 gsp_args_gpa; /* RMARGS region pa */ + + /* queue buffer description (from GSP_ARGUMENTS_CACHED) */ + struct gsprm_mq_init_args mq; + u64 *ptes; /* pageTableEntryCount guest-phys addrs */ + + /* computed queue geometry. cmd_off/stat_off are offsets into the shared + * buffer (linear); GPAs must be computed per-access via the page table + * (buffer is NV_MEMORY_NONCONTIGUOUS). */ + u64 cmd_off; + u64 stat_off; + u64 cmd_gpa; /* GPA of command queue base (diagnostic) */ + u64 stat_gpa; /* GPA of status queue base (diagnostic) */ + u32 cmd_size; /* bytes */ + u32 stat_size; /* bytes */ + u32 cmd_msgcount; + u32 stat_msgcount; + u32 entry_off; /* 0x1000 (msgq entryOff, both queues) */ + + /* GSP-side cursors */ + u32 cmd_read; /* elements consumed from command queue */ + u32 stat_write; /* elements written to status queue */ + u32 stat_seq; /* status record sequence numbers */ + bool init_done_sent; + + /* stats */ + u64 rpcs_seen; + u64 rpcs_replied; + u64 unknown_rpcs; +}; + +int gsp_rm_emu_init(struct nv_vgpu_device *vgpu, struct gsp_emu_context *ctx); +void gsp_rm_emu_fini(struct gsp_emu_context *ctx); +void gsp_rm_emu_reset(struct gsp_emu_context *ctx); +void gsp_rm_emu_on_booter_done(struct gsp_emu_context *ctx); +void gsp_rm_emu_poll(struct gsp_emu_context *ctx); + +#endif /* _GSP_RM_EMULATION_H_ */ diff --git a/kernel/gsp_rm_tu104.h b/kernel/gsp_rm_tu104.h new file mode 100644 index 0000000..dc08b1d --- /dev/null +++ b/kernel/gsp_rm_tu104.h @@ -0,0 +1,694 @@ +/* + * TU104 Hardware Tables for GSP-RM Emulation + * + * Static device/engine/interrupt tables for the emulated TU104 GPU. + * Data extracted from NVIDIA driver material (ext-tree) and hardware + * reference headers (hwref/turing/tu104). + * + * These tables replace what the real GSP-RM firmware would read from + * PTOP hardware registers during boot. + */ + +#ifndef _GSP_RM_TU104_H_ +#define _GSP_RM_TU104_H_ + +#include + +/* ============================================================ + * Wire structures (from 570.144 reference, ABI-checked) + * ============================================================ */ + +/* NV2080_CTRL_FIFO_DEVICE_ENTRY (ctrl2080fifo.h) - 100 bytes */ +struct tu104_fifo_device_entry { + u32 engineData[16]; /* ENGINE_INFO_TYPE_* indexed */ + u32 pbdmaIds[2]; + u32 pbdmaFaultIds[2]; + u32 numPbdmas; + char engineName[16]; +}; + +/* NV2080_CTRL_FIFO_GET_DEVICE_INFO_TABLE_PARAMS - 3212 bytes max */ +#define TU104_FIFO_TABLE_MAX_ENTRIES 32 +#define TU104_FIFO_TABLE_MAX_DEVICES 256 + +struct tu104_fifo_get_device_info_params { + u32 baseIndex; + u32 numEntries; + u8 bMore; + u8 _pad[3]; + struct tu104_fifo_device_entry entries[TU104_FIFO_TABLE_MAX_ENTRIES]; +}; + +/* NV2080_CTRL_INTERNAL_DEVICE_INFO (ctrl2080internal.h) - 48 bytes */ +struct tu104_internal_device_info { + u32 faultId; + u32 instanceId; + u32 typeEnum; + u32 resetId; + u32 devicePriBase; + u32 isEngine; + u32 rlEngId; + u32 runlistPriBase; + u32 groupId; + u32 ginTargetId; + u32 deviceBroadcastPriBase; + u32 groupLocalInstanceId; +}; + +/* NV2080_CTRL_INTERNAL_GET_DEVICE_INFO_TABLE_PARAMS - up to 512 entries */ +#define TU104_INTERNAL_TABLE_MAX_ENTRIES 512 + +struct tu104_internal_get_device_info_params { + u32 numEntries; + struct tu104_internal_device_info deviceInfoTable[TU104_INTERNAL_TABLE_MAX_ENTRIES]; +}; + +/* NV2080_CTRL_INTERNAL_INTR_GET_KERNEL_TABLE_PARAMS */ +#define TU104_INTR_TABLE_MAX_SIZE 128 + +struct tu104_intr_table_entry { + u16 engineIdx; + u16 _pad0; + u32 pmcIntrMask; + u32 vectorStall; + u32 vectorNonStall; +}; + +struct tu104_intr_get_kernel_table_params { + u32 tableLen; + struct tu104_intr_table_entry table[TU104_INTR_TABLE_MAX_SIZE]; + u32 subtreeMap[7]; /* NV2080_INTR_CATEGORY_* count */ +}; + +/* NV2080_CTRL_GPU_CONSTRUCTED_FALCON_INFO (ctrl2080gpu.h) - 20 bytes */ +struct tu104_constructed_falcon_info { + u32 engDesc; + u32 ctxAttr; + u32 ctxBufferSize; + u32 addrSpaceList; + u32 registerBase; +}; + +#define TU104_MAX_CONSTRUCTED_FALCONS 0x40 + +struct tu104_get_constructed_falcon_info_params { + u32 numConstructedFalcons; + struct tu104_constructed_falcon_info constructedFalconsTable[TU104_MAX_CONSTRUCTED_FALCONS]; +}; + +/* ============================================================ + * ENGINE_INFO_TYPE indices (engine_info.h) - frozen vGPU ABI + * ============================================================ */ +#define EI_ENG_DESC 0 +#define EI_FIFO_TAG 1 +#define EI_RM_ENGINE_TYPE 2 +#define EI_RUNLIST 3 +#define EI_MMU_FAULT_ID 4 +#define EI_RC_MASK 5 +#define EI_RESET 6 +#define EI_INTR 7 +#define EI_MC 8 +#define EI_DEV_TYPE_ENUM 9 +#define EI_INSTANCE_ID 10 +#define EI_RUNLIST_PRI_BASE 11 +#define EI_IS_HOST_DRIVEN_ENGINE 12 +#define EI_RUNLIST_ENGINE_ID 13 +#define EI_CHRAM_PRI_BASE 14 +#define EI_KERNEL_RM_MAX 15 + +/* ============================================================ + * ENG_DESC values (MKENGDESC = classId << 8 | instance) + * ============================================================ */ +#define TU104_ENG_GR0 0xd334df00 /* classId(Graphics)=0xd334df, inst 0 */ +#define TU104_ENG_CE0 0x793ceb00 /* classId(OBJCE)=0x793ceb, inst 0 */ +#define TU104_ENG_CE1 0x793ceb01 +#define TU104_ENG_CE2 0x793ceb02 +#define TU104_ENG_CE3 0x793ceb03 +#define TU104_ENG_CE4 0x793ceb04 +#define TU104_ENG_NVDEC0 0x8f99e100 /* classId(OBJBSP)=0x8f99e1, inst 0 */ +#define TU104_ENG_NVDEC1 0x8f99e101 +#define TU104_ENG_NVENC0 0xe97b6c00 /* classId(OBJMSENC)=0xe97b6c, inst 0 */ +#define TU104_ENG_SEC2 0x28c40800 /* classId(OBJSEC2)=0x28c408, inst 0 */ +#define TU104_ENG_NVJPG 0x2b3a5400 /* classId(OBJNVJPG)=0x2b3a54, inst 0 */ +#define TU104_ENG_SW 0x95a6f500 /* classId(OBJSWENG)=0x95a6f5, inst 0 */ +#define TU104_ENG_FBFLCN 0x8a20bf00 /* classId(OBJFBFLCN)=0x8a20bf, inst 0 */ + +/* ============================================================ + * RM_ENGINE_TYPE values (gpu_engine_type.h) + * ============================================================ */ +#define TU104_RM_ENGINE_GR0 0x01 +#define TU104_RM_ENGINE_CE0 0x09 +#define TU104_RM_ENGINE_CE1 0x0a +#define TU104_RM_ENGINE_CE2 0x0b +#define TU104_RM_ENGINE_CE3 0x0c +#define TU104_RM_ENGINE_CE4 0x0d +#define TU104_RM_ENGINE_NVDEC0 0x1d +#define TU104_RM_ENGINE_NVDEC1 0x1e +#define TU104_RM_ENGINE_NVENC0 0x25 +#define TU104_RM_ENGINE_SW 0x2d +#define TU104_RM_ENGINE_SEC2 0x31 +#define TU104_RM_ENGINE_HOST 0x32 +#define TU104_RM_ENGINE_PMU 0x34 +#define TU104_RM_ENGINE_FBFLCN 0x35 +#define TU104_RM_ENGINE_NVJPG0 0x36 + +/* ============================================================ + * MC_ENGINE_IDX values (engine_idx.h) + * ============================================================ */ +#define TU104_MC_ENGINE_GR 84 +#define TU104_MC_ENGINE_CE0 15 +#define TU104_MC_ENGINE_CE1 16 +#define TU104_MC_ENGINE_CE2 17 +#define TU104_MC_ENGINE_CE3 18 +#define TU104_MC_ENGINE_CE4 19 +#define TU104_MC_ENGINE_NVDEC0 65 +#define TU104_MC_ENGINE_NVDEC1 66 +#define TU104_MC_ENGINE_NVENC0 38 +#define TU104_MC_ENGINE_SEC2 47 +#define TU104_MC_ENGINE_NVJPG 51 + +/* ============================================================ + * NV_PFAULT_MMU_ENG_ID values (tu104/dev_fault.h) + * ============================================================ */ +#define TU104_FAULT_ID_GR 64 +#define TU104_FAULT_ID_CE0 15 +#define TU104_FAULT_ID_CE1 16 +#define TU104_FAULT_ID_CE2 17 +#define TU104_FAULT_ID_CE3 18 +#define TU104_FAULT_ID_CE4 19 +#define TU104_FAULT_ID_NVDEC0 10 +#define TU104_FAULT_ID_NVDEC1 25 +#define TU104_FAULT_ID_NVENC0 11 +#define TU104_FAULT_ID_SEC2 14 +#define TU104_FAULT_ID_NVJPG0 24 +#define TU104_FAULT_ID_GSP 2 +#define TU104_FAULT_ID_PMU 6 + +/* PBDMA fault ID base (NV_PFIFO_CFG0_PBDMA_FAULT_ID_INIT = 32) */ +#define TU104_PBDMA_FAULT_ID_BASE 32 +#define TU104_NUM_PBDMAS 12 + +/* ============================================================ + * ROBUST_CHANNEL values (rc.h) + * ============================================================ */ +#define TU104_RC_GRAPHICS 40 +#define TU104_RC_CE0 22 +#define TU104_RC_CE1 23 +#define TU104_RC_CE2 24 +#define TU104_RC_CE3 25 +#define TU104_RC_CE4 26 +#define TU104_RC_NVDEC0 14 +#define TU104_RC_NVDEC1 15 +#define TU104_RC_NVENC0 11 +#define TU104_RC_SEC2 2 +#define TU104_RC_NVJPG0 32 + +/* ============================================================ + * NV_PTOP_DEVICE_INFO_TYPE_ENUM (tu104/dev_top.h) + * ============================================================ */ +#define TU104_PTOP_TYPE_GRAPHICS 0 +#define TU104_PTOP_TYPE_LCE 19 +#define TU104_PTOP_TYPE_NVDEC 16 +#define TU104_PTOP_TYPE_NVENC 14 +#define TU104_PTOP_TYPE_NVJPG 21 +#define TU104_PTOP_TYPE_SEC 13 +#define TU104_PTOP_TYPE_GSP 20 +#define TU104_PTOP_TYPE_IOCTRL 18 + +/* ============================================================ + * FIFO_TAG values (hardware engine enum, 4 bits) + * ============================================================ */ +#define TU104_TAG_GR 0 +#define TU104_TAG_CE0 1 +#define TU104_TAG_CE1 2 +#define TU104_TAG_CE2 3 +#define TU104_TAG_CE3 4 +#define TU104_TAG_CE4 5 +#define TU104_TAG_NVDEC0 10 +#define TU104_TAG_NVDEC1 11 +#define TU104_TAG_NVENC0 13 +#define TU104_TAG_NVJPG 14 +#define TU104_TAG_SEC2 15 + +/* ============================================================ + * TU104 FIFO Engine Table (11 hardware engines + 1 SW) + * + * Runlist assignment: each engine gets its own runlist. + * Runlist 0=GR, 1-5=CE0-CE4, 6-7=NVDEC0-1, 8=NVENC0, 9=SEC2, 10=NVJPG + * PBDMA assignment: 1:1 with runlists (PBDMA N services runlist N). + * SW engine shares NVJPG's runlist/PBDMA (per fifoConstructSwEngine). + * ============================================================ */ + +static const struct tu104_fifo_device_entry tu104_engine_table[] = { + /* [0] GR0 - Graphics */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_GR0, + [EI_FIFO_TAG] = TU104_TAG_GR, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_GR0, + [EI_RUNLIST] = 0, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_GR, + [EI_RC_MASK] = TU104_RC_GRAPHICS, + [EI_RESET] = 12, /* NV_PMC_ENABLE_PGRAPH bit */ + [EI_INTR] = 0, + [EI_MC] = TU104_MC_ENGINE_GR, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_GRAPHICS, + [EI_INSTANCE_ID] = 0, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {0, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 0, 0}, + .numPbdmas = 1, + .engineName = "GRAPHICS", + }, + /* [1] CE0 */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_CE0, + [EI_FIFO_TAG] = TU104_TAG_CE0, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_CE0, + [EI_RUNLIST] = 1, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_CE0, + [EI_RC_MASK] = TU104_RC_CE0, + [EI_RESET] = 1, + [EI_INTR] = 1, + [EI_MC] = TU104_MC_ENGINE_CE0, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_LCE, + [EI_INSTANCE_ID] = 0, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {1, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 1, 0}, + .numPbdmas = 1, + .engineName = "CE0", + }, + /* [2] CE1 */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_CE1, + [EI_FIFO_TAG] = TU104_TAG_CE1, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_CE1, + [EI_RUNLIST] = 2, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_CE1, + [EI_RC_MASK] = TU104_RC_CE1, + [EI_RESET] = 2, + [EI_INTR] = 2, + [EI_MC] = TU104_MC_ENGINE_CE1, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_LCE, + [EI_INSTANCE_ID] = 1, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {2, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 2, 0}, + .numPbdmas = 1, + .engineName = "CE1", + }, + /* [3] CE2 */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_CE2, + [EI_FIFO_TAG] = TU104_TAG_CE2, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_CE2, + [EI_RUNLIST] = 3, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_CE2, + [EI_RC_MASK] = TU104_RC_CE2, + [EI_RESET] = 3, + [EI_INTR] = 3, + [EI_MC] = TU104_MC_ENGINE_CE2, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_LCE, + [EI_INSTANCE_ID] = 2, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {3, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 3, 0}, + .numPbdmas = 1, + .engineName = "CE2", + }, + /* [4] CE3 */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_CE3, + [EI_FIFO_TAG] = TU104_TAG_CE3, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_CE3, + [EI_RUNLIST] = 4, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_CE3, + [EI_RC_MASK] = TU104_RC_CE3, + [EI_RESET] = 4, + [EI_INTR] = 4, + [EI_MC] = TU104_MC_ENGINE_CE3, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_LCE, + [EI_INSTANCE_ID] = 3, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {4, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 4, 0}, + .numPbdmas = 1, + .engineName = "CE3", + }, + /* [5] CE4 */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_CE4, + [EI_FIFO_TAG] = TU104_TAG_CE4, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_CE4, + [EI_RUNLIST] = 5, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_CE4, + [EI_RC_MASK] = TU104_RC_CE4, + [EI_RESET] = 5, + [EI_INTR] = 5, + [EI_MC] = TU104_MC_ENGINE_CE4, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_LCE, + [EI_INSTANCE_ID] = 4, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {5, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 5, 0}, + .numPbdmas = 1, + .engineName = "CE4", + }, + /* [6] NVDEC0 */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_NVDEC0, + [EI_FIFO_TAG] = TU104_TAG_NVDEC0, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_NVDEC0, + [EI_RUNLIST] = 6, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_NVDEC0, + [EI_RC_MASK] = TU104_RC_NVDEC0, + [EI_RESET] = 14, + [EI_INTR] = 6, + [EI_MC] = TU104_MC_ENGINE_NVDEC0, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_NVDEC, + [EI_INSTANCE_ID] = 0, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {6, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 6, 0}, + .numPbdmas = 1, + .engineName = "NVDEC0", + }, + /* [7] NVDEC1 */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_NVDEC1, + [EI_FIFO_TAG] = TU104_TAG_NVDEC1, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_NVDEC1, + [EI_RUNLIST] = 7, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_NVDEC1, + [EI_RC_MASK] = TU104_RC_NVDEC1, + [EI_RESET] = 15, + [EI_INTR] = 7, + [EI_MC] = TU104_MC_ENGINE_NVDEC1, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_NVDEC, + [EI_INSTANCE_ID] = 1, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {7, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 7, 0}, + .numPbdmas = 1, + .engineName = "NVDEC1", + }, + /* [8] NVENC0 */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_NVENC0, + [EI_FIFO_TAG] = TU104_TAG_NVENC0, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_NVENC0, + [EI_RUNLIST] = 8, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_NVENC0, + [EI_RC_MASK] = TU104_RC_NVENC0, + [EI_RESET] = 17, + [EI_INTR] = 8, + [EI_MC] = TU104_MC_ENGINE_NVENC0, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_NVENC, + [EI_INSTANCE_ID] = 0, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {8, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 8, 0}, + .numPbdmas = 1, + .engineName = "NVENC0", + }, + /* [9] SEC2 */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_SEC2, + [EI_FIFO_TAG] = TU104_TAG_SEC2, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_SEC2, + [EI_RUNLIST] = 9, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_SEC2, + [EI_RC_MASK] = TU104_RC_SEC2, + [EI_RESET] = 19, + [EI_INTR] = 9, + [EI_MC] = TU104_MC_ENGINE_SEC2, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_SEC, + [EI_INSTANCE_ID] = 0, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {9, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 9, 0}, + .numPbdmas = 1, + .engineName = "SEC2", + }, + /* [10] NVJPG */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_NVJPG, + [EI_FIFO_TAG] = TU104_TAG_NVJPG, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_NVJPG0, + [EI_RUNLIST] = 10, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_NVJPG0, + [EI_RC_MASK] = TU104_RC_NVJPG0, + [EI_RESET] = 20, + [EI_INTR] = 10, + [EI_MC] = TU104_MC_ENGINE_NVJPG, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_NVJPG, + [EI_INSTANCE_ID] = 0, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {10, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 10, 0}, + .numPbdmas = 1, + .engineName = "NVJPG", + }, + /* [11] SW (software engine - shares NVJPG's runlist per fifoConstructSwEngine) */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_SW, + [EI_FIFO_TAG] = 0xffffffff, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_SW, + [EI_RUNLIST] = 10, /* NVJPG's runlist */ + [EI_MMU_FAULT_ID] = 0xffffffff, + [EI_RC_MASK] = 0, + [EI_RESET] = 0, + [EI_INTR] = 0, + [EI_MC] = 0, + [EI_DEV_TYPE_ENUM] = 0, + [EI_INSTANCE_ID] = 0, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 0, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {10, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 10, 0}, + .numPbdmas = 1, + .engineName = "SOFTWARE", + }, +}; + +#define TU104_ENGINE_TABLE_SIZE 12 + +/* ============================================================ + * TU104 Interrupt Table (from intr_phys_tu102.c, kernel table) + * + * These are the CPU-RM (guest) visible interrupts. + * NV_PMC_INTR_INVALID_MASK = 0 (no PMC mask for these). + * NV_INTR_VECTOR_INVALID = 0xFFFFFFFF. + * ============================================================ */ + +#define TU104_NV_INTR_VECTOR_INVALID 0xFFFFFFFF + +static const struct tu104_intr_table_entry tu104_intr_kernel_table[] = { + /* MC_ENGINE_IDX_REPLAYABLE_FAULT = 59 */ + { .engineIdx = 59, .pmcIntrMask = 0, + .vectorStall = 64, /* NV_PFB_PRI_MMU_INT_VECTOR_FAULT_NOTIFY_REPLAYABLE */ + .vectorNonStall = TU104_NV_INTR_VECTOR_INVALID }, + /* MC_ENGINE_IDX_REPLAYABLE_FAULT_ERROR = 62 */ + { .engineIdx = 62, .pmcIntrMask = 0, + .vectorStall = 131, /* NV_PFB_PRI_MMU_INT_VECTOR_FAULT_ERROR_REPLAYABLE */ + .vectorNonStall = TU104_NV_INTR_VECTOR_INVALID }, + /* MC_ENGINE_IDX_ACCESS_CNTR = 60 */ + { .engineIdx = 60, .pmcIntrMask = 0, + .vectorStall = 72, /* HUB_ACCESS_CNTR_INTR_VECTOR */ + .vectorNonStall = TU104_NV_INTR_VECTOR_INVALID }, + /* MC_ENGINE_IDX_CPU_DOORBELL = 73 */ + { .engineIdx = 73, .pmcIntrMask = 0, + .vectorStall = 129, /* NV_CTRL_CPU_DOORBELL_VECTORID_VALUE_CONSTANT */ + .vectorNonStall = TU104_NV_INTR_VECTOR_INVALID }, + /* MC_ENGINE_IDX_PRIV_DOORBELL = 74 */ + { .engineIdx = 74, .pmcIntrMask = 0, + .vectorStall = 130, /* NV_CTRL_VIRTUAL_INTR_VECTORID_VALUE_CONSTANT */ + .vectorNonStall = TU104_NV_INTR_VECTOR_INVALID }, + /* MC_ENGINE_IDX_GR_FECS_LOG = 156 */ + { .engineIdx = 156, .pmcIntrMask = 0, + .vectorStall = TU104_NV_INTR_VECTOR_INVALID, + .vectorNonStall = TU104_NV_INTR_VECTOR_INVALID }, + /* MC_ENGINE_IDX_GSP = 50 */ + { .engineIdx = 50, .pmcIntrMask = 0, + .vectorStall = 155, /* GSP_INTR_0_VECTOR */ + .vectorNonStall = TU104_NV_INTR_VECTOR_INVALID }, + /* MC_ENGINE_IDX_DISP = 2 */ + { .engineIdx = 2, .pmcIntrMask = 0, + .vectorStall = 154, /* PDISP_VECTOR */ + .vectorNonStall = TU104_NV_INTR_VECTOR_INVALID }, +}; + +#define TU104_INTR_KERNEL_TABLE_SIZE 8 + +/* ============================================================ + * TU104 Constructed Falcon Table + * + * Only FBFLCN (framebuffer falcon) is a "constructed falcon" on Turing + * (SEC2, GSP, PMU are skipped as they have their own kernel objects). + * ============================================================ */ + +static const struct tu104_constructed_falcon_info tu104_falcon_table[] = { + { + .engDesc = TU104_ENG_FBFLCN, + .ctxAttr = 0, + .ctxBufferSize = 0x400, /* 1KB context buffer */ + .addrSpaceList = 0, + .registerBase = 0x009a4000, /* NV_PFBFALCON base */ + }, +}; + +#define TU104_FALCON_TABLE_SIZE 1 + +/* ============================================================ + * TU104 Internal Device Info Table + * + * Engines + key non-engine devices. The guest driver queries this + * by typeEnum to find device register bases and fault IDs. + * ============================================================ */ + +static const struct tu104_internal_device_info tu104_internal_device_table[] = { + /* Engines (isEngine=1) */ + { .faultId = TU104_FAULT_ID_GR, .instanceId = 0, .typeEnum = TU104_PTOP_TYPE_GRAPHICS, .resetId = 12, .devicePriBase = 0x00400000, .isEngine = 1, .rlEngId = 0, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 0 }, + { .faultId = TU104_FAULT_ID_CE0, .instanceId = 0, .typeEnum = TU104_PTOP_TYPE_LCE, .resetId = 1, .devicePriBase = 0x00040000, .isEngine = 1, .rlEngId = 1, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 0 }, + { .faultId = TU104_FAULT_ID_CE1, .instanceId = 1, .typeEnum = TU104_PTOP_TYPE_LCE, .resetId = 2, .devicePriBase = 0x00041000, .isEngine = 1, .rlEngId = 2, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 1 }, + { .faultId = TU104_FAULT_ID_CE2, .instanceId = 2, .typeEnum = TU104_PTOP_TYPE_LCE, .resetId = 3, .devicePriBase = 0x00042000, .isEngine = 1, .rlEngId = 3, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 2 }, + { .faultId = TU104_FAULT_ID_CE3, .instanceId = 3, .typeEnum = TU104_PTOP_TYPE_LCE, .resetId = 4, .devicePriBase = 0x00043000, .isEngine = 1, .rlEngId = 4, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 3 }, + { .faultId = TU104_FAULT_ID_CE4, .instanceId = 4, .typeEnum = TU104_PTOP_TYPE_LCE, .resetId = 5, .devicePriBase = 0x00044000, .isEngine = 1, .rlEngId = 5, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 4 }, + { .faultId = TU104_FAULT_ID_NVDEC0, .instanceId = 0, .typeEnum = TU104_PTOP_TYPE_NVDEC, .resetId = 14, .devicePriBase = 0x00060000, .isEngine = 1, .rlEngId = 6, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 0 }, + { .faultId = TU104_FAULT_ID_NVDEC1, .instanceId = 1, .typeEnum = TU104_PTOP_TYPE_NVDEC, .resetId = 15, .devicePriBase = 0x00061000, .isEngine = 1, .rlEngId = 7, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 1 }, + { .faultId = TU104_FAULT_ID_NVENC0, .instanceId = 0, .typeEnum = TU104_PTOP_TYPE_NVENC, .resetId = 17, .devicePriBase = 0x00070000, .isEngine = 1, .rlEngId = 8, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 0 }, + { .faultId = TU104_FAULT_ID_SEC2, .instanceId = 0, .typeEnum = TU104_PTOP_TYPE_SEC, .resetId = 19, .devicePriBase = 0x00840000, .isEngine = 1, .rlEngId = 9, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 0 }, + { .faultId = TU104_FAULT_ID_NVJPG0, .instanceId = 0, .typeEnum = TU104_PTOP_TYPE_NVJPG, .resetId = 20, .devicePriBase = 0x00080000, .isEngine = 1, .rlEngId = 10, .runlistPriBase = 0, .groupId = 0, .ginTargetId = 0, .deviceBroadcastPriBase = 0, .groupLocalInstanceId = 0 }, +}; + +#define TU104_INTERNAL_DEVICE_TABLE_SIZE 11 + +/* ============================================================ + * GspStaticConfigInfo field offsets (ABI-verified, 1656 bytes total) + * ============================================================ */ +#define TU104_GSCI_OFF_grCapsBits 0 /* 23 bytes */ +#define TU104_GSCI_OFF_gidInfo 24 /* 268 bytes */ +#define TU104_GSCI_OFF_SKUInfo 292 /* 48 bytes */ +#define TU104_GSCI_OFF_fbRegionInfoParams 344 /* 776 bytes */ +#define TU104_GSCI_OFF_sriovCaps 1120 /* 80 bytes */ +#define TU104_GSCI_OFF_sriovMaxGfid 1200 /* 4 bytes */ +#define TU104_GSCI_OFF_engineCaps 1204 /* 12 bytes */ +#define TU104_GSCI_OFF_poisonFuseEnabled 1216 /* 1 byte */ +#define TU104_GSCI_OFF_fb_length 1224 /* 8 bytes */ +#define TU104_GSCI_OFF_fbio_mask 1232 /* 8 bytes */ +#define TU104_GSCI_OFF_fb_bus_width 1240 /* 4 bytes */ +#define TU104_GSCI_OFF_fb_ram_type 1244 /* 4 bytes */ +#define TU104_GSCI_OFF_fbp_mask 1248 /* 8 bytes */ +#define TU104_GSCI_OFF_l2_cache_size 1256 /* 4 bytes */ +#define TU104_GSCI_OFF_gpuNameString 1260 /* 64 bytes */ +#define TU104_GSCI_OFF_gpuShortNameString 1324 /* 64 bytes */ +#define TU104_GSCI_OFF_gpuNameString_Unicode 1388 /* 128 bytes */ +#define TU104_GSCI_OFF_bIsMobile 1524 /* 1 byte */ +#define TU104_GSCI_OFF_bar1PdeBase 1536 /* 8 bytes */ +#define TU104_GSCI_OFF_bar2PdeBase 1544 /* 8 bytes */ +#define TU104_GSCI_OFF_bVbiosValid 1552 /* 1 byte */ +#define TU104_GSCI_OFF_vbiosSubVendor 1556 /* 4 bytes */ +#define TU104_GSCI_OFF_vbiosSubDevice 1560 /* 4 bytes */ +#define TU104_GSCI_OFF_hInternalClient 1600 /* 4 bytes */ +#define TU104_GSCI_OFF_hInternalDevice 1604 /* 4 bytes */ +#define TU104_GSCI_OFF_hInternalSubdevice 1608 /* 4 bytes */ +#define TU104_GSCI_OFF_ecidInfo 1616 /* 24 bytes */ +#define TU104_GSCI_OFF_fwWprLayoutOffset 1640 /* 16 bytes */ +#define TU104_GSCI_SIZE 1656 + +/* Internal handle values (g_gpu_nvoc.h, g_resserv_nvoc.h) */ +#define TU104_H_INTERNAL_CLIENT 0xC1E00000 +#define TU104_H_INTERNAL_DEVICE 0xABCD0080 +#define TU104_H_INTERNAL_SUBDEVICE 0xABCD2080 + +/* GSP_RM_CONTROL sub-commands */ +#define TU104_CTRL_INTERNAL_GET_DEVICE_INFO_TABLE 0x20800a40 +#define TU104_CTRL_FIFO_GET_DEVICE_INFO_TABLE 0x20801112 +#define TU104_CTRL_INTERNAL_INTR_GET_KERNEL_TABLE 0x20800a5c +#define TU104_CTRL_GPU_GET_CONSTRUCTED_FALCON_INFO 0x208001b0 + +/* FB region info params: numFBRegions(4) + 16 × 48B entries = 772 → aligned 776 */ +struct tu104_fb_region { + u64 base; + u64 limit; + u64 reserved; + u32 performance; + u8 supportCompressed; + u8 supportISO; + u8 bProtected; + u8 blackList[17]; + u8 _pad[6]; +}; + +#endif /* _GSP_RM_TU104_H_ */ -- cgit v1.2.3