summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_phys_booter.c111
1 files changed, 109 insertions, 2 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index aa011e4..daacf59 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -17880,6 +17880,99 @@ gsp_phys_gr_deposit_rb_log(const struct gsp_phys_gr_submit *req,
GSP_PHYS_GR_QMD_SENTINEL_WORD * 4));
}
+/*
+ * CODEHASH (2026-10-18): code-fetch content verification. At author time,
+ * sample the host-readable bytes the engine is about to fetch for this
+ * launch's program (the QMD word 48/49 pair): the first 16 B at the entry
+ * offset plus 16 B at PA+0 / PA+mid / PA+end of the entry page, and the
+ * same three windows on up to three further consecutive tracked pages of
+ * the code run. One bounded line per launch, emitted when the content is
+ * suspicious (all-zero samples, or the first four entry words identical -
+ * a page that never landed or a repeated filler word) or for the first few
+ * healthy launches per boot (so the healthy signature is visible without
+ * flooding). A missing host-readable landing (hpa 0 on the entry page)
+ * logs CODEHASH-UNREADABLE. sh0 is the engine-visible shadow page's entry
+ * word when the Tier-2 copy exists (NULL on the Tier-1/guest path).
+ * noinline: keeps the (warning-bound) submit frame unchanged.
+ */
+static bool gsp_phys_gr_codehash_done;
+static noinline void
+gsp_phys_gr_codehash(const struct gsp_phys_gr_submit *req,
+ const struct gsp_phys_gr_page *pg, const u8 *sh_page)
+{
+ static u32 ch_checked, ch_logged, ch_unread;
+ u64 va, h0 = 0, ent = 0, sh0 = 0;
+ u32 off, n = 0, w[4] = { 0, 0, 0, 0 };
+ bool zero3 = false, suspect = false;
+ const u8 *p = NULL;
+ u32 i;
+
+ if (gsp_phys_gr_codehash_done)
+ return;
+ gsp_phys_gr_codehash_done = true;
+ ch_checked++;
+ off = (u32)(req->qmd[48] & (GSP_PHYS_PAGE_SIZE - 1));
+ va = (u64)req->qmd[48] | ((u64)(req->qmd[49] & 0x1ffffu) << 32);
+ if (!pg->hpa) {
+ if (ch_unread < 32) {
+ ch_unread++;
+ pr_info("vgsp-gr: CODEHASH-UNREADABLE va=0x%llx off=0x%x (entry page has no host-readable landing)\n",
+ va, off);
+ }
+ return;
+ }
+ p = (const u8 *)__va(pg->hpa);
+ n = 1;
+ ent = get_unaligned_le64(p + off);
+ h0 = get_unaligned_le64(p);
+ for (i = 0; i < 4; i++)
+ w[i] = get_unaligned_le32(p + off + i * 4);
+ zero3 = !(get_unaligned_le64(p) | get_unaligned_le64(p + 8) |
+ get_unaligned_le64(p + 0x800) |
+ get_unaligned_le64(p + 0x808) |
+ get_unaligned_le64(p + 0xff0) |
+ get_unaligned_le64(p + 0xff8));
+ for (i = 1; i < 4; i++) {
+ u64 tva = pg->guest_va + ((u64)i << 12);
+ u32 j;
+
+ for (j = 0; j < req->n_pages; j++)
+ if (req->pages[j].guest_va == tva &&
+ req->pages[j].hpa)
+ break;
+ if (j == req->n_pages)
+ break;
+ {
+ const u8 *q = (const u8 *)__va(req->pages[j].hpa);
+
+ n++;
+ if (get_unaligned_le64(q) || get_unaligned_le64(q + 8) ||
+ get_unaligned_le64(q + 0x800) ||
+ get_unaligned_le64(q + 0x808) ||
+ get_unaligned_le64(q + 0xff0) ||
+ get_unaligned_le64(q + 0xff8))
+ zero3 = false;
+ }
+ }
+ if (sh_page)
+ sh0 = get_unaligned_le64(sh_page + off);
+ /*
+ * Conservative plausibility test: all three sampled windows zero; or
+ * the first two entry words zero; or the first four entry words
+ * identical (an all-zero page also lands here).
+ */
+ suspect = zero3 || (!w[0] && !w[1]) ||
+ (w[0] == w[1] && w[1] == w[2] && w[2] == w[3]);
+ if (!suspect && ch_logged >= 3)
+ return;
+ if (ch_logged >= 64)
+ return;
+ ch_logged++;
+ pr_info("vgsp-gr: CODEHASH va=0x%llx pa=0x%llx n=%u h0=0x%016llx ent=0x%016llx off=0x%x w=%08x %08x %08x %08x sh0=0x%016llx %s (first 16B)\n",
+ va, pg->hpa, n, h0, ent, off, w[0], w[1], w[2], w[3], sh0,
+ suspect ? "SUSPECT" : "ok");
+}
+
int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
struct gsp_phys_backend_copy_result *out)
{
@@ -17965,6 +18058,7 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
fast_rearmed = false;
deposit_rb_done = false;
+ gsp_phys_gr_codehash_done = false; /* CODEHASH: once per launch */
reloc_gap = 0;
reloc_gap_first = 0;
/*
@@ -18154,9 +18248,16 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
if (pg->hpa) {
ret = gsp_phys_rmcall_gr_map_va_locked(bar0, pg_va,
pg->hpa, epoch);
- if (!ret)
+ if (!ret) {
mapped = true;
- else if (ret == -ENOSPC)
+ /*
+ * CODEHASH: on the Tier-1 identity path the
+ * engine reads the guest page itself; sample
+ * the entry before the QMD pair is authored.
+ */
+ if (pg->qmd_word == 48u)
+ gsp_phys_gr_codehash(req, pg, NULL);
+ } else if (ret == -ENOSPC)
gsp_phys_gr_pool_exhausted++;
}
/*
@@ -18356,6 +18457,12 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
w[27], w[28], w[29], w[30], w[31],
w[32], w[33], w[34], w[35], w[36],
w[37], w[38], w[39]);
+ /*
+ * CODEHASH: the engine-visible code copy (sh_page)
+ * is authored here - sample source + shadow
+ * before the QMD pair points at this page.
+ */
+ gsp_phys_gr_codehash(req, pg, sh_page);
}
if (pg->hpa && pg->qmd_word == 32u) {
const u32 *w = (const u32 *)sh_page;