summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_rm_emulation.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gsp_rm_emulation.c')
-rw-r--r--kernel/gsp_rm_emulation.c519
1 files changed, 504 insertions, 15 deletions
diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c
index ec6ca20..03f861e 100644
--- a/kernel/gsp_rm_emulation.c
+++ b/kernel/gsp_rm_emulation.c
@@ -1913,7 +1913,20 @@ static void gsp_rm_handle_bus_get_pci_bar_info(struct gsp_emu_context *ctx,
* 36 bytes (observed paramsSize): feHwSysCap(0) windowPresentMask(4)
* bFbRemapperEnabled(8) numHeads(12) i2cPort(16) internalDispActiveMask(20)
* embeddedDisplayPortMask(24) muxSupported(28) numDispChannels(32).
- * Report a minimal headless display: 1 head, 1 (core) channel.
+ *
+ * r651: this reply is also the gate for the WINDOW-channel alloc path.
+ * Live gdb on the guest nvidia.ko pinned the display path 0x56 to
+ * kdispGetChannelNum_v03_00: for dispChnClass_Win it requires
+ * pStaticInfo->windowPresentMask & NVBIT32(instance) and returns
+ * NV_ERR_NOT_SUPPORTED (0x56) when the bit is clear; the Curs class reads
+ * HEAD_EXISTS bits from feHwSysCap the same way (0x49+instance channel num).
+ * The previous reply left both masks zero, so nvRmApiAlloc(0xc57e) died in
+ * the guest's local resource construct (clientAllocResource ->
+ * resservResourceFactory -> dispchnConstruct -> kdispGetChannelNum) before
+ * any GSP-RM RPC was issued. Report the same one-head / 8-window model the
+ * NV0073 shim and the caps notifier expose (feHwSysCap head-0 exists,
+ * windowPresentMask bits 0..7, numDispChannels covering the cursor channel
+ * numbers 73..80, i.e. 81 entries).
*/
static void gsp_rm_handle_display_get_static_info(struct gsp_emu_context *ctx,
u32 sequence,
@@ -1925,15 +1938,205 @@ static void gsp_rm_handle_display_get_static_info(struct gsp_emu_context *ctx,
memset(out_params, 0, 36);
+ /*
+ * feHwSysCap: NV_PDISP_FE_HW_SYS_CAP_HEAD_EXISTS(i) occupies bit i
+ * (dev_disp.h v03_00); head 0 exists on the emulated board.
+ */
+ val32 = 0x1;
+ memcpy(out_params + 0, &val32, 4);
+
+ /*
+ * windowPresentMask: bit i = window instance i present. Mirrors the
+ * caps notifier's SYS_CAPB=0xff and the NV0073 window assignment
+ * (windows 0/1 flexible) - eight usable windows.
+ */
+ val32 = 0xff;
+ memcpy(out_params + 4, &val32, 4);
+
val32 = 1; /* numHeads */
memcpy(out_params + 12, &val32, 4);
- val32 = 1; /* numDispChannels (core channel) */
+
+ /*
+ * numDispChannels sizes pClientChannelTable[], indexed by the packed
+ * channel number kdispGetChannelNum returns: CORE=0, WIN(i)=1+i,
+ * WINIM(i)=33+i, CURS(i)=73+i. Cover CURS(7)=80 -> 81 entries so any
+ * instance the guest allocates stays in bounds.
+ */
+ val32 = 81;
memcpy(out_params + 32, &val32, 4);
*out_params_size = 36;
*out_status = 0;
- nv_vgpu_info(ctx->vgpu, "vgsp-rm: DISPLAY_GET_STATIC_INFO -> heads=1 dispChannels=1");
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: DISPLAY_GET_STATIC_INFO -> heads=1 dispChannels=81 winMask=0xff feHwSysCap=0x1");
+}
+
+/*
+ * INTERNAL_DISPLAY_GET_ACTIVE_DISPLAY_DEVICES (0x20800a5d)
+ *
+ * dispswConstruct_IMPL (disp_sw.c) asks for {displayMask, numHeads} and
+ * rejects every logicalHeadId >= numHeads with "invalid logical head
+ * number: N" + NV_ERR_INSUFFICIENT_RESOURCES; the generic all-zero reply
+ * made numHeads = 0, so nvkms' DISPLAY_SW objects could not be constructed
+ * on any head (observed r653: heads 0..3 all rejected -> vkCreateDevice -3).
+ * Report the same one-head model as DISPLAY_GET_STATIC_INFO: display id 0
+ * active, one head.
+ *
+ * Params ABI (NV2080_CTRL_INTERNAL_DISPLAY_GET_ACTIVE_DISPLAY_DEVICES_PARAMS,
+ * 8 bytes): displayMask(0) numHeads(4).
+ */
+static void gsp_rm_handle_display_active_devices(struct gsp_emu_context *ctx,
+ u32 *out_status, u8 *out_params,
+ u32 *out_params_size)
+{
+ u32 displayMask = 0x1; /* display id 0 */
+ u32 numHeads = 1;
+
+ memcpy(out_params + 0, &displayMask, 4);
+ memcpy(out_params + 4, &numHeads, 4);
+
+ *out_params_size = 8;
+ *out_status = 0;
+
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: DISPLAY_GET_ACTIVE_DISPLAY_DEVICES -> displayMask=0x1 numHeads=1");
+}
+
+/*
+ * NVC370_CTRL_CMD_GET_CHANNEL_INFO (0xc3700104)
+ *
+ * nvRMSyncEvoChannel() (nvkms-rm.c) polls this control through the display
+ * object after every EVO channel kickoff and waits up to 2 s for the channel
+ * to report NVC370_CTRL_GET_CHANNEL_INFO_STATE_IDLE, otherwise it logs
+ * "Idling display engine timed out: <class>:<instance>:<sd>:<token>" and
+ * fails the whole modeset init (observed on the r652 window path: 0xc57d and
+ * 0xc57e instances 0/1). The emulated display engine has no pushbuffer
+ * consumer, so the state would never leave BUSY; report IDLE immediately.
+ *
+ * Params ABI (NVC370_CTRL_CMD_GET_CHANNEL_INFO_PARAMS, 20 bytes):
+ * subdeviceIndex(0) channelClass(4) channelInstance(8)
+ * IsChannelInDebugMode(12) channelState(16 = NVBIT(0) IDLE).
+ * Echo the request so class/instance round-trip and set the state word.
+ */
+static void gsp_rm_handle_display_channel_info(struct gsp_emu_context *ctx,
+ const u8 *in_params, u32 in_size,
+ u32 *out_status, u8 *out_params,
+ u32 *out_params_size)
+{
+ u32 idle = 1; /* NVC370_CTRL_GET_CHANNEL_INFO_STATE_IDLE */
+ u32 class_id = 0, instance = 0;
+ u32 size = in_size;
+
+ if (size > 64)
+ size = 64; /* defensive cap; the real params are 20 bytes */
+ if (size >= 20) {
+ memcpy(out_params, in_params, size);
+ memcpy(&class_id, in_params + 4, 4);
+ memcpy(&instance, in_params + 8, 4);
+ memcpy(out_params + 16, &idle, 4);
+ } else {
+ memset(out_params, 0, 20);
+ memcpy(out_params + 16, &idle, 4);
+ size = 20;
+ }
+
+ *out_params_size = size;
+ *out_status = 0;
+
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: GET_CHANNEL_INFO class=0x%x instance=%u -> IDLE",
+ class_id, instance);
+}
+
+/*
+ * NV0080_CTRL_CMD_FIFO_GET_ENGINE_CONTEXT_PROPERTIES (0x801707)
+ *
+ * The Vulkan/GL client queries the device for the size and alignment of an
+ * engine's context save region while building its GR channel context
+ * (observed: engineId 0x8 = GRAPHICS_ZCULL, one query at the end of every
+ * vkCreateDevice attempt). The generic all-zero reply handed back size = 0;
+ * the client's context-buffer allocation then failed (memdesc of size 0)
+ * and device creation aborted with VK_ERROR_INITIALIZATION_FAILED
+ * (observed r661: four create attempts, each ending at this control, then
+ * teardown and VKFAIL -3).
+ *
+ * The sizes below mirror the real 570.144 GSP-RM geometry captured on this
+ * TU104 (rm-call golden runs); entries marked "fill" were not covered by
+ * the capture and use nonzero page-aligned placeholders - revisit them when
+ * a real capture for the remaining engine ids exists.
+ *
+ * Params ABI (NV0080_CTRL_FIFO_GET_ENGINE_CONTEXT_PROPERTIES_PARAMS,
+ * 12 bytes): engineId(0, in) alignment(4, out) size(8, out).
+ */
+static const struct gsp_rm_engine_ctx_geom {
+ u32 engine_id;
+ u32 alignment;
+ u32 size;
+} gsp_rm_engine_ctx_geoms[] = {
+ { 0x00, 0x1000, 0xf2d00 }, /* GRAPHICS (captured) */
+ { 0x08, 0x1000, 0x10000 }, /* GRAPHICS_ZCULL (fill) */
+ { 0x09, 0x1000, 0xd100 }, /* GRAPHICS_PM (captured) */
+ { 0x0a, 0x1000, 0x1000 }, /* COMPUTE_PREEMPT (fill) */
+ { 0x0b, 0x1000, 0x1000 }, /* GRAPHICS_PREEMPT (fill) */
+ { 0x0c, 0x1000, 0x1000 }, /* GRAPHICS_SPILL (fill) */
+ { 0x0d, 0x100, 0x20000 }, /* GRAPHICS_PAGEPOOL (fill) */
+ { 0x0e, 0x100, 0x8000 }, /* GRAPHICS_BETACB (fill) */
+ { 0x0f, 0x100, 0x80000 }, /* GRAPHICS_RTV (fill) */
+ { 0x10, 0x1000, 0x5000 }, /* GRAPHICS_PATCH (captured) */
+ { 0x11, 0x100, 0x3000 }, /* GRAPHICS_BUNDLE_CB (captured) */
+ { 0x12, 0x100, 0x20000 }, /* GRAPHICS_PAGEPOOL_GLOBAL (captured) */
+ { 0x13, 0x1000, 0x7d7400 }, /* GRAPHICS_ATTRIBUTE_CB (captured) */
+ { 0x14, 0x100, 0x80000 }, /* GRAPHICS_RTV_CB_GLOBAL (captured) */
+ { 0x15, 0x100, 0x20000 }, /* GRAPHICS_GFXP_POOL (fill) */
+ { 0x16, 0x100, 0x1000 }, /* GRAPHICS_GFXP_CTRL_BLK (fill) */
+ { 0x17, 0x1000, 0x10000 }, /* GRAPHICS_FECS_EVENT (captured) */
+ { 0x18, 0x1000, 0x80000 }, /* GRAPHICS_PRIV_ACCESS_MAP (captured) */
+ { 0x19, 0x100, 0x1000 }, /* GRAPHICS_SETUP (fill) */
+};
+
+static void gsp_rm_handle_fifo_engine_ctx_props(struct gsp_emu_context *ctx,
+ const u8 *in_params, u32 in_size,
+ u32 *out_status, u8 *out_params,
+ u32 *out_params_size)
+{
+ u32 engine_id = ~0u;
+ u32 alignment = 0x1000, size = 0x1000;
+ u32 i;
+ bool found = false;
+
+ if (in_size >= 4)
+ memcpy(&engine_id, in_params + 0, 4);
+
+ for (i = 0; i < ARRAY_SIZE(gsp_rm_engine_ctx_geoms); i++) {
+ if (gsp_rm_engine_ctx_geoms[i].engine_id == engine_id) {
+ alignment = gsp_rm_engine_ctx_geoms[i].alignment;
+ size = gsp_rm_engine_ctx_geoms[i].size;
+ found = true;
+ break;
+ }
+ }
+
+ if (engine_id > 0x19) {
+ /* Same rejection the real RM's VF handler uses for ids
+ * outside the engine context properties range. */
+ *out_status = 0x56; /* NV_ERR_NOT_SUPPORTED */
+ *out_params_size = 0;
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: FIFO_GET_ENGINE_CONTEXT_PROPERTIES engineId=0x%x -> NOT_SUPPORTED",
+ engine_id);
+ return;
+ }
+
+ memcpy(out_params + 0, &engine_id, 4);
+ memcpy(out_params + 4, &alignment, 4);
+ memcpy(out_params + 8, &size, 4);
+ *out_params_size = 12;
+ *out_status = 0;
+
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: FIFO_GET_ENGINE_CONTEXT_PROPERTIES engineId=0x%x -> alignment=0x%x size=0x%x%s",
+ engine_id, alignment, size, found ? "" : " (default)");
}
/*
@@ -2462,6 +2665,20 @@ static void gsp_rm_handle_kgr_get_context_buffers_info(
* L2/LTC topology from here. Echo the requested indices and fill in the
* emulated TU104 values.
*/
+/*
+ * m113: FB_GET_INFO_V2 COMPRESSION_SIZE (index 0x01, bytes). The header:
+ * "A return value of 0 indicates the GPU does not support compression" -
+ * but the guest driver still attempts a COMPR-enabled, 64 KB-aligned
+ * comptag allocation (vendor VidHeap alloc, attr COMPR=2) and fails it
+ * NV_ERR_NO_MEMORY (0x51) when the comptag pool has no space, which
+ * libcuda maps to VK_ERROR_OUT_OF_DEVICE_MEMORY (the vkAllocateMemory -2
+ * gate). Report a comptag pool sized like the real Turing parts (~fb/16).
+ * 0 = the historic "no compression" reply.
+ */
+static uint gsp_rm_fb_compr_mb = 128;
+module_param_named(wall2_fb_compr_mb, gsp_rm_fb_compr_mb, uint, 0644);
+MODULE_PARM_DESC(wall2_fb_compr_mb, "FB_GET_INFO_V2 COMPRESSION_SIZE in MiB (default 128, 0 = none)");
+
static u32 tu104_fb_info_data(struct gsp_emu_context *ctx, u32 index)
{
u64 fb = nv_vgpu_fb_emul_size(ctx->vgpu);
@@ -2470,7 +2687,8 @@ static u32 tu104_fb_info_data(struct gsp_emu_context *ctx, u32 index)
case TU104_FB_INFO_IDX_TILE_REGION_COUNT:
return 0;
case TU104_FB_INFO_IDX_COMPRESSION_SIZE:
- return 0;
+ /* m113: non-zero comptag pool (bytes) - see gsp_rm_fb_compr_mb. */
+ return gsp_rm_fb_compr_mb << 20;
case TU104_FB_INFO_IDX_PARTITION_COUNT:
return 1;
case TU104_FB_INFO_IDX_BAR1_SIZE:
@@ -2956,11 +3174,22 @@ static void gsp_rm_handle_rm_alloc(struct gsp_emu_context *ctx,
gsp_rm_register_channel(ctx, ho, params, params_size);
/*
- * NV01_EVENT (allocated with class 0x79 in the guest ABI; params are
- * NV0005_ALLOC_PARAMETERS {hParentClient, hSrcResource, hClass,
- * notifyIndex, data}). The driver allocates one per channel and waits
- * for the GPU to post the event before it considers submitted work
- * complete - remember them so completed work can be signalled back.
+ * NV01_EVENT (params are NV0005_ALLOC_PARAMETERS {hParentClient,
+ * hSrcResource, hClass, notifyIndex, data}). The driver allocates one
+ * per channel and waits for the GPU to post the event before it
+ * considers submitted work complete - remember them so completed work
+ * can be signalled back.
+ *
+ * r682: the guest ABI uses THREE event classes and only the
+ * NV01_EVENT_OS_EVENT (0x79) shape was registered: libcuda's channel
+ * events arrive as 0x79, but the modern nvkms/Vulkan path allocates
+ * NV01_EVENT_KERNEL_CALLBACK_EX (0x7E, nvos.h) - observed live as
+ * `GSP_RM_ALLOC class=0x7e paramsSize=24 hParent=<subdevice>
+ * hObject=0xcaf00003` from the second RM client while its main
+ * thread sat in the vkCreateDevice wait loop (readiness word in a
+ * /dev/nvidiactl notification page, never signalled). Register 0x78
+ * (KERNEL_CALLBACK) and 0x7E alongside 0x79 so the completion
+ * wake-ups reach those events too.
*
* The table is append+retire: FREE retires records (valid=false) but
* never shrinks events_n, so long-lived boots with many client
@@ -2973,7 +3202,8 @@ static void gsp_rm_handle_rm_alloc(struct gsp_emu_context *ctx,
* first retired slot for a new registration, and log loudly when the
* table is genuinely full of live entries.
*/
- if (class_id == 0x79 && params_size >= 24) {
+ if ((class_id == 0x79 || class_id == 0x7e || class_id == 0x78) &&
+ params_size >= 24) {
u32 h_src = 0, notify_index = 0, hclass = 0;
u64 data64 = 0;
int hc_off = -1, i;
@@ -2991,16 +3221,17 @@ static void gsp_rm_handle_rm_alloc(struct gsp_emu_context *ctx,
rm->events_n, ho);
else {
/*
- * Locate hClass (0x79) inside the params instead of
- * trusting a fixed offset: NV0005_ALLOC_PARAMETERS is
- * {hParentClient, hSrcResource, hClass, notifyIndex,
- * data}, with hSrcResource immediately before hClass.
+ * Locate hClass (0x78/0x79/0x7e) inside the params
+ * instead of trusting a fixed offset:
+ * NV0005_ALLOC_PARAMETERS is {hParentClient,
+ * hSrcResource, hClass, notifyIndex, data}, with
+ * hSrcResource immediately before hClass.
*/
for (i = 0; i + 4 <= (int)params_size; i += 4) {
u32 v;
memcpy(&v, params + i, 4);
- if (v == 0x79) {
+ if (v == 0x79 || v == 0x7e || v == 0x78) {
hc_off = i;
break;
}
@@ -3237,6 +3468,95 @@ MODULE_PARM_DESC(gsp_rm_binapi07_fwd,
"Forward only the guest GL core's BINAPI 0x20810107 query to the live GSP session (default 0; diagnostic/experiment)");
/*
+ * session 75: reply words for the client-pending control 0x2080a097 (the
+ * libnvidia-eglcore engine-readiness poll). Runtime-tunable so one boot can
+ * sweep reply variants: the client reads +0x204, +0x488 and +0x48c back
+ * (file offsets 0xecd45f..0xecd475) before its engine loop re-issues the
+ * control. Defaults: flag=1 (the request sets it to 0), the other words 0.
+ */
+static uint gsp_rm_a097_flag = 1;
+module_param_named(wall2_a097_flag, gsp_rm_a097_flag, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_flag, "0x2080a097 reply word at params+0x204 (default 1)");
+static uint gsp_rm_a097_v488;
+module_param_named(wall2_a097_v488, gsp_rm_a097_v488, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_v488, "0x2080a097 reply word at params+0x488 (default 0)");
+static uint gsp_rm_a097_v48c;
+module_param_named(wall2_a097_v48c, gsp_rm_a097_v48c, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_v48c, "0x2080a097 reply word at params+0x48c (default 0)");
+
+/*
+ * session 76: the 0x2080a097 reply's RECORD area (params+4..+0x23). The
+ * real host RM handler overwrites up to four {u32, u32} mode pairs there
+ * (per-mode engine status + a x1000 clock/percent value); the client
+ * (count=1) copies back the first 16 bytes into its per-engine query
+ * buffer, and the second word of the first pair is consumed as the
+ * "engine progress" word (libnvidia-eglcore d5e3e0: *(inner+4) ->
+ * session+0xe104, compared against per-boot config thresholds).
+ * Each word defaults to 0xffffffff = leave the echoed request word
+ * untouched; any other value replaces it, so one boot can sweep the
+ * record matrix without rebuilding.
+ */
+static uint gsp_rm_a097_count = ~0u;
+module_param_named(wall2_a097_count, gsp_rm_a097_count, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_count, "0x2080a097 reply count word at params+0 (0xffffffff = echo)");
+static uint gsp_rm_a097_rec0 = ~0u, gsp_rm_a097_rec1 = ~0u;
+static uint gsp_rm_a097_rec2 = ~0u, gsp_rm_a097_rec3 = ~0u;
+static uint gsp_rm_a097_rec4 = ~0u, gsp_rm_a097_rec5 = ~0u;
+static uint gsp_rm_a097_rec6 = ~0u, gsp_rm_a097_rec7 = ~0u;
+module_param_named(wall2_a097_rec0, gsp_rm_a097_rec0, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_rec0, "0x2080a097 reply word at params+0x4 (0xffffffff = echo)");
+module_param_named(wall2_a097_rec1, gsp_rm_a097_rec1, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_rec1, "0x2080a097 reply word at params+0x8 (0xffffffff = echo)");
+module_param_named(wall2_a097_rec2, gsp_rm_a097_rec2, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_rec2, "0x2080a097 reply word at params+0xc (0xffffffff = echo)");
+module_param_named(wall2_a097_rec3, gsp_rm_a097_rec3, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_rec3, "0x2080a097 reply word at params+0x10 (0xffffffff = echo)");
+module_param_named(wall2_a097_rec4, gsp_rm_a097_rec4, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_rec4, "0x2080a097 reply word at params+0x14 (0xffffffff = echo)");
+module_param_named(wall2_a097_rec5, gsp_rm_a097_rec5, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_rec5, "0x2080a097 reply word at params+0x18 (0xffffffff = echo)");
+module_param_named(wall2_a097_rec6, gsp_rm_a097_rec6, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_rec6, "0x2080a097 reply word at params+0x1c (0xffffffff = echo)");
+module_param_named(wall2_a097_rec7, gsp_rm_a097_rec7, uint, 0644);
+MODULE_PARM_DESC(wall2_a097_rec7, "0x2080a097 reply word at params+0x20 (0xffffffff = echo)");
+
+/*
+ * m112b: the NV04_VID_HEAP_CONTROL enumeration pair 0x90960106/0x90960107
+ * (legacy NVOS32 VidHeap API, class 0x9096). libnvidia-eglcore (file offset
+ * 0x586d80) walks three heaps: per heap it queries 0x90960106 (12 bytes:
+ * {u32 firstIdx, u32 lastIdx}; the client then enumerates the heap's memory
+ * blocks by index via 0x90960107 (56 bytes) while firstIdx+1 <= idx <=
+ * lastIdx). The 56-byte block entry is read at +0x00/+0x08 (two u64s), +0x28
+ * (a u32 type compared against a table at eglcore 0x101b1c0) and +0x30 (a
+ * flag BYTE: when 0 the heap/block is skipped). The generic zero reply makes
+ * every heap look empty, the client concludes out-of-device-memory and
+ * vkAllocateMemory fails -2 WITHOUT any alloc reaching the emulation
+ * (dmesg-corroborated: only 0x9096* controls + a class 0xc5c0 alloc).
+ * Runtime-tunable so one boot can sweep reply variants (same pattern as the
+ * wall2_a097_* words).
+ */
+static uint gsp_rm_9096_lo; /* 0x90960106 firstIdx */
+static uint gsp_rm_9096_hi = 1; /* 0x90960106 lastIdx */
+module_param_named(wall2_9096_lo, gsp_rm_9096_lo, uint, 0644);
+MODULE_PARM_DESC(wall2_9096_lo, "0x90960106 reply firstIdx (default 0)");
+module_param_named(wall2_9096_hi, gsp_rm_9096_hi, uint, 0644);
+MODULE_PARM_DESC(wall2_9096_hi, "0x90960106 reply lastIdx (default 1)");
+static uint gsp_rm_9096_ent_addr; /* 0x90960107 entry +0x00 (u64) */
+module_param_named(wall2_9096_ent_addr, gsp_rm_9096_ent_addr, uint, 0644);
+MODULE_PARM_DESC(wall2_9096_ent_addr, "0x90960107 entry word at +0x00 (default 0)");
+static uint gsp_rm_9096_ent_size = 0x80000000u; /* entry +0x08 (u64) */
+module_param_named(wall2_9096_ent_size, gsp_rm_9096_ent_size, uint, 0644);
+MODULE_PARM_DESC(wall2_9096_ent_size, "0x90960107 entry word at +0x08 (default 0x80000000)");
+static uint gsp_rm_9096_ent_type = 3; /* entry +0x28 (u32) */
+module_param_named(wall2_9096_ent_type, gsp_rm_9096_ent_type, uint, 0644);
+MODULE_PARM_DESC(wall2_9096_ent_type, "0x90960107 entry word at +0x28 (default 3)");
+static uint gsp_rm_9096_ent_flag = 1; /* entry +0x30 (u8) */
+
+module_param_named(wall2_9096_ent_flag, gsp_rm_9096_ent_flag, uint, 0644);
+MODULE_PARM_DESC(wall2_9096_ent_flag, "0x90960107 entry flag byte at +0x30 (default 1)");
+
+
+/*
* The live session's oracle client/object for the 0x20810107 experiment,
* created on demand; 0 until the first query builds it. One device per module
* load, so file-statics are enough; reset by unload.
@@ -3777,6 +4097,26 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size);
break;
+ case 0xc3700104: /* NVC370_CTRL_CMD_GET_CHANNEL_INFO */
+ gsp_rm_handle_display_channel_info(ctx,
+ req_data + GSP_RM_CTRL_HDR_SIZE, req.paramsSize,
+ &out_status,
+ reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size);
+ break;
+
+ case 0x20800a5d: /* NV2080_CTRL_CMD_INTERNAL_DISPLAY_GET_ACTIVE_DISPLAY_DEVICES */
+ gsp_rm_handle_display_active_devices(ctx,
+ &out_status,
+ reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size);
+ 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,
+ &out_status,
+ reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size);
+ break;
+
case 0x730102: /* NV0073_CTRL_CMD_SYSTEM_GET_NUM_HEADS */
case 0x730287: /* NV0073_CTRL_CMD_SPECIFIC_GET_ALL_HEAD_MASK */
case 0x7302ad: /* NV0073_CTRL_CMD_SPECIFIC_GET_VALID_HEAD_WINDOW_ASSIGNMENT */
@@ -3992,6 +4332,113 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
break;
}
+ case 0x2080a097: { /* client-pending engine-context query (session 75)
+ *
+ * libnvidia-eglcore issues this via NVOS54 and then
+ * POLLS the reply's params words: the request sets
+ * +0x204 = 0, and the caller re-issues the whole
+ * control every ~1.8 s while its readiness check
+ * stays negative (observed: 32-36 polls against the
+ * generic all-zero reply, then the client gives up
+ * and vkCreateDevice returns -4). The params are an
+ * in/out record block: u32 count at +0, then count
+ * 16-byte records, then +0x204, and the client reads
+ * back +0, +0x204, +0x488 and +0x48c after the call
+ * (libnvidia-eglcore.so.570.144 file offsets
+ * 0xecd45f..0xecd475). The readiness wait itself
+ * lives in the client's engine loop
+ * (0xd6ef40..0xd6f0ab polling byte +0x1b038 of the
+ * engine object, which a91ea0 recomputes from many
+ * state fields), so the reply words below are
+ * runtime-tunable for a single-cycle sweep without
+ * rebuilding: the wall2_a097_* module parameters.
+ *
+ * Reply: verbatim echo (the request records are the
+ * client's own query data) with the tunable words
+ * applied. Defaults keep the completion-style flag. */
+ u8 *rp = reply + GSP_RM_CTRL_HDR_SIZE;
+ const u8 *pp = req_data + GSP_RM_CTRL_HDR_SIZE;
+
+ if (req.paramsSize >= 0x490 &&
+ req.paramsSize + GSP_RM_CTRL_HDR_SIZE <=
+ GSPRM_MAX_SINGLE_ELEM_DATA) {
+ u32 count = 0, flag;
+ static const u32 *const recp[8] = {
+ &gsp_rm_a097_rec0, &gsp_rm_a097_rec1,
+ &gsp_rm_a097_rec2, &gsp_rm_a097_rec3,
+ &gsp_rm_a097_rec4, &gsp_rm_a097_rec5,
+ &gsp_rm_a097_rec6, &gsp_rm_a097_rec7,
+ };
+ int i;
+
+ memcpy(rp, pp, req.paramsSize);
+ memcpy(&count, pp, 4);
+ memcpy(&flag, pp + 0x204, 4);
+ if (gsp_rm_a097_count != ~0u)
+ *(u32 *)(rp + 0) = gsp_rm_a097_count;
+ for (i = 0; i < 8; i++) {
+ if (*recp[i] != ~0u)
+ *(u32 *)(rp + 4 + i * 4) = *recp[i];
+ }
+ *(u32 *)(rp + 0x204) = gsp_rm_a097_flag;
+ *(u32 *)(rp + 0x488) = gsp_rm_a097_v488;
+ *(u32 *)(rp + 0x48c) = gsp_rm_a097_v48c;
+ if (gsp_sema_dbg)
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: ctrl 0x2080a097: echoed %u bytes (count=%u, req flag=%u) -> flag=%u v488=0x%x v48c=0x%x",
+ req.paramsSize, count, flag,
+ gsp_rm_a097_flag, gsp_rm_a097_v488,
+ gsp_rm_a097_v48c);
+ out_status = 0;
+ out_params_size = req.paramsSize;
+ } else {
+ out_status = 0;
+ out_params_size = 0;
+ }
+ break;
+ }
+
+ case 0x90960106: { /* NV04_VID_HEAP_CONTROL block-index range (m112b) */
+ u8 *rp = reply + GSP_RM_CTRL_HDR_SIZE;
+ const u8 *pp = req_data + GSP_RM_CTRL_HDR_SIZE;
+
+ if (req.paramsSize >= 12) {
+ memcpy(rp, pp, req.paramsSize);
+ *(u32 *)(rp + 0) = gsp_rm_9096_lo;
+ *(u32 *)(rp + 4) = gsp_rm_9096_hi;
+ if (gsp_sema_dbg)
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: ctrl 0x90960106 -> first=%u last=%u",
+ gsp_rm_9096_lo, gsp_rm_9096_hi);
+ }
+ out_status = 0;
+ out_params_size = req.paramsSize;
+ break;
+ }
+
+ case 0x90960107: { /* NV04_VID_HEAP_CONTROL block entry by index (m112b) */
+ u8 *rp = reply + GSP_RM_CTRL_HDR_SIZE;
+ const u8 *pp = req_data + GSP_RM_CTRL_HDR_SIZE;
+
+ if (req.paramsSize >= 56) {
+ memcpy(rp, pp, req.paramsSize);
+ *(u64 *)(rp + 0x00) = (u64)gsp_rm_9096_ent_addr;
+ *(u64 *)(rp + 0x08) = (u64)gsp_rm_9096_ent_size;
+ *(u32 *)(rp + 0x28) = gsp_rm_9096_ent_type;
+ *(u8 *)(rp + 0x30) = (u8)gsp_rm_9096_ent_flag;
+ if (gsp_sema_dbg)
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: ctrl 0x90960107 -> addr=0x%x size=0x%x type=%u flag=%u",
+ gsp_rm_9096_ent_addr,
+ gsp_rm_9096_ent_size,
+ gsp_rm_9096_ent_type,
+ gsp_rm_9096_ent_flag);
+ }
+ out_status = 0;
+ out_params_size = req.paramsSize;
+ break;
+ }
+
default:
/* Unknown control command - return success with zeroed params.
* Do NOT echo the request: pure queries pass uninitialized
@@ -4433,6 +4880,23 @@ static void gsp_rm_handle_element(struct gsp_emu_context *ctx, const u8 *elem)
* For vGPU, we just acknowledge success. */
nv_vgpu_info(ctx->vgpu, "vgsp-rm: RPC UPDATE_BAR_PDE(%u) seq=%u len=%u -> SUCCESS",
function, sequence, hdr->length);
+ {
+ const u8 *pl = (const u8 *)hdr + sizeof(*hdr);
+ u32 n = min_t(u32, hdr->length, 64);
+ char dump[256];
+ int o = 0;
+ u32 i;
+
+ for (i = 0; i < n; i += 4) {
+ u32 v = 0;
+
+ memcpy(&v, pl + i, 4);
+ o += snprintf(dump + o, sizeof(dump) - o,
+ "%08x ", v);
+ }
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: bar-pde payload: %s", dump);
+ }
if (gsp_rm_send(ctx, function, sequence, 0, NULL, 0))
nv_vgpu_err(ctx->vgpu, "vgsp-rm: UPDATE_BAR_PDE reply send failed");
else
@@ -4461,6 +4925,31 @@ static void gsp_rm_handle_element(struct gsp_emu_context *ctx, const u8 *elem)
nv_vgpu_info(ctx->vgpu, "vgsp-rm: RPC %s(%u) seq=%u len=%u -> generic SUCCESS reply (unknown #%llu)",
gsp_rm_rpc_name(function), function,
sequence, hdr->length, rm->unknown_rpcs);
+ /*
+ * Bounded payload dump for the first unknown RPCs: the vGPU
+ * guest maps its client VA spaces through some RPC; the
+ * mapping data (va pages / PAs / memdesc handles) must be
+ * visible here to be captured. Never echo it back (pure
+ * queries would eat their own stack garbage).
+ */
+ if (rm->unknown_rpcs <= 96 && hdr->length) {
+ const u8 *pl = (const u8 *)hdr + sizeof(*hdr);
+ u32 n = min_t(u32, hdr->length, 64);
+ char dump[256];
+ int o = 0;
+ u32 i;
+
+ for (i = 0; i < n; i += 4) {
+ u32 v = 0;
+
+ memcpy(&v, pl + i, 4);
+ o += snprintf(dump + o, sizeof(dump) - o,
+ "%08x ", v);
+ }
+ nv_vgpu_info(ctx->vgpu,
+ "vgsp-rm: unknown fn=%u seq=%u len=%u payload: %s",
+ function, sequence, hdr->length, dump);
+ }
if (gsp_rm_send(ctx, function, sequence, 0, NULL, 0))
nv_vgpu_err(ctx->vgpu, "vgsp-rm: reply send failed fn=%u", function);
else