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.c345
1 files changed, 343 insertions, 2 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index afe92b6..4125192 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -19425,6 +19425,64 @@ static void gsp_phys_gr_stall_log(const char *reason, u64 push_va,
}
/*
+ * SESSION 153-CONTINUED-27: big-surface alias bookkeeping. A replayed
+ * 3D window's colour target (C597 INC 0x800+j*0x40 [upper, lower, w, h,
+ * fmt] and its TWOD twin INC 0x218 [w, h, upper, lower]) can be a surface
+ * far larger than the launch's 16-page set (the X screen 0x120400000,
+ * 1280x720x4 = 900 pages). Two earlier routes are dead ends for it:
+ * - one-page shadow relocation (r992: `pair addr=0x840 va=0x120400000
+ * -> MISS` then FAULT_PDE @ +0xa000 - the raster walk crosses the
+ * single alias page);
+ * - Tier-1 windows at the surface's own VAs (CONT.27 r993: PDE_UPDATE
+ * for pdeIndex 2306/2307 status=0x0, yet GPC2/GPC5 still fault
+ * FAULT_PDE @ +0x4000/+0xa000 - same class as the r929 v2 FE faults;
+ * the GPC engines only see the scratch-PT pool).
+ * The surviving route is the proven one: a CONTIGUOUS run of never-used
+ * scratch-PT fetch lines (one line = 16 PTEs = 64 KB VA; lines are
+ * monotonic and consecutive lines are VA-contiguous inside each pool
+ * block), one PTE per surface page, so the engine reaches all 900 pages
+ * through one contiguous alias VA and the raster walk never crosses into
+ * an unmapped page. The pair operands are rewritten to <alias>|<offset>
+ * by gsp_phys_rmcall_gr_3d_reloc. The table is per-boot: the alias PTEs
+ * are never rewritten (fetch-line rule) and the screen's HPAs are fixed
+ * by its single allocation, so every later launch is a pure rewrite.
+ */
+struct gsp_phys_gr_surf_keep {
+ u64 va; /* guest surface base VA (page aligned) */
+ u64 alias; /* engine-visible contiguous shadow alias base */
+ u64 npages; /* pages aliased (declared + slack tail) */
+};
+static struct gsp_phys_gr_surf_keep gsp_phys_gr_surf[4];
+static u32 gsp_phys_gr_surf_n;
+
+/*
+ * SESSION 153-CONTINUED-27: slack tail for the alias. r994 measured the
+ * replay's colour writes landing past the declared w*h*4 footprint
+ * (1280x720x4 = 0x384000) by up to +0xE000 (11.2 rows) - FAULT_PTE on
+ * the last fetch line's unwritten PTE slots (0x5d6000..0x5e2000 with the
+ * alias at 0x250000). The tail pages are aliased too: a page that still
+ * resolves (inside the guest allocation's own padding - exactly where
+ * native overdraw would land) maps the real HPA; one that does not
+ * resolves to a dedicated zeroed module page so the stray writes vanish
+ * instead of faulting. The chunk is allocated lazily and lives until
+ * module unload (4 KB).
+ */
+#define GSP_PHYS_GR_SURF_SLACK_PAGES 32u
+static struct gsp_phys_boot_chunk gsp_phys_gr_surf_dummy;
+
+static u64 gsp_phys_gr_surf_dummy_dma_locked(void)
+{
+ struct gsp_phys_boot_chunk *d = &gsp_phys_gr_surf_dummy;
+
+ if (!d->pages) {
+ if (gsp_phys_chunk_alloc(boot_stage.pdev, d, 1u))
+ return 0;
+ gsp_phys_chunk_zero(d);
+ }
+ return (u64)gsp_phys_chunk_page_dma(d, 0);
+}
+
+/*
* r929 v3 (Path A): relocate one 3D-window address operand to a shadow-pool
* VA. The guest VAs the 3D methods name (uploads 0x1_214f8f00.., vertex,
* program, tex pools) are installed in the ladder page tree by the Tier-1
@@ -19447,6 +19505,25 @@ static bool gsp_phys_rmcall_gr_3d_reloc(u32 *up_w, u32 *lo_w,
if (!va || !page)
return false;
+ /*
+ * SESSION 153-CONTINUED-27: a big surface (the X screen) is aliased
+ * as ONE contiguous run by the pre-pass; rewrite the operand to the
+ * alias base | offset. A one-page shadow relocation of it would
+ * break the raster walk at the first page crossing (r992), and the
+ * raw VA has no PTE chain the GPC engines can see (r993).
+ */
+ for (i = 0; i < gsp_phys_gr_surf_n; i++) {
+ if (va >= gsp_phys_gr_surf[i].va &&
+ va < gsp_phys_gr_surf[i].va +
+ (gsp_phys_gr_surf[i].npages << 12)) {
+ sh = gsp_phys_gr_surf[i].alias +
+ (va - gsp_phys_gr_surf[i].va);
+ *lo_w = (*lo_w & 0x3u) | ((u32)sh & 0xfffffffcu);
+ *up_w = (*up_w & ~0xffu) |
+ ((u32)(sh >> 32) & 0xffu);
+ return true;
+ }
+ }
for (i = 0; i < req->n_pages; i++) {
if ((req->pages[i].guest_va &
~(u64)(GSP_PHYS_PAGE_SIZE - 1)) != page ||
@@ -19531,6 +19608,48 @@ static bool gsp_phys_rmcall_gr_win_drop_match(u32 addr)
}
/*
+ * SESSION 153-CONTINUED-27: auto-drop of the software-emulated lanes from
+ * every verbatim 3D replay. Three method triggers in a captured window
+ * cause the physical engine to repeat work the sema service already
+ * executes in software - and, on the replay channel, with the WRONG
+ * context state (no previous-window sticky registers):
+ * - 0x1b0 LAUNCH_DMA (C597 CE lane): triggers both the CE copy and the
+ * LOAD_INLINE_DATA upload. The service authors every LID payload into
+ * the guest pages itself (svc_gl_lid/svc_grce_inline_write) and
+ * emulates the CE strips (svc_op2d); physically re-running them with
+ * an unset source/context walks unbounded memory (r992-r996: the
+ * "march" - faults at whatever page the alias cap ended, e.g.
+ * 0x5d6000/0x5f4000, after having written every mapped page).
+ * - 0x5e0 (TWOD RECTS fill trigger): the second point pair of the
+ * client's screen clear. The service's FERMI_TWOD executor runs every
+ * fill (r986: 46x `sema-twod-fill ... done=3686400 rc=0`); the
+ * physical re-run uses the replay channel's unset dst PITCH and
+ * marches (same fault class).
+ * - 0x10c NOTIFY: the completion write; without the preceding
+ * SET_NOTIFY_A/B (which live in other pushes) the engine writes
+ * address 0 (r996: `faulted @ 0x0_00000000 FAULT_PTE VIRT_WRITE`).
+ * The kick-notify lane + the backend's release tail author every
+ * completion the client actually waits on.
+ * All three are pure triggers - their setup methods stay in the window,
+ * so dropping the run can never misalign display state. The emulated
+ * lanes are the live ones (the r986 pixel proof: the screen paints via
+ * svc_twod fills, and every CE strip retires rc=0 in software). Default
+ * on; set 0 to replay the raw window unmodified.
+ */
+static bool gsp_phys_gr_win_drop_auto = 1;
+module_param_named(sema_gl_win_drop_auto, gsp_phys_gr_win_drop_auto, bool,
+ 0644);
+MODULE_PARM_DESC(sema_gl_win_drop_auto,
+ "Drop the software-emulated trigger methods (0x10c NOTIFY, 0x1b0 LAUNCH_DMA, 0x5e0 TWOD fill) from verbatim 3D replays (default 1)");
+
+static bool gsp_phys_rmcall_gr_win_drop_auto_match(u32 addr)
+{
+ if (!READ_ONCE(gsp_phys_gr_win_drop_auto))
+ return false;
+ return addr == 0x010cu || addr == 0x01b0u || addr == 0x05e0u;
+}
+
+/*
* SESSION 153-CONTINUED-14 (FECS residency / draw-stall isolation): patch
* the DATA of one method inside a replayed 3D window before it reaches the
* engine. Format: comma-separated `addr:data` hex pairs, addr = method
@@ -19627,7 +19746,8 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
if ((op == 1 || op == 3 || op == 5) && cnt &&
off + 1u + cnt <= n)
span = 1u + cnt;
- if (gsp_phys_rmcall_gr_win_drop_match(addr)) {
+ if (gsp_phys_rmcall_gr_win_drop_match(addr) ||
+ gsp_phys_rmcall_gr_win_drop_auto_match(addr)) {
dropped++;
off += span;
continue;
@@ -19703,6 +19823,18 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
* Observed [1 222fa200 40 4 f7 0]. */
lo_i = 0;
pair = true;
+ } else if (op == 1 && addr == 0x218 && cnt >= 4 &&
+ (d[2] & ~0xffu) == 0u &&
+ (d[3] & 0xfffu) == 0u) {
+ /* SESSION 153-CONTINUED-27: the TWOD twin
+ * of the colour target - INC 0x218 cnt>=4
+ * covers 0x218..0x224 = [w, h, upper,
+ * lower] (the emulator's SET_DST sequence;
+ * the glxgears client window fills the X
+ * screen through it). Shape-checked so a
+ * non-surface run can never be rewritten. */
+ lo_i = 2;
+ pair = true;
}
if (pair) {
u64 pva = ((u64)(d[lo_i] & 0xffu) << 32) |
@@ -19735,6 +19867,205 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
return reloc;
}
+/*
+ * SESSION 153-CONTINUED-27: alias-build for a big colour target. Consumes
+ * ceil(npages/16) never-used scratch-PT fetch lines and writes one PTE per
+ * surface page, so the whole surface is reachable through one CONTIGUOUS
+ * alias VA (lines are monotonic; consecutive lines are VA-contiguous
+ * inside a pool block - both pool blocks are checked). Every page's HPA
+ * comes from the launch's own page set when present, else from the
+ * service's trusted resolver - the identical vetted translation the
+ * shadow relocation uses. Lines are consumed even on failure (they are
+ * never reused, so a half-written line can never be shadowed later);
+ * the caller then falls back to the existing per-page path. Called with
+ * gsp_phys_rm_lock held.
+ */
+static bool gsp_phys_gr_surface_alias_locked(const struct gsp_phys_gr_submit *req,
+ u64 va, u64 npages, u64 *alias_out)
+{
+ u64 map_pages = npages + GSP_PHYS_GR_SURF_SLACK_PAGES;
+ u32 nlines;
+ u32 base_line;
+ u32 i, j;
+ u64 first_va, last_va;
+
+ if (map_pages > 4096u)
+ map_pages = 4096u;
+ nlines = (u32)((map_pages +
+ GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES - 1) /
+ GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES);
+ base_line = boot_stage.rmcall_guest_pt_next_line;
+ if (!nlines || base_line + nlines > GSP_PHYS_RMCALL_GUEST_PT_TOTAL_LINES)
+ return false;
+ first_va = gsp_phys_rmcall_guest_pt_win_va(
+ base_line / GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE) +
+ ((u64)(base_line %
+ GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE) << 16);
+ last_va = gsp_phys_rmcall_guest_pt_win_va(
+ (base_line + nlines - 1u) /
+ GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE) +
+ ((u64)((base_line + nlines - 1u) %
+ GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE) << 16);
+ /* one contiguous run: never straddle a pool block boundary */
+ if (last_va != first_va + ((u64)(nlines - 1u) << 16))
+ return false;
+ /*
+ * Consume the lines before writing: a failure mid-way leaves the
+ * partially written lines consumed (never reused), so the monotonic
+ * line invariant - a cached line can never shadow a later mapping -
+ * holds unconditionally.
+ */
+ boot_stage.rmcall_guest_pt_next_line = base_line + nlines;
+ for (i = 0; i < nlines; i++) {
+ u32 line = base_line + i;
+ u32 page = line / GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE;
+ u32 lip = line % GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE;
+ u8 *pt;
+
+ if (gsp_phys_rmcall_gr_scratch_win_install_locked(page))
+ return false;
+ pt = gsp_phys_rmcall_guest_pt_page_va(page);
+ if (!pt)
+ return false;
+ for (j = 0; j < GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES; j++) {
+ u64 k = (u64)i * GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES +
+ j;
+ u64 pv, hpa = 0;
+ u32 pi;
+
+ if (k >= map_pages)
+ break;
+ pv = va + (k << 12);
+ for (pi = 0; pi < req->n_pages; pi++) {
+ if ((req->pages[pi].guest_va &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1)) == pv &&
+ req->pages[pi].hpa) {
+ hpa = req->pages[pi].hpa;
+ break;
+ }
+ }
+ if (!hpa) {
+ struct gsp_sema_gr_reloc r;
+
+ if (gsp_sema_gr_reloc_resolve(pv, &r) ||
+ !r.hpa) {
+ /*
+ * A declared page must resolve; a
+ * slack-tail page past the guest
+ * allocation gets the dummy so the
+ * engine's overdraw vanishes.
+ */
+ if (k < npages)
+ return false;
+ hpa = gsp_phys_gr_surf_dummy_dma_locked();
+ if (!hpa)
+ return false;
+ } else {
+ hpa = r.hpa;
+ }
+ }
+ put_unaligned_le64(
+ gsp_phys_rmcall_pte(hpa),
+ pt + (size_t)(GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES *
+ lip +
+ j) *
+ 8);
+ }
+ dma_wmb();
+ }
+ *alias_out = first_va;
+ return true;
+}
+
+/*
+ * SESSION 153-CONTINUED-27: pre-pass over a raw 3D window. Scans the
+ * colour-target runs (C597 INC 0x800+j*0x40 [upper, lower, w, h, fmt,
+ * blksz] and - same surface, shape seen on the TWOD subchannel - INC
+ * 0x218 cnt>=4 [w, h, upper, lower]) and aliases each big surface once
+ * per boot so the relocation pass can rewrite its operands to the alias
+ * (r992/r993 evidence: the X screen 0x120400000, 1280x720 fmt 0xe6, is
+ * the draw's render target AND the TWOD fill's destination). Small
+ * surfaces keep the historical one-pair shadow relocation. Called with
+ * gsp_phys_rm_lock held.
+ */
+static void gsp_phys_gr_surface_alias_scan_locked(
+ const u32 *win, u32 n, const struct gsp_phys_gr_submit *req)
+{
+ u32 off = 0;
+
+ while (off < n) {
+ u32 hdr = win[off];
+ u32 op = (hdr >> 29) & 0x7u;
+ u32 cnt = (hdr >> 16) & 0xfffu;
+ u32 addr = (hdr & 0x1fffu) << 2;
+ u32 span = 1u;
+
+ if ((op == 1 || op == 3 || op == 5) && cnt &&
+ off + 1u + cnt <= n) {
+ span = 1u + cnt;
+ } else {
+ off++;
+ continue;
+ }
+ if (op == 1 && ((addr >= 0x0800 && addr < 0x0c00 &&
+ (addr & 0x3fu) == 0u && cnt >= 4) ||
+ (addr == 0x218 && cnt >= 4))) {
+ const u32 *d = &win[off + 1u];
+ u64 va;
+ u32 w, h, fmt;
+ u64 bytes, npages;
+ u32 sdi;
+ u64 alias = 0;
+
+ if (addr == 0x218) {
+ va = ((u64)(d[2] & 0xffu) << 32) |
+ (u64)(d[3] & 0xfffff000u);
+ w = d[0];
+ h = d[1];
+ fmt = 0xe6u; /* TWOD run carries fmt on
+ * its own IMMD; the surface
+ * class here is 32bpp */
+ } else {
+ va = ((u64)(d[0] & 0xffu) << 32) |
+ (u64)(d[1] & 0xfffff000u);
+ w = d[2];
+ h = d[3];
+ fmt = d[4];
+ }
+ if (!va || (va & 0xfffu) || fmt != 0xe6u ||
+ !w || !h || w > 8192u || h > 8192u)
+ goto next;
+ bytes = (u64)w * 4u * (u64)h;
+ npages = (bytes + GSP_PHYS_PAGE_SIZE - 1) >> 12;
+ if (npages < 2u || npages > 4096u)
+ goto next;
+ for (sdi = 0; sdi < gsp_phys_gr_surf_n; sdi++)
+ if (gsp_phys_gr_surf[sdi].va == va)
+ goto next; /* already aliased */
+ if (gsp_phys_gr_surface_alias_locked(req, va, npages,
+ &alias)) {
+ if (gsp_phys_gr_surf_n <
+ ARRAY_SIZE(gsp_phys_gr_surf)) {
+ gsp_phys_gr_surf[gsp_phys_gr_surf_n].va =
+ va;
+ gsp_phys_gr_surf[gsp_phys_gr_surf_n]
+ .alias = alias;
+ gsp_phys_gr_surf[gsp_phys_gr_surf_n]
+ .npages = npages;
+ gsp_phys_gr_surf_n++;
+ }
+ pr_info("vgsp-gr: SURFACE alias va=0x%llx pages=%llu (%ux%u fmt=0x%x) -> 0x%llx (contiguous shadow run; operands rewritten)\n",
+ va, npages, w, h, fmt, alias);
+ } else {
+ pr_info("vgsp-gr: SURFACE alias FAILED va=0x%llx (%ux%u fmt=0x%x): operands fall to the per-page path\n",
+ va, w, h, fmt);
+ }
+ }
+next:
+ off += span;
+ }
+}
+
int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
struct gsp_phys_backend_copy_result *out)
{
@@ -20991,7 +21322,17 @@ gr_page_unmapped:
if (stream3d) {
u32 win_n = req->stream_n;
u32 ndrop = 0;
- u32 nrel = gsp_phys_rmcall_gr_3d_patch_window(
+ u32 nrel;
+
+ /*
+ * SESSION 153-CONTINUED-27: a big colour target (the X
+ * screen) is aliased as ONE contiguous shadow run BEFORE
+ * the relocation pass runs, so the pass rewrites its
+ * operands to the alias (see the table above).
+ */
+ gsp_phys_gr_surface_alias_scan_locked(req->stream,
+ req->stream_n, req);
+ nrel = gsp_phys_rmcall_gr_3d_patch_window(
req->stream, req->stream_n, stream_patch, &win_n,
&ndrop, req, &n_shadow);