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.c229
1 files changed, 228 insertions, 1 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 027c81c..0fd78a4 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -46,6 +46,7 @@
#include <linux/vgaarb.h>
#include <linux/acpi.h>
#include <linux/mm.h>
+#include <linux/vmalloc.h>
#include <asm/barrier.h>
#include <asm/unaligned.h>
@@ -23780,6 +23781,190 @@ static void gsp_phys_gr_pb_copy(const u32 *methods, u32 words)
}
}
+/*
+ * SESSION 153-CONTINUED-95: CTX-BIND launch receipt - is the client's
+ * promoted GR context buffer engine-visible on the replay channel?
+ *
+ * The guest driver promotes its per-channel context buffers through
+ * NV2080_CTRL_CMD_GPU_PROMOTE_CTX entries (MAIN bufferId 0 and PATCH
+ * bufferId 2 carry bInitialize=1 on a fresh context). The emulated GSP-RM
+ * answers SUCCESS and records the statements (gsp_sema_service_ctx_
+ * promote_note), but it does not install them anywhere: the replay
+ * channel's engine context is the buffer the kernel's own ladder built
+ * (server FBMEM MAIN, mapped at GSP_PHYS_RMCALL_VA_GR_CTX and committed by
+ * the mode-35 lane promote). This receipt logs, at the first launches of a
+ * boot, the exact promote statements next to the engine's binding so the
+ * "context state never replayed" hypothesis is decided by bytes: the
+ * client's MAIN/PATCH pa/va/size/bInitialize, and the first words of the
+ * client MAIN buffer read through the GPA->HPA map (a zeroed buffer is the
+ * expected state before a server-side ctx init that the emulation never
+ * performs).
+ */
+static void gsp_phys_ctx_bind_receipt(void)
+{
+ static u32 receipt_n;
+ u64 cpa = 0, cva = 0, csz = 0, ppa = 0, pva = 0, psz = 0;
+ u32 cattr = 0, cinit = 0, pattr = 0, pinit = 0;
+ bool have_main, have_patch;
+
+ if (receipt_n >= 4u)
+ return;
+ receipt_n++;
+ have_main = gsp_sema_service_ctx_promote_get(GSP_PHYS_RMCALL_ENGINE_GR0,
+ 0u, &cpa, &cva, &csz,
+ &cattr, &cinit) == 0;
+ have_patch = gsp_sema_service_ctx_promote_get(GSP_PHYS_RMCALL_ENGINE_GR0,
+ 2u, &ppa, &pva, &psz,
+ &pattr, &pinit) == 0;
+ pr_info("vgsp-gr: CTX-BIND client-main=%s pa=0x%llx va=0x%llx size=0x%llx attr=0x%x binit=%u; client-patch=%s pa=0x%llx va=0x%llx size=0x%llx binit=%u; replay-ctx pa=0x%llx size=0x%x fbmem=%u va=0x%llx srv_ctxva=%u; installed=0 (engine ctx is the ladder's buffer, NOT the client promote)\n",
+ have_main ? "seen" : "none", cpa, cva, csz, cattr, cinit,
+ have_patch ? "seen" : "none", ppa, pva, psz, pinit,
+ boot_stage.rmcall_ctx_pa, boot_stage.rmcall_ctx_size,
+ boot_stage.rmcall_ctx_fbmem ? 1u : 0u,
+ boot_stage.rmcall_ctx_va, boot_stage.rmcall_srv_ctxva ? 1u : 0u);
+ if (have_main && cpa) {
+ u64 hpa = 0;
+
+ if (gsp_phys_gpa_to_hpa_vgpu(NULL, cpa & ~(u64)(GSP_PHYS_PAGE_SIZE - 1), &hpa) == 0 &&
+ pfn_valid(hpa >> PAGE_SHIFT)) {
+ const u32 *w = (const u32 *)__va(hpa);
+
+ pr_info("vgsp-gr: CTX-BIND client-main content[0..7]=%08x %08x %08x %08x %08x %08x %08x %08x (gpa=0x%llx hpa=0x%llx)\n",
+ get_unaligned_le32((const u8 *)&w[0]),
+ get_unaligned_le32((const u8 *)&w[1]),
+ get_unaligned_le32((const u8 *)&w[2]),
+ get_unaligned_le32((const u8 *)&w[3]),
+ get_unaligned_le32((const u8 *)&w[4]),
+ get_unaligned_le32((const u8 *)&w[5]),
+ get_unaligned_le32((const u8 *)&w[6]),
+ get_unaligned_le32((const u8 *)&w[7]),
+ cpa, hpa);
+ } else {
+ pr_info("vgsp-gr: CTX-BIND client-main content: GPA 0x%llx -> HPA translation unavailable (no guest RAM registration yet)\n",
+ cpa);
+ }
+ }
+}
+
+/*
+ * SESSION 153-CONTINUED-95: engine context readback (gsp-phys-ctx-dump).
+ *
+ * What the DS validates SPHs against is the *engine's* context buffer, not
+ * this host's view of anything the client promoted. The replay channel's
+ * context is the ladder's buffer (server FBMEM MAIN) mapped into the
+ * channel VAS at GSP_PHYS_RMCALL_VA_GR_CTX with VIDMEM PTEs. The host has
+ * no aperture to FBMEM (no BAR1 window), so the copy engine reads it: one
+ * 4 KB CE copy per page from the ctx VA to a host page placed on a fresh
+ * never-reused scratch fetch line, polled on the module semaphore exactly
+ * like the P6a guest copies. Runs once per boot at the first launch (the
+ * channel is idle: single-inflight work item), while the ladder's channel
+ * is not server-memory and the identity DMA domain holds. Bounded: stops
+ * at the first refused/timed-out page and records how many bytes landed.
+ */
+#define GSP_PHYS_CTX_RB_BYTES (64u * 1024u)
+
+static u8 *gsp_phys_ctx_rb_buf;
+static u32 gsp_phys_ctx_rb_len;
+static bool gsp_phys_ctx_rb_tried;
+
+static const u8 *gsp_phys_ctx_readback_get(u32 *len)
+{
+ if (len)
+ *len = READ_ONCE(gsp_phys_ctx_rb_len);
+ return gsp_phys_ctx_rb_buf;
+}
+
+static void gsp_phys_ctx_readback_try(void)
+{
+ struct gsp_phys_boot_chunk *sem =
+ &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_SEM];
+ void __iomem *bar0;
+ struct page *pg;
+ u64 hpa;
+ u32 i, bytes = 0;
+
+ if (READ_ONCE(gsp_phys_ctx_rb_tried))
+ return;
+ if (!boot_stage.rmcall_ctx_valid || !boot_stage.rmcall_ctx_pa ||
+ !boot_stage.rmcall_channel ||
+ boot_stage.rmcall_channel_server_mem)
+ return;
+ /*
+ * CE submits are legal on this GR0 ladder (the ladder itself copies
+ * through the CE after the GR channel exists), but
+ * gsp_phys_backend_ladder_ready() refuses them on a GR0 ladder by
+ * design (gsp_phys_rmcall_gr_ce_refuse is about forwarding guest CE
+ * work, not the ladder's own copy). Gate the readback on the same
+ * pieces the submission needs: claimed, RM init-done, not unloading,
+ * a live token.
+ */
+ if (gsp_phys_backend_state() != GSP_PHYS_BACKEND_READY)
+ return;
+ if (!gsp_phys_gpu_claimed() || !boot_stage.rm_init_done ||
+ boot_stage.rm_unload_submitted || !boot_stage.rmcall_token)
+ return;
+ if (gsp_phys_dma_domain_identity() != 1)
+ return;
+ bar0 = gsp_phys_gpu_bar0();
+ if (!bar0)
+ return;
+ if (!gsp_phys_ctx_rb_buf) {
+ gsp_phys_ctx_rb_buf = vmalloc(GSP_PHYS_CTX_RB_BYTES);
+ if (!gsp_phys_ctx_rb_buf)
+ return;
+ }
+ pg = alloc_page(GFP_KERNEL);
+ if (!pg)
+ return;
+ hpa = (u64)page_to_phys(pg);
+ for (i = 0; i < GSP_PHYS_CTX_RB_BYTES / GSP_PHYS_PAGE_SIZE; i++) {
+ u64 src_va = 0, dst_va = 0, sem_va = 0;
+ u32 poll = 0;
+ unsigned long deadline;
+ int ret;
+
+ mutex_lock(&gsp_phys_rm_lock);
+ ret = gsp_phys_rmcall_guest_map_locked(hpa, hpa, 0, &src_va,
+ &dst_va, &sem_va);
+ if (ret) {
+ mutex_unlock(&gsp_phys_rm_lock);
+ break;
+ }
+ memset((u8 *)__va(hpa), 0xab, GSP_PHYS_PAGE_SIZE);
+ ret = gsp_phys_rmcall_ce_submit_locked(bar0,
+ GSP_PHYS_RMCALL_VA_GR_CTX +
+ (u64)i * GSP_PHYS_PAGE_SIZE,
+ dst_va, GSP_PHYS_RMCALL_VA_SEM,
+ GSP_PHYS_PAGE_SIZE,
+ GSP_PHYS_RMCALL_SEM_PAYLOAD, false, NULL);
+ mutex_unlock(&gsp_phys_rm_lock);
+ if (ret)
+ break;
+ deadline = jiffies + msecs_to_jiffies(250);
+ for (;;) {
+ poll = READ_ONCE(*(u32 *)gsp_phys_chunk_page_va(sem, 0));
+ if (poll == GSP_PHYS_RMCALL_SEM_PAYLOAD)
+ break;
+ if (time_after_eq(jiffies, deadline)) {
+ poll = 0;
+ break;
+ }
+ gsp_phys_rmcall_wait_quantum();
+ }
+ if (poll != GSP_PHYS_RMCALL_SEM_PAYLOAD)
+ break;
+ memcpy(gsp_phys_ctx_rb_buf + (size_t)i * GSP_PHYS_PAGE_SIZE,
+ (const u8 *)__va(hpa), GSP_PHYS_PAGE_SIZE);
+ bytes += GSP_PHYS_PAGE_SIZE;
+ }
+ __free_page(pg);
+ WRITE_ONCE(gsp_phys_ctx_rb_len, bytes);
+ WRITE_ONCE(gsp_phys_ctx_rb_tried, true);
+ pr_info("vgsp-gr: CTX-READBACK engine ctx pa=0x%llx copied=%u/%u bytes (CE copies from VA 0x%llx; server/own buffer, not the client promote; WPR2 untouched)\n",
+ boot_stage.rmcall_ctx_pa, bytes, GSP_PHYS_CTX_RB_BYTES,
+ GSP_PHYS_RMCALL_VA_GR_CTX);
+}
+
int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
struct gsp_phys_backend_copy_result *out)
{
@@ -23862,6 +24047,14 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
sked_tick = 0;
sked_first = true;
guest_param_pg = NULL;
+ /*
+ * SESSION 153-CONTINUED-95: prove the client's promoted GR context
+ * buffer binding at the first launches, then read the engine's own
+ * context buffer back for byte-level inspection (both bounded, both
+ * diagnostic; no behavior change to the launch itself).
+ */
+ gsp_phys_ctx_bind_receipt();
+ gsp_phys_ctx_readback_try();
u32 qmd_win_pde = 0, qmd_win_installed = 0;
int qmd_win_ret = 0;
bool qmd_win_diag = false;
@@ -30669,6 +30862,29 @@ static const struct file_operations gsp_phys_init_push_fops = {
.release = single_release,
};
+/*
+ * SESSION 153-CONTINUED-95: gsp-phys-ctx-dump - raw binary readback of the
+ * engine context buffer (see gsp_phys_ctx_readback_try). Empty until the
+ * first launch of the boot has run the CE readback.
+ */
+static ssize_t gsp_phys_ctx_dump_read(struct file *filp, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ const u8 *buf;
+ u32 len = 0;
+
+ buf = gsp_phys_ctx_readback_get(&len);
+ if (!buf || !len)
+ return 0;
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations gsp_phys_ctx_dump_fops = {
+ .owner = THIS_MODULE,
+ .read = gsp_phys_ctx_dump_read,
+ .llseek = default_llseek,
+};
+
int gsp_phys_boot_debugfs_init(void)
{
struct dentry *file;
@@ -30860,7 +31076,18 @@ int gsp_phys_boot_debugfs_init(void)
&gsp_phys_init_push_fops);
if (IS_ERR_OR_NULL(file))
pr_warn("vgsp-boot: debugfs file gsp-phys-init-push creation failed (init-push capture unavailable)\n");
- pr_info("vgsp-boot: debugfs files /sys/kernel/debug/vgsp/{gsp-phys-state read-only, gsp-phys-log-dump read-only, gsp-phys-status-drain read-only, gsp-phys-reg-probe read-only, gsp-phys-ds-spy read-only, gsp-phys-unload-rpc write-only, gsp-phys-rm-probe write-only, gsp-phys-rm-call write-only, gsp-phys-guest-gpa write-only, gsp-phys-guest-ram write-only} ready\n");
+ /*
+ * SESSION 153-CONTINUED-95: raw binary dump of the engine context
+ * buffer readback (64 KB from GSP_PHYS_RMCALL_VA_GR_CTX, taken once
+ * per boot at the first launch). 0444; empty until the first launch
+ * happens. Read with `dd if=...gsp-phys-ctx-dump of=/tmp/ctx.bin`.
+ */
+ file = debugfs_create_file("gsp-phys-ctx-dump", 0444,
+ gsp_phys_debugfs_root, NULL,
+ &gsp_phys_ctx_dump_fops);
+ if (IS_ERR_OR_NULL(file))
+ pr_warn("vgsp-boot: debugfs file gsp-phys-ctx-dump creation failed (engine ctx readback unavailable)\n");
+ pr_info("vgsp-boot: debugfs files /sys/kernel/debug/vgsp/{gsp-phys-state read-only, gsp-phys-log-dump read-only, gsp-phys-status-drain read-only, gsp-phys-reg-probe read-only, gsp-phys-ds-spy read-only, gsp-phys-ctx-dump read-only, gsp-phys-unload-rpc write-only, gsp-phys-rm-probe write-only, gsp-phys-rm-call write-only, gsp-phys-guest-gpa write-only, gsp-phys-guest-ram write-only} ready\n");
return 0;
}