summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_rm_emulation.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-22 16:00:07 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-22 16:00:07 -0400
commitfe2191aa24e766d6792964561868563690d36992 (patch)
tree4909629c2cdd5fbcc9fa7a95fb90faae8eba294d /kernel/gsp_rm_emulation.c
parentbdf1369d39d6767b4c857e7ddb8f8fdd87cb682e (diff)
downloadopen-iov-fe2191aa24e766d6792964561868563690d36992.tar.gz
open-iov-fe2191aa24e766d6792964561868563690d36992.zip
r489 the proper large-reply framing for GSP_RM_CONTROL: replies beyond the driver's pRpc->maxRpcSize (65080; pinned by the guest's own remainingSize 0x940 arithmetic for the 67448-byte PERF_VPSTATES_INFO reply) are now sent with the GSP continuation-record protocol - first frame length = maxRpcSize, then NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD frames of up to maxRpcSize-32 bytes each, exactly like the host GSP's _rpcWriteContinuationEntries. Replaces the r487 clamp (which produced a record the guest refused, and a size the guest then waited on fn-71 continuations for). Root: the GL VASpace construction and 3D engine selection die on this control's reply
Diffstat (limited to 'kernel/gsp_rm_emulation.c')
-rw-r--r--kernel/gsp_rm_emulation.c105
1 files changed, 78 insertions, 27 deletions
diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c
index 53270b8..30d5a8d 100644
--- a/kernel/gsp_rm_emulation.c
+++ b/kernel/gsp_rm_emulation.c
@@ -3048,6 +3048,65 @@ fallback:
return false;
}
+/*
+ * r489: a GSP_RM_CONTROL reply whose rpc record exceeds the driver's
+ * pRpc->maxRpcSize must be sent with the GSP's continuation-record protocol
+ * (nv gsp_rpc.c _rpcWriteContinuationEntries): the FIRST record carries
+ * exactly maxRpcSize bytes of [rpc header + ctrl header + params]; each
+ * following NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD record carries up to
+ * maxRpcSize - sizeof(hdr) bytes of the remainder. The driver's
+ * _issueRpcLarge consumes the first frame as min(bufSize, maxRpcSize) and
+ * then requires one continuation record per remaining chunk - without them
+ * it times out ("rpcRecvPoll timedout for fn 71 continuation record") and
+ * the control fails (NV_ERR_INVALID_PARAMETER), which is how the GL VASpace
+ * construction died on cmd 0x2080a0a4 (paramsSize 67392 > maxRpcSize).
+ *
+ * maxRpcSize is the guest driver's GSP_MSG_QUEUE_RPC_SIZE_MAX; the value is
+ * pinned by the driver's own arithmetic: for the 0x2080a0a4 reply
+ * (full_param_size = 32 + 24 + 67392 = 67448) it reported remainingSize
+ * 0x940 = 2368 after the first frame, i.e. maxRpcSize = 67448 - 2368 =
+ * 65080. A build whose constant differs shows up immediately as a
+ * mismatched continuation count in the guest log; adjust here.
+ */
+#define GSPRM_CRM_CONT_MAXRPC 65080u
+
+static int gsp_rm_send_rm_control(struct gsp_emu_context *ctx, u32 sequence,
+ const void *data, u32 data_len)
+{
+ u32 total = sizeof(struct gsprm_rpc_header) + data_len;
+ const u8 *p = data;
+ u32 off = 0;
+ int ret;
+
+ if (total <= GSPRM_CRM_CONT_MAXRPC)
+ return gsp_rm_send(ctx, GSPRM_RPC_FUNC_GSP_RM_CONTROL,
+ sequence, 0, data, data_len);
+
+ /* Frame 1: rpc.length = maxRpcSize. */
+ ret = gsp_rm_send(ctx, GSPRM_RPC_FUNC_GSP_RM_CONTROL, sequence, 0,
+ p, GSPRM_CRM_CONT_MAXRPC -
+ sizeof(struct gsprm_rpc_header));
+ if (ret)
+ return ret;
+ off = GSPRM_CRM_CONT_MAXRPC - sizeof(struct gsprm_rpc_header);
+
+ /* Continuations: rpc.length = hdr + chunk. */
+ while (off < data_len) {
+ u32 chunk = data_len - off;
+
+ if (chunk > GSPRM_CRM_CONT_MAXRPC -
+ sizeof(struct gsprm_rpc_header))
+ chunk = GSPRM_CRM_CONT_MAXRPC -
+ sizeof(struct gsprm_rpc_header);
+ ret = gsp_rm_send(ctx, GSPRM_RPC_FUNC_CONTINUATION_RECORD,
+ sequence, 0, p + off, chunk);
+ if (ret)
+ return ret;
+ off += chunk;
+ }
+ return 0;
+}
+
static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
const u8 *elem, u32 sequence)
{
@@ -3410,32 +3469,24 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
}
/*
- * r487 THE GL/VULKAN VASPACE WALL (Xorg/EGL/GZDOOM, every session to
- * date): the wire RPC record is bounded at 16 x 4096 bytes
- * (GSP_MSG_QUEUE_ELEMENT_SIZE_MIN * 16 = 65536; the guest's
- * GspMsgQueueReceiveStatus rejects any record whose
- * hdr + rpc.length exceeds it - observed "Incorrect message length
- * 67448", rpcRecvPoll status 0x3a, NV_ERR_INVALID_PARAMETER at
- * rpc.c:9998). The stock driver sends large in/out controls
- * (NV2080_CTRL_CMD_PERF_VPSTATES_INFO 0x2080a0a4 paramsSize=67392
- * during Xorg VASpace construction) and the generic reply echoed the
- * whole params buffer - a 17-element record the guest refused, so
- * the control failed, vmmCreateVaspace returned ffff and GL died
- * with "Failed to allocate VASpace object" / EGL_BAD_ALLOC. A real
- * GSP-RM cannot reply past the wire bound either; clamp the echoed
- * params to the largest record the queue carries
- * (65536 - guest elem hdr 8 - rpc hdr 32 - ctrl hdr 24), the tail
- * staying zero exactly like the small-control generic reply.
+ * r487/r489 THE GL/VULKAN VASPACE WALL (Xorg/EGL/GZDOOM, every
+ * session to date): the reply must follow the GSP's own large-reply
+ * framing. r487 clamped the echo to fit one 64 KB record; the guest
+ * then refused the record ("Incorrect message length 65528") and,
+ * when it fit, waited for CONTINUATION records that never came
+ * ("rpcRecvPoll timedout for fn 71 continuation record
+ * (remainingSize=0x940)") - rpc.c's _issueRpcLarge consumes the
+ * first frame up to pRpc->maxRpcSize (65080) and then requires
+ * NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD frames for the rest
+ * (the GSP's own _rpcWriteContinuationEntries). The size is left
+ * unclamped here; gsp_rm_send_rm_control() frames it per that
+ * protocol. Root evidence: cmd 0x2080a0a4 (PERF_VPSTATES_INFO,
+ * paramsSize 67392, FINN-serialized) sent during Xorg VASpace
+ * construction; its failure made vmmCreateVaspace return ffff and
+ * killed GL - and, after the length was fixed, the 3D-engine
+ * selection ("Failed to select a 3D engine") until the framing was
+ * correct.
*/
-#define GSPRM_WIRE_REPLY_PARAMS_MAX \
- (GSPRM_QUEUE_ELEMENT_SIZE * 16 - 512)
- if (out_params_size > GSPRM_WIRE_REPLY_PARAMS_MAX) {
- nv_vgpu_info(ctx->vgpu,
- "vgsp-rm: RM_CONTROL cmd=0x%x out=%u clamped to %u (wire record limit; guest would reject the record)",
- req.cmd, out_params_size,
- (u32)GSPRM_WIRE_REPLY_PARAMS_MAX);
- out_params_size = GSPRM_WIRE_REPLY_PARAMS_MAX;
- }
/* Build the reply header */
memcpy(reply + 0, &req.hClient, 4);
@@ -3456,8 +3507,8 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
out_status, dt_us);
}
- if (gsp_rm_send(ctx, GSPRM_RPC_FUNC_GSP_RM_CONTROL, sequence, 0,
- reply, GSP_RM_CTRL_HDR_SIZE + out_params_size))
+ if (gsp_rm_send_rm_control(ctx, sequence, reply,
+ GSP_RM_CTRL_HDR_SIZE + out_params_size))
nv_vgpu_err(ctx->vgpu, "vgsp-rm: RM_CONTROL reply send failed cmd=0x%x",
req.cmd);
else