summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_sema_service.c78
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);