summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-28 06:12:02 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-28 06:12:02 -0400
commit595e023abff7d5f7e25ad1afd0907989115df541 (patch)
tree2666610b6e267135b33df9f4903bcbb6c72c1924
parent5a98e65007cde5526bb33c97d7d86233e869a0c3 (diff)
downloadopen-iov-595e023abff7d5f7e25ad1afd0907989115df541.tar.gz
open-iov-595e023abff7d5f7e25ad1afd0907989115df541.zip
vgsp: engine executes synthesized GL launches - inline upload applies + real SASS path (SESSION 149 r891)
- svc_gl_flush applies the section read upload list via svc_grce_inline_write; walker pairs subch0 0x180->0x1b0 runs on eng==0 channels and applies on sight (19/19 applied; once-only (push,hash,dst,bytes) dedupe). - sema_gl_prog_hex param + source priority trusted VA read > prog-hex > bounded LID fallback (12 B). - Evidence (r891): code64 = real sm_75 SASS (nvcc-assembled EXIT;NOP cubin); engine writes qmd_rel=0x600df00d ~500 ms after submit (executed + retired); 0 Illegal-Instruction lines (r890: 7). Completion still declared failed (rc=-110; ch_rel witness unwritten) -> SYNTH-FAULT + SW release; bounded. Feature gated by sema_gl_xlate (default off).
-rw-r--r--kernel/gsp_sema_service.c222
1 files changed, 221 insertions, 1 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 13d99cc..4674987 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -6640,6 +6640,15 @@ static bool svc_gr_cand_push_seen(u64 push_va, u64 hash);
/* SESSION 149 R16 (M3): trusted-walk VA read, defined below the decode. */
static int svc_read_va_trusted(struct nv_vgpu_device *vgpu, u64 va, void *buf,
size_t len);
+/*
+ * SESSION 149 R17 (M4): the section's memory-writing upload commands are
+ * applied through the shared inline-write path (defined below the section
+ * code) with its (push, hash, dst, bytes) once-only guard.
+ */
+static int svc_grce_inline_write(struct nv_vgpu_device *vgpu, u64 dst_va,
+ const u8 *data, u32 bytes, u32 ch_h);
+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);
/* QMD field decode: the exact shape the host CUDA bridge uses. */
static void svc_gr_qmd_decode(struct svc_gr_cand_rec *c)
@@ -6805,6 +6814,18 @@ module_param_named(sema_gl_xlate, gsp_sema_gl_xlate, bool, 0644);
MODULE_PARM_DESC(sema_gl_xlate,
"GL/3D (C597) direct stream -> synthesized GR candidate (default 0 = off)");
+/*
+ * SESSION 149 R17 (M4): one recorded section upload command. The 3D-class
+ * i2m shape is a 0x180 (LINE_LENGTH_IN) run naming the destination followed
+ * by a 0x1b0 (LAUNCH_DMA + LOAD_INLINE_DATA) run carrying the payload.
+ */
+#define SVC_GL_UPLOAD_MAX 24
+struct svc_gl_up {
+ u64 dst_va; /* from the 0x180 run's OFFSET_OUT_UPPER|OFFSET_OUT */
+ u32 len; /* payload bytes copied (payload = run data minus cfg) */
+ u8 data[SVC_GR_INLINE_BYTES];
+};
+
struct svc_gl_sec {
bool seen;
u32 subch;
@@ -6816,6 +6837,18 @@ struct svc_gl_sec {
u64 ptr[SVC_GR_POINTER_MAX];
u32 n_ptr;
/*
+ * SESSION 149 R17 (M4): the section's memory-writing upload commands
+ * collected in order. On real hardware the 3D engine executes these
+ * i2m copies (constants, tex pools, ...); in this emulation no engine
+ * ran them, so every destination stayed zero. svc_gl_flush applies
+ * the recorded bytes through svc_grce_inline_write (FB aperture ->
+ * vidmem shadow, sysmem -> guest RAM, unresolved -> VA shadow).
+ */
+ u64 up_dst; /* destination of the last 0x180 run */
+ u32 up_bytes; /* its length word (stream); witness only */
+ u32 n_up;
+ struct svc_gl_up up[SVC_GL_UPLOAD_MAX];
+ /*
* SESSION 149 R15 (M2-S1): bytes of the first LAUNCH_DMA /
* LOAD_INLINE_DATA run observed inside the section (the observed
* upload payload). Attached to the synthesized candidate as an
@@ -6830,6 +6863,66 @@ struct svc_gl_sec {
static struct svc_gl_sec svc_gl; /* service thread only */
static u32 svc_gl_rep_log; /* bounded replay-already-captured witness */
static u32 svc_gl_prog_log; /* R16 bounded program-read witness */
+static u32 svc_gl_up_log; /* R17 bounded section-upload-apply witness */
+/*
+ * SESSION 149 R17 (M4): explicit program bytes for the synthesized GL
+ * launch (hex string, <= GSP_BRIDGE_INLINE_BYTES). The GL program page
+ * (e.g. 0x121201b00) is empty in every buffer this emulation can see: no
+ * memory-writing command in any readable push targets it and the
+ * context-init upload never reached a decodable ring. When set, these
+ * bytes are attached as the program record (after a trusted VA read, so a
+ * real upload still wins). Development use only: a minimal real sm_75
+ * SASS program (EXIT;NOP;...) proves the engine executes OUR bytes.
+ */
+static char *sema_gl_prog_hex;
+module_param_named(sema_gl_prog_hex, sema_gl_prog_hex, charp, 0644);
+MODULE_PARM_DESC(sema_gl_prog_hex,
+ "explicit program bytes (hex) for the synthesized GL launch (<=64 B)");
+static u8 svc_gl_prog[GSP_BRIDGE_INLINE_BYTES];
+static u32 svc_gl_prog_len;
+static bool svc_gl_prog_parsed;
+
+static void svc_gl_prog_apply_param(void)
+{
+ const char *h = sema_gl_prog_hex;
+ u32 n, i;
+
+ if (svc_gl_prog_parsed)
+ return;
+ svc_gl_prog_parsed = true;
+ if (!h || !h[0])
+ return;
+ n = (u32)strlen(h);
+ if (n % 2 || n / 2 > GSP_BRIDGE_INLINE_BYTES) {
+ pr_warn("sema-gl: prog-hex param ignored (bad length %u)\n",
+ n);
+ return;
+ }
+ for (i = 0; i < n; i += 2) {
+ unsigned int b;
+
+ if (sscanf(h + i, "%2x", &b) != 1) {
+ pr_warn("sema-gl: prog-hex param bad at %u\n", i);
+ return;
+ }
+ svc_gl_prog[i / 2] = (u8)b;
+ }
+ svc_gl_prog_len = n / 2;
+ pr_info("sema-gl: prog-hex param: %u bytes stored\n",
+ svc_gl_prog_len);
+}
+/*
+ * SESSION 149 R17 (M4): standalone GL-class upload tracker. A context-init
+ * upload push (the one-shot program upload) is NOT required to sit inside a
+ * C597 section, but it carries the same subchannel-0 0x180/0x1b0 i2m shape.
+ * The tracker pairs the last 0x180 destination with each following 0x1b0
+ * payload and applies it on sight (sema_gl_xlate, eng==0, trusted/allow,
+ * once-only) so the destination (e.g. the program page) gets its content
+ * even when the push carries no class bind this pass recognises.
+ */
+static u64 svc_gl_spot_dst; /* last 0x180 destination (per pass) */
+static u32 svc_gl_spot_bytes; /* its length word (witness) */
+static u32 svc_gl_lid_log; /* bounded standalone-apply witness */
static void svc_gl_add_ptr(u64 va)
{
@@ -6895,6 +6988,13 @@ static void svc_gl_field(u32 addr, u32 count, const u8 *data, u32 dlen)
if (count >= 4)
svc_gl_add_ptr(((u64)(v[2] & 0xff) << 32) |
(u64)(v[3] & 0xfffffffc));
+ /* R17: this run names the destination the next 0x1b0
+ * LAUNCH_DMA/LOAD_INLINE_DATA run writes to. */
+ if (count >= 5) {
+ svc_gl.up_dst = ((u64)(v[2] & 0xff) << 32) |
+ (u64)(v[3] & 0xfffffffc);
+ svc_gl.up_bytes = v[0];
+ }
return;
case 0x01b0: /* LAUNCH_DMA + LOAD_INLINE_DATA (op3/op5): the run's
* data words ARE the observed upload payload. Keep the
@@ -6921,6 +7021,27 @@ static void svc_gl_field(u32 addr, u32 count, const u8 *data, u32 dlen)
svc_gl.blob_len = n;
}
}
+ /*
+ * R17: record the run's payload at the destination the
+ * preceding 0x180 run named (the payload starts after the
+ * LAUNCH_DMA config word - same split as the blob above).
+ */
+ if (svc_gl.up_dst && count > 1 && dlen > 4 &&
+ svc_gl.n_up < SVC_GL_UPLOAD_MAX) {
+ struct svc_gl_up *u = &svc_gl.up[svc_gl.n_up];
+ u32 n = (count - 1) * 4;
+
+ if (n > dlen - 4)
+ n = dlen - 4;
+ if (n > sizeof(u->data))
+ n = (u32)sizeof(u->data);
+ if (n) {
+ u->dst_va = svc_gl.up_dst;
+ u->len = n;
+ memcpy(u->data, data + 4, n);
+ svc_gl.n_up++;
+ }
+ }
return;
default:
return;
@@ -6955,7 +7076,9 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
u32 blob_n = 0;
int blob_rc = -1;
bool blob_trusted = false;
+ const char *blob_src = "none";
+ svc_gl_prog_apply_param();
if (!svc_gl.seen || !svc_gl.prog_va || !svc_gl.param_va ||
!svc_gl.rel_va || !trusted)
goto out;
@@ -7057,6 +7180,41 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
* exactly that"). A trusted read that lands on an all-zero page is
* treated as "not real" and falls through to the LID bytes.
*/
+ /*
+ * SESSION 149 R17 (M4): apply the section's memory-writing upload
+ * commands BEFORE reading the program page, so an upload that
+ * targets the program VA is visible to the read below (the section's
+ * engine commands never ran: every destination - constants, tex
+ * pools, program - stayed zero). The shared inline-write path
+ * resolves each destination (FB aperture -> emulated vidmem shadow,
+ * sysmem -> guest RAM, unresolved -> VA shadow) and the once-only
+ * guard is keyed by (push, content hash, dst, bytes) so a
+ * replay/rescue pass cannot double-apply.
+ */
+ {
+ u64 ph = svc_push_hash(buf, len);
+ u32 ap_n = 0, ap_b = 0;
+
+ for (k = 0; k < svc_gl.n_up && k < SVC_GL_UPLOAD_MAX; k++) {
+ struct svc_gl_up *u = &svc_gl.up[k];
+
+ if (svc_inline_once_seen(push_va, ph, u->dst_va,
+ u->len))
+ continue;
+ svc_inline_once_store(push_va, ph, u->dst_va, u->len);
+ if (!svc_grce_inline_write(vgpu, u->dst_va, u->data,
+ u->len, ch->h_object)) {
+ ap_n++;
+ ap_b += u->len;
+ }
+ }
+ if (gsp_sema_dbg && svc_gl_up_log < 8) {
+ svc_gl_up_log++;
+ pr_info("sema-gl: uploads applied=%u/%u bytes=%u h=0x%x push=0x%llx\n",
+ ap_n, svc_gl.n_up, ap_b, ch->h_object,
+ push_va);
+ }
+ }
memset(blob, 0, sizeof(blob));
blob_rc = svc_read_va_trusted(vgpu, svc_gl.prog_va, blob,
sizeof(blob));
@@ -7070,9 +7228,15 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
}
if (blob_trusted) {
blob_n = sizeof(blob);
+ blob_src = "trusted-prog";
+ } else if (svc_gl_prog_len) {
+ blob_n = svc_gl_prog_len;
+ memcpy(blob, svc_gl_prog, blob_n);
+ blob_src = "prog-hex";
} else if (svc_gl.blob_len) {
blob_n = svc_gl.blob_len;
memcpy(blob, svc_gl.blob, blob_n);
+ blob_src = "lid-fallback";
}
if (blob_n) {
memset(&pr, 0, sizeof(pr));
@@ -7095,7 +7259,7 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
s2.qmd[19], s2.qmd[20], s2.qmd[29], s2.qmd[31],
s2.qmd[5]);
pr_info("sema-gl: M3 code src=%s read_rc=%d len=%u lid=%u first=%08x %08x %08x %08x\n",
- blob_trusted ? "trusted-prog" : "lid-fallback",
+ blob_src,
blob_rc, blob_n, svc_gl.blob_len,
get_unaligned_le32(blob),
get_unaligned_le32(blob + 4),
@@ -11348,6 +11512,10 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
/* r391f: the inline-write once-only key needs the push's identity. */
svc_pass_hash = svc_push_hash(buf, len);
+ /* SESSION 149 R17 (M4): per-pass reset of the standalone 0x180/0x1b0
+ * upload pair (the tracker pairs one run with its successor). */
+ svc_gl_spot_dst = 0;
+ svc_gl_spot_bytes = 0;
/*
* r316: pending releases record the channel they were decoded for;
@@ -11863,6 +12031,58 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
len - off - 4);
}
+ /*
+ * SESSION 149 R17 (M4): standalone GL-class upload apply. The
+ * context-init upload push (the one-shot program upload) need
+ * not sit inside a recognised C597 section, but it carries
+ * the same subchannel-0 0x180 (LINE_LENGTH_IN) + 0x1b0
+ * (LAUNCH_DMA + LOAD_INLINE_DATA) i2m shape. Pair the last
+ * 0x180 destination with each following 0x1b0 payload and
+ * apply it through the shared inline-write path (the engine
+ * never ran these commands). Once-only by (push, content
+ * hash, dst, bytes); the witness line is bounded.
+ */
+ if (gsp_sema_gl_xlate && ch->engine_type == 0 && subch == 0) {
+ if (op == 1 && addr == 0x180 && count >= 4) {
+ u32 w2, w3;
+
+ memcpy(&w2, buf + off + 12, 4);
+ memcpy(&w3, buf + off + 16, 4);
+ svc_gl_spot_dst =
+ ((u64)(w2 & 0xff) << 32) |
+ (u64)(w3 & 0xfffffffc);
+ memcpy(&svc_gl_spot_bytes, buf + off + 4, 4);
+ } else if (op == 5 && addr == 0x1b0 && count > 1 &&
+ svc_gl_spot_dst &&
+ (trusted || allow_ops)) {
+ u32 bytes = (count - 1) * 4;
+
+ if (bytes && !svc_inline_once_seen(push_va,
+ svc_pass_hash, svc_gl_spot_dst,
+ bytes)) {
+ svc_inline_once_store(push_va,
+ svc_pass_hash,
+ svc_gl_spot_dst, bytes);
+ (void)svc_grce_inline_write(vgpu,
+ svc_gl_spot_dst,
+ buf + off + 8, bytes,
+ ch->h_object);
+ if (gsp_sema_dbg &&
+ svc_gl_lid_log < 24) {
+ svc_gl_lid_log++;
+ pr_info("sema-gl-lid: h=0x%x push=0x%llx dst=0x%llx bytes=%u len=%u trusted=%d first=%08x %08x %08x\n",
+ ch->h_object, push_va,
+ svc_gl_spot_dst, bytes,
+ svc_gl_spot_bytes,
+ trusted,
+ *(const u32 *)(buf + off + 8),
+ off + 16 <= len ? *(const u32 *)(buf + off + 12) : 0,
+ off + 20 <= len ? *(const u32 *)(buf + off + 16) : 0);
+ }
+ }
+ }
+ }
+
if (op == 3 || op == 5) {
/*
* NON_INC / ONE_INC methods carry their own data