summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_phys_booter.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-18 07:55:15 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-18 07:56:01 -0400
commit03d1ddbcbba333ed8bcc834ee9e6623e24f813e1 (patch)
treecb8406264e59625d56fe21a8e7863195afd3dbda /kernel/gsp_phys_booter.c
parentb025786c0b70cdb4f83c6f7af2d10fcbd4e93f05 (diff)
downloadopen-iov-03d1ddbcbba333ed8bcc834ee9e6623e24f813e1.tar.gz
open-iov-03d1ddbcbba333ed8bcc834ee9e6623e24f813e1.zip
vgsp: GR gap classification (RELOC-GAP-UNMAPPED) + launch-internal late-content ground-truth check
Diffstat (limited to 'kernel/gsp_phys_booter.c')
-rw-r--r--kernel/gsp_phys_booter.c116
1 files changed, 103 insertions, 13 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 3c2da44..095a0c3 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -17222,31 +17222,105 @@ static bool gsp_phys_gr_page_tracked(const struct gsp_phys_gr_submit *req,
}
/*
- * relfix PART 3: one rate-limited RELOC-GAP-STAGE line per distinct gap VA
- * per launch. `stage` is the resolver source chain (r.stage, e.g.
- * "ipin:-2,pin:-2,lat:-2,map:-2,vas:-2,disc:-2,ledger:-2," - the resolving
- * source ends in ":0,") for a word no source answered, or a short authoring
- * refusal token ("span-overlap", "region-full", "wordmax-full",
- * "base-tracked") for a word that resolved but was deliberately not
- * authored. Together with rc=-1/errno this classifies every remaining gap:
- * chain-of-misses = unresolvable at rewrite time (compare the fill's view of
- * the same VA); "span-overlap"/"region-full" = resolvable but refused for
- * proxy-safety/capacity; "wordmax-full" = table bound hit.
+ * relfix PART 3: one rate-limited log per distinct gap VA per launch
+ * (the dedup helper both gap diagnostics below share).
*/
-static void gsp_phys_gr_gap_stage_log(u64 va, const char *stage, int rc)
+static bool gsp_phys_gr_gap_log_once(u64 va)
{
u32 i;
for (i = 0; i < gsp_phys_gr_gap_logged_n; i++)
if (gsp_phys_gr_gap_logged[i] == va)
- return;
+ return false;
if (gsp_phys_gr_gap_logged_n < ARRAY_SIZE(gsp_phys_gr_gap_logged))
gsp_phys_gr_gap_logged[gsp_phys_gr_gap_logged_n++] = va;
+ return true;
+}
+
+/*
+ * relfix PART 3/4: one rate-limited RELOC-GAP-STAGE line per distinct gap VA
+ * per launch. `stage` is the resolver source chain (r.stage, e.g.
+ * "ipin:-2,pin:-2,lat:-2,map:-2,vas:-2,disc:-2,ledger:-2," - the resolving
+ * source ends in ":0,") for a word no source answered, or a short authoring
+ * refusal token ("span-overlap", "region-full", "wordmax-full",
+ * "base-tracked") for a word that resolved but was deliberately not
+ * authored. A FULL chain of -2 is classified as RELOC-GAP-UNMAPPED instead
+ * (see gsp_phys_gr_gap_unmapped_log): the sources are exhausted, so the word
+ * cannot be authored this launch; note the map stage's population is
+ * range-gated to [SEMA_VA_LO 4 GiB, SEMA_VA_HI 128 GiB) so its -2 is
+ * uninformative for VAs above that window - the vas/disc WALKS (49-bit
+ * full-range) are the authoritative absence signal.
+ */
+static void gsp_phys_gr_gap_stage_log(u64 va, const char *stage, int rc)
+{
+ if (!gsp_phys_gr_gap_log_once(va))
+ return;
pr_info_ratelimited("vgsp-gr: RELOC-GAP-STAGE va=0x%llx stage=%s rc=%d\n",
va, stage, rc);
}
/*
+ * relfix PART 4: definitive classification for a shape-passing word that NO
+ * resolver source answers (chain fully -2, r218: 0x760275000000,
+ * 0x760273000000, 0x3000000000, 0x17400000000, 0x17c00000000 - every stage
+ * -ENOENT). The walks cover the full 49-bit range, so a walk miss + no
+ * pin/cache/ledger entry means no mapping we can see exists anywhere;
+ * r218 additionally shows these words produce NO engine MMU fault (unlike
+ * the dereferenced argument words the late path now fixes), i.e. the engine
+ * does not consume them: they are descriptor/metadata values that are merely
+ * VA-shaped ("junk" as pointers). Any remaining engine fault is therefore
+ * NOT from this class - it redirects diagnosis to the code-pointer/entry
+ * word and page-content paths.
+ */
+static void gsp_phys_gr_gap_unmapped_log(u64 va)
+{
+ if (!gsp_phys_gr_gap_log_once(va))
+ return;
+ pr_info_ratelimited("vgsp-gr: RELOC-GAP-UNMAPPED va=0x%llx (all stages -2; VA-shape junk)\n",
+ va);
+}
+
+/*
+ * relfix PART 4: bounded content bound for a late-resolved word whose page is
+ * one of the launch's OWN pages (req->pages[], the only class with ground
+ * truth). The late copy-in itself is a direct copy of the resolved source
+ * page (gsp_phys_gr_fill_gap_bounce: src = __va(r.hpa)), so a dst-vs-src
+ * compare is vacuous; the meaningful question is whether the RESOLVER named
+ * the same page the launch pipeline uses. Compare the resolved source page
+ * against req->pages[i].hpa content and log the mission's
+ * LATE-CONTENT-MISMATCH line once per launch when they differ: that is the
+ * only way the engine could have been handed wrong bytes through a late
+ * proxy. Words whose page is not launch-internal have no ground truth and
+ * are skipped (their resolution is validated by the same walk/pin sources as
+ * every tracked data page).
+ */
+static void gsp_phys_gr_late_content_check(const struct gsp_phys_gr_submit *req,
+ u64 wb, u64 resolved_hpa, u32 off)
+{
+ u32 pi;
+
+ for (pi = 0; pi < req->n_pages; pi++) {
+ const u8 *gt, *src;
+
+ if ((req->pages[pi].guest_va &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1)) != wb)
+ continue;
+ if (!req->pages[pi].hpa)
+ break;
+ gt = (const u8 *)__va(req->pages[pi].hpa);
+ src = (const u8 *)__va(resolved_hpa);
+ if (!gt || !src)
+ break;
+ if (memcmp(gt, src, GSP_PHYS_PAGE_SIZE) &&
+ gsp_phys_gr_gap_log_once(wb))
+ pr_info_ratelimited("vgsp-gr: LATE-CONTENT-MISMATCH word_va=0x%llx page=0x%llx off=%u src=0x%llx\n",
+ wb, (u64)req->pages[pi].hpa, off,
+ resolved_hpa);
+ break;
+ }
+}
+
+/*
* relfix: resolve and author one unmatched word (see gsp_phys_gr_gap_page).
* The base VA resolves through the sema service's trusted resolver; on a hit
* the page is queued and the word rewritten to <reloc VA> | offset - the
@@ -17297,10 +17371,26 @@ static bool gsp_phys_gr_reloc_fix_word(const struct gsp_phys_gr_submit *req,
return false;
}
if (gsp_sema_gr_reloc_resolve(wb, &r)) {
- gsp_phys_gr_gap_stage_log(wb, r.stage, r.stage_rc);
+ /*
+ * A FULL chain of misses is the definitive
+ * no-mapping-anywhere class (PART 4); anything else
+ * (hpa conversion failure) keeps the stage chain.
+ */
+ if (r.stage_rc == -ENOENT)
+ gsp_phys_gr_gap_unmapped_log(wb);
+ else
+ gsp_phys_gr_gap_stage_log(wb, r.stage,
+ r.stage_rc);
return false;
}
base_pa = r.pa;
+ /*
+ * PART 4: the copy-in is a direct copy of this source page;
+ * for a launch-internal word, bound it against the launch's
+ * own page content (the LATE-CONTENT-MISMATCH witness).
+ */
+ gsp_phys_gr_late_content_check(req, wb, r.hpa,
+ (u32)(v & (u64)(GSP_PHYS_PAGE_SIZE - 1)));
if (!gsp_phys_gr_gap_add(req->n_data, wb, r.hpa, &reloc_va)) {
gsp_phys_gr_gap_stage_log(wb, "region-full", -ENOSPC);
return false;