summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-05 21:01:17 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-05 21:01:17 -0400
commit3c9d31151b8aa2d9e789de6787b1f18b84bb09d3 (patch)
treebbe62306c8f18175e3a31009770729ceccd2b004
parentfed5e574a3b326c384d5960dae9b7b7b5f94d9be (diff)
downloadopen-iov-3c9d31151b8aa2d9e789de6787b1f18b84bb09d3.tar.gz
open-iov-3c9d31151b8aa2d9e789de6787b1f18b84bb09d3.zip
vgsp: contiguous shadow PTEs for multi-page GR buffers (R4)
The mail launch's param buffer spans two pages but the QMD names only the base page. The second page has no pointer word, so the Tier-2 shadow path skipped it and the launch was refused. Map a page with no pointer word that is contiguous with the previous shadow-mapped page into the next PTE of the same 128 B fetch line (each line covers up to 16 consecutive 4 KB pages), keeping the rewritten base-pointer buffer contiguous for the engine.
-rw-r--r--kernel/gsp_phys_booter.c68
1 files changed, 67 insertions, 1 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index d90a683..a008754 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -12943,6 +12943,42 @@ static int gsp_phys_rmcall_gr_map_shadow_locked(u32 slot, u64 hpa,
return 0;
}
+/*
+ * Tier-2 contiguity: write one more 4 KB page into an already-consumed
+ * shadow fetch line at PTE `pte` (0..CACHE_PTES-1) of that line, so a
+ * multi-page guest buffer whose base pointer was rewritten to the line base
+ * VA keeps its pages contiguous (the engine reads base+offset). Returns the
+ * page's VA. Called with gsp_phys_rm_lock held.
+ */
+static int gsp_phys_rmcall_gr_map_shadow_contig_locked(u32 line, u32 pte,
+ u64 hpa, u64 *va_out)
+{
+ struct gsp_phys_boot_chunk *pt;
+ u32 page, line_in_page;
+ u8 *pt_va;
+
+ if (pte >= GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES)
+ return -EINVAL;
+ if (line >= GSP_PHYS_RMCALL_GUEST_PT_PAGES *
+ GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE)
+ return -ENOSPC;
+ page = line / GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE;
+ line_in_page = line % GSP_PHYS_RMCALL_GUEST_PT_LINES_PER_PAGE;
+ pt = &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_GUEST_PT0 + page];
+ if (!pt->pages)
+ return -EIO;
+ pt_va = gsp_phys_chunk_page_va(pt, 0);
+ if (!pt_va)
+ return -EIO;
+ put_unaligned_le64(gsp_phys_rmcall_pte(hpa),
+ pt_va + (size_t)(GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES *
+ line_in_page + pte) * 8);
+ dma_wmb();
+ *va_out = boot_stage.rmcall_va_guest_pt + ((u64)page << 21) +
+ ((u64)line_in_page << 16) + (u64)pte * GSP_PHYS_PAGE_SIZE;
+ return 0;
+}
+
/* GR forward fault observation is armed by the GR parameters, not the CE
* ones (gsp_phys_fault_relay_active() requires gsp_phys_work_forward). */
static bool gsp_phys_gr_fault_active(void)
@@ -13082,8 +13118,16 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
* Pages: Tier 1 maps the resolved HPA at the guest VA; a page without
* a usable Tier-1 mapping falls to Tier 2 when captured inline bytes
* cover it and a QMD pointer pair names it (the pair is rewritten to
- * the shadow VA); anything else refuses the launch.
+ * the shadow VA); anything else refuses the launch. A page with no
+ * pointer pair of its own that is contiguous with the previous
+ * shadow-mapped page gets the next PTE of the same shadow line, so a
+ * multi-page buffer the QMD names once stays contiguous.
*/
+ {
+ u64 last_shadow_va = 0, last_guest_va = 0;
+ u32 last_shadow_line = 0;
+ bool last_shadow_valid = false;
+
for (i = 0; i < req->n_pages; i++) {
const struct gsp_phys_gr_page *pg = &req->pages[i];
u64 pg_va, sh_va = 0;
@@ -13104,6 +13148,20 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
else if (ret == -ENOSPC)
gsp_phys_gr_pool_exhausted++;
}
+ if (!mapped && pg->hpa && last_shadow_valid &&
+ pg->qmd_word == GSP_PHYS_GR_QMD_WORD_NONE &&
+ pg_va == last_guest_va + GSP_PHYS_PAGE_SIZE) {
+ u32 pte = (u32)((last_shadow_va >> 12) & 0xfu);
+ u64 va = 0;
+
+ if (pte + 1 < GSP_PHYS_RMCALL_GUEST_PT_CACHE_PTES &&
+ !gsp_phys_rmcall_gr_map_shadow_contig_locked(
+ last_shadow_line, pte + 1, pg->hpa, &va)) {
+ mapped = true;
+ last_shadow_va = va;
+ last_guest_va = pg_va;
+ }
+ }
if (!mapped && pg->qmd_word != GSP_PHYS_GR_QMD_WORD_NONE &&
pg->qmd_word + 1 < GSP_PHYS_GR_QMD_WORDS) {
struct gsp_phys_boot_chunk *sh =
@@ -13162,6 +13220,9 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
copied++;
}
if (copied) {
+ u32 sh_line =
+ boot_stage.rmcall_guest_pt_next_line;
+
ret = gsp_phys_rmcall_gr_map_shadow_locked(
n_shadow,
(u64)gsp_phys_chunk_page_dma(sh,
@@ -13179,6 +13240,10 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
~0x1ffffu) |
((u32)(ptr_va >> 32) & 0x1ffffu);
mapped = true;
+ last_shadow_line = sh_line;
+ last_shadow_va = sh_va;
+ last_guest_va = pg_va;
+ last_shadow_valid = true;
}
}
}
@@ -13193,6 +13258,7 @@ gr_page_unmapped:
goto out;
}
}
+ }
/*
* Channel release: author it into the guest semaphore page when the