diff --git a/arch/src/lib.rs b/arch/src/lib.rs index e6e2ce02a..bf0e52e46 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -21,7 +21,6 @@ extern crate linux_loader; extern crate vm_memory; use std::result; -use vm_memory::GuestAddress; #[derive(Debug, PartialEq)] pub enum Error { @@ -53,9 +52,6 @@ pub enum RegionType { Reserved, } -// 1MB. We don't put anything above here except the kernel itself. -pub const HIMEM_START: GuestAddress = GuestAddress(0x100000); - #[cfg(target_arch = "aarch64")] pub mod aarch64; @@ -70,6 +66,5 @@ pub mod x86_64; #[cfg(target_arch = "x86_64")] pub use x86_64::{ - arch_memory_regions, configure_system, get_32bit_gap_start as get_reserved_mem_addr, - layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, + arch_memory_regions, configure_system, layout, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, }; diff --git a/arch/src/x86_64/acpi.rs b/arch/src/x86_64/acpi.rs index 8148fc6b9..1ff11db88 100644 --- a/arch/src/x86_64/acpi.rs +++ b/arch/src/x86_64/acpi.rs @@ -10,6 +10,8 @@ use vm_memory::{GuestAddress, GuestMemoryMmap}; use vm_memory::{Address, ByteValued, Bytes}; +use super::layout; + #[repr(packed)] struct LocalAPIC { pub r#type: u8, @@ -209,7 +211,7 @@ pub fn create_acpi_tables( end_of_device_area: GuestAddress, ) -> GuestAddress { // RSDP is at the EBDA - let rsdp_offset = super::EBDA_START; + let rsdp_offset = layout::RSDP_POINTER; let mut tables: Vec = Vec::new(); // DSDT @@ -296,7 +298,7 @@ pub fn create_acpi_tables( // 32-bit PCI enhanced configuration mechanism mcfg.append(PCIRangeEntry { - base_address: super::MEM_32BIT_DEVICES_GAP_SIZE, + base_address: layout::MEM_32BIT_DEVICES_START.0, segment: 0, start: 0, end: 0xff, diff --git a/arch/src/x86_64/layout.rs b/arch/src/x86_64/layout.rs index d0703ec75..a5fbabe54 100644 --- a/arch/src/x86_64/layout.rs +++ b/arch/src/x86_64/layout.rs @@ -5,9 +5,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD-3-Clause file. -use vm_memory::GuestAddress; +use vm_memory::{GuestAddress, GuestUsize}; -/// Magic addresses externally used to lay out x86_64 VMs. +/* + +Memory layout documentation and constants +~~~~~~ ~~~~~~ ~~~~~~~~~~~~~ ~~~ ~~~~~~~~~ + +Constants are in order and grouped by range. Take care to update all references +when making changes and keep them in order. + +*/ + +// ** Low RAM (start: 0, length: 640KiB) ** +pub const LOW_RAM_START: GuestAddress = GuestAddress(0x0); + +// == Fixed addresses within the "Low RAM" range: == + +/// The 'zero page', a.k.a linux kernel bootparams. +pub const ZERO_PAGE_START: GuestAddress = GuestAddress(0x7000); /// Initial stack for the boot CPU. pub const BOOT_STACK_START: GuestAddress = GuestAddress(0x8000); @@ -18,8 +34,37 @@ pub const CMDLINE_START: GuestAddress = GuestAddress(0x20000); /// Kernel command line start address maximum size. pub const CMDLINE_MAX_SIZE: usize = 0x10000; -/// Address for the TSS setup. -pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffbd000); +// == End of "Low RAM" range. == -/// The 'zero page', a.k.a linux kernel bootparams. -pub const ZERO_PAGE_START: GuestAddress = GuestAddress(0x7000); +// ** EBDA reserved area (start: 640KiB, length: 384KiB) ** +pub const EBDA_START: GuestAddress = GuestAddress(0xa0000); + +// == Fixed constants within the "EBDA" range == + +// ACPI RSDP table +pub const RSDP_POINTER: GuestAddress = EBDA_START; + +// == End of "EBDA" range == + +// ** High RAM (start: 1MiB, length: 3071MiB) ** +pub const HIGH_RAM_START: GuestAddress = GuestAddress(0x100000); + +// == No fixed addresses in the "High RAM" range == + +// ** 32-bit reserved area (start: 3GiB, length: 1GiB) ** +pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xc000_0000); +pub const MEM_32BIT_RESERVED_SIZE: GuestUsize = (1024 << 20); + +// == Fixed constants within the "32-bit reserved" range == + +// Sub range: 32-bit PCI devices (start: 3GiB, length: 768Mib) +pub const MEM_32BIT_DEVICES_START: GuestAddress = MEM_32BIT_RESERVED_START; +pub const MEM_32BIT_DEVICES_SIZE: GuestUsize = (768 << 20); + +/// Address for the TSS setup. +pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000); + +// == End of "32-bit reserved" range. == + +// ** 64-bit RAM start (start: 4GiB, length: varies) ** +pub const RAM_64BIT_START: GuestAddress = GuestAddress(0x1_0000_0000); diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index c01384e3a..0108fb6c8 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -48,73 +48,53 @@ impl From for super::Error { } } -// Where BIOS/VGA magic would live on a real PC. -const EBDA_START: GuestAddress = GuestAddress(0xa0000); -const FIRST_ADDR_PAST_32BITS: GuestAddress = GuestAddress(1 << 32); - -// Our 32-bit memory gap starts at 3G. -pub const MEM_32BIT_GAP_START: GuestAddress = GuestAddress(0xc000_0000); - -// Our 32-bit memory gap size is 1GB. -const MEM_32BIT_GAP_SIZE: GuestUsize = (1024 << 20); - -// We reserve 768MB in our memory gap for 32-bit devices (e.g. 32-bit PCI BARs). -const MEM_32BIT_DEVICES_GAP_SIZE: GuestUsize = (768 << 20); - /// Returns a Vec of the valid memory addresses. /// These should be used to configure the GuestMemory structure for the platform. /// For x86_64 all addresses are valid from the start of the kernel except a /// carve out at the end of 32bit address space. pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> { - let reserved_memory_gap_start = MEM_32BIT_GAP_START - .checked_add(MEM_32BIT_DEVICES_GAP_SIZE) + let reserved_memory_gap_start = layout::MEM_32BIT_RESERVED_START + .checked_add(layout::MEM_32BIT_DEVICES_SIZE) .expect("32-bit reserved region is too large"); let requested_memory_size = GuestAddress(size as u64); let mut regions = Vec::new(); // case1: guest memory fits before the gap - if size as u64 <= MEM_32BIT_GAP_START.raw_value() { + if size as u64 <= layout::MEM_32BIT_RESERVED_START.raw_value() { regions.push((GuestAddress(0), size as usize, RegionType::Ram)); // case2: guest memory extends beyond the gap } else { // push memory before the gap regions.push(( GuestAddress(0), - MEM_32BIT_GAP_START.raw_value() as usize, + layout::MEM_32BIT_RESERVED_START.raw_value() as usize, RegionType::Ram, )); regions.push(( - FIRST_ADDR_PAST_32BITS, - requested_memory_size.unchecked_offset_from(MEM_32BIT_GAP_START) as usize, + layout::RAM_64BIT_START, + requested_memory_size.unchecked_offset_from(layout::MEM_32BIT_RESERVED_START) as usize, RegionType::Ram, )); } // Add the 32-bit device memory hole as a sub region. regions.push(( - MEM_32BIT_GAP_START, - MEM_32BIT_DEVICES_GAP_SIZE as usize, + layout::MEM_32BIT_RESERVED_START, + layout::MEM_32BIT_DEVICES_SIZE as usize, RegionType::SubRegion, )); // Add the 32-bit reserved memory hole as a sub region. regions.push(( reserved_memory_gap_start, - (MEM_32BIT_GAP_SIZE - MEM_32BIT_DEVICES_GAP_SIZE) as usize, + (layout::MEM_32BIT_RESERVED_SIZE - layout::MEM_32BIT_DEVICES_SIZE) as usize, RegionType::Reserved, )); regions } -/// X86 specific memory hole/memory mapped devices/reserved area. -pub fn get_32bit_gap_start() -> GuestAddress { - FIRST_ADDR_PAST_32BITS - .checked_sub(MEM_32BIT_GAP_SIZE) - .expect("32-bit hole is too large") -} - /// Configures the system and should be called once per vm before starting vcpu threads. /// /// # Arguments @@ -136,10 +116,6 @@ pub fn configure_system( const KERNEL_HDR_MAGIC: u32 = 0x53726448; const KERNEL_LOADER_OTHER: u8 = 0xff; const KERNEL_MIN_ALIGNMENT_BYTES: u32 = 0x1000000; // Must be non-zero. - let first_addr_past_32bits = FIRST_ADDR_PAST_32BITS; - let end_32bit_gap_start = get_32bit_gap_start(); - - let himem_start = super::HIMEM_START; // Note that this puts the mptable at the last 1k of Linux's 640k base RAM mptable::setup_mptable(guest_mem, num_cpus).map_err(Error::MpTableSetup)?; @@ -159,28 +135,28 @@ pub fn configure_system( params.0.hdr.kernel_alignment = KERNEL_MIN_ALIGNMENT_BYTES; }; - add_e820_entry(&mut params.0, 0, EBDA_START.raw_value(), E820_RAM)?; + add_e820_entry(&mut params.0, 0, layout::EBDA_START.raw_value(), E820_RAM)?; let mem_end = guest_mem.end_addr(); - if mem_end < end_32bit_gap_start { + if mem_end < layout::MEM_32BIT_RESERVED_START { add_e820_entry( &mut params.0, - himem_start.raw_value(), - mem_end.unchecked_offset_from(himem_start) + 1, + layout::HIGH_RAM_START.raw_value(), + mem_end.unchecked_offset_from(layout::HIGH_RAM_START) + 1, E820_RAM, )?; } else { add_e820_entry( &mut params.0, - himem_start.raw_value(), - end_32bit_gap_start.unchecked_offset_from(himem_start), + layout::HIGH_RAM_START.raw_value(), + layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::HIGH_RAM_START), E820_RAM, )?; - if mem_end > first_addr_past_32bits { + if mem_end > layout::RAM_64BIT_START { add_e820_entry( &mut params.0, - first_addr_past_32bits.raw_value(), - mem_end.unchecked_offset_from(first_addr_past_32bits) + 1, + layout::RAM_64BIT_START.raw_value(), + mem_end.unchecked_offset_from(layout::RAM_64BIT_START) + 1, E820_RAM, )?; } @@ -188,8 +164,8 @@ pub fn configure_system( #[cfg(feature = "acpi")] { - let start_of_device_area = if mem_end < end_32bit_gap_start { - first_addr_past_32bits + let start_of_device_area = if mem_end < layout::MEM_32BIT_RESERVED_START { + layout::RAM_64BIT_START } else { guest_mem.end_addr().unchecked_add(1) }; @@ -255,16 +231,6 @@ mod tests { assert_eq!(GuestAddress(1 << 32), regions[1].0); } - #[test] - fn test_32bit_gap() { - assert_eq!( - get_32bit_gap_start(), - FIRST_ADDR_PAST_32BITS - .checked_sub(MEM_32BIT_GAP_SIZE as u64) - .expect("32-bit hole is too large") - ); - } - #[test] fn test_system_configuration() { let no_vcpus = 4; diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 16ae47aa8..86faf307a 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -687,7 +687,7 @@ impl Vm { mem.deref(), None, &mut self.kernel, - Some(arch::HIMEM_START), + Some(arch::layout::HIGH_RAM_START), ) { Ok(entry_addr) => entry_addr, Err(linux_loader::loader::Error::InvalidElfMagicNumber) => { @@ -695,7 +695,7 @@ impl Vm { mem.deref(), None, &mut self.kernel, - Some(arch::HIMEM_START), + Some(arch::layout::HIGH_RAM_START), ) .map_err(Error::KernelLoad)? }