summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-11-08 05:57:26 -0500
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-11-08 05:57:26 -0500
commit7c7d4c15a09556ff79c19c8d4890f25af43e531d (patch)
tree24ec78f50b7b611e9c93b7120d513ec00d2f4b40 /kernel
parent5e6cd3cf5df1ab61208848866c30c29ce8907530 (diff)
downloadopen-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')
-rw-r--r--kernel/gsp_display_engine.c165
-rw-r--r--kernel/gsp_rm_emulation.c37
-rw-r--r--kernel/gsp_rm_tu104.h1
-rw-r--r--kernel/include/gsp_display_engine.h9
4 files changed, 210 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));
diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c
index b05ee3d..c1b98f3 100644
--- a/kernel/gsp_rm_emulation.c
+++ b/kernel/gsp_rm_emulation.c
@@ -4453,6 +4453,43 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
break;
}
+ case TU104_CTRL_INTERNAL_DISPLAY_WRITE_INST_MEM: {
+ /*
+ * The kernel RM announces the display instance-memory block
+ * (8 KB context-DMA hash table + context-DMA entries) and
+ * its location. The host engine resolves the notifier
+ * context-DMA handle carried by SET_NOTIFIER_DMA through
+ * this block; without it the notifier PA stays unknown and
+ * the UPDATE FINISHED word is never written (r1627: UPDATE
+ * armed=1 handle=0x10013 -> pa=0x0).
+ *
+ * Params: instMemPhysAddr@0 (u64), instMemSize@8 (u64),
+ * instMemAddrSpace@16 (u32), instMemCpuCacheAttr@20 (u32).
+ */
+ const u8 *pp = req_data + GSP_RM_CTRL_HDR_SIZE;
+ u8 *rp = reply + GSP_RM_CTRL_HDR_SIZE;
+
+ pr_info("rm-ctl: display write inst mem size=%u pa=0x%llx space=%u\n",
+ req.paramsSize,
+ req.paramsSize >= 16 ? *(const u64 *)(pp + 0) : 0,
+ req.paramsSize >= 24 ? *(const u32 *)(pp + 16) : 0);
+ if (req.paramsSize >= 24 &&
+ req.paramsSize + GSP_RM_CTRL_HDR_SIZE <=
+ GSPRM_MAX_SINGLE_ELEM_DATA) {
+ u64 pa = 0, size = 0;
+ u32 space = 0;
+
+ memcpy(&pa, pp + 0, 8);
+ memcpy(&size, pp + 8, 8);
+ memcpy(&space, pp + 16, 4);
+ memcpy(rp, pp, req.paramsSize);
+ gsp_disp_engine_inst_mem(ctx->vgpu, pa, size, space);
+ }
+ out_status = 0;
+ out_params_size = req.paramsSize;
+ break;
+ }
+
case 0x801707: /* NV0080_CTRL_CMD_FIFO_GET_ENGINE_CONTEXT_PROPERTIES */
gsp_rm_handle_fifo_engine_ctx_props(ctx,
req_data + GSP_RM_CTRL_HDR_SIZE, req.paramsSize,
diff --git a/kernel/gsp_rm_tu104.h b/kernel/gsp_rm_tu104.h
index 033014b..815da3b 100644
--- a/kernel/gsp_rm_tu104.h
+++ b/kernel/gsp_rm_tu104.h
@@ -736,6 +736,7 @@ static const struct tu104_internal_device_info tu104_internal_device_table[] = {
#define TU104_CTRL_INTERNAL_DISPLAY_GET_STATIC_INFO 0x20800a01
#define TU104_CTRL_INTERNAL_DISPLAY_GET_IP_VERSION 0x20800a4b
#define TU104_CTRL_INTERNAL_DISPLAY_CHANNEL_PUSHBUFFER 0x20800a58
+#define TU104_CTRL_INTERNAL_DISPLAY_WRITE_INST_MEM 0x20800a49
#define TU104_CTRL_I2C_TRANSACTION 0x402c0105
#define TU104_CTRL_CE_GET_FAULT_METHOD_BUFFER_SIZE 0x20802a08
#define TU104_CTRL_CE_GET_PHYSICAL_CAPS 0x20802a07
diff --git a/kernel/include/gsp_display_engine.h b/kernel/include/gsp_display_engine.h
index bf2ee58..aa0ad98 100644
--- a/kernel/include/gsp_display_engine.h
+++ b/kernel/include/gsp_display_engine.h
@@ -57,6 +57,15 @@ void gsp_disp_engine_note_object(struct nv_vgpu_device *vgpu, u32 hclass,
/* Run one pass for a guest (called from the sema service thread). */
void gsp_disp_engine_pump(struct nv_vgpu_device *vgpu);
+/*
+ * Display instance memory location from
+ * NV2080_CTRL_CMD_INTERNAL_DISPLAY_WRITE_INST_MEM (0x20800a49):
+ * instMemPhysAddr / instMemSize / instMemAddrSpace. The notifier
+ * context-DMA handles resolve through this block's hash table.
+ */
+void gsp_disp_engine_inst_mem(struct nv_vgpu_device *vgpu, u64 pa, u64 size,
+ u32 addr_space);
+
/* Drop all display channels of one guest (mdev cleanup). */
void gsp_disp_engine_vgpu_cleanup(struct nv_vgpu_device *vgpu);