vmm: Move Vcpu::configure() to arch crate

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
This commit is contained in:
Michael Zhao 2020-05-28 15:27:22 +08:00 committed by Sebastien Boeuf
parent 969e5e0b51
commit 8f7dc73562
4 changed files with 172 additions and 151 deletions

View File

@ -5,7 +5,16 @@ pub mod layout;
use crate::RegionType; use crate::RegionType;
use kvm_ioctls::*; use kvm_ioctls::*;
use vm_memory::{GuestAddress, GuestMemoryMmap, GuestUsize}; use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap, GuestUsize};
#[derive(Debug)]
pub enum Error {}
impl From<Error> 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. /// Stub function that needs to be implemented when aarch64 functionality is added.
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> { pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
@ -20,6 +29,15 @@ pub struct EntryPoint {
pub entry_addr: GuestAddress, pub entry_addr: GuestAddress,
} }
pub fn configure_vcpu(
_fd: &VcpuFd,
_id: u8,
_kernel_entry_point: Option<EntryPoint>,
_vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
) -> super::Result<()> {
unimplemented!();
}
/// Stub function that needs to be implemented when aarch64 functionality is added. /// Stub function that needs to be implemented when aarch64 functionality is added.
pub fn configure_system( pub fn configure_system(
_guest_mem: &GuestMemoryMmap, _guest_mem: &GuestMemoryMmap,

View File

@ -30,6 +30,9 @@ pub enum Error {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
/// X86_64 specific error triggered during system configuration. /// X86_64 specific error triggered during system configuration.
X86_64Setup(x86_64::Error), 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. /// The zero page extends past the end of guest_mem.
ZeroPagePastRamEnd, ZeroPagePastRamEnd,
/// Error writing the zero page of guest memory. /// Error writing the zero page of guest memory.
@ -76,8 +79,9 @@ pub mod aarch64;
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
pub use aarch64::{ pub use aarch64::{
arch_memory_regions, check_required_kvm_extensions, configure_system, get_host_cpu_phys_bits, arch_memory_regions, check_required_kvm_extensions, configure_system, configure_vcpu,
get_reserved_mem_addr, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, EntryPoint, get_host_cpu_phys_bits, get_reserved_mem_addr, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START,
EntryPoint,
}; };
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
@ -85,9 +89,9 @@ pub mod x86_64;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
pub use x86_64::{ pub use x86_64::{
arch_memory_regions, check_required_kvm_extensions, configure_system, get_host_cpu_phys_bits, arch_memory_regions, check_required_kvm_extensions, configure_system, configure_vcpu,
initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, regs, get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE,
BootProtocol, EntryPoint, layout::CMDLINE_START, regs, BootProtocol, CpuidPatch, CpuidReg, EntryPoint,
}; };
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`. /// Safe wrapper for `sysconf(_SC_PAGESIZE)`.

View File

@ -15,6 +15,7 @@ mod mptable;
pub mod regs; pub mod regs;
use crate::InitramfsConfig; use crate::InitramfsConfig;
use crate::RegionType; use crate::RegionType;
use kvm_bindings::CpuId;
use kvm_ioctls::*; use kvm_ioctls::*;
use linux_loader::loader::bootparam::{boot_params, setup_header}; use linux_loader::loader::bootparam::{boot_params, setup_header};
use linux_loader::loader::elf::start_info::{ use linux_loader::loader::elf::start_info::{
@ -22,8 +23,8 @@ use linux_loader::loader::elf::start_info::{
}; };
use std::mem; use std::mem;
use vm_memory::{ use vm_memory::{
Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
GuestUsize, GuestMemoryMmap, GuestMemoryRegion, GuestUsize,
}; };
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -96,6 +97,24 @@ pub enum Error {
#[cfg(not(feature = "acpi"))] #[cfg(not(feature = "acpi"))]
/// Error writing MP table to memory. /// Error writing MP table to memory.
MpTableSetup(mptable::Error), 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<Error> for super::Error { impl From<Error> for super::Error {
@ -104,6 +123,113 @@ impl From<Error> 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<u8>,
pub eax_bit: Option<u8>,
pub ebx_bit: Option<u8>,
pub ecx_bit: Option<u8>,
pub edx_bit: Option<u8>,
}
impl CpuidPatch {
pub fn set_cpuid_reg(
cpuid: &mut CpuId,
function: u32,
index: Option<u32>,
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<CpuidPatch>) {
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<EntryPoint>,
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
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. /// Returns a Vec of the valid memory addresses.
/// These should be used to configure the GuestMemory structure for the platform. /// 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 /// For x86_64 all addresses are valid from the start of the kernel except a

View File

@ -20,6 +20,8 @@ use anyhow::anyhow;
#[cfg(feature = "acpi")] #[cfg(feature = "acpi")]
use arch::layout; use arch::layout;
use arch::EntryPoint; use arch::EntryPoint;
#[cfg(target_arch = "x86_64")]
use arch::{CpuidPatch, CpuidReg};
use devices::{interrupt_controller::InterruptController, BusDevice}; use devices::{interrupt_controller::InterruptController, BusDevice};
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
use kvm_bindings::{ use kvm_bindings::{
@ -35,8 +37,6 @@ use std::os::unix::thread::JoinHandleExt;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Barrier, Mutex}; use std::sync::{Arc, Barrier, Mutex};
use std::{cmp, io, result, thread}; use std::{cmp, io, result, thread};
#[cfg(target_arch = "x86_64")]
use vm_memory::{Address, GuestAddressSpace};
use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap}; use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
use vm_migration::{ use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable, Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
@ -110,18 +110,6 @@ pub enum Error {
/// Cannot patch the CPU ID /// Cannot patch the CPU ID
PatchCpuId(kvm_ioctls::Error), 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. /// The call to KVM_SET_CPUID2 failed.
SetSupportedCpusFailed(kvm_ioctls::Error), SetSupportedCpusFailed(kvm_ioctls::Error),
@ -129,9 +117,8 @@ pub enum Error {
/// Cannot set the local interruption due to bad configuration. /// Cannot set the local interruption due to bad configuration.
LocalIntConfiguration(arch::x86_64::interrupts::Error), LocalIntConfiguration(arch::x86_64::interrupts::Error),
#[cfg(target_arch = "x86_64")] /// Error configuring VCPU
/// Error configuring the MSR registers VcpuConfiguration(arch::Error),
MSRSConfiguration(arch::x86_64::regs::Error),
/// Unexpected KVM_RUN exit reason /// Unexpected KVM_RUN exit reason
VcpuUnhandledKvmExit, VcpuUnhandledKvmExit,
@ -207,85 +194,6 @@ pub enum Error {
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
#[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<u8>,
pub eax_bit: Option<u8>,
pub ebx_bit: Option<u8>,
pub ecx_bit: Option<u8>,
pub edx_bit: Option<u8>,
}
#[cfg(target_arch = "x86_64")]
impl CpuidPatch {
fn set_cpuid_reg(
cpuid: &mut CpuId,
function: u32,
index: Option<u32>,
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<CpuidPatch>) {
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")] #[cfg(feature = "acpi")]
#[repr(packed)] #[repr(packed)]
struct LocalAPIC { struct LocalAPIC {
@ -376,63 +284,28 @@ impl Vcpu {
}))) })))
} }
#[cfg(target_arch = "aarch64")] /// Configures a vcpu and should be called once per vcpu when created.
/// Configures a aarch64 specific vcpu and should be called once per vcpu when created.
/// ///
/// # Arguments /// # 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. /// * `kernel_entry_point` - Kernel entry point address in guest memory and boot protocol used.
/// * `vm_memory` - Guest memory. /// * `vm_memory` - Guest memory.
/// * `cpuid` - (x86_64) CpuId, wrapper over the `kvm_cpuid2` structure.
pub fn configure( pub fn configure(
&mut self, &self,
_kernel_entry_point: Option<EntryPoint>,
_vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
) -> 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,
kernel_entry_point: Option<EntryPoint>, kernel_entry_point: Option<EntryPoint>,
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>, vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
cpuid: CpuId, #[cfg(target_arch = "x86_64")] cpuid: CpuId,
) -> Result<()> { ) -> Result<()> {
let mut cpuid = cpuid; #[cfg(target_arch = "aarch64")]
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(self.id)); arch::configure_vcpu(&self.fd, self.id, kernel_entry_point, vm_memory)
self.fd .map_err(Error::VcpuConfiguration)?;
.set_cpuid2(&cpuid)
.map_err(Error::SetSupportedCpusFailed)?; #[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(()) Ok(())
} }