From fda77c731e1d96eaf9aceb264f3415d51a26d39b Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Mon, 5 Oct 2026 21:46:02 -0400 Subject: vgsp: point inline-QMD launch at the guest QMD buffer + GR post-mortem logging --- kernel/gsp_phys_booter.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++- kernel/gsp_phys_booter.h | 3 +- kernel/gsp_sema_service.c | 54 +++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-) diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index b95bd81..7113663 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -13024,6 +13024,7 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req, bool qmd_rel = false, ch_rel = false; u64 qmd_rel_hpa = 0, ch_rel_hpa = 0; u64 ch_sem_va = GSP_PHYS_RMCALL_VA_SEM + 4; + u64 qmd_dst = GSP_PHYS_RMCALL_VA_SRC; u32 qmd_rel_off = 0, ch_rel_off = 0; u32 poll = 0, w0 = 0, w1 = 0; int ret = 0; @@ -13350,6 +13351,38 @@ gr_page_unmapped: } } + /* + * Inline-QMD destination: the CUDA driver points ADDRESS_A/B at its + * own QMD buffer (turing_launch.c:1629) and the IQ2M copies the 64 + * inline words there before scheduling. Map the guest QMD page and + * use it as the destination when the caller resolved it; a Tier-1 + * install keeps the guest VA (the guest sees its own QMD), a Tier-2 + * alias keeps the launch runnable, and an unresolved page keeps the + * module scratch VA. + */ + if (req->qmd_hpa) { + u64 qmd_page = req->qmd_va & ~(u64)(GSP_PHYS_PAGE_SIZE - 1); + + if (qmd_page && + !gsp_phys_rmcall_gr_map_va_locked(bar0, qmd_page, + req->qmd_hpa, epoch)) { + qmd_dst = qmd_page; + } else { + u64 sh_va = 0; + + if (n_shadow < GSP_PHYS_RMCALL_GR_SHADOW_PAGES && + !gsp_phys_rmcall_gr_map_shadow_locked( + n_shadow, req->qmd_hpa, &sh_va)) { + n_shadow++; + qmd_dst = sh_va + + (req->qmd_va & + (GSP_PHYS_PAGE_SIZE - 1)); + } + } + pr_info("vgsp-gr: inline-QMD dest va=0x%llx hpa=0x%llx -> 0x%llx (shadow=%u)\n", + req->qmd_va, req->qmd_hpa, qmd_dst, n_shadow); + } + /* * Channel release: author it into the guest semaphore page when the * caller resolved one, else into the module semaphore's second word. @@ -13380,9 +13413,12 @@ gr_page_unmapped: wmb(); words = gsp_phys_rmcall_build_gr_launch(methods, qmd, - GSP_PHYS_RMCALL_VA_SRC, + qmd_dst, ch_sem_va, want_ch); memcpy(gsp_phys_chunk_page_va(pb, 0), methods, words * 4); + pr_info("vgsp-gr: qmd w0..3=%08x %08x %08x %08x w4..7=%08x %08x %08x %08x w23..25=%08x %08x %08x w32/33=%08x %08x w48/49=%08x %08x\n", + qmd[0], qmd[1], qmd[2], qmd[3], qmd[4], qmd[5], qmd[6], qmd[7], + qmd[23], qmd[24], qmd[25], qmd[32], qmd[33], qmd[48], qmd[49]); ret = gsp_phys_rmcall_publish_locked(bar0, boot_stage.rmcall_va_pb, words, &sub); @@ -13436,6 +13472,41 @@ gr_page_unmapped: } } } + /* + * Post-mortem: if the IQ2M landed the QMD in the guest + * buffer, the inline window was processed; GP_GET shows + * the front-end consumed the entry; the release word is + * re-read one last time. + */ + { + u8 *ud = gsp_phys_chunk_page_va( + &boot_stage.rmcall_chunks + [GSP_PHYS_RMCALL_BUF_USERD], + 0); + u32 gpget_post = ud ? + get_unaligned_le32( + ud + GSP_PHYS_RMCALL_USERD_GPGET_OFF) + : 0; + u32 q0 = 0, q1 = 0, q23 = 0, q24 = 0, q25 = 0; + u32 relw = 0; + + if (req->qmd_hpa) { + u8 *qp = (u8 *)__va(req->qmd_hpa); + + q0 = get_unaligned_le32(qp); + q1 = get_unaligned_le32(qp + 4); + q23 = get_unaligned_le32(qp + 92); + q24 = get_unaligned_le32(qp + 96); + q25 = get_unaligned_le32(qp + 100); + } + if (qmd_rel_hpa) + relw = READ_ONCE( + *(u32 *)((u8 *)__va(qmd_rel_hpa) + + qmd_rel_off)); + pr_info("vgsp-gr: post-mortem gpget=%u->%u qmd_page w0=%08x w1=%08x w23..25=%08x %08x %08x relw=%08x\n", + sub.gpget, gpget_post, q0, q1, q23, q24, + q25, relw); + } poll = w1; ret = -ETIMEDOUT; goto out; diff --git a/kernel/gsp_phys_booter.h b/kernel/gsp_phys_booter.h index 73c21ad..42bf1ff 100644 --- a/kernel/gsp_phys_booter.h +++ b/kernel/gsp_phys_booter.h @@ -353,7 +353,8 @@ struct gsp_phys_gr_inline { struct gsp_phys_gr_submit { const u32 *qmd; /* 64 rewritten-QMD source words */ u32 qmd_words; /* must be GSP_PHYS_GR_QMD_WORDS */ - u64 qmd_va; /* guest VA of the QMD (logging only) */ + u64 qmd_va; /* guest VA of the QMD buffer */ + u64 qmd_hpa; /* guest QMD buffer page HPA, or 0 */ const struct gsp_phys_gr_page *pages; u32 n_pages; const struct gsp_phys_gr_inline *inl; diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index b43b222..7467b45 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "nvidia-vgpu-vfio.h" #include "gsp_sema_service.h" @@ -3330,6 +3331,21 @@ static void svc_gr_cand_capture(struct svc_channel *ch, svc_gr_cand.param_size, svc_gr_cand.rel_va, svc_gr_cand.rel_payload, svc_gr_cand.rel_flags, svc_gr_cand.n_pointer, svc_gr_cand.n_inline); + if (push && push_len >= 16) { + u32 n = push_len / 4; + char buf[6 * 16 + 1]; + u32 i, o = 0; + + if (n > 32) + n = 32; + for (i = 0; i < n; i++) { + o += scnprintf(buf + o, sizeof(buf) - o, "%08x ", + get_unaligned_le32(push + i * 4)); + if (o >= sizeof(buf) - 12) + break; + } + pr_info("sema-gr: push[%u dwords]: %s\n", n, buf); + } } /* @@ -7987,6 +8003,15 @@ struct svc_fwd_req { u64 gr_push_va; u32 gr_push_len; u64 gr_qmd_dev_va; + /* + * The QMD buffer page (the inline-QMD destination the CUDA driver + * uses), resolved VA->guest PA on the service thread. The work item + * turns gr_qmd_pa into an HPA; the backend maps that page and points + * SET_INLINE_QMD_ADDRESS_A/B at it so the IQ2M copies the QMD into + * the guest's own buffer, exactly like the driver's qmd->devVA. + */ + u64 gr_qmd_pa; + u8 gr_qmd_ap; /* SVC_APERTURE_* of gr_qmd_pa */ u32 gr_rel_flags; u64 gr_rel_va; u32 gr_rel_payload; @@ -8873,6 +8898,18 @@ static void svc_fwd_gr_work(void) gs.qmd = svc_fwd_req.gr_qmd; gs.qmd_words = SVC_GR_QMD_WORDS; gs.qmd_va = svc_fwd_req.gr_qmd_dev_va; + gs.qmd_hpa = 0; + if (svc_fwd_req.gr_qmd_pa) { + u8 qkind = (svc_fwd_req.gr_qmd_ap == SVC_APERTURE_FB) ? + SVC_APERTURE_FB : SVC_APERTURE_SYS; + u64 qhpa = 0; + u32 qoff = 0; + + if (!svc_fwd_operand_hpa(svc_fwd_req.vgpu, qkind, + svc_fwd_req.gr_qmd_pa, 0, + SEMA_PAGE_SIZE, &qhpa, &qoff) && !qoff) + gs.qmd_hpa = qhpa; + } gs.pages = pages; gs.n_pages = svc_fwd_req.gr_n_pages; gs.inl = (const struct gsp_phys_gr_inline *)svc_fwd_req.gr_inline; @@ -9229,6 +9266,23 @@ static int svc_gr_req_fill(struct nv_vgpu_device *vgpu, svc_fwd_req.gr_push_va = svc_gr_cand.push_va; svc_fwd_req.gr_push_len = svc_gr_cand.push_len; svc_fwd_req.gr_qmd_dev_va = svc_gr_cand.qmd_dev_va; + /* + * Resolve the QMD buffer page now, on the service thread. A miss + * leaves gr_qmd_pa 0 and the backend keeps the module scratch page as + * the inline-QMD destination (fail-open; the launch can still run). + */ + svc_fwd_req.gr_qmd_pa = 0; + svc_fwd_req.gr_qmd_ap = SVC_APERTURE_SYS; + if (svc_gr_cand.qmd_dev_va) { + u64 qpa = 0; + u32 qap = SVC_APERTURE_SYS; + + if (!svc_resolve_va(vgpu, svc_gr_cand.qmd_dev_va, &qpa, &qap)) { + svc_fwd_req.gr_qmd_pa = + qpa & ~(u64)(SEMA_PAGE_SIZE - 1); + svc_fwd_req.gr_qmd_ap = (u8)svc_ap_of(qap); + } + } svc_fwd_req.gr_rel_flags = svc_gr_cand.rel_flags; svc_fwd_req.gr_rel_va = svc_gr_cand.rel_va; svc_fwd_req.gr_rel_payload = svc_gr_cand.rel_payload; -- cgit v1.2.3