diff --git a/arch/src/aarch64/mod.rs b/arch/src/aarch64/mod.rs index 0ad0ae325..55d0c2929 100644 --- a/arch/src/aarch64/mod.rs +++ b/arch/src/aarch64/mod.rs @@ -5,7 +5,16 @@ pub mod layout; use crate::RegionType; use kvm_ioctls::*; -use vm_memory::{GuestAddress, GuestMemoryMmap, GuestUsize}; +use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap, GuestUsize}; + +#[derive(Debug)] +pub enum Error {} + +impl From for super::Error { + fn from(e: Error) -> super::Error { + super::Error::AArch64Setup(e) + } +} /// Stub function that needs to be implemented when aarch64 functionality is added. pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> { @@ -20,6 +29,15 @@ pub struct EntryPoint { pub entry_addr: GuestAddress, } +pub fn configure_vcpu( + _fd: &VcpuFd, + _id: u8, + _kernel_entry_point: Option, + _vm_memory: &GuestMemoryAtomic, +) -> super::Result<()> { + unimplemented!(); +} + /// Stub function that needs to be implemented when aarch64 functionality is added. pub fn configure_system( _guest_mem: &GuestMemoryMmap, diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 34b817ee9..f565d225a 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -30,6 +30,9 @@ pub enum Error { #[cfg(target_arch = "x86_64")] /// X86_64 specific error triggered during system configuration. X86_64Setup(x86_64::Error), + #[cfg(target_arch = "aarch64")] + /// AArch64 specific error triggered during system configuration. + AArch64Setup(aarch64::Error), /// The zero page extends past the end of guest_mem. ZeroPagePastRamEnd, /// Error writing the zero page of guest memory. @@ -76,8 +79,9 @@ pub mod aarch64; #[cfg(target_arch = "aarch64")] pub use aarch64::{ - arch_memory_regions, check_required_kvm_extensions, configure_system, get_host_cpu_phys_bits, - get_reserved_mem_addr, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, EntryPoint, + arch_memory_regions, check_required_kvm_extensions, configure_system, configure_vcpu, + get_host_cpu_phys_bits, get_reserved_mem_addr, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, + EntryPoint, }; #[cfg(target_arch = "x86_64")] @@ -85,9 +89,9 @@ pub mod x86_64; #[cfg(target_arch = "x86_64")] pub use x86_64::{ - arch_memory_regions, check_required_kvm_extensions, configure_system, get_host_cpu_phys_bits, - initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, regs, - BootProtocol, EntryPoint, + arch_memory_regions, check_required_kvm_extensions, configure_system, configure_vcpu, + get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE, + layout::CMDLINE_START, regs, BootProtocol, CpuidPatch, CpuidReg, EntryPoint, }; /// Safe wrapper for `sysconf(_SC_PAGESIZE)`. diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index 070cc5983..e9438168d 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -15,6 +15,7 @@ mod mptable; pub mod regs; use crate::InitramfsConfig; use crate::RegionType; +use kvm_bindings::CpuId; use kvm_ioctls::*; use linux_loader::loader::bootparam::{boot_params, setup_header}; use linux_loader::loader::elf::start_info::{ @@ -22,8 +23,8 @@ use linux_loader::loader::elf::start_info::{ }; use std::mem; use vm_memory::{ - Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, - GuestUsize, + Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic, + GuestMemoryMmap, GuestMemoryRegion, GuestUsize, }; #[derive(Debug, Copy, Clone)] @@ -96,6 +97,24 @@ pub enum Error { #[cfg(not(feature = "acpi"))] /// Error writing MP table to memory. MpTableSetup(mptable::Error), + + /// Error configuring the general purpose registers + REGSConfiguration(regs::Error), + + /// Error configuring the special registers + SREGSConfiguration(regs::Error), + + /// Error configuring the floating point related registers + FPUConfiguration(regs::Error), + + /// Error configuring the MSR registers + MSRSConfiguration(regs::Error), + + /// The call to KVM_SET_CPUID2 failed. + SetSupportedCpusFailed(kvm_ioctls::Error), + + /// Cannot set the local interruption due to bad configuration. + LocalIntConfiguration(interrupts::Error), } impl From for super::Error { @@ -104,6 +123,113 @@ impl From for super::Error { } } +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum CpuidReg { + EAX, + EBX, + ECX, + EDX, +} + +pub struct CpuidPatch { + pub function: u32, + pub index: u32, + pub flags_bit: Option, + pub eax_bit: Option, + pub ebx_bit: Option, + pub ecx_bit: Option, + pub edx_bit: Option, +} + +impl CpuidPatch { + pub fn set_cpuid_reg( + cpuid: &mut CpuId, + function: u32, + index: Option, + reg: CpuidReg, + value: u32, + ) { + let entries = cpuid.as_mut_slice(); + + for entry in entries.iter_mut() { + if entry.function == function && (index == None || index.unwrap() == entry.index) { + match reg { + CpuidReg::EAX => { + entry.eax = value; + } + CpuidReg::EBX => { + entry.ebx = value; + } + CpuidReg::ECX => { + entry.ecx = value; + } + CpuidReg::EDX => { + entry.edx = value; + } + } + } + } + } + + pub fn patch_cpuid(cpuid: &mut CpuId, patches: Vec) { + let entries = cpuid.as_mut_slice(); + + for entry in entries.iter_mut() { + for patch in patches.iter() { + if entry.function == patch.function && entry.index == patch.index { + if let Some(flags_bit) = patch.flags_bit { + entry.flags |= 1 << flags_bit; + } + if let Some(eax_bit) = patch.eax_bit { + entry.eax |= 1 << eax_bit; + } + if let Some(ebx_bit) = patch.ebx_bit { + entry.ebx |= 1 << ebx_bit; + } + if let Some(ecx_bit) = patch.ecx_bit { + entry.ecx |= 1 << ecx_bit; + } + if let Some(edx_bit) = patch.edx_bit { + entry.edx |= 1 << edx_bit; + } + } + } + } + } +} + +pub fn configure_vcpu( + fd: &VcpuFd, + id: u8, + kernel_entry_point: Option, + vm_memory: &GuestMemoryAtomic, + cpuid: CpuId, +) -> super::Result<()> { + let mut cpuid = cpuid; + CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id)); + fd.set_cpuid2(&cpuid) + .map_err(Error::SetSupportedCpusFailed)?; + + regs::setup_msrs(fd).map_err(Error::MSRSConfiguration)?; + if let Some(kernel_entry_point) = kernel_entry_point { + // Safe to unwrap because this method is called after the VM is configured + regs::setup_regs( + fd, + kernel_entry_point.entry_addr.raw_value(), + layout::BOOT_STACK_POINTER.raw_value(), + layout::ZERO_PAGE_START.raw_value(), + kernel_entry_point.protocol, + ) + .map_err(Error::REGSConfiguration)?; + regs::setup_fpu(fd).map_err(Error::FPUConfiguration)?; + regs::setup_sregs(&vm_memory.memory(), fd, kernel_entry_point.protocol) + .map_err(Error::SREGSConfiguration)?; + } + interrupts::set_lint(fd).map_err(Error::LocalIntConfiguration)?; + Ok(()) +} + /// 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 diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index 0a2ea299d..05f6b83c5 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -20,6 +20,8 @@ use anyhow::anyhow; #[cfg(feature = "acpi")] use arch::layout; use arch::EntryPoint; +#[cfg(target_arch = "x86_64")] +use arch::{CpuidPatch, CpuidReg}; use devices::{interrupt_controller::InterruptController, BusDevice}; #[cfg(target_arch = "x86_64")] use kvm_bindings::{ @@ -35,8 +37,6 @@ use std::os::unix::thread::JoinHandleExt; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Barrier, Mutex}; use std::{cmp, io, result, thread}; -#[cfg(target_arch = "x86_64")] -use vm_memory::{Address, GuestAddressSpace}; use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap}; use vm_migration::{ Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable, @@ -110,18 +110,6 @@ pub enum Error { /// Cannot patch the CPU ID PatchCpuId(kvm_ioctls::Error), - #[cfg(target_arch = "x86_64")] - /// Error configuring the general purpose registers - REGSConfiguration(arch::x86_64::regs::Error), - - #[cfg(target_arch = "x86_64")] - /// Error configuring the special registers - SREGSConfiguration(arch::x86_64::regs::Error), - - #[cfg(target_arch = "x86_64")] - /// Error configuring the floating point related registers - FPUConfiguration(arch::x86_64::regs::Error), - /// The call to KVM_SET_CPUID2 failed. SetSupportedCpusFailed(kvm_ioctls::Error), @@ -129,9 +117,8 @@ pub enum Error { /// Cannot set the local interruption due to bad configuration. LocalIntConfiguration(arch::x86_64::interrupts::Error), - #[cfg(target_arch = "x86_64")] - /// Error configuring the MSR registers - MSRSConfiguration(arch::x86_64::regs::Error), + /// Error configuring VCPU + VcpuConfiguration(arch::Error), /// Unexpected KVM_RUN exit reason VcpuUnhandledKvmExit, @@ -207,85 +194,6 @@ pub enum Error { } pub type Result = result::Result; -#[cfg(target_arch = "x86_64")] -#[allow(dead_code)] -#[derive(Copy, Clone)] -enum CpuidReg { - EAX, - EBX, - ECX, - EDX, -} - -#[cfg(target_arch = "x86_64")] -pub struct CpuidPatch { - pub function: u32, - pub index: u32, - pub flags_bit: Option, - pub eax_bit: Option, - pub ebx_bit: Option, - pub ecx_bit: Option, - pub edx_bit: Option, -} - -#[cfg(target_arch = "x86_64")] -impl CpuidPatch { - fn set_cpuid_reg( - cpuid: &mut CpuId, - function: u32, - index: Option, - reg: CpuidReg, - value: u32, - ) { - let entries = cpuid.as_mut_slice(); - - for entry in entries.iter_mut() { - if entry.function == function && (index == None || index.unwrap() == entry.index) { - match reg { - CpuidReg::EAX => { - entry.eax = value; - } - CpuidReg::EBX => { - entry.ebx = value; - } - CpuidReg::ECX => { - entry.ecx = value; - } - CpuidReg::EDX => { - entry.edx = value; - } - } - } - } - } - - pub fn patch_cpuid(cpuid: &mut CpuId, patches: Vec) { - let entries = cpuid.as_mut_slice(); - - for entry in entries.iter_mut() { - for patch in patches.iter() { - if entry.function == patch.function && entry.index == patch.index { - if let Some(flags_bit) = patch.flags_bit { - entry.flags |= 1 << flags_bit; - } - if let Some(eax_bit) = patch.eax_bit { - entry.eax |= 1 << eax_bit; - } - if let Some(ebx_bit) = patch.ebx_bit { - entry.ebx |= 1 << ebx_bit; - } - if let Some(ecx_bit) = patch.ecx_bit { - entry.ecx |= 1 << ecx_bit; - } - if let Some(edx_bit) = patch.edx_bit { - entry.edx |= 1 << edx_bit; - } - } - } - } - } -} - #[cfg(feature = "acpi")] #[repr(packed)] struct LocalAPIC { @@ -376,63 +284,28 @@ impl Vcpu { }))) } - #[cfg(target_arch = "aarch64")] - /// Configures a aarch64 specific vcpu and should be called once per vcpu when created. + /// Configures a vcpu and should be called once per vcpu when created. /// /// # Arguments /// - /// * `machine_config` - Specifies necessary info used for the CPUID configuration. + /// * `fd` - VcpuFd. /// * `kernel_entry_point` - Kernel entry point address in guest memory and boot protocol used. /// * `vm_memory` - Guest memory. + /// * `cpuid` - (x86_64) CpuId, wrapper over the `kvm_cpuid2` structure. pub fn configure( - &mut self, - _kernel_entry_point: Option, - _vm_memory: &GuestMemoryAtomic, - ) -> Result<()> { - unimplemented!(); - } - - #[cfg(target_arch = "x86_64")] - /// Configures a x86_64 specific vcpu and should be called once per vcpu when created. - /// - /// # Arguments - /// - /// * `machine_config` - Specifies necessary info used for the CPUID configuration. - /// * `kernel_entry_point` - Kernel entry point address in guest memory and boot protocol used. - /// * `vm_memory` - Guest memory. - /// * `cpuid` - CpuId, wrapper over the `kvm_cpuid2` structure. - pub fn configure( - &mut self, + &self, kernel_entry_point: Option, vm_memory: &GuestMemoryAtomic, - cpuid: CpuId, + #[cfg(target_arch = "x86_64")] cpuid: CpuId, ) -> Result<()> { - let mut cpuid = cpuid; - CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(self.id)); - self.fd - .set_cpuid2(&cpuid) - .map_err(Error::SetSupportedCpusFailed)?; + #[cfg(target_arch = "aarch64")] + arch::configure_vcpu(&self.fd, self.id, kernel_entry_point, vm_memory) + .map_err(Error::VcpuConfiguration)?; + + #[cfg(target_arch = "x86_64")] + arch::configure_vcpu(&self.fd, self.id, kernel_entry_point, vm_memory, cpuid) + .map_err(Error::VcpuConfiguration)?; - arch::x86_64::regs::setup_msrs(&self.fd).map_err(Error::MSRSConfiguration)?; - if let Some(kernel_entry_point) = kernel_entry_point { - // Safe to unwrap because this method is called after the VM is configured - arch::x86_64::regs::setup_regs( - &self.fd, - kernel_entry_point.entry_addr.raw_value(), - arch::x86_64::layout::BOOT_STACK_POINTER.raw_value(), - arch::x86_64::layout::ZERO_PAGE_START.raw_value(), - kernel_entry_point.protocol, - ) - .map_err(Error::REGSConfiguration)?; - arch::x86_64::regs::setup_fpu(&self.fd).map_err(Error::FPUConfiguration)?; - arch::x86_64::regs::setup_sregs( - &vm_memory.memory(), - &self.fd, - kernel_entry_point.protocol, - ) - .map_err(Error::SREGSConfiguration)?; - } - arch::x86_64::interrupts::set_lint(&self.fd).map_err(Error::LocalIntConfiguration)?; Ok(()) }