summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_sema_service.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-11-05 19:47:44 -0500
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-11-05 19:47:44 -0500
commitdecf1b2e57454c5d05eb66c4b7906bcf51238ede (patch)
tree957ff417a5d4751a54d5cc767082d7caacf6d4fc /kernel/gsp_sema_service.c
parentbe25496cdc7d08226daa26f0707997dee12612dd (diff)
downloadopen-iov-decf1b2e57454c5d05eb66c4b7906bcf51238ede.tar.gz
open-iov-decf1b2e57454c5d05eb66c4b7906bcf51238ede.zip
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.
Diffstat (limited to 'kernel/gsp_sema_service.c')
-rw-r--r--kernel/gsp_sema_service.c591
1 files changed, 559 insertions, 32 deletions
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 <linux/vmalloc.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/io.h>
#include <asm/unaligned.h>
#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:
@@ -15613,6 +15663,14 @@ 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
* the first frame (r1492 read back a method page with the same VA and
@@ -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,10 +16140,393 @@ 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.
*
* CONT-93's "single most concrete next fix" was that the GL driver's