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.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c
index d6aef9e..b0ad98f 100644
--- a/kernel/gsp_rm_emulation.c
+++ b/kernel/gsp_rm_emulation.c
@@ -10,12 +10,15 @@
#include <linux/slab.h>
#include <linux/vfio.h>
#include <linux/delay.h>
+#include <linux/string.h>
#include "nvidia-vgpu-vfio.h"
#include "gsp_emulation.h"
#include "gsp_rm_emulation.h"
#include "gsp_rm_tu104.h"
#include "gsp_sema_service.h"
+#include "gsp_phys_booter.h"
+#include "gsp_phys_gpu.h"
/* ============================================================
* Guest-memory access ("virtual DMA" on behalf of the device)
@@ -1167,6 +1170,64 @@ static void gsp_rm_build_static_info(u8 *buf)
memcpy(buf + TU104_GSCI_OFF_fwWprLayoutOffset + 8, &val64, 8);
}
+/*
+ * P2 static-config identity overlay: replace the emulated identity fields
+ * (GPU name, short name, VBIOS valid/subvendor/subdevice) with the live
+ * decode from the backend snapshot. FB geometry, masks, engine caps, PDE
+ * bases and handles stay emulated in this phase. The wait is bounded and
+ * sleeps; any miss leaves the emulated reply intact (fail-open).
+ */
+#define GSPRM_STATIC_LIVE_WAIT_MS 2000
+
+static void gsp_rm_apply_static_overlay(struct gsp_emu_context *ctx, u8 *buf)
+{
+ struct gsp_rm_emu *rm = ctx->rm_emu;
+ struct gsp_phys_backend_static_config snap;
+ u16 val16;
+ int ret;
+
+ if (!gsp_phys_static_live_enabled())
+ return;
+
+ ret = gsp_phys_backend_static_wait(&snap, GSPRM_STATIC_LIVE_WAIT_MS);
+ if (ret) {
+ rm->static_fallback++;
+ if (ret == -ENODATA)
+ rm->static_timeout++;
+ return;
+ }
+
+ /* Wire slots: name 64 B at 1260, short name 64 B at 1324. An empty
+ * live string never clears the emulated one. */
+ if (snap.gpu_name[0])
+ strscpy((char *)(buf + TU104_GSCI_OFF_gpuNameString),
+ snap.gpu_name,
+ TU104_GSCI_OFF_gpuShortNameString -
+ TU104_GSCI_OFF_gpuNameString);
+ if (snap.gpu_short_name[0])
+ strscpy((char *)(buf + TU104_GSCI_OFF_gpuShortNameString),
+ snap.gpu_short_name,
+ TU104_GSCI_OFF_gpuNameString_Unicode -
+ TU104_GSCI_OFF_gpuShortNameString);
+
+ /* Only overlay the VBIOS identity when the live decode carries a valid
+ * VBIOS; never clear an emulated valid flag. */
+ if (snap.b_vbios_valid) {
+ buf[TU104_GSCI_OFF_bVbiosValid] = 1;
+ val16 = (u16)snap.vbios_sub_vendor;
+ memcpy(buf + TU104_GSCI_OFF_vbiosSubVendor, &val16, sizeof(val16));
+ val16 = (u16)snap.vbios_sub_device;
+ memcpy(buf + TU104_GSCI_OFF_vbiosSubDevice, &val16, sizeof(val16));
+ }
+
+ rm->static_live++;
+ nv_vgpu_info(ctx->vgpu,
+ "GSPRM: static-info overlay applied (generation=%u): name='%s' short='%s' vbiosValid=%u subVendor=0x%04x subDevice=0x%04x",
+ snap.generation, snap.gpu_name, snap.gpu_short_name,
+ snap.b_vbios_valid, (u16)snap.vbios_sub_vendor,
+ (u16)snap.vbios_sub_device);
+}
+
static void gsp_rm_handle_get_static_info(struct gsp_emu_context *ctx,
u32 sequence)
{
@@ -1180,6 +1241,7 @@ static void gsp_rm_handle_get_static_info(struct gsp_emu_context *ctx,
}
gsp_rm_build_static_info(reply);
+ gsp_rm_apply_static_overlay(ctx, reply);
nv_vgpu_info(ctx->vgpu, "GSPRM: GET_GSP_STATIC_INFO seq=%u -> %u bytes (fb=%lluMB, engines=0x%x/0x%x)",
sequence, TU104_GSCI_SIZE,
@@ -2608,6 +2670,154 @@ gsp_rm_handle_promote_fault_method_buffers(struct gsp_emu_context *ctx,
}
}
+/*
+ * Bounded logging for the P3 forward counters: the first four fallbacks (so a
+ * session that cannot forward is visible) and every 64th event. Never called
+ * when the parameter is off.
+ */
+static void gsp_rm_ctrl_forward_note(struct gsp_emu_context *ctx)
+{
+ struct gsp_rm_emu *rm = ctx->rm_emu;
+ u64 events = rm->ctrl_fwd_ok + rm->ctrl_fwd_fallback;
+
+ if ((rm->ctrl_fwd_fallback && rm->ctrl_fwd_fallback <= 4) ||
+ (events && (events % 64) == 0))
+ nv_vgpu_info(ctx->vgpu,
+ "GSPRM: RM_CONTROL forward: ok=%llu fallback=%llu timeout=%llu",
+ rm->ctrl_fwd_ok, rm->ctrl_fwd_fallback,
+ rm->ctrl_fwd_timeout);
+}
+
+/*
+ * P3 read-only control forwarding (gsp_phys_ctrl_forward=1; default off).
+ *
+ * A small whitelist of side-effect-free queries is sent to the live backend
+ * session with the guest's emulated internal handles translated to the
+ * snapshot's adopted server handles; the server's echoed params become the
+ * guest reply. Everything else - the vGPU-specific controls, anything
+ * mutating, unknown IDs, an unreadable/absent snapshot, unmappable handles,
+ * a multi-element request, a backend error/timeout or a malformed reply -
+ * returns false and the emulated per-command handler runs unchanged
+ * (fail-open).
+ *
+ * gsp_phys_backend_rpc() returns with gsp_phys_rm_lock released and the reply
+ * in a kmalloc'd copy; the status queue is only written afterwards by
+ * gsp_rm_send(), never while a backend wait is pending (doc 11 section 6.5).
+ */
+static bool gsp_rm_ctrl_forward(struct gsp_emu_context *ctx,
+ const struct gsp_rm_control_req *req,
+ const u8 *req_params, u8 *reply,
+ u32 reply_data_len, u32 sequence)
+{
+ struct gsp_rm_emu *rm = ctx->rm_emu;
+ struct gsp_phys_backend_static_config snap;
+ struct gsp_rm_control_req fwd_hdr;
+ u8 *fwd_req;
+ void *rsp = NULL;
+ u32 rsp_len = 0;
+ u32 out_status;
+ u32 out_params_size;
+ int ret;
+
+ /* Wave 1 whitelist: compile-time side-effect-free queries only. */
+ switch (req->cmd) {
+ case TU104_CTRL_BUS_GET_PCI_BAR_INFO:
+ case TU104_CTRL_BUS_GET_INFO_V2:
+ case TU104_CTRL_FB_GET_INFO_V2:
+ case TU104_CTRL_INTERNAL_GMMU_GET_STATIC_INFO:
+ break;
+ default:
+ return false;
+ }
+
+ /* The capture hands us only the first queue element, so a request that
+ * does not fit one element (paramsSize+24 <= GSPRM_MAX_SINGLE_ELEM_DATA)
+ * cannot be reassembled and stays emulated. */
+ if (req->paramsSize > GSPRM_MAX_SINGLE_ELEM_DATA - GSP_RM_CTRL_HDR_SIZE)
+ goto fallback;
+
+ /* A valid snapshot implies a READY session and the adopted handles. */
+ if (gsp_phys_backend_static_get(&snap))
+ goto fallback;
+
+ /* Translate the guest's emulated internal handles; any other handle
+ * domain stays emulated. */
+ if (req->hClient != TU104_H_INTERNAL_CLIENT ||
+ (req->hObject != TU104_H_INTERNAL_DEVICE &&
+ req->hObject != TU104_H_INTERNAL_SUBDEVICE))
+ goto fallback;
+
+ fwd_req = kmalloc(GSP_RM_CTRL_HDR_SIZE + req->paramsSize, GFP_KERNEL);
+ if (!fwd_req)
+ goto fallback;
+ memset(&fwd_hdr, 0, sizeof(fwd_hdr));
+ fwd_hdr.hClient = snap.h_internal_client;
+ fwd_hdr.hObject = req->hObject == TU104_H_INTERNAL_DEVICE ?
+ snap.h_internal_device : snap.h_internal_subdevice;
+ fwd_hdr.cmd = req->cmd;
+ fwd_hdr.paramsSize = req->paramsSize;
+ fwd_hdr.flags = req->flags;
+ memcpy(fwd_req, &fwd_hdr, GSP_RM_CTRL_HDR_SIZE);
+ if (req->paramsSize)
+ memcpy(fwd_req + GSP_RM_CTRL_HDR_SIZE, req_params,
+ req->paramsSize);
+
+ ret = gsp_phys_backend_rpc(GSPRM_RPC_FUNC_GSP_RM_CONTROL, fwd_req,
+ GSP_RM_CTRL_HDR_SIZE + req->paramsSize,
+ &rsp, &rsp_len, 500);
+ kfree(fwd_req);
+ if (ret) {
+ if (ret == -ETIMEDOUT)
+ rm->ctrl_fwd_timeout++;
+ goto fallback;
+ }
+
+ /*
+ * The reply is rpc_gsp_rm_control_v03_00: status at +12, paramsSize
+ * at +16, echoed params at +24 - the same 24-byte prefix the emulated
+ * handler builds below. The RPC-level rpc_result was already checked
+ * by gsp_phys_backend_rpc().
+ */
+ if (!rsp || rsp_len < GSP_RM_CTRL_HDR_SIZE)
+ goto fallback;
+ memcpy(&out_status, (u8 *)rsp + 12, 4);
+ memcpy(&out_params_size, (u8 *)rsp + 16, 4);
+ if (out_status != 0 ||
+ out_params_size > rsp_len - GSP_RM_CTRL_HDR_SIZE ||
+ out_params_size > reply_data_len - GSP_RM_CTRL_HDR_SIZE)
+ goto fallback;
+
+ /* Build the guest reply exactly as the emulated handler does: the
+ * guest's own handles/cmd and the server's status/params. */
+ memcpy(reply + 0, &req->hClient, 4);
+ memcpy(reply + 4, &req->hObject, 4);
+ memcpy(reply + 8, &req->cmd, 4);
+ memcpy(reply + 12, &out_status, 4);
+ memcpy(reply + 16, &out_params_size, 4);
+ memcpy(reply + 20, &req->flags, 4);
+ if (out_params_size)
+ memcpy(reply + GSP_RM_CTRL_HDR_SIZE,
+ (u8 *)rsp + GSP_RM_CTRL_HDR_SIZE, out_params_size);
+ kfree(rsp);
+
+ if (gsp_rm_send(ctx, GSPRM_RPC_FUNC_GSP_RM_CONTROL, sequence, 0,
+ reply, GSP_RM_CTRL_HDR_SIZE + out_params_size)) {
+ nv_vgpu_err(ctx->vgpu, "GSPRM: RM_CONTROL forward reply send failed cmd=0x%x",
+ req->cmd);
+ rm->ctrl_fwd_fallback++;
+ } else {
+ rm->ctrl_fwd_ok++;
+ }
+ gsp_rm_ctrl_forward_note(ctx);
+ return true;
+
+fallback:
+ kfree(rsp);
+ rm->ctrl_fwd_fallback++;
+ gsp_rm_ctrl_forward_note(ctx);
+ return false;
+}
+
static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
const u8 *elem, u32 sequence)
{
@@ -2636,6 +2846,19 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
return;
}
+ /*
+ * P3: when enabled, forward a whitelist of read-only queries to the
+ * live backend. The helper builds and sends the reply itself; a false
+ * return means nothing was sent and the emulated switch below runs
+ * byte-identically to the parameter-off path.
+ */
+ if (gsp_phys_ctrl_forward_enabled() &&
+ gsp_rm_ctrl_forward(ctx, &req, req_data + GSP_RM_CTRL_HDR_SIZE,
+ reply, reply_data_len, sequence)) {
+ kfree(reply);
+ return;
+ }
+
switch (req.cmd) {
case TU104_CTRL_FIFO_GET_DEVICE_INFO_TABLE:
gsp_rm_handle_fifo_device_table(ctx, sequence, &req,