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.c208
1 files changed, 136 insertions, 72 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index f0f4baa..535c8ad 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -17036,7 +17036,7 @@ static u32 gsp_phys_gr_data_bi(u64 reloc_va)
*/
static noinline u32
gsp_phys_gr_output_retire_wait(const struct gsp_phys_gr_submit *req,
- const u32 *csum_in)
+ const u32 *csum_in, u32 *n_tracked)
{
/*
* B-2: one entry per page of the grown region (64 KB), static like the
@@ -17052,6 +17052,14 @@ gsp_phys_gr_output_retire_wait(const struct gsp_phys_gr_submit *req,
const char *why = " (TIMEOUT)";
unsigned long wait_start = jiffies;
+ /*
+ * m179: report how many pages were actually trackable. "wrote == 0"
+ * from a launch with n_tracked == 0 (no data entry resolves to a
+ * bounce page) is not a no-execution signature and must not trigger
+ * the no-write retry/accept path.
+ */
+ if (n_tracked)
+ *n_tracked = 0;
if (!req->n_data)
return 0;
for (di = 0; di < req->n_data; di++) {
@@ -17071,6 +17079,8 @@ gsp_phys_gr_output_retire_wait(const struct gsp_phys_gr_submit *req,
}
if (!n_all)
return 0;
+ if (n_tracked)
+ *n_tracked = n_all;
for (iter = 0; iter < 300u; iter++) {
u32 j;
@@ -17313,15 +17323,16 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
&boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_PB];
void __iomem *bar0;
struct gsp_phys_rmcall_ce_submit sub;
- struct gsp_phys_fault_info fault = { };
/*
* Static scratch: the only caller is the single-inflight sema work
* item (ordered gsp-phys-work queue), so no concurrent submission can
- * touch these. Keeping the 96-dword method block and the 64-word QMD
- * copy off the stack keeps the frame under the warning bound.
+ * touch these. Keeping the 96-dword method block, the 64-word QMD
+ * copy and the ~370-byte fault record off the stack keeps the frame
+ * under the warning bound.
*/
static u32 methods[GSP_PHYS_RMCALL_GR_METHOD_WORDS];
static u32 qmd[GSP_PHYS_GR_QMD_WORDS];
+ static struct gsp_phys_fault_info fault;
unsigned long start = jiffies;
unsigned long deadline;
u8 *sem_va;
@@ -17382,6 +17393,12 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
fast_rearmed = false;
deposit_rb_done = false;
/*
+ * The fault record is static scratch (frame bound): reset it per
+ * submission so a previous launch's fault can never leak into this
+ * one's result - the old on-stack `= { }` semantics.
+ */
+ memset(&fault, 0, sizeof(fault));
+ /*
* F2: the shortened first window is part of the fast path; with
* gsp_phys_gr_retry_fast=0 the launch keeps today's exact behavior
* (the historical 5 s first window and ladder).
@@ -18879,37 +18896,60 @@ gr_page_unmapped:
* retirement wait is the only execution witness. On zero writes,
* refill the bounce window from the guest inputs, re-publish the
* same method block on a fresh ring entry, re-issue the schedule and
- * re-poll, then re-wait - bounded by the same attempt budget as the
- * timeout path. After the budget the launch proceeds to copy-out
- * with whatever the engine produced: a genuinely read-only kernel is
- * indistinguishable from a no-execution here and simply pays the
- * bounded extra polls (safe to re-execute by construction - a kernel
- * that writes nothing has no side effects to duplicate). A
+ * re-poll, then re-wait.
+ *
+ * m179: the budget is now DEDICATED (gsp_phys_gr_nowrite_retries,
+ * default 3) instead of sharing the timeout ladder's attempt counter -
+ * a launch that had already spent attempts on the ladder used to reach
+ * the accept path with no re-execution at all (the r200-r202
+ * memcpy_roundtrip / multikernel all-wrong class). The trigger is
+ * specific: only a launch with n_tracked > 0 tracked data pages can be
+ * a no-execution, so a launch whose data entries resolve nothing
+ * legitimately reports zero writes without entering this path. After
+ * the budget the launch proceeds to copy-out with whatever the engine
+ * produced, and the accept is logged loudly (pr_warn) and classified
+ * by the QMD fetch sentinel (CONFIRMED lost vs AMBIGUOUS): a
+ * genuinely read-only kernel is indistinguishable from a
+ * no-execution here and simply pays the bounded extra polls (safe to
+ * re-execute by construction - a kernel that writes nothing has no
+ * side effects to duplicate). A
* wrong-place write detector was tried and removed: the highest-reloc
* data entry is often a UVM-internal buffer, not the output, so it
* false-fired on correct launches and would re-execute
* side-effecting kernels (atomics) up to 3x.
*/
if (req->n_data && gsp_phys_gr_data_chunk()->pages) {
- u32 wrote = gsp_phys_gr_output_retire_wait(req, bounce_csum_in);
+ u32 n_tracked = 0;
+ u32 nw_attempts = 0;
+ u32 wrote = gsp_phys_gr_output_retire_wait(req, bounce_csum_in,
+ &n_tracked);
- if (wrote == 0) {
+ /*
+ * m179: the zero-write test is specific to launches with a
+ * non-empty TRACKED data set. A launch whose data entries do
+ * not resolve to any bounce page legitimately reports zero
+ * writes; treating that as a lost launch would re-execute a
+ * kernel for no reason (a false positive). Only
+ * n_tracked > 0 makes "wrote == 0" a possible no-execution
+ * signature.
+ */
+ if (wrote == 0 && n_tracked) {
bool ff = gsp_phys_gr_qmd_fetched(qmd_dst_mem);
- pr_info("vgsp-gr: first-attempt witness: engine fetch %s (qmd sentinel %s)\n",
+ pr_info("vgsp-gr: first-attempt witness: engine fetch %s (qmd sentinel %s) tracked=%u\n",
ff ? "OK" : "NONE",
- ff ? "overwritten" : "intact");
+ ff ? "overwritten" : "intact", n_tracked);
}
- if (gsp_phys_rmcall_gr_nowrite_retry_value()) {
- while (attempts < GSP_PHYS_RMCALL_GR_ATTEMPTS) {
- if (wrote > 0)
- break;
- attempts++;
- if (attempts > GSP_PHYS_RMCALL_GR_ATTEMPTS)
- break;
- pr_info("vgsp-gr: outcome retry %u/%u (wrote=%u): refill + re-publish\n",
- attempts, GSP_PHYS_RMCALL_GR_ATTEMPTS,
- wrote);
+ if (wrote == 0 && n_tracked &&
+ gsp_phys_rmcall_gr_nowrite_retry_value()) {
+ u32 nw_retries =
+ (u32)gsp_phys_gr_nowrite_retries_value();
+
+ while (wrote == 0 && nw_attempts < nw_retries) {
+ nw_attempts++;
+ pr_info("vgsp-gr: outcome retry %u/%u (wrote=%u tracked=%u): refill + re-publish\n",
+ nw_attempts, nw_retries,
+ wrote, n_tracked);
/*
* m156 (E5): on the first zero-write retry, push the
* proven stage-4 GR semaphore block before re-publishing.
@@ -18923,7 +18963,7 @@ gr_page_unmapped:
* (r155: 3 outcome retries + rearm all dead after the
* wedge). Both outcomes are logged distinctly.
*/
- if (attempts == 1) {
+ if (nw_attempts == 1) {
int sp_ret;
mutex_lock(&gsp_phys_rm_lock);
@@ -18931,7 +18971,7 @@ gr_page_unmapped:
bar0);
mutex_unlock(&gsp_phys_rm_lock);
pr_info("vgsp-gr: outcome retry %u: warm sem push rc=%d (engine %s)\n",
- attempts, sp_ret,
+ nw_attempts, sp_ret,
sp_ret ? "WEDGED - skipping further retries" :
"ALIVE");
if (sp_ret) {
@@ -19027,6 +19067,20 @@ gr_page_unmapped:
GSP_PHYS_GR_QMD_SENTINEL,
qmd_dst_mem +
GSP_PHYS_GR_QMD_SENTINEL_WORD * 4);
+ /*
+ * m179: order the fresh
+ * deposit before the
+ * re-publish/doorbell
+ * below (the first-attempt
+ * path has the same
+ * barrier; the publish tail
+ * also carries dma_wmb +
+ * wmb, this keeps the
+ * deposit->kick order
+ * explicit on the retry
+ * path).
+ */
+ wmb();
}
}
if (qmdwin_swap || win0_moved) {
@@ -19039,17 +19093,17 @@ gr_page_unmapped:
}
if (qmdwin_swap)
pr_info("vgsp-gr: outcome retry %u: release words zeroed + fresh qmdwin bank %u dest 0x%llx + win0 bank %u%s + PB rebuilt\n",
- attempts, qw_bank,
+ nw_attempts, qw_bank,
qmd_dst, win0_bank_base,
win0_moved ?
" (shadow pages re-copied)" :
"");
else if (win0_moved)
pr_info("vgsp-gr: outcome retry %u: release words zeroed + fresh win0 bank %u (shadow pages re-copied) + PB rebuilt\n",
- attempts, win0_bank_base);
+ nw_attempts, win0_bank_base);
else
pr_info("vgsp-gr: outcome retry %u: release words zeroed (win0/qmdwin off)\n",
- attempts);
+ nw_attempts);
}
gsp_phys_gr_fill_bounce(req, bounce_csum_in);
mutex_lock(&gsp_phys_rm_lock);
@@ -19062,7 +19116,7 @@ gr_page_unmapped:
mutex_unlock(&gsp_phys_rm_lock);
if (ret) {
pr_info("vgsp-gr: outcome retry %u publish/resched failed rc=%d (accepting current outcome)\n",
- attempts, ret);
+ nw_attempts, ret);
ret = 0;
break;
}
@@ -19102,7 +19156,7 @@ gr_page_unmapped:
msleep(GSP_PHYS_RMCALL_POLL_MS);
}
pr_info("vgsp-gr: outcome retry %u: fresh ack %s (qmd_rel=0x%08x want=0x%08x ch_rel=0x%08x after %u ms)\n",
- attempts,
+ nw_attempts,
acked ? "OK" : "TIMEOUT",
rw0, want_qmd, rw1,
jiffies_to_msecs(jiffies -
@@ -19133,59 +19187,69 @@ gr_page_unmapped:
msleep(5);
}
pr_info("vgsp-gr: outcome retry %u: engine fetch witness %s (qmd sentinel %s)\n",
- attempts,
+ nw_attempts,
fetched ? "OK" : "NONE",
fetched ? "overwritten" : "intact");
}
wrote = gsp_phys_gr_output_retire_wait(
- req, bounce_csum_in);
+ req, bounce_csum_in, &n_tracked);
pr_info("vgsp-gr: outcome retry %u done: engine wrote %u page(s)\n",
- attempts, wrote);
+ nw_attempts, wrote);
}
+ }
+ /*
+ * m160/m179: if every fresh-ack retry still produced no engine
+ * writes, the release words may be left zeroed from the last
+ * retry. Restore the expected payloads so the guest client
+ * observes its completion and proceeds to its own result check
+ * (a genuinely lost launch then surfaces as the client's own
+ * FAIL, never as a hang). This accept runs even when the retry
+ * loop is disabled (gsp_phys_rmcall_gr_nowrite_retry=0) so a
+ * zero-write outcome is never silent.
+ */
+ if (wrote == 0 && n_tracked) {
+ bool restored = false;
+ bool fetched = gsp_phys_gr_qmd_fetched(qmd_dst_mem);
+
/*
- * m160: if every fresh-ack retry still produced no
- * engine writes, the release words may be left zeroed
- * from the last retry. Restore the expected payloads so
- * the guest client observes its completion and proceeds
- * to its own result check (a genuinely lost launch then
- * surfaces as the client's own FAIL, never as a hang).
+ * m179: the no-write accept is the last resort after
+ * the dedicated re-execution budget
+ * (gsp_phys_gr_nowrite_retries). It stays
+ * unconditional - the alternative is stalling the
+ * client forever - but it is now LOUD and classified
+ * by the hardware fetch witness: an intact sentinel
+ * means the deposit never reached the engine (a
+ * CONFIRMED lost launch, the sweep/hold-faked-ack
+ * class); an overwritten sentinel means the engine
+ * consumed the deposit, so the zero tracked writes
+ * may also be a legitimately read-only kernel
+ * (AMBIGUOUS).
*/
- if (wrote == 0) {
- bool restored = false;
-
- /*
- * C6 (GAP-2 A1): the no-write accept. Unconditional
- * by design - it is the rare, load-bearing event
- * (zero engine writes still accepted as success),
- * so it must be visible even when no release word
- * needed restoring.
- */
- pr_info("vgsp-gr: NOWRITE h=0x%x push=0x%llx n_data=%u wrote=0 qmd_rel=0x%x\n",
- req->h_object, req->push_va, req->n_data,
- want_qmd);
-
- if (qmd_rel_hpa) {
- u8 *rp = (u8 *)__va(qmd_rel_hpa) +
- qmd_rel_off;
-
- if (READ_ONCE(*(u32 *)rp) != want_qmd) {
- WRITE_ONCE(*(u32 *)rp, want_qmd);
- restored = true;
- }
+ pr_warn("vgsp-gr: NOWRITE-%s h=0x%x push=0x%llx n_data=%u tracked=%u wrote=0 retries=%u qmd_rel=0x%x (engine fetch %s)\n",
+ fetched ? "AMBIGUOUS" : "CONFIRMED",
+ req->h_object, req->push_va, req->n_data,
+ n_tracked, nw_attempts, want_qmd,
+ fetched ? "OK" : "NONE");
+
+ if (qmd_rel_hpa) {
+ u8 *rp = (u8 *)__va(qmd_rel_hpa) + qmd_rel_off;
+
+ if (READ_ONCE(*(u32 *)rp) != want_qmd) {
+ WRITE_ONCE(*(u32 *)rp, want_qmd);
+ restored = true;
}
- if (ch_rel_hpa) {
- u8 *rp = (u8 *)__va(ch_rel_hpa) +
- ch_rel_off;
+ }
+ if (ch_rel_hpa) {
+ u8 *rp = (u8 *)__va(ch_rel_hpa) + ch_rel_off;
- if (READ_ONCE(*(u32 *)rp) != want_ch) {
- WRITE_ONCE(*(u32 *)rp, want_ch);
- restored = true;
- }
+ if (READ_ONCE(*(u32 *)rp) != want_ch) {
+ WRITE_ONCE(*(u32 *)rp, want_ch);
+ restored = true;
}
- if (restored)
- pr_info("vgsp-gr: no-write accept: release words restored (qmd=0x%08x ch=0x%08x); client's own check decides\n",
- want_qmd, want_ch);
}
+ if (restored)
+ pr_warn("vgsp-gr: no-write accept: release words restored (qmd=0x%08x ch=0x%08x); client's own check decides\n",
+ want_qmd, want_ch);
}
}
/*