From 8ed478eab127a9937a6509968f6da36fba93dcd0 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Thu, 3 Sep 2026 10:17:05 -0700 Subject: [PATCH] loader: keep the page table region off a large page boundary The page table region sits immediately after the relocation region and is excluded from it, but the boot identity map uses large pages. A relocating loader only fixes up a leaf entry when the region it maps overlaps the relocation region, so when the page table region starts exactly on a large page boundary its leaf stays identity mapped at the pre-relocation address. The relocated cr3 is then unmapped and the first page table access triple faults VTL2 with no IDT loaded. Latent; only trips when unrelated image growth lands the region on the boundary. Pad by a page so it always shares a large page with the relocation region. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- vm/loader/src/paravisor.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/vm/loader/src/paravisor.rs b/vm/loader/src/paravisor.rs index 0cffa3c6b28..bf682d50f25 100644 --- a/vm/loader/src/paravisor.rs +++ b/vm/loader/src/paravisor.rs @@ -483,6 +483,13 @@ where )?; offset += heap_size; + // Relocating loaders only fix up identity map entries that overlap the + // relocation region, so keep the page table region sharing a large page + // with it. Otherwise the relocated cr3 is unmapped and VTL2 triple faults. + if offset.is_multiple_of(X64_LARGE_PAGE_SIZE) { + offset += HV_PAGE_SIZE; + } + // The end of memory used by the loader, excluding pagetables. let end_of_underhill_mem = offset; @@ -1242,6 +1249,12 @@ where )?; next_addr += heap_size; + // Keep the page table region sharing a large page with the relocation + // region, so the identity map entry covering it is relocated with it. + if next_addr.is_multiple_of(u64::from(Arm64PageSize::Large)) { + next_addr += HV_PAGE_SIZE; + } + // The end of memory used by the loader, excluding pagetables. let end_of_underhill_mem = next_addr;