From 04af7dbbbd3db3a052cfd2764e66a70b99ad29f4 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Thu, 3 Sep 2026 13:28:09 -0700 Subject: [PATCH 1/2] loader: relocate the page table region's own identity mapping The page table region is declared relocatable by its own IGVM_VHS_PAGE_TABLE_RELOCATION header and must remain identity mapped (VA = PA), but the loader only fixed up entries against the IGVM_VHS_RELOCATABLE_REGION range. The leaf mapping the page tables kept its pre-relocation VA, so the relocated root was unmapped and the first page table access triple faulted VTL2 with no IDT loaded. This only tripped when the page table region started exactly on a large page boundary; otherwise the leaf covering it happened to also overlap the relocation region and was fixed up by luck. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- openvmm/openvmm_core/src/worker/vm_loaders/igvm.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openvmm/openvmm_core/src/worker/vm_loaders/igvm.rs b/openvmm/openvmm_core/src/worker/vm_loaders/igvm.rs index 8796e751596..32d6185c808 100644 --- a/openvmm/openvmm_core/src/worker/vm_loaders/igvm.rs +++ b/openvmm/openvmm_core/src/worker/vm_loaders/igvm.rs @@ -1217,6 +1217,14 @@ fn load_igvm_x86( relocation_region.base_gpa..=relocation_region.base_gpa + relocation_region.size - 1, offset as i64, ); + // The page table region is itself relocatable and must stay identity + // mapped, so its own range needs fixing up too. Otherwise the leaf + // entry mapping it keeps the pre-relocation VA and the root is + // unmapped. + reloc_regions.insert( + page_table_fixup.gpa..=page_table_fixup.gpa + page_table_fixup.size - 1, + offset as i64, + ); let page_table = page_table_fixup .build(offset as i64, reloc_regions, page_table_cpu_state) .map_err(Error::PageTableBuilder)?; From 9ccf71b3b9f8683fe40ce203be95fa2ff6dd4ab3 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Thu, 3 Sep 2026 15:07:16 -0700 Subject: [PATCH 2/2] loader: keep the page table region off a large page boundary Deployed loaders only fix up identity map entries that overlap the relocation region, so a page table region starting exactly on a large page boundary is left identity mapped at its pre-relocation address and VTL2 triple faults on the relocated root. The loader side is fixed separately, but images must keep booting on loaders that already shipped. Pad by a page so the region always shares a large page with the relocation region. The two regions stay disjoint, as the spec requires. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- vm/loader/src/paravisor.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/vm/loader/src/paravisor.rs b/vm/loader/src/paravisor.rs index 3518e36b735..6207106e392 100644 --- a/vm/loader/src/paravisor.rs +++ b/vm/loader/src/paravisor.rs @@ -409,6 +409,12 @@ where )?; offset += heap_size; + // Some loaders only fix up identity map entries that overlap the relocation + // region, so keep the page table region in the same large page as it. + 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; @@ -1155,6 +1161,12 @@ where )?; next_addr += heap_size; + // Some loaders only fix up identity map entries that overlap the relocation + // region, so keep the page table region in the same large page as 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;