summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
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);