diff options
| -rw-r--r-- | kernel/gsp_phys_booter.c | 246 | ||||
| -rw-r--r-- | kernel/gsp_phys_booter.h | 17 |
2 files changed, 166 insertions, 97 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index 69a749a..94b0cae 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -311,20 +311,19 @@ bool gsp_phys_boot_requested(void) /* * P6a guest-memory windows (gsp_phys_guest_mem=1). Two more slots in the same * 2 MB PD0 slot / small-PT page as the five ladder pages, so VA_GUEST_W takes - * PT slot 0x105 and VA_GUEST_R slot 0x106. When a guest GPA was already - * pinned before stage 3 (gsp-phys-guest-gpa, gsp_phys_rmcall_pdb_build runs - * before any engine access) both slots are pre-built with the translated - * guest HPA there: the engine's GMMU caches the PTE fetch line covering slots - * 0x100..0x107 during the stage-4/P5 walks and no invalidate exists, so a - * later runtime write to one of these slots could be invisible and the copy - * would time out. Triggers 6/7 keep programming their slot as the fallback - * path (a GPA pinned after stage 3 has only that route). Without a pin the - * slots stay zero: a zero small PTE is invalid, so an engine fetch through - * them faults (it can never fall through to a stale mapping from a previous - * GPA). Trigger 7 programs the read slot and copies guest -> ladder DST; - * trigger 6 programs the write slot and copies ladder SRC -> guest. The - * walk-up indices (root/PD2/PD1/PD0) are unchanged because both VAs still - * live inside the same 2 MB PD0 slot. + * PT slot 0x105 and VA_GUEST_R slot 0x106. These VAs are legacy: triggers 6/7 + * no longer translate through them and no longer program them at runtime. + * Both directions now resolve the GPA->HPA at trigger time and run the copy + * through the P6b fresh-scratch-line pool (internal trigger variant of + * gsp_phys_backend_guest_ce_copy), one never-reused 128 B fetch line per + * pair, so a host PA that moved since the pin cannot be shadowed by an + * engine-cached PTE fetch line. The stage-3 pre-build below + * (gsp_phys_rmcall_pdb_build) is kept harmless: it may still fill the slots + * when a GPA was pinned before stage 3, but the triggers no longer depend on + * it. Without a pin the slots stay zero: a zero small PTE is invalid, so an + * engine fetch through them faults rather than falling through to a stale + * mapping from a previous GPA. The walk-up indices (root/PD2/PD1/PD0) are + * unchanged because both VAs still live inside the same 2 MB PD0 slot. */ #define GSP_PHYS_RMCALL_VA_GUEST_W (GSP_PHYS_RMCALL_VA_BASE + 0x5000) #define GSP_PHYS_RMCALL_VA_GUEST_R (GSP_PHYS_RMCALL_VA_BASE + 0x6000) @@ -1030,10 +1029,11 @@ static struct gsp_phys_boot_stage { /* * P6a guest-memory pin (debugfs gsp-phys-guest-gpa) and proof flags * (ladder triggers 6/7; gsp_phys_guest_mem=1). One GPA per boot: the - * PT slot is programmed with the translated HPA and the engine's GMMU - * may cache that translation, so switching GPAs under a live channel - * would risk a stale-TLB hit; a second different GPA is refused. - * rmcall_guest_hpa keeps the last translation for the state dump, + * guest page's host PA may move under a live channel, so switching + * GPAs would risk a stale engine translation; a second different GPA + * is refused. Triggers 6/7 re-translate the GPA at trigger time and + * copy through fresh 128 B scratch lines, so the held rmcall_guest_hpa + * is only for the state dump and the once-per-boot change note. * rmcall_guest_r_ok records that the G2H direction proved the * translation, rmcall_guest_w2g_ok records the W2G proof, and * rmcall_guest_selftest_ok records that trigger 8 saw a runtime @@ -1042,6 +1042,7 @@ static struct gsp_phys_boot_stage { u64 rmcall_guest_gpa; /* pinned guest GPA (page-aligned) */ bool rmcall_guest_gpa_valid; u64 rmcall_guest_hpa; /* last GPA->HPA translation */ + bool rmcall_guest_hpa_note_logged; /* "guest HPA changed" noted */ bool rmcall_guest_r_ok; /* trigger 7: guest page read proved */ bool rmcall_guest_w2g_ok; /* trigger 6: guest page write proved */ bool rmcall_guest_selftest_ok; /* trigger 8: fresh-line PTE write proved */ @@ -1604,6 +1605,7 @@ static void gsp_phys_rmcall_reset(void) boot_stage.rmcall_guest_gpa = 0; boot_stage.rmcall_guest_gpa_valid = false; boot_stage.rmcall_guest_hpa = 0; + boot_stage.rmcall_guest_hpa_note_logged = false; boot_stage.rmcall_guest_r_ok = false; boot_stage.rmcall_guest_w2g_ok = false; boot_stage.rmcall_guest_selftest_ok = false; @@ -5904,15 +5906,14 @@ static void gsp_phys_rmcall_pdb_build(void) * P6a guest windows: start from explicit zero (invalid PTE) for both * slots, then, when the guest GPA is already pinned (it must be pinned * before stage 3 for this), pre-build both slots with the translated - * guest HPA. The engine's GMMU caches the PTE fetch line covering - * slots 0x100..0x107 during the stage-4/P5 walks and this driver has - * no invalidate, so programming 0x105/0x106 only at trigger time - * could be invisible (the first live attempt timed out that way). - * Building them here, before any engine access, makes the first fetch - * of the line load the final translation. Triggers 6/7 still rewrite - * their slot as the fallback path for a GPA pinned after stage 3. On - * translation failure the slots stay zero so an engine fetch through - * them still faults instead of ever reusing a stale mapping. + * guest HPA. Triggers 6/7 no longer depend on these slots or on this + * pre-build: they re-translate the GPA at trigger time and run both + * directions through the P6b fresh 128 B scratch-line pool, so a host + * PA that changed since this stage cannot be masked by the cached PTE + * fetch line covering slots 0x100..0x107. The pre-build is kept + * harmless. On translation failure the slots stay zero so an engine + * fetch through them still faults instead of ever reusing a stale + * mapping. */ put_unaligned_le64(0, (u8 *)gsp_phys_chunk_page_va(pt, 0) + (size_t)((GSP_PHYS_RMCALL_VA_GUEST_W >> 12) & 0x1ffu) * 8); @@ -6409,10 +6410,16 @@ static u64 gsp_phys_work_released; static u64 gsp_phys_work_timed_out; static u64 gsp_phys_work_refused; -bool gsp_phys_backend_work_ready(void) +/* + * Ladder/backend readiness shared by the P5 shadow API and the P6a trigger + * guest copies: READY backend session, live claim, no unload committed, a + * built client-memory channel, a work-submit token and all ladder VAs. The + * P5/P6b forwarding APIs additionally require gsp_phys_work_forward (see + * gsp_phys_backend_work_ready below); triggers 6/7 must run with forwarding + * off, so their guest-copy variant uses this gate directly. + */ +static bool gsp_phys_backend_ladder_ready(void) { - if (!gsp_phys_work_forward_enabled()) - return false; if (gsp_phys_backend_state() != GSP_PHYS_BACKEND_READY) return false; if (!gsp_phys_gpu_claimed() || !boot_stage.rm_init_done || @@ -6427,6 +6434,13 @@ bool gsp_phys_backend_work_ready(void) return false; return true; } + +bool gsp_phys_backend_work_ready(void) +{ + if (!gsp_phys_work_forward_enabled()) + return false; + return gsp_phys_backend_ladder_ready(); +} EXPORT_SYMBOL_GPL(gsp_phys_backend_work_ready); bool gsp_phys_backend_work_operands(u64 *src_va, u64 *dst_va, u64 *sem_va, @@ -6664,8 +6678,20 @@ static int gsp_phys_rmcall_guest_map_locked(u64 src_hpa, u64 dst_hpa, return 0; } -int gsp_phys_backend_guest_ce_copy(u64 src_hpa, u32 src_off, u64 dst_hpa, - u32 dst_off, u32 bytes, u32 poll_ms) +/* + * Shared body of the P6b forwarding API and the P6a trigger variant. When + * need_work_forward is true this is exactly the exported forwarding gate + * (gsp_phys_work_forward required); when false it keeps every other gate + * (READY backend/ladder, gsp_phys_guest_mem, identity DMA domain, active + * guest-RAM file, operand bounds, fresh never-reused 128 B scratch line, + * module semaphore release, direct-map verify) and only drops the + * gsp_phys_work_forward requirement, so the P6a triggers can share the same + * fresh-line copy path with forwarding. *sem_out receives the last + * semaphore word seen on success or timeout. + */ +static int gsp_phys_guest_ce_copy_common(u64 src_hpa, u32 src_off, u64 dst_hpa, + u32 dst_off, u32 bytes, u32 poll_ms, + bool need_work_forward, u32 *sem_out) { struct gsp_phys_boot_chunk *sem = &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_SEM]; @@ -6679,9 +6705,16 @@ int gsp_phys_backend_guest_ce_copy(u64 src_hpa, u32 src_off, u64 dst_hpa, * Every gate is fail-closed: the sema service's ladder fallback keeps * the software release authoritative, so a refusal never loses work. */ - if (!gsp_phys_backend_work_ready()) { - gsp_phys_guest_refused++; - return -EAGAIN; + if (need_work_forward) { + if (!gsp_phys_backend_work_ready()) { + gsp_phys_guest_refused++; + return -EAGAIN; + } + } else { + if (!gsp_phys_backend_ladder_ready()) { + gsp_phys_guest_refused++; + return -EAGAIN; + } } if (!gsp_phys_guest_mem_enabled() || gsp_phys_dma_domain_identity() != 1 || @@ -6772,6 +6805,8 @@ int gsp_phys_backend_guest_ce_copy(u64 src_hpa, u32 src_off, u64 dst_hpa, break; if (time_after_eq(jiffies, deadline)) { gsp_phys_guest_refused++; + if (sem_out) + *sem_out = poll; pr_err("gsp-phys-work: GUEST CE COPY FAILED: timeout after %u ms (sem=0x%08x want=0x%08x) src_hpa=0x%llx dst_hpa=0x%llx bytes=%u\n", poll_ms, poll, GSP_PHYS_RMCALL_SEM_PAYLOAD, src_hpa, dst_hpa, bytes); @@ -6795,15 +6830,41 @@ int gsp_phys_backend_guest_ce_copy(u64 src_hpa, u32 src_off, u64 dst_hpa, gsp_phys_guest_verify_ok++; gsp_phys_guest_fwd_ok++; + if (sem_out) + *sem_out = poll; if (gsp_phys_guest_fwd_ok == 1 || (gsp_phys_guest_fwd_ok & 63) == 0) pr_info("gsp-phys-work: GUEST CE COPY OK src_hpa=0x%llx dst_hpa=0x%llx bytes=%u sem=0x%08x verify_ok=1 copied=%llu\n", src_hpa, dst_hpa, bytes, poll, gsp_phys_guest_fwd_ok); return 0; } + +int gsp_phys_backend_guest_ce_copy(u64 src_hpa, u32 src_off, u64 dst_hpa, + u32 dst_off, u32 bytes, u32 poll_ms) +{ + return gsp_phys_guest_ce_copy_common(src_hpa, src_off, dst_hpa, dst_off, + bytes, poll_ms, true, NULL); +} EXPORT_SYMBOL_GPL(gsp_phys_backend_guest_ce_copy); /* + * P6a trigger variant: identical to the exported forwarding call except that + * the gsp_phys_work_forward parameter gate is dropped (every other guard and + * the whole fresh-scratch-line copy/verify body are shared). Triggers 6/7 use + * this so they re-translate the pinned GPA at trigger time and never depend + * on the stage-3 pre-built 0x105/0x106 slots. *sem_out carries the observed + * module-semaphore word for the trigger's OK/FAILED logs. + */ +static int gsp_phys_backend_guest_ce_copy_trigger(u64 src_hpa, u32 src_off, + u64 dst_hpa, u32 dst_off, + u32 bytes, u32 poll_ms, + u32 *sem_out) +{ + return gsp_phys_guest_ce_copy_common(src_hpa, src_off, dst_hpa, dst_off, + bytes, poll_ms, false, sem_out); +} + +/* * Stage 4: the corrected token sequence - BIND (COPY0) -> GPFIFO_SCHEDULE -> * TSG_GPFIFO_SCHEDULE -> SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX -> * GET_WORK_SUBMIT_TOKEN - then the first real work item: write the CE method @@ -7090,37 +7151,52 @@ static int gsp_phys_rmcall_stage5(void __iomem *bar0) /* * P6a triggers 6/7 (gsp_phys_guest_mem=1): prove the real CE engine can read - * and write one pinned guest RAM page. The direction order is deliberate: - * trigger 7 (G2H, guest read) is the translation proof - it resolves the GPA - * through the mdev's VFIO type-1 domain, requires the physical device's own - * domain to be IOMMU_DOMAIN_IDENTITY (the module programs host PAs into the - * engine page tables), programs the read window PT slot, poisons the ladder - * DST page and copies 4 KB guest -> DST. Only after G2H proved the - * translation may trigger 6 (W2G, guest write) program the write slot, fill - * the ladder SRC page with the 0x5a^(i&0xff) pattern and copy 4 KB SRC -> - * guest. Both run the shared bounded submit/poll helper; refusals are logged - * explicitly and never retried. + * and write one pinned guest RAM page. Both directions resolve the GPA->HPA + * through the mdev's VFIO type-1 domain at trigger time and run the copy + * through the internal trigger variant of the P6b fresh-scratch-line guest + * copy (gsp_phys_backend_guest_ce_copy_trigger): one whole never-reused + * 128 B GMMU PTE fetch line per pair, so a host PA that changed since the pin + * (observed live: the same guest GPA translated to a different HPA between + * ladder stage 3 and trigger time) lands in a line the engine has never + * fetched and cannot be shadowed by an engine-cached translation. The + * stage-3 pre-built 0x105/0x106 slots are no longer used by these triggers. + * + * The direction order is deliberate: trigger 7 (G2H, guest read) is the + * translation proof - it requires the physical device's own domain to be + * IOMMU_DOMAIN_IDENTITY (the module programs host PAs into the engine page + * tables), poisons the ladder DST page and copies 4 KB guest -> DST. Only + * after G2H proved the translation may trigger 6 (W2G, guest write) fill the + * ladder SRC page with the 0x5a^(i&0xff) pattern and copy 4 KB SRC -> guest. + * Both use the shared bounded submit/poll/verify body; refusals are logged + * explicitly and never retried. An HPA that differs from the last successful + * translation is informational (one "guest HPA changed" note per boot), not + * a failure: the fresh-line re-translation is the point of this path, while + * the guest probe pattern guard below still decides whether the page may be + * written. * - * The direct-map comparisons below (memcmp against __va(hpa)) are safe: the + * The direct-map comparisons (memcmp against __va(hpa)) are safe: the * physical device is in an identity domain (checked above) and * gsp_phys_gpa_to_hpa() validated the PFN (pfn_valid) of the pinned host PA, - * so __va(hpa) is a valid kernel mapping. The PT slot program + dma_wmb() - * precede the submit under the human-driven debugfs interface. When the GPA - * was pinned before stage 3, gsp_phys_rmcall_pdb_build() already pre-built - * both slots before any engine walk (the cached-fetch-line fix); the trigger - * rewrite below remains the fallback path for a GPA pinned after stage 3 and - * re-programs exactly one slot per run. + * so __va(hpa) is a valid kernel mapping. */ +static void gsp_phys_rmcall_guest_hpa_note(u64 gpa, u64 hpa) +{ + if (!boot_stage.rmcall_guest_hpa || + boot_stage.rmcall_guest_hpa == hpa || + boot_stage.rmcall_guest_hpa_note_logged) + return; + boot_stage.rmcall_guest_hpa_note_logged = true; + pr_info("gsp-phys-boot: rm-call: guest HPA changed: gpa=0x%llx was 0x%llx now 0x%llx (re-translated at trigger time; informational, not a failure)\n", + gpa, boot_stage.rmcall_guest_hpa, hpa); +} + static int gsp_phys_rmcall_guest_g2h(void __iomem *bar0) { - struct gsp_phys_boot_chunk *pt = - &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_PT]; struct gsp_phys_boot_chunk *dst = &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_DST]; u64 gpa = boot_stage.rmcall_guest_gpa; u64 hpa = 0; u64 pte = 0; - u32 pt_idx = (u32)((GSP_PHYS_RMCALL_VA_GUEST_R >> 12) & 0x1ffu); u32 sem = 0, i; u8 *dst_page; int dom, ret; @@ -7139,29 +7215,24 @@ static int gsp_phys_rmcall_guest_g2h(void __iomem *bar0) return -EIO; } + /* pte is log-only now: the copy runs through a fresh scratch line. */ pte = gsp_phys_rmcall_pte(hpa); - boot_stage.rmcall_guest_hpa = hpa; + gsp_phys_rmcall_guest_hpa_note(gpa, hpa); /* Poison the destination so a no-op copy cannot pass by accident. */ - memset(gsp_phys_chunk_page_va(dst, 0), 0xc3, - GSP_PHYS_RMCALL_COPY_SIZE); - - put_unaligned_le64(pte, (u8 *)gsp_phys_chunk_page_va(pt, 0) + - (size_t)pt_idx * 8); - dma_wmb(); + dst_page = gsp_phys_chunk_page_va(dst, 0); + memset(dst_page, 0xc3, GSP_PHYS_RMCALL_COPY_SIZE); - ret = gsp_phys_rmcall_ce_copy_va(bar0, GSP_PHYS_RMCALL_VA_GUEST_R, - GSP_PHYS_RMCALL_VA_DST, - GSP_PHYS_RMCALL_VA_SEM, - GSP_PHYS_RMCALL_COPY_SIZE, &sem); + ret = gsp_phys_backend_guest_ce_copy_trigger( + hpa, 0, (u64)gsp_phys_chunk_page_dma(dst, 0), 0, + GSP_PHYS_RMCALL_COPY_SIZE, GSP_PHYS_RMCALL_GUEST_POLL_MS, &sem); if (ret) { pr_err("gsp-phys-boot: rm-call: GUEST G2H COPY FAILED: hpa=0x%llx pte=0x%016llx: CE submit/poll failed (%d, sem=0x%08x want=0x%08x)\n", hpa, pte, ret, sem, GSP_PHYS_RMCALL_SEM_PAYLOAD); return -EIO; } - if (memcmp(gsp_phys_chunk_page_va(dst, 0), __va(hpa), - GSP_PHYS_RMCALL_COPY_SIZE) != 0) { + if (memcmp(dst_page, __va(hpa), GSP_PHYS_RMCALL_COPY_SIZE) != 0) { pr_err("gsp-phys-boot: rm-call: GUEST G2H COPY FAILED: hpa=0x%llx pte=0x%016llx: semaphore released (sem=0x%08x) but DST does not match the guest page at hpa=0x%llx (gpa=0x%llx)\n", hpa, pte, sem, hpa, gpa); return -EIO; @@ -7175,7 +7246,6 @@ static int gsp_phys_rmcall_guest_g2h(void __iomem *bar0) * the translated page is the guard that makes a wrong translation * detectable before any engine write to guest memory is allowed. */ - dst_page = gsp_phys_chunk_page_va(dst, 0); for (i = 0; i < GSP_PHYS_RMCALL_COPY_SIZE; i++) { if (dst_page[i] != (u8)(0xa5 ^ (i & 0xff))) { /* Dump both heads to tell a bad translation from a bad copy. */ @@ -7185,6 +7255,7 @@ static int gsp_phys_rmcall_guest_g2h(void __iomem *bar0) } } + boot_stage.rmcall_guest_hpa = hpa; boot_stage.rmcall_guest_r_ok = true; pr_info("gsp-phys-boot: rm-call: GUEST G2H COPY OK gpa=0x%llx hpa=0x%llx bytes=%u sem=0x%08x\n", gpa, hpa, GSP_PHYS_RMCALL_COPY_SIZE, sem); @@ -7193,14 +7264,11 @@ static int gsp_phys_rmcall_guest_g2h(void __iomem *bar0) static int gsp_phys_rmcall_guest_w2g(void __iomem *bar0) { - struct gsp_phys_boot_chunk *pt = - &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_PT]; struct gsp_phys_boot_chunk *src = &boot_stage.rmcall_chunks[GSP_PHYS_RMCALL_BUF_SRC]; u64 gpa = boot_stage.rmcall_guest_gpa; u64 hpa = 0; u64 pte = 0; - u32 pt_idx = (u32)((GSP_PHYS_RMCALL_VA_GUEST_W >> 12) & 0x1ffu); u32 sem = 0, i; u8 *src_page; int dom, ret; @@ -7230,14 +7298,16 @@ static int gsp_phys_rmcall_guest_w2g(void __iomem *bar0) return -EIO; } + /* pte is log-only now: the copy runs through a fresh scratch line. */ pte = gsp_phys_rmcall_pte(hpa); - boot_stage.rmcall_guest_hpa = hpa; + gsp_phys_rmcall_guest_hpa_note(gpa, hpa); /* * The page at hpa must still carry the guest probe pattern that G2H * proved (P6a debugfs contract with tools/test/gpage_test.c); if it * changed, the re-translate produced a stale/incorrect hpa and the - * engine must not write to guest memory. + * engine must not write to guest memory. A changed HPA itself is not a + * failure (the note above records it): the pattern is the guard. */ for (i = 0; i < GSP_PHYS_RMCALL_COPY_SIZE; i++) { if (((const u8 *)__va(hpa))[i] != (u8)(0xa5 ^ (i & 0xff))) { @@ -7252,14 +7322,9 @@ static int gsp_phys_rmcall_guest_w2g(void __iomem *bar0) for (i = 0; i < GSP_PHYS_RMCALL_COPY_SIZE; i++) src_page[i] = (u8)(0x5a ^ (i & 0xff)); - put_unaligned_le64(pte, (u8 *)gsp_phys_chunk_page_va(pt, 0) + - (size_t)pt_idx * 8); - dma_wmb(); - - ret = gsp_phys_rmcall_ce_copy_va(bar0, GSP_PHYS_RMCALL_VA_SRC, - GSP_PHYS_RMCALL_VA_GUEST_W, - GSP_PHYS_RMCALL_VA_SEM, - GSP_PHYS_RMCALL_COPY_SIZE, &sem); + ret = gsp_phys_backend_guest_ce_copy_trigger( + (u64)gsp_phys_chunk_page_dma(src, 0), 0, hpa, 0, + GSP_PHYS_RMCALL_COPY_SIZE, GSP_PHYS_RMCALL_GUEST_POLL_MS, &sem); if (ret) { pr_err("gsp-phys-boot: rm-call: GUEST W2G COPY FAILED: hpa=0x%llx pte=0x%016llx: CE submit/poll failed (%d, sem=0x%08x want=0x%08x)\n", hpa, pte, ret, sem, GSP_PHYS_RMCALL_SEM_PAYLOAD); @@ -7272,6 +7337,7 @@ static int gsp_phys_rmcall_guest_w2g(void __iomem *bar0) return -EIO; } + boot_stage.rmcall_guest_hpa = hpa; boot_stage.rmcall_guest_w2g_ok = true; pr_info("gsp-phys-boot: rm-call: GUEST W2G COPY OK gpa=0x%llx hpa=0x%llx bytes=%u sem=0x%08x\n", gpa, hpa, GSP_PHYS_RMCALL_COPY_SIZE, sem); @@ -7281,14 +7347,14 @@ static int gsp_phys_rmcall_guest_w2g(void __iomem *bar0) /* * P6a trigger 8 (gsp_phys_guest_mem=1): characterize a runtime small-PTE * write on a fresh GMMU fetch line. Slots 0x100..0x106 all share the one - * 64 B line that the stage-4/P5 walks already cached, so the trigger 6/7 - * runtime writes to 0x105/0x106 may be invisible to the engine; slot 0x183 - * sits on a different, never-walked line. The self-test builds the SRC page - * PT entry for that slot at runtime, runs the same 4 KB CE copy path as the - * guest triggers (VA_GUEST_S -> VA_DST) and verifies DST against the 0x3c - * pattern SRC. No guest GPA is needed: this measures write visibility, not - * the guest translation. It uses the shared bounded submit/poll helper and - * never retries. + * 64 B line that the stage-4/P5 walks already cached (triggers 6/7 no longer + * use 0x105/0x106: they run through the fresh-line scratch pool above), so + * the self-test picks slot 0x183 on a different, never-walked line. The + * self-test builds the SRC page PT entry for that slot at runtime, runs the + * same 4 KB CE copy path as the guest triggers (VA_GUEST_S -> VA_DST) and + * verifies DST against the 0x3c pattern SRC. No guest GPA is needed: this + * measures write visibility, not the guest translation. It uses the shared + * bounded submit/poll helper and never retries. */ static int gsp_phys_rmcall_guest_selftest(void __iomem *bar0) { diff --git a/kernel/gsp_phys_booter.h b/kernel/gsp_phys_booter.h index cc3b935..ada3280 100644 --- a/kernel/gsp_phys_booter.h +++ b/kernel/gsp_phys_booter.h @@ -317,13 +317,16 @@ int gsp_phys_boot_rm_unload(void __iomem *bar0); * gsp_phys_guest_mem=1 and a GPA pinned through gsp-phys-guest-gpa, * writing 7 (G2H, guest read) and then 6 (W2G, guest write) additionally * run one 4 KB CE copy each against that guest RAM page: the GPA is - * translated through the active mdev's VFIO type-1 domain, the physical - * device's domain must be identity, the corresponding channel PDB slot - * (0x106 read / 0x105 write) is programmed with the translated HPA, and - * the result is verified with a direct-map memcmp. Both directions are - * gated on a live READY claim, ladder stage >= 3 and the pinned GPA; - * every refusal is a logged no-op and no guest byte is touched unless - * G2H already proved the translation. + * re-translated through the active mdev's VFIO type-1 domain at trigger + * time, the physical device's domain must be identity, and the copy runs + * through the P6b fresh 128 B scratch-line path (one never-reused line + * per pair; the internal trigger variant of the guest-copy backend, so + * gsp_phys_work_forward is not required), so a host PA that changed since + * the pin cannot be shadowed by an engine-cached PTE fetch. The result is + * verified with a direct-map memcmp. Both directions are gated on a live + * READY claim, ladder stage >= 3 and the pinned GPA; every refusal is a + * logged no-op and no guest byte is touched unless G2H already proved + * the translation. * - "gsp-phys-guest-gpa" (write-only, mode 0200): pin the one guest RAM * GPA ("0x<hex>", 4 KB page-aligned) that the rm-call guest directions * may touch. One GPA per boot (a second different GPA is refused: |
