summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_phys_booter.c109
-rw-r--r--kernel/gsp_phys_gpu.c26
-rw-r--r--kernel/gsp_phys_gpu.h5
-rw-r--r--kernel/gsp_sema_service.c45
-rw-r--r--kernel/gsp_sema_service.h8
5 files changed, 182 insertions, 11 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 8756d83..c8e224b 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -25725,14 +25725,41 @@ gsp_phys_gr_m91_fault_cap(const struct gsp_phys_gr_submit *req,
* (gsp_sema_gr_reloc_resolve: pins/map/walks before the ledger, the
* guest-walk-first counterpart of the fill's ledger-first order). Only a
* different, plausibly-code page is adopted (mode ALT; the engine-visible
- * copy is taken from it); anything else leaves the QMD pair at the guest
- * VA (mode GUEST_VA, the ptr_guest behaviour for this pair). One
- * rate-limited CODESRC-SUSPECT line per detection.
+ * copy is taken from it).
+ *
+ * r1625 (the c361-c368 wedge): mode GUEST_VA used to leave the QMD pair at
+ * the guest VA, which is above every engine-PDB window - the GPCCLIENT_GCC
+ * I-fetch then raised FAULT_PDE and parked the FIFO permanently (the
+ * WEDGE-REARM TSG probe cannot revive a dead PBDMA). With
+ * gsp_phys_gr_codesrc_reloc (default 1) the refused page is instead
+ * relocated to the engine-mapped window-0 shadow and the QMD pair is
+ * rewritten there. The source page is copied when it is only empty (so any
+ * content elsewhere in the page is preserved and inline records still land
+ * on top); PTE/module pages are never republished. The program entry
+ * (original QMD offset preserved) is then overwritten with the client's own
+ * known-good SPH+SASS image for the bound stage (gsp_sema_gl_prog_img) -
+ * a raw EXIT stub would fail the engine's SPH validation - truncated at a
+ * 128-bit instruction boundary with a terminal EXIT appended. If the shadow
+ * mapping cannot be installed the launch is refused (the existing unmapped-
+ * page path), so a QMD whose program pointer is not engine-mapped is never
+ * published. One rate-limited CODESRC-SUSPECT line per detection, plus a
+ * CODESRC-RELOC line for each relocation.
*/
#define GSP_PHYS_GR_CODESRC_OK 0
#define GSP_PHYS_GR_CODESRC_ALT 1
#define GSP_PHYS_GR_CODESRC_GUEST_VA 2
+/*
+ * r1625: terminal 16 B sm_75 SASS (MOV R1, c[0x0][0x0]; EXIT), taken from
+ * the first two instructions of a compiled sm_75 kernel's .text. Appended
+ * after a truncated relocation image so control can never run into the
+ * zero-filled tail of the shadow page.
+ */
+static const u8 gsp_phys_gr_codesrc_exit[16] = {
+ 0x02, 0x7a, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00,
+ 0x00, 0x0f, 0x00, 0x00, 0x00, 0xc4, 0x0f, 0x00,
+};
+
static bool
gsp_phys_gr_codesrc_chunk_has(const struct gsp_phys_boot_chunk *c, u64 hpa)
{
@@ -25816,7 +25843,8 @@ static bool gsp_phys_gr_codesrc_empty(const u8 *p, u32 off)
static noinline int
gsp_phys_gr_code_src(const struct gsp_phys_gr_submit *req,
- const struct gsp_phys_gr_page *pg, u64 *alt_hpa)
+ const struct gsp_phys_gr_page *pg, u64 *alt_hpa,
+ const char **why_out, bool *unsafe_out)
{
u64 page = pg->guest_va & ~(u64)(GSP_PHYS_PAGE_SIZE - 1);
u32 off = (u32)(req->qmd[48] & (GSP_PHYS_PAGE_SIZE - 1));
@@ -25826,6 +25854,10 @@ gsp_phys_gr_code_src(const struct gsp_phys_gr_submit *req,
int mode = GSP_PHYS_GR_CODESRC_GUEST_VA;
*alt_hpa = 0;
+ if (why_out)
+ *why_out = NULL;
+ if (unsafe_out)
+ *unsafe_out = false;
if (off > GSP_PHYS_PAGE_SIZE - 16u)
off = GSP_PHYS_PAGE_SIZE - 16u; /* keep 16 B in-page */
if (gsp_phys_gr_codesrc_empty(p, off))
@@ -25836,6 +25868,14 @@ gsp_phys_gr_code_src(const struct gsp_phys_gr_submit *req,
why = "module-pt-page";
if (!why)
return GSP_PHYS_GR_CODESRC_OK;
+ if (why_out)
+ *why_out = why;
+ /* The page content must not be republished as code: PTE/filler
+ * bytes would execute as SASS. Empty is safe to copy (all zero)
+ * but is republished with a known-good image at the entry. */
+ if (unsafe_out && why &&
+ (!strcmp(why, "pte-page") || !strcmp(why, "module-pt-page")))
+ *unsafe_out = true;
/*
* Alternative resolution order; only a different page that itself
* passes the content checks is adopted.
@@ -29208,16 +29248,63 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
* CODESRC: the program page (qmd_word 48) is the page
* the engine's I-fetch reads; screen its source
* before publishing it. A refused source is either
- * replaced by the alternative resolver's page or the
- * pair is left at the guest VA (no copy, no rewrite,
- * no mapping - the page is fully withdrawn from the
- * submission).
+ * replaced by the alternative resolver's page or
+ * (r1625) relocated to this shadow with the client's
+ * known-good stage image at the entry, so the QMD
+ * pair is rewritten to an engine-mapped VA. Leaving
+ * it at the guest VA makes the I-fetch fault (PDE)
+ * and parks the FIFO; that legacy withdrawal is now
+ * gated behind gsp_phys_gr_codesrc_reloc=0.
*/
if (pg->hpa && pg->qmd_word == 48u) {
u64 alt_hpa = 0;
- int cs = gsp_phys_gr_code_src(req, pg, &alt_hpa);
-
- if (cs == GSP_PHYS_GR_CODESRC_GUEST_VA) {
+ const char *why = NULL;
+ bool unsafe = false;
+ int cs = gsp_phys_gr_code_src(req, pg,
+ &alt_hpa, &why,
+ &unsafe);
+
+ if (cs == GSP_PHYS_GR_CODESRC_GUEST_VA &&
+ gsp_phys_gr_codesrc_reloc_value()) {
+ u32 po = qmd[48] &
+ (GSP_PHYS_PAGE_SIZE - 1);
+ const u8 *img = NULL;
+ u32 imlen = 0, cp = 0, fit;
+
+ if (po > GSP_PHYS_PAGE_SIZE - 16u)
+ po = GSP_PHYS_PAGE_SIZE - 16u;
+ if (!unsafe)
+ memcpy(sh_page,
+ (u8 *)__va(pg->hpa),
+ GSP_PHYS_PAGE_SIZE);
+ img = gsp_sema_gl_prog_img(
+ pg->guest_va, &imlen);
+ fit = GSP_PHYS_PAGE_SIZE - po;
+ if (imlen > fit)
+ fit = fit >
+ sizeof(gsp_phys_gr_codesrc_exit) ?
+ fit -
+ sizeof(gsp_phys_gr_codesrc_exit) :
+ 0u;
+ if (img && imlen) {
+ cp = imlen < fit ? imlen : fit;
+ memcpy(sh_page + po, img, cp);
+ }
+ if (cp < imlen || !img || !imlen)
+ memcpy(sh_page + po + cp,
+ gsp_phys_gr_codesrc_exit,
+ sizeof(gsp_phys_gr_codesrc_exit));
+ copied++;
+ pr_info_ratelimited("vgsp-gr: CODESRC-RELOC va=0x%llx pa=0x%llx why=%s src=%s img_len=%u cp=%u off=0x%x trunc=%u h=0x%x push=0x%llx (engine copy + QMD pair rewrite)\n",
+ pg->guest_va & ~(u64)(GSP_PHYS_PAGE_SIZE - 1),
+ pg->hpa, why ? why : "none",
+ unsafe ? "stub-only" :
+ img ? "image" : "exit",
+ imlen, cp, po,
+ cp < imlen ? 1u : 0u,
+ req->h_object,
+ req->push_va);
+ } else if (cs == GSP_PHYS_GR_CODESRC_GUEST_VA) {
code_keep_guest = true;
mapped = true;
} else {
diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c
index f1d72f7..33fae68 100644
--- a/kernel/gsp_phys_gpu.c
+++ b/kernel/gsp_phys_gpu.c
@@ -1333,6 +1333,32 @@ int gsp_phys_rmcall_gr_ptr_guest_value(void)
EXPORT_SYMBOL_GPL(gsp_phys_rmcall_gr_ptr_guest_value);
/*
+ * r1625: relocate a CODESRC-SUSPECT program page (gsp_phys_gr_codesrc_reloc;
+ * default 1). The old conservative refusal left the QMD program pointer at
+ * the guest VA when the program page read empty/PTE-shaped. That VA is above
+ * every engine-PDB window, so the GPCCLIENT_GCC I-fetch raised a FAULT_PDE
+ * and the FIFO parked (the c361-c368 wedge). With this set, the builder
+ * instead copies the page into the window-0 shadow and rewrites
+ * qmd[48]/[49] to the shadow VA; an empty/unsafe source is replaced by the
+ * client's own known-good SPH+SASS image at the program offset (a raw
+ * EXIT stub would be rejected by the SPH validation). If the shadow mapping
+ * cannot be installed the launch is refused (fail-closed), so a QMD whose
+ * program pointer is not engine-mapped is never published. 0 restores the
+ * old guest-VA behaviour for a one-flip A/B.
+ */
+static int gsp_phys_gr_codesrc_reloc_param = 1;
+module_param_named(gsp_phys_gr_codesrc_reloc,
+ gsp_phys_gr_codesrc_reloc_param, int, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_codesrc_reloc,
+ "Relocate a CODESRC-SUSPECT GR program page to an engine-mapped shadow page and rewrite the QMD pointer (default 1; 0 = keep the old guest-VA behaviour)");
+
+int gsp_phys_gr_codesrc_reloc_value(void)
+{
+ return gsp_phys_gr_codesrc_reloc_param;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_gr_codesrc_reloc_value);
+
+/*
* m96: KIND (PTE bits 63:56) for the window-0 shadow pool PTEs. On Turing
* the v2 PTE has no PITCH kind; UVM sets GENERIC_MEMORY (0x06). A kind=0
* PTE is a candidate for the SM I-fetch MMU_NACK while the FE tolerates it.
diff --git a/kernel/gsp_phys_gpu.h b/kernel/gsp_phys_gpu.h
index d2faa1e..7b95409 100644
--- a/kernel/gsp_phys_gpu.h
+++ b/kernel/gsp_phys_gpu.h
@@ -413,6 +413,11 @@ 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);
+/* r1625: relocate a CODESRC-SUSPECT program page to an engine-mapped
+ * shadow page and rewrite the QMD pointer pair (default 1; 0 = the old
+ * guest-VA behaviour). */
+int gsp_phys_gr_codesrc_reloc_value(void);
+
/* m95: program GPC MMU_CTRL from PFB MMU_CTRL before a forwarded launch. */
int gsp_phys_rmcall_gr_gpc_mmu_init_value(void);
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 5071314..e7a0172 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -10447,6 +10447,51 @@ int gsp_sema_gl_sph_blob_version(void)
}
/*
+ * r1625: the engine-visible known-good program image for a refused
+ * program page (CODESRC-SUSPECT relocation in the GR launch builder).
+ * A program page the engine is about to I-fetch must contain an SPH+SASS
+ * image, not raw filler: the client's per-window program upload never
+ * ran on this lane, so the page reads empty while the client's own
+ * SPH+SASS image is already published (`svc_gl_vs`/`svc_gl_ps`, the
+ * captured upload or the built-in known-good image). Return the image
+ * for the pipeline stage the section bound at this VA (the sticky
+ * SET_PIPELINE_SHADER TYPE: 5 = PIXEL, otherwise VTG), falling back to
+ * the PIXEL image - the observed empty-program class is the pixel stage
+ * (its 0x2154 upload was never captured). NULL when no image is loaded.
+ */
+const u8 *gsp_sema_gl_prog_img(u64 va, u32 *len_out)
+{
+ u64 page = va & ~(u64)(SEMA_PAGE_SIZE - 1);
+ u32 i, slot = 0, t = 0xff;
+
+ if (len_out)
+ *len_out = 0;
+ for (i = 0; i < svc_gl.n_progs && i < SVC_GL_PROG_MAX; i++) {
+ if ((svc_gl.progs[i] & ~(u64)(SEMA_PAGE_SIZE - 1)) != page)
+ continue;
+ slot = svc_gl.prog_slot[i] < 32 ? svc_gl.prog_slot[i] : 0;
+ t = svc_gl_shader_type[slot];
+ break;
+ }
+ if (t != 5 && t != 0xff && svc_gl_vs_len) {
+ if (len_out)
+ *len_out = svc_gl_vs_len;
+ return svc_gl_vs;
+ }
+ if (svc_gl_ps_len) {
+ if (len_out)
+ *len_out = svc_gl_ps_len;
+ return svc_gl_ps;
+ }
+ if (svc_gl_vs_len) {
+ if (len_out)
+ *len_out = svc_gl_vs_len;
+ return svc_gl_vs;
+ }
+ return NULL;
+}
+
+/*
* SESSION 153 (GL client draw sections): the GL client's own per-frame
* pushes bind TURING_A (0xC597) exactly like the X server's one-shot
* section, but carry no program/param/release operands at all (measured:
diff --git a/kernel/gsp_sema_service.h b/kernel/gsp_sema_service.h
index 6f07209..b40dad2 100644
--- a/kernel/gsp_sema_service.h
+++ b/kernel/gsp_sema_service.h
@@ -676,6 +676,14 @@ bool gsp_sema_mme_scratch25_get(u32 *val);
int gsp_sema_gl_sph_blob_version(void);
/*
+ * r1625: the engine-visible known-good SPH+SASS image for a refused
+ * program page (GR CODESRC-SUSPECT relocation). Stage-typed from the
+ * section's own pipeline bind, falling back to the PIXEL image; NULL
+ * when no image is loaded. *len_out receives the byte count.
+ */
+const u8 *gsp_sema_gl_prog_img(u64 va, u32 *len_out);
+
+/*
* SESSION 153-CONTINUED-68: furthest IR instruction covered by the CAPTURED
* 0x114 uploads (pc + ceil(code words / 3)). The GR launch builder places
* its end-noop macro stub at this address (aligned past the replayed