summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_phys_booter.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gsp_phys_booter.c')
-rw-r--r--kernel/gsp_phys_booter.c585
1 files changed, 558 insertions, 27 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index f7b43fb..afe92b6 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -4959,6 +4959,96 @@ static u32 gsp_phys_engine_type_from_errstr(const char *err)
}
/*
+ * SESSION 153-CONTINUED-12: last published GR launch identity, set by the
+ * single-inflight submit path so a PBDMA fault dump can name the entry the
+ * PBDMA is stuck on (block length in dwords, guest push VA, object handle).
+ */
+static u32 gsp_phys_gr_last_words;
+static u32 gsp_phys_gr_last_h;
+static u64 gsp_phys_gr_last_push_va;
+
+/*
+ * SESSION 153-CONTINUED-12: on a robust-channel PBDMA error (exceptType
+ * 0x20) dump every PBDMA's latched instruction state and, for the PBDMA
+ * whose PB_FETCH sits inside the module pushbuffer page, the raw block words
+ * around the fetch point plus the whole block with indices. The latched
+ * raw instruction (HDR_SHADOW) can be matched against the block dump to
+ * name the exact word the PBDMA rejected ("invalid method encoding"); the
+ * fetch position shows how far the walk got. Read-only MMIO + a racy read
+ * of the pb page (best effort, diagnostic only); bounded per boot.
+ */
+#define GSP_PHYS_PBDMA_DUMP_MAX 6u
+
+static const char *gsp_phys_pbdma_type_name(u32 t)
+{
+ static const char *names[8] = { "SSDM", "INC", "STORE_SDM", "NON_INC",
+ "IMMD", "INC_ONCE", "USE_SDM", "END_SEG" };
+
+ return names[t & 7u];
+}
+
+static void gsp_phys_pbdma_fault_dump(u32 chid)
+{
+ static u32 dump_n;
+ void __iomem *bar0 = gsp_phys_gpu_bar0();
+ u64 pb_va = boot_stage.rmcall_va_pb;
+ u32 i;
+
+ if (!bar0 || !gsp_phys_gr_last_words || dump_n >= GSP_PHYS_PBDMA_DUMP_MAX)
+ return;
+ dump_n++;
+ pr_info("vgsp-gr: PB-FAULT-DUMP chid=%u last_block words=%u h=0x%x push=0x%llx pb_va=0x%llx\n",
+ chid, gsp_phys_gr_last_words, gsp_phys_gr_last_h,
+ gsp_phys_gr_last_push_va, pb_va);
+ for (i = 0; i < 12; i++) {
+ u32 b = 0x40000u + i * 0x2000u;
+ u32 intr0 = readl(bar0 + b + 0x108u);
+ u32 gpf = readl(bar0 + b + 0x050u);
+ u32 pbf = readl(bar0 + b + 0x054u);
+ u32 pbfhi = readl(bar0 + b + 0x058u);
+ u32 pbhd = readl(bar0 + b + 0x084u);
+ u32 pbcnt = readl(bar0 + b + 0x088u);
+ u32 pbd0 = readl(bar0 + b + 0x08cu);
+ u32 hdrsh = readl(bar0 + b + 0x118u);
+ u32 fetch64 = ((pbfhi & 0xffu) << 32) | (pbf & ~3u);
+
+ if (!intr0 && !pbf && !hdrsh && !pbhd)
+ continue;
+ pr_info("vgsp-gr: PB-FAULT-DUMP P%u intr0=%08x gpf=%u pbfetch=0x%llx len=%u hdr=%08x(%s m=0x%x cnt=%u sub=%u) cnt=%08x data0=%08x shadow=%08x\n",
+ i, intr0, gpf, fetch64, pbfhi >> 11,
+ hdrsh, gsp_phys_pbdma_type_name(hdrsh >> 29),
+ (hdrsh & 0x1fffu) << 2,
+ (hdrsh >> 16) & 0xfffu, (hdrsh >> 13) & 7u,
+ pbcnt, pbd0, hdrsh);
+ }
+ {
+ struct gsp_phys_boot_chunk *pbch =
+ &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_PB];
+ u32 *pw = pbch->pages ? gsp_phys_chunk_page_va(pbch, 0) : NULL;
+
+ if (pw && pb_va) {
+ u32 n = gsp_phys_gr_last_words < 384u ?
+ gsp_phys_gr_last_words : 384u;
+ u32 w;
+
+ for (w = 0; w < n; w += 16) {
+ u32 m;
+
+ pr_info("vgsp-gr: PB-FAULT-DUMP pb[%3u]= %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ w, pw[w], w + 1 < n ? pw[w + 1] : 0,
+ w + 2 < n ? pw[w + 2] : 0, w + 3 < n ? pw[w + 3] : 0,
+ w + 4 < n ? pw[w + 4] : 0, w + 5 < n ? pw[w + 5] : 0,
+ w + 6 < n ? pw[w + 6] : 0, w + 7 < n ? pw[w + 7] : 0,
+ w + 8 < n ? pw[w + 8] : 0, w + 9 < n ? pw[w + 9] : 0,
+ w + 10 < n ? pw[w + 10] : 0, w + 11 < n ? pw[w + 11] : 0,
+ w + 12 < n ? pw[w + 12] : 0, w + 13 < n ? pw[w + 13] : 0,
+ w + 14 < n ? pw[w + 14] : 0, w + 15 < n ? pw[w + 15] : 0);
+ }
+ }
+ }
+}
+
+/*
* Note every consumed 0x1006 in the bounded last-4 ring, advance the fault
* epoch (the relay submit snapshot compares against it) and log the designed
* observation line when the fault relay is armed. Decoding and counting are
@@ -4978,6 +5068,8 @@ static void gsp_phys_fault_observe(const struct gsp_phys_rx_record *rec,
pr_info_ratelimited("vgsp-fault: func=0x1006 seq=%u exceptType=0x%x runlistId=%u chid=%u prevXid=0x%x errString=\"%s\"\n",
rec->seq_num, out->except_type, out->runlist_id,
out->chid, out->prev_xid, out->err);
+ if (out->except_type == 0x20u)
+ gsp_phys_pbdma_fault_dump(out->chid);
}
/* Note every consumed 0x1020 for the C3 corroboration ring. */
@@ -15259,10 +15351,24 @@ static int gsp_phys_rmcall_publish_locked(void __iomem *bar0, u64 pb_gpu_va,
* site passes false, so the default COPY0 ladder emits the same bytes as
* before.
*/
-static int gsp_phys_rmcall_ce_submit_locked(void __iomem *bar0, u64 src_va,
- u64 dst_va, u64 sem_va, u32 bytes,
- u32 sem_payload, bool gr,
- struct gsp_phys_rmcall_ce_submit *out)
+/*
+ * SESSION 153-CONTINUED-12: pb_off selects where in the module pushbuffer
+ * page the method block is written and fetched from. The warm-semaphore
+ * probe (gsp_phys_rmcall_gr_sem_push_locked) uses the upper half so it can
+ * never overwrite a forwarded launch's pending method block: measured with
+ * the PBDMA dump, the probe's 10-dword block landing on words 0..9 of a
+ * pending 139-word 3D replay made the PBDMA walk the probe's
+ * SET_OBJECT/REPORT_SEM tail and then decode the replay window's operand
+ * 0x00230200 as an instruction -> PBENTRY on chid 4 and no retire. Every
+ * forwarded GR/CE block stays at offset 0 (<= 512 dwords = 0x800 bytes by
+ * the GSP_PHYS_RMCALL_GR_METHOD_WORDS bound).
+ */
+static int gsp_phys_rmcall_ce_submit_off_locked(void __iomem *bar0,
+ u64 src_va, u64 dst_va,
+ u64 sem_va, u32 bytes,
+ u32 sem_payload, bool gr,
+ u32 pb_off,
+ struct gsp_phys_rmcall_ce_submit *out)
{
struct gsp_phys_boot_chunk *pb =
&boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_PB];
@@ -15291,11 +15397,16 @@ static int gsp_phys_rmcall_ce_submit_locked(void __iomem *bar0, u64 src_va,
if (!bar0 || !boot_stage.rmcall_token || !bytes ||
bytes > GSP_PHYS_RMCALL_COPY_SIZE)
return -EINVAL;
+ if (pb_off & 3u)
+ return -EINVAL;
pb_gpu_va = boot_stage.rmcall_va_pb;
if (!pb_gpu_va || (pb_gpu_va & 3))
return -EIO;
- pb_va = gsp_phys_chunk_page_va(pb, 0);
+ if (!gsp_phys_chunk_page_va(pb, 0) || pb_off > GSP_PHYS_PAGE_SIZE)
+ return -EIO;
+ pb_va = (u32 *)((u8 *)gsp_phys_chunk_page_va(pb, 0) + pb_off);
+ pb_gpu_va += pb_off;
ring_va = gsp_phys_chunk_page_va(ring, 0);
userd = gsp_phys_chunk_page_va(userd_c, 0);
if (!pb_va || !ring_va || !userd ||
@@ -15316,9 +15427,22 @@ static int gsp_phys_rmcall_ce_submit_locked(void __iomem *bar0, u64 src_va,
dst_va, bytes,
sem_payload);
+ if (pb_off + words * 4 > GSP_PHYS_PAGE_SIZE)
+ return -EIO;
+
return gsp_phys_rmcall_publish_locked(bar0, pb_gpu_va, words, out);
}
+static int gsp_phys_rmcall_ce_submit_locked(void __iomem *bar0, u64 src_va,
+ u64 dst_va, u64 sem_va, u32 bytes,
+ u32 sem_payload, bool gr,
+ struct gsp_phys_rmcall_ce_submit *out)
+{
+ return gsp_phys_rmcall_ce_submit_off_locked(bar0, src_va, dst_va,
+ sem_va, bytes, sem_payload,
+ gr, 0u, out);
+}
+
/*
* H1 stage-4 GR verification: one GR semaphore release on the ladder channel,
* shared by stage 4's GR branch and the debugfs trigger 9 re-run. The caller
@@ -15376,12 +15500,22 @@ static int gsp_phys_rmcall_gr_sem_push_locked(void __iomem *bar0)
unsigned long deadline;
int ret;
- ret = gsp_phys_rmcall_ce_submit_locked(bar0, GSP_PHYS_RMCALL_VA_SRC,
+ /*
+ * SESSION 153-CONTINUED-12: this probe runs while a forwarded launch
+ * may still be pending in the same pushbuffer page. It must not land
+ * on words 0.. of that page: measured PB-FAULT-DUMP shows the probe's
+ * 10-dword block overwriting a pending 139-word 3D replay's head, so
+ * the PBDMA walks the probe then decodes a replay operand as an
+ * instruction (PBENTRY, no retire). Use the upper half (0x800); the
+ * forwarded block is bounded to 512 dwords = 0x800 bytes.
+ */
+ ret = gsp_phys_rmcall_ce_submit_off_locked(bar0,
+ GSP_PHYS_RMCALL_VA_SRC,
GSP_PHYS_RMCALL_VA_DST,
GSP_PHYS_RMCALL_VA_SEM,
GSP_PHYS_RMCALL_COPY_SIZE,
GSP_PHYS_RMCALL_SEM_PAYLOAD, true,
- &sub);
+ 0x800u, &sub);
if (ret) {
pr_err("vgsp-boot: rm-call: GR submission refused before the doorbell (%d)\n",
ret);
@@ -19096,7 +19230,7 @@ gsp_phys_gr_m91_fault_cap(const struct gsp_phys_gr_submit *req,
fn = &gsp_phys_fault_notes[(gsp_phys_fault_note_next +
GSP_PHYS_FAULT_NOTE_MAX - 1u) %
GSP_PHYS_FAULT_NOTE_MAX];
- pr_info("vgsp-m91: FAULT-CAP ok=%u faults=%u retry=%s attempts=%u qmd=0x%llx qmd64=%s prog_w48=0x%08x w49=0x%08x prog_va=0x%llx prog_pa=0x%llx code_src=%s off=0x%x code64=%s fault_et=0x%x fault_chid=%u fault_err=\"%.48s\"\n",
+ pr_info("vgsp-m91: FAULT-CAP ok=%u faults=%u retry=%s attempts=%u qmd=0x%llx qmd64=%s prog_w48=0x%08x w49=0x%08x prog_va=0x%llx prog_pa=0x%llx code_src=%s off=0x%x code64=%s fault_et=0x%x fault_chid=%u fault_err=\"%.200s\"\n",
ok ? 1u : 0u, faults,
fast ? "fast-rearm" : (rearmed ? "resched/fresh-entry" : "none"),
attempts, qmd_dst, qhex, qmd[48], qmd[49], pva, ppa, src, off,
@@ -19343,22 +19477,193 @@ static bool gsp_phys_rmcall_gr_3d_reloc(u32 *up_w, u32 *lo_w,
* number of relocated pair(s). Run widths follow the service's decoder:
* op 1/3/5 are 1+count dwords, everything else resyncs one dword.
*/
+/*
+ * SESSION 153-CONTINUED-13 (draw-window strip experiment): comma-separated
+ * hex NVC597 method byte-addresses to REMOVE from a captured 3D window
+ * before the verbatim replay (whole methods - a dropped run takes its
+ * count data words with it). Answers whether a DRAW window's non-retire
+ * lives in the draw content (BEGIN/END/immediate vertices/DRAW_ARRAY) or
+ * in the state binds/method-stream shape. Empty = off (historical
+ * behaviour); live-tunable via sysfs so one boot can sweep the variants.
+ */
+static char *gsp_phys_rmcall_gr_win_drop;
+module_param_named(sema_gl_win_drop, gsp_phys_rmcall_gr_win_drop, charp, 0644);
+MODULE_PARM_DESC(sema_gl_win_drop,
+ "Hex method byte-addresses (comma-separated) to strip from a captured 3D window before replay (draw isolation; default empty)");
+
+static bool gsp_phys_rmcall_gr_win_drop_match(u32 addr)
+{
+ const char *p = gsp_phys_rmcall_gr_win_drop;
+ u32 n = 0;
+
+ if (!p || !*p)
+ return false;
+ while (*p && n < 64) {
+ u32 v = 0;
+ u32 digits = 0;
+
+ while (*p == ' ' || *p == ',' || *p == ':' || *p == ';')
+ p++;
+ for (;;) {
+ char c = *p;
+ u32 d;
+
+ if (c >= '0' && c <= '9')
+ d = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ d = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ d = c - 'A' + 10;
+ else
+ break;
+ if (v <= 0xfffffffu)
+ v = (v << 4) | d;
+ p++;
+ digits++;
+ }
+ if (!digits)
+ break;
+ n++;
+ if (v == addr)
+ return true;
+ }
+ return false;
+}
+
+/*
+ * SESSION 153-CONTINUED-14 (FECS residency / draw-stall isolation): patch
+ * the DATA of one method inside a replayed 3D window before it reaches the
+ * engine. Format: comma-separated `addr:data` hex pairs, addr = method
+ * byte-address (e.g. 1618 = BEGIN, 1614 = END), data = the replacement
+ * value. Header-only methods (IMMD) get their 13-bit data field rewritten
+ * in place; count-carrying methods (INC/NON_INC/INC_ONCE) get their FIRST
+ * payload word replaced. Live-tunable via sysfs so one boot can sweep
+ * variants (BEGIN OP=7 QUADS -> TRIANGLES etc.) against the draw-stall.
+ */
+static char *gsp_phys_rmcall_gr_win_patch;
+module_param_named(sema_gl_win_patch, gsp_phys_rmcall_gr_win_patch, charp,
+ 0644);
+MODULE_PARM_DESC(sema_gl_win_patch,
+ "Hex addr:data pairs (comma-separated) patched into a captured 3D window method before replay (default empty)");
+
+static bool gsp_phys_rmcall_gr_win_patch_find(u32 addr, u32 *out_data)
+{
+ const char *p = gsp_phys_rmcall_gr_win_patch;
+ u32 n = 0;
+
+ if (!p || !*p)
+ return false;
+ while (*p && n < 64) {
+ u32 v = 0, d = 0;
+ u32 digits = 0;
+ bool have_data = false;
+
+ while (*p == ' ' || *p == ',' || *p == ':' || *p == ';')
+ p++;
+ for (;;) {
+ char c = *p;
+ u32 h;
+
+ if (c >= '0' && c <= '9')
+ h = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ h = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ h = c - 'A' + 10;
+ else
+ break;
+ if (v <= 0xfffffffu)
+ v = (v << 4) | h;
+ p++;
+ digits++;
+ }
+ if (!digits)
+ break;
+ if (*p == ':' || *p == '=') {
+ p++;
+ for (;;) {
+ char c = *p;
+ u32 h;
+
+ if (c >= '0' && c <= '9')
+ h = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ h = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ h = c - 'A' + 10;
+ else
+ break;
+ if (d <= 0xfffffffu)
+ d = (d << 4) | h;
+ p++;
+ have_data = true;
+ }
+ }
+ n++;
+ if (have_data && v == addr) {
+ *out_data = d;
+ return true;
+ }
+ }
+ return false;
+}
+
static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
+ u32 *out_n, u32 *n_dropped,
const struct gsp_phys_gr_submit *req,
u32 *n_shadow)
{
- u32 off = 0, reloc = 0;
+ u32 off = 0, wout = 0, reloc = 0, dropped = 0;
- memcpy(out, in, (size_t)n * 4);
+ *out_n = n;
+ *n_dropped = 0;
while (off < n) {
- u32 hdr = out[off];
+ u32 hdr = in[off];
u32 op = (hdr >> 29) & 0x7u;
u32 cnt = (hdr >> 16) & 0xfffu;
u32 addr = (hdr & 0x1fffu) << 2;
+ u32 span = 1u;
+
+ if ((op == 1 || op == 3 || op == 5) && cnt &&
+ off + 1u + cnt <= n)
+ span = 1u + cnt;
+ if (gsp_phys_rmcall_gr_win_drop_match(addr)) {
+ dropped++;
+ off += span;
+ continue;
+ }
+ memcpy(&out[wout], &in[off], (size_t)span * 4);
+ {
+ /*
+ * SESSION 153-CONTINUED-14: live data patch (BEGIN
+ * OP sweep etc.). Header-only forms rewrite the 13-bit
+ * data field; count-carrying forms rewrite the first
+ * payload word.
+ */
+ u32 pv;
+
+ if (gsp_phys_rmcall_gr_win_patch_find(addr, &pv)) {
+ static u32 patch_log;
+ u32 old;
+ if (span > 1u) {
+ old = out[wout + 1];
+ out[wout + 1] = pv;
+ } else {
+ old = (out[wout] >> 16) & 0x1fffu;
+ out[wout] = (out[wout] & ~0x1fff0000u) |
+ ((pv & 0x1fffu) << 16);
+ }
+ if (patch_log < 32) {
+ patch_log++;
+ pr_info("vgsp-gr: 3D win-patch addr=0x%x off=%u data 0x%x -> 0x%x\n",
+ addr, off, old, pv);
+ }
+ }
+ }
if ((op == 1 || op == 3 || op == 5) && cnt &&
off + 1u + cnt <= n) {
- u32 *d = &out[off + 1];
+ u32 *d = &out[wout + 1];
u32 lo_i = 0;
bool pair = false;
@@ -19375,16 +19680,58 @@ static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
addr < 0x4000 && (addr & 0x3fu) == 0x14 &&
cnt >= 2) {
pair = true;
+ } else if (op == 1 && (addr == 0x1c00 || addr == 0x1c10) &&
+ cnt >= 3) {
+ /* SESSION 153-CONTINUED-10: SET_VERTEX_STREAM_
+ * A_FORMAT(j)+BASE(j) run [fmt, upper, lower]
+ * (j*0x10 stride) - the vertex data the draw
+ * reads. Observed [00001000 1 2150b790]. */
+ lo_i = 1;
+ pair = true;
+ } else if (op == 1 && addr == 0x2380 && cnt >= 3) {
+ /* SET_CONSTANT_BUFFER_SELECTOR_A/B:
+ * [size, upper, lower]; observed
+ * [00010000 1 2150a500]. */
+ lo_i = 1;
+ pair = true;
+ } else if (op == 1 && addr >= 0x0800 && addr < 0x0c00 &&
+ (addr & 0x3fu) == 0u && cnt >= 2) {
+ /* SET_COLOR_TARGET_A/B(j) offset pair:
+ * [upper, lower, w, h, fmt]; the surface the
+ * draw rasterizes into (must live in guest
+ * RAM for the present lane to see pixels).
+ * Observed [1 222fa200 40 4 f7 0]. */
+ lo_i = 0;
+ pair = true;
}
- if (pair && gsp_phys_rmcall_gr_3d_reloc(&d[lo_i],
- &d[lo_i + 1],
- req, n_shadow))
- reloc++;
+ if (pair) {
+ u64 pva = ((u64)(d[lo_i] & 0xffu) << 32) |
+ (u64)(d[lo_i + 1] & 0xfffffffcu);
+ bool ok = gsp_phys_rmcall_gr_3d_reloc(
+ &d[lo_i], &d[lo_i + 1], req,
+ n_shadow);
+ static u32 pair_log;
+
+ if (ok)
+ reloc++;
+ if (pair_log < 48) {
+ pair_log++;
+ pr_info("vgsp-gr: pair addr=0x%x va=0x%llx -> %s0x%llx\n",
+ addr, pva, ok ? "" :
+ "MISS ",
+ ((u64)(d[lo_i] & 0xffu) << 32) |
+ (u64)(d[lo_i + 1] & 0xfffffffcu));
+ }
+ }
+ wout += span;
off += 1u + cnt;
continue;
}
off++; /* header-only / resync, like the service decoder */
+ wout++;
}
+ *out_n = wout;
+ *n_dropped = dropped;
return reloc;
}
@@ -19435,8 +19782,8 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
u32 qmd_rel_off = 0, ch_rel_off = 0;
u32 poll = 0, w0 = 0, w1 = 0, grpoll_n = 0;
bool sem_gated = false;
- static u32 sked_prev[11];
- static u32 sked_sample[11];
+ static u32 sked_prev[14];
+ static u32 sked_sample[14];
static u32 sked_tick;
static bool sked_first;
/*
@@ -20177,6 +20524,48 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
memcpy(sh_page + (inl->dst_va &
(GSP_PHYS_PAGE_SIZE - 1)),
inl->data, len);
+ /*
+ * SESSION 153-CONTINUED-10: a stream3d (3D
+ * method-window) submission maps the engine's
+ * VAs straight at the GUEST page HPA
+ * (gsp_phys_rmcall_gr_map_shadow_locked
+ * installs the guest PTE - there is no
+ * shadow-chunk copy in that path), so the
+ * engine's I-fetch reads the guest page, not
+ * sh_page. The GLX program page is never
+ * written by the guest itself (the driver's
+ * uploads never ran), and the r945 evidence is
+ * exactly that: "Graphics Exception: Shader
+ * Program Header 11/18 Error" on the draw
+ * whose inline prog-hex blob had only reached
+ * this shadow copy. Author the inline bytes
+ * into the guest page too, emulating the
+ * upload that never executed.
+ */
+ if (stream3d && pg->hpa)
+ memcpy((u8 *)__va(pg->hpa) +
+ (inl->dst_va &
+ (GSP_PHYS_PAGE_SIZE - 1)),
+ inl->data, len);
+ /*
+ * SESSION 153-CONTINUED-11: bounded witness of
+ * every inline record applied (which program
+ * page received which image), so the SPH
+ * delivery is auditable from the log.
+ */
+ {
+ static u32 inl_apply_log;
+
+ if (inl_apply_log < 24) {
+ inl_apply_log++;
+ pr_info("vgsp-gr: INLINE-APPLY dst=0x%llx len=%u pg_va=0x%llx guest=%u first=%08x %08x\n",
+ inl->dst_va, len, pg_va,
+ stream3d && pg->hpa ?
+ 1u : 0u,
+ len >= 4 ? get_unaligned_le32(inl->data) : 0u,
+ len >= 8 ? get_unaligned_le32(inl->data + 4) : 0u);
+ }
+ }
copied++;
}
/*
@@ -20600,14 +20989,56 @@ gr_page_unmapped:
* held, before the method block is built.
*/
if (stream3d) {
+ u32 win_n = req->stream_n;
+ u32 ndrop = 0;
u32 nrel = gsp_phys_rmcall_gr_3d_patch_window(
- req->stream, req->stream_n, stream_patch, req,
- &n_shadow);
+ req->stream, req->stream_n, stream_patch, &win_n,
+ &ndrop, req, &n_shadow);
win3d = stream_patch;
- win3d_n = req->stream_n;
- pr_info("vgsp-gr: 3D window relocated %u operand pair(s) to shadow VAs (window=%u shadow=%u)\n",
- nrel, win3d_n, n_shadow);
+ win3d_n = win_n;
+ pr_info("vgsp-gr: 3D window relocated %u operand pair(s) to shadow VAs (window=%u shadow=%u dropped=%u)\n",
+ nrel, win3d_n, n_shadow, ndrop);
+ if (ndrop) {
+ static u32 gr3d_drop_log;
+
+ if (gr3d_drop_log < 32) {
+ gr3d_drop_log++;
+ pr_info("vgsp-gr: 3D win-drop h=0x%x push=0x%llx raw=%u kept=%u dropped=%u list=[%s]\n",
+ req->h_object, req->push_va,
+ req->stream_n, win3d_n, ndrop,
+ gsp_phys_rmcall_gr_win_drop ?
+ gsp_phys_rmcall_gr_win_drop : "");
+ }
+ }
+ /*
+ * SESSION 153-CONTINUED-11: bounded raw window dump - the
+ * exact C597 method stream the engine walks (program binds
+ * 0x2014/0x2018+j*0x40, SET_PIPELINE_SHADER 0x2000+j*0x40,
+ * draws 0x1618/0xd74). Two windows per boot.
+ */
+ {
+ static u32 gr3d_win_log;
+ u32 wi;
+
+ if (gr3d_win_log < 4) {
+ gr3d_win_log++;
+ pr_info("vgsp-gr: 3D window dump h=0x%x push=0x%llx words=%u\n",
+ req->h_object, req->push_va, win3d_n);
+ for (wi = 0; wi < win3d_n && wi < 384;
+ wi += 16)
+ pr_info("vgsp-gr: 3D win[%3u]: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ wi,
+ win3d[wi + 0], win3d[wi + 1],
+ win3d[wi + 2], win3d[wi + 3],
+ win3d[wi + 4], win3d[wi + 5],
+ win3d[wi + 6], win3d[wi + 7],
+ win3d[wi + 8], win3d[wi + 9],
+ win3d[wi + 10], win3d[wi + 11],
+ win3d[wi + 12], win3d[wi + 13],
+ win3d[wi + 14], win3d[wi + 15]);
+ }
+ }
}
words = gsp_phys_rmcall_build_gr_launch(methods, req, qmd,
@@ -20615,6 +21046,10 @@ gr_page_unmapped:
ch_sem_va, want_ch,
win3d, win3d_n);
memcpy(gsp_phys_chunk_page_va(pb, 0), methods, words * 4);
+ /* SESSION 153-CONTINUED-12: identify this block to a later PBDMA dump. */
+ WRITE_ONCE(gsp_phys_gr_last_words, words);
+ WRITE_ONCE(gsp_phys_gr_last_h, req->h_object);
+ WRITE_ONCE(gsp_phys_gr_last_push_va, req->push_va);
pr_info("vgsp-gr: qmd w0..3=%08x %08x %08x %08x w4..7=%08x %08x %08x %08x w23..25=%08x %08x %08x w32/33=%08x %08x w48/49=%08x %08x\n",
qmd[0], qmd[1], qmd[2], qmd[3], qmd[4], qmd[5], qmd[6], qmd[7],
qmd[23], qmd[24], qmd[25], qmd[32], qmd[33], qmd[48], qmd[49]);
@@ -20997,10 +21432,20 @@ gr_page_unmapped:
sked_sample[8] = ioread32(bar0 + 0x00409b00u);
sked_sample[9] = ioread32(bar0 + 0x00409b04u);
sked_sample[10] = ioread32(bar0 + 0x00409400u);
+ /*
+ * SESSION 153-CONTINUED-14: FECS/FE ctxsw handshake
+ * lanes. fe0 = FECS_CTXSW_STATUS_FE_0 (0x409c00),
+ * fehost = FECS_HOST_INT_STATUS (0x409c18), fecsirq
+ * = FECS_IRQSTAT (0x409008). If the FECS dies in a
+ * switch, it should die visibly here first.
+ */
+ sked_sample[11] = ioread32(bar0 + 0x00409c00u);
+ sked_sample[12] = ioread32(bar0 + 0x00409c18u);
+ sked_sample[13] = ioread32(bar0 + 0x00409008u);
if (sked_first ||
memcmp(sked_sample, sked_prev,
sizeof(sked_sample))) {
- pr_info("vgsp-gr: sked rec +%ums cwd0=%08x cwd1=%08x cwd2=%08x info1=%08x info2=%08x act=%08x esr=%08x grfifo=%08x fecs_cur=%08x fecs_new=%08x ctxsw1=%08x\n",
+ pr_info("vgsp-gr: sked rec +%ums cwd0=%08x cwd1=%08x cwd2=%08x info1=%08x info2=%08x act=%08x esr=%08x grfifo=%08x fecs_cur=%08x fecs_new=%08x ctxsw1=%08x fe0=%08x fehost=%08x fecsirq=%08x\n",
sked_tick *
GSP_PHYS_RMCALL_POLL_MS,
sked_sample[0], sked_sample[1],
@@ -21008,7 +21453,8 @@ gr_page_unmapped:
sked_sample[4], sked_sample[5],
sked_sample[6], sked_sample[7],
sked_sample[8], sked_sample[9],
- sked_sample[10]);
+ sked_sample[10], sked_sample[11],
+ sked_sample[12], sked_sample[13]);
memcpy(sked_prev, sked_sample,
sizeof(sked_sample));
sked_first = false;
@@ -21021,6 +21467,50 @@ gr_page_unmapped:
else
w0 = READ_ONCE(*(u32 *)sem_va);
}
+ /*
+ * SESSION 153-CONTINUED-13: early PBDMA fetch probe for a 3D
+ * stream launch - where is the front end while a DRAW window
+ * does not retire? One shot per launch at ~150 ms and ~1.2 s
+ * (5 ms poll), bounded per boot. pbfetch inside the module
+ * pushbuffer page = the walk/exec is stuck in our block;
+ * pbfetch past it = the whole stream was decoded and the
+ * stall is in execution/completion.
+ */
+ if (stream3d && bar0 &&
+ (sked_tick == 30 || sked_tick == 240)) {
+ static u32 winprobe_log;
+ u32 pi;
+
+ if (winprobe_log < 10) {
+ winprobe_log++;
+ pr_info("vgsp-gr: WINPROBE +%ums push=0x%llx words=%u block_dma=0x%llx grfifo=%08x trapped=%08x trapped_lo=%08x\n",
+ sked_tick * GSP_PHYS_RMCALL_POLL_MS,
+ req->push_va, words, sub.pb_dma,
+ ioread32(bar0 + 0x00400504u),
+ ioread32(bar0 + 0x00400704u),
+ ioread32(bar0 + 0x00400708u));
+ for (pi = 0; pi < 12; pi++) {
+ u32 b = 0x40000u + pi * 0x2000u;
+ u32 intr0 = ioread32(bar0 + b + 0x108u);
+ u32 gpf = ioread32(bar0 + b + 0x050u);
+ u32 pbf = ioread32(bar0 + b + 0x054u);
+ u32 pbfhi = ioread32(bar0 + b + 0x058u);
+ u32 hdrsh = ioread32(bar0 + b + 0x118u);
+ u64 fetch64 =
+ ((u64)(pbfhi & 0xffu) << 32) |
+ (u64)(pbf & ~3u);
+
+ if (!intr0 && !gpf && !pbf && !hdrsh)
+ continue;
+ pr_info("vgsp-gr: WINPROBE P%u intr0=%08x gp_put=%u pbfetch=0x%llx hdr=%08x(%s m=0x%x cnt=%u sub=%u)\n",
+ pi, intr0, gpf, fetch64, hdrsh,
+ gsp_phys_pbdma_type_name(hdrsh >> 29),
+ (hdrsh & 0x1fffu) << 2,
+ (hdrsh >> 16) & 0xfffu,
+ (hdrsh >> 13) & 7u);
+ }
+ }
+ }
if (ch_rel_hpa)
w1 = READ_ONCE(*(u32 *)((u8 *)__va(ch_rel_hpa) +
ch_rel_off));
@@ -21456,6 +21946,16 @@ gr_page_unmapped:
ioread32(bar0 + 0x00405b00u),
ioread32(bar0 + 0x00400100u),
ioread32(bar0 + 0x0040013cu));
+ if (bar0)
+ pr_info("vgsp-gr: FECS readback: CUR=0x%08x NEW=0x%08x ST1=0x%08x FE0=0x%08x HOSTINT=0x%08x IRQSTAT=0x%08x CPUCTL=0x%08x OS=0x%08x\n",
+ ioread32(bar0 + 0x00409b00u),
+ ioread32(bar0 + 0x00409b04u),
+ ioread32(bar0 + 0x00409400u),
+ ioread32(bar0 + 0x00409c00u),
+ ioread32(bar0 + 0x00409c18u),
+ ioread32(bar0 + 0x00409008u),
+ ioread32(bar0 + 0x00409100u),
+ ioread32(bar0 + 0x00409080u));
if (bar0) {
u32 g;
@@ -24670,6 +25170,36 @@ static int gsp_phys_reg_probe_show(struct seq_file *m, void *unused)
0x00404194u);
sked_hww_esr = gsp_phys_reg_probe_line(m, bar0, "SKED_HWW_ESR",
0x00407020u);
+ /*
+ * SESSION 153-CONTINUED-11: the shader-program-header fault window.
+ * DS_HWW_ESR bit n = "the SPH with index n failed validation" (hwref:
+ * NV_PGRAPH_PRI_DS_HWW_ESR_SPH0_ERR..SPH23_ERR, ESR_2 for 24..30);
+ * the SPH_INFO registers snapshot the START address of each stage's
+ * faulting SPH, so a post-fault read names the exact VA the engine
+ * fetched.
+ */
+ (void)gsp_phys_reg_probe_line(m, bar0, "DS_HWW_ESR",
+ 0x00405840u);
+ (void)gsp_phys_reg_probe_line(m, bar0, "DS_HWW_ESR_2",
+ 0x00405848u);
+ (void)gsp_phys_reg_probe_line(m, bar0,
+ "DS_HWW_ESR_SPH_INFO_PROGRAM_REGION_A",
+ 0x00405c1cu);
+ (void)gsp_phys_reg_probe_line(m, bar0,
+ "DS_HWW_ESR_SPH_INFO_PROGRAM_REGION_B",
+ 0x00405c20u);
+ {
+ u32 si;
+
+ for (si = 0; si < 6u; si++) {
+ gsp_phys_reg_probe_line(m, bar0,
+ "DS_HWW_ESR_SPH_INFO_STAGE_START",
+ 0x00405c00u + si * 4u);
+ gsp_phys_reg_probe_line(m, bar0,
+ "DS_HWW_ESR_SPH_INFO_STAGE_START_UPPER",
+ 0x00405c24u + si * 4u);
+ }
+ }
sked_activity = gsp_phys_reg_probe_line(m, bar0, "SKED_ACTIVITY",
0x00407054u);
sked_cwd0 = gsp_phys_reg_probe_line(m, bar0,
@@ -25382,8 +25912,9 @@ static_assert(GSP_PHYS_RMCALL_VA_GR_CTX +
static_assert(sizeof(struct gsp_phys_gr_page) == 24,
"the GR forwarding page descriptor must stay {u64,u64,u32,u32} = 24 bytes");
static_assert(offsetof(struct gsp_phys_gr_inline, data) == 12 &&
- sizeof(struct gsp_phys_gr_inline) == 80,
- "the GR forwarding inline record must stay {u64,u32,u8[64]} padded to 80 bytes");
+ sizeof(struct gsp_phys_gr_inline) >=
+ 12 + GSP_PHYS_GR_INLINE_BYTES,
+ "the GR forwarding inline record must stay {u64,u32,u8[N]}");
static_assert((2u + 1u + GSP_PHYS_RMCALL_GR_QMD_INLINE_WORDS + 8u) <=
GSP_PHYS_RMCALL_GR_METHOD_WORDS,
"the GR launch method block (compute init + SET_OBJECT + inline-QMD run + PCAS + REPORT_SEMAPHORE) must fit the bound");