diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-31 07:21:28 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-31 07:21:28 -0400 |
| commit | f8f0a86a07949f913e546de4459c779a9ccbc3f3 (patch) | |
| tree | 01d80fcfa750aa7bfd49aa3ea031c9aca75b3d00 /kernel | |
| parent | df072897fef8421c7f41d7a9ebc9f3415210b402 (diff) | |
| download | open-iov-f8f0a86a07949f913e546de4459c779a9ccbc3f3.tar.gz open-iov-f8f0a86a07949f913e546de4459c779a9ccbc3f3.zip | |
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
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/gsp_phys_booter.c | 159 | ||||
| -rw-r--r-- | kernel/gsp_sema_service.c | 191 | ||||
| -rw-r--r-- | kernel/gsp_sema_service.h | 28 |
3 files changed, 375 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 diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 4d32caa..856947f 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -14052,6 +14052,45 @@ module_param_named(sema_mme_prelude_cap, svc_mme_prelude_flag, uint, 0644); MODULE_PARM_DESC(sema_mme_prelude_cap, "Capture the client's MME context-setup method runs (0x114..0x124, 0x234, 0x550..0x568, 0x3400..0x34fc) from trusted beef pushes for the GR launch builder's one-time preplay (default 1)"); +/* + * SESSION 153-CONTINUED-63: the MME START-ADDRESS RAM table (macro slot -> + * IR instruction address), captured unbounded by the 512-word prelude + * buffer. The r1116 0x238c wall proved the replay's SA table was + * incomplete (the capture buffer filled with the first 11 macros' code and + * 28 further setup runs - the remaining slots - never entered the prelude): + * the window's CALL_MME_MACRO(0x22) then read SA[0x22] = 0 on the fresh + * context, the MME jumped to IR 0 (the slot-0 macro, simulating with the + * caller's args emits LOAD_CONSTANT_BUFFER_OFFSET <- 0x20020; verified + * with the offline MME64 simulator), and the class error poisoned the + * lane. The builder uses this table to complete the SA RAM it replays and + * to stub every called-but-unknown slot with a harmless end-noop. + */ +static u64 svc_mme_sa_present; +static u32 svc_mme_sa_entry[64]; +static u32 svc_mme_sa_log; + +u64 gsp_sema_mme_sa_get(u32 *entries, u32 n) +{ + u32 i; + + if (entries) + for (i = 0; i < n && i < 64u; i++) + entries[i] = svc_mme_sa_entry[i]; + return READ_ONCE(svc_mme_sa_present); +} + +static void svc_mme_sa_record(u32 slot, u32 entry) +{ + if (slot >= 64u) + return; + if (!(svc_mme_sa_present & (1ull << slot)) && svc_mme_sa_log < 64) { + svc_mme_sa_log++; + pr_info("sema-mme-sa: slot[%u] entry=0x%x\n", slot, entry); + } + svc_mme_sa_entry[slot] = entry; + svc_mme_sa_present |= 1ull << slot; +} + static bool svc_mme_setup_run_addr(u32 addr) { return (addr >= 0x114u && addr <= 0x124u) || addr == 0x234u || @@ -14107,6 +14146,25 @@ static void svc_mme_prelude_capture(const u8 *buf, u32 len, u64 push_va, break; span = 1u + cnt; } + /* + * SESSION 153-CONTINUED-63: record the START-ADDRESS RAM + * pairs independent of the prelude buffer's 512-word bound + * (see svc_mme_sa_present). The client's LoadMmeStartAddress + * Ram pair run is INC 0x11c with [slot, IR-address] pairs; a + * second run for the same slots overwrites (last wins), the + * same order the engine would see. + */ + if (addr == 0x11cu && cnt >= 2u && span > 1u) { + u32 k; + + for (k = 0; k + 1u < cnt; k += 2u) { + u32 sv, ev; + + memcpy(&sv, buf + off + 4u + 4u * k, 4); + memcpy(&ev, buf + off + 8u + 4u * k, 4); + svc_mme_sa_record(sv, ev); + } + } if (!svc_mme_setup_run_addr(addr)) { off += span * 4u; continue; @@ -14396,6 +14454,15 @@ static u32 svc_surf_pre_gen; static u64 svc_surf_pre_sig; static u32 svc_surf_pre_log; static u32 svc_surf_pre_flag = 1; +/* + * SESSION 153-CONTINUED-63: the captured colour target's geometry, for the + * fill's render-target tracking (see svc_gr_data_fill and + * gsp_sema_gr_surf_recs): the fill seeds the surface base VA as a tracked + * data candidate so the engine's stores into the target become a retire + * witness and the copy-out ships them back to the guest's own pages. + */ +static u64 svc_surf_pre_va; +static u32 svc_surf_pre_npages; module_param_named(sema_surf_prelude_cap, svc_surf_pre_flag, uint, 0644); MODULE_PARM_DESC(sema_surf_prelude_cap, "Capture the last C597 colour-target bind (INC 0x800+j*0x40, fmt 0xe6) from trusted pushes so the GR launch builder can replay it ahead of 3D windows that carry no render target (default 1)"); @@ -14409,6 +14476,19 @@ const u32 *gsp_sema_surf_prelude_get(u32 *n, u32 *gen) return svc_surf_pre_buf; } +bool gsp_sema_surf_va_get(u64 *va, u32 *npages) +{ + u32 n = READ_ONCE(svc_surf_pre_npages); + + if (!n) + return false; + if (va) + *va = svc_surf_pre_va; + if (npages) + *npages = n; + return true; +} + static void svc_surf_prelude_capture(const u8 *buf, u32 len) { u32 off = 0; @@ -14458,6 +14538,8 @@ static void svc_surf_prelude_capture(const u8 *buf, u32 len) memcpy(&svc_surf_pre_buf[k], buf + off + 4u * k, 4); WRITE_ONCE(svc_surf_pre_n, n); svc_surf_pre_sig = sig; + svc_surf_pre_va = va; + WRITE_ONCE(svc_surf_pre_npages, (u32)npages); WRITE_ONCE(svc_surf_pre_gen, svc_surf_pre_gen + 1); if (svc_surf_pre_log < 8) { svc_surf_pre_log++; @@ -29499,6 +29581,34 @@ static bool svc_gr_page_inline_covers(u64 page) */ #define SVC_GR_DATA_BOUNCE_MAX (GSP_PHYS_RMCALL_GR_DATA_BOUNCE_PAGES / 2u) #define SVC_GR_DATA_BOUNCE_MAX_OLD 205u +/* + * SESSION 153-CONTINUED-63: render-target page tracking (see + * gsp_sema_surf_va_get above). The captured colour target is seeded as a + * tracked data candidate so (a) GR-DEC n_data >= 1, (b) the retire wait + * counts the engine's stores into the target ("N/N pages changed") and + * (c) the copy-out ships the changed pages back to the guest's own screen + * pages. Bounded to one full 1280x720 RGB surface plus slack; the + * completeness record (va + n) arms the booter's rec-over-alias operand + * preference only when EVERY page resolved, so a partial span can never + * hand the engine a base pointer walking past the tracked run. + */ +#define SVC_GR_SURF_TRACK_MAX 1024u +static u64 svc_gr_surf_rec_va; +static u32 svc_gr_surf_rec_n; +static u32 svc_gr_surf_rec_ok; +static u32 svc_gr_surf_rec_log; + +bool gsp_sema_gr_surf_recs(u64 *va, u32 *n) +{ + if (!READ_ONCE(svc_gr_surf_rec_ok)) + return false; + if (va) + *va = svc_gr_surf_rec_va; + if (n) + *n = svc_gr_surf_rec_n; + return true; +} + static u32 svc_gr_data_log; /* m142: bounded per-page attribution log */ static u32 svc_gr_src_log; /* m150: bounded per-candidate source log */ @@ -30387,6 +30497,33 @@ static void svc_gr_data_fill(struct nv_vgpu_device *vgpu, */ n_cand = 0; /* + * SESSION 153-CONTINUED-63: the captured colour target (the surface + * the replayed draw rasterizes into) becomes a tracked data + * candidate FIRST, so its pages are in the launch's data set even + * when the window/content scans fill every other slot (the beef + * windows' 24 scan candidates are all unresolvable junk). Odd: the + * copy-in stages the guest page into the bounce, the engine's + * stores land in the bounce, the retire wait sees them (GR-DEC + * n_data>=1 -> "N/N pages changed") and the copy-out writes the + * changed pages back to the guest's own pages. Completeness is + * recorded for the booter's rec-over-alias preference. + */ + u64 surf_cand_va = 0; + u32 surf_cand_span = 0; + bool surf_cand_used = false; + + WRITE_ONCE(svc_gr_surf_rec_ok, 0); + if (gsp_sema_surf_va_get(&surf_cand_va, &surf_cand_span)) { + if (surf_cand_span > SVC_GR_SURF_TRACK_MAX) + surf_cand_span = SVC_GR_SURF_TRACK_MAX; + if (surf_cand_va && !(surf_cand_va & (SEMA_PAGE_SIZE - 1)) && + surf_cand_span && n_cand < SVC_GR_CAND_MAX) { + wins[n_cand] = surf_cand_va; + is_arg[n_cand] = false; + n_cand++; + } + } + /* * m121: kernel-argument pointers first. The launched kernel's device * pointers live in the param page (qmd_word 32, c[0x0]) at byte * offsets >= 0x150 (m120: mail_kernel out@0x160, msg@0x168; vadd @@ -30777,6 +30914,18 @@ static void svc_gr_data_fill(struct nv_vgpu_device *vgpu, continue; } span = (base >= 0x40000000000ULL) ? per_ptr : 1u; + /* + * SESSION 153-CONTINUED-63: the render-target candidate keeps + * its full surface span (it is a low VA, so the generic span + * rule above would give it ONE page and the engine's raster + * walk would leave the tracked run at the first page + * crossing). surf_cand_used marks the pass so the tail can + * record completeness only for the run that actually filled. + */ + if (surf_cand_va && base == surf_cand_va) { + span = surf_cand_span; + surf_cand_used = true; + } if (is_arg[w] && base >= 0x40000000000ULL) n_arg_hi++; @@ -30958,6 +31107,19 @@ static void svc_gr_data_fill(struct nv_vgpu_device *vgpu, svc_log_stalled_va(va); if (is_arg[w]) svc_fwd_req.gr_arg_partial = true; + /* + * SESSION 153-CONTINUED-63: a + * render-target page that does + * not resolve must NOT hold + * the launch (the target is + * an output, not an operand; + * its alias fallback keeps + * today's behaviour), and the + * broken span must not be + * recorded as complete. + */ + if (surf_cand_va && wins[w] == surf_cand_va) + break; if (dropped_pages < 4) { drop_va[dropped_pages] = va; drop_kind[dropped_pages] = @@ -31180,6 +31342,35 @@ static void svc_gr_data_fill(struct nv_vgpu_device *vgpu, (n_arg_hi > 0 && n_arg_hi_res == 0); svc_fwd_req.gr_dropped = dropped_pages; /* + * SESSION 153-CONTINUED-63: record the render-target tracking run - + * only when the pass actually seeded it AND every requested page + * produced a data rec contiguous from the base (the booter then + * rewrites the target's operand pair to the bounce base; a partial + * run falls back to the identity alias so the engine can never walk + * past the tracked pages). + */ + if (surf_cand_used) { + u32 hit = 0; + + for (i = 0; i < svc_fwd_req.gr_n_data; i++) { + u64 rva = svc_fwd_req.gr_data[i].va; + + if (rva >= surf_cand_va && + rva < surf_cand_va + + ((u64)surf_cand_span << 12)) + hit++; + } + if (hit == surf_cand_span) { + svc_gr_surf_rec_va = surf_cand_va; + svc_gr_surf_rec_n = surf_cand_span; + WRITE_ONCE(svc_gr_surf_rec_ok, 1); + } else if (svc_gr_surf_rec_log < 16) { + svc_gr_surf_rec_log++; + pr_info("vgsp-sema: surf-rec incomplete va=0x%llx want=%u got=%u (target falls back to the identity alias)\n", + surf_cand_va, surf_cand_span, hit); + } + } + /* * GRFIX FIX1e (r215): gsp_phys_gr_fill_hold is the master gate. * Default 0 = LOG-ONLY: the fill NEVER holds on dropped pages - the * stalled verdict keeps only the historical pre-8b058884 aggregate diff --git a/kernel/gsp_sema_service.h b/kernel/gsp_sema_service.h index 0f32265..2b99274 100644 --- a/kernel/gsp_sema_service.h +++ b/kernel/gsp_sema_service.h @@ -507,6 +507,34 @@ const u64 *gsp_sema_attr_prelude_ptrs(u32 *n); const u32 *gsp_sema_surf_prelude_get(u32 *n, u32 *gen); /* + * SESSION 153-CONTINUED-63: surface geometry for render-target tracking + * (base VA + page count of the captured colour target). + */ +bool gsp_sema_surf_va_get(u64 *va, u32 *npages); + +/* + * SESSION 153-CONTINUED-63: the fill's completed render-target tracking run + * (base VA + page count), true only when every page of the surface resolved + * into contiguous data recs this pass. The booter prefers these recs over + * the identity surface alias when rewriting the target's operand pair, so + * the engine's stores land in the bounce (witness + copy-out) instead of + * the guest pages directly. + */ +bool gsp_sema_gr_surf_recs(u64 *va, u32 *n); + +/* + * SESSION 153-CONTINUED-63: the captured MME START-ADDRESS RAM table + * (macro slot -> IR instruction address; present-bitmask returned, entries + * copied into `entries`). The prelude buffer is capped at 512 words, so + * the SA table is recorded independently; the GR launch builder completes + * the SA RAM it replays and stubs every called-but-unknown slot with a + * harmless end-noop (the r1116 0x238c wall: SA[0x22] = 0 on a fresh + * context made CALL_MME_MACRO(0x22) jump to IR 0 and the slot-0 macro's + * emission trapped the lane). + */ +u64 gsp_sema_mme_sa_get(u32 *entries, u32 n); + +/* * r378 write guards. Both re-validate a guest-memory write target that was * resolved earlier (ledger/pin/walk/counter registration, or the GR submit's * per-page resolution) against the CURRENT resolution before the bytes land, |
