diff options
| -rw-r--r-- | kernel/gsp_sema_service.c | 78 |
1 files changed, 74 insertions, 4 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 50639e3..ddc7078 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -4703,13 +4703,50 @@ static void svc_counter_autoreg(struct nv_vgpu_device *vgpu, if (ch->engine_type || n_gr <= 0) return; + if (ch->gr_index >= n_gr) + return; + va = 0x20400fec0ULL + (u64)(n_gr - 1 - ch->gr_index) * 0x10ULL; if (ch->counter_pa) { + /* + * FORENSICS-v2 anomaly 6: a slot derived in an earlier pass + * can disagree with a later-arriving injected pin (r1: + * derived 0x123a43ff0 vs pin-backed 0x123a28ff0, off by + * 0x1b000). Injected pins are the authoritative guest truth - + * svc_counter_learn re-checks them when a release decodes - + * but nothing re-checked them while counter_pa was already + * registered and no push had been decoded yet, so a publish + * would have advanced a page the client never polls. Re-check + * the rank's slot on every pass and re-register on a mismatch + * so the publish is proof-gated through the pin. + */ + u64 ipa = 0; + int iap = SVC_APERTURE_SYS; + + if (!svc_ipin_lookup(va, &ipa, &iap) && + iap == SVC_APERTURE_SYS && + (ipa & ~0x3ULL) != ch->counter_pa) { + unsigned long flags; + u64 old = ch->counter_pa; + int j; + + spin_lock_irqsave(&svc.chan_lock, flags); + ch->counter_pa = ipa & ~0x3ULL; + for (j = 0; j < SVC_MAX_CHANNELS; j++) { + if (svc.chan[j].valid && + svc.chan[j].h_object == ch->h_object) { + svc.chan[j].counter_pa = + ch->counter_pa; + break; + } + } + spin_unlock_irqrestore(&svc.chan_lock, flags); + pr_info("sema-chan: counter pin override h=0x%x rank=%u pa=0x%llx (was 0x%llx; injected pin is authoritative)\n", + ch->h_object, ch->gr_index, + ch->counter_pa, old); + } svc_counter_flush_pending(vgpu, ch); return; } - if (ch->gr_index >= n_gr) - return; - va = 0x20400fec0ULL + (u64)(n_gr - 1 - ch->gr_index) * 0x10ULL; /* * The injected-pin table is the authoritative source; only it may * shadow a self-discovered map entry (which can hold a stale @@ -11193,8 +11230,34 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) * remains. */ if (!ch->engine_type) { + u32 pput = 0; + svc_counter_autoreg(vgpu, ch, n_gr); svc_counter_flush_pending(vgpu, ch); + /* + * H3 (FORENSICS-v2): the end-of-pass publish below is + * unreachable on the passes that early-`continue` + * before it (USERD not resolvable, GP_PUT unreadable) + * even though the counter slot is registered and the + * client's tracker waits on it - v2 had pub_calls=0 + * with 20/20 slots registered and the GR ctx-init + * releases unread. Publish here, next to the safety + * net, from the registered USERD when it is readable. + * The value is a real GPFIFO put read from the + * channel's own control block, never a guess, and the + * target is the proof-gated slot registration + * (injected pin, or the read-back-validated derive of + * svc_counter_autoreg). Skip while a relay hold owns + * the channel, exactly like the end-of-pass call. + */ + if (ch->counter_pa && ch->userd_pa && + !(svc_fwd_hold.active && + svc_fwd_hold.vgpu == vgpu && + svc_fwd_hold.ch_handle == ch->h_object) && + !svc_read_ap(vgpu, + ch->userd_pa + SVC_USERD_GP_PUT, + ch->userd_as, &pput, sizeof(pput))) + svc_counter_publish(vgpu, ch, pput); } /* @@ -12385,8 +12448,15 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) * safety net at the top of the pass, so this must not * re-register: once per pass is enough and the derivation is * not free. + * + * H3 (FORENSICS-v2): GR channels publish in the safety-net + * block at the top of the pass - the early `continue`s above + * make this call unreachable exactly when the ring or USERD + * cannot be read, which is the case the publish must still + * cover. Keep this site for the CE channels only, so a GR + * pass does not publish twice. */ - if (!held) + if (!held && ch->engine_type) svc_counter_publish(vgpu, ch, put); spin_lock_irqsave(&svc.chan_lock, flags); |
