diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-05 20:34:52 -0500 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-05 20:34:52 -0500 |
| commit | 095c31b8b6007fb82ac783b2a56c5126595b543e (patch) | |
| tree | 6c1fe982e15f26c5e2e43118882cdbd76f9a1111 | |
| parent | decf1b2e57454c5d05eb66c4b7906bcf51238ede (diff) | |
| download | open-iov-095c31b8b6007fb82ac783b2a56c5126595b543e.tar.gz open-iov-095c31b8b6007fb82ac783b2a56c5126595b543e.zip | |
vgsp-gl: decode the client fixed-CB upload runs and gate the assembled image
| -rw-r--r-- | docs/04-implementation/04-real-render-walls.md | 27 | ||||
| -rw-r--r-- | kernel/gsp_sema_service.c | 295 |
2 files changed, 322 insertions, 0 deletions
diff --git a/docs/04-implementation/04-real-render-walls.md b/docs/04-implementation/04-real-render-walls.md index 3119b75..ec1b75f 100644 --- a/docs/04-implementation/04-real-render-walls.md +++ b/docs/04-implementation/04-real-render-walls.md @@ -1154,3 +1154,30 @@ index-buffer bind, the vertex-shader position output and the viewport/clip transform must be checked against the client's own draw arguments (the active index page currently reads as vertex floats at its bound VA, so the index/vertex role or the index count is also a candidate). + +## Session 154 (r1515-r1517): the fixed-CB upload is decoded but its bulk is not in the method stream + +The client publishes the fixed-CB base once per context +(`SET_MME_SHADOW_SCRATCH(0x18)`, method `0x3460`, value = VA >> 8) and then +writes the constant store through the C597 methods +`SET_CONSTANT_BUFFER_SELECTOR_A/B/C` (`0x2380/0x2384/0x2388`), +`LOAD_CONSTANT_BUFFER_OFFSET` (`0x238c`) and `LOAD_CONSTANT_BUFFER(i)` +(`0x2390`). The emulator now scans every decoded push of the client's GL +channel for those runs, assembles the first 4 KB of the constant buffer, +validates it with the strict projection gate, and delivers a passing image +to the forced MME seed page; a receipt reports the push, base, selector, +run/word counts, the `c[0x0][0x80..0xbc]` block and whether the projection +shape passed (`CB-UPLOAD`, `CB-UPLOAD-SCAN`, `cb-copy src=const-upload`). + +Measured on a validated boot (r1517, glxgears formed, draws submitted): the +60216-byte setup push is read and the scratch-18 base is captured +(VA `0x4460000`), but its `0x2390` content is only 10 state-patch words +(selector/offset pairs, no matrix rows). The base page resolves to the +emulated vidmem offset `0xb10000` and reads zero; the neighbouring +`0x43e0000` page is client vertex data, and the guest-RAM projection-shape +scanner finds only transient copies. The draw therefore still runs with the +synthetic identity (r1517 `GL-COVER` clip space inside 50/64 sampled +vertices) and the window capture is black (`xwd -id`, non-black 0). The +remaining work is to capture the constant-buffer *bulk* transfer itself +(the staging buffer to the vidmem CB page), not the method-stream patches. + diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index c5a2eda..2f08064 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -15789,6 +15789,272 @@ static void svc_gl_cb_dump_matrix(const u8 *buf, const char *src, u64 pa, } /* + * r1515: client fixed-CB upload capture. + * + * The client's fixed constant buffer is not a page the guest writes with a + * plain memory store: the driver publishes its base once per context + * (SET_MME_SHADOW_SCRATCH(0x18), method 0x3460, value = VA >> 8) and then + * streams the constants through the C597 constant-buffer methods: + * - 0x2380/0x2384/0x2388 SET_CONSTANT_BUFFER_SELECTOR_A/B/C + * (A size, B address[39:32], C address[31:0]); + * - 0x238c LOAD_CONSTANT_BUFFER_OFFSET (byte offset into the store); + * - 0x2390 LOAD_CONSTANT_BUFFER(i) data runs (NON_INC streams the + * constants from the current offset; INC/ONE_INC index them). + * r1512/r1514 measured the consequence of not applying them: the page the + * VS's c[0x0] bank resolves to holds screen/rotation data, the position + * block at 0x80 is not a projection, and 57/64 sampled gear vertices clip. + * + * This scanner runs on every decoded push of the beef GL channel (the + * 60216-byte setup push carries the 0x3460 write at off 0x464c and the + * constant stream after it). It maintains a 4 KB image of the first CB + * page (the VS reads c[0x0][0x80..0x888], all inside page 0), validates it + * with the strict projection gate, and only then marks it authoritative. + * svc_gl_cb_copy() prefers this image over any memory-page discovery and + * writes it into the forced MME seed page, so the replayed VS reads the + * client's real transform. The write is a pure emulated engine store (the + * client page is authored through the same inline-write path when it + * resolves). No FP in the kernel. + */ +static u8 svc_gl_cb_img[SEMA_PAGE_SIZE]; +static bool svc_gl_cb_img_valid; +static u64 svc_gl_cb_img_base; +static u64 svc_gl_cb_img_words; +static u64 svc_gl_cb_img_runs; +static u32 svc_gl_cb_off; +static u32 svc_gl_cb_sel_a; +static u64 svc_gl_cb_sel_bc; +static bool svc_gl_cb_sel_seen; +static u32 svc_gl_cb_scan_log; +static u32 svc_gl_cb_apply_log; + +#define SVC_GL_CB_IMG_LIMIT SEMA_PAGE_SIZE + +static void svc_gl_cb_img_reset(u64 base) +{ + memset(svc_gl_cb_img, 0, sizeof(svc_gl_cb_img)); + svc_gl_cb_img_valid = false; + svc_gl_cb_img_base = base; + svc_gl_cb_img_words = 0; + svc_gl_cb_img_runs = 0; + svc_gl_cb_off = 0; +} + +static void svc_gl_cb_upload_scan(struct nv_vgpu_device *vgpu, u32 h_object, + const u8 *buf, u32 len, u64 push_va) +{ + u32 off = 0, runs = 0, words = 0; + u64 base = 0; + bool dirty = false; + u32 i; + + (void)vgpu; + if ((h_object & 0xfffff000u) != 0xbeef0000u || !buf || len < 16u) + return; + /* + * A new SET_MME_SHADOW_SCRATCH(0x18) names a new fixed-CB base: + * discard the previous image so a stale transform can never be + * applied to a new context. + */ + if (svc_scratch18_seen && READ_ONCE(svc_scratch18_val)) + base = (u64)READ_ONCE(svc_scratch18_val) << 8; + if (base && base != svc_gl_cb_img_base) + svc_gl_cb_img_reset(base); + while (off + 4u <= len) { + u32 hdr, op, cnt, addr, w; + + memcpy(&hdr, buf + off, 4); + if (!hdr) { + off += 4u; + continue; + } + op = (hdr >> 29) & 7u; + cnt = (hdr >> 16) & 0xfffu; + addr = (hdr & 0x1fffu) << 2; + if (op == 0u || op == 2u || op == 7u) { + off += 4u; + continue; + } + if (op == 4u) { + /* IMMD: 13-bit payload; offset/selector only. */ + w = (hdr >> 16) & 0x1fffu; + if (addr == 0x3460u) { + base = (u64)w << 8; + if (base && base != svc_gl_cb_img_base) + svc_gl_cb_img_reset(base); + } else if (addr == 0x238cu) { + svc_gl_cb_off = w; + dirty = true; + } else if (addr == 0x2384u) { + svc_gl_cb_sel_bc = (svc_gl_cb_sel_bc & + ~0xffffffffULL) | + (u64)w; + svc_gl_cb_sel_seen = true; + } else if (addr == 0x2388u) { + svc_gl_cb_sel_bc = (svc_gl_cb_sel_bc & + 0xffffffffULL) | + ((u64)w << 32); + svc_gl_cb_sel_seen = true; + } + off += 4u; + continue; + } + if (op != 1u && op != 3u && op != 5u) { + off += 4u; + continue; + } + if (!cnt || cnt > 0xfffu || + off + 4u + cnt * 4u > len) { + /* + * A run that overruns this push (or a data word that + * happens to decode as a header) is not the end of the + * stream: the client's setup push interleaves large + * inline payloads with method runs. Resync one word + * later, exactly like svc_scratch18_watch does, so the + * constant stream after the data block is still found. + */ + off += 4u; + continue; + } + if (addr == 0x238cu) { + memcpy(&w, buf + off + 4u, 4); + svc_gl_cb_off = w; + dirty = true; + off += 4u + cnt * 4u; + continue; + } + if (addr >= 0x2380u && addr <= 0x238cu) { + u32 j; + + for (j = 0; j < cnt; j++) { + u32 m = (op == 3u) ? addr : addr + 4u * j; + + memcpy(&w, buf + off + 4u + 4u * j, 4); + switch (m) { + case 0x2380u: + svc_gl_cb_sel_a = w; + break; + case 0x2384u: + svc_gl_cb_sel_bc = + (svc_gl_cb_sel_bc & + ~0xffffffffULL) | + (u64)w; + svc_gl_cb_sel_seen = true; + break; + case 0x2388u: + svc_gl_cb_sel_bc = + (svc_gl_cb_sel_bc & + 0xffffffffULL) | + ((u64)w << 32); + svc_gl_cb_sel_seen = true; + break; + case 0x238cu: + svc_gl_cb_off = w; + dirty = true; + break; + default: + break; + } + } + } else if (addr == 0x3460u) { + memcpy(&w, buf + off + 4u, 4); + base = (u64)w << 8; + if (base && base != svc_gl_cb_img_base) + svc_gl_cb_img_reset(base); + } else if (addr >= 0x2390u && addr < 0x2390u + 0x1000u) { + u32 idx = (addr - 0x2390u) / 4u; + + for (i = 0; i < cnt; i++) { + u64 doff; + + memcpy(&w, buf + off + 4u + 4u * i, 4); + /* + * NON_INC streams from the published offset; + * INC/ONE_INC index from the run's method. + */ + if (op == 3u) + doff = (u64)svc_gl_cb_off + + 4ULL * (u64)i; + else + doff = (u64)svc_gl_cb_off + + 4ULL * (u64)(idx + i); + if (doff + 4ULL > SVC_GL_CB_IMG_LIMIT) + continue; + memcpy(svc_gl_cb_img + doff, &w, 4); + words++; + dirty = true; + } + runs++; + if (op == 3u) + svc_gl_cb_off += 4u * cnt; + } + off += 4u + cnt * 4u; + } + if (!dirty || !words) + return; + svc_gl_cb_img_words += words; + svc_gl_cb_img_runs += runs; + /* + * The strict projection gate is the validator: only a page whose + * c[0x0][0x80..0xbc] block is a projection transform becomes the + * authoritative CB source. A partial stream keeps accumulating. + */ + if (!svc_gl_cb_img_valid && svc_gl_cb_mvp_ok(svc_gl_cb_img)) { + svc_gl_cb_img_valid = true; + pr_info("vgsp-sema: sema-gl: CB-UPLOAD h=0x%x push=0x%llx base=0x%llx sel=%u a=0x%08x bc=0x%llx off=0x%x runs=%llu words=%llu projection=ok first8=%08x %08x %08x %08x %08x %08x %08x %08x\n", + h_object, push_va, svc_gl_cb_img_base, + svc_gl_cb_sel_seen ? 1u : 0u, svc_gl_cb_sel_a, + svc_gl_cb_sel_bc, svc_gl_cb_off, + svc_gl_cb_img_runs, svc_gl_cb_img_words, + get_unaligned_le32(svc_gl_cb_img), + get_unaligned_le32(svc_gl_cb_img + 4), + get_unaligned_le32(svc_gl_cb_img + 8), + get_unaligned_le32(svc_gl_cb_img + 12), + get_unaligned_le32(svc_gl_cb_img + 16), + get_unaligned_le32(svc_gl_cb_img + 20), + get_unaligned_le32(svc_gl_cb_img + 24), + get_unaligned_le32(svc_gl_cb_img + 28)); + pr_info("vgsp-sema: sema-gl: CB-UPLOAD m80=%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", + get_unaligned_le32(svc_gl_cb_img + 0x80), + get_unaligned_le32(svc_gl_cb_img + 0x84), + get_unaligned_le32(svc_gl_cb_img + 0x88), + get_unaligned_le32(svc_gl_cb_img + 0x8c), + get_unaligned_le32(svc_gl_cb_img + 0x90), + get_unaligned_le32(svc_gl_cb_img + 0x94), + get_unaligned_le32(svc_gl_cb_img + 0x98), + get_unaligned_le32(svc_gl_cb_img + 0x9c), + get_unaligned_le32(svc_gl_cb_img + 0xa0), + get_unaligned_le32(svc_gl_cb_img + 0xa4), + get_unaligned_le32(svc_gl_cb_img + 0xa8), + get_unaligned_le32(svc_gl_cb_img + 0xac), + get_unaligned_le32(svc_gl_cb_img + 0xb0), + get_unaligned_le32(svc_gl_cb_img + 0xb4), + get_unaligned_le32(svc_gl_cb_img + 0xb8), + get_unaligned_le32(svc_gl_cb_img + 0xbc)); + } + if (svc_gl_cb_scan_log < 24u) { + svc_gl_cb_scan_log++; + pr_info("vgsp-sema: sema-gl: CB-UPLOAD-SCAN h=0x%x push=0x%llx base=0x%llx off=0x%x runs=%u words=%u valid=%u total_words=%llu\n", + h_object, push_va, svc_gl_cb_img_base, svc_gl_cb_off, + runs, words, svc_gl_cb_img_valid ? 1u : 0u, + svc_gl_cb_img_words); + } + if (svc_gl_cb_img_valid && sema_gl_cb_copy_to && + svc_gl_cb_apply_log < 24u) { + svc_gl_cb_apply_log++; + pr_info("vgsp-sema: sema-gl: CB-UPLOAD-APPLY h=0x%x base=0x%llx -> seed=0x%llx first8=%08x %08x %08x %08x %08x %08x %08x %08x (client CB image written by cb-copy)\n", + h_object, svc_gl_cb_img_base, sema_gl_cb_copy_to, + get_unaligned_le32(svc_gl_cb_img), + get_unaligned_le32(svc_gl_cb_img + 4), + get_unaligned_le32(svc_gl_cb_img + 8), + get_unaligned_le32(svc_gl_cb_img + 12), + get_unaligned_le32(svc_gl_cb_img + 16), + get_unaligned_le32(svc_gl_cb_img + 20), + get_unaligned_le32(svc_gl_cb_img + 24), + get_unaligned_le32(svc_gl_cb_img + 28)); + } +} + +/* * r1505 (mission task 3): content-based fixed-CB discovery. The client's * fixed-CB base VA is boot-dependent and the scratch18/0x0cd70000 chain can * miss on a boot (r1504: 96/96 synthetic-id). The CB page is 4 KB whose @@ -15847,6 +16113,22 @@ static void svc_gl_cb_copy(struct nv_vgpu_device *vgpu, u32 h_object) if ((h_object & 0xfffff000u) != 0xbeef0000u) return; /* + * r1515: the client's own constant-buffer upload is the highest + * authority. Once svc_gl_cb_upload_scan() has assembled the first CB + * page from the setup push's 0x2380/0x238c/0x2390 runs and the + * strict projection gate accepted it, deliver that image directly - + * no memory-page discovery can be more faithful than the bytes the + * client itself streamed into the store. + */ + if (svc_gl_cb_img_valid) { + memcpy(buf, svc_gl_cb_img, sizeof(svc_gl_cb_img)); + src_va = svc_gl_cb_img_base; + val = (u32)(src_va >> 8); + src = "const-upload"; + ssrc = 15; + goto have; + } + /* * r1513: source selection by CONTENT, in authority order. r1512's * coverage receipt measured the forced historical page 0x0cd70000 * delivering a rotation block as the "MVP" (clip w ~= 1.1, bottom @@ -18453,6 +18735,11 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf, if (ch) svc_scratch18_watch(buf, len, push_va, ch->h_object); + /* r1515: apply the client's C597 constant-buffer upload (0x2390 + * runs) into the fixed-CB image before this pass's launch. */ + if (ch) + svc_gl_cb_upload_scan(vgpu, ch->h_object, buf, len, push_va); + /* r1475: feed the client's real fixed-CB page into the forced MME * seed page before this pass's launch (beef GL channels only). */ if (ch) @@ -37883,6 +38170,14 @@ static u32 svc_gr_ahead_push(struct nv_vgpu_device *vgpu, struct svc_channel *ch methods = svc_ahead_parse(buf, len, tail, taillen, &used_tail); if (!methods) return 0; + /* + * r1515: the init-push read worth catching is exactly the entry the + * main decode may skip as stale. Apply the client's C597 fixed-CB + * upload (0x2390 runs) from this translated push as well, so the + * beef channel's setup entry feeds the CB image even on boots where + * only the util/continuation sweep reads it. + */ + svc_gl_cb_upload_scan(vgpu, ch->h_object, buf, len, push_va); memset(&stq, 0, sizeof(stq)); stq.push_va = push_va; |
