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.c223
1 files changed, 221 insertions, 2 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index d097726..8fc663f 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -7263,7 +7263,13 @@ static struct svc_fwd_cand svc_fwd_cand;
*/
#define SVC_GR_QMD_WORDS 64
#define SVC_GR_INLINE_MAX 8
-#define SVC_GR_INLINE_BYTES 256
+/*
+ * SESSION 153-CONTINUED-88: 256 -> 2048 (static_asserts below tie it to
+ * GSP_BRIDGE_INLINE_BYTES / GSP_PHYS_GR_INLINE_BYTES). The GL window's real
+ * program upload payloads are 1536 B / 1408 B; the 256 B cap truncated them
+ * to their first 256 bytes, so the engine never saw the full image.
+ */
+#define SVC_GR_INLINE_BYTES 2048
#define SVC_GR_POINTER_MAX 8
#define SVC_GR_PAGES_MAX 16
/*
@@ -7591,6 +7597,16 @@ static int svc_grce_inline_write(struct nv_vgpu_device *vgpu, u64 dst_va,
static bool svc_inline_once_seen(u64 push_va, u64 hash, u64 dst, u32 bytes);
static void svc_inline_once_store(u64 push_va, u64 hash, u64 dst, u32 bytes);
+/*
+ * SESSION 153-CONTINUED-88: newest captured inline-write payload landing
+ * exactly at `dst_va` with at least `min_len` bytes (defined next to the
+ * destination ring below). Used by svc_gl_flush to deliver the window's REAL
+ * program uploads (the driver's own SPH+SASS images) to the engine-visible
+ * page instead of the minimal authored blob.
+ */
+static bool svc_grce_dst_newest(u64 dst_va, u32 min_len, u32 *len, u8 *buf,
+ u32 buf_sz);
+
/* QMD field decode: the exact shape the host CUDA bridge uses. */
static void svc_gr_qmd_decode(struct svc_gr_cand_rec *c)
{
@@ -9435,7 +9451,14 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
u32 at_off)
{
struct svc_push_state s2;
- struct gsp_bridge_inline prs[SVC_GL_PROG_MAX];
+ /*
+ * SESSION 153-CONTINUED-88: static (was stack). With the inline
+ * record grown to 2048 B the 8-record array is ~16 KB - too much for
+ * a kernel stack; this function runs on the single service thread
+ * (same reason as the static recs[] in svc_gr_forward and the ops
+ * array in svc_decode_stream), so a static array is safe.
+ */
+ static struct gsp_bridge_inline prs[SVC_GL_PROG_MAX];
u32 n_recs = 0, k;
/*
* SESSION 149 R16 (M3): the inline attachment's source. Priority:
@@ -9886,6 +9909,98 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
u8 t = svc_gl_shader_type[svc_gl.prog_slot[pi] < 32 ?
svc_gl.prog_slot[pi] : 0];
/*
+ * SESSION 153-CONTINUED-88: deliver the REAL captured
+ * upload payload to the engine-visible page. The
+ * window's own program uploads (the client's 0x180
+ * LINE_LENGTH_IN + 0x1b0 LOAD_INLINE_DATA runs -
+ * observed 1536 B at 0x4350900 and 1408 B at
+ * 0x4350f00, w0=0x020a0481 = the driver's real SPH
+ * V4/VTG image) were captured at decode time into the
+ * destination ring but landed only in the BAR1 shadow;
+ * the engine validates the SPH of every bound program
+ * at the FILL-resolved page. Attach the real payload
+ * as an inline record at its own destination VA - the
+ * backend authors it into the engine-visible page on
+ * the stream3d path - and let it win over the minimal
+ * authored image below. Exact-VA match: the bind VA
+ * IS the upload destination.
+ */
+ {
+ u32 rlen = 0;
+ u32 ri0 = n_recs;
+
+ memset(&prs[n_recs], 0, sizeof(prs[n_recs]));
+ if (svc_grce_dst_newest(svc_gl.progs[pi], 128u,
+ &rlen, prs[n_recs].data,
+ (u32)sizeof(prs[n_recs].data))) {
+ u64 d = svc_gl.progs[pi];
+ u32 head = 4096u - (u32)(d & 0xfff);
+
+ if (rlen <= head) {
+ prs[n_recs].dst_va = d;
+ prs[n_recs].len = rlen;
+ n_recs++;
+ } else {
+ /*
+ * The payload crosses its
+ * 4K page (observed: the
+ * 1408 B image at
+ * 0x4350f00). The backend
+ * applies a record only
+ * within one page, so
+ * split: page remainder +
+ * tail at the next page's
+ * VA (skipped by the
+ * backend when that page
+ * is not in the request -
+ * the SPH head still
+ * lands).
+ */
+ prs[n_recs].dst_va = d;
+ prs[n_recs].len = head;
+ n_recs++;
+ if (n_recs <
+ GSP_BRIDGE_INLINE_MAX) {
+ memset(&prs[n_recs], 0,
+ sizeof(prs[n_recs]));
+ prs[n_recs].dst_va =
+ (d & ~(u64)0xfff) +
+ 0x1000;
+ prs[n_recs].len =
+ rlen - head;
+ memcpy(prs[n_recs].data,
+ prs[n_recs - 1].data + head,
+ rlen - head);
+ n_recs++;
+ }
+ /*
+ * SESSION 153-CONTINUED-88:
+ * the tail record is applied
+ * only when its page is in the
+ * launch's page set. Add the
+ * tail page as a candidate
+ * operand page (the engine
+ * will fetch the program's
+ * second half from it), so
+ * the request carries it and
+ * the split delivery is
+ * complete instead of
+ * head-only.
+ */
+ svc_gl_add_ptr((d & ~(u64)0xfff) +
+ 0x1000);
+ }
+ if (gsp_sema_dbg &&
+ svc_gl_prog_log < 64) {
+ svc_gl_prog_log++;
+ pr_info("sema-gl: real-upload record va=0x%llx len=%u first=%08x (engine-visible delivery)\n",
+ d, rlen,
+ get_unaligned_le32(prs[ri0].data));
+ }
+ continue;
+ }
+ }
+ /*
* 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
@@ -11854,6 +11969,44 @@ static int svc_copy_1d(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t tot
* VA through the trusted sources (pins, discovered map, published page
* directories, targeted rm_mem resolve), pin the page and write the bytes.
*/
+/*
+ * SESSION 153-CONTINUED-88: write an inline payload into one landing page
+ * (page-aligned PA, aperture) at the destination VA's in-page offset. Shared
+ * by the primary landing write and the engine-visible pin dual-write. The
+ * write is clamped to the page end: a crossing payload's tail belongs to the
+ * NEXT page's own landing, not to whatever follows this page in the aperture.
+ */
+static int svc_grce_land_write(struct nv_vgpu_device *vgpu, u64 pa_page,
+ int ap, u64 dst_va, const u8 *data, u32 bytes)
+{
+ u64 pa = pa_page + (dst_va & 0xfff);
+ u32 room = 4096u - (u32)(dst_va & 0xfff);
+
+ if (bytes > room)
+ bytes = room;
+ if (!bytes)
+ return 0;
+ if (ap == SVC_APERTURE_FB) {
+ u8 *base = vgpu->bars[1].vaddr;
+
+ if (!base || pa + bytes > svc_fb_shadow_size(vgpu))
+ return -EINVAL;
+ memcpy(base + pa, data, bytes);
+ return 0;
+ }
+ {
+ u32 o;
+
+ for (o = 0; o < bytes; o += 4096) {
+ u32 chunk = bytes - o > 4096 ? 4096 : bytes - o;
+
+ if (svc_write(vgpu, pa + o, (void *)(data + o), chunk))
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
static int svc_grce_inline_write(struct nv_vgpu_device *vgpu, u64 dst_va,
const u8 *data, u32 bytes, u32 ch_h)
{
@@ -11888,6 +12041,45 @@ static int svc_grce_inline_write(struct nv_vgpu_device *vgpu, u64 dst_va,
pa = lpa & ~0xfffULL;
ap = (lap == SVC_APERTURE_FB) ?
SVC_APERTURE_FB : SVC_APERTURE_SYS;
+ /*
+ * SESSION 153-CONTINUED-88: dual-write to the
+ * engine-visible page. 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 - a ledger landing in the BAR1 shadow
+ * (ap=vidmem) is NOT what the engine fetches. The
+ * observed case: the real program uploads (1536 B at
+ * 0x4350900, 1408 B at 0x4350f00) landed at pa
+ * 0x1b0900/0x1b0f00 (vidmem) while the engine read
+ * the pinned sysmem page (0x529ff4000). When an
+ * injected pin names a different page, author the
+ * bytes there too (the ledger landing stays written
+ * for its existing consumers).
+ */
+ {
+ u64 ipa = 0;
+ int iap = SVC_APERTURE_SYS;
+
+ if (!svc_ipin_lookup(dst_va & ~0xfffULL,
+ &ipa, &iap) &&
+ ipa && (ipa & ~0xfffULL) !=
+ (pa & ~0xfffULL)) {
+ if (!svc_grce_land_write(vgpu,
+ ipa & ~0xfffULL,
+ iap, dst_va,
+ data, bytes) &&
+ gsp_sema_dbg &&
+ svc.grce_log < 64) {
+ svc.grce_log++;
+ pr_info("sema-grce: inline dual-write dst=0x%llx pa=0x%llx ap=%s bytes=%u (engine-visible pin) h=0x%x\n",
+ dst_va,
+ ipa & ~0xfffULL,
+ iap == SVC_APERTURE_FB ?
+ "vidmem" : "ram",
+ bytes, ch_h);
+ }
+ }
+ }
svc_pin_store(dst_va & ~0xfffULL, pa, ap);
goto write_landing;
}
@@ -12987,6 +13179,33 @@ struct svc_grce_rec {
static struct svc_grce_rec svc_grce_dst_ring[SVC_GRCE_DST_MAX];
static u32 svc_grce_dst_ring_next;
+/*
+ * SESSION 153-CONTINUED-88: newest captured inline-write payload landing
+ * exactly at `dst_va` with at least `min_len` bytes. Newest-first scan so a
+ * fresh upload always wins over a stale ring entry. Copies the payload into
+ * the caller's buffer (buf_sz bounds the copy) and returns true on a hit.
+ */
+static bool svc_grce_dst_newest(u64 dst_va, u32 min_len, u32 *len, u8 *buf,
+ u32 buf_sz)
+{
+ u32 ri;
+
+ for (ri = 0; ri < SVC_GRCE_DST_MAX; ri++) {
+ u32 idx = (svc_grce_dst_ring_next + SVC_GRCE_DST_MAX - 1u -
+ ri) % SVC_GRCE_DST_MAX;
+ const struct svc_grce_rec *r = &svc_grce_dst_ring[idx];
+
+ if (r->len < min_len || !r->dst_va)
+ continue;
+ if (r->dst_va != dst_va)
+ continue;
+ *len = min_t(u32, r->len, buf_sz);
+ memcpy(buf, r->data, *len);
+ return true;
+ }
+ return false;
+}
+
static void svc_counter_learn(struct nv_vgpu_device *vgpu, u32 h, u64 sem_va);
static void svc_pending_add(u64 va, u32 payload, u32 op);
static int svc_rescue_push_page(struct nv_vgpu_device *vgpu,