summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_phys_booter.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gsp_phys_booter.c')
-rw-r--r--kernel/gsp_phys_booter.c159
1 files changed, 156 insertions, 3 deletions
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
@@ -17204,6 +17204,26 @@ 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
* emitted once on the first beef launch (gen=1, 512 words), that launch
@@ -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);
}
}
}
@@ -17436,6 +17524,40 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
}
}
/*
+ * 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
* runs after the MME prelude and ahead of the window,
@@ -20017,6 +20139,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
* alias base | offset. A one-page shadow relocation of it would