diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-05 14:31:10 -0500 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-05 14:31:10 -0500 |
| commit | c126441814ded6498b2e06ebb2a33ee05faac994 (patch) | |
| tree | 878fde7d5a0951ae07dbc4b27707a03df0a90c3a /kernel/gsp_phys_booter.c | |
| parent | 22f77ffbf5ea2a3f6f678b825c7d296c16471175 (diff) | |
| download | open-iov-c126441814ded6498b2e06ebb2a33ee05faac994.tar.gz open-iov-c126441814ded6498b2e06ebb2a33ee05faac994.zip | |
vgsp-gr: keep the validated fixed CB across launches and map each VTG stream contiguously
- Remember the last fixed-CB page that resolved through a SYS source and
passed the matrix gate; a later resolution miss (or a method-page answer)
reuses it instead of zeroing the seed page with the synthetic identity.
Only a boot with no validated read ever uses the identity fallback. Do not
skip the write on an unchanged hash: the seed page lives in the client's
screen allocation and the guest can rewrite it between launches.
- Treat the CALL_MME_MACRO(2) vertex stream as one range: map [base,end) into
a fresh contiguous fetch line and rewrite both ends from it, preserving the
client's stream length across 4 KB boundaries. Unresolvable pages map the
module zero page (a zero vertex, never a fault). The generic per-word pass
remains the fallback.
- Diagnostics: cb-copy logs the first 8 seed-page words and the cached flag;
MME-VSTREAM-LINE dumps the resolved base page (client VA, HPA, page count,
first 8 words).
- Evidence: r1494 (poison 11/17 -> 1/16, real vertex floats at the stream
bases, CB cache verified live with src=vaswalk -> src=cached-real) and
r1495/r1496 (the active draw's vertex/index page is an all-zero vidmem
shadow page while the data exists in guest sysmem - the new wall).
Diffstat (limited to 'kernel/gsp_phys_booter.c')
| -rw-r--r-- | kernel/gsp_phys_booter.c | 188 |
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]); |
