Skip to content

Commit d0e84f3

Browse files
committed
arch: define 64-bit capable MMIO memory regions
PCIe distinguishes MMIO regions between 32bit and 64bit, caring for devices that can't deal with 64-bit addresses. This commit defines the appropriate regions for both x86 and aarch64 architectures, extends the resource allocator to handle allocations for both of these regions and adjusts the logic that calculates the memory regions for the architecture. Signed-off-by: Babis Chalios <[email protected]>
1 parent 3eb70dd commit d0e84f3

File tree

10 files changed

+326
-131
lines changed

10 files changed

+326
-131
lines changed

src/vmm/src/arch/aarch64/layout.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,46 @@ pub const IRQ_BASE: u32 = 32;
8282

8383
/// Below this address will reside the GIC, above this address will reside the MMIO devices.
8484
pub const MAPPED_IO_START: u64 = 1 << 30; // 1 GB
85+
86+
/// The start of the memory area reserved for MMIO 32-bit accesses.
87+
pub const MMIO32_MEM_START: u64 = MAPPED_IO_START;
88+
/// The size of the memory area reserved for MMIO 32-bit accesses (1GiB).
89+
pub const MMIO32_MEM_SIZE: u64 = DRAM_MEM_START - MMIO32_MEM_START;
90+
91+
// The rest of the MMIO address space (256 MiB) we dedicate to PCIe for memory-mapped access to
92+
// configuration.
93+
/// Size of MMIO region for PCIe configuration accesses.
94+
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
95+
/// Start of MMIO region for PCIe configuration accesses.
96+
pub const PCI_MMCONFIG_START: u64 = DRAM_MEM_START - PCI_MMCONFIG_SIZE;
97+
/// MMIO space per PCIe segment
98+
pub const PCI_MMIO_CONFIG_SIZE_PER_SEGMENT: u64 = 4096 * 256;
99+
100+
// We reserve 768 MiB for devices at the beginning of the MMIO region. This includes space both for
101+
// pure MMIO and PCIe devices.
102+
103+
/// Memory region start for boot device.
104+
pub const BOOT_DEVICE_MEM_START: u64 = MMIO32_MEM_START;
105+
/// Memory region start for RTC device.
106+
pub const RTC_MEM_START: u64 = BOOT_DEVICE_MEM_START + 0x1000;
107+
/// Memory region start for Serial device.
108+
pub const SERIAL_MEM_START: u64 = RTC_MEM_START + 0x1000;
109+
110+
/// Beginning of memory region for device MMIO 32-bit accesses
111+
pub const MEM_32BIT_DEVICES_START: u64 = SERIAL_MEM_START + 0x1000;
112+
/// Size of memory region for device MMIO 32-bit accesses
113+
pub const MEM_32BIT_DEVICES_SIZE: u64 = PCI_MMCONFIG_START - MEM_32BIT_DEVICES_START;
114+
115+
// 64-bits region for MMIO accesses
116+
/// The start of the memory area reserved for MMIO 64-bit accesses.
117+
pub const MMIO64_MEM_START: u64 = 256 << 30;
118+
/// The size of the memory area reserved for MMIO 64-bit accesses.
119+
pub const MMIO64_MEM_SIZE: u64 = 256 << 30;
120+
121+
// At the moment, all of this region goes to devices
122+
/// Beginning of memory region for device MMIO 64-bit accesses
123+
pub const MEM_64BIT_DEVICES_START: u64 = MMIO64_MEM_START;
124+
/// Size of memory region for device MMIO 32-bit accesses
125+
pub const MEM_64BIT_DEVICES_SIZE: u64 = MMIO64_MEM_SIZE;
126+
/// First address past the 64-bit MMIO gap
127+
pub const FIRST_ADDR_PAST_64BITS_MMIO: u64 = MMIO64_MEM_START + MMIO64_MEM_SIZE;

src/vmm/src/arch/aarch64/mod.rs

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ use linux_loader::loader::pe::PE as Loader;
2424
use linux_loader::loader::{Cmdline, KernelLoader};
2525
use vm_memory::GuestMemoryError;
2626

27-
use crate::arch::{BootProtocol, EntryPoint};
27+
use crate::arch::{BootProtocol, EntryPoint, arch_memory_regions_with_gap};
2828
use crate::cpu_config::aarch64::{CpuConfiguration, CpuConfigurationError};
2929
use crate::cpu_config::templates::CustomCpuTemplate;
3030
use crate::initrd::InitrdConfig;
31-
use crate::utils::{align_up, usize_to_u64};
31+
use crate::utils::{align_up, u64_to_usize, usize_to_u64};
3232
use crate::vmm_config::machine_config::MachineConfig;
3333
use crate::vstate::memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap};
3434
use crate::vstate::vcpu::KvmVcpuError;
@@ -51,11 +51,6 @@ pub enum ConfigurationError {
5151
VcpuConfigure(#[from] KvmVcpuError),
5252
}
5353

54-
/// The start of the memory area reserved for MMIO devices.
55-
pub const MMIO_MEM_START: u64 = layout::MAPPED_IO_START;
56-
/// The size of the memory area reserved for MMIO devices.
57-
pub const MMIO_MEM_SIZE: u64 = layout::DRAM_MEM_START - layout::MAPPED_IO_START; //>> 1GB
58-
5954
/// Returns a Vec of the valid memory addresses for aarch64.
6055
/// See [`layout`](layout) module for a drawing of the specific memory model for this platform.
6156
///
@@ -71,7 +66,10 @@ pub fn arch_memory_regions(offset: usize, size: usize) -> Vec<(GuestAddress, usi
7166
"offset outside allowed DRAM range"
7267
);
7368

74-
let dram_size = min(size, layout::DRAM_MEM_MAX_SIZE - offset);
69+
let dram_size = min(
70+
size,
71+
layout::DRAM_MEM_MAX_SIZE - offset - u64_to_usize(layout::MMIO64_MEM_SIZE),
72+
);
7573

7674
if dram_size != size {
7775
logger::warn!(
@@ -83,10 +81,18 @@ pub fn arch_memory_regions(offset: usize, size: usize) -> Vec<(GuestAddress, usi
8381
);
8482
}
8583

86-
vec![(
87-
GuestAddress(layout::DRAM_MEM_START + offset as u64),
88-
dram_size,
89-
)]
84+
let mut regions = vec![];
85+
if let Some((offset, remaining)) = arch_memory_regions_with_gap(
86+
&mut regions,
87+
usize::try_from(layout::DRAM_MEM_START).unwrap() + offset,
88+
size,
89+
u64_to_usize(layout::MMIO64_MEM_START),
90+
u64_to_usize(layout::MMIO64_MEM_SIZE),
91+
) {
92+
regions.push((GuestAddress(offset as u64), remaining));
93+
}
94+
95+
regions
9096
}
9197

9298
/// Configures the system for booting Linux.
@@ -228,23 +234,44 @@ mod verification {
228234

229235
let regions = arch_memory_regions(offset as usize, len as usize);
230236

231-
// No MMIO gap on ARM
232-
assert_eq!(regions.len(), 1);
233-
234-
let (GuestAddress(start), actual_len) = regions[0];
235-
let actual_len = actual_len as u64;
237+
// On Arm we have one MMIO gap that might fall within addressable ranges,
238+
// so we can get either 1 or 2 regions.
239+
assert!(regions.len() >= 1);
240+
assert!(regions.len() <= 2);
236241

242+
// The very first address should be offset bytes past DRAM_MEM_START
237243
assert_eq!(start, layout::DRAM_MEM_START + offset);
244+
// The total length of all regions cannot exceed DRAM_MEM_MAX_SIZE
245+
let actual_len = regions.iter().map(|&(_, len)| len).sum::<usize>();
238246
assert!(actual_len <= layout::DRAM_MEM_MAX_SIZE as u64);
247+
// The total length is smaller or equal to the length we asked
239248
assert!(actual_len <= len);
240-
249+
// If it's smaller, it's because we asked more than the the maximum possible.
241250
if actual_len < len {
242-
assert_eq!(
243-
start + actual_len,
244-
layout::DRAM_MEM_START + layout::DRAM_MEM_MAX_SIZE as u64
245-
);
246251
assert!(offset + len >= layout::DRAM_MEM_MAX_SIZE as u64);
247252
}
253+
254+
// No region overlaps the 64-bit MMIO gap
255+
assert!(
256+
regions
257+
.iter()
258+
.all(|&(start, len)| start.0 >= FIRST_ADDR_PAST_64BITS_MMIO
259+
|| start.0 + len as u64 <= MMIO64_MEM_START)
260+
);
261+
262+
// All regions start after our specified offset
263+
assert!(regions.iter().all(|&(start, _)| start.0 >= offset as u64));
264+
265+
// All regions have non-zero length
266+
assert!(regions.iter().all(|&(_, len)| len > 0));
267+
268+
// If there's two regions, they perfectly snuggle up the 64bit MMIO gap
269+
if regions.len() == 2 {
270+
kani::cover!();
271+
272+
assert_eq!(regions[0].0.0 + regions[0].1 as u64, MMIO32_MEM_START);
273+
assert_eq!(regions[1].0.0, FIRST_ADDR_PAST_32BITS);
274+
}
248275
}
249276
}
250277

src/vmm/src/arch/mod.rs

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ pub use aarch64::vcpu::*;
2020
pub use aarch64::vm::{ArchVm, ArchVmError, VmState};
2121
#[cfg(target_arch = "aarch64")]
2222
pub use aarch64::{
23-
ConfigurationError, MMIO_MEM_SIZE, MMIO_MEM_START, arch_memory_regions,
24-
configure_system_for_boot, get_kernel_start, initrd_load_addr, layout::CMDLINE_MAX_SIZE,
25-
layout::IRQ_BASE, layout::IRQ_MAX, layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START,
26-
load_kernel,
23+
ConfigurationError, arch_memory_regions, configure_system_for_boot, get_kernel_start,
24+
initrd_load_addr, layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX,
25+
layout::MEM_32BIT_DEVICES_SIZE, layout::MEM_32BIT_DEVICES_START,
26+
layout::MEM_64BIT_DEVICES_SIZE, layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE,
27+
layout::MMIO32_MEM_START, layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, load_kernel,
2728
};
2829

2930
/// Module for x86_64 related functionality.
@@ -39,11 +40,14 @@ pub use x86_64::vm::{ArchVm, ArchVmError, VmState};
3940

4041
#[cfg(target_arch = "x86_64")]
4142
pub use crate::arch::x86_64::{
42-
ConfigurationError, MMIO_MEM_SIZE, MMIO_MEM_START, arch_memory_regions,
43-
configure_system_for_boot, get_kernel_start, initrd_load_addr, layout::APIC_ADDR,
44-
layout::CMDLINE_MAX_SIZE, layout::IOAPIC_ADDR, layout::IRQ_BASE, layout::IRQ_MAX,
43+
ConfigurationError, arch_memory_regions, configure_system_for_boot, get_kernel_start,
44+
initrd_load_addr, layout::APIC_ADDR, layout::CMDLINE_MAX_SIZE, layout::IOAPIC_ADDR,
45+
layout::IRQ_BASE, layout::IRQ_MAX, layout::MEM_32BIT_DEVICES_SIZE,
46+
layout::MEM_32BIT_DEVICES_START, layout::MEM_64BIT_DEVICES_SIZE,
47+
layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE, layout::MMIO32_MEM_START,
4548
layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, load_kernel,
4649
};
50+
use crate::utils::u64_to_usize;
4751

4852
/// Types of devices that can get attached to this platform.
4953
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy, Serialize, Deserialize)]
@@ -114,3 +118,32 @@ pub struct EntryPoint {
114118
/// Specifies which boot protocol to use
115119
pub protocol: BootProtocol,
116120
}
121+
122+
/// Adds in [`regions`] the valid memory regions suitable for RAM taking into account a gap in the
123+
/// available address space and returns the remaining region (if any) past this gap
124+
fn arch_memory_regions_with_gap(
125+
regions: &mut Vec<(GuestAddress, usize)>,
126+
offset: usize,
127+
size: usize,
128+
gap_start: usize,
129+
gap_size: usize,
130+
) -> Option<(usize, usize)> {
131+
let first_addr_past_gap = gap_start + gap_size;
132+
match (size + offset).checked_sub(gap_start) {
133+
// case0: region fits all before gap
134+
None | Some(0) => {
135+
regions.push((GuestAddress(offset as u64), size));
136+
None
137+
}
138+
// case1: region starts before the gap and goes past it
139+
Some(remaining) if offset < gap_start => {
140+
regions.push((
141+
GuestAddress(offset as u64),
142+
u64_to_usize(gap_start as u64) - offset,
143+
));
144+
Some((first_addr_past_gap, remaining))
145+
}
146+
// case2: region starts past the gap
147+
Some(remaining) => Some((first_addr_past_gap.max(offset), remaining)),
148+
}
149+
}

src/vmm/src/arch/x86_64/layout.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
//! Magic addresses externally used to lay out x86_64 VMs.
99
10+
use crate::utils::mib_to_bytes;
11+
1012
/// Initial stack for the boot CPU.
1113
pub const BOOT_STACK_POINTER: u64 = 0x8ff0;
1214

@@ -77,3 +79,45 @@ pub const SYSTEM_MEM_START: u64 = 0x9fc00;
7779
/// 257KiB is more than we need, however we reserve this space for potential future use of
7880
/// ACPI features (new tables and/or devices).
7981
pub const SYSTEM_MEM_SIZE: u64 = RSDP_ADDR - SYSTEM_MEM_START;
82+
83+
/// First address that cannot be addressed using 32 bit anymore.
84+
pub const FIRST_ADDR_PAST_32BITS: u64 = 1 << 32;
85+
86+
/// The size of the memory area reserved for MMIO 32-bit accesses.
87+
pub const MMIO32_MEM_SIZE: u64 = mib_to_bytes(1024) as u64;
88+
/// The start of the memory area reserved for MMIO 32-bit accesses.
89+
pub const MMIO32_MEM_START: u64 = FIRST_ADDR_PAST_32BITS - MMIO32_MEM_SIZE;
90+
91+
// We dedicate the last 256 MiB of the 32-bit MMIO address space PCIe for memory-mapped access to
92+
// configuration.
93+
/// Size of MMIO region for PCIe configuration accesses.
94+
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
95+
/// Start of MMIO region for PCIe configuration accesses.
96+
pub const PCI_MMCONFIG_START: u64 = FIRST_ADDR_PAST_32BITS - PCI_MMCONFIG_SIZE;
97+
/// MMIO space per PCIe segment
98+
pub const PCI_MMIO_CONFIG_SIZE_PER_SEGMENT: u64 = 4096 * 256;
99+
100+
// We reserve 768 MiB for devices at the beginning of the MMIO region. This includes space both for
101+
// pure MMIO and PCIe devices.
102+
103+
/// Memory region start for boot device.
104+
pub const BOOT_DEVICE_MEM_START: u64 = MMIO32_MEM_START;
105+
106+
/// Beginning of memory region for device MMIO 32-bit accesses
107+
pub const MEM_32BIT_DEVICES_START: u64 = BOOT_DEVICE_MEM_START + 0x1000;
108+
/// Size of memory region for device MMIO 32-bit accesses
109+
pub const MEM_32BIT_DEVICES_SIZE: u64 = PCI_MMCONFIG_START - MEM_32BIT_DEVICES_START;
110+
111+
// 64-bits region for MMIO accesses
112+
/// The start of the memory area reserved for MMIO 64-bit accesses.
113+
pub const MMIO64_MEM_START: u64 = 256 << 30;
114+
/// The size of the memory area reserved for MMIO 64-bit accesses.
115+
pub const MMIO64_MEM_SIZE: u64 = 256 << 30;
116+
117+
// At the moment, all of this region goes to devices
118+
/// Beginning of memory region for device MMIO 64-bit accesses
119+
pub const MEM_64BIT_DEVICES_START: u64 = MMIO64_MEM_START;
120+
/// Size of memory region for device MMIO 32-bit accesses
121+
pub const MEM_64BIT_DEVICES_SIZE: u64 = MMIO64_MEM_SIZE;
122+
/// First address past the 64-bit MMIO gap
123+
pub const FIRST_ADDR_PAST_64BITS_MMIO: u64 = MMIO64_MEM_START + MMIO64_MEM_SIZE;

0 commit comments

Comments
 (0)