From b6fa409e91d23641ecec0d6b0948f04cb6e9ebda Mon Sep 17 00:00:00 2001 From: John Starks Date: Sat, 23 May 2026 21:39:19 -0700 Subject: [PATCH] openvmm_core: start aarch64 Linux direct boot RAM at 1 GiB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On aarch64, iommufd reserves the 128 MiB–129 MiB region in IOVA space for the host MSI doorbell. When openvmm places guest RAM starting at address 0, identity-mapped DMA for passthrough devices fails because iommufd cannot allocate IOVAs that overlap this reservation. Avoid the conflict by starting RAM at 1 GiB for aarch64 Linux direct boot. This is implemented via a new ram_start_address field on MemoryLayoutInput that reserves the low GPA range so the memory layout allocator places RAM above it. UEFI boot is not yet handled because UEFI currently requires low memory to start; this is left as future work. --- openvmm/openvmm_core/src/worker/dispatch.rs | 20 +++++++++++++++++++ .../openvmm_core/src/worker/memory_layout.rs | 13 ++++++++++++ 2 files changed, 33 insertions(+) diff --git a/openvmm/openvmm_core/src/worker/dispatch.rs b/openvmm/openvmm_core/src/worker/dispatch.rs index 7acf844917..5afbd1050b 100644 --- a/openvmm/openvmm_core/src/worker/dispatch.rs +++ b/openvmm/openvmm_core/src/worker/dispatch.rs @@ -1003,6 +1003,25 @@ impl InitializedVm { #[cfg(not(guest_arch = "aarch64"))] let smmu_count = 0; + // On aarch64 Linux direct boot, start RAM at 1 GiB to avoid the low GPA + // region (128 MiB–129 MiB) that iommufd reserves for the host MSI + // doorbell in IOVA space. Without this gap, iommufd identity-mapped DMA + // for passthrough devices fails because it cannot allocate IOVAs in + // that range. + // + // FUTURE: this needs to be present for UEFI as well, but UEFI cannot + // only boot from low memory. Either: + // 1. Fix Linux to allow configuring the reserved IOVA range. + // 2. Fix UEFI to allow booting from >0. + // 3. Install a little bit of low memory, enough for UEFI to get to DXE + // (which can run anywhere.) + let ram_start_address = + if cfg!(guest_arch = "aarch64") && matches!(cfg.load_mode, LoadMode::Linux { .. }) { + 1024 * 1024 * 1024 // 1 GiB + } else { + 0 + }; + let resolved_layout = resolve_memory_layout(MemoryLayoutInput { mem_size: cfg.memory.mem_size, numa_mem_sizes: cfg.memory.numa_mem_sizes.as_deref(), @@ -1011,6 +1030,7 @@ impl InitializedVm { virtio_mmio_count, smmu_count, vtl2_layout, + ram_start_address, physical_address_size, }) .context("invalid memory configuration")?; diff --git a/openvmm/openvmm_core/src/worker/memory_layout.rs b/openvmm/openvmm_core/src/worker/memory_layout.rs index 0ecd36c445..5cd7b924e7 100644 --- a/openvmm/openvmm_core/src/worker/memory_layout.rs +++ b/openvmm/openvmm_core/src/worker/memory_layout.rs @@ -102,6 +102,11 @@ pub(super) struct MemoryLayoutInput<'a> { /// Optional IGVM VTL2 private-memory request. This is allocated after all /// VTL0-visible RAM and MMIO and is carried separately from ordinary RAM. pub vtl2_layout: Option, + /// Minimum guest physical address for ordinary RAM. When nonzero, the + /// range `0..ram_start_address` is reserved so RAM is placed above it. + /// This is used on aarch64 Linux direct boot to avoid the low GPA region + /// that conflicts with iommufd IOVA reservations. + pub ram_start_address: u64, /// Host-supported physical address width used only after allocation. The /// allocator computes the smallest layout it can; host fit is validation. pub physical_address_size: u8, @@ -138,6 +143,13 @@ pub(super) fn resolve_memory_layout( // least the architectural reserved zone (LAPIC, IOAPIC, TPM, ...) so // guests can arbitrate fixed-address children like TPM2 against this // window; the caller-requested size may extend it lower. + // Reserve low addresses so RAM starts above `ram_start_address`. This is + // used on aarch64 Linux direct boot to skip the 128 MiB–129 MiB IOVA + // region that iommufd reserves for the host MSI doorbell. + if input.ram_start_address > 0 { + builder.reserve("low-ram-gap", MemoryRange::new(0..input.ram_start_address)); + } + let arch_reserved = if cfg!(guest_arch = "x86_64") { ARCH_RESERVED_X86_64 } else { @@ -542,6 +554,7 @@ mod tests { virtio_mmio_count: 0, smmu_count: 0, vtl2_layout, + ram_start_address: 0, physical_address_size: 46, } }