diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-08 05:57:26 -0500 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-08 05:57:26 -0500 |
| commit | 7c7d4c15a09556ff79c19c8d4890f25af43e531d (patch) | |
| tree | 24ec78f50b7b611e9c93b7120d513ec00d2f4b40 /kernel/gsp_display_engine.c | |
| parent | 5e6cd3cf5df1ab61208848866c30c29ce8907530 (diff) | |
| download | open-iov-7c7d4c15a09556ff79c19c8d4890f25af43e531d.tar.gz open-iov-7c7d4c15a09556ff79c19c8d4890f25af43e531d.zip | |
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.
Diffstat (limited to 'kernel/gsp_display_engine.c')
| -rw-r--r-- | kernel/gsp_display_engine.c | 165 |
1 files changed, 163 insertions, 2 deletions
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; @@ -339,20 +344,80 @@ static int gsp_disp_instmem_find(struct nv_vgpu_device *vgpu, u32 handle) } /* + * 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)); |
