summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_sema_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gsp_sema_service.c')
-rw-r--r--kernel/gsp_sema_service.c207
1 files changed, 207 insertions, 0 deletions
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;