summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_phys_booter.c163
-rw-r--r--kernel/gsp_phys_booter.h18
-rw-r--r--kernel/gsp_phys_gpu.h8
-rw-r--r--kernel/gsp_sema_service.c207
-rw-r--r--kernel/gsp_sema_service.h10
5 files changed, 404 insertions, 2 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 5a30d40..5c1941a 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -17906,6 +17906,33 @@ MODULE_PARM_DESC(sema_gl_sph_version,
"Emit the Turing channel-init SET_SPH_VERSION/CHECK_SPH_VERSION pair (0x16a4/0x16a8 <- 0x00040004) ahead of each replayed 3D window (default 1; A/B with 0 reproduces the Shader Program Header 9/11/18 fault)");
/*
+ * SESSION 153-CONTINUED-87: the missing TESS_CONTROL/PIXEL program binds.
+ * The client's real channel state binds every pipeline stage once per
+ * context; the captured init-push image (guest RAM scrape) carries
+ * `20020825 [0, 04400200]` (j=2 TESS_CONTROL) and
+ * `20020855 [0, 04400000]` (j=5 PIXEL) beside the SPH version pair -
+ * but the captured per-frame window carries only the j=1 (VTG) pair
+ * (0x4400900/0x4400300). The DS validates the SPH of every pipeline
+ * stage the draw activates, so the unbound stages latch their header
+ * errors (DS_HWW_ESR 0xa2040a00 = SPH9|SPH11|SPH18) while the V3 blob
+ * passes wherever a stage IS bound. Emit both binds ahead of the
+ * window at the window's own program-family page (all four VAs share
+ * one 4K page: 0x4400000 j=5, 0x4400200 j=2, 0x4400300/0x4400900 j=1),
+ * pushed through the same relocation pass as the window - a MISS skips
+ * the bind entirely (never a raw client VA). Beef client channels only
+ * (the family layout is the client's). Default 1; A/B with 0.
+ */
+static bool gsp_phys_rmcall_gr_sph_bind = true;
+module_param_named(sema_gl_sph_bind, gsp_phys_rmcall_gr_sph_bind, bool, 0644);
+MODULE_PARM_DESC(sema_gl_sph_bind,
+ "Emit the client's missing TESS_CONTROL (j=2) and PIXEL (j=5) pipeline program binds ahead of replayed 3D windows (program-family page of the window's prog VA; default 1)");
+
+bool gsp_phys_rmcall_gr_sph_bind_value(void)
+{
+ return READ_ONCE(gsp_phys_rmcall_gr_sph_bind);
+}
+
+/*
* SESSION 153-CONTINUED-84: SET_MME_SHADOW_RAM_CONTROL mode before the
* window. The vendor comment (mesh shader bind path) is explicit:
* "MME filtering swallows them all if old and new state values are the
@@ -18553,10 +18580,27 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
* it exactly.
*/
if (READ_ONCE(gsp_phys_rmcall_gr_sph_version)) {
+ /*
+ * SESSION 153-CONTINUED-87: the window's accepted
+ * version range must COVER the images the engine
+ * will fetch. The client's own init push writes
+ * 4|(4<<16), but our authored (V3) images are
+ * rejected by a 4/4 window (SPH9|11|18) while the
+ * r950-era acceptance - same V3 image - ran with
+ * NO version window. Emit oldest=V(blob),
+ * current=max(V(blob),4) so both the V3 image and
+ * a future V4 re-tag stay inside the range.
+ */
+ u32 bv = (u32)gsp_sema_gl_sph_blob_version();
+ u32 val = 0x00040004u;
+
+ if (bv >= 1u && bv <= 15u)
+ val = ((bv < 4u ? 4u : bv) << 16) | bv;
+
*p++ = ((u32)GSP_PHYS_RMCALL_METHOD_IMM << 29) |
(2u << 16) | (0x16a4u >> 2);
- *p++ = 0x00040004u;
- *p++ = 0x00040004u;
+ *p++ = val;
+ *p++ = val;
}
/*
* SESSION 153-CONTINUED-84: SET_MME_VERSION=1 (0x234).
@@ -23885,6 +23929,32 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
len >= 8 ? get_unaligned_le32(inl->data + 4) : 0u);
}
}
+ /*
+ * SESSION 153-CONTINUED-87: readback witness.
+ * For a stream3d window the engine's I-fetch
+ * walks the GUEST page (the inline bytes were
+ * just authored there above); read the exact
+ * engine-visible location back so the fetch
+ * content is provable from the log - if the DS
+ * still rejects the header, the bytes are known
+ * present at the VA it fetched.
+ */
+ if (stream3d && pg->hpa) {
+ static u32 inl_rb_log;
+ const u32 *gw = (const u32 *)
+ ((u8 *)__va(pg->hpa) +
+ (inl->dst_va &
+ (GSP_PHYS_PAGE_SIZE - 1)));
+
+ if (inl_rb_log < 12u) {
+ inl_rb_log++;
+ pr_info("vgsp-gr: INLINE-RB dst=0x%llx pa=0x%llx rb=%08x %08x %08x %08x %08x %08x %08x %08x\n",
+ inl->dst_va, pg->hpa,
+ gw[0], gw[1], gw[2],
+ gw[3], gw[4], gw[5],
+ gw[6], gw[7]);
+ }
+ }
copied++;
}
/*
@@ -24360,6 +24430,95 @@ gr_page_unmapped:
gsp_phys_rmcall_gr_3d_last_subdrop,
gsp_phys_rmcall_gr_3d_last_sdmdrop);
/*
+ * SESSION 153-CONTINUED-87: emit the client's missing
+ * pipeline-stage program binds (see the sema_gl_sph_bind
+ * comment). The window carries only the j=1 (VTG) pair; the
+ * real client state also binds j=2 (TESS_CONTROL at
+ * family+0x200) and j=5 (PIXEL at family+0x0) - both present
+ * verbatim in the init-push image scraped from guest RAM
+ * (`20020825 [0,04400200]`, `20020855 [0,04400000]`). Build
+ * the two runs and push them through the same window
+ * relocation pass (miss -> the pair stays raw and the whole
+ * bind is skipped, never a raw client VA to the engine),
+ * then prepend them ahead of the window. Beef client
+ * channels only: the family layout is that client's.
+ */
+ if (READ_ONCE(gsp_phys_rmcall_gr_sph_bind) &&
+ (req->h_object & 0xfffff000u) == 0xbeef0000u &&
+ req->prog_va &&
+ win3d_n + 40u + 13u <= GSP_PHYS_RMCALL_GR_METHOD_WORDS) {
+ static u32 sph_bind_log;
+ u64 fam = req->prog_va &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1);
+ u32 in[40], out[40];
+ u32 nfin = 0, nrun = 0, on = 0, odrop = 0, orel;
+ u32 bi;
+
+ /*
+ * SESSION 153-CONTINUED-87 (second half): the window's
+ * OWN binds first. The captured per-frame window
+ * carries draws (BEGIN at word 93) BEFORE its program
+ * binds (j=1 pair at word 184): on real hardware the
+ * bind happened in an earlier push, but the replay
+ * channel's fresh context has nothing bound when the
+ * early draws validate their stages - the
+ * Shader-Program-Header poison (r1277+ 9/11). Re-emit
+ * every bind the window itself carries ahead of it.
+ */
+ for (bi = 0; bi < req->n_prog_binds && bi < 8u; bi++) {
+ u64 va = req->prog_bind_va[bi];
+ u32 sl = req->prog_bind_slot[bi] & 0x1fu;
+
+ if (!va || nfin + 3u > 40u)
+ continue;
+ in[nfin++] = 0x20000000u | (2u << 16) |
+ ((0x2014u + sl * 0x40u) >> 2);
+ in[nfin++] = (u32)(va >> 32) & 0xffu;
+ in[nfin++] = (u32)va & 0xfffffffcu;
+ nrun++;
+ }
+ /*
+ * Then the client's missing stage binds (see the
+ * sema_gl_sph_bind comment): TESS_CONTROL (j=2 at
+ * family+0x200) and PIXEL (j=5 at family+0x0), both
+ * present verbatim in the init-push image scraped
+ * from guest RAM (`20020825 [0,04400200]`,
+ * `20020855 [0,04400000]`).
+ */
+ if (nfin + 6u <= 40u) {
+ in[nfin++] = 0x20000000u | (2u << 16) |
+ (0x2094u >> 2);
+ in[nfin++] = (u32)((fam + 0x200u) >> 32) & 0xffu;
+ in[nfin++] = (u32)(fam + 0x200u) & 0xfffffffcu;
+ in[nfin++] = 0x20000000u | (2u << 16) |
+ (0x2154u >> 2);
+ in[nfin++] = (u32)(fam >> 32) & 0xffu;
+ in[nfin++] = (u32)fam & 0xfffffffcu;
+ nrun += 2;
+ }
+ orel = gsp_phys_rmcall_gr_3d_patch_window(
+ in, nfin, out, &on, &odrop, req, &n_shadow);
+ if (orel == nrun && on == nfin) {
+ memmove(&stream_patch[nfin], stream_patch,
+ (size_t)win3d_n * 4u);
+ memcpy(stream_patch, out, (size_t)nfin * 4u);
+ win3d_n += nfin;
+ if (sph_bind_log < 16u) {
+ sph_bind_log++;
+ pr_info("vgsp-gr: SPH-BIND emitted fam=0x%llx win-binds=%u j2=0x%llx j5=0x%llx win=%u h=0x%x push=0x%llx\n",
+ fam, req->n_prog_binds,
+ fam + 0x200u, fam,
+ win3d_n, req->h_object,
+ req->push_va);
+ }
+ } else if (sph_bind_log < 16u) {
+ sph_bind_log++;
+ pr_info("vgsp-gr: SPH-BIND skipped (reloc=%u/%u kept=%u/%u fam=0x%llx h=0x%x push=0x%llx)\n",
+ orel, nrun, on, nfin, fam,
+ req->h_object, req->push_va);
+ }
+ }
+ /*
* SESSION 153-CONTINUED-62: surface (render-target) prelude -
* see the gsp_phys_gr_surf_pre comment. Injected only when the
* window carries no usable colour-target bind of its own; the
diff --git a/kernel/gsp_phys_booter.h b/kernel/gsp_phys_booter.h
index 39824e3..073f839 100644
--- a/kernel/gsp_phys_booter.h
+++ b/kernel/gsp_phys_booter.h
@@ -485,6 +485,24 @@ struct gsp_phys_gr_submit {
*/
const u32 *stream;
u32 stream_n; /* dwords */
+ /*
+ * SESSION 153-CONTINUED-87: the first program VA the captured
+ * window binds (SET_PIPELINE_PROGRAM_ADDRESS pair; 0 when none).
+ * The launch builder uses its 4K page as the program family for
+ * the client's missing stage binds (sema_gl_sph_bind).
+ */
+ u64 prog_va;
+ /*
+ * SESSION 153-CONTINUED-87: every program bind the captured window
+ * itself carries (VA + pipeline slot). The launch builder re-emits
+ * them AHEAD of the window: the window's own draws (BEGIN 0x1618)
+ * precede its binds (measured word 93 vs 184), and on the replay
+ * channel's fresh context the early draws would validate an unbound
+ * stage and latch the Shader Program Header fault.
+ */
+ u32 n_prog_binds;
+ u64 prog_bind_va[8];
+ u32 prog_bind_slot[8];
};
/* True when gsp_phys_gr_forward is set and the ladder is a live GR channel
diff --git a/kernel/gsp_phys_gpu.h b/kernel/gsp_phys_gpu.h
index 4d9f47b..b8d92cd 100644
--- a/kernel/gsp_phys_gpu.h
+++ b/kernel/gsp_phys_gpu.h
@@ -340,6 +340,14 @@ int gsp_phys_rmcall_faultbuf_sys_value(void);
* (proven small-PT line) instead of the window-1 scratch range (default 0). */
int gsp_phys_rmcall_gr_shadow_win0_value(void);
+/*
+ * SESSION 153-CONTINUED-87: emit the client's missing TESS_CONTROL (j=2)
+ * and PIXEL (j=5) pipeline program binds ahead of replayed 3D windows
+ * (sema_gl_sph_bind, default 1). The sema service uses the same value to
+ * author the matching program images at those family VAs.
+ */
+bool gsp_phys_rmcall_gr_sph_bind_value(void);
+
/* m91: keep the forwarded QMD pointer pairs at the guest VAs (default 0). */
int gsp_phys_rmcall_gr_ptr_guest_value(void);
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 12a7368..d097726 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -7350,6 +7350,9 @@ struct svc_gr_data_win {
u64 reloc_base; /* relocated window base */
};
+/* CONT-87: max program binds carried from a captured window (mirrors
+ * SVC_GL_PROG_MAX; defined here because the candidate struct precedes it). */
+#define SVC_GR_CAND_PROG_BINDS 8
struct svc_gr_cand_rec {
bool valid;
bool offered; /* consumed by a request or a fallback */
@@ -7380,6 +7383,20 @@ struct svc_gr_cand_rec {
bool stream_3d;
u32 stream_off; /* dword offset of the window inside raw[] */
u32 stream_n; /* dwords of the replayable window */
+ /*
+ * SESSION 153-CONTINUED-87: every SET_PIPELINE_PROGRAM_ADDRESS pair
+ * the captured window itself binds (VA + pipeline slot), copied from
+ * svc_gl at capture. The launch builder re-emits them AHEAD of the
+ * window: the captured per-frame window carries draws (BEGIN 0x1618)
+ * BEFORE its own program binds (measured: draw at window word 93,
+ * j=1 pair at word 184), on the real hardware the bind already
+ * happened in an earlier push - on the replay channel's fresh
+ * context the early draws would validate an UNBOUND stage and latch
+ * the Shader Program Header fault (the r1277+ 9/11 wall).
+ */
+ u32 n_prog_binds;
+ u64 prog_bind_va[SVC_GR_CAND_PROG_BINDS];
+ u32 prog_bind_slot[SVC_GR_CAND_PROG_BINDS];
u32 raw[SVC_GR_PUSH_WORDS];
u32 qmd[SVC_GR_QMD_WORDS];
u64 qmd_dev_va;
@@ -7397,6 +7414,7 @@ struct svc_gr_cand_rec {
u32 pointer_word[SVC_GR_POINTER_MAX]; /* QMD word of the pair */
};
+
/*
* fail4: the capture was a single slot; two concurrent launches (streams2's
* vadd pair on two channels) overwrote each other's candidate while the
@@ -8223,6 +8241,33 @@ static void svc_gl_prog_apply_param(void)
}
/*
+ * SESSION 153-CONTINUED-87: the SPH VERSION of the live program images.
+ * The launch builder emits SET_SPH_VERSION from this value so the
+ * window's accepted version range always covers the images the engine
+ * is about to fetch. The client's own init push carries 4|(4<<16), but
+ * the r950 gate proved the V3 image executes; a 4/4 window over a V3
+ * SPH rejects every fetched header (SPH9|SPH11|SPH18, r1273-r1275) -
+ * the r950-era acceptance ran with no version window at all. Returns
+ * the max VERSION field of the loaded blobs (0 = none).
+ */
+int gsp_sema_gl_sph_blob_version(void)
+{
+ u32 v = 0, ver;
+
+ if (svc_gl_vs_len >= 4) {
+ ver = (get_unaligned_le32(svc_gl_vs) >> 5) & 0x1fu;
+ if (ver > v)
+ v = ver;
+ }
+ if (svc_gl_ps_len >= 4) {
+ ver = (get_unaligned_le32(svc_gl_ps) >> 5) & 0x1fu;
+ if (ver > v)
+ v = ver;
+ }
+ return (int)v;
+}
+
+/*
* SESSION 153 (GL client draw sections): the GL client's own per-frame
* pushes bind TURING_A (0xC597) exactly like the X server's one-shot
* section, but carry no program/param/release operands at all (measured:
@@ -9840,6 +9885,40 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
u32 bl = 0;
u8 t = svc_gl_shader_type[svc_gl.prog_slot[pi] < 32 ?
svc_gl.prog_slot[pi] : 0];
+ /*
+ * SESSION 153-CONTINUED-87: the REAL program upload
+ * wins. When the window carries its own upload of a
+ * program-sized payload (>= 128 B) to this same 4K
+ * page, the minimal authored image must NOT overwrite
+ * the driver's bytes at the engine-visible page (the
+ * upload now lands there through the pin-aware
+ * svc_grce_inline_write). Keep an explicit image only
+ * where no real upload exists (the synthesized stage
+ * binds below).
+ */
+ {
+ u32 uu;
+ bool real_up = false;
+
+ for (uu = 0; uu < svc_gl.n_up; uu++) {
+ if (svc_gl.up[uu].len < 128u)
+ continue;
+ if ((svc_gl.up[uu].dst_va &
+ ~(u64)(SEMA_PAGE_SIZE - 1)) ==
+ (svc_gl.progs[pi] &
+ ~(u64)(SEMA_PAGE_SIZE - 1))) {
+ real_up = true;
+ break;
+ }
+ }
+ if (real_up) {
+ if (gsp_sema_dbg &&
+ svc_gl_prog_log < 64)
+ pr_info("sema-gl: real-upload wins va=0x%llx (authored image skipped)\n",
+ svc_gl.progs[pi]);
+ continue;
+ }
+ }
if (t == 5 && svc_gl_ps_len) {
b = svc_gl_ps;
@@ -9862,6 +9941,64 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
memcpy(prs[n_recs].data, b, bl);
n_recs++;
}
+ /*
+ * SESSION 153-CONTINUED-87: author the program images for the
+ * client's missing stage binds. The replayed window binds only
+ * the j=1 (VTG) pair; the booter emits the client's own
+ * TESS_CONTROL (j=2, family+0x200) and PIXEL (j=5, family+0x0)
+ * binds ahead of the window (sema_gl_sph_bind), so their SPH
+ * bytes must exist at those VAs too - otherwise the DS fetches
+ * an empty/foreign page and still latches its header error.
+ * Observed family layout (guest-RAM scrape of the init push
+ * and the window itself): 0x4400000 j=5, 0x4400200 j=2,
+ * 0x4400300/0x4400900 j=1 - all in the window prog VA's own
+ * 4K page. The TESS image is the VTG image with the SPH
+ * SHADER_TYPE field (bits 13:10) re-pointed to 2, exactly
+ * like the driver's own placeholder bind. Beef client
+ * channels only, and only when the window does not already
+ * bind that exact VA.
+ */
+ if (gsp_phys_rmcall_gr_sph_bind_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++) {
+ 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;
+ bool dup = false;
+ u32 pi3;
+
+ if (!b || bl < 4u)
+ continue;
+ for (pi3 = 0; pi3 < svc_gl.n_progs; pi3++)
+ if (svc_gl.progs[pi3] == dst)
+ dup = true;
+ if (dup)
+ continue;
+ memset(&prs[n_recs], 0, sizeof(prs[n_recs]));
+ prs[n_recs].dst_va = dst;
+ prs[n_recs].len = bl;
+ memcpy(prs[n_recs].data, b, bl);
+ if (k) {
+ u32 w0 = get_unaligned_le32(
+ prs[n_recs].data);
+
+ w0 = (w0 & ~(0xfu << 10)) |
+ (2u << 10); /* TESS_CONTROL */
+ put_unaligned_le32(w0, prs[n_recs].data);
+ }
+ n_recs++;
+ if (gsp_sema_dbg && svc_gl_prog_log < 64) {
+ pr_info("sema-gl: stage-image dst=0x%llx slot=%u len=%u first=%08x\n",
+ dst, k ? 2u : 5u, bl,
+ get_unaligned_le32(prs[n_recs - 1].data));
+ }
+ }
+ }
if (n_recs && gsp_sema_dbg && svc_gl_prog_log < 64) {
u32 pi2;
@@ -9924,6 +10061,26 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
svc_gr_cand_capture(ch, &s2, n_recs ? prs : NULL, n_recs, buf, len,
push_va);
/*
+ * CONT-87: carry the window's own program binds so the launch
+ * builder can re-emit them ahead of the window (the window's early
+ * draws precede its binds; see svc_gr_cand_rec).
+ */
+ {
+ u32 pb;
+
+ svc_gr_cand.n_prog_binds = 0;
+ for (pb = 0; pb < svc_gl.n_progs &&
+ pb < SVC_GR_CAND_PROG_BINDS; pb++) {
+ if (!svc_gl.progs[pb])
+ continue;
+ svc_gr_cand.prog_bind_va[svc_gr_cand.n_prog_binds] =
+ svc_gl.progs[pb];
+ svc_gr_cand.prog_bind_slot[svc_gr_cand.n_prog_binds] =
+ svc_gl.prog_slot[pb];
+ svc_gr_cand.n_prog_binds++;
+ }
+ }
+ /*
* r929 (Path A): a stream-derived capture (op_src=stream: the section
* carried its own operands) is a raw 3D method window. Mark it for
* verbatim replay - but only when the WHOLE push survived in raw[]
@@ -11735,6 +11892,27 @@ static int svc_grce_inline_write(struct nv_vgpu_device *vgpu, u64 dst_va,
goto write_landing;
}
}
+ /*
+ * SESSION 153-CONTINUED-87: the launch fill resolves a page through
+ * the LIVE PINS first ("pin wins", see svc_gr_page_note) and the
+ * engine reads exactly that page. An upload whose trusted landing is
+ * a VIDMEM aperture page must also reach the pinned engine-visible
+ * page, or the engine validates stale content: the real program
+ * uploads (1536 B at 0x4350900, 1408 B at 0x4350f00) currently land
+ * only in the BAR1 shadow while the fill hands the engine the pinned
+ * sysmem page. Write to the pin when it names a different page.
+ */
+ {
+ u64 ipa = 0;
+ int iap = SVC_APERTURE_SYS;
+
+ if (!svc_ipin_lookup(dst_va & ~0xfffULL, &ipa, &iap) &&
+ iap != SVC_APERTURE_FB && ipa) {
+ pa = ipa & ~0xfffULL;
+ ap = SVC_APERTURE_SYS;
+ goto write_landing;
+ }
+ }
if (svc_lookup_trusted(vgpu, dst_va & ~0xfffULL, &pa, &ap)) {
if (!svc_vas_write(dst_va, data, bytes)) {
svc.vas_shadowed++;
@@ -26991,6 +27169,10 @@ struct svc_fwd_req {
u64 gr_rel_pa;
u8 gr_rel_ap; /* SVC_APERTURE_* of gr_rel_pa */
u64 gr_prog_va;
+ /* CONT-87: the window's own program binds (see svc_gr_cand_rec). */
+ u32 gr_n_prog_binds;
+ u64 gr_prog_bind_va[SVC_GR_CAND_PROG_BINDS];
+ u32 gr_prog_bind_slot[SVC_GR_CAND_PROG_BINDS];
u64 gr_param_va;
u32 gr_param_size;
u32 gr_qmd[SVC_GR_QMD_WORDS];
@@ -29787,6 +29969,19 @@ static void svc_fwd_gr_work(void)
gs.stream = svc_fwd_req.gr_raw + svc_fwd_req.gr_stream_off;
gs.stream_n = svc_fwd_req.gr_stream_n;
}
+ gs.prog_va = svc_fwd_req.gr_prog_va; /* CONT-87: family binds */
+ gs.n_prog_binds = svc_fwd_req.gr_n_prog_binds;
+ {
+ u32 pb;
+
+ for (pb = 0; pb < svc_fwd_req.gr_n_prog_binds && pb < 8u;
+ pb++) {
+ gs.prog_bind_va[pb] =
+ svc_fwd_req.gr_prog_bind_va[pb];
+ gs.prog_bind_slot[pb] =
+ svc_fwd_req.gr_prog_bind_slot[pb];
+ }
+ }
gs.h_object = svc_fwd_req.gr_h_object;
/*
@@ -32243,6 +32438,18 @@ static int svc_gr_req_fill(struct nv_vgpu_device *vgpu,
svc_fwd_req.gr_stream_3d = svc_gr_cand.stream_3d;
svc_fwd_req.gr_stream_off = svc_gr_cand.stream_off;
svc_fwd_req.gr_stream_n = svc_gr_cand.stream_n;
+ svc_fwd_req.gr_n_prog_binds = svc_gr_cand.n_prog_binds;
+ {
+ u32 pb;
+
+ for (pb = 0; pb < svc_gr_cand.n_prog_binds &&
+ pb < SVC_GR_CAND_PROG_BINDS; pb++) {
+ svc_fwd_req.gr_prog_bind_va[pb] =
+ svc_gr_cand.prog_bind_va[pb];
+ svc_fwd_req.gr_prog_bind_slot[pb] =
+ svc_gr_cand.prog_bind_slot[pb];
+ }
+ }
memcpy(svc_fwd_req.gr_raw, svc_gr_cand.raw,
sizeof(svc_fwd_req.gr_raw));
svc_fwd_req.gr_qmd_dev_va = svc_gr_cand.qmd_dev_va;
diff --git a/kernel/gsp_sema_service.h b/kernel/gsp_sema_service.h
index 257a0ef..0f22904 100644
--- a/kernel/gsp_sema_service.h
+++ b/kernel/gsp_sema_service.h
@@ -535,6 +535,16 @@ bool gsp_sema_gr_surf_recs(u64 *va, u32 *n);
u64 gsp_sema_mme_sa_get(u32 *entries, u32 n);
/*
+ * SESSION 153-CONTINUED-87: the SPH VERSION of the live program images
+ * (sema_gl_vs/ps_hex blobs), so the GR launch builder emits a
+ * SET_SPH_VERSION window that MATCHES the images the engine will fetch
+ * (the client's own init push says 4/4, but the r950 gate proved the V3
+ * images execute; a 4/4 window over a V3 SPH reproduces the
+ * SPH9|SPH11|SPH18 fault). 0 when no image is loaded.
+ */
+int gsp_sema_gl_sph_blob_version(void);
+
+/*
* SESSION 153-CONTINUED-68: furthest IR instruction covered by the CAPTURED
* 0x114 uploads (pc + ceil(code words / 3)). The GR launch builder places
* its end-noop macro stub at this address (aligned past the replayed