summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_phys_booter.c188
-rw-r--r--kernel/gsp_sema_service.c56
2 files changed, 227 insertions, 17 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 494cb25..e7879cc 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -25633,6 +25633,42 @@ static u64 gsp_phys_rmcall_gr_prog_next_hpa_locked(
return 0;
}
+/*
+ * r1494: resolve one guest page through the same chain the relocation pass
+ * uses: the launch's own page set (copy-in authored pages), the tracked
+ * data recs, the GR ledger/pin/walk resolver, then the late-resolved gap
+ * pages. 0 = not resolvable. Unlike prog_next_hpa this takes any page, not
+ * just page0+4K.
+ */
+static u64 gsp_phys_rmcall_gr_stream_page_hpa_locked(
+ const struct gsp_phys_gr_submit *req, u64 page)
+{
+ u32 i;
+
+ for (i = 0; i < req->n_pages; i++)
+ if ((req->pages[i].guest_va &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1)) == page &&
+ req->pages[i].hpa)
+ return req->pages[i].hpa;
+ for (i = 0; i < req->n_data; i++)
+ if ((req->data[i].va &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1)) == page &&
+ req->data[i].hpa)
+ return req->data[i].hpa;
+ {
+ struct gsp_sema_gr_reloc r;
+
+ if (!gsp_sema_gr_reloc_resolve(page, &r) && r.hpa)
+ return r.hpa;
+ }
+ for (i = 0; i < gsp_phys_gr_gap_n; i++)
+ if ((gsp_phys_gr_gap[i].va &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1)) == page &&
+ gsp_phys_gr_gap[i].hpa)
+ return gsp_phys_gr_gap[i].hpa;
+ return 0;
+}
+
static void gsp_phys_rmcall_gr_alias_cache_set_locked(u64 hpa, u64 va)
{
u32 i;
@@ -25652,6 +25688,109 @@ static void gsp_phys_rmcall_gr_alias_cache_set_locked(u64 hpa, u64 va)
}
/*
+ * r1494: a vertex stream in the MME payload is
+ * [stream, end_hi, end_lo, base_hi, base_lo]; END and BASE are both client
+ * VAs and the MME derives the stream length from end - base. The generic
+ * per-word payload pass relocated each end independently to its own
+ * single-page shadow fetch line, so any stream that crosses a 4 KB
+ * boundary (the client's vertex buffers here are 0x600..0x2d2c bytes and
+ * several cross) came out with its two ends in unrelated shadow windows:
+ * end - base went negative, and the VTG's fetch past the base page hit an
+ * invalid PTE (the 0xbadf1201 miss=1 priv-error seen once the real VS
+ * started executing). Map the whole [base, end) range into ONE fresh
+ * fetch line (up to 16 x 4 KB) and rewrite BOTH words from that line, so
+ * the size the MME computes is the client's real size and every page of
+ * the stream is engine-visible at base+offset. A page that cannot be
+ * resolved maps the module zero page: a zero attribute is a degenerate
+ * vertex, never a fault. Returns the line base VA; caller consumes one
+ * shadow slot.
+ */
+static u64 gsp_phys_rmcall_gr_stream_line_locked(
+ const struct gsp_phys_gr_submit *req, u64 base_va, u64 end_va,
+ u32 slot)
+{
+ struct gsp_phys_boot_chunk *sh = &boot_stage.rmcall_gr_shadow;
+ u64 base_page = base_va & ~(u64)(GSP_PHYS_PAGE_SIZE - 1);
+ u64 end_page = (end_va - 1u) & ~(u64)(GSP_PHYS_PAGE_SIZE - 1);
+ u64 hpa[GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES];
+ u64 va0, dummy = 0;
+ u32 npages, i, line, page, line_in_page, dummy_n = 0;
+ u8 *pt;
+ int ret;
+
+ if (end_va <= base_va)
+ return 0;
+ npages = (u32)((end_page - base_page) >> 12) + 1u;
+ if (npages > GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES)
+ return 0;
+ for (i = 0; i < npages; i++) {
+ hpa[i] = gsp_phys_rmcall_gr_stream_page_hpa_locked(req,
+ base_page + ((u64)i << 12));
+ if (!hpa[i]) {
+ if (!dummy)
+ dummy = gsp_phys_gr_surf_dummy_dma_locked();
+ if (!dummy)
+ return 0;
+ hpa[i] = dummy;
+ dummy_n++;
+ }
+ }
+ if (!sh->pages) {
+ ret = gsp_phys_chunk_alloc(boot_stage.pdev, sh,
+ GSP_PHYS_RMCALL_GR_SHADOW_PAGES);
+ if (ret)
+ return 0;
+ gsp_phys_chunk_zero(sh);
+ }
+ if (slot >= sh->pages || !boot_stage.rmcall_va_guest_pt)
+ return 0;
+ line = boot_stage.rmcall_guest_pt_next_line;
+ if (line >= GSP_PHYS_RMCALL_GUEST_PT_TOTAL_LINES)
+ return 0;
+ page = line / GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE;
+ line_in_page = line % GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE;
+ ret = gsp_phys_rmcall_gr_scratch_win_install_locked(page);
+ if (ret)
+ return 0;
+ pt = gsp_phys_rmcall_guest_pt_page_va(page);
+ if (!pt)
+ return 0;
+ for (i = 0; i < npages; i++)
+ put_unaligned_le64(gsp_phys_rmcall_gr_pte_leaf(hpa[i]),
+ pt + (size_t)(
+ GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES *
+ line_in_page + i) * 8);
+ dma_wmb();
+ boot_stage.rmcall_guest_pt_next_line = line + 1u;
+ va0 = gsp_phys_rmcall_guest_pt_win_va(page) +
+ ((u64)line_in_page << 16);
+ for (i = 0; i < npages; i++)
+ gsp_phys_rmcall_gr_alias_cache_set_locked(hpa[i],
+ va0 + ((u64)i << 12));
+ {
+ static u32 stream_line_log;
+ u32 w[8] = { 0 }, k;
+ u64 hpa0 = hpa[0];
+
+ if (pfn_valid((unsigned long)(hpa0 >> PAGE_SHIFT))) {
+ const u8 *p = (const u8 *)__va(
+ hpa0 + (base_va & (GSP_PHYS_PAGE_SIZE - 1)));
+
+ for (k = 0; k < 8u; k++)
+ w[k] = get_unaligned_le32(p + (size_t)k * 4u);
+ }
+ if (stream_line_log < 32u) {
+ stream_line_log++;
+ pr_info("vgsp-gr: MME-VSTREAM-LINE h=0x%x base=0x%llx end=0x%llx pages=%u dummy=%u line=0x%llx hpa0=0x%llx first8=%08x %08x %08x %08x %08x %08x %08x %08x\n",
+ req ? req->h_object : 0u, base_va, end_va,
+ npages, dummy_n, va0, hpa0, w[0], w[1], w[2],
+ w[3], w[4], w[5], w[6], w[7]);
+ }
+ }
+ return va0;
+}
+
+/*
* Allocate (or reuse) the pair line. Returns the line base VA on success,
* 0 when the second half cannot be resolved or the pool is exhausted (the
* caller then falls through to the single-page path). The fresh line is
@@ -26770,12 +26909,57 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
if (READ_ONCE(gsp_phys_rmcall_gr_mme_reloc) &&
addr >= 0x3800u && addr < 0x3a00u) {
u32 wi;
+ bool stream_pair = false;
+ /*
+ * r1494: VERTEX_STREAM macro(2) carries
+ * [stream, end_hi, end_lo, base_hi, base_lo]
+ * where END and BASE are client VAs (the MME
+ * computes the stream length as end - base).
+ * Relocating the two words independently put
+ * a page-crossing stream's ends in unrelated
+ * shadow windows: end - base went negative
+ * and the VTG fetch past the base page read
+ * an invalid PTE -> 0xbadf1201 miss=1 once
+ * the real VS ran. Map the whole range into
+ * one fresh contiguous line and rewrite both
+ * words from it; fall back to the historical
+ * per-word pass only when the range cannot be
+ * mapped.
+ */
+ if (addr == 0x3810u && cnt >= 5u &&
+ !d[1] && !d[3]) {
+ u64 bva = (u64)d[4];
+ u64 eva = (u64)d[2];
+ u64 lva;
+
+ if (bva >= 0x10000u && eva > bva &&
+ (eva - bva) <= 0x10000u &&
+ !(bva & 3u) && !(eva & 3u) &&
+ *n_shadow <
+ GSP_PHYS_RMCALL_GR_SHADOW_PAGES &&
+ (lva = gsp_phys_rmcall_gr_stream_line_locked(
+ req, bva, eva,
+ *n_shadow)) != 0) {
+ u64 bpage = bva &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1);
+
+ d[2] = (u32)(lva + (eva - bpage));
+ d[4] = (u32)(lva + (bva &
+ (GSP_PHYS_PAGE_SIZE - 1)));
+ (*n_shadow)++;
+ stream_pair = true;
+ reloc += 2;
+ }
+ }
for (wi = 0; wi < cnt; wi++) {
u32 wv = d[wi];
u32 up = 0, lo = wv;
static u32 mme_log;
+ if (stream_pair &&
+ (wi == 2u || wi == 4u))
+ continue;
if (wv < 0x10000u || (wv & 3u))
continue;
if (!gsp_phys_rmcall_gr_3d_reloc(
@@ -26897,9 +27081,9 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
if (addr == 0x3810u && cnt >= 5u) {
static u32 vstream_log;
- if (vstream_log < 8u) {
+ if (vstream_log < 24u) {
vstream_log++;
- pr_info("vgsp-gr: MME-VSTREAM h=0x%x stream=%u limit=%08x:%08x addr=%08x:%08x (post-reloc)\n",
+ pr_info("vgsp-gr: MME-VSTREAM h=0x%x stream=%u end=%08x:%08x base=%08x:%08x (post-reloc)\n",
req ? req->h_object : 0u,
d[0], d[1], d[2],
d[3], d[4]);
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 5bc387e..dd02aa0 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -15344,6 +15344,16 @@ static u32 svc_gl_cb_copy_h;
static u32 svc_gl_cb_copy_val;
static u32 svc_gl_cb_copy_hash;
static u32 svc_gl_cb_copy_log;
+/*
+ * r1494: the last fixed-CB page that resolved through a SYS (engine-page)
+ * source and passed the matrix gate. The client VA can move/reuse after
+ * the first frame (r1492 read back a method page with the same VA and
+ * r1493 fell back to identity), so a later resolution miss must not zero
+ * the seed page: reuse this page for the rest of the boot. Only a page
+ * that was never credibly resolved falls back to the synthetic identity.
+ */
+static u8 svc_gl_cb_good[SEMA_PAGE_SIZE];
+static bool svc_gl_cb_good_valid;
static u32 svc_gl_cb_hash(const u8 *p, u32 n)
{
@@ -15558,33 +15568,49 @@ have:
* r1491: a source whose 0x80 block is not a credible matrix would
* still collapse every vertex. Only a SYS (engine-page) read is
* trusted; an FB-shadow read is by construction not the engine's
- * page, so author the identity positive control there too.
- */
- if ((ssrc == 4 || ssrc == 5 || ssrc == 6 || ssrc == 7) ||
- !svc_gl_cb_matrix_ok(buf)) {
+ * page, so it must not become the seed either.
+ *
+ * r1494: split "is this the real CB?" from "what do we write?".
+ * A validated SYS page becomes the boot's remembered real CB. When
+ * a later resolution misses (or answers with a method page), write
+ * that remembered page again instead of the identity fallback -
+ * the fallback may only run when no real CB was ever resolved this
+ * boot. Do not skip the write on an unchanged hash: the seed page
+ * lives in the client's screen allocation and the guest can have
+ * rewritten it between launches, so "same content as last time" is
+ * not the same as "page still holds it".
+ */
+ if (ssrc != 4 && ssrc != 5 && ssrc != 6 && ssrc != 7 &&
+ svc_gl_cb_matrix_ok(buf)) {
+ memcpy(svc_gl_cb_good, buf, sizeof(svc_gl_cb_good));
+ svc_gl_cb_good_valid = true;
+ } else if (svc_gl_cb_good_valid) {
+ memcpy(buf, svc_gl_cb_good, sizeof(svc_gl_cb_good));
+ src = "cached-real";
+ ssrc = 9;
+ } else {
memset(buf, 0, sizeof(buf));
put_unaligned_le32(0x3f800000u, buf + 0x80); /* m00 */
put_unaligned_le32(0x3f800000u, buf + 0x94); /* m11 */
put_unaligned_le32(0x3f800000u, buf + 0xa8); /* m22 */
put_unaligned_le32(0x3f800000u, buf + 0xbc); /* m33 */
src = "synthetic-id";
+ ssrc = 7;
}
hash = svc_gl_cb_hash(buf, sizeof(buf));
- if (svc_gl_cb_copy_h == h_object && svc_gl_cb_copy_val == val &&
- svc_gl_cb_copy_hash == hash)
- return;
+ (void)hash;
rc = svc_grce_inline_write(vgpu, sema_gl_cb_copy_to, buf, sizeof(buf),
h_object);
- if (svc_gl_cb_copy_log < 24u) {
- u32 w0, w1;
+ if (svc_gl_cb_copy_log < 96u) {
+ u32 w[8];
- memcpy(&w0, buf, 4);
- memcpy(&w1, buf + 4, 4);
+ memcpy(w, buf, sizeof(w));
svc_gl_cb_copy_log++;
- pr_info("vgsp-sema: sema-gl: cb-copy src=0x%llx dst=0x%llx pa=0x%llx ap=%u hpa=0x%llx ssrc=%d first=%08x %08x src=%s write=%d (fixed-CB page -> forced seed page)\n",
- src_va, sema_gl_cb_copy_to, pa, ap, hpa, ssrc, w0, w1,
- src, rc);
- if (svc_gl_cb_copy_log <= 4u)
+ pr_info("vgsp-sema: sema-gl: cb-copy src=0x%llx dst=0x%llx pa=0x%llx ap=%u hpa=0x%llx ssrc=%d first8=%08x %08x %08x %08x %08x %08x %08x %08x src=%s write=%d cached=%d (fixed-CB page -> forced seed page)\n",
+ src_va, sema_gl_cb_copy_to, pa, ap, hpa, ssrc,
+ w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7],
+ src, rc, svc_gl_cb_good_valid ? 1 : 0);
+ if (svc_gl_cb_copy_log <= 12u)
svc_gl_cb_dump_matrix(buf, src, pa, ap, ssrc);
}
if (!rc) {