diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/gsp_phys_booter.c | 188 | ||||
| -rw-r--r-- | kernel/gsp_sema_service.c | 56 |
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) { |
