summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/gsp_cuda_bridge.h10
-rw-r--r--kernel/gsp_phys_booter.c1257
-rw-r--r--kernel/gsp_phys_booter.h26
-rw-r--r--kernel/gsp_phys_gpu.h8
-rw-r--r--kernel/gsp_sema_service.c497
-rw-r--r--kernel/gsp_sema_service.h10
6 files changed, 1735 insertions, 73 deletions
diff --git a/kernel/gsp_cuda_bridge.h b/kernel/gsp_cuda_bridge.h
index 243392e..472fafb 100644
--- a/kernel/gsp_cuda_bridge.h
+++ b/kernel/gsp_cuda_bridge.h
@@ -15,7 +15,15 @@ struct nv_vgpu_device;
* targeted resolve).
*/
#define GSP_BRIDGE_INLINE_MAX 16
-#define GSP_BRIDGE_INLINE_BYTES 256
+/*
+ * SESSION 153-CONTINUED-88: 256 -> 2048. The GL program delivery needs the
+ * driver's REAL captured upload payloads (observed: a 1536 B SPH+SASS image
+ * at 0x4350900 and a 1408 B image at 0x4350f00, w0=0x020a0481) to reach the
+ * engine-visible page; the 256 B cap truncated them. Every user of this
+ * record type grows with it (rings, bridge queues - all static storage; the
+ * only stack user, svc_gl_flush's prs[], was moved to static storage).
+ */
+#define GSP_BRIDGE_INLINE_BYTES 2048
struct gsp_bridge_inline {
u64 dst_va;
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 7d861ed..3a24967 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -5103,12 +5103,124 @@ module_param_named(sema_mme_fast_ms, gsp_phys_mme_fast_ms, uint, 0644);
MODULE_PARM_DESC(sema_mme_fast_ms,
"Spin-sample the MME fault registers right after each GR launch doorbell for this many ms, logging every transition before the GSP-RM clears them (0 = off; default 400)");
+/*
+ * SESSION 153-CONTINUED-83: store-side emission forensics.
+ *
+ * The store wall: draws retire but `output retirement wait: 0/N pages
+ * changed` and gen0=0 (no MME-generated method witnessed). The client's
+ * Turing draw is EMITTED BY AN MME MACRO (slot 34 = the draw macro:
+ * BEGIN(0x1618) -> SET_VERTEX_ARRAY_START(0xd74) -> END(0x1614), per the
+ * offline replay of sa_full_stream2.bin and the nvk reference builder), so
+ * "no stores" == "the draw macro never emitted". This probe answers three
+ * questions with one line, at the launch boundary:
+ *
+ * 1. DS_HWW_ESR (0x405840) + the per-stage SPH_INFO snapshot
+ * (0x405c00+si*4 START / 0x405c24+si*4 UPPER / 0x405c1c,c20 REGION):
+ * which pipeline stages failed SPH validation / what VA they fetched.
+ * 2. MME_HWW_ESR_INFO (0x404494) PC + MACRO_ADDRESS + PC_VALID,
+ * INFO_EX (0x4044a4) + IR words INFO2/3/4 (0x40449c/a8/ac): did the
+ * MME actually execute a macro, and where did it stop.
+ * 3. FE_DECODE_STATE_EX(i) (0x40a630+i*4): MME_PC 11:0, MACRO 21:12,
+ * GENERATED 31 - the generated-method witness, all four lanes, at
+ * the launch end (the retire-time read races the release writes).
+ *
+ * Bounded: 24 calls per boot, beef family only, one line per call.
+ */
+/*
+ * SESSION 153-CONTINUED-83: MME shadow-RAM read port (bar0 0x404488 index,
+ * 0x40448c data). The MME tracks class methods in its shadow RAM when the
+ * class's SET_MME_SHADOW_RAM_CONTROL mode selects tracking; reading the
+ * port tells us (a) whether the tracked window's methods ever reached the
+ * MME and (b) what values the MME holds for the draw-relevant methods.
+ * A read is: index = nvclass(15:0) | method_addr(27:16) | READ(30), then
+ * read data. Bounded by the caller; host-side diagnostic only.
+ */
+static u32 gsp_phys_mme_shadow_read(void __iomem *bar0, u32 nvclass, u32 mthd)
+{
+ iowrite32(nvclass | ((mthd & 0xfffu) << 16) | (1u << 30),
+ bar0 + 0x00404488u);
+ return ioread32(bar0 + 0x0040448cu);
+}
+
+static void gsp_phys_store_side_log(void __iomem *bar0, const char *tag,
+ u32 h_object)
+{
+ static u32 ss_n;
+ u32 si, sph[6], sphu[6], rr_a, rr_b, ds, ds2, mminfo, mmex;
+ u32 mm2, mm3, mm4, fex[4], fed[4], fds[4];
+
+ if (!bar0 || ss_n >= 40u)
+ return;
+ ss_n++;
+ for (si = 0; si < 6u; si++) {
+ sph[si] = ioread32(bar0 + 0x00405c00u + si * 4u);
+ sphu[si] = ioread32(bar0 + 0x00405c24u + si * 4u);
+ }
+ for (si = 0; si < 4u; si++) {
+ fex[si] = ioread32(bar0 + 0x0040a630u + si * 4u);
+ fed[si] = ioread32(bar0 + 0x0040a620u + si * 4u);
+ fds[si] = ioread32(bar0 + 0x0040a640u + si * 4u);
+ }
+ rr_a = ioread32(bar0 + 0x00405c1cu);
+ rr_b = ioread32(bar0 + 0x00405c20u);
+ ds = ioread32(bar0 + 0x00405840u);
+ ds2 = ioread32(bar0 + 0x00405848u);
+ mminfo = ioread32(bar0 + 0x00404494u);
+ mmex = ioread32(bar0 + 0x004044a4u);
+ mm2 = ioread32(bar0 + 0x0040449cu);
+ mm3 = ioread32(bar0 + 0x004044a8u);
+ mm4 = ioread32(bar0 + 0x004044acu);
+ pr_info("vgsp-store: %s h=0x%x ds=%08x/%08x sph=%08x,%08x,%08x,%08x,%08x,%08x sphU=%08x,%08x,%08x,%08x,%08x,%08x rr=%08x,%08x\n",
+ tag, h_object, ds, ds2,
+ sph[0], sph[1], sph[2], sph[3], sph[4], sph[5],
+ sphu[0], sphu[1], sphu[2], sphu[3], sphu[4], sphu[5],
+ rr_a, rr_b);
+ pr_info("vgsp-store: %s h=0x%x mme_esr_info=%08x(pcv=%u pc=%u mac=0x%x) ex=%08x ir=%08x,%08x,%08x fed=%08x,%08x,%08x,%08x fex=%08x,%08x,%08x,%08x\n",
+ tag, h_object, mminfo, (mminfo >> 28) & 1u, mminfo & 0xfffu,
+ (mminfo >> 12) & 0xffu, mmex, mm2, mm3, mm4,
+ fed[0], fed[1], fed[2], fed[3],
+ fex[0], fex[1], fex[2], fex[3]);
+ /*
+ * MME shadow-RAM readback for the draw-relevant C597 methods (tracked
+ * methods show the last value the MME saw; zeros = never tracked).
+ * Also probe the MME setup pair and the draw trigger/vertex-start.
+ */
+ pr_info("vgsp-store: %s h=0x%x shadow[c597] 0x114=%08x 0x11c=%08x 0x124=%08x 0x260=%08x 0x840=%08x 0xd74=%08x 0xd78=%08x 0x1618=%08x\n",
+ tag, h_object,
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x114u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x11cu),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x124u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x260u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x840u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0xd74u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0xd78u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x1618u));
+ /*
+ * SESSION 153-CONTINUED-83 probe 2: the CALL trigger methods the
+ * window uses (are the calls even reaching the MME?), a repeat read
+ * (does a read clear?), and the dword-address interpretation of the
+ * draw/RT methods (0xd74>>2, 0x840>>2) to pin the index units.
+ */
+ pr_info("vgsp-store: %s h=0x%x shadow2[c597] 0x3800=%08x 0x3810=%08x 0x3910=%08x 0x3938=%08x 0x3940=%08x 0x840r=%08x 0xd74dw=%08x 0x840dw=%08x 0x1618r=%08x\n",
+ tag, h_object,
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x3800u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x3810u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x3910u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x3938u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x3940u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x840u),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0xd74u >> 2),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x840u >> 2),
+ gsp_phys_mme_shadow_read(bar0, 0xc597u, 0x1618u >> 2));
+}
+
static bool gsp_phys_mme_fast_sample(void __iomem *bar0, const char *tag,
u32 h_object)
{
static u32 fast_boot_n;
u32 ms = READ_ONCE(gsp_phys_mme_fast_ms);
u32 esr_p, ds_p, gf_p, tr_p, n = 0;
+ u32 fed0_p, fed1_p;
ktime_t t_end;
bool poisoned = false;
@@ -5126,16 +5238,28 @@ static bool gsp_phys_mme_fast_sample(void __iomem *bar0, const char *tag,
ds_p = ioread32(bar0 + 0x00405840u);
gf_p = ioread32(bar0 + 0x00400504u);
tr_p = ioread32(bar0 + 0x00400704u);
- pr_info("vgsp-gr: MME-FAST begin %s h=0x%x esr=%08x ds=%08x grfifo=%08x trap=%08x (spin %u ms)\n",
- tag, h_object, esr_p, ds_p, gf_p, tr_p, ms);
+ /*
+ * SESSION 153-CONTINUED-84: FE_DECODE_STATE_EX(0)/(1) also drive a
+ * transition log - the MME's macro/generated witness can flip
+ * there without touching ESR/DS/grfifo, which is exactly the
+ * "did the MME start a macro after this doorbell" question the
+ * reloaded windows keep failing.
+ */
+ fed0_p = ioread32(bar0 + 0x0040a630u);
+ fed1_p = ioread32(bar0 + 0x0040a634u);
+ pr_info("vgsp-gr: MME-FAST begin %s h=0x%x esr=%08x ds=%08x grfifo=%08x trap=%08x fe0=%08x fe1=%08x (spin %u ms)\n",
+ tag, h_object, esr_p, ds_p, gf_p, tr_p, fed0_p, fed1_p, ms);
t_end = ktime_get() + (s64)ms * 1000000ll;
for (;;) {
u32 esr = ioread32(bar0 + 0x00404490u);
u32 ds = ioread32(bar0 + 0x00405840u);
u32 gf = ioread32(bar0 + 0x00400504u);
u32 tr = ioread32(bar0 + 0x00400704u);
+ u32 fed0 = ioread32(bar0 + 0x0040a630u);
+ u32 fed1 = ioread32(bar0 + 0x0040a634u);
- if (esr != esr_p || ds != ds_p || gf != gf_p || tr != tr_p) {
+ if (esr != esr_p || ds != ds_p || gf != gf_p || tr != tr_p ||
+ fed0 != fed0_p || fed1 != fed1_p) {
if (n < 12u) {
u32 info = ioread32(bar0 + 0x00404494u);
u32 info2 = ioread32(bar0 + 0x00404498u);
@@ -5154,7 +5278,7 @@ static bool gsp_phys_mme_fast_sample(void __iomem *bar0, const char *tag,
* the launch; slot 1 is the second
* lane of the same record set.
*/
- pr_warn("vgsp-gr: MME-FAST %s h=0x%x esr %08x->%08x info=%08x(pc_valid=%u pc=%u mac=0x%x) info2_ir=%08x ds %08x->%08x grfifo %08x->%08x trap=%08x data=%08x fed=%08x,%08x (pc0=%u mac0=0x%x gen0=%u)\n",
+ pr_warn("vgsp-gr: MME-FAST %s h=0x%x esr %08x->%08x info=%08x(pc_valid=%u pc=%u mac=0x%x) info2_ir=%08x ds %08x->%08x grfifo %08x->%08x trap=%08x data=%08x fed=%08x,%08x (pc0=%u mac0=0x%x gen0=%u) fe1=%08x (pc1=%u mac1=0x%x gen1=%u)\n",
tag, h_object, esr_p, esr,
info, (info >> 28) & 1u,
info & 0xfffu,
@@ -5163,13 +5287,19 @@ static bool gsp_phys_mme_fast_sample(void __iomem *bar0, const char *tag,
mmedata, mmeir, mmeir1,
mmeir & 0xfffu,
(mmeir >> 12) & 0x3ffu,
- (mmeir >> 31) & 1u);
+ (mmeir >> 31) & 1u,
+ fed1,
+ fed1 & 0xfffu,
+ (fed1 >> 12) & 0x3ffu,
+ (fed1 >> 31) & 1u);
}
n++;
esr_p = esr;
ds_p = ds;
gf_p = gf;
tr_p = tr;
+ fed0_p = fed0;
+ fed1_p = fed1;
/*
* The priv-error poison is the terminal state: the
* FECS has already logged and the rest of the
@@ -5187,6 +5317,16 @@ static bool gsp_phys_mme_fast_sample(void __iomem *bar0, const char *tag,
pr_info("vgsp-gr: MME-FAST end %s h=0x%x events=%u%s\n",
tag, h_object, n, poisoned ? " (poison)" : "");
/*
+ * SESSION 153-CONTINUED-83 store-side emission forensics: right after
+ * the launch's retire spin, snapshot the DS shader-program-header
+ * latches (which pipeline stages failed SPH validation), the MME
+ * execution state (did the MME run a macro, and where is its PC), and
+ * the FE decode-state EX slots (did any method decode carry the MME
+ * GENERATED bit). Bounded per boot (beef-family only).
+ */
+ if ((h_object & 0xfffff000u) == 0xbeef0000u)
+ gsp_phys_store_side_log(bar0, tag, h_object);
+ /*
* SESSION 153-CONTINUED-72: return the poison observation to the
* launch site. The r1149 race: the poison flooded during this spin
* and the FECS cleared it before the poll loop's WEDGE-RECOVER check
@@ -5328,6 +5468,49 @@ static void gsp_phys_pbdma_fault_dump(u32 chid)
}
/*
+ * SESSION 153-CONTINUED-86: the fault-time register witness. The relayed
+ * errString names the fault; these reads add the machine state behind it at
+ * the earliest host observation point (the 0x1006 consume), before the
+ * FECS/GSP-RM clear the latches: MME_HWW_ESR (0x404490) + INFO (0x404494:
+ * PC_VALID/PC/MACRO_ADDRESS) + INFO2 (IR word), DS_HWW_ESR (0x405840) +
+ * ESR_2 (0x405848) + the SPH_INFO stage starts (0x405c00+si*4) that name the
+ * exact VA each failing pipeline stage fetched its shader program header
+ * from, the SPH program-region pair (0x405c1c/c20), and the trapped method
+ * address/data (0x400704/8). GR-class faults only (0xd graphics exception,
+ * 0x45 class error; PBDMA 0x20 has its own dump), bounded per boot so a fault
+ * storm cannot flood dmesg.
+ */
+static void gsp_phys_fault_regs_log(u32 except_type)
+{
+ static u32 frl_n;
+ void __iomem *bar0 = gsp_phys_gpu_bar0();
+ u32 esr, info, info2, ds, ds2, trap_a, trap_d, rr_a, rr_b, sph[6], si;
+
+ if (!bar0 || frl_n >= 48u)
+ return;
+ if (except_type != 0xdu && except_type != 0x45u)
+ return;
+ frl_n++;
+ esr = ioread32(bar0 + 0x00404490u);
+ info = ioread32(bar0 + 0x00404494u);
+ info2 = ioread32(bar0 + 0x00404498u);
+ ds = ioread32(bar0 + 0x00405840u);
+ ds2 = ioread32(bar0 + 0x00405848u);
+ for (si = 0; si < 6u; si++)
+ sph[si] = ioread32(bar0 + 0x00405c00u + si * 4u);
+ rr_a = ioread32(bar0 + 0x00405c1cu);
+ rr_b = ioread32(bar0 + 0x00405c20u);
+ trap_a = ioread32(bar0 + 0x00400704u);
+ trap_d = ioread32(bar0 + 0x00400708u);
+ pr_info("vgsp-fault: regs except=0x%x mme_esr=%08x(pcv=%u ill=%u miss=%u extra=%u) mme_info=%08x(pc=%u mac=0x%x) mme_ir=%08x ds=%08x/%08x sph=%08x,%08x,%08x,%08x,%08x,%08x rr=%08x,%08x trap=%08x data=%08x\n",
+ except_type, esr, (info >> 28) & 1u, (esr >> 2) & 1u,
+ esr & 1u, (esr >> 1) & 1u, info, info & 0xfffu,
+ (info >> 12) & 0xffu, info2, ds, ds2,
+ sph[0], sph[1], sph[2], sph[3], sph[4], sph[5],
+ rr_a, rr_b, trap_a, trap_d);
+}
+
+/*
* Note every consumed 0x1006 in the bounded last-4 ring, advance the fault
* epoch (the relay submit snapshot compares against it) and log the designed
* observation line when the fault relay is armed. Decoding and counting are
@@ -5343,10 +5526,12 @@ static void gsp_phys_fault_observe(const struct gsp_phys_rx_record *rec,
GSP_PHYS_FAULT_NOTE_MAX;
gsp_phys_fault_seen++;
gsp_phys_fault_epoch++;
- if (gsp_phys_fault_relay_enabled())
+ if (gsp_phys_fault_relay_enabled()) {
pr_info_ratelimited("vgsp-fault: func=0x1006 seq=%u exceptType=0x%x runlistId=%u chid=%u prevXid=0x%x errString=\"%s\"\n",
rec->seq_num, out->except_type, out->runlist_id,
out->chid, out->prev_xid, out->err);
+ gsp_phys_fault_regs_log(out->except_type);
+ }
if (out->except_type == 0x20u)
gsp_phys_pbdma_fault_dump(out->chid);
}
@@ -17577,6 +17762,217 @@ MODULE_PARM_DESC(sema_gl_mme_keep_mask,
"Slots whose CALL_MME_MACRO/DATA runs are never dropped/stubbed (u64 bitmask; one slot at a time for macro re-enable)");
/*
+ * SESSION 153-CONTINUED-79: per-launch method-block word cap + prelude drain.
+ * The r1172-r1175 A/B: every beef launch whose emitted block exceeded ~2000
+ * words (3083-word full-SA prelude + window, 3315-3484 words) NEVER STARTED
+ * on the physical engine (MME-FAST events=0, grfifo frozen, ch_rel never
+ * written) while every block <= ~1300 words (1002-word partial capture +
+ * window) executed (retires in 90-170 us when the executed macro stayed
+ * inside the loaded IR). The failure class is fetch-side (a stuck GPFIFO
+ * entry), not content: once parked, every later entry also failed until a
+ * poison-latch TSG re-arm reset the PBDMA. The full-SA capture (55 slots,
+ * 3083 words, macros that chain past the first 15 slots e.g. macro 0 ->
+ * IR 0x201 = slot-25 entry) therefore cannot be replayed in ONE block.
+ * Emit at most gsp_phys_rmcall_gr_block_cap words per launch and drain the
+ * (SDM-sanitized, run-aligned) prelude across consecutive launches with a
+ * generation-keyed cursor; the emission latch is set only when the final
+ * part has been emitted, so a completed program is re-requested after the
+ * WEDGE-RECOVER latch clear. Default 2048; 0 = legacy 4096 module buffer.
+ */
+static u32 gsp_phys_rmcall_gr_block_cap = 2048u;
+module_param_named(sema_gl_mme_block_cap, gsp_phys_rmcall_gr_block_cap, uint,
+ 0644);
+MODULE_PARM_DESC(sema_gl_mme_block_cap,
+ "Max words emitted per GR launch method block (0 = 4096 module buffer; the >2048-word blocks never start on the physical engine)");
+
+/* Generation-keyed drain cursor for the split MME prelude emission. */
+static u32 gsp_phys_gr_mme_pre_emit_gen;
+static u32 gsp_phys_gr_mme_pre_emit_h;
+static u32 gsp_phys_gr_mme_pre_emit_off;
+
+/*
+ * SESSION 153-CONTINUED-79: parameter-conserving NOP for kept parking slots.
+ * The r1177 bisect: keep={0,2,57} (full 3089-word program resident, split
+ * emission) parks the PBDMA on the emitted block (MME-FAST events=0, grfifo
+ * frozen, warm sem WEDGED) while keep={0,2} retires every launch - slot 57's
+ * CALL_MME_MACRO(57) run is the r1142 park class. Rather than drop the run
+ * (which also removes its parameter words from the stream), rewrite the call
+ * run's header to target the CALL_MME_DATA port (method+4) so the parameter
+ * words are still written (conserved) but no macro executes: the trigger
+ * word becomes a plain port write and the PBDMA never opens a macro context
+ * whose parameter drain can back up. A slot named in this mask must also be
+ * in sema_gl_mme_keep_mask (kept = retained in the window). Default 0.
+ */
+static u64 gsp_phys_rmcall_gr_nop_param_mask;
+module_param_named(sema_gl_mme_nop_param_mask, gsp_phys_rmcall_gr_nop_param_mask,
+ ullong, 0644);
+MODULE_PARM_DESC(sema_gl_mme_nop_param_mask,
+ "Kept slots whose CALL_MME_MACRO trigger is rewritten to a plain CALL_MME_DATA port write (params conserved, macro not executed; u64 bitmask)");
+
+/*
+ * SESSION 153-CONTINUED-80: the r1181 live A/B validated the retarget above
+ * and it PARKS the PBDMA exactly like the raw kept call - with
+ * keep=5|1<<57 + nop=1<<57 the first post-change launch froze
+ * (MME-FAST begin grfifo=0x00000001, end events=0, no poison, warm sem
+ * WEDGED) while keep=5 (slot 57 call+params elided) retired in 94-106 us
+ * on the same boot (r1176/r1181 baseline). The bare DATA-port run without an
+ * open macro context is not a safe landing for the trigger. With this knob
+ * set, a nop-mask slot joins the drop path instead (the r1144-r1177
+ * proven-safe behavior: whole run elided, retires clean, params NOT
+ * conserved). Use for the all-real-macro A/B: sa_stub_all=0 leaves
+ * 34/39/40/48/51 real while this drops only 57. Default 0 preserves the
+ * retarget behavior so the r1179 transform evidence stays reproducible.
+ */
+static u64 gsp_phys_rmcall_gr_nop_drop;
+module_param_named(sema_gl_mme_nop_drop, gsp_phys_rmcall_gr_nop_drop, ullong,
+ 0644);
+MODULE_PARM_DESC(sema_gl_mme_nop_drop,
+ "Slots whose call runs are dropped entirely (proven-safe retire, SA[slot] stubbed) instead of retargeted to the CALL_MME_DATA port (u64 bitmask; default 0)");
+
+/*
+ * SESSION 153-CONTINUED-82: hold real CALL runs until the prelude latch.
+ * The r1220 (all-real, nop_drop=1<<57, sa_stub_all=N) wedge: the window's
+ * real CALL runs executed in the SAME launch that emitted only the first
+ * PART of a fresh capture generation (`MME-PRELUDE part gen=3 off=1776
+ * words=1776/3083 ... (continuation next launch)`), so the called macros
+ * fetched IR code that was not resident yet - `MME-FAST launch ...
+ * pc_valid=1 pc=513`, priv-error poison, `Shader Program Header 18 Error`,
+ * `Class Error ... Offset 0000238c, Data 00020020` (the r1116 lane-poison
+ * class) and the boot wedged. The prelude latch (gsp_phys_gr_mme_prelude_*
+ * gen/h) is set only when the FINAL part has gone out; with this knob the
+ * launch builder holds (elides, the proven-safe drop shape) every remaining
+ * CALL_MME_MACRO/DATA run of the emitted window while EITHER the latch for
+ * this generation+channel is not set after this launch, OR this launch
+ * uploaded generation code for the first time (latch invalid before the
+ * emission). The r1222 all-real boot proved the second clause necessary:
+ * the drain-completing launch (part-2 IR uploads + window real calls in the
+ * SAME block, latch set by its own emission) still hit ILLEGAL_OPCODE -
+ * real calls resume on the next launch, when only the idempotent keep-forced
+ * re-emission of the already-resident prefix is in flight. Default 1 (the
+ * fix); A/B with 0 to reproduce the r1209/r1220/r1222 wedge.
+ */
+static bool gsp_phys_rmcall_gr_mme_hold_calls = true;
+module_param_named(sema_gl_mme_hold_calls, gsp_phys_rmcall_gr_mme_hold_calls,
+ bool, 0644);
+MODULE_PARM_DESC(sema_gl_mme_hold_calls,
+ "Hold (elide) window CALL_MME_MACRO/DATA runs until the MME prelude latches for the current generation+channel (default 1; 0 = legacy, real calls run against a partial prelude)");
+
+/*
+ * SESSION 153-CONTINUED-84: emit NVC597_SET_MME_VERSION=1 (0x234) with the
+ * prelude step. The channel-init defaults the client pushes once per channel
+ * (Turing+) are never replayed, so the replayed context's MME stays in its
+ * reset/disabled state and every CALL_MME_MACRO run parks the front-end
+ * waiting for a macro that cannot start (r1245: call-bearing launches hang
+ * with all MME registers idle). Default 1; A/B with 0.
+ */
+static bool gsp_phys_rmcall_gr_mme_version = true;
+module_param_named(sema_gl_mme_version, gsp_phys_rmcall_gr_mme_version,
+ bool, 0644);
+MODULE_PARM_DESC(sema_gl_mme_version,
+ "Emit SET_MME_VERSION=1 ahead of the replayed MME prelude (default 1; the Turing channel-init method the guest pushes once and the capture never carries)");
+
+/*
+ * SESSION 153-CONTINUED-86: the missing SPH (shader-program-header) state.
+ *
+ * The executed beef launches fault `Graphics Exception: Shader Program
+ * Header 9/11/18 Error` + `DS_HWW_ESR 0x405840=0xa2040a00` (r1272): the DS
+ * unit validates the header of every pipeline program bound when a draw
+ * issues, including the SPH VERSION against the channel's programmed
+ * window. The Turing channel-init (the 3D driver's own
+ * _nv3dInitChannelTuring) writes the SPH version pair ONCE per channel:
+ *
+ * INC run @0x16a4, count 2:
+ * METHOD 0x16a4 (SET_SPH_VERSION) <- 4 | (4 << 16) = 0x00040004
+ * METHOD 0x16a8 (CHECK_SPH_VERSION) <- 0x00040004
+ *
+ * (fields: CURRENT 15:0, OLDEST_SUPPORTED 31:16; the value 4 is the SPH V4
+ * the Turing shader images carry). The replayed context never receives it:
+ * the capture families (MME prelude 0x114..0x124/0x234/0x550..0x568/
+ * 0x3400..0x34fc, attr prelude 0x1100..0x11fc/0x2000..0x205c) do not cover
+ * 0x16a4, and the captured 3D windows contain no such write (verified in the
+ * emitted-window decode and the raw guest-RAM stream). With the register
+ * unset (0/0) every fetched SPH fails its version window -> per-SPH error
+ * bits -> 0 GR-STATE retires and no stores. The client's own emitted init
+ * pair was located verbatim in the guest pushbuffer image (guest PA
+ * 0x8672d5b4: `200205a9 00040004 00040004`), so the launch builder emits
+ * exactly that run ahead of the window (right after SET_OBJECT, beside the
+ * MME version write). 3 words, idempotent; emitted on every replayed 3D
+ * window. Default 1; A/B with 0 reproduces the SPH fault.
+ */
+static bool gsp_phys_rmcall_gr_sph_version = true;
+module_param_named(sema_gl_sph_version, gsp_phys_rmcall_gr_sph_version,
+ bool, 0644);
+MODULE_PARM_DESC(sema_gl_sph_version,
+ "Emit the Turing channel-init SET_SPH_VERSION/CHECK_SPH_VERSION pair (0x16a4/0x16a8 <- 0x00040004) ahead of each replayed 3D window (default 1; A/B with 0 reproduces the Shader Program Header 9/11/18 fault)");
+
+/*
+ * SESSION 153-CONTINUED-87: the missing TESS_CONTROL/PIXEL program binds.
+ * The client's real channel state binds every pipeline stage once per
+ * context; the captured init-push image (guest RAM scrape) carries
+ * `20020825 [0, 04400200]` (j=2 TESS_CONTROL) and
+ * `20020855 [0, 04400000]` (j=5 PIXEL) beside the SPH version pair -
+ * but the captured per-frame window carries only the j=1 (VTG) pair
+ * (0x4400900/0x4400300). The DS validates the SPH of every pipeline
+ * stage the draw activates, so the unbound stages latch their header
+ * errors (DS_HWW_ESR 0xa2040a00 = SPH9|SPH11|SPH18) while the V3 blob
+ * passes wherever a stage IS bound. Emit both binds ahead of the
+ * window at the window's own program-family page (all four VAs share
+ * one 4K page: 0x4400000 j=5, 0x4400200 j=2, 0x4400300/0x4400900 j=1),
+ * pushed through the same relocation pass as the window - a MISS skips
+ * the bind entirely (never a raw client VA). Beef client channels only
+ * (the family layout is the client's). Default 1; A/B with 0.
+ */
+static bool gsp_phys_rmcall_gr_sph_bind = true;
+module_param_named(sema_gl_sph_bind, gsp_phys_rmcall_gr_sph_bind, bool, 0644);
+MODULE_PARM_DESC(sema_gl_sph_bind,
+ "Emit the client's missing TESS_CONTROL (j=2) and PIXEL (j=5) pipeline program binds ahead of replayed 3D windows (program-family page of the window's prog VA; default 1)");
+
+bool gsp_phys_rmcall_gr_sph_bind_value(void)
+{
+ return READ_ONCE(gsp_phys_rmcall_gr_sph_bind);
+}
+
+/*
+ * SESSION 153-CONTINUED-84: SET_MME_SHADOW_RAM_CONTROL mode before the
+ * window. The vendor comment (mesh shader bind path) is explicit:
+ * "MME filtering swallows them all if old and new state values are the
+ * same" - METHOD_TRACK_WITH_FILTER (1) suppresses every method write whose
+ * value equals the shadowed one. Our replay repeats the same window every
+ * launch, so after the first replay every redundant write - including the
+ * CALL_MME_MACRO/CALL_MME_DATA parameter writes - matches the shadow and
+ * is swallowed; the macro call never reaches the MME (r1236-r1250: calls
+ * in the emitted window, all MME registers idle, some launches park the
+ * front-end). The mode-bearing writes live in the captured PRELUDE
+ * (0x124 x4 in the client's setup push), which the win_patch window sweep
+ * does not touch; emit METHOD_TRACK (0 = filtering OFF, semantics
+ * unchanged: the write simply applies) right before the SA re-arm and the
+ * window. Value = sema_gl_mme_track (0 = TRACK; 1/2/3 = other modes; -1 =
+ * do not emit).
+ */
+static int gsp_phys_rmcall_gr_mme_track;
+module_param_named(sema_gl_mme_track, gsp_phys_rmcall_gr_mme_track,
+ int, 0644);
+MODULE_PARM_DESC(sema_gl_mme_track,
+ "SET_MME_SHADOW_RAM_CONTROL mode emitted before each replayed window (0=TRACK filtering off (default); 1=TRACK_WITH_FILTER; 2=PASSTHROUGH; 3=REPLAY; -1=off)");
+
+/*
+ * SESSION 153-CONTINUED-85: A/B knob for the SA re-arm (the r1237 fix).
+ * Before CONT-84 the calls in the replayed window resolved to the SA state
+ * the prelude left behind (its last captured SA write re-clobbers the slot;
+ * r1231: SA[34]=0 lands last, every draw call resolved to macro 0 and
+ * emitted nothing - and the r1228-phase launches retired). With the re-arm
+ * the calls resolve to the captured IR entries; every call-bearing launch
+ * since (r1245/r1250/r1265/r1270) parks or poisons the front-end. This knob
+ * restores the pre-arm resolution in one boot so the two states can be A/B'd
+ * without a rebuild (0 = do not re-point; default 1 = re-arm).
+ */
+static bool gsp_phys_rmcall_gr_mme_sa_rearm = true;
+module_param_named(sema_gl_mme_sa_rearm, gsp_phys_rmcall_gr_mme_sa_rearm,
+ bool, 0644);
+MODULE_PARM_DESC(sema_gl_mme_sa_rearm,
+ "Re-emit LOAD_MME_START_ADDRESS_RAM pairs before each replayed window so CALL_MME_MACRO resolves to the captured SA entries (default 1; 0 = pre-arm resolution, A/B)");
+
+/*
* The same stubbed-slot predicate the launch builder applies (see the
* sa_stub emit decision): a slot is stubbed when stubbing is on and either
* the all-slots knob is set, the capture holds no SA entry, or the IR
@@ -17587,6 +17983,16 @@ static bool gsp_phys_gr_slot_stubbed(u32 j, u64 sa_mask, const u32 *sa_ent)
{
if (j >= 64u || !gsp_sema_gr_sa_stub_value())
return false;
+ /*
+ * SESSION 153-CONTINUED-80: a nop-drop slot is force-stubbed even
+ * when it is also kept or covered - the r1186 evidence shows a kept
+ * slot 57 loses its SA end-noop stub (the stub builder skips kept
+ * slots) and any call-run encoding the drop filter misses then
+ * executes the real macro 57 (the park class). Dropping the runs and
+ * stubbing SA[57] together is the safe combination.
+ */
+ if (READ_ONCE(gsp_phys_rmcall_gr_nop_drop) & (1ull << j))
+ return true;
if (READ_ONCE(gsp_phys_rmcall_gr_keep_mask) & (1ull << j))
return false;
if (READ_ONCE(gsp_phys_rmcall_gr_sa_stub_all))
@@ -17607,7 +18013,7 @@ static bool gsp_phys_gr_slot_stubbed(u32 j, u64 sa_mask, const u32 *sa_ent)
static u32 gsp_phys_gr_mme_drop_calls(u32 *win, u32 n, u64 sa_mask,
const u32 *sa_ent, u64 *dropped_mask)
{
- u32 r = 0, w = 0, dropped = 0;
+ u32 r = 0, w = 0, dropped = 0, transform = 0;
u64 dmask = 0;
while (r < n) {
@@ -17633,8 +18039,39 @@ static u32 gsp_phys_gr_mme_drop_calls(u32 *win, u32 n, u64 sa_mask,
if (m >= 0x3800u && m <= 0x39fcu) {
u32 j = (m - 0x3800u) / 8u;
- if (gsp_phys_gr_slot_stubbed(j, sa_mask,
- sa_ent)) {
+ if ((READ_ONCE(gsp_phys_rmcall_gr_keep_mask) &
+ (1ull << j)) &&
+ (READ_ONCE(gsp_phys_rmcall_gr_nop_param_mask) &
+ (1ull << j))) {
+ if (READ_ONCE(gsp_phys_rmcall_gr_nop_drop)) {
+ /*
+ * SESSION 153-CONTINUED-80: the
+ * retarget parks the PBDMA
+ * live (see the
+ * sema_gl_mme_nop_drop
+ * comment) - drop the whole
+ * run instead.
+ */
+ iscall = true;
+ dmask |= 1ull << j;
+ break;
+ }
+ /*
+ * SESSION 153-CONTINUED-79: kept +
+ * nop-param slot - keep the run but
+ * retarget its header to the DATA
+ * port so the params still land
+ * (see the sema_gl_mme_nop_param_mask
+ * comment).
+ */
+ u32 nm = 0x3804u + 8u * j;
+
+ win[r] = (h & ~0xfffu) |
+ ((nm >> 2) & 0xfffu);
+ dmask |= 1ull << j;
+ transform++;
+ } else if (gsp_phys_gr_slot_stubbed(j, sa_mask,
+ sa_ent)) {
iscall = true;
dmask |= 1ull << j;
break;
@@ -17651,6 +18088,9 @@ static u32 gsp_phys_gr_mme_drop_calls(u32 *win, u32 n, u64 sa_mask,
w += span;
r += span;
}
+ if (transform)
+ pr_info("vgsp-gr: MME-CALL-NOP %u call run(s) retargeted to the CALL_MME_DATA port (params conserved)\n",
+ transform);
if (dropped_mask)
*dropped_mask = dmask;
return dropped;
@@ -17812,7 +18252,7 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
* off for this mode).
*/
if (gsp_phys_rmcall_gr_3d && req && win3d && win3d_n &&
- win3d_n + 2u + 8u <= GSP_PHYS_RMCALL_GR_METHOD_WORDS) {
+ win3d_n + 2u + 3u + 8u <= GSP_PHYS_RMCALL_GR_METHOD_WORDS) {
/*
* SESSION 153-CONTINUED-53: MME context-setup prelude (see the
* sema_gl_mme_prelude knob above). Preplay the captured client
@@ -17826,8 +18266,27 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
* emission then happens on the next launch that fits.
*/
bool emit_prelude = false;
+ bool pre_done = false;
+ bool mme_hold_calls = false;
+ bool latch_before = false;
+ u32 pre_words_out = 0;
const u32 *pl = NULL;
- u32 pl_n = 0, pl_gen = 0;
+ u32 pl_n = 0, pl_gen = 0, pl_room = 0;
+ u32 block_cap = READ_ONCE(gsp_phys_rmcall_gr_block_cap);
+
+ if (block_cap < 64u ||
+ block_cap > GSP_PHYS_RMCALL_GR_METHOD_WORDS)
+ block_cap = GSP_PHYS_RMCALL_GR_METHOD_WORDS;
+ /*
+ * SESSION 153-CONTINUED-86: harden the block cap against the
+ * emissions the block-cap arithmetic below does not fully
+ * count (the SA re-arm, 3 words per slot, up to 32 slots -
+ * see the MME-SA-REARM site). Clamping the cap keeps every
+ * combination inside the 4096-word method buffer; with the
+ * default 2048 cap this changes nothing.
+ */
+ if (block_cap > GSP_PHYS_RMCALL_GR_METHOD_WORDS - 128u)
+ block_cap = GSP_PHYS_RMCALL_GR_METHOD_WORDS - 128u;
/*
* SESSION 153-CONTINUED-63: MME START-ADDRESS RAM completion.
* The prelude capture is capped at 512 words, so the client's
@@ -17931,7 +18390,9 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
*/
if (sa_mask & (1ull << j)) {
if (!READ_ONCE(gsp_phys_rmcall_gr_sa_stub_all) &&
- gsp_sema_mme_ir_covered(sa_ent[j])) {
+ gsp_sema_mme_ir_covered(sa_ent[j]) &&
+ !(READ_ONCE(gsp_phys_rmcall_gr_nop_drop) &
+ (1ull << j))) {
for (t = 0; t < sa_ncov; t++)
if (sa_cov[t] == j)
have = true;
@@ -18025,18 +18486,81 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
* priv-errored (ILLEGAL_OPCODE -> poison). Force
* re-emission on every launch while any slot is kept
* (idempotent per the emission contract).
+ *
+ * SESSION 153-CONTINUED-79: the emission is drained in
+ * per-launch parts capped at block_cap words (see the
+ * sema_gl_mme_block_cap comment; oversized blocks never
+ * start). The latch (gsp_phys_gr_mme_prelude_gen/h) is
+ * set only when the final part has gone out.
*/
- if (pl && pl_n && pl_gen &&
- (READ_ONCE(gsp_phys_rmcall_gr_keep_mask) ||
- pl_gen != READ_ONCE(gsp_phys_gr_mme_prelude_gen) ||
- req->h_object != READ_ONCE(gsp_phys_gr_mme_prelude_h))) {
- if (win3d_n + 2u + 8u + pl_n + sa_block <=
- GSP_PHYS_RMCALL_GR_METHOD_WORDS) {
- emit_prelude = true;
+ if (pl && pl_n && pl_gen) {
+ /*
+ * Fixed overhead: SET_OBJECT bind (2), SA-stub
+ * block, window, and the 4x2-word release tail.
+ */
+ u32 fixed = 2u + sa_block + win3d_n + 8u;
+
+ /*
+ * SESSION 153-CONTINUED-82: latch state BEFORE
+ * this launch's emission (see the hold decision
+ * below). A launch that first uploads a
+ * generation's MME code must not run real CALL
+ * runs in the same block (the r1222 evidence:
+ * the drain-completing launch bundled part-2's
+ * IR uploads with the window's real calls and
+ * hit ILLEGAL_OPCODE even though the latch was
+ * set by its own emission step).
+ */
+ latch_before =
+ (pl_gen == READ_ONCE(
+ gsp_phys_gr_mme_prelude_gen) &&
+ req->h_object == READ_ONCE(
+ gsp_phys_gr_mme_prelude_h));
+
+ pl_room = (fixed + 24u <= block_cap) ?
+ block_cap - fixed : 0u;
+ if (pl_room > pl_n)
+ pl_room = pl_n;
+ /*
+ * (Re)start the drain on a new generation/
+ * channel, or when the request is forced (kept
+ * slots; a context-wiping wedge recovery clears
+ * the latch) and nothing is left to drain.
+ */
+ if (pl_gen !=
+ READ_ONCE(gsp_phys_gr_mme_pre_emit_gen) ||
+ req->h_object !=
+ READ_ONCE(gsp_phys_gr_mme_pre_emit_h) ||
+ (READ_ONCE(gsp_phys_gr_mme_pre_emit_off) >=
+ pl_n &&
+ (READ_ONCE(gsp_phys_rmcall_gr_keep_mask) ||
+ pl_gen != READ_ONCE(
+ gsp_phys_gr_mme_prelude_gen)))) {
+ WRITE_ONCE(gsp_phys_gr_mme_pre_emit_gen,
+ pl_gen);
+ WRITE_ONCE(gsp_phys_gr_mme_pre_emit_h,
+ req->h_object);
+ WRITE_ONCE(gsp_phys_gr_mme_pre_emit_off, 0);
+ }
+ if (pl_room >= 24u) {
+ emit_prelude =
+ (pl_gen != READ_ONCE(
+ gsp_phys_gr_mme_prelude_gen) ||
+ req->h_object != READ_ONCE(
+ gsp_phys_gr_mme_prelude_h) ||
+ READ_ONCE(
+ gsp_phys_gr_mme_pre_emit_off) <
+ pl_n);
} else {
- pr_info("vgsp-gr: MME-PRELUDE deferred (window=%u prelude=%u sa=%u exceeds block; replaying window alone) h=0x%x\n",
- win3d_n, pl_n, sa_block,
- req->h_object);
+ static u32 pre_defer_log;
+
+ if (pre_defer_log < 8) {
+ pre_defer_log++;
+ pr_info("vgsp-gr: MME-PRELUDE deferred (window=%u prelude=%u sa=%u cap=%u) h=0x%x\n",
+ win3d_n, pl_n, sa_block,
+ block_cap,
+ req->h_object);
+ }
}
}
}
@@ -18045,53 +18569,118 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
(0u << 13) |
(GSP_PHYS_RMCALL_GR_SET_OBJECT >> 2);
*p++ = GSP_PHYS_RMCALL_CLASS_GR_3D;
+ /*
+ * SESSION 153-CONTINUED-86: the Turing channel-init SPH version
+ * pair (see the sema_gl_sph_version knob). Emitted on EVERY
+ * replayed 3D window (3 words, idempotent): the DS validates
+ * each bound program's SPH version against this window at draw
+ * time, and the replay context never received the client's
+ * once-per-channel write. Emitted before the prelude-room
+ * computations below so the live (p - pb) accounting covers
+ * it exactly.
+ */
+ if (READ_ONCE(gsp_phys_rmcall_gr_sph_version)) {
+ /*
+ * SESSION 153-CONTINUED-87: the window's accepted
+ * version range must COVER the images the engine
+ * will fetch. The client's own init push writes
+ * 4|(4<<16), but our authored (V3) images are
+ * rejected by a 4/4 window (SPH9|11|18) while the
+ * r950-era acceptance - same V3 image - ran with
+ * NO version window. Emit oldest=V(blob),
+ * current=max(V(blob),4) so both the V3 image and
+ * a future V4 re-tag stay inside the range.
+ */
+ u32 bv = (u32)gsp_sema_gl_sph_blob_version();
+ u32 val = 0x00040004u;
+
+ if (bv >= 1u && bv <= 15u)
+ val = ((bv < 4u ? 4u : bv) << 16) | bv;
+
+ *p++ = ((u32)GSP_PHYS_RMCALL_METHOD_IMM << 29) |
+ (2u << 16) | (0x16a4u >> 2);
+ *p++ = val;
+ *p++ = val;
+ }
+ /*
+ * SESSION 153-CONTINUED-84: SET_MME_VERSION=1 (0x234).
+ *
+ * The Turing channel-init defaults push
+ * `nvcMethodPusher: NVC597_SET_MME_VERSION <- 1` once per
+ * channel (class c597, TURING_OR_NEWER), and the replayed
+ * context otherwise never sees it: the captured MME setup
+ * families carry IR/SA/shadow-scratch loads but no 0x234 run
+ * (verified in the injected full-SA stream too). With the
+ * version register unset the MME never starts a macro - the
+ * r1245 evidence: call-bearing launches (real {0,2,34,39,40},
+ * full 55-slot prelude, SA re-armed before the window) hang
+ * the front-end (`MME-FAST ... events=0` -> `GR LAUNCH FAILED:
+ * timeout after 2500 ms` with qmd_rel=0/ch_rel=0) while every
+ * MME register stays idle (`pc_valid=0`, `gen0=0`,
+ * `mme_esr=80000000`): the FE waits forever for a macro the
+ * halted MME cannot run. Emitted with the prelude step (the
+ * resident program is (re)loaded right after), ahead of the
+ * window, so the version select has landed before the first
+ * CALL_MME_MACRO run. Knob: sema_gl_mme_version (default 1;
+ * A/B with 0 reproduces the parked launches).
+ */
+ if (emit_prelude && READ_ONCE(gsp_phys_rmcall_gr_mme_version)) {
+ *p++ = ((u32)GSP_PHYS_RMCALL_METHOD_IMM << 29) |
+ (1u << 16) |
+ (0x234u >> 2);
+ *p++ = 1u;
+ }
if (emit_prelude) {
- u32 g0 = pl_gen, g1 = 0, sdrop = 0, pwords = pl_n;
+ u32 g0 = pl_gen, g1 = 0, sdrop = 0, pwords = 0;
+ u32 doff = READ_ONCE(gsp_phys_gr_mme_pre_emit_off);
+ u32 room = (block_cap > (u32)(p - pb) + win3d_n + 8u +
+ sa_block) ?
+ block_cap - (u32)(p - pb) - win3d_n - 8u -
+ sa_block : 0u;
- if (READ_ONCE(gsp_phys_rmcall_gr_sdm_drop)) {
- u32 r = 0, w = 0;
+ /*
+ * SESSION 153-CONTINUED-79: run-aligned drain at the
+ * generation cursor; whole runs only, SDM-family
+ * single-word entries skipped exactly like the legacy
+ * sanitize (see gsp_phys_rmcall_gr_sdm_drop).
+ */
+ while (doff < pl_n && pwords + 1u <= room) {
+ u32 h = pl[doff];
+ u32 op = (h >> 29) & 7u;
+ u32 cnt = (h >> 16) & 0xfffu;
+ u32 span = ((op == 1u || op == 3u ||
+ op == 5u) && cnt &&
+ doff + 1u + cnt <= pl_n) ?
+ 1u + cnt : 1u;
- /*
- * SESSION 153-CONTINUED-59: SDM sanitize (see the
- * gsp_phys_rmcall_gr_sdm_drop comment). Walk the
- * prelude verbatim but drop single-word SDM-family
- * encodings; they are the r1102 PBENTRY offenders.
- */
- while (r < pl_n) {
- u32 h = pl[r];
- u32 op = (h >> 29) & 7u;
- u32 cnt = (h >> 16) & 0xfffu;
- u32 span = ((op == 1u || op == 3u ||
- op == 5u) && cnt &&
- r + 1u + cnt <= pl_n) ?
- 1u + cnt : 1u;
-
- if (h && (op == 0u || op == 2u ||
- op == 6u)) {
- sdrop++;
- r += span;
- continue;
- }
- memcpy(&p[w], &pl[r], (size_t)span * 4u);
- w += span;
- r += span;
+ if (doff + span > pl_n)
+ span = pl_n - doff;
+ if (READ_ONCE(gsp_phys_rmcall_gr_sdm_drop) &&
+ h && (op == 0u || op == 2u || op == 6u)) {
+ sdrop += span;
+ doff += span;
+ continue;
}
- p += w;
- pwords = w;
- } else {
- memcpy(p, pl, (size_t)pl_n * 4);
- p += pl_n;
+ if (pwords + span > room)
+ break;
+ memcpy(p, &pl[doff], (size_t)span * 4u);
+ p += span;
+ pwords += span;
+ doff += span;
}
+ WRITE_ONCE(gsp_phys_gr_mme_pre_emit_off, doff);
+ pre_done = (doff >= pl_n);
+ pre_words_out = pwords;
/* seqcheck: a mid-copy append must not be recorded as
* emitted (the copy may be a torn prefix). */
gsp_sema_mme_prelude_get(NULL, &g1);
- if (g1 == g0) {
+ if (pre_done && g1 == g0) {
WRITE_ONCE(gsp_phys_gr_mme_prelude_gen, g0);
WRITE_ONCE(gsp_phys_gr_mme_prelude_h,
req->h_object);
- pr_info("vgsp-gr: MME-PRELUDE emitted gen=%u words=%u window=%u h=0x%x push=0x%llx sdm_drop=%u\n",
- g0, pwords, win3d_n, req->h_object,
- req->push_va, sdrop);
+ pr_info("vgsp-gr: MME-PRELUDE emitted gen=%u words=%u window=%u h=0x%x push=0x%llx sdm_drop=%u cap=%u\n",
+ g0, pl_n, win3d_n, req->h_object,
+ req->push_va, sdrop, block_cap);
/*
* SESSION 153-CONTINUED-70: one full dump of the
* replayed MME context-setup program per boot
@@ -18128,12 +18717,47 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
sj, sa_ent[sj],
gsp_sema_mme_ir_covered(sa_ent[sj]));
}
+ } else if (pre_done) {
+ pr_info("vgsp-gr: MME-PRELUDE copy raced an append (gen %u->%u); will re-emit next launch\n",
+ g0, g1);
} else {
- pr_info("vgsp-gr: MME-PRELUDE copy raced an append (gen %u->%u); emitted words=%u, will re-emit next launch\n",
- g0, g1, pl_n);
+ pr_info("vgsp-gr: MME-PRELUDE part gen=%u off=%u words=%u/%u window=%u h=0x%x push=0x%llx sdm_drop=%u cap=%u (continuation next launch)\n",
+ g0, doff, pwords, pl_n, win3d_n,
+ req->h_object, req->push_va, sdrop,
+ block_cap);
}
}
/*
+ * SESSION 153-CONTINUED-82: hold real CALL runs until the
+ * prelude latch for this generation+channel is set (see the
+ * sema_gl_mme_hold_calls comment). Evaluated AFTER the
+ * emission step above, so a launch that completed the drain
+ * in this same block has just written the latch. Real calls
+ * stay held when EITHER
+ * - the latch for this generation+channel is not valid after
+ * this launch (partial drain, raced append, or a fresh
+ * generation whose code is not fully resident), or
+ * - this launch uploaded generation code for the FIRST time
+ * (emitted words with the latch invalid before the step) -
+ * the r1222 evidence: the drain-completing launch bundled
+ * part-2's IR uploads with the window's real calls and hit
+ * ILLEGAL_OPCODE even though its own emission set the
+ * latch. A later launch (latch already valid, only the
+ * idempotent keep-forced re-emission of the prefix) runs
+ * the real calls against the now-resident program.
+ */
+ if (pl && pl_n && pl_gen &&
+ READ_ONCE(gsp_phys_rmcall_gr_mme_prelude) &&
+ READ_ONCE(gsp_phys_rmcall_gr_mme_hold_calls)) {
+ u32 lg = READ_ONCE(gsp_phys_gr_mme_prelude_gen);
+ u32 lh = READ_ONCE(gsp_phys_gr_mme_prelude_h);
+ bool latch_after = (pl_gen == lg &&
+ req->h_object == lh);
+
+ mme_hold_calls = !latch_after ||
+ (pre_words_out && !latch_before);
+ }
+ /*
* SESSION 153-CONTINUED-63: emit the SA-RAM completion computed
* above - a one-instruction end-noop stub in the IR plus one
* ONE_INC 0x11c run loading [slot, stub] for every called slot
@@ -18237,10 +18861,20 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
*/
if (attr_pre && attr_pre_n &&
READ_ONCE(gsp_phys_rmcall_gr_attr_prelude)) {
- u32 avail = GSP_PHYS_RMCALL_GR_METHOD_WORDS -
+ u32 avail = block_cap -
(u32)(p - pb) - (win3d_n + 2u + 8u);
u32 off, copied = 0;
+ /*
+ * SESSION 153-CONTINUED-79: the attr prelude shares
+ * the same per-launch block cap (see
+ * sema_gl_mme_block_cap); unsigned underflow would
+ * hand it the whole module buffer on an oversized
+ * window.
+ */
+ if ((u32)(p - pb) + win3d_n + 2u + 8u >= block_cap)
+ avail = 0;
+
if (gsp_phys_gr_attr_emit_gen !=
READ_ONCE(gsp_phys_gr_attr_pre_gen)) {
WRITE_ONCE(gsp_phys_gr_attr_emit_gen,
@@ -18297,8 +18931,132 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
}
}
}
- memcpy(p, win3d, (size_t)win3d_n * 4);
- p += win3d_n;
+ /*
+ * SESSION 153-CONTINUED-84: shadow-RAM mode before the window
+ * (see the sema_gl_mme_track comment). METHOD_TRACK (0) is
+ * emitted by default so the repeated CALL/DATA parameter
+ * writes are not swallowed by the redundant-method filter.
+ */
+ if (READ_ONCE(gsp_phys_rmcall_gr_mme_track) >= 0 &&
+ p + 2u <= pb + GSP_PHYS_RMCALL_GR_METHOD_WORDS) {
+ *p++ = ((u32)GSP_PHYS_RMCALL_METHOD_IMM << 29) |
+ (1u << 16) | (0x124u >> 2);
+ *p++ = (u32)READ_ONCE(gsp_phys_rmcall_gr_mme_track);
+ }
+ /*
+ * SESSION 153-CONTINUED-84: SA re-arm BEFORE the window.
+ * The r1236 evidence: this block was emitted AFTER the
+ * window copy (contradicting its own "before the window"
+ * log), so the window's CALL_MME_MACRO(j) runs executed
+ * against the SA table state left by the prelude - whose
+ * last captured SA write re-clobbers the slot (r1231:
+ * SA[34]=0 lands last, every draw call resolved to macro
+ * 0 and emitted nothing). Because the prelude is
+ * re-emitted on every launch while any slot is kept, the
+ * late re-arm was re-clobbered before the next window
+ * anyway; the re-point only ever held between a window
+ * and the next prelude re-emission. Emitting the re-arm
+ * ahead of the window makes every launch's calls resolve
+ * to the captured SA entry.
+ */
+ if (READ_ONCE(gsp_phys_rmcall_gr_mme_sa_rearm)) {
+ u32 sa_ent[64];
+ u64 sa_mask = gsp_sema_mme_sa_get(sa_ent, 64);
+ u32 r = 0, rearm = 0;
+
+ while (r < win3d_n && rearm < 32u) {
+ u32 h = win3d[r];
+ u32 op = (h >> 29) & 7u;
+ u32 cnt = (h >> 16) & 0xfffu;
+ u32 span = ((op == 1u || op == 3u ||
+ op == 5u) && cnt &&
+ r + 1u + cnt <= win3d_n) ?
+ 1u + cnt : 1u;
+ u32 mb = (h & 0xfffu) << 2;
+
+ if (mb >= 0x3800u && mb <= 0x39fcu) {
+ u32 j = (mb - 0x3800u) / 8u;
+
+ if (j < 64u &&
+ (sa_mask & (1ull << j)) &&
+ sa_ent[j]) {
+ *p++ = ((u32)
+ GSP_PHYS_RMCALL_METHOD_IMM
+ << 29) |
+ (2u << 16) |
+ (0x11cu >> 2);
+ *p++ = j;
+ *p++ = sa_ent[j];
+ rearm++;
+ }
+ }
+ r += span;
+ }
+ if (rearm)
+ pr_info("vgsp-gr: MME-SA-REARM %u slot(s) re-pointed before the window (h=0x%x push=0x%llx)\n",
+ rearm, req->h_object, req->push_va);
+ }
+ if (mme_hold_calls) {
+ /*
+ * SESSION 153-CONTINUED-82: the prelude latch for
+ * this generation+channel is not set - elide every
+ * remaining CALL_MME_MACRO/DATA run of the window
+ * (the proven-safe drop shape; see the
+ * sema_gl_mme_hold_calls comment). Runs touching
+ * the MME macro method space 0x3800..0x39fc are the
+ * ones that execute a macro; the drop decision is
+ * the same walk gsp_phys_gr_mme_drop_calls uses.
+ */
+ u32 r = 0, w = 0, held = 0;
+
+ while (r < win3d_n) {
+ u32 h = win3d[r];
+ u32 op = (h >> 29) & 7u;
+ u32 cnt = (h >> 16) & 0xfffu;
+ u32 span = 1u;
+ bool iscall = false;
+ u32 k;
+
+ if ((op == 1u || op == 3u || op == 5u) &&
+ cnt && r + 1u + cnt <= win3d_n)
+ span = 1u + cnt;
+ for (k = 0; k < span; k++) {
+ u32 m = ((h & 0xfffu) << 2) +
+ 4u * k;
+
+ if (k && op != 1u && op != 3u)
+ break;
+ if (m >= 0x3800u && m <= 0x39fcu) {
+ iscall = true;
+ break;
+ }
+ }
+ if (iscall) {
+ held += span;
+ r += span;
+ continue;
+ }
+ memcpy(&p[w], &win3d[r], (size_t)span * 4u);
+ w += span;
+ r += span;
+ }
+ if (held)
+ pr_info("vgsp-gr: MME-CALL-HOLD %u word(s) of real CALL runs held (prelude gen=%u not latched for h=0x%x; window=%u->%u) push=0x%llx\n",
+ held, pl_gen, req->h_object,
+ win3d_n, w, req->push_va);
+ p += w;
+ } else {
+ memcpy(p, win3d, (size_t)win3d_n * 4);
+ p += win3d_n;
+ }
+ /*
+ * SESSION 153-CONTINUED-84: the SA re-arm block used to be
+ * emitted HERE (after the window). It was moved ahead of the
+ * window above; see that block's comment for the r1236
+ * evidence. An SA[slot]=entry re-point behind the call that
+ * needs it cannot help that call, and the per-launch
+ * prelude re-emission re-clobbers it before the next window.
+ */
p = gsp_phys_rmcall_push_gr(p,
GSP_PHYS_RMCALL_GR_SET_REPORT_SEMAPHORE_A,
(u32)(sem_va >> 32));
@@ -21554,6 +22312,7 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
u32 *n_shadow)
{
u32 off = 0, wout = 0, reloc = 0, dropped = 0, subdrop = 0, sdm_drop = 0;
+ u32 wp_match = 0, wp_lowrun = 0;
*out_n = n;
*n_dropped = 0;
@@ -21604,6 +22363,21 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
*/
if (READ_ONCE(gsp_phys_rmcall_gr_sdm_drop) &&
hdr && (op == 0u || op == 2u || op == 6u)) {
+ /*
+ * SESSION 153-CONTINUED-86: name every dropped SDM
+ * entry (bounded) so the A/B arm can be audited: the
+ * "keep the drop only for entries proven unrelated"
+ * decision needs the actual words, not the count.
+ */
+ static u32 sdm_log;
+
+ if (sdm_log < 24u) {
+ sdm_log++;
+ pr_info("vgsp-gr: SDM-DROP in[%u]=%08x op=%u addr=%#x sub=%u cnt/val=%#x (span=%u)\n",
+ off, hdr, op, addr, sub,
+ (cnt ? cnt : ((hdr >> 16) & 0x1fffu)),
+ span);
+ }
dropped++;
sdm_drop++;
off += span;
@@ -21701,6 +22475,7 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
static u32 patch_log;
u32 old;
+ wp_match++;
if (span > 1u) {
old = out[wout + 1];
out[wout + 1] = pv;
@@ -21714,6 +22489,21 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
pr_info("vgsp-gr: 3D win-patch addr=0x%x off=%u data 0x%x -> 0x%x\n",
addr, off, old, pv);
}
+ } else if (addr >= 0x100u && addr < 0x200u) {
+ /*
+ * SESSION 153-CONTINUED-84: the win_patch
+ * knob (sema_gl_win_patch=124:1) reaches
+ * this pass only if the window carries a
+ * run at the patch address; log the low-page
+ * runs so a decode mismatch is visible
+ * instead of silent.
+ */
+ wp_lowrun++;
+ if (wp_lowrun <= 4u &&
+ gsp_phys_rmcall_gr_win_patch &&
+ *gsp_phys_rmcall_gr_win_patch)
+ pr_info("vgsp-gr: WIN-PATCH lowrun addr=0x%x op=%u cnt=%u hdr=%08x\n",
+ addr, op, cnt, hdr);
}
}
if ((op == 1 || op == 3 || op == 5) && cnt &&
@@ -21978,6 +22768,22 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
off++; /* header-only / resync, like the service decoder */
wout++;
}
+ /*
+ * SESSION 153-CONTINUED-84: prove the win_patch knob reached the
+ * pass (the r1236 "never logged" was a late write, not a decode
+ * gap - this line makes the distinction explicit on every boot).
+ */
+ if (gsp_phys_rmcall_gr_win_patch && *gsp_phys_rmcall_gr_win_patch) {
+ static u32 wp_dbg;
+
+ if (wp_dbg < 24u) {
+ wp_dbg++;
+ pr_info("vgsp-gr: WIN-PATCH sweep knob=%s runs=%u matched=%u lowruns=%u h=0x%x\n",
+ gsp_phys_rmcall_gr_win_patch, n, wp_match,
+ wp_lowrun,
+ req ? req->h_object : 0u);
+ }
+ }
*out_n = wout;
*n_dropped = dropped;
gsp_phys_rmcall_gr_3d_last_subdrop = subdrop;
@@ -22900,6 +23706,59 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
pg->guest_va, sh_va, slot);
}
}
+ /*
+ * SESSION 153-CONTINUED-89: inline records that land on a
+ * qmd_word-NONE page. The record-apply loop below belongs to
+ * the QMD-word shadow-*copy* path and never runs for these
+ * pages; they are resolved to the guest HPA directly (Tier-1
+ * identity, contiguous shadow-line extension, or the alias
+ * above), so authoring the record into pg->hpa is exactly
+ * what the engine will read. The concrete case: the real
+ * captured upload at 0x4350f00 (1408 B) crosses its 4 KB
+ * page - the head (256 B) lands on the program page 0x4350000
+ * and applies, but the tail page 0x4351000 is qmd_word=NONE
+ * and was silently dropped, leaving the draw's winning j=1
+ * program (the later bind) truncated at 256 B at the engine
+ * (the DS rejects the SASS it cannot fetch: SPH 9/11).
+ */
+ if (mapped && pg->hpa &&
+ pg->qmd_word == GSP_PHYS_GR_QMD_WORD_NONE) {
+ u32 jx;
+
+ for (jx = 0; jx < req->n_inl; jx++) {
+ const struct gsp_phys_gr_inline *inl =
+ &req->inl[jx];
+ u32 len;
+
+ if ((inl->dst_va &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1)) != pg_va)
+ continue;
+ len = inl->len;
+ if (len > GSP_PHYS_GR_INLINE_BYTES)
+ len = GSP_PHYS_GR_INLINE_BYTES;
+ if ((u64)(inl->dst_va &
+ (GSP_PHYS_PAGE_SIZE - 1)) + len >
+ GSP_PHYS_PAGE_SIZE)
+ continue;
+ memcpy((u8 *)__va(pg->hpa) +
+ (inl->dst_va &
+ (GSP_PHYS_PAGE_SIZE - 1)),
+ inl->data, len);
+ {
+ static u32 inl_alias_log;
+
+ if (inl_alias_log < 12u) {
+ inl_alias_log++;
+ pr_info("vgsp-gr: INLINE-APPLY-ALIAS dst=0x%llx len=%u pa=0x%llx first=%08x (qmd_word=NONE page authored)\n",
+ inl->dst_va, len,
+ pg->hpa,
+ len >= 4 ?
+ get_unaligned_le32(inl->data) :
+ 0u);
+ }
+ }
+ }
+ }
if (!mapped && pg->qmd_word != GSP_PHYS_GR_QMD_WORD_NONE &&
pg->qmd_word + 1 < GSP_PHYS_GR_QMD_WORDS) {
struct gsp_phys_boot_chunk *sh =
@@ -23123,6 +23982,32 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
len >= 8 ? get_unaligned_le32(inl->data + 4) : 0u);
}
}
+ /*
+ * SESSION 153-CONTINUED-87: readback witness.
+ * For a stream3d window the engine's I-fetch
+ * walks the GUEST page (the inline bytes were
+ * just authored there above); read the exact
+ * engine-visible location back so the fetch
+ * content is provable from the log - if the DS
+ * still rejects the header, the bytes are known
+ * present at the VA it fetched.
+ */
+ if (stream3d && pg->hpa) {
+ static u32 inl_rb_log;
+ const u32 *gw = (const u32 *)
+ ((u8 *)__va(pg->hpa) +
+ (inl->dst_va &
+ (GSP_PHYS_PAGE_SIZE - 1)));
+
+ if (inl_rb_log < 12u) {
+ inl_rb_log++;
+ pr_info("vgsp-gr: INLINE-RB dst=0x%llx pa=0x%llx rb=%08x %08x %08x %08x %08x %08x %08x %08x\n",
+ inl->dst_va, pg->hpa,
+ gw[0], gw[1], gw[2],
+ gw[3], gw[4], gw[5],
+ gw[6], gw[7]);
+ }
+ }
copied++;
}
/*
@@ -23598,6 +24483,233 @@ gr_page_unmapped:
gsp_phys_rmcall_gr_3d_last_subdrop,
gsp_phys_rmcall_gr_3d_last_sdmdrop);
/*
+ * SESSION 153-CONTINUED-87: emit the client's missing
+ * pipeline-stage program binds (see the sema_gl_sph_bind
+ * comment). The window carries only the j=1 (VTG) pair; the
+ * real client state also binds j=2 (TESS_CONTROL at
+ * family+0x200) and j=5 (PIXEL at family+0x0) - both present
+ * verbatim in the init-push image scraped from guest RAM
+ * (`20020825 [0,04400200]`, `20020855 [0,04400000]`). Build
+ * the two runs and push them through the same window
+ * relocation pass (miss -> the pair stays raw and the whole
+ * bind is skipped, never a raw client VA to the engine),
+ * then prepend them ahead of the window. Beef client
+ * channels only: the family layout is that client's.
+ */
+ if (READ_ONCE(gsp_phys_rmcall_gr_sph_bind) &&
+ (req->h_object & 0xfffff000u) == 0xbeef0000u &&
+ req->prog_va &&
+ win3d_n + 64u <= GSP_PHYS_RMCALL_GR_METHOD_WORDS) {
+ static u32 sph_bind_log;
+ u64 fam = req->prog_va &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1);
+ u32 in[64], out[64];
+ u32 nfin = 0, nrun = 0, on = 0, odrop = 0, orel;
+ u32 bi;
+
+ /*
+ * SESSION 153-CONTINUED-89 (2nd half): the client's
+ * full channel-init pipeline block, harvested from the
+ * guest-RAM init-push image (guest PA 0x81efc5b4, this
+ * boot) and identical in the X channel's own captured
+ * window (methods 0x21c, 0x155c, 0x1574 in that exact
+ * order). The per-frame window carries none of it -
+ * on real hardware the client wrote it ONCE at
+ * channel init, and the replay channel's fresh context
+ * never receives it, so the DS validates the fetched
+ * SPHs against unset context state (9/11). Emitted
+ * verbatim in the client's own order, ahead of the
+ * stage binds:
+ * 0x21c <- 0x1001 INVALIDATE_SHADER_CACHES
+ * (INSTRUCTION|CONSTANT)
+ * 0x155c <- 1, 0x214fa200, 0
+ * TEX_SAMPLER_POOL A/B/C
+ * 0x1574 <- 1, 0x214fa220, 0x10
+ * TEX_HEADER_POOL A/B/C
+ * 0x310 <- 0x705 SET_SPA_VERSION
+ * 0x22c <- 0x800 SET_GO_IDLE_TIMEOUT
+ * 0x1128 <- ffffffff, ffffffff
+ * DA_OUTPUT_ATTRIBUTE_SKIP_MASK
+ * ZCULL_BOUNDS(0x196c) is already carried by the
+ * window itself. All constant runs (no relocation).
+ */
+ in[nfin++] = 0x20000000u | (1u << 16) |
+ (0x21cu >> 2);
+ in[nfin++] = 0x1001u;
+ {
+ /*
+ * The pool runs are address-bearing (the patch
+ * pass's own rules: addr 0x155c/0x1574, cnt >=
+ * 3) and count toward the relocated-pair
+ * guard, so emit them only when their page is
+ * in this launch's page set (r1290: emitted
+ * unconditionally -> unaccounted pairs ->
+ * `SPH-BIND skipped (reloc=6/4)` and the whole
+ * prelude was lost). Both VAs share one page.
+ */
+ bool pool_pg = false;
+ u32 pk;
+
+ for (pk = 0; pk < req->n_pages; pk++)
+ if ((req->pages[pk].guest_va &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1)) ==
+ (0x214fa200u &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1)))
+ pool_pg = true;
+ if (pool_pg) {
+ in[nfin++] = 0x20000000u | (3u << 16) |
+ (0x155cu >> 2);
+ in[nfin++] = 0x1u;
+ in[nfin++] = 0x214fa200u;
+ in[nfin++] = 0x0u;
+ in[nfin++] = 0x20000000u | (3u << 16) |
+ (0x1574u >> 2);
+ in[nfin++] = 0x1u;
+ in[nfin++] = 0x214fa220u;
+ in[nfin++] = 0x10u;
+ nrun += 2;
+ }
+ }
+ in[nfin++] = 0x20000000u | (1u << 16) |
+ (0x310u >> 2);
+ in[nfin++] = 0x705u;
+ in[nfin++] = 0x20000000u | (1u << 16) |
+ (0x22cu >> 2);
+ in[nfin++] = 0x800u;
+ in[nfin++] = 0x20000000u | (2u << 16) |
+ (0x1128u >> 2);
+ in[nfin++] = 0xffffffffu;
+ in[nfin++] = 0xffffffffu;
+
+ /*
+ * SESSION 153-CONTINUED-89: the client's init-push
+ * pipeline state for the window's own stage 1. The
+ * guest-RAM init-push image (live at guest PA
+ * 0x81efc5b4, this boot) carries, beside the SPH
+ * version pair:
+ * 0x2040 <- 0x11 SET_PIPELINE_SHADER(1)
+ * ENABLE|TYPE=VERTEX
+ * 0x2054 <- 1, 0x21201b00 program address pair
+ * 0x204c <- 0x1a, 0x0 REGISTER_COUNT(1)=26,
+ * BINDING(1)=0
+ * The per-frame window carries only the address pair,
+ * so the replay channel's TYPE/COUNT/BINDING stage
+ * registers stay unset - the state the DS validates
+ * the fetched SPH against (9/11). Emit the TYPE run
+ * before the binds and the COUNT/BINDING pair after
+ * them, in the client's own order. Both are constant
+ * runs (no relocation).
+ */
+ in[nfin++] = 0x20000000u | (1u << 16) |
+ (0x2040u >> 2);
+ in[nfin++] = 0x11u;
+
+ /*
+ * SESSION 153-CONTINUED-87 (second half): the window's
+ * OWN binds first. The captured per-frame window
+ * carries draws (BEGIN at word 93) BEFORE its program
+ * binds (j=1 pair at word 184): on real hardware the
+ * bind happened in an earlier push, but the replay
+ * channel's fresh context has nothing bound when the
+ * early draws validate their stages - the
+ * Shader-Program-Header poison (r1277+ 9/11). Re-emit
+ * every bind the window itself carries ahead of it.
+ */
+ for (bi = 0; bi < req->n_prog_binds && bi < 8u; bi++) {
+ u64 va = req->prog_bind_va[bi];
+ u32 sl = req->prog_bind_slot[bi] & 0x1fu;
+
+ if (!va || nfin + 3u > 64u)
+ continue;
+ in[nfin++] = 0x20000000u | (2u << 16) |
+ ((0x2014u + sl * 0x40u) >> 2);
+ in[nfin++] = (u32)(va >> 32) & 0xffu;
+ in[nfin++] = (u32)va & 0xfffffffcu;
+ nrun++;
+ }
+ /*
+ * Then the client's missing stage binds (see the
+ * sema_gl_sph_bind comment): TESS_CONTROL (j=2 at
+ * family+0x200) and PIXEL (j=5 at family+0x0), both
+ * present verbatim in the init-push image scraped
+ * from guest RAM (`20020825 [0,04400200]`,
+ * `20020855 [0,04400000]`).
+ */
+ if (nfin + 13u <= 64u) {
+ in[nfin++] = 0x20000000u | (2u << 16) |
+ (0x2094u >> 2);
+ in[nfin++] = (u32)((fam + 0x200u) >> 32) & 0xffu;
+ in[nfin++] = (u32)(fam + 0x200u) & 0xfffffffcu;
+ in[nfin++] = 0x20000000u | (2u << 16) |
+ (0x2154u >> 2);
+ in[nfin++] = (u32)(fam >> 32) & 0xffu;
+ in[nfin++] = (u32)fam & 0xfffffffcu;
+ /*
+ * SESSION 153-CONTINUED-88: the per-slot
+ * SET_PIPELINE_SHADER(j) TYPE state. The
+ * client's init push writes it once per
+ * channel (init-push image: `20010810
+ * 00000011` = SET_PIPELINE_SHADER(1) <-
+ * ENABLE|TYPE=VTG); the captured per-frame
+ * windows carry only the program-address
+ * pairs, so the replay context's stage
+ * registers for the synthesized binds would
+ * stay unset. Emit them for the two
+ * synthesized slots with the known types
+ * (j=2 TESS_CONTROL -> 2, j=5 PIXEL -> 5).
+ */
+ in[nfin++] = 0x20000000u | (1u << 16) |
+ (0x2080u >> 2);
+ in[nfin++] = 1u | (2u << 4);
+ in[nfin++] = 0x20000000u | (1u << 16) |
+ (0x2140u >> 2);
+ in[nfin++] = 1u | (5u << 4);
+ /*
+ * SESSION 153-CONTINUED-89: the client's
+ * init-push stage-1 REGISTER_COUNT/BINDING
+ * pair (`20020813 0000001a 00000000` in the
+ * guest-RAM init image): REGISTER_COUNT(1) =
+ * 26 (the window SPH's reg count), BINDING(1)
+ * = group 0. Emitted after the address pair,
+ * exactly as the client's own push orders it.
+ */
+ in[nfin++] = 0x20000000u | (2u << 16) |
+ (0x204cu >> 2);
+ in[nfin++] = 0x1au;
+ in[nfin++] = 0x0u;
+ /*
+ * nrun counts only the two ADDRESS-bearing
+ * binds above: the guard below matches it
+ * against the relocation pass's
+ * relocated-pair count, and the constant
+ * type runs can never be relocated.
+ */
+ nrun += 2;
+ }
+ orel = gsp_phys_rmcall_gr_3d_patch_window(
+ in, nfin, out, &on, &odrop, req, &n_shadow);
+ if (orel == nrun && on == nfin) {
+ memmove(&stream_patch[nfin], stream_patch,
+ (size_t)win3d_n * 4u);
+ memcpy(stream_patch, out, (size_t)nfin * 4u);
+ win3d_n += nfin;
+ if (sph_bind_log < 16u) {
+ sph_bind_log++;
+ pr_info("vgsp-gr: SPH-BIND emitted fam=0x%llx win-binds=%u j2=0x%llx j5=0x%llx j1-type=0x11 rc=0x1a initblk=17 win=%u (pre=%u) h=0x%x push=0x%llx\n",
+ fam, req->n_prog_binds,
+ fam + 0x200u, fam,
+ win3d_n, nfin,
+ req->h_object,
+ req->push_va);
+ }
+ } else if (sph_bind_log < 16u) {
+ sph_bind_log++;
+ pr_info("vgsp-gr: SPH-BIND skipped (reloc=%u/%u kept=%u/%u fam=0x%llx h=0x%x push=0x%llx)\n",
+ orel, nrun, on, nfin, fam,
+ req->h_object, req->push_va);
+ }
+ }
+ /*
* SESSION 153-CONTINUED-62: surface (render-target) prelude -
* see the gsp_phys_gr_surf_pre comment. Injected only when the
* window carries no usable colour-target bind of its own; the
@@ -24449,6 +25561,13 @@ gr_page_unmapped:
* pre-retire - the r1110 mme_pc=513 class).
*/
WRITE_ONCE(gsp_phys_gr_mme_prelude_gen, 0);
+ /*
+ * SESSION 153-CONTINUED-79: also rewind the
+ * split-emission drain cursor, else a cleared
+ * latch with an exhausted cursor would skip
+ * the owed re-preload.
+ */
+ WRITE_ONCE(gsp_phys_gr_mme_pre_emit_off, 0);
pr_warn("vgsp-gr: WEDGE-RECOVER after re-arm grfifo=0x%08x (%s); recovery #%u this boot; MME prelude latch cleared (context re-preload owed)\n",
g1,
(g1 & 0xffff0000u) == 0xbadf0000u ?
@@ -25688,6 +26807,18 @@ gr_page_unmapped:
late, late_tracked,
late ? "LATE-STORES - copy-out will ship them" :
"NO-STORES - dispatch lost or read-only kernel");
+ /*
+ * SESSION 153-CONTINUED-83: the no-writes moment
+ * is the store wall's fingerprint - snapshot the
+ * DS/SPH/MME/FE-decode evidence here so the
+ * stage that never received a program header (or
+ * the MME PC that never emitted the draw) is on
+ * the same line as the failing launch.
+ */
+ if (!late)
+ gsp_phys_store_side_log(gsp_phys_gpu_bar0(),
+ "no-writes",
+ req->h_object);
}
if (qmd_rel_hpa) {
diff --git a/kernel/gsp_phys_booter.h b/kernel/gsp_phys_booter.h
index 39824e3..6f4d1ea 100644
--- a/kernel/gsp_phys_booter.h
+++ b/kernel/gsp_phys_booter.h
@@ -354,7 +354,13 @@ void gsp_phys_backend_relay_counters(u64 *requested, u64 *ok, u64 *verify_fail,
*/
#define GSP_PHYS_GR_MAX_PAGES 16u
#define GSP_PHYS_GR_MAX_INLINE 8u
-#define GSP_PHYS_GR_INLINE_BYTES 256u
+/*
+ * SESSION 153-CONTINUED-88: 256 -> 2048 (must stay equal to the sema
+ * service's SVC_GR_INLINE_BYTES and the bridge's GSP_BRIDGE_INLINE_BYTES -
+ * static_asserts enforce it). The GL window's real program uploads are
+ * 1536 B / 1408 B and must be carried whole to the engine-visible page.
+ */
+#define GSP_PHYS_GR_INLINE_BYTES 2048u
#define GSP_PHYS_GR_QMD_WORDS 64u
/*
* IQ2M sentinel: written to the inline-QMD destination's word 63 after the
@@ -485,6 +491,24 @@ struct gsp_phys_gr_submit {
*/
const u32 *stream;
u32 stream_n; /* dwords */
+ /*
+ * SESSION 153-CONTINUED-87: the first program VA the captured
+ * window binds (SET_PIPELINE_PROGRAM_ADDRESS pair; 0 when none).
+ * The launch builder uses its 4K page as the program family for
+ * the client's missing stage binds (sema_gl_sph_bind).
+ */
+ u64 prog_va;
+ /*
+ * SESSION 153-CONTINUED-87: every program bind the captured window
+ * itself carries (VA + pipeline slot). The launch builder re-emits
+ * them AHEAD of the window: the window's own draws (BEGIN 0x1618)
+ * precede its binds (measured word 93 vs 184), and on the replay
+ * channel's fresh context the early draws would validate an unbound
+ * stage and latch the Shader Program Header fault.
+ */
+ u32 n_prog_binds;
+ u64 prog_bind_va[8];
+ u32 prog_bind_slot[8];
};
/* True when gsp_phys_gr_forward is set and the ladder is a live GR channel
diff --git a/kernel/gsp_phys_gpu.h b/kernel/gsp_phys_gpu.h
index 4d9f47b..b8d92cd 100644
--- a/kernel/gsp_phys_gpu.h
+++ b/kernel/gsp_phys_gpu.h
@@ -340,6 +340,14 @@ int gsp_phys_rmcall_faultbuf_sys_value(void);
* (proven small-PT line) instead of the window-1 scratch range (default 0). */
int gsp_phys_rmcall_gr_shadow_win0_value(void);
+/*
+ * SESSION 153-CONTINUED-87: emit the client's missing TESS_CONTROL (j=2)
+ * and PIXEL (j=5) pipeline program binds ahead of replayed 3D windows
+ * (sema_gl_sph_bind, default 1). The sema service uses the same value to
+ * author the matching program images at those family VAs.
+ */
+bool gsp_phys_rmcall_gr_sph_bind_value(void);
+
/* m91: keep the forwarded QMD pointer pairs at the guest VAs (default 0). */
int gsp_phys_rmcall_gr_ptr_guest_value(void);
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 29d28d6..8fc663f 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -7263,7 +7263,13 @@ static struct svc_fwd_cand svc_fwd_cand;
*/
#define SVC_GR_QMD_WORDS 64
#define SVC_GR_INLINE_MAX 8
-#define SVC_GR_INLINE_BYTES 256
+/*
+ * SESSION 153-CONTINUED-88: 256 -> 2048 (static_asserts below tie it to
+ * GSP_BRIDGE_INLINE_BYTES / GSP_PHYS_GR_INLINE_BYTES). The GL window's real
+ * program upload payloads are 1536 B / 1408 B; the 256 B cap truncated them
+ * to their first 256 bytes, so the engine never saw the full image.
+ */
+#define SVC_GR_INLINE_BYTES 2048
#define SVC_GR_POINTER_MAX 8
#define SVC_GR_PAGES_MAX 16
/*
@@ -7350,6 +7356,9 @@ struct svc_gr_data_win {
u64 reloc_base; /* relocated window base */
};
+/* CONT-87: max program binds carried from a captured window (mirrors
+ * SVC_GL_PROG_MAX; defined here because the candidate struct precedes it). */
+#define SVC_GR_CAND_PROG_BINDS 8
struct svc_gr_cand_rec {
bool valid;
bool offered; /* consumed by a request or a fallback */
@@ -7380,6 +7389,20 @@ struct svc_gr_cand_rec {
bool stream_3d;
u32 stream_off; /* dword offset of the window inside raw[] */
u32 stream_n; /* dwords of the replayable window */
+ /*
+ * SESSION 153-CONTINUED-87: every SET_PIPELINE_PROGRAM_ADDRESS pair
+ * the captured window itself binds (VA + pipeline slot), copied from
+ * svc_gl at capture. The launch builder re-emits them AHEAD of the
+ * window: the captured per-frame window carries draws (BEGIN 0x1618)
+ * BEFORE its own program binds (measured: draw at window word 93,
+ * j=1 pair at word 184), on the real hardware the bind already
+ * happened in an earlier push - on the replay channel's fresh
+ * context the early draws would validate an UNBOUND stage and latch
+ * the Shader Program Header fault (the r1277+ 9/11 wall).
+ */
+ u32 n_prog_binds;
+ u64 prog_bind_va[SVC_GR_CAND_PROG_BINDS];
+ u32 prog_bind_slot[SVC_GR_CAND_PROG_BINDS];
u32 raw[SVC_GR_PUSH_WORDS];
u32 qmd[SVC_GR_QMD_WORDS];
u64 qmd_dev_va;
@@ -7397,6 +7420,7 @@ struct svc_gr_cand_rec {
u32 pointer_word[SVC_GR_POINTER_MAX]; /* QMD word of the pair */
};
+
/*
* fail4: the capture was a single slot; two concurrent launches (streams2's
* vadd pair on two channels) overwrote each other's candidate while the
@@ -7573,6 +7597,16 @@ static int svc_grce_inline_write(struct nv_vgpu_device *vgpu, u64 dst_va,
static bool svc_inline_once_seen(u64 push_va, u64 hash, u64 dst, u32 bytes);
static void svc_inline_once_store(u64 push_va, u64 hash, u64 dst, u32 bytes);
+/*
+ * SESSION 153-CONTINUED-88: newest captured inline-write payload landing
+ * exactly at `dst_va` with at least `min_len` bytes (defined next to the
+ * destination ring below). Used by svc_gl_flush to deliver the window's REAL
+ * program uploads (the driver's own SPH+SASS images) to the engine-visible
+ * page instead of the minimal authored blob.
+ */
+static bool svc_grce_dst_newest(u64 dst_va, u32 min_len, u32 *len, u8 *buf,
+ u32 buf_sz);
+
/* QMD field decode: the exact shape the host CUDA bridge uses. */
static void svc_gr_qmd_decode(struct svc_gr_cand_rec *c)
{
@@ -8223,6 +8257,33 @@ static void svc_gl_prog_apply_param(void)
}
/*
+ * SESSION 153-CONTINUED-87: the SPH VERSION of the live program images.
+ * The launch builder emits SET_SPH_VERSION from this value so the
+ * window's accepted version range always covers the images the engine
+ * is about to fetch. The client's own init push carries 4|(4<<16), but
+ * the r950 gate proved the V3 image executes; a 4/4 window over a V3
+ * SPH rejects every fetched header (SPH9|SPH11|SPH18, r1273-r1275) -
+ * the r950-era acceptance ran with no version window at all. Returns
+ * the max VERSION field of the loaded blobs (0 = none).
+ */
+int gsp_sema_gl_sph_blob_version(void)
+{
+ u32 v = 0, ver;
+
+ if (svc_gl_vs_len >= 4) {
+ ver = (get_unaligned_le32(svc_gl_vs) >> 5) & 0x1fu;
+ if (ver > v)
+ v = ver;
+ }
+ if (svc_gl_ps_len >= 4) {
+ ver = (get_unaligned_le32(svc_gl_ps) >> 5) & 0x1fu;
+ if (ver > v)
+ v = ver;
+ }
+ return (int)v;
+}
+
+/*
* SESSION 153 (GL client draw sections): the GL client's own per-frame
* pushes bind TURING_A (0xC597) exactly like the X server's one-shot
* section, but carry no program/param/release operands at all (measured:
@@ -9390,7 +9451,14 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
u32 at_off)
{
struct svc_push_state s2;
- struct gsp_bridge_inline prs[SVC_GL_PROG_MAX];
+ /*
+ * SESSION 153-CONTINUED-88: static (was stack). With the inline
+ * record grown to 2048 B the 8-record array is ~16 KB - too much for
+ * a kernel stack; this function runs on the single service thread
+ * (same reason as the static recs[] in svc_gr_forward and the ops
+ * array in svc_decode_stream), so a static array is safe.
+ */
+ static struct gsp_bridge_inline prs[SVC_GL_PROG_MAX];
u32 n_recs = 0, k;
/*
* SESSION 149 R16 (M3): the inline attachment's source. Priority:
@@ -9840,6 +9908,132 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
u32 bl = 0;
u8 t = svc_gl_shader_type[svc_gl.prog_slot[pi] < 32 ?
svc_gl.prog_slot[pi] : 0];
+ /*
+ * SESSION 153-CONTINUED-88: deliver the REAL captured
+ * upload payload to the engine-visible page. The
+ * window's own program uploads (the client's 0x180
+ * LINE_LENGTH_IN + 0x1b0 LOAD_INLINE_DATA runs -
+ * observed 1536 B at 0x4350900 and 1408 B at
+ * 0x4350f00, w0=0x020a0481 = the driver's real SPH
+ * V4/VTG image) were captured at decode time into the
+ * destination ring but landed only in the BAR1 shadow;
+ * the engine validates the SPH of every bound program
+ * at the FILL-resolved page. Attach the real payload
+ * as an inline record at its own destination VA - the
+ * backend authors it into the engine-visible page on
+ * the stream3d path - and let it win over the minimal
+ * authored image below. Exact-VA match: the bind VA
+ * IS the upload destination.
+ */
+ {
+ u32 rlen = 0;
+ u32 ri0 = n_recs;
+
+ memset(&prs[n_recs], 0, sizeof(prs[n_recs]));
+ if (svc_grce_dst_newest(svc_gl.progs[pi], 128u,
+ &rlen, prs[n_recs].data,
+ (u32)sizeof(prs[n_recs].data))) {
+ u64 d = svc_gl.progs[pi];
+ u32 head = 4096u - (u32)(d & 0xfff);
+
+ if (rlen <= head) {
+ prs[n_recs].dst_va = d;
+ prs[n_recs].len = rlen;
+ n_recs++;
+ } else {
+ /*
+ * The payload crosses its
+ * 4K page (observed: the
+ * 1408 B image at
+ * 0x4350f00). The backend
+ * applies a record only
+ * within one page, so
+ * split: page remainder +
+ * tail at the next page's
+ * VA (skipped by the
+ * backend when that page
+ * is not in the request -
+ * the SPH head still
+ * lands).
+ */
+ prs[n_recs].dst_va = d;
+ prs[n_recs].len = head;
+ n_recs++;
+ if (n_recs <
+ GSP_BRIDGE_INLINE_MAX) {
+ memset(&prs[n_recs], 0,
+ sizeof(prs[n_recs]));
+ prs[n_recs].dst_va =
+ (d & ~(u64)0xfff) +
+ 0x1000;
+ prs[n_recs].len =
+ rlen - head;
+ memcpy(prs[n_recs].data,
+ prs[n_recs - 1].data + head,
+ rlen - head);
+ n_recs++;
+ }
+ /*
+ * SESSION 153-CONTINUED-88:
+ * the tail record is applied
+ * only when its page is in the
+ * launch's page set. Add the
+ * tail page as a candidate
+ * operand page (the engine
+ * will fetch the program's
+ * second half from it), so
+ * the request carries it and
+ * the split delivery is
+ * complete instead of
+ * head-only.
+ */
+ svc_gl_add_ptr((d & ~(u64)0xfff) +
+ 0x1000);
+ }
+ if (gsp_sema_dbg &&
+ svc_gl_prog_log < 64) {
+ svc_gl_prog_log++;
+ pr_info("sema-gl: real-upload record va=0x%llx len=%u first=%08x (engine-visible delivery)\n",
+ d, rlen,
+ get_unaligned_le32(prs[ri0].data));
+ }
+ continue;
+ }
+ }
+ /*
+ * SESSION 153-CONTINUED-87: the REAL program upload
+ * wins. When the window carries its own upload of a
+ * program-sized payload (>= 128 B) to this same 4K
+ * page, the minimal authored image must NOT overwrite
+ * the driver's bytes at the engine-visible page (the
+ * upload now lands there through the pin-aware
+ * svc_grce_inline_write). Keep an explicit image only
+ * where no real upload exists (the synthesized stage
+ * binds below).
+ */
+ {
+ u32 uu;
+ bool real_up = false;
+
+ for (uu = 0; uu < svc_gl.n_up; uu++) {
+ if (svc_gl.up[uu].len < 128u)
+ continue;
+ if ((svc_gl.up[uu].dst_va &
+ ~(u64)(SEMA_PAGE_SIZE - 1)) ==
+ (svc_gl.progs[pi] &
+ ~(u64)(SEMA_PAGE_SIZE - 1))) {
+ real_up = true;
+ break;
+ }
+ }
+ if (real_up) {
+ if (gsp_sema_dbg &&
+ svc_gl_prog_log < 64)
+ pr_info("sema-gl: real-upload wins va=0x%llx (authored image skipped)\n",
+ svc_gl.progs[pi]);
+ continue;
+ }
+ }
if (t == 5 && svc_gl_ps_len) {
b = svc_gl_ps;
@@ -9862,6 +10056,64 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
memcpy(prs[n_recs].data, b, bl);
n_recs++;
}
+ /*
+ * SESSION 153-CONTINUED-87: author the program images for the
+ * client's missing stage binds. The replayed window binds only
+ * the j=1 (VTG) pair; the booter emits the client's own
+ * TESS_CONTROL (j=2, family+0x200) and PIXEL (j=5, family+0x0)
+ * binds ahead of the window (sema_gl_sph_bind), so their SPH
+ * bytes must exist at those VAs too - otherwise the DS fetches
+ * an empty/foreign page and still latches its header error.
+ * Observed family layout (guest-RAM scrape of the init push
+ * and the window itself): 0x4400000 j=5, 0x4400200 j=2,
+ * 0x4400300/0x4400900 j=1 - all in the window prog VA's own
+ * 4K page. The TESS image is the VTG image with the SPH
+ * SHADER_TYPE field (bits 13:10) re-pointed to 2, exactly
+ * like the driver's own placeholder bind. Beef client
+ * channels only, and only when the window does not already
+ * bind that exact VA.
+ */
+ if (gsp_phys_rmcall_gr_sph_bind_value() &&
+ (ch->h_object & 0xfffff000u) == 0xbeef0000u &&
+ svc_gl.progs[0]) {
+ u64 fam = svc_gl.progs[0] &
+ ~(u64)(SEMA_PAGE_SIZE - 1);
+ u32 k;
+
+ for (k = 0; k < 2u && n_recs < SVC_GL_PROG_MAX; k++) {
+ u64 dst = fam + (k ? 0x200u : 0x0u);
+ const u8 *b = k ? svc_gl_vs : svc_gl_ps;
+ u32 bl = k ? svc_gl_vs_len : svc_gl_ps_len;
+ bool dup = false;
+ u32 pi3;
+
+ if (!b || bl < 4u)
+ continue;
+ for (pi3 = 0; pi3 < svc_gl.n_progs; pi3++)
+ if (svc_gl.progs[pi3] == dst)
+ dup = true;
+ if (dup)
+ continue;
+ memset(&prs[n_recs], 0, sizeof(prs[n_recs]));
+ prs[n_recs].dst_va = dst;
+ prs[n_recs].len = bl;
+ memcpy(prs[n_recs].data, b, bl);
+ if (k) {
+ u32 w0 = get_unaligned_le32(
+ prs[n_recs].data);
+
+ w0 = (w0 & ~(0xfu << 10)) |
+ (2u << 10); /* TESS_CONTROL */
+ put_unaligned_le32(w0, prs[n_recs].data);
+ }
+ n_recs++;
+ if (gsp_sema_dbg && svc_gl_prog_log < 64) {
+ pr_info("sema-gl: stage-image dst=0x%llx slot=%u len=%u first=%08x\n",
+ dst, k ? 2u : 5u, bl,
+ get_unaligned_le32(prs[n_recs - 1].data));
+ }
+ }
+ }
if (n_recs && gsp_sema_dbg && svc_gl_prog_log < 64) {
u32 pi2;
@@ -9924,6 +10176,26 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
svc_gr_cand_capture(ch, &s2, n_recs ? prs : NULL, n_recs, buf, len,
push_va);
/*
+ * CONT-87: carry the window's own program binds so the launch
+ * builder can re-emit them ahead of the window (the window's early
+ * draws precede its binds; see svc_gr_cand_rec).
+ */
+ {
+ u32 pb;
+
+ svc_gr_cand.n_prog_binds = 0;
+ for (pb = 0; pb < svc_gl.n_progs &&
+ pb < SVC_GR_CAND_PROG_BINDS; pb++) {
+ if (!svc_gl.progs[pb])
+ continue;
+ svc_gr_cand.prog_bind_va[svc_gr_cand.n_prog_binds] =
+ svc_gl.progs[pb];
+ svc_gr_cand.prog_bind_slot[svc_gr_cand.n_prog_binds] =
+ svc_gl.prog_slot[pb];
+ svc_gr_cand.n_prog_binds++;
+ }
+ }
+ /*
* r929 (Path A): a stream-derived capture (op_src=stream: the section
* carried its own operands) is a raw 3D method window. Mark it for
* verbatim replay - but only when the WHOLE push survived in raw[]
@@ -11697,6 +11969,44 @@ static int svc_copy_1d(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t tot
* VA through the trusted sources (pins, discovered map, published page
* directories, targeted rm_mem resolve), pin the page and write the bytes.
*/
+/*
+ * SESSION 153-CONTINUED-88: write an inline payload into one landing page
+ * (page-aligned PA, aperture) at the destination VA's in-page offset. Shared
+ * by the primary landing write and the engine-visible pin dual-write. The
+ * write is clamped to the page end: a crossing payload's tail belongs to the
+ * NEXT page's own landing, not to whatever follows this page in the aperture.
+ */
+static int svc_grce_land_write(struct nv_vgpu_device *vgpu, u64 pa_page,
+ int ap, u64 dst_va, const u8 *data, u32 bytes)
+{
+ u64 pa = pa_page + (dst_va & 0xfff);
+ u32 room = 4096u - (u32)(dst_va & 0xfff);
+
+ if (bytes > room)
+ bytes = room;
+ if (!bytes)
+ return 0;
+ if (ap == SVC_APERTURE_FB) {
+ u8 *base = vgpu->bars[1].vaddr;
+
+ if (!base || pa + bytes > svc_fb_shadow_size(vgpu))
+ return -EINVAL;
+ memcpy(base + pa, data, bytes);
+ return 0;
+ }
+ {
+ u32 o;
+
+ for (o = 0; o < bytes; o += 4096) {
+ u32 chunk = bytes - o > 4096 ? 4096 : bytes - o;
+
+ if (svc_write(vgpu, pa + o, (void *)(data + o), chunk))
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
static int svc_grce_inline_write(struct nv_vgpu_device *vgpu, u64 dst_va,
const u8 *data, u32 bytes, u32 ch_h)
{
@@ -11731,10 +12041,70 @@ static int svc_grce_inline_write(struct nv_vgpu_device *vgpu, u64 dst_va,
pa = lpa & ~0xfffULL;
ap = (lap == SVC_APERTURE_FB) ?
SVC_APERTURE_FB : SVC_APERTURE_SYS;
+ /*
+ * SESSION 153-CONTINUED-88: dual-write to the
+ * engine-visible page. The launch fill resolves a
+ * page through the LIVE PINS first ("pin wins", see
+ * svc_gr_page_note) and the engine reads exactly
+ * that page - a ledger landing in the BAR1 shadow
+ * (ap=vidmem) is NOT what the engine fetches. The
+ * observed case: the real program uploads (1536 B at
+ * 0x4350900, 1408 B at 0x4350f00) landed at pa
+ * 0x1b0900/0x1b0f00 (vidmem) while the engine read
+ * the pinned sysmem page (0x529ff4000). When an
+ * injected pin names a different page, author the
+ * bytes there too (the ledger landing stays written
+ * for its existing consumers).
+ */
+ {
+ u64 ipa = 0;
+ int iap = SVC_APERTURE_SYS;
+
+ if (!svc_ipin_lookup(dst_va & ~0xfffULL,
+ &ipa, &iap) &&
+ ipa && (ipa & ~0xfffULL) !=
+ (pa & ~0xfffULL)) {
+ if (!svc_grce_land_write(vgpu,
+ ipa & ~0xfffULL,
+ iap, dst_va,
+ data, bytes) &&
+ gsp_sema_dbg &&
+ svc.grce_log < 64) {
+ svc.grce_log++;
+ pr_info("sema-grce: inline dual-write dst=0x%llx pa=0x%llx ap=%s bytes=%u (engine-visible pin) h=0x%x\n",
+ dst_va,
+ ipa & ~0xfffULL,
+ iap == SVC_APERTURE_FB ?
+ "vidmem" : "ram",
+ bytes, ch_h);
+ }
+ }
+ }
svc_pin_store(dst_va & ~0xfffULL, pa, ap);
goto write_landing;
}
}
+ /*
+ * SESSION 153-CONTINUED-87: the launch fill resolves a page through
+ * the LIVE PINS first ("pin wins", see svc_gr_page_note) and the
+ * engine reads exactly that page. An upload whose trusted landing is
+ * a VIDMEM aperture page must also reach the pinned engine-visible
+ * page, or the engine validates stale content: the real program
+ * uploads (1536 B at 0x4350900, 1408 B at 0x4350f00) currently land
+ * only in the BAR1 shadow while the fill hands the engine the pinned
+ * sysmem page. Write to the pin when it names a different page.
+ */
+ {
+ u64 ipa = 0;
+ int iap = SVC_APERTURE_SYS;
+
+ if (!svc_ipin_lookup(dst_va & ~0xfffULL, &ipa, &iap) &&
+ iap != SVC_APERTURE_FB && ipa) {
+ pa = ipa & ~0xfffULL;
+ ap = SVC_APERTURE_SYS;
+ goto write_landing;
+ }
+ }
if (svc_lookup_trusted(vgpu, dst_va & ~0xfffULL, &pa, &ap)) {
if (!svc_vas_write(dst_va, data, bytes)) {
svc.vas_shadowed++;
@@ -12809,6 +13179,33 @@ struct svc_grce_rec {
static struct svc_grce_rec svc_grce_dst_ring[SVC_GRCE_DST_MAX];
static u32 svc_grce_dst_ring_next;
+/*
+ * SESSION 153-CONTINUED-88: newest captured inline-write payload landing
+ * exactly at `dst_va` with at least `min_len` bytes. Newest-first scan so a
+ * fresh upload always wins over a stale ring entry. Copies the payload into
+ * the caller's buffer (buf_sz bounds the copy) and returns true on a hit.
+ */
+static bool svc_grce_dst_newest(u64 dst_va, u32 min_len, u32 *len, u8 *buf,
+ u32 buf_sz)
+{
+ u32 ri;
+
+ for (ri = 0; ri < SVC_GRCE_DST_MAX; ri++) {
+ u32 idx = (svc_grce_dst_ring_next + SVC_GRCE_DST_MAX - 1u -
+ ri) % SVC_GRCE_DST_MAX;
+ const struct svc_grce_rec *r = &svc_grce_dst_ring[idx];
+
+ if (r->len < min_len || !r->dst_va)
+ continue;
+ if (r->dst_va != dst_va)
+ continue;
+ *len = min_t(u32, r->len, buf_sz);
+ memcpy(buf, r->data, *len);
+ return true;
+ }
+ return false;
+}
+
static void svc_counter_learn(struct nv_vgpu_device *vgpu, u32 h, u64 sem_va);
static void svc_pending_add(u64 va, u32 payload, u32 op);
static int svc_rescue_push_page(struct nv_vgpu_device *vgpu,
@@ -14433,6 +14830,14 @@ static void svc_mme_prelude_capture(const u8 *buf, u32 len, u64 push_va,
{
u32 off = 0, added = 0, dups = 0, full = 0;
u32 words0 = svc_mme_prelude_n;
+ /*
+ * SESSION 153-CONTINUED-84: which 4K pages of the push delivered at
+ * least one setup run. r1240's carrier read decoded only pages 0 of
+ * 0x4001004 (15 slots, 933 code words) while r1236's identical
+ * stream decoded all 16 (55 slots, 2854 code words) - the bitmap
+ * names the truncation page in one line.
+ */
+ u64 page_bits = 0;
if (!READ_ONCE(svc_mme_prelude_flag))
return;
@@ -14511,6 +14916,7 @@ static void svc_mme_prelude_capture(const u8 *buf, u32 len, u64 push_va,
off += span * 4u;
continue;
}
+ page_bits |= 1ull << ((off >> 12) & 63u);
sig = svc_mme_run_sig(buf + off, span, hdr);
for (i = 0; i < svc_mme_prelude_sig_n; i++) {
if (svc_mme_prelude_sigs[i] == sig) {
@@ -14572,10 +14978,10 @@ static void svc_mme_prelude_capture(const u8 *buf, u32 len, u64 push_va,
u32 k;
svc_mme_prelude_dump_n++;
- pr_info("sema-mme-prelude: capture h=0x%x va=0x%llx len=%u added=%u words=%u gen=%u (was %u)\n",
+ pr_info("sema-mme-prelude: capture h=0x%x va=0x%llx len=%u added=%u words=%u gen=%u (was %u) pages=0x%llx\n",
h_object, push_va, len, added,
svc_mme_prelude_n, svc_mme_prelude_gen,
- words0);
+ words0, (unsigned long long)page_bits);
for (k = 0; k + 4u <= svc_mme_prelude_n &&
k < 512u; k += 4u)
pr_info("sema-mme-prelude[%03u] %08x %08x %08x %08x\n",
@@ -14653,12 +15059,23 @@ static u64 svc_attr_prelude_ptr[SVC_ATTR_PRELUDE_PTRS];
static u32 svc_attr_prelude_ptr_n;
module_param_named(sema_attr_prelude_cap, svc_attr_prelude_flag, uint, 0644);
MODULE_PARM_DESC(sema_attr_prelude_cap,
- "Capture the client's attr/pipeline method runs (0x1100..0x11fc, 0x2000..0x205c) from trusted beef pushes for the GR launch builder's preplay ahead of the first 3D window (default 1)");
+ "Capture the client's attr/pipeline method runs (0x1100..0x11fc, 0x2000..0x21fc) from trusted beef pushes for the GR launch builder's preplay ahead of the first 3D window (default 1)");
static bool svc_attr_setup_run_addr(u32 addr)
{
+ /*
+ * SESSION 153-CONTINUED-86: the pipeline family extends across ALL
+ * eight pipeline records (SET_PIPELINE_SHADER/BINDING/PROGRAM_ADDRESS
+ * for j=0..7 = 0x2000..0x21fc), not just j=0..1. The r1273 scrape of
+ * the client's own push image proved the guest driver binds stage 2
+ * (TESS_CONTROL, VA 0x4400200) and stage 5 (PIXEL, VA 0x4400000) with
+ * the same pair runs; the old 0x2060 cut-off dropped those binds, the
+ * replay channel's stages 2/5 kept stale/zero program addresses, and
+ * the draw's SPH validation failed for the stages it could not fetch
+ * ("Shader Program Header 9/11/18 Error", DS_HWW_ESR 0xa2040a00).
+ */
return (addr >= 0x1100u && addr < 0x1200u) ||
- (addr >= 0x2000u && addr < 0x2060u);
+ (addr >= 0x2000u && addr < 0x2200u);
}
const u32 *gsp_sema_attr_prelude_get(u32 *n, u32 *gen)
@@ -14736,7 +15153,7 @@ static void svc_attr_prelude_capture(const u8 *buf, u32 len, u64 push_va,
* lower(31:0)]: remember the program VA so its page can join
* the launch page set (see the header comment).
*/
- if (addr >= 0x2014u && addr < 0x2060u &&
+ if (addr >= 0x2014u && addr < 0x2200u &&
(addr & 0x3fu) == 0x14u && span >= 3u) {
u32 v0 = 0, v1 = 0;
@@ -26971,6 +27388,10 @@ struct svc_fwd_req {
u64 gr_rel_pa;
u8 gr_rel_ap; /* SVC_APERTURE_* of gr_rel_pa */
u64 gr_prog_va;
+ /* CONT-87: the window's own program binds (see svc_gr_cand_rec). */
+ u32 gr_n_prog_binds;
+ u64 gr_prog_bind_va[SVC_GR_CAND_PROG_BINDS];
+ u32 gr_prog_bind_slot[SVC_GR_CAND_PROG_BINDS];
u64 gr_param_va;
u32 gr_param_size;
u32 gr_qmd[SVC_GR_QMD_WORDS];
@@ -29767,6 +30188,19 @@ static void svc_fwd_gr_work(void)
gs.stream = svc_fwd_req.gr_raw + svc_fwd_req.gr_stream_off;
gs.stream_n = svc_fwd_req.gr_stream_n;
}
+ gs.prog_va = svc_fwd_req.gr_prog_va; /* CONT-87: family binds */
+ gs.n_prog_binds = svc_fwd_req.gr_n_prog_binds;
+ {
+ u32 pb;
+
+ for (pb = 0; pb < svc_fwd_req.gr_n_prog_binds && pb < 8u;
+ pb++) {
+ gs.prog_bind_va[pb] =
+ svc_fwd_req.gr_prog_bind_va[pb];
+ gs.prog_bind_slot[pb] =
+ svc_fwd_req.gr_prog_bind_slot[pb];
+ }
+ }
gs.h_object = svc_fwd_req.gr_h_object;
/*
@@ -32223,6 +32657,18 @@ static int svc_gr_req_fill(struct nv_vgpu_device *vgpu,
svc_fwd_req.gr_stream_3d = svc_gr_cand.stream_3d;
svc_fwd_req.gr_stream_off = svc_gr_cand.stream_off;
svc_fwd_req.gr_stream_n = svc_gr_cand.stream_n;
+ svc_fwd_req.gr_n_prog_binds = svc_gr_cand.n_prog_binds;
+ {
+ u32 pb;
+
+ for (pb = 0; pb < svc_gr_cand.n_prog_binds &&
+ pb < SVC_GR_CAND_PROG_BINDS; pb++) {
+ svc_fwd_req.gr_prog_bind_va[pb] =
+ svc_gr_cand.prog_bind_va[pb];
+ svc_fwd_req.gr_prog_bind_slot[pb] =
+ svc_gr_cand.prog_bind_slot[pb];
+ }
+ }
memcpy(svc_fwd_req.gr_raw, svc_gr_cand.raw,
sizeof(svc_fwd_req.gr_raw));
svc_fwd_req.gr_qmd_dev_va = svc_gr_cand.qmd_dev_va;
@@ -39405,6 +39851,23 @@ int gsp_sema_service_dump_channels(void)
#define SVC_PIN_REWIND 8
#define SVC_MAP_ADD_MAX 4096
+/*
+ * SESSION 153-CONTINUED-84: optional length guard for injected mappings.
+ * A stale producer on the host (observed 2026-11-01: a periodic writer
+ * enqueueing (0x4001004,0x11f168000,0x10000) every ~2 s from an unknown
+ * leftover process) re-applies a whole-64K contiguous mapping over the
+ * per-page SA-carrier pins and can win the read of the client's push
+ * region, leaving every page past the first reading zeros (r1240/r1260:
+ * the replayed prelude captured only the first 15 SA slots while r1236/
+ * r1245/r1250 captured all 55). When set, requests longer than this many
+ * bytes are refused with a bounded log, so only per-page (4K) pins land.
+ * 0 = accept any length (the historical behaviour).
+ */
+static u32 gsp_sema_map_add_max;
+module_param_named(sema_map_add_max, gsp_sema_map_add_max, uint, 0644);
+MODULE_PARM_DESC(sema_map_add_max,
+ "Refuse injected mapping requests longer than this many bytes (0 = off; set 4096 to defeat stale whole-window injections)");
+
struct svc_map_add_req {
u64 va;
u64 pa;
@@ -39488,6 +39951,24 @@ static void svc_handle_map_add(void)
svc_map_add_tail++;
spin_unlock_irqrestore(&svc_map_add_lock, flags);
+ /*
+ * SESSION 153-CONTINUED-84: refuse oversized requests when
+ * the guard is set (see the sema_map_add_max comment) - the
+ * page pins from pin_loop2 (len 0x1000) are unaffected.
+ */
+ if (READ_ONCE(gsp_sema_map_add_max) &&
+ req.len > (u64)READ_ONCE(gsp_sema_map_add_max)) {
+ static u32 map_rej_log;
+
+ if (map_rej_log < 8) {
+ map_rej_log++;
+ pr_info("sema-svc: injected mapping va=0x%llx pa=0x%llx len=0x%llx REFUSED (len > sema_map_add_max=%u)\n",
+ req.va, req.pa, req.len,
+ READ_ONCE(gsp_sema_map_add_max));
+ }
+ continue;
+ }
+
ap = req.ap ? req.ap : SVC_APERTURE_SYS;
for (off = 0; off < (req.len ? req.len : 1);
off += SEMA_PAGE_SIZE) {
@@ -39511,7 +39992,7 @@ static void svc_handle_map_add(void)
*/
if (svc_va_hits_chan_ring(va)) {
svc_ring_pin_skips++;
- if (svc_ring_pin_skips <= 8)
+ if (svc_ring_pin_skips <= 64)
pr_info("sema-svc: injected pin va=0x%llx DROPPED (live channel ring range; ring reads stay on the channel's own translation)\n",
va);
continue;
diff --git a/kernel/gsp_sema_service.h b/kernel/gsp_sema_service.h
index 257a0ef..0f22904 100644
--- a/kernel/gsp_sema_service.h
+++ b/kernel/gsp_sema_service.h
@@ -535,6 +535,16 @@ bool gsp_sema_gr_surf_recs(u64 *va, u32 *n);
u64 gsp_sema_mme_sa_get(u32 *entries, u32 n);
/*
+ * SESSION 153-CONTINUED-87: the SPH VERSION of the live program images
+ * (sema_gl_vs/ps_hex blobs), so the GR launch builder emits a
+ * SET_SPH_VERSION window that MATCHES the images the engine will fetch
+ * (the client's own init push says 4/4, but the r950 gate proved the V3
+ * images execute; a 4/4 window over a V3 SPH reproduces the
+ * SPH9|SPH11|SPH18 fault). 0 when no image is loaded.
+ */
+int gsp_sema_gl_sph_blob_version(void);
+
+/*
* SESSION 153-CONTINUED-68: furthest IR instruction covered by the CAPTURED
* 0x114 uploads (pc + ceil(code words / 3)). The GR launch builder places
* its end-noop macro stub at this address (aligned past the replayed