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.c165
1 files changed, 116 insertions, 49 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 535c8ad..f60f6c5 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -17034,34 +17034,24 @@ static u32 gsp_phys_gr_data_bi(u64 reloc_va)
* noinline so this rarely-taken wait does not grow the submit frame, which
* is already near the warning bound.
*/
+/*
+ * m180: scan the request's unique tracked bounce pages and return how many
+ * differ from the copy-in checksum baseline. `hash_out` (optional) receives
+ * an FNV-1a fold of the per-page checksums - the content fingerprint the
+ * settle rule needs so a store ramp with a constant changed-page COUNT can
+ * never read as settled again. Static scratch: the only caller is the
+ * single-inflight ordered gsp-phys-work item (same discipline as the other
+ * submit-scope statics).
+ */
static noinline u32
-gsp_phys_gr_output_retire_wait(const struct gsp_phys_gr_submit *req,
- const u32 *csum_in, u32 *n_tracked)
+gsp_phys_gr_scan_pages(const struct gsp_phys_gr_submit *req,
+ const u32 *csum_in, u32 *n_tracked, u64 *hash_out)
{
- /*
- * B-2: one entry per page of the grown region (64 KB), static like the
- * other submit-scope scratch: the only caller is the single-inflight
- * ordered work item (see the static scratch note in
- * gsp_phys_backend_guest_gr_submit). 64 KB must not sit on the kernel
- * stack (this frame was already near the warning bound at 205 pages).
- */
static u32 checked[GSP_PHYS_RMCALL_GR_DATA_BOUNCE_PAGES];
struct gsp_phys_boot_chunk *dchunk = gsp_phys_gr_data_chunk();
- u32 n_all = 0, di, iter;
- u32 changed = 0, prev = 0xffffffffu;
- const char *why = " (TIMEOUT)";
- unsigned long wait_start = jiffies;
+ u32 n_all = 0, di, j, changed = 0;
+ u64 hash = 1469598103934665603ULL;
- /*
- * 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++) {
u32 bi = gsp_phys_gr_data_bi(req->data[di].reloc_va);
u32 k;
@@ -17077,41 +17067,68 @@ gsp_phys_gr_output_retire_wait(const struct gsp_phys_gr_submit *req,
if (!dup)
checked[n_all++] = bi;
}
- if (!n_all)
- return 0;
if (n_tracked)
*n_tracked = n_all;
- for (iter = 0; iter < 300u; iter++) {
- u32 j;
+ for (j = 0; j < n_all; j++) {
+ const u8 *src = gsp_phys_chunk_page_va(dchunk, checked[j]);
+ u32 ci, csum = 0;
- changed = 0;
- for (j = 0; j < n_all; j++) {
- const u8 *src = gsp_phys_chunk_page_va(
- dchunk, checked[j]);
- u32 ci, csum = 0;
+ if (!src)
+ continue;
+ for (ci = 0; ci < GSP_PHYS_PAGE_SIZE; ci += 4)
+ csum += get_unaligned_le32(src + ci);
+ if (csum != csum_in[checked[j]])
+ changed++;
+ hash ^= csum;
+ hash *= 1099511628211ULL;
+ }
+ if (hash_out)
+ *hash_out = hash;
+ return changed;
+}
- if (!src)
- continue;
- for (ci = 0; ci < GSP_PHYS_PAGE_SIZE; ci += 4)
- csum += get_unaligned_le32(src + ci);
- if (csum != csum_in[checked[j]])
- changed++;
- }
+static noinline u32
+gsp_phys_gr_output_retire_wait(const struct gsp_phys_gr_submit *req,
+ const u32 *csum_in, u32 *n_tracked)
+{
+ u32 n_all = 0, iter;
+ u32 changed = 0;
+ u64 prev_hash = 0;
+ 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;
+ n_all = gsp_phys_gr_scan_pages(req, csum_in, n_tracked, NULL);
+ if (!n_all)
+ return 0;
+ for (iter = 0; iter < 300u; iter++) {
+ u64 hash = 0;
+
+ changed = gsp_phys_gr_scan_pages(req, csum_in, NULL, &hash);
if (changed >= n_all) {
why = "";
break;
}
/*
- * m162: a partial count must be stable for ~300 ms before it
- * counts as settled. The old 3-iteration (~18 ms) window
- * accepted a stalled store ramp or an un-dispatched grid as
- * "settled": observed 1024-page vecadd outputs copied out with
- * only 2 pages written (the client then verified 32736 bad
- * elements). A genuinely masked/short kernel costs at most
- * 300 ms extra; a ramping engine gets the time its stores
- * need.
+ * m180 (H3): a partial count must be CONTENT-stable for
+ * ~300 ms before it counts as settled. The m162 count-only
+ * rule accepted a continuing store ramp whose page count
+ * happened to be constant (the always-failing class's
+ * "17/32 settled" copy-outs), and accepted an
+ * un-dispatched grid as settled. A genuinely masked/short
+ * kernel costs at most 300 ms extra; a ramping engine gets
+ * the time its stores need.
*/
- if (changed > 0 && changed == prev && iter >= 60u) {
+ if (changed > 0 && hash == prev_hash && iter >= 60u) {
why = " (settled)";
break;
}
@@ -17119,7 +17136,7 @@ gsp_phys_gr_output_retire_wait(const struct gsp_phys_gr_submit *req,
why = " (no writes)";
break;
}
- prev = changed;
+ prev_hash = hash;
usleep_range(4000, 6000);
}
pr_info("vgsp-gr: output retirement wait: %u/%u pages changed after %u ms%s\n",
@@ -19231,6 +19248,56 @@ gr_page_unmapped:
n_tracked, nw_attempts, want_qmd,
fetched ? "OK" : "NONE");
+ /*
+ * m180 (lottery discriminators, agent-order H1/H2).
+ *
+ * (a) Deposit identity without changing the sentinel
+ * contract: read BOTH qmdwin banks' sentinel words.
+ * Ours intact + the other bank overwritten means an
+ * OLDER deposit was consumed instead of this one
+ * (stale/mis-targeted deposit, H2); ours overwritten
+ * means the engine really fetched THIS deposit
+ * (lost-dispatch vs read-only kernel, H1); both
+ * intact means no deposit ever reached the engine.
+ */
+ if (qmdwin_on && boot_stage.rmcall_gr_qmdwin.pages &&
+ qmd_dst_mem) {
+ u8 *om = gsp_phys_chunk_page_va(
+ &boot_stage.rmcall_gr_qmdwin,
+ qw_bank ^ 1u);
+
+ pr_warn("vgsp-gr: zero-write deposit stamp: bank=%u sentinel=0x%08x other-bank=%u sentinel=%s\n",
+ qw_bank,
+ get_unaligned_le32(
+ qmd_dst_mem +
+ GSP_PHYS_GR_QMD_SENTINEL_WORD * 4),
+ qw_bank ^ 1u,
+ om ? (gsp_phys_gr_qmd_fetched(om) ?
+ "overwritten (older deposit consumed)" :
+ "intact") : "n/a");
+ }
+ /*
+ * (b) Late-store check: a store ramp that retires after
+ * the host's window is indistinguishable from a lost
+ * dispatch at the accept moment. Re-scan once at +1 s
+ * and report whether the tracked pages changed in the
+ * meantime; the copy-out below picks up any stores
+ * that arrived late, so this both classifies and heals
+ * the late sub-case.
+ */
+ {
+ u32 late_tracked = 0;
+ u32 late;
+
+ msleep(1000);
+ late = gsp_phys_gr_scan_pages(req, bounce_csum_in,
+ &late_tracked, NULL);
+ pr_warn("vgsp-gr: zero-write late-check: %u/%u pages changed after +1000 ms (%s)\n",
+ late, late_tracked,
+ late ? "LATE-STORES - copy-out will ship them" :
+ "NO-STORES - dispatch lost or read-only kernel");
+ }
+
if (qmd_rel_hpa) {
u8 *rp = (u8 *)__va(qmd_rel_hpa) + qmd_rel_off;