summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 19:57:48 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 19:57:48 -0400
commitb217f7d45f1c571d648619d607fb0175aa4b0179 (patch)
tree5cca1d332be6c613e743619fe6ea3e1cec0bf6cb
parentc99d6eaa464937f979b474ab971b63250d340ea7 (diff)
downloadopen-iov-b217f7d45f1c571d648619d607fb0175aa4b0179.tar.gz
open-iov-b217f7d45f1c571d648619d607fb0175aa4b0179.zip
gsp-phys: FBMEM error notifier and the bind/notif-index/token order before submission
-rw-r--r--kernel/gsp_phys_booter.c509
1 files changed, 397 insertions, 112 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 920ccdb..3ba8714 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -199,10 +199,15 @@ bool gsp_phys_boot_requested(void)
* NV20_SUBDEVICE_0 0x2080 (class/cl2080.h), KEPLER_CHANNEL_GROUP_A 0xA06C
* (class/cla06c.h:33), TURING_CHANNEL_GPFIFO_A 0xC46F (class/clc46f.h:43),
* TURING_DMA_COPY_A 0xC5B5 (class/clc5b5.h:33), NV01_MEMORY_LIST_SYSTEM
- * 0x81 (class/cl84a0.h:45), NVC36F_CTRL_CMD_GPFIFO_GET_WORK_SUBMIT_TOKEN
- * 0xC36F0108 (ctrl/ctrlc36f.h:82), NVA06F_CTRL_CMD_GPFIFO_SCHEDULE
- * 0xA06F0103 (ctrl/ctrla06f/ctrla06fgpfifo.h:54), FERMI_VASPACE_A 0x90F1
- * (class/cl90f1.h:30), NV0080_CTRL_CMD_DMA_SET_PAGE_DIRECTORY 0x801813
+ * 0x81 (class/cl84a0.h:45), NV01_MEMORY_LIST_FBMEM 0x82
+ * (class/cl84a0.h:47), NV01_MEMORY_LOCAL_USER 0x40 (class/cl0040.h:34),
+ * NVC36F_CTRL_CMD_GPFIFO_GET_WORK_SUBMIT_TOKEN 0xC36F0108
+ * (ctrl/ctrlc36f.h:82), NVC36F_CTRL_CMD_GPFIFO_SET_WORK_SUBMIT_TOKEN_
+ * NOTIF_INDEX 0xC36F010A (ctrl/ctrlc36f.h:135), NVA06F_CTRL_CMD_
+ * GPFIFO_SCHEDULE 0xA06F0103 (ctrl/ctrla06f/ctrla06fgpfifo.h:54),
+ * NVA06F_CTRL_CMD_BIND 0xA06F0104 (ctrl/ctrla06f/ctrla06fgpfifo.h:83),
+ * FERMI_VASPACE_A 0x90F1 (class/cl90f1.h:30),
+ * NV0080_CTRL_CMD_DMA_SET_PAGE_DIRECTORY 0x801813
* (ctrl/ctrl0080dma.h:832-840), COPY0 engine type 0x9
* (class/cl2080_notification.h:292).
*/
@@ -214,16 +219,36 @@ bool gsp_phys_boot_requested(void)
#define GSP_PHYS_RMCALL_CLASS_DEVICE 0x0080u
#define GSP_PHYS_RMCALL_CLASS_SUBDEVICE 0x2080u
#define GSP_PHYS_RMCALL_CLASS_MEMLIST_SYSTEM 0x0081u
+#define GSP_PHYS_RMCALL_CLASS_MEMORY_LOCAL_USER 0x0040u
#define GSP_PHYS_RMCALL_CLASS_TSG 0xa06cu
#define GSP_PHYS_RMCALL_CLASS_CHANNEL 0xc46fu
#define GSP_PHYS_RMCALL_CLASS_CE 0xc5b5u
#define GSP_PHYS_RMCALL_CLASS_VASPACE 0x90f1u
#define GSP_PHYS_RMCALL_CTRL_GET_WORK_SUBMIT_TOKEN 0xc36f0108u
+#define GSP_PHYS_RMCALL_CTRL_SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX 0xc36f010au
#define GSP_PHYS_RMCALL_CTRL_GPFIFO_SCHEDULE 0xa06f0103u
+#define GSP_PHYS_RMCALL_CTRL_BIND_CHANNEL 0xa06f0104u
#define GSP_PHYS_RMCALL_CTRL_DMA_SET_PAGE_DIRECTORY 0x801813u
#define GSP_PHYS_RMCALL_ENGINE_COPY0 0x9u
/*
+ * Work-submit token notifier slot. The default index is
+ * NV_CHANNELGPFIFO_NOTIFICATION_TYPE_WORK_SUBMIT_TOKEN = 1
+ * (nvos.h:2465-2468); the reference ladder requests __SIZE_1 + deviceIndex
+ * = 3 for subdevice 0 before asking for the token
+ * (nvidia-push-init.c:303-316), so the 4 KB FBMEM notifier below must span at
+ * least (3 + 1) * sizeof(NvNotification) = 64 B.
+ */
+#define GSP_PHYS_RMCALL_NOTIF_INDEX 3u
+
+/* NVOS32 type of the error-notifier allocation (nvos.h:939); attr bits
+ * 26:25 = VIDMEM location and 31:29 = UNCACHED coherency are both zero, the
+ * shape the RM's own channel-notifier allocation uses
+ * (mem_utils_gm107.c:344-352). */
+#define GSP_PHYS_RMCALL_OS32_TYPE_IMAGE 0u
+#define GSP_PHYS_RMCALL_OS32_ATTR_VIDMEM_UNCACHED 0u
+
+/*
* FERMI_VASPACE_A allocation (nvos.h:2756-2765): flags bit 3
* (IS_EXTERNALLY_OWNED) and bit 8 (ALLOW_ZERO_ADDRESS); the 256 MB window is
* the smallest deterministic size this ladder needs and keeps vaBase 0. The
@@ -262,13 +287,14 @@ bool gsp_phys_boot_requested(void)
#define GSP_PHYS_RMCALL_CHANNEL_INTERNALFLAGS_PRIV_KERNEL 0x2u
/*
- * NV_MEMORY_DESC_PARAMS enums: ADDR_SYSMEM=1 (kernel/vgpu/
- * rm_plugin_shared_code.h:66) and NV_MEMORY_UNCACHED=1 (kernel/os/
- * nv_memory_type.h:31), which is the default RM system-memory CPU cache
- * attribute (system_mem.c:73-74) and selects the NONCOHERENT_SYSMEM CE
+ * NV_MEMORY_DESC_PARAMS enums: ADDR_SYSMEM=1 and ADDR_FBMEM=2
+ * (kernel/vgpu/rm_plugin_shared_code.h:66-67) and NV_MEMORY_UNCACHED=1
+ * (kernel/os/nv_memory_type.h:31), which is the default RM system-memory CPU
+ * cache attribute (system_mem.c:73-74) and selects the NONCOHERENT_SYSMEM CE
* aperture (clc5b5.h:56-60).
*/
#define GSP_PHYS_RMCALL_ADDR_SYSMEM 1u
+#define GSP_PHYS_RMCALL_ADDR_FBMEM 2u
#define GSP_PHYS_RMCALL_CACHE_UNCACHED 1u
#define GSP_PHYS_RMCALL_CE_TARGET_NONCOHERENT_SYSMEM 2u
@@ -291,6 +317,7 @@ bool gsp_phys_boot_requested(void)
#define GSP_PHYS_RMCALL_HANDLE_CHANNEL_V2 0xcaf00014u
#define GSP_PHYS_RMCALL_HANDLE_CHANNEL_V3 0xcaf00018u
#define GSP_PHYS_RMCALL_HANDLE_CHANNEL_V5 0xcaf00024u
+#define GSP_PHYS_RMCALL_HANDLE_NOTIFIER_MEM 0xcaf00028u
#define GSP_PHYS_RMCALL_HANDLE_MEM_BASE 0xcaf01000u
#define GSP_PHYS_RMCALL_HANDLE_MEM_STRIDE 4u
@@ -327,6 +354,7 @@ enum gsp_phys_rmcall_buf {
};
#define GSP_PHYS_RMCALL_COPY_SIZE 0x1000u
+#define GSP_PHYS_RMCALL_NOTIFIER_SIZE 0x1000u
#define GSP_PHYS_RMCALL_GPFIFO_ENTRIES 8u
#define GSP_PHYS_RMCALL_USERD_GPPUT_OFF 0x8cu /* Nvc46fControl GPPut (clc46f.h:57) */
#define GSP_PHYS_RMCALL_USERD_GPGET_OFF 0x88u /* Nvc46fControl GPGet (clc46f.h:56) */
@@ -861,6 +889,9 @@ static struct gsp_phys_boot_stage {
* the variant table for its number) */
bool rmcall_channel_server_mem; /* winning variant had server-allocated mem */
u32 rmcall_token; /* GET_WORK_SUBMIT_TOKEN result */
+ u32 rmcall_notifier_mem; /* FBMEM error-notifier object handle */
+ u64 rmcall_notifier_base; /* its returned frame-buffer address */
+ bool rmcall_notifier_valid; /* the FBMEM notifier alloc succeeded */
struct pci_dev *pdev; /* borrowed claim pdev (valid while primed) */
struct gsp_phys_boot_chunk rmcall_chunks[GSP_PHYS_RMCALL_BUF_COUNT];
u32 rmcall_mem_handles[GSP_PHYS_RMCALL_BUF_COUNT];
@@ -1373,6 +1404,9 @@ static void gsp_phys_rmcall_reset(void)
boot_stage.rmcall_channel_variant = 0;
boot_stage.rmcall_channel_server_mem = false;
boot_stage.rmcall_token = 0;
+ boot_stage.rmcall_notifier_mem = 0;
+ boot_stage.rmcall_notifier_base = 0;
+ boot_stage.rmcall_notifier_valid = false;
for (i = 0; i < GSP_PHYS_RMCALL_BUF_COUNT; i++) {
boot_stage.rmcall_mem_handles[i] = 0;
boot_stage.rmcall_mem_registered[i] = false;
@@ -4208,6 +4242,41 @@ struct gsp_phys_rmcall_mem_desc {
__le32 cache_attrib;
};
+/*
+ * NV_MEMORY_ALLOCATION_PARAMS (nvos.h:1634-1668), 128 bytes. The u64 fields
+ * are 8-byte aligned; offset and limit are [IN/OUT], so a frame-buffer
+ * allocation returns the allocated FB offset (and its limit) in the echoed
+ * params. type/attr carry the NVOS32 enums (type IMAGE, location VIDMEM,
+ * coherency UNCACHED are all the zero/default values).
+ */
+struct gsp_phys_rmcall_memory_alloc_params {
+ __le32 owner;
+ __le32 type;
+ __le32 flags;
+ __le32 width;
+ __le32 height;
+ __le32 pitch;
+ __le32 attr;
+ __le32 attr2;
+ __le32 format;
+ __le32 compr_covg;
+ __le32 zcull_covg;
+ __le32 pad0;
+ __le64 range_lo;
+ __le64 range_hi;
+ __le64 size;
+ __le64 alignment;
+ __le64 offset;
+ __le64 limit;
+ __le64 address;
+ __le32 ctag_offset;
+ __le32 h_va_space;
+ __le32 internalflags;
+ __le32 tag;
+ __le32 numa_node;
+ __le32 pad1;
+};
+
/* NV_VASPACE_ALLOCATION_PARAMETERS (nvos.h:2756-2765), 48 bytes: index, flags,
* vaSize, vaStartInternal, vaLimitInternal, bigPageSize, vaBase. */
struct gsp_phys_rmcall_vas_params {
@@ -4565,13 +4634,103 @@ static int gsp_phys_rmcall_register_mem(void __iomem *bar0, u32 index)
}
/*
+ * Allocate the channel error-context notifier in FBMEM. On GSP the channel
+ * notify path rejects a non-FBMEM error-context memdesc with
+ * NV_ERR_INVALID_STATE (kernel_channel.c:1924-1925), and the server's channel
+ * constructor materializes that memdesc from NV_CHANNEL_ALLOC_PARAMS'
+ * errorNotifierMem base/size/addressSpace; the notifier slot it writes is
+ * notifyIndex (set to GSP_PHYS_RMCALL_NOTIF_INDEX by the stage-4 sequence).
+ *
+ * The memlist registration used for the sysmem ladder buffers cannot produce
+ * a safe FBMEM memdesc for a host client (classes 0x81/0x82 only reference
+ * frame-buffer pages a guest already owns; 0x82 would accept our host PFNs as
+ * literal FB offsets). The frame-buffer page is therefore allocated from the
+ * GSP-RM itself with GSP_RM_ALLOC (103) class NV01_MEMORY_LOCAL_USER (0x40),
+ * mirroring the RM's own channel-notifier allocation (type IMAGE, location
+ * VIDMEM, coherency UNCACHED; mem_utils_gm107.c:344-352); the server returns
+ * the allocated FB offset in NV_MEMORY_ALLOCATION_PARAMS.offset, which is what
+ * errorNotifierMem.base carries.
+ *
+ * Failure is non-fatal: the channel alloc then keeps a zeroed notifier
+ * memdesc (server SYSMEM/ADDR_UNKNOWN, the cycle-19 shape) and the stage-4
+ * GET_WORK_SUBMIT_TOKEN recovery path still obtains the token from the
+ * control reply. The FB object is a server object like the VAS/TSG/channel
+ * and is not torn down by rm-call fini (UNLOAD + rmmod remains the fail-safe).
+ */
+static int gsp_phys_rmcall_alloc_notifier_mem(void __iomem *bar0)
+{
+ struct gsp_phys_rmcall_memory_alloc_params mem;
+ struct gsp_phys_rmcall_rm_alloc_req *req;
+ struct gsp_phys_rx_record rec;
+ u32 req_len = sizeof(*req) + sizeof(mem);
+ u32 status;
+ u64 base;
+ int ret;
+
+ memset(&mem, 0, sizeof(mem));
+ mem.type = cpu_to_le32(GSP_PHYS_RMCALL_OS32_TYPE_IMAGE);
+ mem.size = cpu_to_le64(GSP_PHYS_RMCALL_NOTIFIER_SIZE);
+ mem.attr = cpu_to_le32(GSP_PHYS_RMCALL_OS32_ATTR_VIDMEM_UNCACHED);
+
+ req = kvzalloc(req_len, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+ req->h_client = cpu_to_le32(boot_stage.rmcall_client);
+ req->h_parent = cpu_to_le32(boot_stage.rmcall_device);
+ req->h_object = cpu_to_le32(GSP_PHYS_RMCALL_HANDLE_NOTIFIER_MEM);
+ req->h_class = cpu_to_le32(GSP_PHYS_RMCALL_CLASS_MEMORY_LOCAL_USER);
+ req->params_size = cpu_to_le32(sizeof(mem));
+ memcpy(req->params, &mem, sizeof(mem));
+
+ ret = gsp_phys_rmcall_rpc(bar0, GSP_PHYS_RMCALL_FUNC_GSP_RM_ALLOC,
+ req, req_len, 20, &rec, true, NULL);
+ kvfree(req);
+ if (ret) {
+ pr_err("gsp-phys-boot: rm-call: FBMEM error-notifier alloc (hMemory=0x%x class=0x40 size=0x%x) RPC failed: %d\n",
+ GSP_PHYS_RMCALL_HANDLE_NOTIFIER_MEM,
+ GSP_PHYS_RMCALL_NOTIFIER_SIZE, ret);
+ gsp_phys_rx_record_free(&rec);
+ return ret;
+ }
+
+ status = gsp_phys_rmcall_reply_u32(&rec, 16);
+ if (status != 0 ||
+ rec.payload_len < 32 + offsetof(struct gsp_phys_rmcall_memory_alloc_params,
+ offset) + 8) {
+ pr_err("gsp-phys-boot: rm-call: FBMEM error-notifier alloc rejected: status=0x%x payload=%u B (need the echoed params through offset at +%zu)\n",
+ status, rec.payload_len,
+ offsetof(struct gsp_phys_rmcall_memory_alloc_params,
+ offset) + 8);
+ gsp_phys_rx_record_free(&rec);
+ return -EIO;
+ }
+ base = get_unaligned_le64(rec.buf + GSP_PHYS_MSG_ELEMENT_PAYLOAD_OFF + 32 +
+ offsetof(struct gsp_phys_rmcall_memory_alloc_params,
+ offset));
+ gsp_phys_rx_record_free(&rec);
+
+ boot_stage.rmcall_notifier_mem = GSP_PHYS_RMCALL_HANDLE_NOTIFIER_MEM;
+ boot_stage.rmcall_notifier_base = base;
+ boot_stage.rmcall_notifier_valid = true;
+ pr_info("gsp-phys-boot: rm-call: FBMEM error-notifier allocated: hMemory=0x%x class=0x40 size=0x%x FB base=0x%llx (notifyIndex=%u)\n",
+ boot_stage.rmcall_notifier_mem, GSP_PHYS_RMCALL_NOTIFIER_SIZE,
+ boot_stage.rmcall_notifier_base, GSP_PHYS_RMCALL_NOTIF_INDEX);
+ return 0;
+}
+
+/*
* One GSP_RM_CONTROL (76). Reply status at payload+12, echoed params at
- * payload+24 (rpcRmApiControl_GSP, rpc.c:9797-10031).
+ * payload+24 (rpcRmApiControl_GSP, rpc.c:9797-10031). out_status (optional)
+ * receives the server control status even when it is nonzero; the echoed
+ * params are copied into out before that status is checked, so a caller can
+ * still recover server-written output (the work-submit token) from a failed
+ * reply.
*/
static int gsp_phys_rmcall_control(void __iomem *bar0, u32 h_object, u32 cmd,
const void *params, u32 params_len,
u32 reply_min, void *out, u32 out_len,
- u32 *out_rpc_result, const char *what)
+ u32 *out_rpc_result, u32 *out_status,
+ const char *what)
{
struct gsp_phys_rmcall_rm_control_req *req;
struct gsp_phys_rx_record rec;
@@ -4579,6 +4738,8 @@ static int gsp_phys_rmcall_control(void __iomem *bar0, u32 h_object, u32 cmd,
u32 status;
int ret;
+ if (out_status)
+ *out_status = 0;
req = kvzalloc(req_len, GFP_KERNEL);
if (!req)
return -ENOMEM;
@@ -4609,6 +4770,8 @@ static int gsp_phys_rmcall_control(void __iomem *bar0, u32 h_object, u32 cmd,
return -EIO;
}
status = gsp_phys_rmcall_reply_u32(&rec, 12);
+ if (out_status)
+ *out_status = status;
if (out && out_len && rec.payload_len > 24) {
u32 n = min_t(u32, out_len, rec.payload_len - 24);
@@ -4806,6 +4969,22 @@ gsp_phys_rmcall_channel_params_init(struct gsp_phys_rmcall_channel_params *ch,
ch->cid = cpu_to_le32(1); /* client hint; server assigns its own */
ch->sub_device_id = cpu_to_le32(0);
ch->internal_flags = cpu_to_le32(v->internal_flags);
+ /*
+ * The error-context notifier must be an FBMEM memdesc on GSP
+ * (kernel_channel.c:1924-1925) and the server builds pErrContextMemDesc
+ * from these fields; carry the stage-3 FBMEM allocation when it exists.
+ * The other memdescs stay the client sysmem chunks.
+ */
+ if (boot_stage.rmcall_notifier_valid) {
+ ch->error_notifier_mem.base =
+ cpu_to_le64(boot_stage.rmcall_notifier_base);
+ ch->error_notifier_mem.size =
+ cpu_to_le64(GSP_PHYS_RMCALL_NOTIFIER_SIZE);
+ ch->error_notifier_mem.address_space =
+ cpu_to_le32(GSP_PHYS_RMCALL_ADDR_FBMEM);
+ ch->error_notifier_mem.cache_attrib =
+ cpu_to_le32(GSP_PHYS_RMCALL_CACHE_UNCACHED);
+ }
if (v->server_mem)
return; /* zeroed memdescs, gpFifoOffset/gpFifoEntries 0 */
ch->gp_fifo_offset = cpu_to_le64((u64)gsp_phys_chunk_page_dma(
@@ -4818,28 +4997,94 @@ gsp_phys_rmcall_channel_params_init(struct gsp_phys_rmcall_channel_params *ch,
}
/*
+ * Channel-alloc variant walk (same already-held gsp_phys_rm_lock and gates):
+ * the first rpc_result==0 wins and records its handle/variant/server-mem
+ * state. Failed variants log one compact line each and no payload dump; an
+ * all-failed walk ends in one summary line with every variant's
+ * rpc_result/status. Kept out of stage 3 so the inline stack stays small.
+ */
+static int gsp_phys_rmcall_alloc_channel(void __iomem *bar0)
+{
+ struct gsp_phys_rmcall_channel_params ch;
+ u32 v_rpc[ARRAY_SIZE(gsp_phys_rmcall_channel_variants)];
+ u32 v_status[ARRAY_SIZE(gsp_phys_rmcall_channel_variants)];
+ u32 i;
+ int ret;
+
+ ret = -EIO;
+ for (i = 0; i < ARRAY_SIZE(gsp_phys_rmcall_channel_variants); i++) {
+ const struct gsp_phys_rmcall_channel_variant *v =
+ &gsp_phys_rmcall_channel_variants[i];
+ u32 parent = v->tsg_parent ? boot_stage.rmcall_tsg :
+ boot_stage.rmcall_device;
+
+ gsp_phys_rmcall_channel_params_init(&ch, v);
+ v_rpc[i] = 0;
+ v_status[i] = 0;
+ ret = gsp_phys_rmcall_alloc(bar0,
+ boot_stage.rmcall_client, parent,
+ v->handle,
+ GSP_PHYS_RMCALL_CLASS_CHANNEL,
+ &ch, sizeof(ch), false, NULL, v->name,
+ false, &v_rpc[i], &v_status[i]);
+ pr_info("gsp-phys-boot: rm-call: stage 3 channel variant V%u (%u/%zu, %s): hParent=0x%x hObject=0x%x flags=0x%x internalFlags=0x%x ret=%d rpc_result=0x%x status=0x%x\n",
+ v->id, i + 1,
+ ARRAY_SIZE(gsp_phys_rmcall_channel_variants),
+ v->name, parent, v->handle, v->flags,
+ v->internal_flags, ret, v_rpc[i], v_status[i]);
+ if (!ret) {
+ boot_stage.rmcall_channel = v->handle;
+ boot_stage.rmcall_channel_variant = v->id;
+ boot_stage.rmcall_channel_server_mem = v->server_mem;
+ pr_info("gsp-phys-boot: rm-call: stage 3 channel variant V%u (%s) WON: hChannel=0x%x\n",
+ v->id, v->name, v->handle);
+ return 0;
+ }
+ }
+
+ {
+ char summary[256];
+ u32 pos = 0;
+
+ for (i = 0; i < ARRAY_SIZE(gsp_phys_rmcall_channel_variants); i++)
+ pos += scnprintf(summary + pos, sizeof(summary) - pos,
+ "%sV%u %s rpc=0x%x status=0x%x",
+ i ? "; " : "",
+ gsp_phys_rmcall_channel_variants[i].id,
+ gsp_phys_rmcall_channel_variants[i].name,
+ v_rpc[i], v_status[i]);
+ pr_err("gsp-phys-boot: rm-call: stage 3: all %zu channel variants failed: %s\n",
+ ARRAY_SIZE(gsp_phys_rmcall_channel_variants), summary);
+ }
+ return ret;
+}
+
+/*
* Stage 3: explicit VAS with its page directory, then channel group (COPY0,
* physical) and the channel. The VAS and PDB must both succeed: a channel
* whose VAS has no page-directory base fails the context-share/instance-block
* setup with NV_ERR_INVALID_STATE, and no variant avoids the VAS. The channel
- * alloc then walks the variants below in order (same already-held
- * gsp_phys_rm_lock and gates); the first rpc_result==0 wins. Failed variants
- * log one compact line each and no payload dump, and an all-failed walk ends
- * in one summary line with every variant's rpc_result/status. Then
- * GET_WORK_SUBMIT_TOKEN. The token is requested before scheduling as
- * specified; because the server's TU102 generator requires the channel to be
- * on a runlist already (kernel_fifo_tu102.c:110-116, which GPFIFO_SCHEDULE
- * establishes at kernel_channel.c:3244), a nonzero status is logged and
- * retried after the schedule in stage 4 instead of aborting.
+ * alloc then walks the variants in gsp_phys_rmcall_alloc_channel in order
+ * (same already-held gsp_phys_rm_lock and gates); the first rpc_result==0
+ * wins. Failed variants log one compact line each and no payload dump, and an
+ * all-failed walk ends in one summary line with every variant's
+ * rpc_result/status.
+ *
+ * Before the walk, one 4 KB FBMEM error-notifier page is allocated from the
+ * GSP-RM and its memdesc fields are carried in every channel variant (the
+ * server's GSP-only notify path requires the error-context notifier to live
+ * in ADDR_FBMEM, kernel_channel.c:1924-1925). The work-submit token is not
+ * requested here: its generator requires BIND + GPFIFO_SCHEDULE (runlist
+ * membership, kernel_fifo_tu102.c:110-116), so the whole BIND -> SCHEDULE ->
+ * SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX -> GET_WORK_SUBMIT_TOKEN order lives in
+ * stage 4.
*/
static int gsp_phys_rmcall_stage3(void __iomem *bar0)
{
struct gsp_phys_rmcall_vas_params vas;
struct gsp_phys_rmcall_pdb_params pdb;
struct gsp_phys_rmcall_tsg_params tsg;
- struct gsp_phys_rmcall_channel_params ch;
u32 vas_rpc = 0, vas_status = 0, pdb_rpc = 0;
- u32 token = 0;
int ret;
/*
@@ -4881,7 +5126,7 @@ static int gsp_phys_rmcall_stage3(void __iomem *bar0)
ret = gsp_phys_rmcall_control(bar0, boot_stage.rmcall_device,
GSP_PHYS_RMCALL_CTRL_DMA_SET_PAGE_DIRECTORY,
&pdb, sizeof(pdb), 16, NULL, 0,
- &pdb_rpc, "DMA_SET_PAGE_DIRECTORY");
+ &pdb_rpc, NULL, "DMA_SET_PAGE_DIRECTORY");
if (ret) {
pr_err("gsp-phys-boot: rm-call: stage 3 aborted: DMA_SET_PAGE_DIRECTORY on hVas=0x%x numEntries=%u failed (rpc_result=0x%x); the VAS has no page-directory base\n",
boot_stage.rmcall_vas, le32_to_cpu(pdb.num_entries),
@@ -4910,79 +5155,27 @@ static int gsp_phys_rmcall_stage3(void __iomem *bar0)
return ret;
boot_stage.rmcall_tsg = GSP_PHYS_RMCALL_HANDLE_TSG;
- {
- u32 v_rpc[ARRAY_SIZE(gsp_phys_rmcall_channel_variants)];
- u32 v_status[ARRAY_SIZE(gsp_phys_rmcall_channel_variants)];
- u32 i;
-
- ret = -EIO;
- for (i = 0; i < ARRAY_SIZE(gsp_phys_rmcall_channel_variants); i++) {
- const struct gsp_phys_rmcall_channel_variant *v =
- &gsp_phys_rmcall_channel_variants[i];
- u32 parent = v->tsg_parent ? boot_stage.rmcall_tsg :
- boot_stage.rmcall_device;
-
- gsp_phys_rmcall_channel_params_init(&ch, v);
- v_rpc[i] = 0;
- v_status[i] = 0;
- ret = gsp_phys_rmcall_alloc(bar0,
- boot_stage.rmcall_client, parent,
- v->handle,
- GSP_PHYS_RMCALL_CLASS_CHANNEL,
- &ch, sizeof(ch), false, NULL, v->name,
- false, &v_rpc[i], &v_status[i]);
- pr_info("gsp-phys-boot: rm-call: stage 3 channel variant V%u (%u/%zu, %s): hParent=0x%x hObject=0x%x flags=0x%x internalFlags=0x%x ret=%d rpc_result=0x%x status=0x%x\n",
- v->id, i + 1,
- ARRAY_SIZE(gsp_phys_rmcall_channel_variants),
- v->name, parent, v->handle, v->flags,
- v->internal_flags, ret, v_rpc[i], v_status[i]);
- if (!ret) {
- boot_stage.rmcall_channel = v->handle;
- boot_stage.rmcall_channel_variant = v->id;
- boot_stage.rmcall_channel_server_mem = v->server_mem;
- pr_info("gsp-phys-boot: rm-call: stage 3 channel variant V%u (%s) WON: hChannel=0x%x\n",
- v->id, v->name, v->handle);
- break;
- }
- }
- if (ret) {
- char summary[256];
- u32 pos = 0;
-
- for (i = 0;
- i < ARRAY_SIZE(gsp_phys_rmcall_channel_variants);
- i++)
- pos += scnprintf(summary + pos,
- sizeof(summary) - pos,
- "%sV%u %s rpc=0x%x status=0x%x",
- i ? "; " : "",
- gsp_phys_rmcall_channel_variants[i].id,
- gsp_phys_rmcall_channel_variants[i].name,
- v_rpc[i], v_status[i]);
- pr_err("gsp-phys-boot: rm-call: stage 3: all %zu channel variants failed: %s\n",
- ARRAY_SIZE(gsp_phys_rmcall_channel_variants),
- summary);
- return ret;
- }
- }
-
- ret = gsp_phys_rmcall_control(bar0, boot_stage.rmcall_channel,
- GSP_PHYS_RMCALL_CTRL_GET_WORK_SUBMIT_TOKEN,
- &token, sizeof(token), 28, &token,
- sizeof(token), NULL,
- "GET_WORK_SUBMIT_TOKEN");
- if (ret) {
- pr_warn("gsp-phys-boot: rm-call: work-submit token not available before GPFIFO_SCHEDULE (ret %d); stage 4 retries after scheduling\n",
+ /*
+ * Allocate the channel's error-context notifier page in FBMEM before
+ * the channel alloc so every variant carries the memdesc. A failure is
+ * logged and tolerated: the channel then keeps the zeroed (server
+ * ADDR_UNKNOWN/SYSMEM) notifier and stage 4's token recovery covers the
+ * notify rejection.
+ */
+ ret = gsp_phys_rmcall_alloc_notifier_mem(bar0);
+ if (ret)
+ pr_warn("gsp-phys-boot: rm-call: FBMEM error-notifier allocation failed (%d); channel allocs carry a zeroed notifier memdesc and only the stage-4 token recovery can save the token\n",
ret);
- boot_stage.rmcall_token = 0;
- } else {
- boot_stage.rmcall_token = token;
- }
- pr_info("gsp-phys-boot: rm-call: stage 3 complete: hVas=0x%x hTsg=0x%x hChannel=0x%x variant=V%u token=0x%x engine=COPY0 (cid is server-assigned, request value ignored)\n",
+ ret = gsp_phys_rmcall_alloc_channel(bar0);
+ if (ret)
+ return ret;
+
+ pr_info("gsp-phys-boot: rm-call: stage 3 complete: hVas=0x%x hTsg=0x%x hChannel=0x%x variant=V%u notifier=%s base=0x%llx engine=COPY0 (cid is server-assigned, request value ignored; token is requested in stage 4)\n",
boot_stage.rmcall_vas, boot_stage.rmcall_tsg,
boot_stage.rmcall_channel, boot_stage.rmcall_channel_variant,
- boot_stage.rmcall_token);
+ boot_stage.rmcall_notifier_valid ? "FBMEM" : "none",
+ boot_stage.rmcall_notifier_base);
return 0;
}
@@ -5042,11 +5235,21 @@ static u32 gsp_phys_rmcall_build_methods(u32 *pb, u64 sem_dma, u64 src_dma,
}
/*
- * Stage 4: GPFIFO_SCHEDULE, (retry) GET_WORK_SUBMIT_TOKEN, then the first
+ * Stage 4: the corrected token sequence - BIND (COPY0) -> GPFIFO_SCHEDULE ->
+ * SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX -> GET_WORK_SUBMIT_TOKEN - then the first
* real work item: write the CE method block to the pushbuffer, publish the
* GPFIFO entry and the USERD GP_PUT (mem_utils_gm107.c:1815-1904), ring the
* PF channel doorbell (kernel_fifo_tu102.c:46-59) and poll the sysmem
* semaphore word. Afterwards dst must equal src byte for byte.
+ *
+ * BIND is the precondition the TU102 token generator needs (runlist
+ * membership, kernel_fifo_tu102.c:110-116; GPFIFO_SCHEDULE alone only sets
+ * RUNLIST_SET at kernel_channel.c:3244), and SET_NOTIF_INDEX (index 3 for
+ * subdevice 0, nvidia-push-init.c:303-316) gives the FBMEM notifier write a
+ * target slot. Every control failure logs its rpc_result/status and aborts
+ * before the doorbell; a 0x40 GET_WORK_SUBMIT_TOKEN whose echoed params carry
+ * a nonzero token still proceeds (the server writes the token before the
+ * notifier update, kernel_channel.c:3471-3477).
*/
static int gsp_phys_rmcall_stage4(void __iomem *bar0)
{
@@ -5056,10 +5259,13 @@ static int gsp_phys_rmcall_stage4(void __iomem *bar0)
struct gsp_phys_boot_chunk *sem = &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_SEM];
struct gsp_phys_boot_chunk *src = &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_SRC];
struct gsp_phys_boot_chunk *dst = &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_DST];
+ u32 bind_params = GSP_PHYS_RMCALL_ENGINE_COPY0; /* NVA06F engineType */
+ u32 notif_index = GSP_PHYS_RMCALL_NOTIF_INDEX; /* NVC36F index */
u8 sched_params[2] = { 1, 0 }; /* NVA06F bEnable=1, bSkipSubmit=0 */
resource_size_t bar0_len;
u32 *pb_va, *userd, *ring_va;
u32 words, gpput, gpget, slot, e0, e1, poll;
+ u32 token, rpc_result, status, runlist_id, ch_id, doorbell;
u64 pb_dma, sem_dma, src_dma, dst_dma;
unsigned long deadline;
int ret;
@@ -5086,27 +5292,78 @@ static int gsp_phys_rmcall_stage4(void __iomem *bar0)
return -EIO;
}
+ /* (a) BIND the channel to the COPY0 engine (nvidia-push-init.c:343-353). */
+ rpc_result = 0;
+ status = 0;
+ ret = gsp_phys_rmcall_control(bar0, boot_stage.rmcall_channel,
+ GSP_PHYS_RMCALL_CTRL_BIND_CHANNEL,
+ &bind_params, sizeof(bind_params), 16,
+ NULL, 0, &rpc_result, &status,
+ "BIND_CHANNEL");
+ if (ret) {
+ pr_err("gsp-phys-boot: rm-call: stage 4 aborted before the doorbell: BIND_CHANNEL (0xa06f0104 engineType=0x%x) failed (rpc_result=0x%x status=0x%x)\n",
+ bind_params, rpc_result, status);
+ return ret;
+ }
+
+ /* (b) GPFIFO_SCHEDULE bEnable=1, bSkipSubmit=0 (existing step). */
+ rpc_result = 0;
+ status = 0;
ret = gsp_phys_rmcall_control(bar0, boot_stage.rmcall_channel,
GSP_PHYS_RMCALL_CTRL_GPFIFO_SCHEDULE,
sched_params, sizeof(sched_params), 16,
- NULL, 0, NULL, "GPFIFO_SCHEDULE");
- if (ret)
+ NULL, 0, &rpc_result, &status,
+ "GPFIFO_SCHEDULE");
+ if (ret) {
+ pr_err("gsp-phys-boot: rm-call: stage 4 aborted before the doorbell: GPFIFO_SCHEDULE (0xa06f0103 bEnable=1 bSkipSubmit=0) failed (rpc_result=0x%x status=0x%x)\n",
+ rpc_result, status);
return ret;
+ }
- if (!boot_stage.rmcall_token) {
- u32 token = 0;
+ /* (c) SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX (nvidia-push-init.c:303-316). */
+ rpc_result = 0;
+ status = 0;
+ ret = gsp_phys_rmcall_control(bar0, boot_stage.rmcall_channel,
+ GSP_PHYS_RMCALL_CTRL_SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX,
+ &notif_index, sizeof(notif_index), 16,
+ NULL, 0, &rpc_result, &status,
+ "SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX");
+ if (ret) {
+ pr_err("gsp-phys-boot: rm-call: stage 4 aborted before the doorbell: SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX (0xc36f010a index=%u) failed (rpc_result=0x%x status=0x%x)\n",
+ notif_index, rpc_result, status);
+ return ret;
+ }
- ret = gsp_phys_rmcall_control(
- bar0, boot_stage.rmcall_channel,
- GSP_PHYS_RMCALL_CTRL_GET_WORK_SUBMIT_TOKEN,
- &token, sizeof(token), 28, &token, sizeof(token),
- NULL, "GET_WORK_SUBMIT_TOKEN(after schedule)");
- if (ret) {
- pr_err("gsp-phys-boot: rm-call: no work-submit token after GPFIFO_SCHEDULE; refusing to ring the doorbell\n");
- return ret;
- }
- boot_stage.rmcall_token = token;
+ /*
+ * (d) GET_WORK_SUBMIT_TOKEN. The helper copies the echoed params into
+ * token before checking status, so a nonzero token can be recovered
+ * from a failed (0x40) reply: the server writes the token into the
+ * caller's params before the notifier update (kernel_channel.c:3471-
+ * 3477). A zero token on failure still aborts.
+ */
+ token = 0;
+ rpc_result = 0;
+ status = 0;
+ ret = gsp_phys_rmcall_control(bar0, boot_stage.rmcall_channel,
+ GSP_PHYS_RMCALL_CTRL_GET_WORK_SUBMIT_TOKEN,
+ &token, sizeof(token), 28, &token,
+ sizeof(token), &rpc_result, &status,
+ "GET_WORK_SUBMIT_TOKEN");
+ if (ret && token != 0) {
+ pr_warn("gsp-phys-boot: rm-call: GET_WORK_SUBMIT_TOKEN failed (rpc_result=0x%x status=0x%x) but the echoed params carry token=0x%x; the server wrote it before the notifier update, proceeding to submission\n",
+ rpc_result, status, token);
+ ret = 0;
}
+ if (ret) {
+ pr_err("gsp-phys-boot: rm-call: stage 4 aborted before the doorbell: GET_WORK_SUBMIT_TOKEN (0xc36f0108 after BIND/SCHEDULE/notif-index=%u) failed (rpc_result=0x%x status=0x%x token=0x%x); refusing to ring the doorbell\n",
+ notif_index, rpc_result, status, token);
+ return ret;
+ }
+ if (!token) {
+ pr_err("gsp-phys-boot: rm-call: stage 4 aborted before the doorbell: GET_WORK_SUBMIT_TOKEN returned a zero token; refusing to ring the doorbell\n");
+ return -EIO;
+ }
+ boot_stage.rmcall_token = token;
pb_va = gsp_phys_chunk_page_va(pb, 0);
ring_va = gsp_phys_chunk_page_va(ring, 0);
@@ -5142,12 +5399,26 @@ static int gsp_phys_rmcall_stage4(void __iomem *bar0)
dma_wmb();
put_unaligned_le32((gpput + 1) % GSP_PHYS_RMCALL_GPFIFO_ENTRIES,
(u8 *)userd + GSP_PHYS_RMCALL_USERD_GPPUT_OFF);
+ /*
+ * Doorbell value: workSubmitToken = ((runlistId << 16) & 0x7f00000) |
+ * (chId & 0xfff) (kernel_fifo_tu102.c:118-121). The server-generated
+ * token already is that value; log its runlist/ChID decomposition and
+ * write the formula's result (identical unless the token carries stray
+ * high bits, which is logged).
+ */
+ runlist_id = (boot_stage.rmcall_token >> 16) & 0x7fu;
+ ch_id = boot_stage.rmcall_token & 0xfffu;
+ doorbell = ((runlist_id << 16) & 0x7f00000u) | (ch_id & 0xfffu);
+ if (doorbell != boot_stage.rmcall_token)
+ pr_warn("gsp-phys-boot: rm-call: token 0x%x carries bits outside runlistId[22:16]|chId[11:0]; writing the recomposed doorbell 0x%x\n",
+ boot_stage.rmcall_token, doorbell);
wmb();
- writel(boot_stage.rmcall_token, bar0 + GSP_PHYS_RMCALL_DOORBELL_OFF);
+ writel(doorbell, bar0 + GSP_PHYS_RMCALL_DOORBELL_OFF);
- pr_info("gsp-phys-boot: rm-call: submitted: pb=0x%llx words=%u slot=%u (was GPGet=%u GPPut=%u) e0=0x%08x e1=0x%08x token=0x%08x doorbell@0x%x; polling sem=0x%llx for 0x%08x (<=%u ms)\n",
+ pr_info("gsp-phys-boot: rm-call: submitted: pb=0x%llx words=%u slot=%u (was GPGet=%u GPPut=%u) e0=0x%08x e1=0x%08x token=0x%08x (runlistId=%u chId=0x%x) doorbell=0x%08x @0x%x; polling sem=0x%llx for 0x%08x (<=%u ms)\n",
pb_dma, words, slot, gpget, gpput, e0, e1,
- boot_stage.rmcall_token, GSP_PHYS_RMCALL_DOORBELL_OFF, sem_dma,
+ boot_stage.rmcall_token, runlist_id, ch_id, doorbell,
+ GSP_PHYS_RMCALL_DOORBELL_OFF, sem_dma,
GSP_PHYS_RMCALL_SEM_PAYLOAD, GSP_PHYS_RMCALL_TIMEOUT_MS);
deadline = jiffies + msecs_to_jiffies(GSP_PHYS_RMCALL_TIMEOUT_MS);
@@ -5320,7 +5591,9 @@ static int gsp_phys_rmcall_run(void __iomem *bar0, u32 target)
/*
* Free every ladder chunk and clear all ladder state, so a re-prime in the
* same module load starts from zero (no stale server handles/tokens/stage).
- * Safe on a partially built ladder.
+ * Safe on a partially built ladder. The FBMEM error-notifier is a server
+ * object like the VAS/TSG/channel (no host chunk); only its state is cleared,
+ * teardown is UNLOAD + rmmod.
*/
static void gsp_phys_rmcall_fini(struct pci_dev *pdev)
{
@@ -5419,7 +5692,7 @@ static int gsp_phys_state_show(struct seq_file *m, void *v)
boot_stage.rm_probe_last_function,
boot_stage.rm_probe_last_payload_len);
seq_printf(m,
- "rm_call: ran=%u stage=%u last_stage=%u last_ret=%d rpcs=%u hClient=0x%x hDevice=0x%x hSubdevice=0x%x hVas=0x%x hTsg=0x%x hChannel=0x%x variant=V%u token=0x%x\n",
+ "rm_call: ran=%u stage=%u last_stage=%u last_ret=%d rpcs=%u hClient=0x%x hDevice=0x%x hSubdevice=0x%x hVas=0x%x hTsg=0x%x hChannel=0x%x variant=V%u token=0x%x notifier=%s base=0x%llx\n",
boot_stage.rmcall_ran, boot_stage.rmcall_stage,
boot_stage.rmcall_last_stage, boot_stage.rmcall_last_ret,
boot_stage.rmcall_rpcs, boot_stage.rmcall_client,
@@ -5427,7 +5700,9 @@ static int gsp_phys_state_show(struct seq_file *m, void *v)
boot_stage.rmcall_vas, boot_stage.rmcall_tsg,
boot_stage.rmcall_channel,
boot_stage.rmcall_channel_variant,
- boot_stage.rmcall_token);
+ boot_stage.rmcall_token,
+ boot_stage.rmcall_notifier_valid ? "FBMEM" : "none",
+ boot_stage.rmcall_notifier_base);
seq_printf(m,
"wpr2: LO=0x%08x (VAL=0x%x) HI=0x%08x (VAL=0x%x) state=%s\n",
wpr2_lo, gsp_phys_fwsec_wpr2_lo_val(bar0),
@@ -6271,6 +6546,12 @@ static_assert(offsetof(struct gsp_phys_rmcall_alloc_memory_req, pte) == 56,
"rpc_alloc_memory_v13_01 first PTE must be at payload offset 56");
static_assert(sizeof(struct gsp_phys_rmcall_mem_desc) == 24,
"NV_MEMORY_DESC_PARAMS must be 24 bytes");
+static_assert(sizeof(struct gsp_phys_rmcall_memory_alloc_params) == 128,
+ "NV_MEMORY_ALLOCATION_PARAMS must be 128 bytes (reference ABI)");
+static_assert(offsetof(struct gsp_phys_rmcall_memory_alloc_params, size) == 64,
+ "NV_MEMORY_ALLOCATION_PARAMS.size must be at offset 64");
+static_assert(offsetof(struct gsp_phys_rmcall_memory_alloc_params, offset) == 80,
+ "NV_MEMORY_ALLOCATION_PARAMS.offset [IN/OUT] must be at offset 80");
static_assert(sizeof(struct gsp_phys_rmcall_vas_params) == 48,
"NV_VASPACE_ALLOCATION_PARAMETERS must be 48 bytes (reference ABI)");
static_assert(sizeof(struct gsp_phys_rmcall_pdb_params) == 32,
@@ -6301,6 +6582,10 @@ static_assert(offsetof(struct gsp_phys_rmcall_channel_params, h_phys_channel_gro
"NV_CHANNEL_ALLOC_PARAMS.hPhysChannelGroup must be at offset 240");
static_assert(offsetof(struct gsp_phys_rmcall_channel_params, internal_flags) == 244,
"NV_CHANNEL_ALLOC_PARAMS.internalFlags must be at offset 244");
+static_assert(offsetof(struct gsp_phys_rmcall_channel_params, error_notifier_mem) == 248,
+ "NV_CHANNEL_ALLOC_PARAMS.errorNotifierMem (FBMEM on GSP) must be at offset 248");
+static_assert(offsetof(struct gsp_phys_rmcall_channel_params, ecc_error_notifier_mem) == 272,
+ "NV_CHANNEL_ALLOC_PARAMS.eccErrorNotifierMem must be at offset 272");
static_assert(offsetof(struct gsp_phys_rmcall_channel_params, process_id) == 296,
"NV_CHANNEL_ALLOC_PARAMS.ProcessID must be at offset 296");
static_assert(offsetof(struct gsp_phys_rmcall_channel_params, tpc_config_id) == 360,