From 88e0b15326a7bacb6c02f351b26a4b28a7933ad8 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Fri, 6 Nov 2026 20:09:42 -0500 Subject: vgsp-gl: synthesized GS SPH image + map variants for the SPH18 gate --- kernel/gsp_phys_booter.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++- kernel/gsp_phys_gpu.h | 14 +++++++ kernel/gsp_sema_service.c | 83 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 186 insertions(+), 4 deletions(-) diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index 1dedf7c..fdd8e88 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -18610,6 +18610,53 @@ module_param_named(sema_gl_sph_stage_disable, gsp_phys_rmcall_gr_sph_stage_disable, uint, 0644); MODULE_PARM_DESC(sema_gl_sph_stage_disable, "Per-stage synth-bind disable mask (bit j = synth slot j: 0=VSA,1=VSB,2=TCS,3=TES,4=GS,5=PS): emit the client's SET_PIPELINE_SHADER disable record for that stage and skip its synthesized bind (default 0; legacy bytes)"); + +/* + * SESSION 155 CONT 11: the last DS latch bit in the r1562 replay is SPH18 + * (GS check2, attribute map). r1562 proved the DS validates the GS stage + * even when the driver's own disable record (0x2100 <- 0x40) leaves it + * unbound: SPH18 latched with stage4 START=0. The client has no geometry + * program, so no GS SPH exists anywhere in its push. This knob binds a + * synthesized header-only GEOMETRY SPH (128 B, pass-through maps) to + * stage 4 while keeping SET_PIPELINE_SHADER(4) ENABLE=0, so the DS has a + * valid operand at SPH_INFO_STAGE4_START but the engine never executes + * it. The image is authored by the sema service at family+0x780 (same 4K + * page as the bound VTG/TCS/PS images) and the address pair below is + * relocated by the same window pass. Default 0 = legacy bytes. + */ +static bool gsp_phys_rmcall_gr_sph_gs_img; +module_param_named(sema_gl_sph_gs_img, gsp_phys_rmcall_gr_sph_gs_img, + bool, 0644); +MODULE_PARM_DESC(sema_gl_sph_gs_img, + "Bind a synthesized header-only GEOMETRY SPH to stage 4 with ENABLE=0 (family+0x780; satisfies the SPH18 GS attribute-map check; default 0)"); + +bool gsp_phys_rmcall_gr_sph_gs_img_value(void) +{ + return READ_ONCE(gsp_phys_rmcall_gr_sph_gs_img); +} + +/* + * SESSION 155 CONT 12: the synthesized GS SPH's attribute-map variant. + * The r1564 run proved the DS fetches the image (stage4 START nonzero) + * yet SPH18 persists and the VTG-class check2 bits return, so the map + * CONTENT is the discriminator: + * 0 = pass-through (IMAP = previous OMAP = POSITION+COLOR; + * OMAP = PS IMAP superset = POSITION+COLOR) + * 1 = all IMAP/OMAP zero (the mesh-dummy contract) + * 2 = PS-exact (IMAP = POSITION.W; OMAP = POSITION.XYZW) + * 3 = IMAP POSITION+COLOR, OMAP POSITION.XYZW only + * A/B live: the sema service reads this per capture. + */ +static u32 gsp_phys_rmcall_gr_sph_gs_maps; +module_param_named(sema_gl_sph_gs_maps, gsp_phys_rmcall_gr_sph_gs_maps, + uint, 0644); +MODULE_PARM_DESC(sema_gl_sph_gs_maps, + "Synth GS SPH attribute-map variant: 0=pass-through(POS+COLOR both), 1=zero maps, 2=PS-exact(POS.W/POS.XYZW), 3=IMAP POS+COLOR / OMAP POS.XYZW (default 0)"); + +u32 gsp_phys_rmcall_gr_sph_gs_maps_value(void) +{ + return READ_ONCE(gsp_phys_rmcall_gr_sph_gs_maps); +} static bool gsp_phys_gr_mme_pairs_emitted; /* launch emitted a pair run */ static u32 gsp_phys_gr_mme_pairs_gen; /* gen whose pairs retired OK */ static u32 gsp_phys_gr_mme_launch_gen; /* last built launch's gen */ @@ -29659,6 +29706,15 @@ gr_page_unmapped: (sph_bind_all || (sph_bind_mask & 0x01u)); bool emit_j4 = !(stage_disable_mask & 0x10u) && (sph_bind_all || (sph_bind_mask & 0x10u)); + /* + * SESSION 155 CONT 11: synthesized GS image bind + * (see the sema_gl_sph_gs_img comment). Independent + * of the j4 slot mask: it replaces the disable-only + * GS state with a disabled stage that still has a + * valid SPH operand. + */ + bool emit_gsimg = + READ_ONCE(gsp_phys_rmcall_gr_sph_gs_img); /* * SESSION 155 CONT 9: the prelude is prepended ahead of @@ -29861,7 +29917,8 @@ gr_page_unmapped: (emit_j0 ? (bind_binding ? 8u : 7u) : 0u) + (emit_j2 ? (bind_binding ? 8u : 7u) : 0u) + (emit_j4 ? (bind_binding ? 8u : 7u) : 0u) + - (emit_j5 ? (bind_binding ? 8u : 7u) : 0u) <= 96u) { + (emit_j5 ? (bind_binding ? 8u : 7u) : 0u) + + (emit_gsimg ? 9u : 0u) <= 96u) { /* * SESSION 155 CONT 9: pair_safe emits each * synthesized stage's SET_PIPELINE_SHADER @@ -30052,9 +30109,39 @@ gr_page_unmapped: (0x204cu >> 2); in[nfin++] = 0x1au; in[nfin++] = 0x0u; + /* + * SESSION 155 CONT 11: the synthesized + * header-only GEOMETRY SPH (128 B at + * family+0x780, authored by the sema + * service). Bind it to stage 4 and keep the + * stage disabled: the DS then validates a + * real GS SPH (its SPH18 check2 attribute + * map) while the engine does not execute the + * header-only image. Emitted after the + * stage-1 pair, before the address-pair + * census. The pair is the only relocatable + * run here (nrun++ below). + */ + if (emit_gsimg) { + u64 gva = fam + 0x780u; + + in[nfin++] = 0x20000000u | (1u << 16) | + (0x2100u >> 2); + in[nfin++] = 0x40u; + in[nfin++] = 0x20000000u | (1u << 16) | + (0x210cu >> 2); + in[nfin++] = 0x1au; + in[nfin++] = 0x20000000u | (2u << 16) | + (0x2114u >> 2); + in[nfin++] = (u32)(gva >> 32) & 0xffu; + in[nfin++] = (u32)gva & 0xfffffffcu; + in[nfin++] = 0x20000000u | (1u << 16) | + (0x2100u >> 2); + in[nfin++] = 0x40u; + } /* * nrun counts only the ADDRESS-bearing - * binds above: the guard below matches it + * binds: the guard below matches it * against the relocation pass's * relocated-pair count, and the constant * type runs can never be relocated. @@ -30067,6 +30154,8 @@ gr_page_unmapped: nrun++; if (emit_j5) nrun++; + if (emit_gsimg) + nrun++; } orel = gsp_phys_rmcall_gr_3d_patch_window( in, nfin, out, &on, &odrop, req, &n_shadow, diff --git a/kernel/gsp_phys_gpu.h b/kernel/gsp_phys_gpu.h index 6e16062..6cb6aef 100644 --- a/kernel/gsp_phys_gpu.h +++ b/kernel/gsp_phys_gpu.h @@ -348,6 +348,20 @@ int gsp_phys_rmcall_gr_shadow_win0_value(void); */ bool gsp_phys_rmcall_gr_sph_bind_value(void); +/* + * SESSION 155 CONT 11: bind a synthesized header-only GEOMETRY SPH to + * stage 4 with ENABLE=0 (sema_gl_sph_gs_img, default 0). The sema service + * uses this value to author the 128-byte image at family+0x780. + */ +bool gsp_phys_rmcall_gr_sph_gs_img_value(void); + +/* + * SESSION 155 CONT 12: attribute-map variant for the synthesized GS SPH + * (0=pass-through, 1=zero maps, 2=PS-exact, 3=IMAP POS+COLOR / OMAP + * POS.XYZW). The sema service reads this per capture. + */ +u32 gsp_phys_rmcall_gr_sph_gs_maps_value(void); + /* * r1512: the viewport state the 3D replay injects for the current launch * (sema_gl_vp_prelude mode + its literal words). Read-only; used by the diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index dafbad6..d8aa4dc 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -10579,14 +10579,16 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu, * channels only, and only when the window does not already * bind that exact VA. */ - if (gsp_phys_rmcall_gr_sph_bind_value() && + if ((gsp_phys_rmcall_gr_sph_bind_value() || + gsp_phys_rmcall_gr_sph_gs_img_value()) && (ch->h_object & 0xfffff000u) == 0xbeef0000u && svc_gl.progs[0]) { u64 fam = svc_gl.progs[0] & ~(u64)(SEMA_PAGE_SIZE - 1); u32 k; - for (k = 0; k < 2u && n_recs < SVC_GL_PROG_MAX; k++) { + for (k = 0; gsp_phys_rmcall_gr_sph_bind_value() && + k < 2u && n_recs < SVC_GL_PROG_MAX; k++) { u64 dst = fam + (k ? 0x200u : 0x0u); const u8 *b = k ? svc_gl_vs : svc_gl_ps; u32 bl = k ? svc_gl_vs_len : svc_gl_ps_len; @@ -10619,6 +10621,83 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu, get_unaligned_le32(prs[n_recs - 1].data)); } } + /* + * SESSION 155 CONT 11: the synthesized header-only + * GEOMETRY SPH at family+0x780. The client never + * emits a geometry program, but the DS validates + * the GS stage anyway (SPH18 = GS check2 attribute + * map) even when the stage is disabled. The booter + * binds this page to stage 4 with ENABLE=0, so the + * engine does not execute it. SPH V4T1 header with + * SHADER_TYPE=GEOMETRY (4), SASS_VERSION=5 and + * pass-through maps (IMAP = the VTG OMAP = + * POSITION+COLOR; OMAP = the PS IMAP superset + * POSITION+COLOR). 128 bytes, same 4K family page + * as the bound VTG/TCS/PS images. + */ + if (gsp_phys_rmcall_gr_sph_gs_img_value() && + n_recs < SVC_GL_PROG_MAX) { + u32 gs_words[32] = { + 0x000a1081u, 0x00000000u, + 0x01000000u, 0x07000000u, + 0x00000003u, 0xf0000000u, + 0x00000000u, 0x00000000u, + 0x00000000u, 0x00000000u, + 0x0000000fu, 0x00000000u, + 0x00000000u, 0x0000f000u, + 0x00000000u, 0x00000000u, + 0x00000000u, 0x000f0000u, + 0x00000000u, 0x00000000u, + 0x00000000u, 0x00000000u, + 0x00000000u, 0x00000000u, + 0x00000000u, 0x00000000u, + 0x00000000u, 0x00000000u, + 0x00000000u, 0x00000000u, + 0x00000000u, 0x00000000u, + }; + u32 gs_maps = + gsp_phys_rmcall_gr_sph_gs_maps_value(); + u64 dst = fam + 0x780u; + bool dup = false; + u32 pi4; + + /* + * SESSION 155 CONT 12: map A/B (see the + * sema_gl_sph_gs_maps comment). 0 keeps the + * pass-through header above. + */ + if (gs_maps == 1u) { + gs_words[5] = 0u; + gs_words[10] = 0u; + gs_words[13] = 0u; + gs_words[17] = 0u; + } else if (gs_maps == 2u) { + gs_words[5] = 0x80000000u; + gs_words[10] = 0u; + gs_words[13] = 0x0000f000u; + gs_words[17] = 0u; + } else if (gs_maps == 3u) { + gs_words[17] = 0u; + } + + for (pi4 = 0; pi4 < svc_gl.n_progs; pi4++) + if (svc_gl.progs[pi4] == dst) + dup = true; + if (!dup) { + memset(&prs[n_recs], 0, + sizeof(prs[n_recs])); + prs[n_recs].dst_va = dst; + prs[n_recs].len = 128u; + memcpy(prs[n_recs].data, gs_words, + sizeof(gs_words)); + n_recs++; + if (gsp_sema_dbg && + svc_gl_prog_log < 64) + pr_info("sema-gl: stage-image dst=0x%llx slot=4 len=128 maps=%u first=%08x w5=%08x w13=%08x\n", + dst, gs_maps, gs_words[0], + gs_words[5], gs_words[13]); + } + } } if (n_recs && gsp_sema_dbg && svc_gl_prog_log < 64) { u32 pi2; -- cgit v1.2.3