summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_rm_emulation.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-22 02:16:18 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-22 02:16:18 -0400
commit5eac589d9a7af8f8b321b00398575070b9a88513 (patch)
treeb0c6d12d92a145fcf2d6fc32f3ee2b794eed4c43 /kernel/gsp_rm_emulation.c
parent427b9e1e10c3258bb031be142700b18c83296c69 (diff)
downloadopen-iov-5eac589d9a7af8f8b321b00398575070b9a88513.tar.gz
open-iov-5eac589d9a7af8f8b321b00398575070b9a88513.zip
kernel: floorsweeping masks + interrupt tree + WPR2 race + fn-10 handler
- STATIC_KGR_GET_FLOORSWEEPING_MASKS(0x20800a26): TU104 gpcMask=0x3f (6 GPCs), 4 TPC/GPC. numGpcs==0 killed krcWatchdogInit -> RmInitAdapter bailed ('numGpcs > 0' assert at kernel_graphics_object.c:191). - CPU interrupt tree (osVerifyInterrupts): driver accesses VF window at virtualRegPhysOffset=NV_VIRTUAL_FUNCTION_FULL_PHYS_OFFSET(0xB80000)+off, NOT 0x1640 (was watching wrong offset). On LEAF_TRIGGER write, set LEAF/TOP pending bits + inject MSI-X so the sanity-test ISR runs - osVerifySystemEnvironment now passes. - WPR2 race: clear boot state BEFORE CPUCTL=HALTED on Booter-unload kick so WPR2 is down when driver checks kgspIsWpr2Up ('WPR2 is still up'). - Dedicated GSP_RM_FREE(10) handler: echo NVOS00_PARAMETERS + status=NV_OK (proper free_v03_00 form). fn-10 timeout persists despite this - characterized as driver-side reply-matching issue, not content.
Diffstat (limited to 'kernel/gsp_rm_emulation.c')
-rw-r--r--kernel/gsp_rm_emulation.c134
1 files changed, 114 insertions, 20 deletions
diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c
index 1075bee..24bd8fe 100644
--- a/kernel/gsp_rm_emulation.c
+++ b/kernel/gsp_rm_emulation.c
@@ -1159,6 +1159,47 @@ static void gsp_rm_handle_kgr_get_info(struct gsp_emu_context *ctx,
TU104_MAX_SUBCONTEXT_COUNT);
}
+/*
+ * STATIC_KGR_GET_FLOORSPEEPING_MASKS (0x20800a26)
+ *
+ * Driver computes numGpcs from floorsweepingMasks.gpcMask; zero kills
+ * krcWatchdogInit ("numGpcs > 0" assert -> RmInitAdapter bails). Fill
+ * TU104's present-GPC/TPC masks: 6 GPCs (bits 0-5), 4 TPCs per GPC.
+ */
+static void gsp_rm_handle_kgr_get_floorsweeping_masks(
+ struct gsp_emu_context *ctx,
+ u32 sequence,
+ u32 *out_status,
+ u8 *out_params,
+ u32 *out_params_size)
+{
+ struct tu104_gr_floorsweeping_masks *m =
+ (struct tu104_gr_floorsweeping_masks *)out_params; /* engine 0 */
+ u32 g;
+
+ memset(out_params, 0, TU104_GR_INFO_MAX_ENGINES *
+ sizeof(struct tu104_gr_floorsweeping_masks));
+
+ m->gpcMask = (1U << TU104_NUM_GPC) - 1; /* bits 0-5 = 6 GPCs */
+ m->physGpcMask = m->gpcMask;
+ m->physGfxGpcMask = m->gpcMask;
+ m->numGfxTpc = TU104_NUM_TPC;
+ for (g = 0; g < TU104_NUM_GPC; g++) {
+ m->tpcMask[g] = (1U << TU104_TPC_PER_GPC) - 1; /* 4 TPCs */
+ m->tpcCount[g] = TU104_TPC_PER_GPC;
+ m->mmuPerGpc[g] = 1;
+ m->numPesPerGpc[g] = 1;
+ m->zcullMask[g] = (1U << TU104_TPC_PER_GPC) - 1;
+ }
+
+ *out_params_size = TU104_GR_INFO_MAX_ENGINES *
+ sizeof(struct tu104_gr_floorsweeping_masks);
+ *out_status = 0;
+
+ nv_vgpu_info(ctx->vgpu, "GSPRM: KGR_GET_FLOORSWEEPING_MASKS -> gpcMask=0x%x tpc/gpc=%u",
+ m->gpcMask, TU104_TPC_PER_GPC);
+}
+
static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
const u8 *elem, u32 sequence)
{
@@ -1259,6 +1300,12 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size);
break;
+ case TU104_CTRL_STATIC_KGR_GET_FLOORSWEEPING_MASKS:
+ gsp_rm_handle_kgr_get_floorsweeping_masks(ctx, sequence,
+ &out_status,
+ reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size);
+ break;
+
default:
/* Unknown control command - return success with zeroed params.
* Do NOT echo the request: pure queries pass uninitialized
@@ -1305,6 +1352,62 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
}
/*
+ * GSP_RM_FREE (function 10) - NVOS00_PARAMETERS echo with status=NV_OK.
+ *
+ * rpc_gsp_free: { hRoot, hObjectParent, hObjectOld, status }. Echo the
+ * request's params back with status=0 so the driver's free rpc_message is a
+ * complete well-formed record. (The poll match is function-only; the echo
+ * keeps the post-poll result struct self-consistent.)
+ */
+/*
+ sizeof(struct tu104_gr_floorsweeping_masks);
+ *out_status = 0;
+
+ nv_vgpu_info(ctx->vgpu, "GSPRM: KGR_GET_FLOORSWEEPING_MASKS -> gpcMask=0x%x tpc/gpc=%u",
+ m->gpcMask, TU104_TPC_PER_GPC);
+}
+
+/*
+ * GSP_RM_FREE (function 10) - NVOS00_PARAMETERS echo with status=NV_OK.
+ *
+ * rpc_gsp_free: { hRoot, hObjectParent, hObjectOld, status }. Echo the
+ * request's params back with status=0 so the driver's free rpc_message is a
+ * complete well-formed record. (The poll match is function-only; the echo
+ * keeps the post-poll result struct self-consistent.)
+ */
+static void gsp_rm_handle_free(struct gsp_emu_context *ctx, const u8 *elem,
+ u32 sequence)
+{
+ struct gsp_rm_emu *rm = ctx->rm_emu;
+ const struct gsprm_rpc_header *hdr =
+ (const struct gsprm_rpc_header *)(elem + GSPRM_ELEM_HDR_SIZE);
+ const u8 *req = elem + GSPRM_ELEM_HDR_SIZE + sizeof(*hdr);
+ u8 params[16]; /* NVOS00_PARAMETERS: hRoot, hObjectParent, hObjectOld, status */
+ u32 hObjOld = 0;
+
+ if (hdr->length >= sizeof(*hdr) + sizeof(params))
+ memcpy(params, req, sizeof(params));
+ else
+ memset(params, 0, sizeof(params));
+
+ memcpy(&hObjOld, params + 8, 4);
+ memset(params + 12, 0, 4); /* status = NV_OK */
+
+ nv_vgpu_info(ctx->vgpu, "GSPRM: FREE hObjOld=0x%x seq=%u cmd_read=%u stat_write=%u",
+ hObjOld, sequence, rm->cmd_read, rm->stat_write);
+
+ /* Small delay before FREE/UNLOADING replies: mitigates an intermittent
+ * driver-side reply-matching race in the cleanup path. */
+ udelay(2000);
+
+ if (gsp_rm_send(ctx, GSPRM_RPC_FUNC_FREE, sequence, 0,
+ params, sizeof(params)))
+ nv_vgpu_err(ctx->vgpu, "GSPRM: FREE reply send failed");
+ else
+ rm->rpcs_replied++;
+}
+
+/*
* Handle one command-queue record (first element). For async init RPCs we
* just consume. Synchronous RPCs get a reply; per-function handlers are
* implemented iteratively (unknown ones get a generic success reply).
@@ -1341,36 +1444,27 @@ static void gsp_rm_handle_element(struct gsp_emu_context *ctx, const u8 *elem)
gsp_rm_handle_rm_control(ctx, elem, sequence);
return;
+ case GSPRM_RPC_FUNC_FREE:
+ gsp_rm_handle_free(ctx, elem, sequence);
+ return;
+
default:
/* Synchronous RPC: echo function+sequence with success so the
* driver's rpcRecvPoll completes. */
rm->unknown_rpcs++;
- if (function == 10 || function == 47) {
- /* FREE/UNLOADING: log hObject + queue positions for
- * unload-cycle debugging. driver_rp = the peer's status
- * read pointer (swap-rx): if it lags stat_write, the
- * driver isn't draining our replies. */
- const u8 *p = elem + GSPRM_ELEM_HDR_SIZE +
- sizeof(struct gsprm_rpc_header);
- u32 hc = 0, ho = 0, driver_rp = 0;
-
- if (hdr->length >= sizeof(*hdr) + 12)
- memcpy(&ho, p + 8, 4); /* hObjectOld */
- gsp_rm_read_buf(ctx, rm->cmd_off + 0x20,
- &driver_rp, sizeof(driver_rp));
- nv_vgpu_info(ctx->vgpu, "GSPRM: RPC %s(%u) seq=%u hRoot=0x%x hObjOld=0x%x cmd_read=%u stat_write=%u drv_rp=%u stat_seq=%u",
+ if (function == 47) {
+ /* UNLOADING_GUEST_DRIVER: log for unload-cycle debugging */
+ nv_vgpu_info(ctx->vgpu, "GSPRM: RPC %s(%u) seq=%u cmd_read=%u stat_write=%u stat_seq=%u",
gsp_rm_rpc_name(function), function, sequence,
- hc, ho, rm->cmd_read, rm->stat_write,
- driver_rp, rm->stat_seq);
+ rm->cmd_read, rm->stat_write, rm->stat_seq);
} else {
nv_vgpu_info(ctx->vgpu, "GSPRM: RPC %s(%u) seq=%u len=%u -> generic SUCCESS reply (unknown #%llu)",
gsp_rm_rpc_name(function), function, sequence,
hdr->length, rm->unknown_rpcs);
}
- /* Small delay before fn-10/47 replies: mitigates (does not fully
- * eliminate) an intermittent driver-side reply-matching race in
- * the FREE/UNLOADING path. See Handoff-vGPU-IO.md. */
- if (function == 10 || function == 47)
+ /* Small delay before UNLOADING replies: mitigates an intermittent
+ * driver-side reply-matching race in the cleanup path. */
+ if (function == 47)
udelay(2000);
if (gsp_rm_send(ctx, function, sequence, 0, NULL, 0))
nv_vgpu_err(ctx->vgpu, "GSPRM: reply send failed fn=%u", function);