diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/gsp_display_engine.c | 165 | ||||
| -rw-r--r-- | kernel/gsp_rm_emulation.c | 37 | ||||
| -rw-r--r-- | kernel/gsp_rm_tu104.h | 1 | ||||
| -rw-r--r-- | kernel/include/gsp_display_engine.h | 9 |
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); |
