diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-31 13:12:31 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-31 13:12:31 -0400 |
| commit | 8528df77141cb6f6ee6b2f4af1cc8f6bfa91a2c3 (patch) | |
| tree | 1f5b09465b5d5921511ea7f999077afd6e991877 | |
| parent | 03ee5bbe4a60c80627e21d3446b6b787d6eae004 (diff) | |
| download | open-iov-8528df77141cb6f6ee6b2f4af1cc8f6bfa91a2c3.tar.gz open-iov-8528df77141cb6f6ee6b2f4af1cc8f6bfa91a2c3.zip | |
vgsp: Mesa mme_tu104_builder_finish terminator stub body (old body emitted SET_OBJECT(0)<-0 twice = Class 0xc597 Offset 0 Data 0 poison), ONEINC cnt 7, sa_block 8+3n; sema_mme_fast_ms pre-reset MME sample (spin ESR/INFO/INFO2/DS/grfifo/trap right after doorbell; poison floods PRI in <6ms, raw latch never visible); sema_gl_mme_stub_pc override; sema_gl_mme_call_drop=1 (drop stubbed-slot CALL runs) - DRAW-CLASS WINDOW RETIRES: MME-CALL-DROP 164 words dropped (window=220), poll break ch=0xc0ffee02 after 94us, 11 poll breaks 0 ILLEGAL_OPCODE 0 WEDGE-RECOVER, ok=11; stores still 0 (0/900 no writes); next: re-enable real macro execution one slot at a time (sim covered macro offline first) - SESSION 153 r1142-r1144
| -rw-r--r-- | kernel/gsp_phys_booter.c | 304 |
1 files changed, 288 insertions, 16 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index ed1cb0f..07c63ee 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -5067,6 +5067,97 @@ static void gsp_phys_mme_regs_log(void __iomem *bar0, const char *tag) (info >> 12) & 0xffu, info2, trapped); } +/* + * SESSION 153-CONTINUED-69: pre-reset MME fault capture. Everything sampled + * so far was post-mortem: by the time the 5 ms launch poll ticks, the + * GSP-RM's FECS exception handler has already read NV_PGRAPH_PRI_MME_HWW_ESR + * (0x404490) - the relayed errString "ESR 0x404490=0x80000004" is the only + * surviving trace of the raw ILLEGAL_OPCODE value - and either cleared the + * latch back to 0x80000000 or left the whole PRI block in the 0xbadfxxxx + * priv-error poison. This bounded spin runs in the submit thread immediately + * after the publish/doorbell (execution starts within the first + * milliseconds) and records EVERY transition of the fault registers at + * MMIO read rate, so the faulting ESR (+INFO pc/macro when the valid bit is + * set, and the instruction register INFO2 that names the rejected + * instruction word) is on the record before any clear. Read-only; bounded + * per boot and per launch (the spin costs at most sema_mme_fast_ms of the + * launch thread's time and stops early once the poison is seen). + */ +static u32 gsp_phys_mme_fast_ms = 400u; +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)"); + +static void 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; + ktime_t t_end; + bool poisoned = false; + + if (!bar0 || !ms || fast_boot_n >= 48u) + return; + /* + * Beef-family channels get the full budget (the draw-class windows + * are the ones with the MME fault); other channels are sampled only + * a few times so a fast X-channel launch train cannot consume it. + */ + if ((h_object & 0xfffff000u) != 0xbeef0000u && fast_boot_n >= 2u) + return; + fast_boot_n++; + esr_p = ioread32(bar0 + 0x00404490u); + 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); + 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); + + if (esr != esr_p || ds != ds_p || gf != gf_p || tr != tr_p) { + if (n < 12u) { + u32 info = ioread32(bar0 + 0x00404494u); + u32 info2 = ioread32(bar0 + 0x00404498u); + u32 mmeir = ioread32(bar0 + 0x0040a630u); + u32 mmedata = ioread32(bar0 + 0x00400708u); + + 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 fed0=%08x\n", + tag, h_object, esr_p, esr, + info, (info >> 28) & 1u, + info & 0xfffu, + (info >> 12) & 0xffu, info2, + ds_p, ds, gf_p, gf, tr, + mmedata, mmeir); + } + n++; + esr_p = esr; + ds_p = ds; + gf_p = gf; + tr_p = tr; + /* + * The priv-error poison is the terminal state: the + * FECS has already logged and the rest of the + * registers are 0xbadfxxxx. Stop early so the launch + * thread resumes its normal poll. + */ + if ((gf & 0xffff0000u) == 0xbadf0000u) { + poisoned = true; + break; + } + } + if (ktime_after(ktime_get(), t_end)) + break; + } + pr_info("vgsp-gr: MME-FAST end %s h=0x%x events=%u%s\n", + tag, h_object, n, poisoned ? " (poison)" : ""); +} + static void gsp_phys_pbdma_fault_dump(u32 chid) { static u32 dump_n; @@ -17338,18 +17429,25 @@ MODULE_PARM_DESC(sema_gl_mme_prelude, */ #define GSP_PHYS_RMCALL_GR_MME_STUB_PC 0xC0u /* - * SESSION 153-CONTINUED-68: the stub body. The r1134/r1136 builds used a - * bare [0, 0, 1] (end_next with every other bit zero) and the engine still - * raised ILLEGAL_OPCODE with pc_valid=0 - a decode failure at the macro's - * FIRST instruction, i.e. the bare END is not an encoding the MME accepts. - * This body is a byte-exact copy of a real compiler-emitted instruction - * from the client's own captured program (macro(0) instruction 3, - * 20208c00 38c40046 038c4301): both ALUs write the discarded ZERO - * register (no side effects), no outputs are emitted, end_next=1 - a - * known-good macro terminator. - */ -static const u32 gsp_phys_gr_mme_stub_code[3] = { - 0x20208c00u, 0x38c40046u, 0x038c4301u + * SESSION 153-CONTINUED-69: the stub body, take 3. The CONT-68 body + * (20208c00 38c40046 038c4301) was copied from macro(0) instruction 3 - + * but that is a MID-MACRO instruction: the offline MME64 simulator shows + * it carries two output writes with mthd=NONE, so in isolation (method + * register still 0) it EMITS SET_OBJECT <- 0 twice - exactly the relayed + * trap "ChID 0001, Class 0000c597, Offset 00000000, Data 00000000" - and + * its end_next=1 requires the following instruction, so the macro also + * runs past the stub into whatever the IR holds there. + * + * The real minimal terminator is what Mesa's mme_tu104_builder_finish() + * emits: two default NOPs with end_next set on the FIRST (end_next - + * "end after the next instruction" - so the second NOP executes and then + * the macro returns). Both NOPs encode as w2=0x318c0300 (pred=ZERO, + * ALU0/1 dst=ZERO src=ZERO,ZERO, outs NONE) and emit NOTHING. The + * end_next bit is w2 bit 0. + */ +static const u32 gsp_phys_gr_mme_stub_code[6] = { + 0x00000000u, 0x318c0000u, 0x318c0301u, /* NOP, end_next=1 */ + 0x00000000u, 0x318c0000u, 0x318c0300u, /* NOP tail (executed) */ }; static bool gsp_phys_rmcall_gr_sa_stub = true; module_param_named(sema_gl_mme_sa_stub, gsp_phys_rmcall_gr_sa_stub, bool, @@ -17375,6 +17473,113 @@ static inline bool gsp_sema_gr_sa_stub_value(void) } /* + * SESSION 153-CONTINUED-69: optional explicit stub IR address override. + * 0 = derive from the capture (ir_end, floor 0xC0). Nonzero pins the stub + * body/call target to this instruction address - the stub-landing A/B for + * the IR-boundary/ONEINC pointer-unit question. + */ +static u32 gsp_phys_rmcall_gr_mme_stub_pc; +module_param_named(sema_gl_mme_stub_pc, gsp_phys_rmcall_gr_mme_stub_pc, uint, + 0644); +MODULE_PARM_DESC(sema_gl_mme_stub_pc, + "Explicit MME stub IR instruction address (0 = derive from capture ir_end; try 0xf0/0x100 for the IR-boundary A/B)"); + +/* + * SESSION 153-CONTINUED-69: no-stall CALL drop. With the stub terminating + * macros as pure NOPs the macro consumes none of the CALL_MME_DATA + * parameter words the window pushes; the MME parameter port then + * backpressures on the window's larger calls - the r1142 stall parked the + * PBDMA mid-block with HDR_SHADOW = the 16-arg CALL_MME_MACRO(57) + * ONEINC run and the block never completed (while every <=4-arg window + * retired in ~100 us). Drop every CALL_MME_MACRO/CALL_MME_DATA run of a + * STUBBED slot from the emitted window instead: no call, no parameter + * words, no backpressure. Covered slots keep their calls (their real code + * consumes the parameters). Default 1; A/B with 0. + */ +static bool gsp_phys_rmcall_gr_call_drop = true; +module_param_named(sema_gl_mme_call_drop, gsp_phys_rmcall_gr_call_drop, bool, + 0644); +MODULE_PARM_DESC(sema_gl_mme_call_drop, + "Drop CALL_MME_MACRO/DATA runs of stubbed slots from emitted 3D windows (prevents the noop-stub parameter-port stall; default 1)"); + +/* + * 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 + * instruction the entry names never entered the replay. + */ +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; + if (READ_ONCE(gsp_phys_rmcall_gr_sa_stub_all)) + return true; + if (!(sa_mask & (1ull << j))) + return true; + return !gsp_sema_mme_ir_covered(sa_ent[j]); +} + +/* + * In-place filter for the relocated 3D window: remove every run that + * writes the CALL_MME_MACRO(j)/CALL_MME_DATA(j) method pair of a stubbed + * slot. Pure run-level removal (runs are self-contained: op 1/3/5 carry + * 1+cnt data words; 0/2/4/6 are single words), so the surviving method + * sequence is unchanged and both the F2A window walk and the engine see + * the same stream shape with the calls elided. + */ +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; + u64 dmask = 0; + + while (r < n) { + u32 h = win[r]; + u32 op = (h >> 29) & 7u; + u32 cnt = (h >> 16) & 0xfffu; + u32 span = 1u, k, m; + bool iscall = false; + + if ((op == 1u || op == 3u || op == 5u) && cnt && + r + 1u + cnt <= n) + span = 1u + cnt; + /* + * Decode the touched methods the way the engine walks them: + * multi-word runs land on m + 4k; single-word entries carry + * their method in the low bits (IMMD and the SDM family both + * put it at 11:0 for our purposes). + */ + for (k = 0; k < span; k++) { + m = ((h & 0xfffu) << 2) + 4u * k; + if (k && op != 1u && op != 3u) + break; + if (m >= 0x3800u && m <= 0x39fcu) { + u32 j = (m - 0x3800u) / 8u; + + if (gsp_phys_gr_slot_stubbed(j, sa_mask, + sa_ent)) { + iscall = true; + dmask |= 1ull << j; + break; + } + } + } + if (iscall) { + dropped += span; + r += span; + continue; + } + if (w != r) + memmove(&win[w], &win[r], (size_t)span * 4u); + w += span; + r += span; + } + if (dropped_mask) + *dropped_mask = dmask; + return dropped; +} + +/* * 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 @@ -17653,7 +17858,7 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, so += sspan; } if (sa_nstub) - sa_block = 5u + 3u * sa_nstub; + sa_block = 8u + 3u * sa_nstub; } /* @@ -17666,11 +17871,24 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, */ { static u32 win_dump_n; + static u32 win_dump_beef; - if (win3d && win3d_n && win_dump_n < 4u) { + /* + * SESSION 153-CONTINUED-69: the first-4 budget was + * consumed by the X channel's early windows in every + * boot, so the beef draw-class window (the one that + * faults) never got its run-header census. Two extra + * dumps are reserved for beef-family windows. + */ + if (win3d && win3d_n && + (win_dump_n < 4u || + (((req->h_object & 0xfffff000u) == 0xbeef0000u) && + win_dump_beef < 2u))) { u32 wo = 0, wr = 0; win_dump_n++; + if ((req->h_object & 0xfffff000u) == 0xbeef0000u) + win_dump_beef++; pr_info("vgsp-gr: MME-WINRUN dump h=0x%x push=0x%llx window=%u\n", req->h_object, req->push_va, win3d_n); while (wo < win3d_n && wr < 200u) { @@ -17789,17 +18007,35 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, if (stub_pc < GSP_PHYS_RMCALL_GR_MME_STUB_PC) stub_pc = GSP_PHYS_RMCALL_GR_MME_STUB_PC; /* + * SESSION 153-CONTINUED-69: explicit stub-PC override + * (sema_gl_mme_stub_pc). The ir_end-derived PC sits at + * the first instruction past the replayed program; if + * the MME's IR allocation/ONEINC pointer semantics + * differ (instruction vs word units, allocation + * boundary), the stub body may land where the engine + * does not look. This knob moves the stub (e.g. to + * 0xf0/0x100, or back to the 0xC0 floor) so the + * landing can be A/B-tested without a rebuild. + */ + if (READ_ONCE(gsp_phys_rmcall_gr_mme_stub_pc)) + stub_pc = READ_ONCE(gsp_phys_rmcall_gr_mme_stub_pc); + /* * The IR upload MUST use the client's own SEC_OP (5, * ONE_INC): with op=1 the method auto-increment walked * the data words onto 0x11c/0x120 and clobbered SA[0] - * while writing no usable stub code (r1131). + * while writing no usable stub code (r1131). count = 7 + * = 1 pointer + 6 code words (two 3-word NOPs; see the + * gsp_phys_gr_mme_stub_code comment). */ *p++ = (GSP_PHYS_RMCALL_METHOD_ONEINC << 29) | - (4u << 16) | (0x114u >> 2); + (7u << 16) | (0x114u >> 2); *p++ = stub_pc; *p++ = gsp_phys_gr_mme_stub_code[0]; *p++ = gsp_phys_gr_mme_stub_code[1]; *p++ = gsp_phys_gr_mme_stub_code[2]; + *p++ = gsp_phys_gr_mme_stub_code[3]; + *p++ = gsp_phys_gr_mme_stub_code[4]; + *p++ = gsp_phys_gr_mme_stub_code[5]; /* * Each [slot, stub] pair is its own INC 0x11c run: the * pair methods are re-written per slot (a single longer @@ -22913,6 +23149,32 @@ gr_page_unmapped: win3d = stream_patch; win3d_n = win_n; + /* + * SESSION 153-CONTINUED-69: drop the CALL_MME_MACRO/DATA runs + * of stubbed slots (see gsp_phys_gr_mme_drop_calls). With the + * stub as a pure NOP terminator the parameter port no longer + * drains; the r1142 stall parked the PBDMA on the 16-arg + * CALL_MME_MACRO(57) run and the block never completed. The + * drop turns those calls into nothing instead of a call whose + * parameters have nowhere to go. + */ + if (READ_ONCE(gsp_phys_rmcall_gr_call_drop) && + gsp_sema_gr_sa_stub_value()) { + u32 sa_ent[64]; + u64 sa_mask = gsp_sema_mme_sa_get(sa_ent, 64); + u64 dmask = 0; + u32 ndrop_c = gsp_phys_gr_mme_drop_calls( + stream_patch, win3d_n, sa_mask, sa_ent, + &dmask); + + if (ndrop_c) { + win3d_n -= ndrop_c; + pr_info("vgsp-gr: MME-CALL-DROP %u word(s) of stubbed-slot CALL runs (window=%u) dropped_mask=0x%llx h=0x%x push=0x%llx\n", + ndrop_c, win3d_n, + (unsigned long long)dmask, + req->h_object, req->push_va); + } + } pr_info("vgsp-gr: 3D window relocated %u operand pair(s) to shadow VAs (window=%u shadow=%u dropped=%u subdrop=%u sdmdrop=%u)\n", nrel, win3d_n, n_shadow, ndrop, gsp_phys_rmcall_gr_3d_last_subdrop, @@ -23562,6 +23824,16 @@ gr_page_unmapped: (unsigned long long)boot_stage.rmcall_guest_pt_win_mask, qmd_win_ret); + /* + * SESSION 153-CONTINUED-69: pre-reset MME fault sample. The engine + * faults within the first tens of ms of the launch; sample the MME + * ESR/INFO/INFO2 + DS ESR + GRFIFO/trap regs at MMIO rate from the + * moment the doorbell rang (in publish) so the faulting values are + * caught before the GSP-RM clears them. + */ + if (stream3d && bar0) + gsp_phys_mme_fast_sample(bar0, "launch", req->h_object); + poll_start_us = (u32)ktime_to_us(ktime_get()); deadline = jiffies + msecs_to_jiffies(poll_ms); for (;;) { |
