From decf1b2e57454c5d05eb66c4b7906bcf51238ede Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Thu, 5 Nov 2026 19:47:44 -0500 Subject: vgsp-gl: receipt the draw's clip coverage and refuse non-MVP fixed-CB pages r1512 adds one bounded GL-COVER receipt per replayed draw. It decodes the window's index buffer (0x17c8/0x17d8 + size), the stream-0 stride (0x1c00) and position offset (0x1160), resolves the CALL_MME_DATA (0x3810) stream base, reads the referenced vertices and evaluates the clip-space position in Q16.16 integer math with the exact c[0x0][0x80..0xbc] block the replay delivers, printing per-vertex clip codes plus clip/NDC/screen bboxes and the injected viewport scale/offset/clip. It also prints the fixed-CB copy provenance and tries the client's own scratch18 page read-only. Measured (r1512): real gear vertices (stride 24, position offset 0, 3.549 0.852 -0.5) but the delivered "matrix" was a rotation block (row3 = 0.1268 0.9922 0 1), giving clip w ~= 1.1 instead of the camera distance (~40) and 57/64 sampled vertices outside clip. The draw was clipped by a non-projection constant block, not by zero/garbage VS output. r1513/r1514 fix the source: the fixed-CB copy now prefers the client's SET_MME_SHADOW_SCRATCH(0x18) page and the client-CB VAs its own writes published (0x43e0000/0x4450000), requires a strict projection shape (|row3.x|,|row3.y| < 0.02 and row3.z/w not both zero), and the resolver no longer accepts an unresolved PA 0 page. Non-MVP pages (including the old forced 0x0cd70000 page) can no longer become the seed; when no real page is found the r1476 identity positive control is used. r1513/r1514 receipts: the strict gate finds no readable client MVP page on these boots (the client's fixed-CB content is not materialised in the emulated memory), so the seed is the controlled identity and the identity still clips gear radii > 1 (12 launches r1514, 20 poison, no fragments; root xwd 2 nonblack pixels at (216,0)-(409,2) 0x77edc). Next wall: get the client's fixed-CB contents into its own page (emulate the VPC/ LOAD_CONSTANT_BUFFER writes from the init push) so the VS receives a real MVP. Build: make -C kernel (green). Evidence: backups/evidence-20261002-victory/r1512..r1514. --- kernel/gsp_phys_booter.c | 19 ++ kernel/gsp_phys_gpu.h | 7 + kernel/gsp_sema_service.c | 591 +++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 585 insertions(+), 32 deletions(-) (limited to 'kernel') diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index 98bbc4a..0ea6076 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -19745,6 +19745,25 @@ module_param_named(sema_gl_vp_prelude_pos, gsp_phys_rmcall_gr_vp_prelude_pos, MODULE_PARM_DESC(sema_gl_vp_prelude_pos, "Insertion point of the synthesized viewport prelude in a replayed 3D window (0 = window head (legacy); 1 = before the first CALL run 0x3800..0x39fc, after the state binds (default); 2 = before the first 0x3910 run); pos 1/2 leave a window without the requested run unchanged (non-draw window), only 0 inserts without a CALL run)"); +/* + * r1512 diagnostic export: the sema service's draw-coverage receipt needs + * the viewport state the replay will inject for the current launch. Pure + * read of the module parameter + the literal table (no side effects). + */ +void gsp_phys_gr_vp_state_get(int *mode, u32 *words, u32 *n) +{ + int m = READ_ONCE(gsp_phys_rmcall_gr_vp_prelude); + u32 len = gsp_phys_gr_vp_pre_len(m); + const u32 *src = gsp_phys_gr_vp_pre_src(m); + + if (mode) + *mode = m; + if (n) + *n = len; + if (words && len && src) + memcpy(words, src, (size_t)len * 4u); +} + /* * CONT-119 (r1450): the BINDING words of the synthesized stage-2/5 * REGISTER_COUNT runs. CONT-90 emitted each as a 2-word diff --git a/kernel/gsp_phys_gpu.h b/kernel/gsp_phys_gpu.h index b8d92cd..6e16062 100644 --- a/kernel/gsp_phys_gpu.h +++ b/kernel/gsp_phys_gpu.h @@ -348,6 +348,13 @@ int gsp_phys_rmcall_gr_shadow_win0_value(void); */ bool gsp_phys_rmcall_gr_sph_bind_value(void); +/* + * r1512: the viewport state the 3D replay injects for the current launch + * (sema_gl_vp_prelude mode + its literal words). Read-only; used by the + * sema service's draw-coverage receipt. + */ +void gsp_phys_gr_vp_state_get(int *mode, u32 *words, u32 *n); + /* 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 4181c0c..c5a2eda 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include "nvidia-vgpu-vfio.h" @@ -9845,6 +9846,17 @@ static void svc_gl_stream_chunks(struct svc_channel *ch, const u8 *buf, } } +/* + * r1512: draw-coverage receipt (defined after the fixed-CB copy, which owns + * the persisted real-CB page this receipt reports). Forward-declared here + * because svc_gl_flush is its only caller. + */ +static void svc_gl_cover_log(struct nv_vgpu_device *vgpu, const u8 *buf, + u32 len, u32 start_off, u32 at_off, + u32 h_object, u64 push_va, u32 topo, u32 isz, + u32 first, u32 count, u64 iva, u32 stride, + u32 voff, const u32 *cand, u32 ncand); + static void svc_gl_flush(struct nv_vgpu_device *vgpu, struct svc_channel *ch, const struct svc_push_state *st, const u8 *buf, @@ -10771,6 +10783,8 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu, u32 topo = 0xffffffffu, isz = 0xffffffffu; u32 first = 0, count = 0; u64 iva = 0; + u32 stride = 0, voff = 0; + u32 cand[4] = { 0, 0, 0, 0 }, ncand = 0; while (o + 4 <= at_off) { u32 h, op, cnt, ad, w0 = 0, w1 = 0, w2 = 0; @@ -10802,6 +10816,36 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu, first = w1; count = w2; } + /* + * r1512: vertex-stream shape for the coverage + * receipt. SET_VERTEX_STREAM_A_FORMAT (0x1c00) + * carries the stream-0 stride in bits 11:0; + * SET_VERTEX_ATTRIBUTE_A(0) (0x1160) carries the + * position attribute's byte offset in bits 20:7. + */ + if (op == 1u && ad == 0x1c00u && cnt >= 1u) + stride = w0 & 0xfffu; + if (op == 1u && ad == 0x1160u && cnt >= 1u) + voff = (w0 >> 7) & 0x3fffu; + /* + * The MME setup macro receives the stream base VAs + * as CALL_MME_DATA (0x3810) parameters; keep the + * plausible address words so the receipt can read + * the vertex page the macro will bind. + */ + if (op == 5u && ad == 0x3810u && cnt >= 3u) { + u32 k; + + for (k = 2; k < cnt && ncand < 4u; k++) { + u32 w = get_unaligned_le32( + buf + o + 4u + 4u * k); + + if (w >= 0x100000u && + w < 0x1000000000ULL && + !(w & 3u)) + cand[ncand++] = w; + } + } o += span * 4u; } if (topo != 0xffffffffu || iva || isz != 0xffffffffu) { @@ -10809,6 +10853,12 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu, pr_info("sema-gl: GL-DRAW h=0x%x push=0x%llx topo=%u isz=%u first=%u count=%u iva=0x%llx\n", ch->h_object, push_va, topo, isz, first, count, iva); + if (topo != 0xffffffffu && count >= 3u && iva) + svc_gl_cover_log(vgpu, buf, len, + svc_gl.start_off, at_off, + ch->h_object, push_va, + topo, isz, first, count, iva, + stride, voff, cand, ncand); } } out: @@ -15612,6 +15662,14 @@ static u32 svc_gl_cb_copy_h; static u32 svc_gl_cb_copy_val; static u32 svc_gl_cb_copy_hash; static u32 svc_gl_cb_copy_log; +/* + * r1512: provenance of the page most recently written into the forced MME + * seed (what the VS's c[0x0] bank will contain). Read by the coverage + * receipt so a bad transform can be attributed to the copy source. + */ +static u64 svc_gl_cb_last_va; +static u64 svc_gl_cb_last_pa; +static s32 svc_gl_cb_last_ssrc; /* * r1494: the last fixed-CB page that resolved through a SYS (engine-page) * source and passed the matrix gate. The client VA can move/reuse after @@ -15671,6 +15729,33 @@ static bool svc_gl_cb_matrix_ok(const u8 *buf) return nz != 0u && normal >= 4u; } +/* + * r1513: strict projection gate. svc_gl_cb_matrix_ok only asks for finite, + * normal-magnitude words, and r1512 showed it accepting a page whose + * c[0x0][0x80..0xbc] block was a rotation/normal block (bottom row + * 0.1268 0.9922 0 1) - the VS then produced w~1 instead of the camera + * distance and every sampled vertex clipped. A transform used as + * gl_Position must leave X/Y independent of the eye-space Z/W row, i.e. + * row3.x and row3.y are zero for both the perspective (0,0,-1,0) and the + * orthographic (0,0,0,1) form, and at least one of row3.z/row3.w is + * non-zero. Keep the finite/normal checks and add that shape test. + */ +static bool svc_gl_cb_mvp_ok(const u8 *buf) +{ + u32 m12 = get_unaligned_le32(buf + 0x80u + 12u * 4u); + u32 m13 = get_unaligned_le32(buf + 0x80u + 13u * 4u); + u32 m14 = get_unaligned_le32(buf + 0x80u + 14u * 4u); + u32 m15 = get_unaligned_le32(buf + 0x80u + 15u * 4u); + u32 a12 = m12 & 0x7fffffffu, a13 = m13 & 0x7fffffffu; + + /* |row3.x|, |row3.y| < 0.02 (0x3ca3d70a); denormals and zero pass. */ + if (a12 > 0x3ca3d70au || a13 > 0x3ca3d70au) + return false; + if (!(m14 & 0x7fffffffu) && !(m15 & 0x7fffffffu)) + return false; + return svc_gl_cb_matrix_ok(buf); +} + static void svc_gl_cb_dump_matrix(const u8 *buf, const char *src, u64 pa, u32 ap, int ssrc) { @@ -15718,21 +15803,30 @@ static bool svc_gl_cb_read_page(struct nv_vgpu_device *vgpu, u64 va, u8 *buf, u64 page = va & ~(u64)(SEMA_PAGE_SIZE - 1); u64 pa = 0; u32 ap = SVC_APERTURE_SYS; + bool resolved = false; if (!page) return false; - if (!svc_chanvas_lookup(vgpu, page, &pa, &ap) || - !svc_walk_vaspaces(vgpu, page, &pa, &ap)) { - pa &= ~(u64)(SEMA_PAGE_SIZE - 1); - if (svc_ap_of(ap) == SVC_APERTURE_SYS && - !svc_read_ap(vgpu, pa, SVC_APERTURE_SYS, buf, - SEMA_PAGE_SIZE)) { - if (pa_out) - *pa_out = pa; - if (ap_out) - *ap_out = svc_ap_of(ap); - return true; - } + if (!svc_chanvas_lookup(vgpu, page, &pa, &ap)) + resolved = true; + else if (!svc_walk_vaspaces(vgpu, page, &pa, &ap)) + resolved = true; + /* + * r1514: a failed resolution leaves pa at 0. Reading guest physical + * page 0 then succeeded and the zero/IVT page was accepted as the + * fixed CB (r1513: cbsrc=0x43e0000 pa=0x0 with a stale/synthetic + * identity). Only a real mapping may answer. + */ + if (!resolved || !pa) + return false; + pa &= ~(u64)(SEMA_PAGE_SIZE - 1); + if (svc_ap_of(ap) == SVC_APERTURE_SYS && + !svc_read_ap(vgpu, pa, SVC_APERTURE_SYS, buf, SEMA_PAGE_SIZE)) { + if (pa_out) + *pa_out = pa; + if (ap_out) + *ap_out = svc_ap_of(ap); + return true; } return false; } @@ -15743,7 +15837,7 @@ static void svc_gl_cb_copy(struct nv_vgpu_device *vgpu, u32 h_object) * single service thread (same reason as the static arrays in * svc_gl_flush and svc_decode_stream). */ static u8 buf[SEMA_PAGE_SIZE]; - u64 src_va, pa = 0, hpa = 0; + u64 src_va = 0, pa = 0, hpa = 0; u32 ap = 0, val, hash; const char *src = "resolve"; int rc, ssrc = 0; @@ -15752,33 +15846,57 @@ static void svc_gl_cb_copy(struct nv_vgpu_device *vgpu, u32 h_object) return; if ((h_object & 0xfffff000u) != 0xbeef0000u) return; + /* + * 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 + * row 0.1268 0.9922 0 1; 57/64 sampled gear vertices clipped out). + * The client's own SET_MME_SHADOW_SCRATCH(0x18) is the authority for + * the fixed-CB base; on the validated boots its low values were + * 0x43e00/0x44500 (VA 0x43e0000/0x4450000), so try those observed + * client-CB VAs too. Every candidate must pass the strict projection + * gate; a page that only looks float-ish is no longer accepted. + */ + { + u64 cand[5]; + u32 ncand = 0, ci; + + if (svc_scratch18_seen && READ_ONCE(svc_scratch18_val)) + cand[ncand++] = (u64)READ_ONCE(svc_scratch18_val) << 8; + cand[ncand++] = 0x43e0000ULL; + cand[ncand++] = 0x4450000ULL; + if (sema_gl_cb_src_va) + cand[ncand++] = sema_gl_cb_src_va; + cand[ncand++] = 0x0cd70000ULL; + + for (ci = 0; ci < ncand; ci++) { + u64 cpa = 0; + u32 cap = 0; + + if (!svc_gl_cb_read_page(vgpu, cand[ci], buf, + &cpa, &cap) || + !cpa || !svc_gl_cb_mvp_ok(buf)) + continue; + src_va = cand[ci]; + pa = cpa; + ap = cap; + val = (u32)(src_va >> 8); + src = (ci == 0u && svc_scratch18_seen) ? + "scratch18" : "mvp-scan"; + ssrc = (ci == 0u && svc_scratch18_seen) ? 13 : 14; + goto have; + } + } + /* No strict-MVP page: keep the historical chain as the fallback. */ if (sema_gl_cb_src_va) { src_va = sema_gl_cb_src_va; val = (u32)(src_va >> 8); } else if (svc_scratch18_seen) { - /* - * r1497: per-boot discovery. The client's own - * SET_MME_SHADOW_SCRATCH(0x18) write (method 0x3460) is the - * authority for the fixed-CB base; svc_scratch18_watch() - * captures it from whichever decoded push carries it. Use - * it instead of a hardcoded VA so a boot whose mapping moved - * still reads the real matrix. - */ val = READ_ONCE(svc_scratch18_val); if (!val) return; src_va = (u64)val << 8; } else { - /* - * The 0x3460 write did not reach a decoded push on this - * boot (r1496: 0 captures). Fall back to the fixed-CB VA - * observed on the boots where the capture did fire - * (0x0cd70000) and let the matrix gate decide whether the - * page is the real CB. This keeps a real matrix instead of - * the synthetic identity whenever the page is present under - * the historical VA; a failed gate still falls through to - * cached-real / synthetic-id below. - */ src_va = 0x0cd70000ULL; val = (u32)(src_va >> 8); src = "captured-miss"; @@ -15960,7 +16078,32 @@ have: * not the same as "page still holds it". */ if (ssrc != 4 && ssrc != 5 && ssrc != 6 && ssrc != 7 && - svc_gl_cb_matrix_ok(buf)) { + ssrc != 13 && ssrc != 14) { + /* + * r1513: a page that failed the strict projection gate must + * not reach the VS as the seed; only the content-discovered + * candidates (13/14) skip this re-check. + */ + if (!svc_gl_cb_mvp_ok(buf)) { + if (svc_gl_cb_good_valid) { + memcpy(buf, svc_gl_cb_good, + sizeof(svc_gl_cb_good)); + src = "cached-real"; + ssrc = 9; + } else { + memset(buf, 0, sizeof(buf)); + put_unaligned_le32(0x3f800000u, buf + 0x80); + put_unaligned_le32(0x3f800000u, buf + 0x94); + put_unaligned_le32(0x3f800000u, buf + 0xa8); + put_unaligned_le32(0x3f800000u, buf + 0xbc); + src = "synthetic-id"; + ssrc = 7; + } + goto cb_ready; + } + memcpy(svc_gl_cb_good, buf, sizeof(svc_gl_cb_good)); + svc_gl_cb_good_valid = true; + } else if (ssrc == 13 || ssrc == 14) { memcpy(svc_gl_cb_good, buf, sizeof(svc_gl_cb_good)); svc_gl_cb_good_valid = true; } else if (svc_gl_cb_good_valid) { @@ -15976,6 +16119,7 @@ have: src = "synthetic-id"; ssrc = 7; } +cb_ready: hash = svc_gl_cb_hash(buf, sizeof(buf)); (void)hash; rc = svc_grce_inline_write(vgpu, sema_gl_cb_copy_to, buf, sizeof(buf), @@ -15996,9 +16140,392 @@ have: svc_gl_cb_copy_h = h_object; svc_gl_cb_copy_val = val; svc_gl_cb_copy_hash = hash; + svc_gl_cb_last_va = src_va; + svc_gl_cb_last_pa = pa; + svc_gl_cb_last_ssrc = ssrc; } } +/* + * r1512: draw-coverage diagnostic. + * + * The remaining wall is "the engine executes the draw but no fragment page + * ever changes". This receipt measures the one quantity that separates the + * two candidate causes (clipped by the viewport vs a zero/garbage vertex + * output): it reports the draw's real inputs and the clip-space result + * computed with the exact matrix the replay delivers to the VS, plus the + * viewport state the replay injects. + * + * Inputs, all from the decoded window: + * - index VA (0x17c8) and count (0x17d8), index size; + * - stream-0 stride (0x1c00 bits 11:0) and position attribute offset + * (0x1160 bits 20:7); + * - the CALL_MME_DATA (0x3810) parameter VAs; the first one that reads + * back non-zero is the vertex-stream base the setup macro will bind. + * + * The VS this replays (1408 B Turing SASS) reads position from attribute + * a[0x80..0x8c] and writes clip space to a[0x70]=x, a[0x74]=y, a[0x78]=z, + * a[0x7c]=w with a row-major 4x4 at c[0x0][0x80..0xbc]: + * x' = X*C80 + Y*C84 + Z*C88 + W*C8c + * y' = X*C90 + Y*C94 + Z*C98 + W*C9c + * z' = X*Ca0 + Y*Ca4 + Z*Ca8 + W*Cac + * w' = X*Cb0 + Y*Cb4 + Z*Cb8 + W*Cbc + * so a correct MVP must produce w ~= camera distance (~40) and |x'|,|y'|, + * |z'| <= w' for on-screen gear vertices. + * + * The matrix is evaluated in Q16.16 integer math (the kernel cannot use + * floating point); the receipt prints milli-units and the raw words stay + * available in the same line for an exact userspace recomputation. + * Read-only, bounded (12 receipts per boot), one group of lines per draw. + */ +#define SVC_GL_COVER_IDX_MAX 64u + +static s64 svc_gl_f2q(u32 b) +{ + u32 e = (b >> 23) & 0xffu; + u32 m = (b & 0x7fffffu) | 0x800000u; + int sh = (int)e - 134; /* exp-127-23+16 */ + s64 v; + + if (e == 0u || e == 0xffu) + return 0; + if (sh >= 0) { + if (sh > 24) + return 0x7fffffffffffffffLL; + v = (s64)m << sh; + } else { + if (sh < -24) + return 0; + v = (s64)m >> (-sh); + } + return (b & 0x80000000u) ? -v : v; +} + +static s64 svc_gl_qmul(s64 a, s64 b) +{ + return (a * b) >> 16; +} + +static s64 svc_gl_qdiv(s64 a, s64 b) +{ + if (!b) + return 0; + return (a << 16) / b; +} + +static bool svc_gl_cover_resolve(struct nv_vgpu_device *vgpu, u64 va, + u64 *pa, u32 *ap) +{ + if (!svc_lookup_trusted(vgpu, va, pa, ap)) + return true; + if (!svc_walk_vaspaces(vgpu, va & ~(u64)(SEMA_PAGE_SIZE - 1u), + pa, ap)) { + *pa += va & (SEMA_PAGE_SIZE - 1u); + return true; + } + return false; +} + +static void svc_gl_cover_idx_read(struct nv_vgpu_device *vgpu, u64 iva, + u32 isz, u32 *idx, u32 *nidx) +{ + u8 raw[SVC_GL_COVER_IDX_MAX * 4u]; + u32 esz = (isz == 0u) ? 1u : (isz == 2u) ? 4u : 2u; + u32 nraw = (u32)sizeof(raw); + u32 i; + + *nidx = 0; + if (svc_read_va_trusted(vgpu, iva, raw, nraw)) + return; + for (i = 0; i + esz <= nraw && *nidx < SVC_GL_COVER_IDX_MAX; i += esz) { + u32 v = 0; + + if (esz == 1u) + v = raw[i]; + else if (esz == 2u) + v = get_unaligned_le16(raw + i); + else + v = get_unaligned_le32(raw + i); + idx[(*nidx)++] = v; + } +} + +static void svc_gl_cover_log(struct nv_vgpu_device *vgpu, const u8 *buf, + u32 len, u32 start_off, u32 at_off, + u32 h_object, u64 push_va, u32 topo, u32 isz, + u32 first, u32 count, u64 iva, u32 stride, + u32 voff, const u32 *cand, u32 ncand) +{ + static u32 cover_n; + static u8 cbalt[SEMA_PAGE_SIZE]; + const u8 *cbp; + u32 cbm[16], cbfirst[8]; + u32 vbase = 0, vpa = 0, vap = 0, vbase_fb = 0; + u32 idx[SVC_GL_COVER_IDX_MAX]; + u32 nidx = 0, i, nsamp, nvert = 0, nwle0 = 0, ninside = 0; + u32 nclip = 0, nbad = 0; + s64 M[4][4]; + s64 minx = 0, maxx = 0, miny = 0, maxy = 0; + s64 minw = 0, maxw = 0, minnx = 0, maxnx = 0, minny = 0, maxny = 0; + s64 minsx = 0, maxsx = 0, minsy = 0, maxsy = 0; + s64 sxq = 0, syq = 0, oxq = 0, oyq = 0; + u32 cliph = 0, clipv = 0, minz = 0, maxz = 0; + int vpmode = 0; + u32 vpw[20] = { 0 }; + u32 vpn = 0; + u32 gr = 0, trap = 0, trapdata = 0, ds = 0; + void __iomem *bar0; + u32 k; + + if (cover_n >= 12u) + return; + cover_n++; + + if (!stride) + stride = 24u; + if (voff >= stride) + voff = 0; + + for (i = 0; i < ncand && !vbase; i++) { + u32 w[3]; + u64 pa = 0; + u32 ap = SVC_APERTURE_SYS; + + if (!svc_gl_cover_resolve(vgpu, cand[i], &pa, &ap)) + continue; + if (svc_read_ap(vgpu, pa, ap == SVC_APERTURE_FB ? + SVC_APERTURE_FB : SVC_APERTURE_SYS, + w, sizeof(w))) + continue; + if (!w[0] && !w[1] && !w[2]) + continue; + vbase = cand[i]; + vpa = (u32)pa; + vap = ap; + } + if (!vbase) { + u64 pa = 0; + u32 ap = SVC_APERTURE_SYS; + + vbase = 0x44ff000u; + vbase_fb = 1; + if (svc_gl_cover_resolve(vgpu, vbase, &pa, &ap)) { + vpa = (u32)pa; + vap = ap; + } + } + + svc_gl_cover_idx_read(vgpu, iva, isz, idx, &nidx); + + if (svc_gl_cb_good_valid) { + cbp = svc_gl_cb_good; + } else { + memset(cbalt, 0, sizeof(cbalt)); + put_unaligned_le32(0x3f800000u, cbalt + 0x80); + put_unaligned_le32(0x3f800000u, cbalt + 0x94); + put_unaligned_le32(0x3f800000u, cbalt + 0xa8); + put_unaligned_le32(0x3f800000u, cbalt + 0xbc); + cbp = cbalt; + } + for (k = 0; k < 8u; k++) + cbfirst[k] = get_unaligned_le32(cbp + 4u * k); + for (k = 0; k < 16u; k++) + cbm[k] = get_unaligned_le32(cbp + 0x80u + 4u * k); + for (i = 0; i < 4u; i++) { + u32 r; + + for (r = 0; r < 4u; r++) + M[i][r] = svc_gl_f2q( + get_unaligned_le32(cbp + 0x80u + + (i * 4u + r) * 4u)); + } + + gsp_phys_gr_vp_state_get(&vpmode, vpw, &vpn); + if (vpn >= 3u && vpw[0] == 0x20020280u) { + sxq = svc_gl_f2q(vpw[1]); + syq = svc_gl_f2q(vpw[2]); + if (vpn >= 8u) { + oxq = svc_gl_f2q(vpw[5]); + oyq = svc_gl_f2q(vpw[6]); + } + if (vpn >= 14u) { + cliph = vpw[9]; + clipv = vpw[10]; + minz = vpw[12]; + maxz = vpw[13]; + } + } else if (vpn >= 3u && vpw[0] == 0x20060280u) { + sxq = svc_gl_f2q(vpw[1]); + syq = svc_gl_f2q(vpw[2]); + if (vpn >= 7u) { + oxq = svc_gl_f2q(vpw[4]); + oyq = svc_gl_f2q(vpw[5]); + } + } + + bar0 = gsp_phys_gpu_bar0(); + if (bar0) { + gr = ioread32(bar0 + 0x00400700u); + trap = ioread32(bar0 + 0x00400704u); + trapdata = ioread32(bar0 + 0x00400708u); + ds = ioread32(bar0 + 0x00405840u); + } + + pr_info("sema-gl: GL-COVER h=0x%x push=0x%llx topo=%u isz=%u first=%u count=%u iva=0x%llx stride=%u voff=%u vbase=0x%x%s vpa=0x%x ap=%d nidx=%u cbsrc=0x%llx cbpa=0x%llx cbssrc=%d cbvalid=%u\n", + h_object, push_va, topo, isz, first, count, iva, stride, + voff, vbase, vbase_fb ? "(fallback)" : "", vpa, vap, nidx, + svc_gl_cb_last_va, svc_gl_cb_last_pa, svc_gl_cb_last_ssrc, + svc_gl_cb_good_valid ? 1u : 0u); + pr_info("sema-gl: GL-COVER vp mode=%d n=%u scale=(%lld,%lld)m offset=(%lld,%lld)m cliph=%08x clipv=%08x minz=%08x maxz=%08x\n", + vpmode, vpn, (sxq * 1000) >> 16, (syq * 1000) >> 16, + (oxq * 1000) >> 16, (oyq * 1000) >> 16, + cliph, clipv, minz, maxz); + pr_info("sema-gl: GL-COVER cb first8=%08x %08x %08x %08x %08x %08x %08x %08x\n", + cbfirst[0], cbfirst[1], cbfirst[2], cbfirst[3], + cbfirst[4], cbfirst[5], cbfirst[6], cbfirst[7]); + pr_info("sema-gl: GL-COVER cb m80=%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", + cbm[0], cbm[1], cbm[2], cbm[3], cbm[4], cbm[5], cbm[6], + cbm[7], cbm[8], cbm[9], cbm[10], cbm[11], cbm[12], + cbm[13], cbm[14], cbm[15]); + /* + * The client's own fixed-CB base (SET_MME_SHADOW_SCRATCH(0x18) -> + * VA>>8) is the authority for c[0x0]. Read it through the page + * tables so the receipt can compare it with the page the copy used. + */ + if (svc_scratch18_seen && READ_ONCE(svc_scratch18_val)) { + u64 sva = (u64)READ_ONCE(svc_scratch18_val) << 8; + u64 spa = 0; + u32 sap = SVC_APERTURE_SYS; + int src = 0; + + if (svc_gl_cover_resolve(vgpu, sva, &spa, &sap) && + !svc_read_ap(vgpu, spa & ~(u64)(SEMA_PAGE_SIZE - 1u), + sap == SVC_APERTURE_FB ? + SVC_APERTURE_FB : SVC_APERTURE_SYS, + cbalt, sizeof(cbalt))) { + u32 aw[8], am[16]; + + src = 1; + for (k = 0; k < 8u; k++) + aw[k] = get_unaligned_le32(cbalt + 4u * k); + for (k = 0; k < 16u; k++) + am[k] = get_unaligned_le32(cbalt + 0x80u + + 4u * k); + pr_info("sema-gl: GL-COVER cb-scratch src=%d va=0x%llx pa=0x%llx ap=%d first8=%08x %08x %08x %08x %08x %08x %08x %08x ok=%u\n", + src, sva, spa, sap, aw[0], aw[1], aw[2], + aw[3], aw[4], aw[5], aw[6], aw[7], + svc_gl_cb_matrix_ok(cbalt) ? 1u : 0u); + pr_info("sema-gl: GL-COVER cb-scratch m80=%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", + am[0], am[1], am[2], am[3], am[4], am[5], + am[6], am[7], am[8], am[9], am[10], am[11], + am[12], am[13], am[14], am[15]); + } else { + pr_info("sema-gl: GL-COVER cb-scratch unreadable va=0x%llx (read=%d)\n", + sva, src); + } + } else { + pr_info("sema-gl: GL-COVER cb-scratch not captured (0x3460 never decoded)\n"); + } + + nsamp = min_t(u32, nidx, SVC_GL_COVER_IDX_MAX); + for (i = 0; i < nsamp; i++) { + u64 va = (u64)vbase + (u64)idx[i] * stride + voff; + u32 p[3]; + s64 P[4], C[4]; + u32 cc = 0; + s64 nx, ny, sxp, syp; + u32 r; + + if (svc_read_va_trusted(vgpu, va, p, sizeof(p))) { + nbad++; + continue; + } + P[0] = svc_gl_f2q(p[0]); + P[1] = svc_gl_f2q(p[1]); + P[2] = svc_gl_f2q(p[2]); + P[3] = 65536; + for (r = 0; r < 4u; r++) + C[r] = svc_gl_qmul(M[r][0], P[0]) + + svc_gl_qmul(M[r][1], P[1]) + + svc_gl_qmul(M[r][2], P[2]) + + svc_gl_qmul(M[r][3], P[3]); + if (C[3] <= 0) { + cc |= 0x40u; + nwle0++; + } else { + if (C[0] < -C[3]) + cc |= 0x01u; + if (C[0] > C[3]) + cc |= 0x02u; + if (C[1] < -C[3]) + cc |= 0x04u; + if (C[1] > C[3]) + cc |= 0x08u; + if (C[2] < -C[3]) + cc |= 0x10u; + if (C[2] > C[3]) + cc |= 0x20u; + } + nx = svc_gl_qdiv(C[0], C[3]); + ny = svc_gl_qdiv(C[1], C[3]); + sxp = svc_gl_qmul(sxq, nx) + oxq; + syp = svc_gl_qmul(syq, ny) + oyq; + if (cc == 0u) + ninside++; + else + nclip++; + if (!nvert) { + minx = maxx = C[0]; + miny = maxy = C[1]; + minw = maxw = C[3]; + minnx = maxnx = nx; + minny = maxny = ny; + minsx = maxsx = sxp; + minsy = maxsy = syp; + } else { + minx = min(minx, C[0]); + maxx = max(maxx, C[0]); + miny = min(miny, C[1]); + maxy = max(maxy, C[1]); + minw = min(minw, C[3]); + maxw = max(maxw, C[3]); + minnx = min(minnx, nx); + maxnx = max(maxnx, nx); + minny = min(minny, ny); + maxny = max(maxny, ny); + minsx = min(minsx, sxp); + maxsx = max(maxsx, sxp); + minsy = min(minsy, syp); + maxsy = max(maxsy, syp); + } + nvert++; + if (nvert <= 4u) + pr_info("sema-gl: GL-COVER v%u idx=%u pos=(%lld,%lld,%lld)m clip=(%lld,%lld,%lld,%lld)m cc=0x%02x ndc=(%lld,%lld)m scr=(%lld,%lld)m\n", + nvert, idx[i], + (P[0] * 1000) >> 16, (P[1] * 1000) >> 16, + (P[2] * 1000) >> 16, + (C[0] * 1000) >> 16, (C[1] * 1000) >> 16, + (C[2] * 1000) >> 16, (C[3] * 1000) >> 16, + cc, (nx * 1000) >> 16, (ny * 1000) >> 16, + (sxp * 1000) >> 16, (syp * 1000) >> 16); + } + pr_info("sema-gl: GL-COVER sum samples=%u verts=%u bad=%u wle0=%u inside=%u clipped=%u ndcx=[%lld,%lld]m ndcy=[%lld,%lld]m scrx=[%lld,%lld]m scry=[%lld,%lld]m clip=(%lld,%lld,%lld,%lld)m minw=%lldm gr=%08x trap=%08x/%08x ds=%08x\n", + nsamp, nvert, nbad, nwle0, ninside, nclip, + (minnx * 1000) >> 16, (maxnx * 1000) >> 16, + (minny * 1000) >> 16, (maxny * 1000) >> 16, + (minsx * 1000) >> 16, (maxsx * 1000) >> 16, + (minsy * 1000) >> 16, (maxsy * 1000) >> 16, + (minx * 1000) >> 16, (maxx * 1000) >> 16, + (miny * 1000) >> 16, (maxy * 1000) >> 16, + nvert ? ((minw * 1000) >> 16) : 0, + gr, trap, trapdata, ds); + (void)first; + (void)len; + (void)start_off; + (void)at_off; + (void)buf; +} + /* * SESSION 153-CONTINUED-94: client init-push capture + local-memory census. * -- cgit v1.2.3