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.c188
1 files changed, 186 insertions, 2 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]);