diff options
Diffstat (limited to 'kernel/gsp_phys_booter.c')
| -rw-r--r-- | kernel/gsp_phys_booter.c | 471 |
1 files changed, 470 insertions, 1 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index 0cd45f5..cc33e31 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -18828,6 +18828,408 @@ void gsp_phys_gr_mme_carry_failed(int rc) } /* + * SAVED-WINDOW RELEASE CARRIER (sema_gl_mme_pack_release). + * + * c756: the 324 B draw window (push 0x4011978) elided 18 words of real + * CALL(34) runs at `MME-CALL-HOLD` because the packed program was still + * partial; the 728 B window (push 0x42192a0) then completed the pack but no + * later client window arrived, so those CALL runs never re-ran and the + * DRAW_IAB mac=0x22 never executed. c716 proved the shape that runs them: a + * later launch of the same window with the calls released. + * + * This carrier snapshots the window and the launch's capture at the hold + * point and, once the pack is complete/resident and no client window + * consumed the release within the settle grace, re-submits it through the + * normal fill/build path with mme_hold_calls=false, mme_load_only=false and + * emit_prelude=false. The RAW (pre-relocation) window is saved, not the + * patched copy: the relocation pass allocates per-launch shadow/bounce VAs, + * so replaying a patched window would name another launch's slots; running + * the same deterministic patch on the raw bytes reproduces the original + * build with fresh VAs. Default off; beef-only; one fire per boot; no + * retries. + */ +#define GSP_PHYS_GR_MME_RELEASE_WIN_MAX GSP_PHYS_GR_RAW_WORDS +#define GSP_PHYS_GR_MME_RELEASE_DATA_MAX 4096u +#define GSP_PHYS_GR_MME_RELEASE_WINS_MAX 31u + +struct gsp_phys_gr_mme_release_state { + bool valid; + bool armed; + bool fired; + u32 h_object; + u32 gen; + u32 held; + u32 win_n; + u64 push_va; + unsigned long due_j; + u32 boot_n; + u32 fired_gen; /* pack_done_gen already released (no retries) */ + /* capture snapshot (see struct gsp_phys_gr_submit) */ + u32 qmd[GSP_PHYS_GR_QMD_WORDS]; + u32 n_pages; + struct gsp_phys_gr_page pages[GSP_PHYS_GR_MAX_PAGES]; + u32 n_inl; + struct gsp_phys_gr_inline inl[GSP_PHYS_GR_MAX_INLINE]; + u32 n_data; + struct gsp_phys_gr_data_page data[GSP_PHYS_GR_MME_RELEASE_DATA_MAX]; + u32 n_data_wins; + struct gsp_phys_gr_data_win data_wins[GSP_PHYS_GR_MME_RELEASE_WINS_MAX]; + u64 qmd_va; + u64 qmd_hpa; + u32 push_len; + u32 stream_rel; + u32 stream_n; + u32 win[GSP_PHYS_GR_MME_RELEASE_WIN_MAX]; + u64 prog_va; + u32 n_prog_binds; + u64 prog_bind_va[8]; + u32 prog_bind_slot[8]; +}; + +static struct gsp_phys_gr_mme_release_state gsp_phys_gr_mme_release; +static DEFINE_SPINLOCK(gsp_phys_gr_mme_release_lock); +static bool gsp_phys_rmcall_gr_mme_pack_release; +module_param_named(sema_gl_mme_pack_release, + gsp_phys_rmcall_gr_mme_pack_release, bool, 0644); +MODULE_PARM_DESC(sema_gl_mme_pack_release, + "Saved-window release carrier: after the packed MME program completes, re-submit the last held-call window with its CALL runs released so DRAW_IAB mac=0x22 executes (default 0 = off)"); +static u32 gsp_phys_rmcall_gr_mme_pack_release_ms = 100u; +module_param_named(sema_gl_mme_pack_release_ms, + gsp_phys_rmcall_gr_mme_pack_release_ms, uint, 0644); +MODULE_PARM_DESC(sema_gl_mme_pack_release_ms, + "Settle delay before the saved-window release carrier fires (clamped 20..5000 ms; default 100)"); + +static u32 gsp_phys_gr_mme_release_due_ms(void) +{ + u32 ms = READ_ONCE(gsp_phys_rmcall_gr_mme_pack_release_ms); + + if (ms < 20u) + ms = 20u; + else if (ms > 5000u) + ms = 5000u; + return ms; +} + +/* + * Snapshot at the held-call window (the caller logs `MME-CALL-HOLD` first). + * Only the raw window and the capture fields are copied; guest pages are + * referenced by HPA (the boot's pages remain valid), and the saved stream + * points at the kernel buffer so the service's gr_raw reuse cannot corrupt + * it. A window/capture too large for the bounded buffers skips with a + * receipt (never a partial save). + */ +static void gsp_phys_gr_mme_release_save(const struct gsp_phys_gr_submit *req, + u32 held, u32 win_n, u32 gen) +{ + struct gsp_phys_gr_mme_release_state *s = &gsp_phys_gr_mme_release; + unsigned long flags; + + if (!READ_ONCE(gsp_phys_rmcall_gr_mme_pack_release) || !req) + return; + /* Beef-only: the saved-window release exists for the direct-GL lane. */ + if ((req->h_object & 0xfffff000u) != 0xbeef0000u) + return; + if (!held || !req->stream || !req->stream_n || + req->stream_n > GSP_PHYS_GR_MME_RELEASE_WIN_MAX || + req->n_pages > GSP_PHYS_GR_MAX_PAGES || + req->n_inl > GSP_PHYS_GR_MAX_INLINE || + req->n_data > GSP_PHYS_GR_MME_RELEASE_DATA_MAX || + req->n_data_wins > GSP_PHYS_GR_MME_RELEASE_WINS_MAX || + req->n_prog_binds > 8u) { + pr_info("vgsp-gr: MME-CARRY-SAVE-SKIP h=0x%x win=%u held=%u stream=%u pages=%u inl=%u data=%u (too large)\n", + req->h_object, win_n, held, req->stream_n, + req->n_pages, req->n_inl, req->n_data); + return; + } + spin_lock_irqsave(&gsp_phys_gr_mme_release_lock, flags); + s->valid = false; + s->armed = false; + s->fired = false; + s->h_object = req->h_object; + s->gen = gen; + s->held = held; + s->win_n = win_n; + s->push_va = req->push_va; + memcpy(s->qmd, req->qmd, sizeof(s->qmd)); + s->n_pages = req->n_pages; + if (req->n_pages) + memcpy(s->pages, req->pages, + (size_t)req->n_pages * sizeof(s->pages[0])); + s->n_inl = req->n_inl; + if (req->n_inl) + memcpy(s->inl, req->inl, + (size_t)req->n_inl * sizeof(s->inl[0])); + s->n_data = req->n_data; + if (req->n_data) + memcpy(s->data, req->data, + (size_t)req->n_data * sizeof(s->data[0])); + s->n_data_wins = req->n_data_wins; + if (req->n_data_wins) + memcpy(s->data_wins, req->data_wins, + (size_t)req->n_data_wins * sizeof(s->data_wins[0])); + s->qmd_va = req->qmd_va; + s->qmd_hpa = req->qmd_hpa; + s->push_len = req->push_len; + s->stream_rel = req->stream_rel; + s->stream_n = req->stream_n; + memcpy(s->win, req->stream, (size_t)req->stream_n * 4u); + s->prog_va = req->prog_va; + s->n_prog_binds = req->n_prog_binds; + memcpy(s->prog_bind_va, req->prog_bind_va, + sizeof(s->prog_bind_va)); + memcpy(s->prog_bind_slot, req->prog_bind_slot, + sizeof(s->prog_bind_slot)); + s->valid = true; + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + pr_info("vgsp-gr: MME-CARRY-SAVE h=0x%x win=%u held=%u push=0x%llx\n", + req->h_object, win_n, held, + (unsigned long long)req->push_va); +} + +/* + * Arm once the packed program is complete/resident. The record survives + * partial emissions, so the completing launch (which may carry no held CALL + * runs of its own) arms the last held-call window. A previously armed release + * is superseded by a newer record; a fired one is never re-armed (one fire + * per boot, no retries). + */ +static void gsp_phys_gr_mme_release_arm(u32 gen) +{ + struct gsp_phys_gr_mme_release_state *s = &gsp_phys_gr_mme_release; + unsigned long flags; + bool pack_done, golden, superseded = false, ok = false, fresh; + u32 ms, h_object, held, win_n, pdg; + + if (!READ_ONCE(gsp_phys_rmcall_gr_mme_pack_release)) + return; + ms = gsp_phys_gr_mme_release_due_ms(); + spin_lock_irqsave(&gsp_phys_gr_mme_release_lock, flags); + h_object = s->h_object; + held = s->held; + win_n = s->win_n; + if (s->fired) { + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + return; + } + if (s->armed) { + s->armed = false; + superseded = true; + } + pdg = READ_ONCE(gsp_phys_gr_mme_pack_done_gen); + pack_done = (READ_ONCE(gsp_phys_gr_mme_pack_done_h) == h_object || + pdg != 0u); + golden = gsp_phys_gr_golden_ready(); + /* + * One fire per pack completion: a completion generation already + * released never re-arms (no retries); a later wipe + fresh completion + * has a new generation and may arm again. + */ + fresh = pack_done && pdg != 0u && pdg != s->fired_gen; + if (s->valid && held && win_n && + (h_object & 0xfffff000u) == 0xbeef0000u && + fresh && golden) { + s->armed = true; + s->gen = gen; + s->due_j = jiffies + msecs_to_jiffies(ms); + ok = true; + } + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + if (superseded) + pr_info("vgsp-gr: MME-CARRY-CANCEL reason=superseded mode=release h=0x%x held=%u win=%u\n", + h_object, held, win_n); + if (ok) + pr_info("vgsp-gr: MME-CARRY-ARM mode=release h=0x%x held=%u win=%u due_ms=%u\n", + h_object, held, win_n, ms); +} + +bool gsp_phys_gr_mme_release_peek(u32 *h_object) +{ + struct gsp_phys_gr_mme_release_state *s = &gsp_phys_gr_mme_release; + unsigned long flags; + bool due; + + spin_lock_irqsave(&gsp_phys_gr_mme_release_lock, flags); + due = s->armed && time_after_eq(jiffies, s->due_j); + if (due && h_object) + *h_object = s->h_object; + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + return due; +} + +/* + * Claim the armed release for one launch, re-validating the live residency + * and handle. Any mismatch cancels (never fires a stale record). + */ +bool gsp_phys_gr_mme_release_take(u32 h_object, u32 *held, u32 *win) +{ + struct gsp_phys_gr_mme_release_state *s = &gsp_phys_gr_mme_release; + const char *reason = NULL; + unsigned long flags; + bool pack_done; + u32 sh, sheld, swin, pdg; + + spin_lock_irqsave(&gsp_phys_gr_mme_release_lock, flags); + if (!s->armed || !time_after_eq(jiffies, s->due_j)) { + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + return false; + } + sh = s->h_object; + sheld = s->held; + swin = s->win_n; + pdg = READ_ONCE(gsp_phys_gr_mme_pack_done_gen); + pack_done = (READ_ONCE(gsp_phys_gr_mme_pack_done_h) == h_object || + pdg != 0u); + if (!s->valid) + reason = "save-invalid"; + else if (sh != h_object) + reason = "h-mismatch"; + else if ((h_object & 0xfffff000u) != 0xbeef0000u) + reason = "not-beef"; + else if (!gsp_phys_gr_golden_ready()) + reason = "golden-lost"; + else if (!pack_done) + reason = "pack-lost"; + else if (pdg != 0u && pdg == s->fired_gen) + reason = "budget"; + if (reason) { + s->armed = false; + s->fired = false; + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + pr_info("vgsp-gr: MME-CARRY-CANCEL reason=%s mode=release h=0x%x held=%u win=%u\n", + reason, sh, sheld, swin); + return false; + } + s->armed = false; + s->fired = true; + s->boot_n++; + s->fired_gen = pdg ? pdg : 1u; + if (held) + *held = sheld; + if (win) + *win = swin; + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + return true; +} + +/* The release is still claimed and not cancelled (builder safety gate). */ +static bool gsp_phys_gr_mme_release_active(void) +{ + struct gsp_phys_gr_mme_release_state *s = &gsp_phys_gr_mme_release; + unsigned long flags; + bool active; + + spin_lock_irqsave(&gsp_phys_gr_mme_release_lock, flags); + active = s->valid && s->fired; + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + return active; +} + +void gsp_phys_gr_mme_release_complete(void) +{ + struct gsp_phys_gr_mme_release_state *s = &gsp_phys_gr_mme_release; + unsigned long flags; + bool fired; + + spin_lock_irqsave(&gsp_phys_gr_mme_release_lock, flags); + fired = s->fired; + s->armed = false; + s->fired = false; + s->valid = false; + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + if (fired) + pr_info("vgsp-gr: MME-CARRY-COMPLETE mode=release\n"); +} + +void gsp_phys_gr_mme_release_cancel(const char *reason) +{ + struct gsp_phys_gr_mme_release_state *s = &gsp_phys_gr_mme_release; + unsigned long flags; + bool had; + u32 sh, sheld, swin; + + spin_lock_irqsave(&gsp_phys_gr_mme_release_lock, flags); + had = s->armed || s->fired; + sh = s->h_object; + sheld = s->held; + swin = s->win_n; + s->armed = false; + s->fired = false; + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + if (had) + pr_info("vgsp-gr: MME-CARRY-CANCEL reason=%s mode=release h=0x%x held=%u win=%u\n", + reason ? reason : "unspecified", sh, sheld, swin); +} + +void gsp_phys_gr_mme_release_failed(int rc) +{ + struct gsp_phys_gr_mme_release_state *s = &gsp_phys_gr_mme_release; + unsigned long flags; + bool had; + u32 sh, sheld, swin; + + spin_lock_irqsave(&gsp_phys_gr_mme_release_lock, flags); + had = s->armed || s->fired; + sh = s->h_object; + sheld = s->held; + swin = s->win_n; + s->armed = false; + s->fired = false; + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + if (!had) + return; + pr_warn("vgsp-gr: MME-CARRY-ABORT mode=release reason=hw-error rc=%d h=0x%x held=%u win=%u\n", + rc, sh, sheld, swin); + gsp_phys_gr_mme_iram_gate_fail(); +} + +/* + * Copy the saved capture into the service's submit request. The caller (the + * release work item) owns the slot until it submits; stream points at the + * saved kernel window and every other pointer at the saved arrays. + */ +bool gsp_phys_gr_mme_release_fill(struct gsp_phys_gr_submit *out) +{ + struct gsp_phys_gr_mme_release_state *s = &gsp_phys_gr_mme_release; + unsigned long flags; + bool ok; + + if (!out) + return false; + spin_lock_irqsave(&gsp_phys_gr_mme_release_lock, flags); + ok = s->valid && s->fired; + if (ok) { + memset(out, 0, sizeof(*out)); + out->qmd = s->qmd; + out->qmd_words = GSP_PHYS_GR_QMD_WORDS; + out->qmd_va = s->qmd_va; + out->qmd_hpa = s->qmd_hpa; + out->pages = s->pages; + out->n_pages = s->n_pages; + out->inl = s->inl; + out->n_inl = s->n_inl; + out->data = s->data; + out->n_data = s->n_data; + out->data_wins = s->data_wins; + out->n_data_wins = s->n_data_wins; + out->stream = s->win; + out->stream_n = s->stream_n; + out->stream_rel = s->stream_rel; + out->push_len = s->push_len; + out->h_object = s->h_object; + out->prog_va = s->prog_va; + out->n_prog_binds = s->n_prog_binds; + memcpy(out->prog_bind_va, s->prog_bind_va, + sizeof(out->prog_bind_va)); + memcpy(out->prog_bind_slot, s->prog_bind_slot, + sizeof(out->prog_bind_slot)); + out->mme_release = true; + /* No guest release/semaphore: rel_* and sem_* stay zero. */ + } + spin_unlock_irqrestore(&gsp_phys_gr_mme_release_lock, flags); + return ok; +} + +/* * The carrier's windowless launch body: WAIT_FOR_IDLE(0x110) <- 0. Filled * into the submit path's per-launch stream_patch scratch (writable: the * pair-chain pass may prepend a closing END if a pair is carried open, and @@ -21007,6 +21409,7 @@ static void gsp_phys_gr_mme_iram_gate_fail(void) { gsp_phys_gr_mme_iram_gate_fails++; gsp_phys_gr_mme_carry_cancel("iram-gate-fail"); + gsp_phys_gr_mme_release_cancel("iram-gate-fail"); WRITE_ONCE(gsp_phys_gr_mme_pending_gen, 0); WRITE_ONCE(gsp_phys_gr_mme_prelude_gen, 0); WRITE_ONCE(gsp_phys_gr_mme_pre_emit_off, 0); @@ -21245,6 +21648,7 @@ static u32 gsp_phys_gr_fe_status(void) static void gsp_phys_gr_ctx_wipe_latches(void) { gsp_phys_gr_mme_carry_cancel("ctx-wipe"); + gsp_phys_gr_mme_release_cancel("ctx-wipe"); WRITE_ONCE(gsp_phys_gr_mme_prelude_gen, 0); WRITE_ONCE(gsp_phys_gr_mme_pre_emit_off, 0); WRITE_ONCE(gsp_phys_gr_mme_pack_off, 0); @@ -21298,6 +21702,7 @@ void gsp_phys_gr_mme_chan_ctx_reset(u32 h) h != READ_ONCE(gsp_phys_gr_mme_pack_done_h)) return; gsp_phys_gr_mme_carry_cancel("chan-ctx-reset"); + gsp_phys_gr_mme_release_cancel("chan-ctx-reset"); /* * eng43: sema_gl_mme_keep_done_across_recycle=1 - the re-register * recycles the client handle but leaves the GR engine context (and @@ -21667,6 +22072,7 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, bool latch_before = false; bool stream_damaged = false; bool carrier = req && req->mme_carrier; + bool release = req && req->mme_release; u32 pre_words_out = 0; u32 win_emit_off = 0; const u32 *pl = NULL; @@ -21697,6 +22103,15 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, stream_damaged = gsp_phys_gr_stream_damaged(req->push_va, req->stream_rel); /* + * A real client 3D window is the "next launch" the saved-window + * release carrier was waiting for: cancel a pending release so + * the client's own window (which may run or hold the calls) + * wins. The hold point below can re-arm from this launch's own + * snapshot when the pack is complete. + */ + if (!carrier && !release && req->stream_n) + gsp_phys_gr_mme_release_cancel("client-first"); + /* * 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 @@ -22032,6 +22447,7 @@ if (gsp_phys_gr_slot_kept(j, sa_mask, READ_ONCE( gsp_phys_rmcall_gr_mme_golden_force))) { gsp_phys_gr_mme_carry_cancel("gen-rewind"); + gsp_phys_gr_mme_release_cancel("gen-rewind"); WRITE_ONCE(gsp_phys_gr_mme_pack_off, 0); WRITE_ONCE(gsp_phys_gr_mme_pack_sa, false); @@ -22087,6 +22503,12 @@ if (gsp_phys_gr_slot_kept(j, sa_mask, */ if (carrier && pl && pl_n && pl_gen) emit_prelude = true; + /* + * Saved-window release carrier: the packed program is already + * resident, so never re-emit it - the window is the payload. + */ + if (release) + emit_prelude = false; *p++ = ((u32)GSP_PHYS_RMCALL_METHOD_IMM << 29) | (1u << 16) | (0u << 13) | @@ -23126,6 +23548,16 @@ if (gsp_phys_gr_slot_kept(j, sa_mask, gsp_phys_gr_mme_carry_complete( req->h_object, pl_gen, maxa); + /* + * The pack is now + * complete/resident: + * arm the saved-window + * release for the last + * held-call window (this + * launch may carry none). + */ + gsp_phys_gr_mme_release_arm( + pl_gen); pr_info("vgsp-gr: MME-PRELUDE pack ir_words=%u runs=%u sa=%u h=0x%x push=0x%llx\n", maxa, nruns, npair, @@ -23536,6 +23968,14 @@ if (gsp_phys_gr_slot_kept(j, sa_mask, READ_ONCE(gsp_phys_gr_mme_pack_done_gen) != 0u)) { static u32 pack_rearm_log; + /* + * This client launch is the release: the saved-window + * carrier is no longer needed (the release carrier + * itself forces the calls kept below and must not + * cancel its own claim). + */ + if (!release) + gsp_phys_gr_mme_release_cancel("released"); if (pack_rearm_log < 8u) { pack_rearm_log++; pr_info("vgsp-gr: MME-PACK-REARM h=0x%x gen=%u (pack complete%s; calls released) push=0x%llx\n", @@ -23595,6 +24035,18 @@ if (gsp_phys_gr_slot_kept(j, sa_mask, } } /* + * Saved-window release carrier: the pack is complete/resident + * and the saved window's held CALL runs are the payload, so + * release them and keep the window. If a gate above cancelled + * the claim (fired cleared), keep the held shape - never run a + * macro against a program a gate just rejected. + */ + if (release && gsp_phys_gr_mme_release_active()) { + mme_load_only = false; + mme_hold_window = false; + mme_hold_calls = false; + } + /* * 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 @@ -24015,6 +24467,17 @@ if (gsp_phys_gr_slot_kept(j, sa_mask, win3d_n, w, req->push_va, mme_hold_fe_busy ? " [FE-not-idle (CONT-104)]" : ""); + /* + * Saved-window release carrier: snapshot this window and + * its capture so the held CALL runs can be re-emitted + * after the pack completes. If the pack is already + * complete this very launch can arm it too. + */ + if (held) { + gsp_phys_gr_mme_release_save(req, held, win3d_n, + pl_gen); + gsp_phys_gr_mme_release_arm(pl_gen); + } if (pelided) pr_info_ratelimited("vgsp-gr: PAIR-CHAIN held-elide %u word(s) of program-pair runs dropped (pairs installed gen=%u h=0x%x; 0x2058 other-writer) push=0x%llx\n", pelided, pl_gen, req->h_object, @@ -29493,6 +29956,12 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req, */ bool carrier = req && req->mme_carrier; /* + * Saved-window release carrier: the saved window is real, so this + * launch runs the normal 3D relocation/build path (not the LOAD-ONLY + * stub branch below) with the CALL runs released by the builder. + */ + bool release = req && req->mme_release; + /* * CONT-73: fresh direct-write witness for this submission. The * window/attr patch passes below record every relocated operand * destination's guest page; the pre-doorbell capture and the retire @@ -29684,7 +30153,7 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req, * into the same software-completion fallback without touching the * engine, and cannot wedge it for the next client. */ - if (!carrier && req->n_data < gsp_phys_gr_min_data_value()) { + if (!carrier && !release && req->n_data < gsp_phys_gr_min_data_value()) { gsp_phys_gr_refused++; pr_err_ratelimited("vgsp-gr: GR submit refused: degenerate data set n_data=%u (min %u) launch_va=0x%llx\n", req->n_data, |
