summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_sema_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gsp_sema_service.c')
-rw-r--r--kernel/gsp_sema_service.c222
1 files changed, 218 insertions, 4 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 0d2c170..4181c0c 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -318,6 +318,13 @@ struct svc_channel {
* way the RC watchdog's ring was starved. */
u8 ce_index; /* rank among the live CE channels (creation order) */
u8 gr_index; /* rank among the live GR channels (creation order) */
+ /* SESSION 154 r1509: the beef GL client's ring head carries its
+ * one-shot init uploads (LOAD_INLINE_DATA code/data images). The
+ * first observed GP_PUT can already be far ahead of those entries
+ * (r1508: put=8982 at registration), so the continuation sweep
+ * never sees them and the fetch pages stay zero. Sweep the head
+ * once per channel registration in inline-only mode. */
+ bool head_inline_done;
u64 gpfifo_va;
u32 gpfifo_entries;
u64 userd_pa;
@@ -16837,6 +16844,187 @@ static u32 svc_surf_owner_vas(struct nv_vgpu_device *vgpu, u32 emit_vas,
return chosen;
}
+/*
+ * SESSION 154 r1510: screen/code-heap overlap quarantine.
+ *
+ * The guest driver's shader/code-heap upload (BAR1 aperture writes that the
+ * mmap'd shadow does not trap) can land on the captured X screen's first
+ * pages (r1508: vidmem 0x200000 held a program image while the X screen VA
+ * 0x120400000 maps there), so the xwd is a picture of code instead of the
+ * render target. The screen allocation is the legitimate owner of those
+ * pages; the code heap is an overlapping guest allocation whose offset the
+ * emulator does not control.
+ *
+ * At the surface capture (the X server's colour-target bind, normally
+ * before the client uploads) take a page snapshot of the captured surface.
+ * On later launches, if the surface's first page currently carries the
+ * client code-heap/SPH signature, restore the whole surface from that
+ * snapshot (once per pollution episode; the baseline stays armed so a later
+ * client context's upload is caught too). The restore is content-gated
+ * (only a page whose +0x700 word is a SPH magic or whose first word is the
+ * observed code-heap header fires) and happens before the engine runs, so
+ * raster stores that follow are preserved and the scanout stops showing
+ * code bytes.
+ */
+#define SVC_SURF_CLEAN_MAX 1024u
+static struct nv_vgpu_device *svc_surf_clean_vgpu;
+static u8 *svc_surf_clean_buf;
+static u64 svc_surf_clean_va;
+static u32 svc_surf_clean_npages;
+static u32 svc_surf_clean_hvas;
+static bool svc_surf_clean_armed;
+static u32 svc_surf_clean_log;
+static u32 svc_surf_clean_restore_log;
+static u8 svc_surf_clean_page[SEMA_PAGE_SIZE];
+static DEFINE_MUTEX(svc_surf_clean_mtx);
+
+static bool svc_surf_code_sig(const u8 *p)
+{
+ u32 w0 = 0, w700 = 0;
+
+ memcpy(&w0, p, 4);
+ memcpy(&w700, p + 0x700, 4);
+ return w0 == 0x0400780cu || w700 == 0x000a1482u ||
+ w700 == 0x020a0481u || w700 == 0x020a0881u;
+}
+
+static void svc_surf_clean_snapshot(struct nv_vgpu_device *vgpu, u64 va,
+ u32 npages, u32 hvas)
+{
+ u32 k;
+
+ if (!vgpu || !va || npages < 2u || npages > SVC_SURF_CLEAN_MAX)
+ return;
+ mutex_lock(&svc_surf_clean_mtx);
+ if (svc_surf_clean_armed && va == svc_surf_clean_va &&
+ npages == svc_surf_clean_npages) {
+ mutex_unlock(&svc_surf_clean_mtx);
+ return;
+ }
+ if (!svc_surf_clean_buf) {
+ svc_surf_clean_buf = vzalloc((size_t)SVC_SURF_CLEAN_MAX *
+ SEMA_PAGE_SIZE);
+ if (!svc_surf_clean_buf) {
+ mutex_unlock(&svc_surf_clean_mtx);
+ return;
+ }
+ }
+ for (k = 0; k < npages; k++) {
+ u64 page = va + ((u64)k << 12);
+ u64 pa = 0;
+ int ap = SVC_APERTURE_SYS;
+ u8 *dst = svc_surf_clean_buf + (size_t)k * SEMA_PAGE_SIZE;
+
+ if (svc_vas_lookup_h(vgpu, hvas, page, &pa, &ap) ||
+ svc_read_ap(vgpu, pa & ~(u64)(SEMA_PAGE_SIZE - 1),
+ svc_ap_of(ap), dst, SEMA_PAGE_SIZE))
+ memset(dst, 0, SEMA_PAGE_SIZE);
+ }
+ if (svc_surf_code_sig(svc_surf_clean_buf)) {
+ /*
+ * Captured after the overlap (the client initialised before
+ * the X bind this boot): the legitimate screen state for
+ * this harness is the X opaque-black clear (the root window
+ * is black and the earlier clean boot's page 0 read
+ * 0xff000000). Use that as the baseline for every page so
+ * the quarantine can still give the render target back to
+ * the engine.
+ */
+ u32 *w = (u32 *)svc_surf_clean_buf;
+ size_t n = (size_t)npages * SEMA_PAGE_SIZE / 4u;
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ w[i] = 0xff000000u;
+ if (svc_surf_clean_log < 8u) {
+ svc_surf_clean_log++;
+ pr_info("SURF-CLEAN-SNAP va=0x%llx pages=%u hvas=0x%x poisoned -> black baseline armed\n",
+ va, npages, hvas);
+ }
+ } else if (svc_surf_clean_log < 8u) {
+ u32 w0 = 0;
+
+ memcpy(&w0, svc_surf_clean_buf, 4);
+ svc_surf_clean_log++;
+ pr_info("SURF-CLEAN-SNAP va=0x%llx pages=%u hvas=0x%x first0=%08x (clean baseline armed)\n",
+ va, npages, hvas, w0);
+ }
+ svc_surf_clean_va = va;
+ svc_surf_clean_npages = npages;
+ svc_surf_clean_hvas = hvas;
+ svc_surf_clean_vgpu = vgpu;
+ svc_surf_clean_armed = true;
+ mutex_unlock(&svc_surf_clean_mtx);
+}
+
+int gsp_sema_surf_clean_restore(u64 va, u64 npages)
+{
+ struct nv_vgpu_device *vgpu;
+ u32 k, changed = 0;
+ u64 page = 0;
+ int ap = SVC_APERTURE_SYS;
+ u8 *cur = svc_surf_clean_page;
+ u8 before[8] = { 0 };
+
+ mutex_lock(&svc_surf_clean_mtx);
+ vgpu = svc_surf_clean_vgpu;
+ if (!svc_surf_clean_armed || !svc_surf_clean_buf || !vgpu ||
+ va != svc_surf_clean_va || npages != svc_surf_clean_npages) {
+ mutex_unlock(&svc_surf_clean_mtx);
+ return 0;
+ }
+ if (svc_vas_lookup_h(vgpu, svc_surf_clean_hvas, va, &page, &ap) ||
+ svc_read_ap(vgpu, page & ~(u64)(SEMA_PAGE_SIZE - 1),
+ svc_ap_of(ap), cur, SEMA_PAGE_SIZE)) {
+ mutex_unlock(&svc_surf_clean_mtx);
+ return 0;
+ }
+ if (!svc_surf_code_sig(cur)) {
+ if (svc_surf_clean_log < 8u) {
+ svc_surf_clean_log++;
+ pr_info("SURF-CLEAN-IDLE va=0x%llx pages=%llu page0_first=%08x (no code signature; baseline kept)\n",
+ va, npages, *(const u32 *)cur);
+ }
+ mutex_unlock(&svc_surf_clean_mtx);
+ return 0;
+ }
+ memcpy(before, cur, sizeof(before));
+ for (k = 0; k < npages; k++) {
+ u64 p = va + ((u64)k << 12);
+ u64 pa = 0;
+ int a = SVC_APERTURE_SYS;
+
+ if (svc_vas_lookup_h(vgpu, svc_surf_clean_hvas, p, &pa, &a)) {
+ a = SVC_APERTURE_SYS;
+ if (svc_walk_vaspaces(vgpu, p, &pa, &a))
+ continue;
+ }
+ if (svc_write_ap(vgpu, pa & ~(u64)(SEMA_PAGE_SIZE - 1),
+ svc_ap_of(a),
+ svc_surf_clean_buf +
+ (size_t)k * SEMA_PAGE_SIZE,
+ SEMA_PAGE_SIZE))
+ break;
+ changed++;
+ }
+ if (!svc_vas_lookup_h(vgpu, svc_surf_clean_hvas, va, &page, &ap) &&
+ !svc_read_ap(vgpu, page & ~(u64)(SEMA_PAGE_SIZE - 1),
+ svc_ap_of(ap), cur, SEMA_PAGE_SIZE))
+ ;
+ if (svc_surf_clean_restore_log < 16u) {
+ svc_surf_clean_restore_log++;
+ pr_info("SURF-CLEAN-RESTORE va=0x%llx pages=%llu changed=%u before8=%08x %08x after8=%08x %08x (screen code-heap quarantine)\n",
+ va, npages, changed,
+ ((const u32 *)before)[0], ((const u32 *)before)[1],
+ ((const u32 *)cur)[0], ((const u32 *)cur)[1]);
+ }
+ /* Keep the baseline armed: a later client context can upload the
+ * code heap again; the content gate only fires on the code
+ * signature, never on raster pixels. */
+ mutex_unlock(&svc_surf_clean_mtx);
+ return (int)changed;
+}
+
static void svc_surf_prelude_capture(struct nv_vgpu_device *vgpu,
const u8 *buf, u32 len, u32 h_vaspace,
u32 h_object, u64 push_va)
@@ -16918,6 +17106,7 @@ static void svc_surf_prelude_capture(struct nv_vgpu_device *vgpu,
svc_surf_owner_name(h_object), h_object,
push_va);
}
+ svc_surf_clean_snapshot(vgpu, va, (u32)npages, h_vaspace);
off += span * 4u;
}
}
@@ -37821,8 +38010,8 @@ static u32 svc_util_lid_scan(const u8 *buf, u32 len, u32 *first_subch)
* this: a shadowed channel read consumed the entry without a parseable
* buffer while the walk resolves the true pages.
*/
-static void svc_gr_util_sweep(struct nv_vgpu_device *vgpu,
- struct svc_channel *ch)
+static void svc_gr_util_sweep_n(struct nv_vgpu_device *vgpu,
+ struct svc_channel *ch, u32 maxent_override)
{
/*
* The observed utility-channel pushes are the ~17 KB setup block and the
@@ -37842,11 +38031,13 @@ static void svc_gr_util_sweep(struct nv_vgpu_device *vgpu,
* channel read was zero-headed */
static u64 svc_util_tail_try; /* ... and the walk attempts (alt is
* the subset that yielded non-zero) */
- u32 i, maxent = min_t(u32, gsp_sema_gr_util_scan, 64);
+ u32 i, maxent = maxent_override ?
+ min_t(u32, maxent_override, 64) :
+ min_t(u32, gsp_sema_gr_util_scan, 64);
u32 swept = 0, nmade = 0;
bool first_sight = false;
- if (!gsp_sema_gr_xlate || !gsp_sema_gr_util_scan)
+ if (!gsp_sema_gr_xlate || !maxent)
return;
if (!ch->gpfifo_va || !ch->gpfifo_entries) {
/* SESSION 143: name every channel the sweep cannot cover. */
@@ -38103,6 +38294,12 @@ static void svc_gr_util_sweep(struct nv_vgpu_device *vgpu,
}
}
+static void svc_gr_util_sweep(struct nv_vgpu_device *vgpu,
+ struct svc_channel *ch)
+{
+ svc_gr_util_sweep_n(vgpu, ch, 0);
+}
+
static void svc_pump_channels(struct nv_vgpu_device *vgpu)
{
if (svc_pdb_disc_pending) {
@@ -38474,6 +38671,23 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
*/
if (gsp_sema_gr_util_scan && gsp_sema_gr_xlate)
svc_gr_util_sweep(vgpu, ch);
+ /*
+ * SESSION 154 r1509: the beef group's one-shot init uploads
+ * (the GL client's LOAD_INLINE_DATA code/constant/vertex
+ * images) sit at the HEAD of the ring, but the first observed
+ * GP_PUT can already be far past them (r1508: put=8982 at
+ * registration), so the continuation sweep above starts
+ * beyond the uploads and the fetch pages stay empty. Scan the
+ * head once per channel with the same read-arbitrated,
+ * tail-aware, inline-only decoder: it materializes the
+ * LOAD_INLINE_DATA/copy-ins and records the destination ring,
+ * while releases and QMD work stay untouched.
+ */
+ if (gsp_sema_gr_xlate && !ch->head_inline_done &&
+ (ch->h_object & 0xfffff000u) == 0xbeef0000u) {
+ ch->head_inline_done = true;
+ svc_gr_util_sweep_n(vgpu, ch, 64);
+ }
/*
* SESSION 153-CONT-7: a kick-family channel whose USERD