From 0a4be0c1c7368b21d66d26b990492a3736a28f4c Mon Sep 17 00:00:00 2001 From: CMGS Date: Mon, 13 Apr 2026 17:23:15 +0800 Subject: [PATCH] vmm: extend last MMIO64 allocator to cover full range The MMIO64 allocator size is computed with alignment truncation: size = (range / alignment) * alignment This loses up to one alignment unit (4 GiB) at the top of the address space. When a guest (Windows with virtio-win 0.1.285) programs a BAR near the top of the physical address space, the allocation fails because the address falls in the truncated gap. Give the last PCI segment allocator all remaining space up to the end of the device area, so no addresses are lost. The `end` parameter of create_mmio_allocators() is an inclusive address (the last valid byte). Fix the 32-bit caller and tests to pass inclusive values, consistent with the 64-bit caller which already uses the inclusive end_of_device_area(). Signed-off-by: CMGS --- vmm/src/device_manager.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 0ae852644..77ef0cc42 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -1140,6 +1140,8 @@ pub struct DeviceManager { ivshmem_device: Option>>, } +/// Create per-PCI-segment MMIO allocators over the range `[start, end]`. +/// Both `start` and `end` are inclusive addresses. fn create_mmio_allocators( start: u64, end: u64, @@ -1157,7 +1159,15 @@ fn create_mmio_allocators( for segment_id in 0..num_pci_segments as u64 { let weight = weights[segment_id as usize] as u64; let mmio_start = start + i * pci_segment_mmio_size; - let mmio_size = pci_segment_mmio_size * weight; + let is_last = segment_id == num_pci_segments as u64 - 1; + // Give the last segment all remaining space so no addresses + // near the top of the physical address space are lost to + // alignment truncation. + let mmio_size = if is_last { + end - mmio_start + 1 + } else { + pci_segment_mmio_size * weight + }; let allocator = Arc::new(Mutex::new( AddressAllocator::new(GuestAddress(mmio_start), mmio_size).unwrap(), )); @@ -1218,7 +1228,8 @@ impl DeviceManager { } let start_of_mmio32_area = layout::MEM_32BIT_DEVICES_START.0; - let end_of_mmio32_area = layout::MEM_32BIT_DEVICES_START.0 + layout::MEM_32BIT_DEVICES_SIZE; + let end_of_mmio32_area = + layout::MEM_32BIT_DEVICES_START.0 + layout::MEM_32BIT_DEVICES_SIZE - 1; let pci_mmio32_allocators = create_mmio_allocators( start_of_mmio32_area, end_of_mmio32_area, @@ -5739,7 +5750,7 @@ mod unit_tests { #[test] fn test_create_mmio_allocators() { - let res = create_mmio_allocators(0x100000, 0x400000, 1, &[1], 4 << 10); + let res = create_mmio_allocators(0x100000, 0x3fffff, 1, &[1], 4 << 10); assert_eq!(res.len(), 1); assert_eq!( res[0].lock().unwrap().base(), @@ -5750,7 +5761,7 @@ mod unit_tests { vm_memory::GuestAddress(0x3fffff) ); - let res = create_mmio_allocators(0x100000, 0x400000, 2, &[1, 1], 4 << 10); + let res = create_mmio_allocators(0x100000, 0x3fffff, 2, &[1, 1], 4 << 10); assert_eq!(res.len(), 2); assert_eq!( res[0].lock().unwrap().base(), @@ -5769,7 +5780,7 @@ mod unit_tests { vm_memory::GuestAddress(0x3fffff) ); - let res = create_mmio_allocators(0x100000, 0x400000, 2, &[2, 1], 4 << 10); + let res = create_mmio_allocators(0x100000, 0x3fffff, 2, &[2, 1], 4 << 10); assert_eq!(res.len(), 2); assert_eq!( res[0].lock().unwrap().base(),