From c38872182f2eb11660887ba73188d22a6b3835de Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Mon, 9 Nov 2026 15:34:33 -0500 Subject: vgsp-sema: read a channel's push through the VAS it declared Several VA spaces can map the same GPU VA. The push read's learned/pin chain is global over VASes, so the GL client's CE channel 0xcaf00012 (which pushes at 0x120000000) was answered with a zero page from another VAS and every entry was 'unreadable' while the channel's own VAS mapped the real method stream. Try the channel's declared VAS first; fall back to the existing source chain when it has no published directory or the walk misses. --- kernel/gsp_sema_service.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index e156339..c805fb5 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -24080,6 +24080,42 @@ static void svc_zrw_reset(u64 page_va) } } +/* + * r1670: walk one VA through the VAS a channel declared at registration. + * The push-buffer read below uses this before the learned/pin chain: that + * chain is global over VASes, and several VASes map the same GPU VA (the GL + * client's CE channel 0xcaf00012 pushes at 0x120000000, which the learned + * map answered with a zero page from a different VAS while the channel's + * own VAS mapped the real method stream). Returns true on a walk hit. + */ +static bool svc_walk_vas_by_handle(struct nv_vgpu_device *vgpu, u32 h_vas, + u64 va, u64 *pa, int *ap) +{ + struct svc_vaspace local[SVC_MAX_VASPACES]; + unsigned long flags; + int i, n = 0; + + if (!h_vas) + return false; + spin_lock_irqsave(&svc.vas_lock, flags); + for (i = 0; i < SVC_MAX_VASPACES; i++) + if (svc.vas[i].valid && svc.vas[i].h_vaspace == h_vas) + local[n++] = svc.vas[i]; + spin_unlock_irqrestore(&svc.vas_lock, flags); + + for (i = 0; i < n; i++) { + int alt = local[i].ap == SVC_APERTURE_FB ? + SVC_APERTURE_SYS : SVC_APERTURE_FB; + + if (!svc_gmmu_walk(vgpu, local[i].pdb, local[i].entries, va, + local[i].ap, pa, ap, false) || + !svc_gmmu_walk(vgpu, local[i].pdb, local[i].entries, va, + alt, pa, ap, false)) + return true; + } + return false; +} + static int svc_read_va_push_ex(struct nv_vgpu_device *vgpu, struct svc_channel *ch, u64 va, void *buf, size_t len, bool skip_ipin) @@ -24104,6 +24140,29 @@ static int svc_read_va_push_ex(struct nv_vgpu_device *vgpu, have_win = !svc_pb_win_lookup(ch, page_va, &win_pa); + /* + * r1670: the VAS the channel declared at registration is the + * authority for its push buffer. The learned/pin chain below + * is global over VASes and can name a page from another + * address space that maps the same GPU VA. + */ + if (ch->h_vaspace) { + u64 vpa = 0; + int vap = SVC_APERTURE_SYS; + u64 in_page = va & (SEMA_PAGE_SIZE - 1); + + if (!svc_walk_vas_by_handle(vgpu, ch->h_vaspace, + page_va, &vpa, &vap) && + vpa && + !svc_read_ap(vgpu, vpa + in_page, vap, p, + chunk)) { + pa = vpa + in_page; + svc_read_last_src = 6; + svc_read_last_pa = pa; + done = true; + } + } + for (src = 0; src < 5 && !done; src++) { bool via_ipin = false; bool untrusted = false; -- cgit v1.2.3