From 7c7d4c15a09556ff79c19c8d4890f25af43e531d Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Sun, 8 Nov 2026 05:57:26 -0500 Subject: vgsp-disp: resolve the notifier via the announced display instance memory The guest announces the display instance-memory block (8 KB context-DMA hash table + entries, vidmem PA 0x1fff0000 on r1627) via NV2080_CTRL_CMD_INTERNAL_DISPLAY_WRITE_INST_MEM (0x20800a49), but the engine ignored it and scanned its FB shadow blindly, never finding the hash entry - so SET_NOTIFIER_DMA handle 0x10013 resolved to pa=0 and UPDATE never wrote FINISHED (r1627: UPDATE armed=1 notifier=0x0/0). Handle 0xa49, record pa/size/address space per guest, read the block through the FB shadow (or guest RAM for sysmem), and parse the hash table from the announced location with bounded diagnostics. --- kernel/gsp_display_engine.c | 165 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 2 deletions(-) (limited to 'kernel/gsp_display_engine.c') diff --git a/kernel/gsp_display_engine.c b/kernel/gsp_display_engine.c index 73f40c6..811def9 100644 --- a/kernel/gsp_display_engine.c +++ b/kernel/gsp_display_engine.c @@ -155,13 +155,18 @@ static struct { struct gsp_disp_chan ch[GSP_DISP_MAX_CHANNELS]; struct gsp_disp_note note[GSP_DISP_MAX_NOTES]; u8 *pbuf; /* 4 KB push-buffer bounce (service thread only) */ + u8 *ibuf; /* 64 KB display instance-memory bounce */ unsigned long boot_jiffies; u32 log_budget; u32 push_dumps; /* bounded raw push dumps (disp_dbg) */ + u32 im_logs; /* bounded instance-memory diagnostics */ /* Cached display-instance-memory base per guest (<= 2 guests). */ struct { struct nv_vgpu_device *vgpu; u64 base; + u64 pa; /* instMemPhysAddr from 0x20800a49 */ + u64 size; + u32 addr_space; int found; /* 1 found, 0 not yet tried, -1 given up */ } im[2]; } disp; @@ -338,21 +343,81 @@ static int gsp_disp_instmem_find(struct nv_vgpu_device *vgpu, u32 handle) return -ENOENT; } +/* + * Read from the display instance memory: ADDR_FBMEM (2) lives in the + * emulated BAR1 shadow; ADDR_SYSMEM (1) is guest physical memory. + */ +static int gsp_disp_im_read(struct nv_vgpu_device *vgpu, u64 pa, + u32 addr_space, u64 off, void *buf, size_t len) +{ + u64 fb_size = nv_vgpu_fb_emul_size(vgpu); + u8 *fb = vgpu->bars[1].vaddr; + + if (addr_space == 2) { /* ADDR_FBMEM */ + if (!fb || pa + off + len > fb_size) + return -ENOENT; + memcpy(buf, fb + pa + off, len); + return 0; + } + return vfio_dma_rw(&vgpu->vfio_dev, pa + off, buf, len, false); +} + +/* + * NV2080_CTRL_CMD_INTERNAL_DISPLAY_WRITE_INST_MEM (0x20800a49) announced the + * display instance memory location. The notifier context-DMA handles resolve + * through this block's 8 KB hash table, so record it per guest. + */ +void gsp_disp_engine_inst_mem(struct nv_vgpu_device *vgpu, u64 pa, u64 size, + u32 addr_space) +{ + unsigned long flags; + int i, slot = -1; + + if (!gsp_disp_engine || !vgpu || !pa || !size) + return; + spin_lock_irqsave(&disp.lock, flags); + for (i = 0; i < ARRAY_SIZE(disp.im); i++) { + if (disp.im[i].vgpu == vgpu) { + slot = i; + break; + } + if (!disp.im[i].vgpu && slot < 0) + slot = i; + } + if (slot >= 0) { + disp.im[slot].vgpu = vgpu; + disp.im[slot].pa = pa; + disp.im[slot].size = size; + disp.im[slot].addr_space = addr_space; + disp.im[slot].base = 0; + disp.im[slot].found = 0; + } + spin_unlock_irqrestore(&disp.lock, flags); + pr_info("inst mem: pa=0x%llx size=0x%llx addrSpace=%u\n", pa, size, + addr_space); +} + /* * Resolve a context-DMA handle to its surface guest PA using the display - * instance hash table. Returns 0 on success and fills *pa, *limit (bytes-1). + * instance hash table. Prefers the block announced by 0x20800a49; falls + * back to the legacy whole-FB scan when that control was not seen. + * Returns 0 on success and fills *pa, *limit (bytes-1). */ static int gsp_disp_ctxdma_pa(struct nv_vgpu_device *vgpu, u32 handle, u64 *pa, u32 *limit) { u64 fb_size = nv_vgpu_fb_emul_size(vgpu); u8 *fb = vgpu->bars[1].vaddr; + unsigned long flags; + u64 im_pa = 0; + u32 im_space = 0; + bool have_im = false; u64 base = 0; bool found = false; int slot = -1; int i; - if (!fb || !handle) + if (!handle) return -ENODEV; if (gsp_disp_notifier_pa) { *pa = gsp_disp_notifier_pa; @@ -360,6 +425,92 @@ static int gsp_disp_ctxdma_pa(struct nv_vgpu_device *vgpu, u32 handle, return 0; } + spin_lock_irqsave(&disp.lock, flags); + for (i = 0; i < ARRAY_SIZE(disp.im); i++) { + if (disp.im[i].vgpu == vgpu && disp.im[i].pa && + disp.im[i].size) { + im_pa = disp.im[i].pa; + im_space = disp.im[i].addr_space; + have_im = true; + break; + } + } + spin_unlock_irqrestore(&disp.lock, flags); + + if (have_im) { + u64 e; + + if (!disp.ibuf) + return -ENOMEM; + if (gsp_disp_im_read(vgpu, im_pa, im_space, 0, disp.ibuf, + GSP_DISP_INST_OBJ_END)) + return -ENOENT; + + if (gsp_disp_dbg && disp.im_logs < 8) { + u32 nz = 0, shown = 0; + + for (e = 0; e < GSP_DISP_INST_HASH_END; e += 8) { + u32 a = *(u32 *)(disp.ibuf + e); + u32 b = *(u32 *)(disp.ibuf + e + 4); + + if (!a && !b) + continue; + nz++; + if (shown < 6) { + pr_info("im hash[0x%llx] = %08x %08x\n", + e, a, b); + shown++; + } + } + disp.im_logs++; + pr_info("inst mem pa=0x%llx space=%u handle=0x%x hash_nz=%u\n", + im_pa, im_space, handle, nz); + } + + for (e = 0; e < GSP_DISP_INST_HASH_END; e += 8) { + u32 obj = *(u32 *)(disp.ibuf + e); + u32 ctx = *(u32 *)(disp.ibuf + e + 4); + u32 inst = (ctx >> 14) & 0x3ff; + u32 chn = (ctx >> 25) & 0x7f; + u64 ent; + u32 w0, w1, w2, w3, w4; + + if (obj != handle) + continue; + ent = (u64)inst * GSP_DISP_INST_ENTRY_SIZE; + if (inst < (GSP_DISP_INST_OBJ_OFF / 0x20) || + inst >= (GSP_DISP_INST_OBJ_END / 0x20) || + ent + GSP_DISP_INST_ENTRY_SIZE > + GSP_DISP_INST_OBJ_END) + continue; + w0 = *(u32 *)(disp.ibuf + ent + 0); + w1 = *(u32 *)(disp.ibuf + ent + 4); + w2 = *(u32 *)(disp.ibuf + ent + 8); + w3 = *(u32 *)(disp.ibuf + ent + 12); + w4 = *(u32 *)(disp.ibuf + ent + 16); + if (gsp_disp_dbg && disp.im_logs < 8) { + disp.im_logs++; + pr_info("im handle 0x%x ctx=0x%x inst=%u chn=%u w0=0x%x w1=0x%x w2=0x%x w3=0x%x w4=0x%x\n", + handle, ctx, inst, chn, w0, w1, w2, + w3, w4); + } + if ((w0 & 0x3) < 1 || (w0 & 0x3) > 3 || + !(w0 & BIT(2))) + continue; + *pa = ((((u64)(w2 & 0x7f)) << 32) | w1) << 8; + *limit = (u32)(((((u64)(w4 & 0x7f)) << 32) | w3) << 8) + | 0xfff; + found = true; + break; + } + if (!found) + return -ENOENT; + return 0; + } + + /* Legacy: locate the block by scanning the FB shadow. */ + if (!fb) + return -ENODEV; for (i = 0; i < ARRAY_SIZE(disp.im); i++) { if (disp.im[i].vgpu == vgpu) { slot = i; @@ -942,8 +1093,16 @@ int gsp_disp_engine_init(void) disp.pbuf = kmalloc(4096, GFP_KERNEL); if (!disp.pbuf) return -ENOMEM; + disp.ibuf = kmalloc(GSP_DISP_INST_OBJ_END, GFP_KERNEL); + if (!disp.ibuf) { + kfree(disp.pbuf); + disp.pbuf = NULL; + return -ENOMEM; + } disp.boot_jiffies = jiffies; disp.log_budget = 0; + disp.push_dumps = 0; + disp.im_logs = 0; pr_info("display engine emulation %s\n", gsp_disp_engine ? "enabled" : "disabled"); return 0; @@ -953,6 +1112,8 @@ void gsp_disp_engine_fini(void) { kfree(disp.pbuf); disp.pbuf = NULL; + kfree(disp.ibuf); + disp.ibuf = NULL; memset(disp.ch, 0, sizeof(disp.ch)); memset(disp.note, 0, sizeof(disp.note)); memset(disp.im, 0, sizeof(disp.im)); -- cgit v1.2.3