summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_sema_service.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-27 20:02:06 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-27 20:02:06 -0400
commit6f2e61934d553d335026a07f0c749a9aa8b2a27a (patch)
tree885513e6b08a8a3eb63c087e55838573a43e3f89 /kernel/gsp_sema_service.c
parent8a4463a63978b2df81703a9c22358884c431d5cf (diff)
downloadopen-iov-6f2e61934d553d335026a07f0c749a9aa8b2a27a.tar.gz
open-iov-6f2e61934d553d335026a07f0c749a9aa8b2a27a.zip
vgsp: xw-mirror page-reuse guards + submit-status clearer scaffold (SESSION 148 r873)
The unguarded 3 ms mirror loop kept writing the count law into its latched page after the guest had reused it (r872 evidence: guest 'Bad page map ... pmd:102656067' pointing at exactly the mirrored page's PA as another process's PTE table). The loop now latches the progress word's base bits and stops permanently when they change or are zeroed, and re-checks the counter object's signature every 64th tick. New sema_xw_comp parameter + vgsp-xwcomp kthread can clear a submit member's armed 0x8000 status (value 0 pattern only); r874 proved an unverified clear into a false member kills Xorg within seconds, so it stays uninjected until the member address is watched live.
Diffstat (limited to 'kernel/gsp_sema_service.c')
-rw-r--r--kernel/gsp_sema_service.c187
1 files changed, 171 insertions, 16 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 831f920..384a232 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -30532,6 +30532,9 @@ static struct task_struct *svc_xw_task;
static int svc_xw_mirror_loop(void *unused)
{
+ u32 orig_top = 0;
+ u32 ticks = 0;
+
while (!kthread_should_stop()) {
struct nv_vgpu_device *d, *vgpu = NULL;
u32 c = 0, cur = 0;
@@ -30551,28 +30554,180 @@ static int svc_xw_mirror_loop(void *unused)
&cur, sizeof(cur))) {
/*
* SESSION 148 (r867): member = counter (dword index,
- * high bits preserved). The r867 "one learned step
- * ahead" experiment produced a PERMANENT +0x70
- * mismatch at every check (member re-advances right
- * after each counter change, so the driver's == test
- * can never land: the xorg.log shows
- * "(2, ..., member_ahead, counter)" timeouts and the
- * GL client gets nothing). The plain tracking law is
+ * high bits preserved). The plain tracking law is
* the correct one: the driver's re-check cadence
- * (~3 ms-scale re-probes observed as 3 s "(0)" success
- * prints) gives the mirror room to settle on the new
- * counter between the driver's increment and its
- * first probe.
+ * (~3 ms-scale re-probes) gives the mirror room to
+ * settle on the new counter between the driver's
+ * increment and its first probe.
+ *
+ * SESSION 148 r873 page-reuse guard: the word page
+ * is only ours while the driver's progress word
+ * lives there. When the page is freed/reused (X
+ * server died, batch BO recycled) its content
+ * changes and the base bits (bits 16..23 = 0x10 or
+ * 0x14) vanish. r872 evidence: the mirror kept
+ * writing 0x26d into a page that had become another
+ * process's PTE table (guest "Bad page map ...
+ * pmd:102656067" pointing at exactly the mirrored
+ * page's PA), corrupting unrelated guest memory.
+ * NEVER write once the base bits have changed; stop
+ * the thread so nothing more touches the page.
*/
- u32 m = (cur & 0xfffc0000u) |
- ((c >> 2) & 0x3ffffu);
+ u32 top = cur & 0xfffc0000u;
- if (m != cur)
- svc_write_ap(vgpu, svc_xw_mem_pa,
- SVC_APERTURE_SYS, &m, sizeof(m));
+ if (!orig_top) {
+ if (!top) {
+ /* not a progress word: write nothing */
+ msleep(3);
+ continue;
+ }
+ orig_top = top;
+ }
+ if (top != orig_top) {
+ pr_warn("sema-xw: mirror STOP: word page 0x%llx repurposed (top 0x%x -> 0x%x)\n",
+ (unsigned long long)svc_xw_mem_pa,
+ orig_top, cur);
+ break;
+ }
+ if (!(++ticks & 63)) {
+ /* counter-side guard: channel signature */
+ u64 sig = 0;
+
+ if (svc_read_ap(vgpu, svc_xw_cnt_pa - 0x50,
+ SVC_APERTURE_SYS, &sig,
+ sizeof(sig)) ||
+ sig != 0x0100001300000001ULL) {
+ pr_warn("sema-xw: mirror STOP: counter object 0x%llx repurposed (sig=0x%llx)\n",
+ (unsigned long long)svc_xw_cnt_pa,
+ (unsigned long long)sig);
+ break;
+ }
+ }
+ {
+ u32 m = top | ((c >> 2) & 0x3ffffu);
+ u16 st = 0;
+
+ if (m != cur)
+ svc_write_ap(vgpu, svc_xw_mem_pa,
+ SVC_APERTURE_SYS, &m,
+ sizeof(m));
+ /*
+ * SESSION 148 r873: submit-kick completion.
+ * The X submit path arms member status
+ * 0x8000 at value_word+0xE (gdb-proven layout:
+ * value @member+0, status @member+0xE) and
+ * polls until it reads 0; nothing else ever
+ * clears it, so each kick paid the driver's
+ * full timeout. Clear it here, right after
+ * the value write, so the poll passes ms
+ * after the frame is submitted.
+ */
+ if (!svc_read_ap(vgpu, svc_xw_mem_pa + 0xE,
+ SVC_APERTURE_SYS, &st,
+ sizeof(st)) &&
+ st == 0x8000) {
+ u16 z = 0;
+
+ svc_write_ap(vgpu, svc_xw_mem_pa + 0xE,
+ SVC_APERTURE_SYS, &z,
+ sizeof(z));
+ if (!(++ticks % 256))
+ pr_info("sema-xw: mirror alive (clears=%u cnt=0x%x)\n",
+ ticks, c);
+ }
+ }
}
msleep(3);
}
+ svc_xw_task = NULL;
+ return 0;
+}
+
+/*
+ * X/GL submit-kick completion clearer (SESSION 148 r873). The X server's
+ * submit path (decoded at nvidia_drv dc170) arms a 16-byte member -
+ * u32 value at +0x0 (0 while pending) and u16 status 0x8000 at +0xE - and
+ * then polls dc330 until EVERY member status reads 0; nothing in the
+ * emulation ever writes that completion, so each kick paid the driver's
+ * full retry/timeout cycle. The member lives in a page whose page offset
+ * is 0xB0 (membase is page-aligned), so the status word is at page
+ * offset 0xBE. The finder in guest RAM reports that PA during an armed
+ * window; this thread clears status 0x8000 -> 0 (and only that exact
+ * pattern) so the kick poll passes within a few ms. Pure tuning aid -
+ * no product logic depends on it.
+ */
+static u64 svc_xw_comp_pa; /* PA of the u16 status word (page off 0xBE) */
+static struct task_struct *svc_xw_comp_task;
+
+static int svc_xw_comp_loop(void *unused)
+{
+ u64 n = 0;
+
+ while (!kthread_should_stop()) {
+ struct nv_vgpu_device *d, *vgpu = NULL;
+ u32 val = 1;
+ u16 st = 0;
+
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_for_each_entry(d, &nv_vgpu_driver.devices, list) {
+ if (atomic_read(&d->vfio_dev_open_count) > 0) {
+ vgpu = d;
+ break;
+ }
+ }
+ mutex_unlock(&nv_vgpu_driver.lock);
+ if (vgpu &&
+ !svc_read_ap(vgpu, svc_xw_comp_pa - 0xE,
+ SVC_APERTURE_SYS, &val, sizeof(val)) &&
+ !svc_read_ap(vgpu, svc_xw_comp_pa, SVC_APERTURE_SYS,
+ &st, sizeof(st))) {
+ /*
+ * Clear ONLY the exact pending pattern (value still
+ * 0, status 0x8000). Anything else is either a
+ * completed member (status 0) or a repurposed page -
+ * leave it alone.
+ */
+ if (st == 0x8000 && val == 0) {
+ u16 z = 0;
+
+ svc_write_ap(vgpu, svc_xw_comp_pa,
+ SVC_APERTURE_SYS, &z, sizeof(z));
+ if (!(++n % 25))
+ pr_info("sema-xw: comp clears=%llu\n",
+ (unsigned long long)n);
+ }
+ }
+ msleep(2);
+ }
+ svc_xw_comp_task = NULL;
+ return 0;
+}
+
+int gsp_sema_service_xw_comp(u64 comp_pa)
+{
+ if (!comp_pa) {
+ if (svc_xw_comp_task) {
+ kthread_stop(svc_xw_comp_task);
+ svc_xw_comp_task = NULL;
+ pr_info("sema-xw: comp stopped\n");
+ }
+ return 0;
+ }
+ if (comp_pa & 1)
+ return -EINVAL;
+ svc_xw_comp_pa = comp_pa;
+ if (!svc_xw_comp_task) {
+ svc_xw_comp_task = kthread_run(svc_xw_comp_loop, NULL,
+ "vgsp-xwcomp");
+ if (IS_ERR(svc_xw_comp_task)) {
+ int err = PTR_ERR(svc_xw_comp_task);
+
+ svc_xw_comp_task = NULL;
+ return err;
+ }
+ }
+ pr_info("sema-xw: comp status_pa=0x%llx running\n",
+ (unsigned long long)comp_pa);
return 0;
}