From 680c0ad248dd54f5778808f19fbb6c2f9835ebab Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Fri, 2 Oct 2026 15:45:55 -0400 Subject: kernel: capture the guest's PROMOTE_CTX entries; add promote mode 19 (guest-faithful 9-entry set) - gsp_rm_emulation: dump every guest GPU_PROMOTE_CTX (0x2080012b) params and entries so the real client's promote shape is observable (captured on the emulation path: MAIN 2 MB PA+VA, PATCH, BUNDLE_CB/PAGEPOOL/ATTRIBUTE_CB/ RTV_CB_GLOBAL VA-only, FECS, PRIV, UNRESTRICTED) - gsp_phys_booter: promote mode 19 replays that exact 9-entry shape on the V5 channel with host buffers (MAIN 2 MB + PDB map, small buffers, booking-only VAs); clamp 0..19 - docs/eng-ops: mode-19 result recorded - the server never replied (fn76 -110) and the release fail-safe held WPR2/GPU until an approved reboot; mode 19 is known-wedging --- docs/eng-ops.md | 2 +- kernel/gsp_phys_booter.c | 215 +++++++++++++++++++++++++++++++++++++++++++++- kernel/gsp_phys_gpu.c | 6 +- kernel/gsp_rm_emulation.c | 61 +++++++++++++ 4 files changed, 277 insertions(+), 7 deletions(-) diff --git a/docs/eng-ops.md b/docs/eng-ops.md index d1bd71f..43103a9 100644 --- a/docs/eng-ops.md +++ b/docs/eng-ops.md @@ -602,7 +602,7 @@ authoritative for the literal default. | `gsp_phys_fault_relay` | 0 | On a relay timeout, drain the status queue for the engine fault (`0x1006`, corroborated by `0x1020`), match runlistId/chid and write the guest channel notifier + event. | Requires `gsp_phys_relay=1` and the three parameters above. | | `gsp_phys_fault_inject` | 0 | Debug: write the relayed pair's source PTE as zero to force an engine fault on the next relay submission; only relayed pairs are affected. | Requires the relay path; default 0 programs the real source HPA. | | `gsp_phys_rmcall_engine` | 0 | Ladder engine: 0 = COPY0 (CE copy submission), 1 = GR0 (compute object, BIND engineType 1, GR semaphore release). Snapshotted when stage 3 starts; mid-session changes are ignored. | Requires boot + init-done; CE forwarding paths refuse on a GR ladder. | -| `gsp_phys_rmcall_gr_promote` | 0 (clamp 0..18) | GR context-promote bisection: **0 never sends `PROMOTE_CTX`** (the ladder stops after the geometry queries, context buffer/PT allocation, PDB mapping and channel/compute alloc, before BIND); 1 = single MAIN entry, initialize/no-nonmapped, size raw+subctx; 2 = two-phase PA-only then VA-only with the same PA/VA/size; 3 = single entry with the exact raw size; 4 = two-phase with the reference client's zero-field VA-only step; 5 = one control with `entryCount=2` (PA-only init + zero-field VA-only); 6 = no-entry `hVirtMemory`: allocate an `NV01_MEMORY_VIRTUAL` (class 0x70) VA-range object on the ladder VAS at the context window, then `PROMOTE_CTX` with `hVirtMemory`/`virtAddress`/`size` and `entryCount=0`; 7 = no-entry bare `(virtAddress, size)` with `entryCount=0` and no `hVirtMemory`; 8 = single MAIN entry like 1 on the KMD-managed channel variant V6 (the V5 channel plus alloc flag `SKIP_CTXBUFFER_ALLOC` bit 31); 9 = same V6 channel with the stock client's zero-field VA-only MAIN entry (`bInitialize=0`); 10 = same V6 channel, `GPU_INITIALIZE_CTX` (`0x2080012d`) installs the physical context memdesc first, then `PROMOTE_CTX` `entryCount=0` bare `(virtAddress, size)`; 11 = mode 10 with the context buffer swapped to a server FBMEM allocation (class 0x40, VIDMEM `physAttr=0x4`, aperture-0 leaf PTEs); 12 = V5 UVM/vGPU-faithful PA-only MAIN entry (`bInitialize=1 bNonmapped=1`, no VA) and continue to BIND/SCHEDULE/submit; 13 = mode 12 plus a PATCH PA-only promote first (size from engine context properties engineId 0x10); 14 = V6 KMD channel + PATCH PA-only then the single-entry MAIN init+VA (the flow that books the context VA and calls the commit); 15 = mode 14 with a server FBMEM MAIN buffer and VIDMEM `physAttr 0x4`; 16 = mode 14 plus a PM ctxsw PA-only promote first (engineId 0x09); 17 = no client promote, paired with `gsp_phys_srv_ctxbuf=1` (pre-boot registry profile with `RMSetClientRMAllocatedCtxBuffer=0`) so the GSP-RM server-manages the context buffers; 18 = mode 8 shape plus a client `FERMI_CONTEXT_SHARE_A` (0x9067) on the TSG carried as `hContextShare`, whose construct allocates the GraphicsSubcontext. Live result: modes 1, 2 step 2 and 4 all wedged GSP-RM (`0x52a8b04`, `NV_ERR_OBJECT_NOT_FOUND` 0x57 in the LOGINIT stack) while the PA-only nonmapped step of modes 2/4 is accepted every time - the VA resolve, not the entry fields, is the wall (our external VAS has no server-side VA booking). Mode 6 (hVirtMemory, V5 channel) was refused **cleanly** (`status=0x1f` `NV_ERR_INVALID_ARGUMENT`, no assert, clean release) because the channel was not KMD-managed. Modes 8-11 all completed with **clean releases** (WPR2 down, driver rebound, 5 locks, pstore empty): the V6 channel alloc is accepted (`flags=0x80800000`); modes 8 and 9 (with and without an init entry) both return a clean `status=0x40` `NV_ERR_INVALID_STATE` (mode 9 without the server error log - entry shape irrelevant); mode 10's `GPU_INITIALIZE_CTX` is accepted (`status=0`) but the bare-VA promote still returns `0x40` with the same server log anchor; mode 11 (server FBMEM context buffer) returns `0x40` with a **different** server log anchor, so FBMEM moves the failure to a different structural check. Session 2 (modes 12-17, all clean): mode 12's V5 PA-only MAIN promote is **accepted** (`status=0`) and BIND succeeds, but the GR push faults `CTXNOTVALID` (`sem=0`, `GPGet=1`) - the accepted entry does not set `ENG_CTX_VALID`; mode 13 (PATCH first) and mode 14/15/16 (V6 KMD + PATCH, +PM, FBMEM/VIDMEM) all leave the MAIN promote at the same `0x40` rec-677 site as mode 8, so PATCH/PM/buffer type are not the missing prerequisite; mode 17 (registry `RMSetClientRMAllocatedCtxBuffer=0`, no promote) also faults `CTXNOTVALID`, so even server-managed context buffers leave the channel invalid; mode 18 (client context share, share alloc accepted but `subctxId` not written back) leaves the MAIN promote at the same `0x40` - the remaining wall is the engine-context commit/RAMFC path, not buffer ownership. Offline, the `0x40` candidate is the helper at `0x5aff328` (returns `NV_ERR_INVALID_STATE` when `*(u64*)(a0+0x1b90)==NULL` or `*(u8*)(a0+0x555)==0`). Nonzero enables can still assert GSP-RM and wedge the device. | GR ladder only; value snapshotted at stage 3; **requires confirmed reboot readiness** before enabling. | +| `gsp_phys_rmcall_gr_promote` | 0 (clamp 0..18) | GR context-promote bisection: **0 never sends `PROMOTE_CTX`** (the ladder stops after the geometry queries, context buffer/PT allocation, PDB mapping and channel/compute alloc, before BIND); 1 = single MAIN entry, initialize/no-nonmapped, size raw+subctx; 2 = two-phase PA-only then VA-only with the same PA/VA/size; 3 = single entry with the exact raw size; 4 = two-phase with the reference client's zero-field VA-only step; 5 = one control with `entryCount=2` (PA-only init + zero-field VA-only); 6 = no-entry `hVirtMemory`: allocate an `NV01_MEMORY_VIRTUAL` (class 0x70) VA-range object on the ladder VAS at the context window, then `PROMOTE_CTX` with `hVirtMemory`/`virtAddress`/`size` and `entryCount=0`; 7 = no-entry bare `(virtAddress, size)` with `entryCount=0` and no `hVirtMemory`; 8 = single MAIN entry like 1 on the KMD-managed channel variant V6 (the V5 channel plus alloc flag `SKIP_CTXBUFFER_ALLOC` bit 31); 9 = same V6 channel with the stock client's zero-field VA-only MAIN entry (`bInitialize=0`); 10 = same V6 channel, `GPU_INITIALIZE_CTX` (`0x2080012d`) installs the physical context memdesc first, then `PROMOTE_CTX` `entryCount=0` bare `(virtAddress, size)`; 11 = mode 10 with the context buffer swapped to a server FBMEM allocation (class 0x40, VIDMEM `physAttr=0x4`, aperture-0 leaf PTEs); 12 = V5 UVM/vGPU-faithful PA-only MAIN entry (`bInitialize=1 bNonmapped=1`, no VA) and continue to BIND/SCHEDULE/submit; 13 = mode 12 plus a PATCH PA-only promote first (size from engine context properties engineId 0x10); 14 = V6 KMD channel + PATCH PA-only then the single-entry MAIN init+VA (the flow that books the context VA and calls the commit); 15 = mode 14 with a server FBMEM MAIN buffer and VIDMEM `physAttr 0x4`; 16 = mode 14 plus a PM ctxsw PA-only promote first (engineId 0x09); 17 = no client promote, paired with `gsp_phys_srv_ctxbuf=1` (pre-boot registry profile with `RMSetClientRMAllocatedCtxBuffer=0`) so the GSP-RM server-manages the context buffers; 18 = mode 8 shape plus a client `FERMI_CONTEXT_SHARE_A` (0x9067) on the TSG carried as `hContextShare`, whose construct allocates the GraphicsSubcontext. Live result: modes 1, 2 step 2 and 4 all wedged GSP-RM (`0x52a8b04`, `NV_ERR_OBJECT_NOT_FOUND` 0x57 in the LOGINIT stack) while the PA-only nonmapped step of modes 2/4 is accepted every time - the VA resolve, not the entry fields, is the wall (our external VAS has no server-side VA booking). Mode 6 (hVirtMemory, V5 channel) was refused **cleanly** (`status=0x1f` `NV_ERR_INVALID_ARGUMENT`, no assert, clean release) because the channel was not KMD-managed. Modes 8-11 all completed with **clean releases** (WPR2 down, driver rebound, 5 locks, pstore empty): the V6 channel alloc is accepted (`flags=0x80800000`); modes 8 and 9 (with and without an init entry) both return a clean `status=0x40` `NV_ERR_INVALID_STATE` (mode 9 without the server error log - entry shape irrelevant); mode 10's `GPU_INITIALIZE_CTX` is accepted (`status=0`) but the bare-VA promote still returns `0x40` with the same server log anchor; mode 11 (server FBMEM context buffer) returns `0x40` with a **different** server log anchor, so FBMEM moves the failure to a different structural check. Session 2 (modes 12-17, all clean): mode 12's V5 PA-only MAIN promote is **accepted** (`status=0`) and BIND succeeds, but the GR push faults `CTXNOTVALID` (`sem=0`, `GPGet=1`) - the accepted entry does not set `ENG_CTX_VALID`; mode 13 (PATCH first) and mode 14/15/16 (V6 KMD + PATCH, +PM, FBMEM/VIDMEM) all leave the MAIN promote at the same `0x40` rec-677 site as mode 8, so PATCH/PM/buffer type are not the missing prerequisite; mode 17 (registry `RMSetClientRMAllocatedCtxBuffer=0`, no promote) also faults `CTXNOTVALID`, so even server-managed context buffers leave the channel invalid; mode 18 (client context share, share alloc accepted but `subctxId` not written back) leaves the MAIN promote at the same `0x40` - the remaining wall is the engine-context commit/RAMFC path, not buffer ownership. Offline, the `0x40` candidate is the helper at `0x5aff328` (returns `NV_ERR_INVALID_STATE` when `*(u64*)(a0+0x1b90)==NULL` or `*(u8*)(a0+0x555)==0`). **Mode 19 (the stock guest's captured 9-entry set, MAIN 2 MB PA+VA on V5) is known-wedging**: fn76 never replied, the debugfs trigger dumped core, and the release fail-safe held WPR2/GPU until an approved reboot - never run it or any V5 promote with a MAIN VA entry again. Nonzero enables can still assert GSP-RM and wedge the device. | GR ladder only; value snapshotted at stage 3; **requires confirmed reboot readiness** before enabling. | | `gsp_phys_repower` | 0 (latched) | Restore the pre-claim runtime-PM/D3cold policy when releasing the GPU; with 0 the D0 locks and runtime reference are deliberately left held until reboot. | **Never enable** (rule 3); re-arming D3cold after a claim can hard-freeze the host. | The emulation-side `sema_*` parameters (ring discovery, release recovery, diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index afbfe28..3d1ccea 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -340,6 +340,13 @@ bool gsp_phys_boot_requested(void) #define GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_MAIN 0u #define GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_PM 1u #define GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_PATCH 2u +#define GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_BUNDLE_CB 3u +#define GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_PAGEPOOL 4u +#define GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_ATTRIBUTE_CB 5u +#define GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_RTV_CB_GLOBAL 6u +#define GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_FECS_EVENT 9u +#define GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_PRIV_MAP 10u +#define GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_UNR_PRIV_MAP 11u /* * GR_PATCH engine id in NV0080_CTRL_CMD_FIFO_GET_ENGINE_CONTEXT_PROPERTIES * (ctrl/ctrl0080/ctrl0080fifo.h:137 -> 0x10), the size source for the @@ -420,8 +427,21 @@ bool gsp_phys_boot_requested(void) * (VEID 0) whose absence is the leading candidate for the promote's 0x40. */ #define GSP_PHYS_RMCALL_GR_PROMOTE_CTXSHARE 18u +/* + * Mode 19 (2026-10-02): the exact promote shape the stock guest driver sends + * (captured from the emulation path). One control, entryCount=9: + * MAIN bInit=1 bNonmapped=0 attr=0x6 PA+VA size=0x200000, + * PATCH bInit=1 bNonmapped=0 attr=0x6 PA+VA size=0x2000, + * BUNDLE_CB/PAGEPOOL/ATTRIBUTE_CB/RTV_CB_GLOBAL VA-only, + * FECS_EVENT bInit=1 bNonmapped=0 attr=0x6 PA+VA size=0x1000, + * PRIV_ACCESS_MAP bInit=1 bNonmapped=1 attr=0x6 PA size=0x1000, + * UNRESTRICTED_PRIV_ACCESS_MAP bInit=1 bNonmapped=0 attr=0x6 PA+VA size=0x1000. + * MAIN is 2 MB (the guest's size, larger than our server-query total) and + * carries both PA and VA in one entry, on the non-KMD V5 channel. + */ +#define GSP_PHYS_RMCALL_GR_PROMOTE_GUEST_SET 19u #define GSP_PHYS_RMCALL_GR_PROMOTE_MAX \ - (GSP_PHYS_RMCALL_GR_PROMOTE_CTXSHARE) + (GSP_PHYS_RMCALL_GR_PROMOTE_GUEST_SET) /* * Work-submit token notifier slot. The default index is @@ -1281,6 +1301,18 @@ static struct gsp_phys_boot_stage { u64 rmcall_pm_pa; /* PM buffer DMA base */ u32 rmcall_pm_size; /* queried PM save size */ struct gsp_phys_boot_chunk rmcall_pm; + /* + * Mode 19: the guest-faithful full promote set's small buffers + * (FECS event, private access map, unrestricted private access map). + * PATCH uses rmcall_patch with the guest's 0x2000 size. + */ + bool rmcall_guest_set_prepared; + struct gsp_phys_boot_chunk rmcall_fecs; + struct gsp_phys_boot_chunk rmcall_priv; + struct gsp_phys_boot_chunk rmcall_unrestricted; + u64 rmcall_fecs_pa; + u64 rmcall_priv_pa; + u64 rmcall_unrestricted_pa; /* * GR context-promote bisection snapshot (stage 3, GR0 only): the 0..3 * gsp_phys_rmcall_gr_promote value, the number of accepted promote @@ -1900,6 +1932,10 @@ static void gsp_phys_rmcall_reset(void) boot_stage.rmcall_pm_prepared = false; boot_stage.rmcall_pm_pa = 0; boot_stage.rmcall_pm_size = 0; + boot_stage.rmcall_guest_set_prepared = false; + boot_stage.rmcall_fecs_pa = 0; + boot_stage.rmcall_priv_pa = 0; + boot_stage.rmcall_unrestricted_pa = 0; boot_stage.rmcall_gr_promote = 0; boot_stage.rmcall_gr_promote_valid = false; boot_stage.rmcall_gr_promote_change_logged = false; @@ -6694,6 +6730,8 @@ static const char *gsp_phys_rmcall_gr_promote_name(void) return "no client promote; server-managed ctx buffers (gsp_phys_srv_ctxbuf=1)"; case GSP_PHYS_RMCALL_GR_PROMOTE_CTXSHARE: return "mode 8 shape plus a client FERMI_CONTEXT_SHARE_A (hContextShare) that allocates the GraphicsSubcontext"; + case GSP_PHYS_RMCALL_GR_PROMOTE_GUEST_SET: + return "stock guest's 9-entry promote set (MAIN 2 MB PA+VA, PATCH/global/FECS/priv maps) on V5"; default: return "off (PROMOTE_CTX never sent)"; } @@ -6884,7 +6922,9 @@ static int gsp_phys_rmcall_alloc_channel(void __iomem *bar0) (boot_stage.rmcall_gr_promote == GSP_PHYS_RMCALL_GR_PROMOTE_PA_ONLY || boot_stage.rmcall_gr_promote == - GSP_PHYS_RMCALL_GR_PROMOTE_PA_ONLY_PATCH)) { + GSP_PHYS_RMCALL_GR_PROMOTE_PA_ONLY_PATCH || + boot_stage.rmcall_gr_promote == + GSP_PHYS_RMCALL_GR_PROMOTE_GUEST_SET)) { vars = &gsp_phys_rmcall_channel_variants[0]; attempts = 1; } @@ -7353,6 +7393,14 @@ static int gsp_phys_rmcall_gr_ctx_prepare(void __iomem *bar0) total = ALIGN((u64)raw, GSP_PHYS_PAGE_SIZE) + (u64)max_subctx * GSP_PHYS_PAGE_SIZE; + /* + * Mode 19 replays the stock guest's promote: its MAIN entry is 2 MB + * (the guest-side context buffer size), so allocate and promote that + * exact size instead of the server-query total. + */ + if (boot_stage.rmcall_gr_promote == + GSP_PHYS_RMCALL_GR_PROMOTE_GUEST_SET) + total = 2u * 1024u * 1024u; if (!total || total > GSP_PHYS_RMCALL_GR_CTX_MAX_SIZE) { pr_err("vgsp-boot: rm-call: GR ctx MAIN size 0x%llx (raw=0x%x maxSubctx=%u) is outside the 1..0x%x sanity bound; aborting the ladder with no retry\n", total, raw, max_subctx, GSP_PHYS_RMCALL_GR_CTX_MAX_SIZE); @@ -7449,6 +7497,27 @@ err_free: return ret; } +/* + * Fill one promote entry explicitly (mode 19). All fields as the stock + * guest's captured promote: bufferId, PA, VA, size, physAttr, and the + * initialize/nonmapped flags. + */ +static void gsp_phys_rmcall_promote_entry_fill( + struct gsp_phys_rmcall_promote_params *p, unsigned int idx, u16 bid, + u64 pa, u64 va, u64 size, u32 attr, u8 init, u8 nonmapped) +{ + struct gsp_phys_rmcall_promote_entry *e = &p->entry[idx]; + + memset(e, 0, sizeof(*e)); + e->gpu_phys_addr = cpu_to_le64(pa); + e->gpu_virt_addr = cpu_to_le64(va); + e->size = cpu_to_le64(size); + e->phys_attr = cpu_to_le32(attr); + e->buffer_id = cpu_to_le16(bid); + e->b_initialize = init; + e->b_nonmapped = nonmapped; +} + /* * Auxiliary GR context-buffer prepare (PATCH/PM): query the save size for * `engine_id` (GET_ENGINE_CONTEXT_PROPERTIES), allocate a zeroed physically @@ -7536,6 +7605,70 @@ static int gsp_phys_rmcall_gr_pm_prepare(void __iomem *bar0) &boot_stage.rmcall_pm_prepared); } +/* + * Mode-19 helper: allocate the small buffers of the guest-faithful promote + * set with the guest's sizes (PATCH 0x2000, FECS event 0x1000, private access + * map 0x1000, unrestricted private access map 0x1000). All sysmem, + * contiguous, zeroed; a failure aborts the ladder with no retry. + */ +static int gsp_phys_rmcall_gr_guest_set_prepare(void __iomem *bar0) +{ + struct gsp_phys_boot_chunk *patch = &boot_stage.rmcall_patch; + struct gsp_phys_boot_chunk *fecs = &boot_stage.rmcall_fecs; + struct gsp_phys_boot_chunk *priv = &boot_stage.rmcall_priv; + struct gsp_phys_boot_chunk *unr = &boot_stage.rmcall_unrestricted; + int ret; + + (void)bar0; + if (!boot_stage.pdev) + return -EINVAL; + if (boot_stage.rmcall_guest_set_prepared) + return 0; + + ret = gsp_phys_chunk_alloc(boot_stage.pdev, patch, 2u); + if (ret || patch->page_dma) + goto fail_patch; + gsp_phys_chunk_zero(patch); + boot_stage.rmcall_patch_pa = (u64)patch->dma; + boot_stage.rmcall_patch_size = 0x2000u; + boot_stage.rmcall_patch_prepared = true; + + ret = gsp_phys_chunk_alloc(boot_stage.pdev, fecs, 1u); + if (ret || fecs->page_dma) + goto fail_fecs; + gsp_phys_chunk_zero(fecs); + boot_stage.rmcall_fecs_pa = (u64)fecs->dma; + + ret = gsp_phys_chunk_alloc(boot_stage.pdev, priv, 1u); + if (ret || priv->page_dma) + goto fail_priv; + gsp_phys_chunk_zero(priv); + boot_stage.rmcall_priv_pa = (u64)priv->dma; + + ret = gsp_phys_chunk_alloc(boot_stage.pdev, unr, 1u); + if (ret || unr->page_dma) + goto fail_unr; + gsp_phys_chunk_zero(unr); + boot_stage.rmcall_unrestricted_pa = (u64)unr->dma; + + boot_stage.rmcall_guest_set_prepared = true; + pr_info("vgsp-boot: rm-call: GR guest-set buffers prepared: patch pa=0x%llx size=0x2000, fecs pa=0x%llx, priv pa=0x%llx, unrestricted pa=0x%llx (all sysmem contiguous zeroed)\n", + boot_stage.rmcall_patch_pa, boot_stage.rmcall_fecs_pa, + boot_stage.rmcall_priv_pa, boot_stage.rmcall_unrestricted_pa); + return 0; + +fail_unr: + gsp_phys_chunk_free(boot_stage.pdev, priv); +fail_priv: + gsp_phys_chunk_free(boot_stage.pdev, fecs); +fail_fecs: + gsp_phys_chunk_free(boot_stage.pdev, patch); +fail_patch: + pr_err("vgsp-boot: rm-call: GR guest-set buffer allocation failed (%d); aborting the ladder with no retry\n", + ret); + return ret ? ret : -ENOMEM; +} + /* * Fill one MAIN promote entry with the prepared PA/VA and the snapshot's * entry size. initialize/nonmapped carry the bisection shape: modes 1/3/5 @@ -7726,7 +7859,8 @@ static int gsp_phys_rmcall_gr_ctx_promote(void __iomem *bar0) * modes 8-11). */ if ((mode == GSP_PHYS_RMCALL_GR_PROMOTE_PA_ONLY || - mode == GSP_PHYS_RMCALL_GR_PROMOTE_PA_ONLY_PATCH) && + mode == GSP_PHYS_RMCALL_GR_PROMOTE_PA_ONLY_PATCH || + mode == GSP_PHYS_RMCALL_GR_PROMOTE_GUEST_SET) && boot_stage.rmcall_channel_variant != gsp_phys_rmcall_channel_variants[0].id) { pr_err("vgsp-boot: rm-call: mode %u requires the V5 channel (V%u %s); winning variant is V%u; no PROMOTE_CTX is sent and the ladder aborts with no retry\n", @@ -8280,6 +8414,78 @@ static int gsp_phys_rmcall_gr_ctx_promote(void __iomem *bar0) */ pr_info("vgsp-boot: rm-call: gr_promote_param=17: no client PROMOTE_CTX is sent; the context buffers are server-managed (gsp_phys_srv_ctxbuf registry profile) and the server commits the engine context itself; continuing to BIND/SCHEDULE/submit\n"); boot_stage.rmcall_gr_promote_step = 0; + } else if (mode == GSP_PHYS_RMCALL_GR_PROMOTE_GUEST_SET) { + /* + * Mode 19: the stock guest's exact multi-entry promote. One + * control, entryCount=9; MAIN carries both PA and VA (2 MB), + * the global context buffers are VA-only, PATCH/FECS/priv maps + * are PA-initialized. VAs other than MAIN's are booking-only + * (the engine does not touch those buffers for the semaphore + * push); they are placed above the ctx window so they cannot + * collide with the PDB-mapped regions. + */ + u32 attr = GSP_PHYS_RMCALL_GR_CTX_PHYS_ATTR; + u64 bk = GSP_PHYS_RMCALL_VA_GR_CTX + 0x400000ULL; + u32 aux_rpc = 0, aux_status = 0; + + ret = gsp_phys_rmcall_gr_guest_set_prepare(bar0); + if (ret) { + kvfree(p); + return ret; + } + + p->entry_count = cpu_to_le32(9); + gsp_phys_rmcall_promote_entry_fill(p, 0, + GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_MAIN, + boot_stage.rmcall_ctx_pa, boot_stage.rmcall_ctx_va, + boot_stage.rmcall_ctx_size, attr, 1, 0); + gsp_phys_rmcall_promote_entry_fill(p, 1, + GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_PATCH, + boot_stage.rmcall_patch_pa, bk + 0x1000, + boot_stage.rmcall_patch_size, attr, 1, 0); + gsp_phys_rmcall_promote_entry_fill(p, 2, + GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_BUNDLE_CB, + 0, bk + 0x2000, 0, 0, 0, 0); + gsp_phys_rmcall_promote_entry_fill(p, 3, + GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_PAGEPOOL, + 0, bk + 0x3000, 0, 0, 0, 0); + gsp_phys_rmcall_promote_entry_fill(p, 4, + GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_ATTRIBUTE_CB, + 0, bk + 0x4000, 0, 0, 0, 0); + gsp_phys_rmcall_promote_entry_fill(p, 5, + GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_RTV_CB_GLOBAL, + 0, bk + 0x5000, 0, 0, 0, 0); + gsp_phys_rmcall_promote_entry_fill(p, 6, + GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_FECS_EVENT, + boot_stage.rmcall_fecs_pa, bk + 0x6000, 0x1000, attr, + 1, 0); + gsp_phys_rmcall_promote_entry_fill(p, 7, + GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_PRIV_MAP, + boot_stage.rmcall_priv_pa, 0, 0x1000, attr, 1, 1); + gsp_phys_rmcall_promote_entry_fill(p, 8, + GSP_PHYS_RMCALL_GR_CTX_BUFFER_ID_UNR_PRIV_MAP, + boot_stage.rmcall_unrestricted_pa, bk + 0x7000, + 0x1000, attr, 1, 0); + + ret = gsp_phys_rmcall_control(bar0, + boot_stage.rmcall_subdevice, + GSP_PHYS_RMCALL_CTRL_GPU_PROMOTE_CTX, + p, sizeof(*p), 24, NULL, 0, + &aux_rpc, &aux_status, + "GPU_PROMOTE_CTX (guest set, 9 entries)"); + pr_info("vgsp-boot: rm-call: PROMOTE_CTX guest-set: entryCount=9 status=0x%x rpc_result=0x%x (hObject=0x%x hChanClient=0x%x engineType=0x%x MAIN pa=0x%llx va=0x%llx size=0x%x; gr_promote_param=%u)\n", + aux_status, aux_rpc, boot_stage.rmcall_channel, + boot_stage.rmcall_client, GSP_PHYS_RMCALL_ENGINE_GR0, + boot_stage.rmcall_ctx_pa, boot_stage.rmcall_ctx_va, + boot_stage.rmcall_ctx_size, mode); + if (ret) { + pr_err("vgsp-boot: rm-call: PROMOTE_CTX guest-set failed (status=0x%x rpc_result=0x%x, %s, ret=%d); the ladder aborts before BIND with no retry\n", + aux_status, aux_rpc, + gsp_phys_rmcall_status_str(aux_status), ret); + kvfree(p); + return ret; + } + boot_stage.rmcall_gr_promote_step = 1; } else { pr_err("vgsp-boot: rm-call: unknown gr_promote_param=%u reached the promote step; no PROMOTE_CTX is sent and the ladder aborts with no retry\n", mode); @@ -10545,6 +10751,9 @@ static void gsp_phys_rmcall_fini(struct pci_dev *pdev) gsp_phys_chunk_free(pdev, &boot_stage.rmcall_ctx_pt[i]); gsp_phys_chunk_free(pdev, &boot_stage.rmcall_patch); gsp_phys_chunk_free(pdev, &boot_stage.rmcall_pm); + gsp_phys_chunk_free(pdev, &boot_stage.rmcall_fecs); + gsp_phys_chunk_free(pdev, &boot_stage.rmcall_priv); + gsp_phys_chunk_free(pdev, &boot_stage.rmcall_unrestricted); gsp_phys_rmcall_reset(); } diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c index c352056..1fa1670 100644 --- a/kernel/gsp_phys_gpu.c +++ b/kernel/gsp_phys_gpu.c @@ -424,8 +424,8 @@ static int gsp_phys_rmcall_gr_promote_set(const char *val, return ret; if (gsp_phys_rmcall_gr_promote_param < 0 || - gsp_phys_rmcall_gr_promote_param > 18) { - pr_warn("vgsp: gsp_phys_rmcall_gr_promote=%d is outside 0..18; clamping to 0 (no PROMOTE_CTX)\n", + gsp_phys_rmcall_gr_promote_param > 19) { + pr_warn("vgsp: gsp_phys_rmcall_gr_promote=%d is outside 0..19; clamping to 0 (no PROMOTE_CTX)\n", gsp_phys_rmcall_gr_promote_param); gsp_phys_rmcall_gr_promote_param = 0; } @@ -444,7 +444,7 @@ module_param_cb(gsp_phys_rmcall_gr_promote, &gsp_phys_rmcall_gr_promote_ops, &gsp_phys_rmcall_gr_promote_param, 0644); MODULE_PARM_DESC(gsp_phys_rmcall_gr_promote, - "GR context-promote bisection (default 0: never send PROMOTE_CTX; 1: single entry init+nonmapped=0 size=raw+subctx, the p6h1b shape; 2: two-phase PA-only nonmapped then VA-only with the same fields; 3: single entry size=raw; 4: two-phase with a zero-field VA-only step; 5: batched entryCount=2 PA-only init + zero-field VA-only; 6: no-entry hVirtMemory, NV01_MEMORY_VIRTUAL (class 0x70) on the VAS at the context window + PROMOTE_CTX entryCount=0; 7: no-entry bare virtAddress+size, entryCount=0; 8: single MAIN entry like 1 on the KMD-managed channel (V6 SKIP_CTXBUFFER_ALLOC), server creates the virtual context memdesc itself; 9: same V6 channel with the stock client's zero-field VA-only MAIN entry, bInitialize=0; 10: same V6 channel, GPU_INITIALIZE_CTX (0x2080012d) installs the physical context memdesc then PROMOTE_CTX entryCount=0 bare virtAddress+size; 11: mode 10 with a server FBMEM context buffer, VIDMEM physAttr 0x4; 12: V5 UVM/vGPU-faithful PA-only MAIN entry (bInitialize=1 bNonmapped=1, no VA) and continue to BIND/SCHEDULE/submit; 13: mode 12 plus a PATCH PA-only promote first, sized from GET_ENGINE_CONTEXT_PROPERTIES engineId 0x10; 14: KMD channel (V6) + PATCH PA-only then the single-entry MAIN init+VA (the flow that books the context VA and commits ENG_CTX_VALID); 15: mode 14 with a server FBMEM MAIN context buffer and VIDMEM physAttr 0x4; 16: mode 14 plus a PM ctxsw PA-only promote first; 17: no client PROMOTE_CTX, paired with gsp_phys_srv_ctxbuf=1 so the GSP-RM server-manages and commits the context; 18: mode 8 shape plus a client FERMI_CONTEXT_SHARE_A on the TSG (hContextShare) whose construct allocates the GraphicsSubcontext); values outside 0..18 clamp to 0 with a warning"); + "GR context-promote bisection (default 0: never send PROMOTE_CTX; 1: single entry init+nonmapped=0 size=raw+subctx, the p6h1b shape; 2: two-phase PA-only nonmapped then VA-only with the same fields; 3: single entry size=raw; 4: two-phase with a zero-field VA-only step; 5: batched entryCount=2 PA-only init + zero-field VA-only; 6: no-entry hVirtMemory, NV01_MEMORY_VIRTUAL (class 0x70) on the VAS at the context window + PROMOTE_CTX entryCount=0; 7: no-entry bare virtAddress+size, entryCount=0; 8: single MAIN entry like 1 on the KMD-managed channel (V6 SKIP_CTXBUFFER_ALLOC), server creates the virtual context memdesc itself; 9: same V6 channel with the stock client's zero-field VA-only MAIN entry, bInitialize=0; 10: same V6 channel, GPU_INITIALIZE_CTX (0x2080012d) installs the physical context memdesc then PROMOTE_CTX entryCount=0 bare virtAddress+size; 11: mode 10 with a server FBMEM context buffer, VIDMEM physAttr 0x4; 12: V5 UVM/vGPU-faithful PA-only MAIN entry (bInitialize=1 bNonmapped=1, no VA) and continue to BIND/SCHEDULE/submit; 13: mode 12 plus a PATCH PA-only promote first, sized from GET_ENGINE_CONTEXT_PROPERTIES engineId 0x10; 14: KMD channel (V6) + PATCH PA-only then the single-entry MAIN init+VA (the flow that books the context VA and commits ENG_CTX_VALID); 15: mode 14 with a server FBMEM MAIN context buffer and VIDMEM physAttr 0x4; 16: mode 14 plus a PM ctxsw PA-only promote first; 17: no client PROMOTE_CTX, paired with gsp_phys_srv_ctxbuf=1 so the GSP-RM server-manages and commits the context; 18: mode 8 shape plus a client FERMI_CONTEXT_SHARE_A on the TSG (hContextShare) whose construct allocates the GraphicsSubcontext; 19: the stock guest's exact 9-entry promote set (MAIN 2 MB PA+VA, PATCH, global buffers, FECS event, private access maps) on V5); values outside 0..19 clamp to 0 with a warning"); int gsp_phys_rmcall_gr_promote_value(void) { diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c index ad0b4f5..1f636de 100644 --- a/kernel/gsp_rm_emulation.c +++ b/kernel/gsp_rm_emulation.c @@ -3027,6 +3027,67 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx, &out_status, reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size); break; + case 0x2080012b: { /* NV2080_CTRL_CMD_GPU_PROMOTE_CTX */ + /* + * The stock guest promotes its GR virtual contexts through this + * control during channel setup. The emulation answers SUCCESS + * (the physical backend owns execution), but the guest's entry + * shapes are the ground truth for what a real client sends: + * capture them (all fields, all entries) so the physical ladder + * can be driven with the exact same promote shape. + */ + const u8 *pp = req_data + GSP_RM_CTRL_HDR_SIZE; + u32 engine_type = 0, h_cli = 0, ch_id = 0, h_chan_cli = 0; + u32 h_obj = 0, h_virt = 0, entry_count = 0, i, ecap; + + if (req.paramsSize >= 48) { + memcpy(&engine_type, pp + 0, 4); + memcpy(&h_cli, pp + 4, 4); + memcpy(&ch_id, pp + 8, 4); + memcpy(&h_chan_cli, pp + 12, 4); + memcpy(&h_obj, pp + 16, 4); + memcpy(&h_virt, pp + 20, 4); + { + u64 va = 0, sz = 0; + + memcpy(&va, pp + 24, 8); + memcpy(&sz, pp + 32, 8); + memcpy(&entry_count, pp + 40, 4); + ecap = min_t(u32, entry_count, 16u); + nv_vgpu_info(ctx->vgpu, + "vgsp-rm: PROMOTE_CTX engine=0x%x hClient=0x%x chId=%u hChanClient=0x%x hObject=0x%x hVirtMemory=0x%x va=0x%llx size=0x%llx entryCount=%u", + engine_type, h_cli, ch_id, + h_chan_cli, h_obj, h_virt, va, + sz, entry_count); + } + for (i = 0; i < ecap && + 48u + (i + 1u) * 32u <= req.paramsSize; i++) { + const u8 *e = pp + 48 + i * 32; + u64 pa = 0, eva = 0, esz = 0; + u32 attr = 0; + u16 bid = 0; + + memcpy(&pa, e + 0, 8); + memcpy(&eva, e + 8, 8); + memcpy(&esz, e + 16, 8); + memcpy(&attr, e + 24, 4); + memcpy(&bid, e + 28, 2); + nv_vgpu_info(ctx->vgpu, + "vgsp-rm: promote[%u] bufferId=%u bInitialize=%u bNonmapped=%u physAttr=0x%x pa=0x%llx va=0x%llx size=0x%llx", + i, bid, e[30], e[31], attr, pa, + eva, esz); + } + } + out_status = 0; + out_params_size = req.paramsSize; + if (req.paramsSize && + req.paramsSize + GSP_RM_CTRL_HDR_SIZE <= + GSPRM_MAX_SINGLE_ELEM_DATA) + memcpy(reply + GSP_RM_CTRL_HDR_SIZE, pp, + req.paramsSize); + break; + } + default: /* Unknown control command - return success with zeroed params. * Do NOT echo the request: pure queries pass uninitialized -- cgit v1.2.3