diff options
| -rw-r--r-- | kernel/gsp_sema_service.c | 43 |
1 files changed, 33 insertions, 10 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 309210d..4cc1624 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -7728,19 +7728,42 @@ static int svc_chan_userd(struct nv_vgpu_device *vgpu, struct svc_channel *ch, * An idle (0/0) read is accepted here because the guest writes the * block on the channel's first push; only unreadable or incoherent * candidates fall through to the old list. + * + * The ring itself must validate first (entry0 = a CE push VA + len, + * same gate as svc_ring_autopin): r41 showed svc_lookup can answer a + * fresh gpfifo VA with an unrelated FB offset (rank4 -> 0x90000), and + * an idle acceptance on that bogus base cemented the wrong USERD. */ if (ring_pa && ch->engine_type != 0) { - u32 p = 0, g = 0; - int rc = svc_userd_probe(vgpu, ring_pa + 0x10000, ring_ap, - ch->gpfifo_entries, &p, &g); + u8 e[SVC_GPFIFO_ENTRY_SIZE]; + u32 e0, e1, len; + u64 va; + bool ring_ok = false; - if (rc == 0 || rc == -EAGAIN) { - ch->userd_pa = ring_pa + 0x10000; - ch->userd_as = ring_ap; - ch->userd_ok = true; - *put = p; - *get = g; - return 0; + if (!svc_read_ap(vgpu, ring_pa, ring_ap, e, sizeof(e))) { + memcpy(&e0, e, 4); + memcpy(&e1, e + 4, 4); + va = ((u64)(e1 & 0xff) << 32) | + (u64)(e0 & 0xfffffffc); + len = ((e1 >> 10) & 0x1fffff) << 2; + ring_ok = !(va & 0xfffff) && + va >= 0x120000000ULL && va < 0x121000000ULL && + len >= 4 && len <= 0x10000; + } + if (ring_ok) { + u32 p = 0, g = 0; + int rc = svc_userd_probe(vgpu, ring_pa + 0x10000, + ring_ap, ch->gpfifo_entries, + &p, &g); + + if (rc == 0 || rc == -EAGAIN) { + ch->userd_pa = ring_pa + 0x10000; + ch->userd_as = ring_ap; + ch->userd_ok = true; + *put = p; + *get = g; + return 0; + } } } |
