diff options
| -rw-r--r-- | kernel/gsp_sema_service.c | 54 |
1 files changed, 51 insertions, 3 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 7837d32..611bb35 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -2184,6 +2184,7 @@ static u32 svc_pool_pa_logged_n; static u64 svc_pool_pa_miss_log; static u32 svc_pool_pa_hit_log; static u32 svc_pool_wr_log; +static u32 svc_pool_ident_rej_dbg; /* First-use-per-page proof line; bounded for the boot. */ static void svc_pool_pa_note(u32 h, u64 va, u64 pa, const char *src) @@ -2228,6 +2229,24 @@ static void svc_pool_wr_note(u32 h, u64 va, u64 pa, const char *src, } } +/* + * Identity translations for pool semaphore VAs are never legitimate: the + * pool is a client GPU-VA range whose backing pages live at distinct guest + * PAs. A degenerate source (stale/misparsed rm_mem record, walk over a + * recycled directory, heuristic map entry) can yield PA == VA; the + * read-back proof does NOT catch it because the SYS aperture happily + * reads/writes at that address, and once cached it poisons every pool + * release: the completion write lands where the guest never polls, the + * client's first tracker wait never wakes, and the guest wedges + * (WEDGE-COMPARE-r7-vs-r10.md: wedges wrote pa=0x121000000 src=trusted, + * greens pa=<guest RAM> src=pin). Never accept or cache one. + */ +static inline bool svc_pool_pa_identity(u64 va, u64 pa) +{ + return (pa & ~(u64)(SEMA_PAGE_SIZE - 1)) == + (va & ~(u64)(SEMA_PAGE_SIZE - 1)); +} + static int svc_pool_pa_lookup(u64 va, u64 *pa, int *ap) { u64 page = va & ~(u64)(SEMA_PAGE_SIZE - 1); @@ -2235,6 +2254,12 @@ static int svc_pool_pa_lookup(u64 va, u64 *pa, int *ap) for (i = 0; i < svc_pool_pa_n; i++) { if (svc_pool_pa[i].va_page == page) { + /* A poisoned (identity) entry must not serve. */ + if (svc_pool_pa_identity(page, + svc_pool_pa[i].pa_page)) { + svc_pool_pa[i].va_page = ~0ULL; + return -ENOENT; + } *pa = svc_pool_pa[i].pa_page | (va & (SEMA_PAGE_SIZE - 1)); *ap = svc_pool_pa[i].ap; @@ -2249,6 +2274,9 @@ static void svc_pool_pa_store(u64 va, u64 pa, int ap) u64 page = va & ~(u64)(SEMA_PAGE_SIZE - 1); u32 i; + /* Never cache an identity translation (see svc_pool_pa_identity). */ + if (svc_pool_pa_identity(page, pa)) + return; for (i = 0; i < svc_pool_pa_n; i++) { if (svc_pool_pa[i].va_page == page) { svc_pool_pa[i].pa_page = @@ -2352,7 +2380,8 @@ static int svc_pool_pa_resolve(struct nv_vgpu_device *vgpu, u32 h, u64 va, u64 exact = (wpa & ~(u64)(SEMA_PAGE_SIZE - 1)) | (va & (SEMA_PAGE_SIZE - 1)); - if (!svc_read_ap(vgpu, exact, SVC_APERTURE_SYS, &probe, + if (!svc_pool_pa_identity(va, exact) && + !svc_read_ap(vgpu, exact, SVC_APERTURE_SYS, &probe, sizeof(probe))) { svc_pool_pa_store(va, exact, SVC_APERTURE_SYS); svc_tr_clear_failure(page); @@ -2392,7 +2421,8 @@ static int svc_pool_pa_resolve(struct nv_vgpu_device *vgpu, u32 h, u64 va, u64 exact = (wpa & ~(u64)(SEMA_PAGE_SIZE - 1)) | (va & (SEMA_PAGE_SIZE - 1)); - if (!svc_read_ap(vgpu, exact, svc_ap_of(wap), &probe, + if (!svc_pool_pa_identity(va, exact) && + !svc_read_ap(vgpu, exact, svc_ap_of(wap), &probe, sizeof(probe))) { svc_pool_pa_store(va, exact, wap); svc_tr_clear_failure(page); @@ -2406,7 +2436,8 @@ static int svc_pool_pa_resolve(struct nv_vgpu_device *vgpu, u32 h, u64 va, } if (!svc_targeted_resolve(vgpu, va, &pa)) { /* Read back before trusting: an unreadable page is not kept. */ - if (!svc_read_ap(vgpu, pa, SVC_APERTURE_SYS, &probe, + if (!svc_pool_pa_identity(va, pa) && + !svc_read_ap(vgpu, pa, SVC_APERTURE_SYS, &probe, sizeof(probe))) { svc_pool_pa_store(va, pa, SVC_APERTURE_SYS); svc_tr_clear_failure(page); @@ -2430,6 +2461,23 @@ static int svc_pool_pa_resolve(struct nv_vgpu_device *vgpu, u32 h, u64 va, * RAM. Proof-gated like every other source. */ if (!svc_lookup_trusted(vgpu, va, &pa, ap)) { + /* + * The last resort can return a degenerate identity mapping + * (PA == VA) that the read-back proof cannot catch; accepting + * it caches a poisoned translation and the completion write + * lands where the guest never polls (the wedge class: + * WEDGE-COMPARE-r7-vs-r10.md). Reject and miss instead so the + * release queues in svc_pending[] and retries until the real + * (pin/walk) translation lands. + */ + if (svc_pool_pa_identity(va, pa)) { + if (svc_pool_ident_rej_dbg < 16) { + svc_pool_ident_rej_dbg++; + pr_info("vgsp-sema: pool-pa identity rejected ch=0x%x va=0x%llx pa=0x%llx (never legit; release queues for the real pin)\n", + h, va, pa); + } + return -ENOENT; + } if (!svc_read_ap(vgpu, pa, svc_ap_of(*ap), &probe, sizeof(probe))) { svc_pool_pa_store(va, pa, *ap); |
