From f8f0a86a07949f913e546de4459c779a9ccbc3f3 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Sat, 31 Oct 2026 07:21:28 -0400 Subject: vgsp+diag: SA-RAM table captured unbounded (entry[64], sema-mme-sa log), surf geometry accessor + completeness recs, fill seeds RT candidate 0 full span (<=1024 pages, completeness only on fully-resolved run); MME slot stub (called-but-uncaptured slots end-noop IR 0xC0, sema_gl_mme_sa_stub=1), reloc prefers tracked rec bounce VA; pin family += 0x4200000 - 0x238c wall root-caused (capture truncated buffer full=28 words=512, SA[0x22]=0 jump IR 0; sim proves slot-0 emits EMIT 0x238c=0x20020); fixes never executed (setup-push read lost, unreadable=1 first8=00040000 00001000); next: trustworthy pagemap resolve or accept ipin first-chunk - SESSION 153 r1117-r1120 --- kernel/gsp_phys_booter.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 3 deletions(-) (limited to 'kernel/gsp_phys_booter.c') diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index ad8f9aa..cf6e33c 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -17203,6 +17203,26 @@ module_param_named(sema_gl_mme_prelude, gsp_phys_rmcall_gr_mme_prelude, bool, MODULE_PARM_DESC(sema_gl_mme_prelude, "Preplay the captured client MME context-setup runs once per capture generation ahead of the first 3D window replay (default 1)"); +/* + * SESSION 153-CONTINUED-63: MME START-ADDRESS RAM completion + stub knob. + * The captured SA table (svc_mme_sa_*) may be incomplete (the prelude + * buffer is capped at 512 words); every CALLED slot the table does not + * cover is loaded with a harmless end-noop stub at a fixed IR address so + * the call becomes a no-op instead of a jump to IR 0 with garbage state + * (the r1116 0x238c poison). Default 1; A/B with 0. + */ +#define GSP_PHYS_RMCALL_GR_MME_STUB_PC 0xC0u +static bool gsp_phys_rmcall_gr_sa_stub = true; +module_param_named(sema_gl_mme_sa_stub, gsp_phys_rmcall_gr_sa_stub, bool, + 0644); +MODULE_PARM_DESC(sema_gl_mme_sa_stub, + "Load a harmless end-noop stub into every MME macro slot the window calls but the captured START-ADDRESS RAM table does not cover (default 1; the r1116 0x238c lane poison class)"); + +static inline bool gsp_sema_gr_sa_stub_value(void) +{ + return READ_ONCE(gsp_phys_rmcall_gr_sa_stub); +} + /* * SESSION 153-CONTINUED-61: generation of the MME prelude already emitted * into the engine context (0 = none). The r1110 evidence: the prelude was @@ -17366,17 +17386,85 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, bool emit_prelude = false; const u32 *pl = NULL; u32 pl_n = 0, pl_gen = 0; + /* + * SESSION 153-CONTINUED-63: MME START-ADDRESS RAM completion. + * The prelude capture is capped at 512 words, so the client's + * SA table (macro slot -> IR address) may be incomplete; on a + * fresh context the missing slots read 0 and CALL_MME_MACRO(j) + * jumps to IR 0 - the slot-0 macro - which with the caller's + * args emitted LOAD_CONSTANT_BUFFER_OFFSET <- 0x20020 and + * poisoned the lane (r1116; verified offline with the MME64 + * simulator). Stubbing every CALLED slot the capture's SA + * table does not cover with a harmless end-noop makes the + * unknown call a no-op instead of a lane-killer. The called + * slots come from the window being emitted (IMMD writes to + * 0x3800+j*8 and INC/ONEINC runs writing any 0x3800..0x39f8 + * method, the .Macro half of each pair). + */ + u32 sa_ent[64]; + u64 sa_mask = 0; + u32 sa_stub[24]; + u32 sa_nstub = 0, sa_block = 0; + + if (gsp_sema_gr_sa_stub_value() && win3d && win3d_n) { + u32 so = 0; + + sa_mask = gsp_sema_mme_sa_get(sa_ent, 64); + while (so < win3d_n && sa_nstub < 24u) { + u32 h = win3d[so]; + u32 sop = (h >> 29) & 7u; + u32 scnt = (h >> 16) & 0x1fffu; + u32 sspan = 1u; + u32 k, slo, shi; + + if ((sop == 1u || sop == 3u || sop == 5u) && + scnt && so + 1u + scnt <= win3d_n) { + sspan = 1u + scnt; + slo = 0; + shi = scnt - 1u; + } else if (sop == 4u) { + slo = 0; + shi = 0; + } else { + so++; + continue; + } + for (k = slo; k <= shi && sa_nstub < 24u; k++) { + u32 mthd = ((h & 0xfffu) << 2) + 4u * k; + u32 j, t; + bool have = false; + + if (mthd < 0x3800u || mthd >= 0x3a00u || + (mthd & 7u)) + continue; + j = (mthd - 0x3800u) / 8u; + if (j >= 64u) + continue; + if (sa_mask & (1ull << j)) + continue; + for (t = 0; t < sa_nstub; t++) + if (sa_stub[t] == j) + have = true; + if (!have) + sa_stub[sa_nstub++] = j; + } + so += sspan; + } + if (sa_nstub) + sa_block = 5u + 3u * sa_nstub; + } if (READ_ONCE(gsp_phys_rmcall_gr_mme_prelude)) { pl = gsp_sema_mme_prelude_get(&pl_n, &pl_gen); if (pl && pl_n && pl_gen && pl_gen != READ_ONCE(gsp_phys_gr_mme_prelude_gen)) { - if (win3d_n + 2u + 8u + pl_n <= + if (win3d_n + 2u + 8u + pl_n + sa_block <= GSP_PHYS_RMCALL_GR_METHOD_WORDS) { emit_prelude = true; } else { - pr_info("vgsp-gr: MME-PRELUDE deferred (window=%u prelude=%u exceeds block; replaying window alone) h=0x%x\n", - win3d_n, pl_n, req->h_object); + 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); } } } @@ -17435,6 +17523,40 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, g0, g1, pl_n); } } + /* + * 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 + * the capture's SA table does not cover (see the sa_stub + * comment at the emit decision). Emitted with the prelude (same + * generation latch), so it lands once per capture generation + * and the context keeps it for every later window. + */ + if (emit_prelude && sa_block) { + u32 t; + + *p++ = ((u32)GSP_PHYS_RMCALL_METHOD_IMM << 29) | + (4u << 16) | (0x114u >> 2); + *p++ = GSP_PHYS_RMCALL_GR_MME_STUB_PC; + *p++ = 0u; + *p++ = 0u; + *p++ = 1u; /* end_next, no emits (harmless) */ + /* + * Each [slot, stub] pair is its own INC 0x11c run: the + * pair methods are re-written per slot (a single longer + * run would walk onto 0x124 SetMmeShadowRamControl and + * beyond). + */ + for (t = 0; t < sa_nstub; t++) { + *p++ = ((u32)GSP_PHYS_RMCALL_METHOD_IMM << 29) | + (2u << 16) | (0x11cu >> 2); + *p++ = sa_stub[t]; + *p++ = GSP_PHYS_RMCALL_GR_MME_STUB_PC; + } + pr_info("vgsp-gr: MME-SA-STUB slots=%u words=%u h=0x%x push=0x%llx\n", + sa_nstub, sa_block, req->h_object, + req->push_va); + } /* * SESSION 153-CONTINUED-57: attr/pipeline state prelude (see the * sema_gl_attr_prelude knob above). Emit the patched captured @@ -20016,6 +20138,37 @@ static bool gsp_phys_rmcall_gr_3d_reloc(u32 *up_w, u32 *lo_w, if (!va || !page) return false; + /* + * SESSION 153-CONTINUED-63: rec-over-alias preference. When the fill + * tracked the render target through the bounce lane (every page + * resolved into contiguous data recs), rewrite the operand to the + * rec's bounce VA so the engine's stores land where the retire wait + * can count them and the copy-out can ship them back to the guest's + * own pages. The completeness gate (gsp_sema_gr_surf_recs) makes a + * partial run impossible to point at. Runs before the alias so the + * identity alias (direct guest writes, invisible to the witness) + * cannot win for a tracked target. + */ + { + u64 sva = 0; + u32 sn = 0; + + if (gsp_sema_gr_surf_recs(&sva, &sn) && + va >= sva && va < sva + ((u64)sn << 12)) { + for (i = 0; i < req->n_data; i++) { + if (req->data[i].va != page || + !req->data[i].reloc_va) + continue; + sh = req->data[i].reloc_va | + (va & (GSP_PHYS_PAGE_SIZE - 1)); + *lo_w = (*lo_w & 0x3u) | + ((u32)sh & 0xfffffffcu); + *up_w = (*up_w & ~0xffu) | + ((u32)(sh >> 32) & 0xffu); + return true; + } + } + } /* * SESSION 153-CONTINUED-27: a big surface (the X screen) is aliased * as ONE contiguous run by the pre-pass; rewrite the operand to the -- cgit v1.2.3