summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_phys_booter.c43
-rw-r--r--kernel/gsp_sema_service.c166
2 files changed, 204 insertions, 5 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index b7476b3..d78e0df 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -15260,7 +15260,35 @@ static int gsp_phys_rmcall_publish_locked(void __iomem *bar0, u64 pb_gpu_va,
gpput, GSP_PHYS_RMCALL_GPFIFO_ENTRIES);
return -EIO;
}
- slot = gpput;
+ /*
+ * SESSION 153-CONTINUED-46: publish at the ENGINE's GP_GET, not at
+ * the raw GP_PUT. Observed on every stalled beef boot: the USERD
+ * drifts (GPGet=5 while GPPut cycles 1/2/4/7 - the engine's GET ran
+ * past the publisher's PUT, e.g. after a wrapped or otherwise
+ * deranged ring), and a ring is MODULO: PUT < GET numerically means
+ * (PUT-GET) mod ENTRIES pending entries - hundreds - so the PBDMA
+ * walks stale slots, executes garbage method blocks and the launch
+ * never retires (the trapped=0x8000022c / no-dispatch class). The
+ * forwarded channel is strictly single-inflight, so the engine's
+ * GET names exactly the next free slot: write our entry there and
+ * advance PUT to GET+1 so the PBDMA sees exactly one pending entry -
+ * ours. When GP_GET is out of range (uninitialised USERD), fall back
+ * to the old PUT-derived slot.
+ */
+ slot = (gpget < GSP_PHYS_RMCALL_GPFIFO_ENTRIES) ? gpget : gpput;
+ {
+ static u32 slotfix_log;
+
+ if (gpget != gpput && slotfix_log < 64) {
+ slotfix_log++;
+ pr_info("vgsp-boot: rm-call: publish at engine GP_GET=%u (raw GP_PUT=%u was %s; pending-would-be=%u)\n",
+ gpget, gpput,
+ gpput == gpget ? "equal" :
+ (gpput > gpget ? "ahead" : "behind"),
+ (gpput + GSP_PHYS_RMCALL_GPFIFO_ENTRIES - gpget) %
+ GSP_PHYS_RMCALL_GPFIFO_ENTRIES);
+ }
+ }
/*
* e0: bits 31:2 hold the GET field, which itself is (fetch address
@@ -15287,8 +15315,10 @@ static int gsp_phys_rmcall_publish_locked(void __iomem *bar0, u64 pb_gpu_va,
* GP_PUT advances on the client sysmem USERD chunk, which is the
* channel's hardware USERD because the params carry that same chunk;
* the host write is therefore what the engine polls (cycle 21).
+ * CONT-46: PUT = slot+1 (slot was chosen from GP_GET above), so the
+ * single new entry is the entire pending set.
*/
- put_unaligned_le32((gpput + 1) % GSP_PHYS_RMCALL_GPFIFO_ENTRIES,
+ put_unaligned_le32((slot + 1) % GSP_PHYS_RMCALL_GPFIFO_ENTRIES,
(u8 *)userd + gsp_phys_rmcall_userd_ctl_off() + GSP_PHYS_RMCALL_USERD_GPPUT_OFF);
/*
* Doorbell value: workSubmitToken = ((runlistId << 16) & 0x7f00000) |
@@ -19601,6 +19631,15 @@ static bool gsp_phys_rmcall_gr_win_drop_match(u32 addr)
while (*p == ' ' || *p == ',' || *p == ':' || *p == ';')
p++;
+ /*
+ * SESSION 153-CONTINUED-46: accept an optional 0x/0X prefix.
+ * The original parser scanned hex digits only, so the natural
+ * "0x3810,0x39c8" input parsed as 0 (the 'x' ended the digit
+ * run after the leading '0') and silently never matched -
+ * cost a full bisect round.
+ */
+ if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
+ p += 2;
for (;;) {
char c = *p;
u32 d;
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 00d458b..4d497d9 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -8168,6 +8168,39 @@ static void svc_gl_field(u32 addr, u32 count, const u8 *data, u32 dlen)
}
}
return;
+ case 0x17c8: /* SET_INDEX_BUFFER_A/B: A=ADDRESS_UPPER(7:0),
+ * B=ADDRESS_LOWER(31:0). SESSION 153-CONTINUED-46:
+ * the indexed draws (glxgears' glDrawElements) fetch
+ * their indices from this VA; r1039 evidence: the
+ * client's 3D window binds it
+ * ("200205f2 ... data=00000000 0450fcc8" -> VA
+ * 0x0450fcc8) but the candidate page set held only the
+ * program page (n_pages=1) while X's retiring windows
+ * carry 3-6 pages - the index page is never mapped in
+ * the replay channel. Collect it like every other
+ * address operand.
+ */
+ if (count >= 2)
+ svc_gl_add_ptr(((u64)(v[0] & 0xff) << 32) |
+ (u64)(v[1] & 0xfffffffc));
+ return;
+ case 0x1c20: /* SET_VERTEX_STREAM_A_FORMAT/BASE(j) for j >= 2:
+ * same [fmt, BASE_UPPER, BASE_LOWER] shape as 0x1c00/
+ * 0x1c10; the old switch only matched j=0/1.
+ */
+ case 0x1c30:
+ case 0x1c40:
+ case 0x1c50:
+ case 0x1c60:
+ case 0x1c70:
+ case 0x1c80:
+ case 0x1c90:
+ case 0x1ca0:
+ case 0x1cb0:
+ case 0x1cc0:
+ case 0x1cd0:
+ case 0x1ce0:
+ case 0x1cf0:
case 0x1c00: /* SET_VERTEX_STREAM_A_FORMAT(j) + j*0x10: the run
* carries [fmt, BASE_UPPER, BASE_LOWER] - the vertex
* stream the draw reads positions/colours from.
@@ -8860,6 +8893,47 @@ static void svc_gl_stream_chunks(struct svc_channel *ch, const u8 *buf,
ch->h_object, svc_gr_cand.push_va,
(at_off - svc_gl.start_off) / 4u, n);
}
+ /*
+ * SESSION 153-CONTINUED-46: the first chunk's full method stream.
+ * Every beef chunk launch stalls the physical front-end (grfifo
+ * frozen, fecs_cur=0, PBDMA stopped mid-block at a
+ * SET_VERTEX_ATTRIBUTE_A run while X windows retire) and the window
+ * tail (`3DSTREAM ... tail=`) alone cannot name the stalling method.
+ * Dump the first chunk's words (up to 96) as operand lines so the
+ * exact method list is on record; bounded per boot.
+ */
+ {
+ static u32 chunkdump_log;
+
+ if (chunkdump_log < 4 && svc_gl.start_off + 8u <= at_off) {
+ u32 cur2 = svc_gl.start_off, end2, k2, wcnt;
+ u32 cw2 = svc_gl_chunk_words_param ?
+ svc_gl_chunk_words_param :
+ SVC_GL_STREAM_CHUNK_WORDS;
+
+ chunkdump_log++;
+ if (cw2 > 448u)
+ cw2 = 448u;
+ end2 = svc_gl_chunk_boundary(buf, len, cur2, at_off,
+ cw2);
+ wcnt = (end2 - cur2) / 4u;
+ if (wcnt > 96u)
+ wcnt = 96u;
+ pr_info("sema-gl: CHUNK1 h=0x%x push=0x%llx off=0x%x words=%u (dump up to 96)\n",
+ ch->h_object, svc_gr_cand.push_va, cur2,
+ (end2 - cur2) / 4u);
+ for (k2 = 0; k2 + 4 <= wcnt; k2 += 4)
+ pr_info("sema-gl: CHUNK1[%02u] %08x %08x %08x %08x\n",
+ k2,
+ get_unaligned_le32(buf + cur2 + 4u * k2),
+ get_unaligned_le32(buf + cur2 + 4u * (k2 + 1)),
+ get_unaligned_le32(buf + cur2 + 4u * (k2 + 2)),
+ get_unaligned_le32(buf + cur2 + 4u * (k2 + 3)));
+ for (; k2 < wcnt; k2++)
+ pr_info("sema-gl: CHUNK1[%02u] %08x\n", k2,
+ get_unaligned_le32(buf + cur2 + 4u * k2));
+ }
+ }
}
static void svc_gl_flush(struct nv_vgpu_device *vgpu,
@@ -8988,6 +9062,21 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
goto out;
}
}
+ /*
+ * SESSION 153-CONTINUED-46: the admission
+ * bound uses the BUILT-IN 384-word chunk
+ * budget, never the sema_gl_chunk_words
+ * bisect knob. r1033 evidence: with the knob
+ * at 64 the bound shrank to 6*64=384 words
+ * and the client's real 1253-word windows
+ * were rejected outright ("flush skip ...
+ * win=1253 words (no operands, no hold)") -
+ * the knob is meant to cut the replayed
+ * chunks smaller, not to stop the capture.
+ * With the full bound every real window is
+ * admitted and chunked (the knob then
+ * truncates the replayed words, logged).
+ */
if (svc_gl.prog_va && svc_gl.rel_va &&
!svc_gl.param_va &&
gsp_sema_gl_xlate && !ch->engine_type &&
@@ -8995,9 +9084,7 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
at_off > svc_gl.start_off &&
(at_off - svc_gl.start_off) / 4u <=
SVC_GL_STREAM_CHUNK_MAX *
- (svc_gl_chunk_words_param ?
- svc_gl_chunk_words_param :
- SVC_GL_STREAM_CHUNK_WORDS))
+ SVC_GL_STREAM_CHUNK_WORDS)
stream_noparam = true;
if (!stream_noparam) {
/*
@@ -18909,6 +18996,79 @@ static int svc_read_va_push_ex(struct nv_vgpu_device *vgpu,
svc_read_last_src = (u8)src;
svc_read_last_pa = pa;
/*
+ * SESSION 153-CONTINUED-46: zero-content rescue for the
+ * direct GL client's push pages. r1032/r1033 evidence:
+ * the beef family's 5-7 KB draws routinely read back
+ * ALL ZERO from the trusted chain (latch/map/walk) -
+ * "sema-pushdec: h=0xbeef0100 va=0xcae2894 len=7148
+ * rc=0 src=4 pa=0x13d181000 ... first8=00000000
+ * 00000000" - and the capture is then skipped as a
+ * zero-head window, so no 3D replay ever launches
+ * (42 such skips on one boot). This is the r817 class
+ * of stale/wrong kernel-buffer translations: the
+ * driver's OWN PROMOTE_CTX statement holds the real
+ * backing. When the first chunk of a big GL push reads
+ * all zero from the trusted chain (src >= 2; the
+ * host-verified injected pins are ground truth and
+ * stay authoritative), probe the promote booking: a
+ * DIFFERENT page with non-zero content is used in
+ * place, the learned cache is healed with it, and the
+ * read is tagged src=9 so the pushdec witness shows
+ * the rescue. Bounded per boot.
+ */
+ if (src >= 2 && first && len >= 512u &&
+ (ch->h_object & 0xffff0000u) == 0xbeef0000u &&
+ !memchr_inv(p, 0, chunk)) {
+ u64 ppa = 0;
+ int pap = SVC_APERTURE_SYS;
+ static u32 pushzero_log;
+
+ if (!svc_promo_lookup(page_va, &ppa, &pap) &&
+ ((ppa & ~(u64)(SEMA_PAGE_SIZE - 1)) !=
+ (pa & ~(u64)(SEMA_PAGE_SIZE - 1)))) {
+ u64 rpa = ppa +
+ (va & (SEMA_PAGE_SIZE - 1));
+ int rrc = svc_read_ap(vgpu, rpa, pap,
+ p, chunk);
+
+ if (!rrc && memchr_inv(p, 0, chunk)) {
+ pa = rpa;
+ svc_read_last_src = 9;
+ svc_read_last_pa = rpa;
+ svc_lat_store(page_va, ppa,
+ pap);
+ if (pushzero_log < 32) {
+ pushzero_log++;
+ pr_info("sema-pushzero: h=0x%x va=0x%llx src=%u pa=0x%llx promo pa=0x%llx rescued=1\n",
+ ch->h_object,
+ (unsigned long long)va,
+ src,
+ (unsigned long long)pa,
+ (unsigned long long)ppa);
+ }
+ } else {
+ /* promo page unusable: put the
+ * zero state back exactly. */
+ memset(p, 0, chunk);
+ if (!rrc && pushzero_log < 32) {
+ pushzero_log++;
+ pr_info("sema-pushzero: h=0x%x va=0x%llx src=%u pa=0x%llx promo pa=0x%llx rescued=0 (promo content zero)\n",
+ ch->h_object,
+ (unsigned long long)va,
+ src,
+ (unsigned long long)pa,
+ (unsigned long long)ppa);
+ }
+ }
+ } else if (pushzero_log < 32) {
+ pushzero_log++;
+ pr_info("sema-pushzero: h=0x%x va=0x%llx src=%u pa=0x%llx rescued=0 (no distinct promo booking)\n",
+ ch->h_object,
+ (unsigned long long)va, src,
+ (unsigned long long)pa);
+ }
+ }
+ /*
* r820: name whether the trusted answer came from a
* driver-declared PROMOTE_CTX buffer; the decode uses
* it for the zero-NOP acceptance (see the flag).