mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
arch, pci, vmm: Initial switch to the hypervisor crate
Start moving the vmm, arch and pci crates to being hypervisor agnostic by using the hypervisor trait and abstractions. This is not a complete switch and there are still some remaining KVM dependencies. Signed-off-by: Muminul Islam <muislam@microsoft.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
committed by
Samuel Ortiz
parent
c48d0c1a67
commit
e4dee57e81
@@ -1,9 +1,9 @@
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::{boxed::Box, result};
|
||||
|
||||
use kvm_ioctls::{DeviceFd, VmFd};
|
||||
use std::sync::Arc;
|
||||
use std::{boxed::Box, result};
|
||||
|
||||
use super::gicv2::GICv2;
|
||||
use super::gicv3::GICv3;
|
||||
@@ -12,7 +12,7 @@ use super::gicv3::GICv3;
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
||||
CreateGIC(kvm_ioctls::Error),
|
||||
CreateGIC(hypervisor::HypervisorVmError),
|
||||
/// Error while setting device attributes for the GIC.
|
||||
SetDeviceAttribute(kvm_ioctls::Error),
|
||||
}
|
||||
@@ -51,7 +51,7 @@ pub trait GICDevice: Send + Sync {
|
||||
Self: Sized;
|
||||
|
||||
/// Initialize a GIC device
|
||||
fn init_device(vm: &VmFd) -> Result<DeviceFd>
|
||||
fn init_device(vm: &Arc<dyn hypervisor::Vm>) -> Result<DeviceFd>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
@@ -120,7 +120,7 @@ pub trait GICDevice: Send + Sync {
|
||||
}
|
||||
|
||||
/// Method to initialize the GIC device
|
||||
fn new(vm: &VmFd, vcpu_count: u64) -> Result<Box<dyn GICDevice>>
|
||||
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GICDevice>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
@@ -140,7 +140,7 @@ pub trait GICDevice: Send + Sync {
|
||||
///
|
||||
/// It will try to create by default a GICv3 device. If that fails it will try
|
||||
/// to fall-back to a GICv2 device.
|
||||
pub fn create_gic(vm: &VmFd, vcpu_count: u64) -> Result<Box<dyn GICDevice>> {
|
||||
pub fn create_gic(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GICDevice>> {
|
||||
GICv3::new(vm, vcpu_count).or_else(|_| GICv2::new(vm, vcpu_count))
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ use kvm_ioctls::*;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use vm_memory::{
|
||||
Address, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic, GuestMemoryMmap,
|
||||
GuestUsize,
|
||||
@@ -41,10 +42,10 @@ pub enum Error {
|
||||
REGSConfiguration(regs::Error),
|
||||
|
||||
/// Error fetching prefered target
|
||||
VcpuArmPreferredTarget(kvm_ioctls::Error),
|
||||
VcpuArmPreferredTarget(hypervisor::HypervisorVmError),
|
||||
|
||||
/// Error doing Vcpu Init on Arm.
|
||||
VcpuArmInit(kvm_ioctls::Error),
|
||||
VcpuArmInit(hypervisor::HypervisorCpuError),
|
||||
}
|
||||
|
||||
impl From<Error> for super::Error {
|
||||
@@ -63,9 +64,9 @@ pub struct EntryPoint {
|
||||
|
||||
/// Configure the specified VCPU, and return its MPIDR.
|
||||
pub fn configure_vcpu(
|
||||
fd: &VcpuFd,
|
||||
fd: &Arc<dyn hypervisor::Vcpu>,
|
||||
id: u8,
|
||||
vm_fd: &VmFd,
|
||||
vm_fd: &Arc<dyn hypervisor::Vm>,
|
||||
kernel_entry_point: Option<EntryPoint>,
|
||||
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
) -> super::Result<u64> {
|
||||
@@ -138,7 +139,7 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(unused_variables)]
|
||||
pub fn configure_system<T: DeviceInfoForFDT + Clone + Debug>(
|
||||
vm_fd: &VmFd,
|
||||
vm_fd: &Arc<dyn hypervisor::Vm>,
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
cmdline_cstring: &CStr,
|
||||
vcpu_count: u64,
|
||||
@@ -200,13 +201,6 @@ pub fn get_host_cpu_phys_bits() -> u8 {
|
||||
40
|
||||
}
|
||||
|
||||
pub fn check_required_kvm_extensions(kvm: &Kvm) -> super::Result<()> {
|
||||
if !kvm.check_extension(Cap::SignalMsi) {
|
||||
return Err(super::Error::CapabilityMissing(Cap::SignalMsi));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
use std::{mem, result};
|
||||
|
||||
use super::get_fdt_addr;
|
||||
use kvm_bindings::{
|
||||
user_pt_regs, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG_CRM_MASK,
|
||||
@@ -16,16 +14,17 @@ use kvm_bindings::{
|
||||
KVM_REG_ARM_CORE, KVM_REG_SIZE_U64,
|
||||
};
|
||||
use kvm_ioctls::VcpuFd;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{mem, result};
|
||||
use vm_memory::GuestMemoryMmap;
|
||||
|
||||
/// Errors thrown while setting aarch64 registers.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to set core register (PC, PSTATE or general purpose ones).
|
||||
SetCoreRegister(kvm_ioctls::Error),
|
||||
SetCoreRegister(hypervisor::HypervisorCpuError),
|
||||
/// Failed to get a system register.
|
||||
GetSysRegister(kvm_ioctls::Error),
|
||||
GetSysRegister(hypervisor::HypervisorCpuError),
|
||||
}
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -122,7 +121,12 @@ arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
||||
/// * `cpu_id` - Index of current vcpu.
|
||||
/// * `boot_ip` - Starting instruction pointer.
|
||||
/// * `mem` - Reserved DRAM for current VM.
|
||||
pub fn setup_regs(vcpu: &VcpuFd, cpu_id: u8, boot_ip: u64, mem: &GuestMemoryMmap) -> Result<()> {
|
||||
pub fn setup_regs(
|
||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||
cpu_id: u8,
|
||||
boot_ip: u64,
|
||||
mem: &GuestMemoryMmap,
|
||||
) -> Result<()> {
|
||||
// Get the register index of the PSTATE (Processor State) register.
|
||||
vcpu.set_one_reg(arm64_core_reg!(pstate), PSTATE_FAULT_BITS_64)
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
@@ -148,7 +152,7 @@ pub fn setup_regs(vcpu: &VcpuFd, cpu_id: u8, boot_ip: u64, mem: &GuestMemoryMmap
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
pub fn read_mpidr(vcpu: &VcpuFd) -> Result<u64> {
|
||||
pub fn read_mpidr(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<u64> {
|
||||
vcpu.get_one_reg(MPIDR_EL1).map_err(Error::GetSysRegister)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate hypervisor;
|
||||
extern crate kvm_bindings;
|
||||
extern crate kvm_ioctls;
|
||||
extern crate libc;
|
||||
@@ -26,7 +27,6 @@ extern crate acpi_tables;
|
||||
extern crate arch_gen;
|
||||
extern crate linux_loader;
|
||||
|
||||
use kvm_ioctls::*;
|
||||
use std::fmt;
|
||||
use std::result;
|
||||
|
||||
@@ -57,8 +57,6 @@ pub enum Error {
|
||||
ModlistSetup(vm_memory::GuestMemoryError),
|
||||
/// RSDP Beyond Guest Memory
|
||||
RSDPPastRamEnd,
|
||||
/// Capability missing
|
||||
CapabilityMissing(Cap),
|
||||
}
|
||||
|
||||
/// Type for returning public functions outcome.
|
||||
@@ -89,9 +87,9 @@ pub mod aarch64;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub use aarch64::{
|
||||
arch_memory_regions, check_required_kvm_extensions, configure_system, configure_vcpu,
|
||||
fdt::DeviceInfoForFDT, get_host_cpu_phys_bits, get_kernel_start, layout,
|
||||
layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, EntryPoint,
|
||||
arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFDT,
|
||||
get_host_cpu_phys_bits, get_kernel_start, layout, layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE,
|
||||
layout::IRQ_MAX, EntryPoint,
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -99,9 +97,9 @@ pub mod x86_64;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use x86_64::{
|
||||
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,
|
||||
arch_memory_regions, 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)`.
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
|
||||
// For GDT details see arch/x86/include/asm/segment.h
|
||||
|
||||
use kvm_bindings::kvm_segment;
|
||||
use hypervisor::x86_64::SegmentRegister;
|
||||
|
||||
/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
|
||||
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
|
||||
@@ -88,14 +87,14 @@ fn get_type(entry: u64) -> u8 {
|
||||
((entry & 0x00000F0000000000) >> 40) as u8
|
||||
}
|
||||
|
||||
/// Automatically build the kvm struct for SET_SREGS from the kernel bit fields.
|
||||
/// Automatically build the struct for SET_SREGS from the kernel bit fields.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `entry` - The gdt entry.
|
||||
/// * `table_index` - Index of the entry in the gdt table.
|
||||
pub fn kvm_segment_from_gdt(entry: u64, table_index: u8) -> kvm_segment {
|
||||
kvm_segment {
|
||||
pub fn segment_from_gdt(entry: u64, table_index: u8) -> SegmentRegister {
|
||||
SegmentRegister {
|
||||
base: get_base(entry),
|
||||
limit: get_limit(entry),
|
||||
selector: (table_index * 8) as u16,
|
||||
@@ -122,7 +121,7 @@ mod tests {
|
||||
#[test]
|
||||
fn field_parse() {
|
||||
let gdt = gdt_entry(0xA09B, 0x100000, 0xfffff);
|
||||
let seg = kvm_segment_from_gdt(gdt, 0);
|
||||
let seg = segment_from_gdt(gdt, 0);
|
||||
// 0xA09B
|
||||
// 'A'
|
||||
assert_eq!(0x1, seg.g);
|
||||
|
||||
@@ -8,19 +8,19 @@
|
||||
use std::io::Cursor;
|
||||
use std::mem;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
use kvm_bindings::kvm_lapic_state;
|
||||
use kvm_ioctls;
|
||||
use hypervisor::x86_64::LapicState;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
GetLapic(kvm_ioctls::Error),
|
||||
SetLapic(kvm_ioctls::Error),
|
||||
GetLapic(anyhow::Error),
|
||||
SetLapic(anyhow::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
pub type Result<T> = result::Result<T, hypervisor::HypervisorCpuError>;
|
||||
|
||||
// Defines poached from apicdef.h kernel header.
|
||||
const APIC_LVT0: usize = 0x350;
|
||||
@@ -28,7 +28,7 @@ const APIC_LVT1: usize = 0x360;
|
||||
const APIC_MODE_NMI: u32 = 0x4;
|
||||
const APIC_MODE_EXTINT: u32 = 0x7;
|
||||
|
||||
fn get_klapic_reg(klapic: &kvm_lapic_state, reg_offset: usize) -> u32 {
|
||||
fn get_klapic_reg(klapic: &LapicState, reg_offset: usize) -> u32 {
|
||||
let sliceu8 = unsafe {
|
||||
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
||||
// Cursors are only readable on arrays of u8, not i8(c_char).
|
||||
@@ -41,7 +41,7 @@ fn get_klapic_reg(klapic: &kvm_lapic_state, reg_offset: usize) -> u32 {
|
||||
.expect("Failed to read klapic register")
|
||||
}
|
||||
|
||||
fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) {
|
||||
fn set_klapic_reg(klapic: &mut LapicState, reg_offset: usize, value: u32) {
|
||||
let sliceu8 = unsafe {
|
||||
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
||||
// Cursors are only readable on arrays of u8, not i8(c_char).
|
||||
@@ -62,8 +62,8 @@ fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `vcpu` - The VCPU object to configure.
|
||||
pub fn set_lint(vcpu: &kvm_ioctls::VcpuFd) -> Result<()> {
|
||||
let mut klapic = vcpu.get_lapic().map_err(Error::GetLapic)?;
|
||||
pub fn set_lint(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
||||
let mut klapic = vcpu.get_lapic()?;
|
||||
|
||||
let lvt_lint0 = get_klapic_reg(&klapic, APIC_LVT0);
|
||||
set_klapic_reg(
|
||||
@@ -78,24 +78,23 @@ pub fn set_lint(vcpu: &kvm_ioctls::VcpuFd) -> Result<()> {
|
||||
set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI),
|
||||
);
|
||||
|
||||
vcpu.set_lapic(&klapic).map_err(Error::SetLapic)
|
||||
vcpu.set_lapic(&klapic)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate kvm_ioctls;
|
||||
|
||||
extern crate rand;
|
||||
use self::rand::Rng;
|
||||
|
||||
use super::*;
|
||||
use kvm_ioctls::Kvm;
|
||||
|
||||
const KVM_APIC_REG_SIZE: usize = 0x400;
|
||||
|
||||
#[test]
|
||||
fn test_set_and_get_klapic_reg() {
|
||||
let reg_offset = 0x340;
|
||||
let mut klapic = kvm_lapic_state::default();
|
||||
let mut klapic = LapicState::default();
|
||||
set_klapic_reg(&mut klapic, reg_offset, 3);
|
||||
let value = get_klapic_reg(&klapic, reg_offset);
|
||||
assert_eq!(value, 3);
|
||||
@@ -105,7 +104,7 @@ mod tests {
|
||||
#[should_panic]
|
||||
fn test_set_and_get_klapic_out_of_bounds() {
|
||||
let reg_offset = KVM_APIC_REG_SIZE + 10;
|
||||
let mut klapic = kvm_lapic_state::default();
|
||||
let mut klapic = LapicState::default();
|
||||
set_klapic_reg(&mut klapic, reg_offset, 3);
|
||||
}
|
||||
|
||||
@@ -122,13 +121,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_setlint() {
|
||||
let kvm = kvm_ioctls::Kvm::new().unwrap();
|
||||
assert!(kvm.check_extension(kvm_ioctls::Cap::Irqchip));
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
//the get_lapic ioctl will fail if there is no irqchip created beforehand.
|
||||
let kvm = hypervisor::kvm::KvmHyperVisor::new().unwrap();
|
||||
let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||
let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||
assert!(hv.check_capability(hypervisor::kvm::Cap::Irqchip));
|
||||
// Calling get_lapic will fail if there is no irqchip before hand.
|
||||
assert!(vm.create_irq_chip().is_ok());
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
let klapic_before: kvm_lapic_state = vcpu.get_lapic().unwrap();
|
||||
let klapic_before: LapicState = vcpu.get_lapic().unwrap();
|
||||
|
||||
// Compute the value that is expected to represent LVT0 and LVT1.
|
||||
let lint0 = get_klapic_reg(&klapic_before, APIC_LVT0);
|
||||
@@ -139,20 +139,10 @@ mod tests {
|
||||
set_lint(&vcpu).unwrap();
|
||||
|
||||
// Compute the value that represents LVT0 and LVT1 after set_lint.
|
||||
let klapic_actual: kvm_lapic_state = vcpu.get_lapic().unwrap();
|
||||
let klapic_actual: LapicState = vcpu.get_lapic().unwrap();
|
||||
let lint0_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT0);
|
||||
let lint1_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT1);
|
||||
assert_eq!(lint0_mode_expected, lint0_mode_actual);
|
||||
assert_eq!(lint1_mode_expected, lint1_mode_actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_setlint_fails() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
// 'get_lapic' ioctl triggered by the 'set_lint' function will fail if there is no
|
||||
// irqchip created beforehand.
|
||||
assert!(set_lint(&vcpu).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
|
||||
use std::sync::Arc;
|
||||
mod gdt;
|
||||
pub mod interrupts;
|
||||
pub mod layout;
|
||||
@@ -15,8 +15,7 @@ mod mptable;
|
||||
pub mod regs;
|
||||
use crate::InitramfsConfig;
|
||||
use crate::RegionType;
|
||||
use kvm_bindings::CpuId;
|
||||
use kvm_ioctls::*;
|
||||
use hypervisor::CpuId;
|
||||
use linux_loader::loader::bootparam::{boot_params, setup_header};
|
||||
use linux_loader::loader::elf::start_info::{
|
||||
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
|
||||
@@ -111,10 +110,10 @@ pub enum Error {
|
||||
MSRSConfiguration(regs::Error),
|
||||
|
||||
/// The call to KVM_SET_CPUID2 failed.
|
||||
SetSupportedCpusFailed(kvm_ioctls::Error),
|
||||
SetSupportedCpusFailed(anyhow::Error),
|
||||
|
||||
/// Cannot set the local interruption due to bad configuration.
|
||||
LocalIntConfiguration(interrupts::Error),
|
||||
LocalIntConfiguration(anyhow::Error),
|
||||
}
|
||||
|
||||
impl From<Error> for super::Error {
|
||||
@@ -200,7 +199,7 @@ impl CpuidPatch {
|
||||
}
|
||||
|
||||
pub fn configure_vcpu(
|
||||
fd: &VcpuFd,
|
||||
fd: &Arc<dyn hypervisor::Vcpu>,
|
||||
id: u8,
|
||||
kernel_entry_point: Option<EntryPoint>,
|
||||
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
@@ -210,7 +209,7 @@ pub fn configure_vcpu(
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
|
||||
fd.set_cpuid2(&cpuid)
|
||||
.map_err(Error::SetSupportedCpusFailed)?;
|
||||
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
||||
|
||||
regs::setup_msrs(fd).map_err(Error::MSRSConfiguration)?;
|
||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||
@@ -227,7 +226,7 @@ pub fn configure_vcpu(
|
||||
regs::setup_sregs(&vm_memory.memory(), fd, kernel_entry_point.protocol)
|
||||
.map_err(Error::SREGSConfiguration)?;
|
||||
}
|
||||
interrupts::set_lint(fd).map_err(Error::LocalIntConfiguration)?;
|
||||
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -616,19 +615,6 @@ pub fn get_host_cpu_phys_bits() -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_required_kvm_extensions(kvm: &Kvm) -> super::Result<()> {
|
||||
if !kvm.check_extension(Cap::SignalMsi) {
|
||||
return Err(super::Error::CapabilityMissing(Cap::SignalMsi));
|
||||
}
|
||||
if !kvm.check_extension(Cap::TscDeadlineTimer) {
|
||||
return Err(super::Error::CapabilityMissing(Cap::TscDeadlineTimer));
|
||||
}
|
||||
if !kvm.check_extension(Cap::SplitIrqchip) {
|
||||
return Err(super::Error::CapabilityMissing(Cap::SplitIrqchip));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_cpuid_topology(
|
||||
cpuid: &mut CpuId,
|
||||
threads_per_core: u8,
|
||||
|
||||
@@ -6,35 +6,29 @@
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{mem, result};
|
||||
|
||||
use super::gdt::{gdt_entry, kvm_segment_from_gdt};
|
||||
use super::gdt::{gdt_entry, segment_from_gdt};
|
||||
use super::BootProtocol;
|
||||
use arch_gen::x86::msr_index;
|
||||
use kvm_bindings::{kvm_fpu, kvm_msr_entry, kvm_regs, kvm_sregs, Msrs};
|
||||
use kvm_ioctls::VcpuFd;
|
||||
use hypervisor::x86_64::{FpuState, SpecialRegisters, StandardRegisters};
|
||||
use layout::{
|
||||
BOOT_GDT_START, BOOT_IDT_START, PDE_START, PDPTE_START, PML4_START, PML5_START, PVH_INFO_START,
|
||||
};
|
||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryMmap};
|
||||
|
||||
// MTRR constants
|
||||
const MTRR_ENABLE: u64 = 0x800; // IA32_MTRR_DEF_TYPE MSR: E (MTRRs enabled) flag, bit 11
|
||||
const MTRR_MEM_TYPE_WB: u64 = 0x6;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to get SREGs for this CPU.
|
||||
GetStatusRegisters(kvm_ioctls::Error),
|
||||
GetStatusRegisters(hypervisor::HypervisorCpuError),
|
||||
/// Failed to set base registers for this CPU.
|
||||
SetBaseRegisters(kvm_ioctls::Error),
|
||||
SetBaseRegisters(hypervisor::HypervisorCpuError),
|
||||
/// Failed to configure the FPU.
|
||||
SetFPURegisters(kvm_ioctls::Error),
|
||||
SetFPURegisters(hypervisor::HypervisorCpuError),
|
||||
/// Setting up MSRs failed.
|
||||
SetModelSpecificRegisters(kvm_ioctls::Error),
|
||||
SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
|
||||
/// Failed to set SREGs for this CPU.
|
||||
SetStatusRegisters(kvm_ioctls::Error),
|
||||
SetStatusRegisters(hypervisor::HypervisorCpuError),
|
||||
/// Checking the GDT address failed.
|
||||
CheckGDTAddr,
|
||||
/// Writing the GDT to RAM failed.
|
||||
@@ -58,8 +52,8 @@ pub type Result<T> = result::Result<T, Error>;
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
pub fn setup_fpu(vcpu: &VcpuFd) -> Result<()> {
|
||||
let fpu: kvm_fpu = kvm_fpu {
|
||||
pub fn setup_fpu(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
||||
let fpu: FpuState = FpuState {
|
||||
fcw: 0x37f,
|
||||
mxcsr: 0x1f80,
|
||||
..Default::default()
|
||||
@@ -73,8 +67,8 @@ pub fn setup_fpu(vcpu: &VcpuFd) -> Result<()> {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
pub fn setup_msrs(vcpu: &VcpuFd) -> Result<()> {
|
||||
vcpu.set_msrs(&boot_msr_entries())
|
||||
pub fn setup_msrs(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
||||
vcpu.set_msrs(&hypervisor::kvm::x86_64::boot_msr_entries())
|
||||
.map_err(Error::SetModelSpecificRegisters)?;
|
||||
|
||||
Ok(())
|
||||
@@ -89,22 +83,22 @@ pub fn setup_msrs(vcpu: &VcpuFd) -> Result<()> {
|
||||
/// * `boot_sp` - Starting stack pointer.
|
||||
/// * `boot_si` - Must point to zero page address per Linux ABI.
|
||||
pub fn setup_regs(
|
||||
vcpu: &VcpuFd,
|
||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||
boot_ip: u64,
|
||||
boot_sp: u64,
|
||||
boot_si: u64,
|
||||
boot_prot: BootProtocol,
|
||||
) -> Result<()> {
|
||||
let regs: kvm_regs = match boot_prot {
|
||||
let regs: StandardRegisters = match boot_prot {
|
||||
// Configure regs as required by PVH boot protocol.
|
||||
BootProtocol::PvhBoot => kvm_regs {
|
||||
BootProtocol::PvhBoot => StandardRegisters {
|
||||
rflags: 0x0000000000000002u64,
|
||||
rbx: PVH_INFO_START.raw_value(),
|
||||
rip: boot_ip,
|
||||
..Default::default()
|
||||
},
|
||||
// Configure regs as required by Linux 64-bit boot protocol.
|
||||
BootProtocol::LinuxBoot => kvm_regs {
|
||||
BootProtocol::LinuxBoot => StandardRegisters {
|
||||
rflags: 0x0000000000000002u64,
|
||||
rip: boot_ip,
|
||||
rsp: boot_sp,
|
||||
@@ -122,8 +116,12 @@ pub fn setup_regs(
|
||||
///
|
||||
/// * `mem` - The memory that will be passed to the guest.
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
pub fn setup_sregs(mem: &GuestMemoryMmap, vcpu: &VcpuFd, boot_prot: BootProtocol) -> Result<()> {
|
||||
let mut sregs: kvm_sregs = vcpu.get_sregs().map_err(Error::GetStatusRegisters)?;
|
||||
pub fn setup_sregs(
|
||||
mem: &GuestMemoryMmap,
|
||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||
boot_prot: BootProtocol,
|
||||
) -> Result<()> {
|
||||
let mut sregs: SpecialRegisters = vcpu.get_sregs().map_err(Error::GetStatusRegisters)?;
|
||||
|
||||
configure_segments_and_sregs(mem, &mut sregs, boot_prot)?;
|
||||
|
||||
@@ -164,7 +162,7 @@ fn write_idt_value(val: u64, guest_mem: &GuestMemoryMmap) -> Result<()> {
|
||||
|
||||
fn configure_segments_and_sregs(
|
||||
mem: &GuestMemoryMmap,
|
||||
sregs: &mut kvm_sregs,
|
||||
sregs: &mut SpecialRegisters,
|
||||
boot_prot: BootProtocol,
|
||||
) -> Result<()> {
|
||||
let gdt_table: [u64; BOOT_GDT_MAX as usize] = match boot_prot {
|
||||
@@ -188,9 +186,9 @@ fn configure_segments_and_sregs(
|
||||
}
|
||||
};
|
||||
|
||||
let code_seg = kvm_segment_from_gdt(gdt_table[1], 1);
|
||||
let data_seg = kvm_segment_from_gdt(gdt_table[2], 2);
|
||||
let tss_seg = kvm_segment_from_gdt(gdt_table[3], 3);
|
||||
let code_seg = segment_from_gdt(gdt_table[1], 1);
|
||||
let data_seg = segment_from_gdt(gdt_table[2], 2);
|
||||
let tss_seg = segment_from_gdt(gdt_table[3], 3);
|
||||
|
||||
// Write segments
|
||||
write_gdt_table(&gdt_table[..], mem)?;
|
||||
@@ -224,9 +222,8 @@ fn configure_segments_and_sregs(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut kvm_sregs) -> Result<()> {
|
||||
fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut SpecialRegisters) -> Result<()> {
|
||||
// Puts PML5 or PML4 right after zero page but aligned to 4k.
|
||||
|
||||
if unsafe { std::arch::x86_64::__cpuid(7).ecx } & (1 << 16) != 0 {
|
||||
// Entry covering VA [0..256TB)
|
||||
mem.write_obj(PML4_START.raw_value() | 0x03, PML5_START)
|
||||
@@ -259,52 +256,12 @@ fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut kvm_sregs) -> Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! kvm_msr {
|
||||
($msr:expr) => {
|
||||
kvm_msr_entry {
|
||||
index: $msr,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! kvm_msr_data {
|
||||
($msr:expr, $data:expr) => {
|
||||
kvm_msr_entry {
|
||||
index: $msr,
|
||||
data: $data,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn boot_msr_entries() -> Msrs {
|
||||
Msrs::from_entries(&[
|
||||
kvm_msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
||||
kvm_msr!(msr_index::MSR_IA32_SYSENTER_ESP),
|
||||
kvm_msr!(msr_index::MSR_IA32_SYSENTER_EIP),
|
||||
kvm_msr!(msr_index::MSR_STAR),
|
||||
kvm_msr!(msr_index::MSR_CSTAR),
|
||||
kvm_msr!(msr_index::MSR_LSTAR),
|
||||
kvm_msr!(msr_index::MSR_KERNEL_GS_BASE),
|
||||
kvm_msr!(msr_index::MSR_SYSCALL_MASK),
|
||||
kvm_msr!(msr_index::MSR_IA32_TSC),
|
||||
kvm_msr_data!(
|
||||
msr_index::MSR_IA32_MISC_ENABLE,
|
||||
msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64
|
||||
),
|
||||
kvm_msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
||||
])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate kvm_ioctls;
|
||||
extern crate vm_memory;
|
||||
|
||||
use super::*;
|
||||
use kvm_ioctls::Kvm;
|
||||
use vm_memory::{GuestAddress, GuestMemoryMmap};
|
||||
|
||||
fn create_guest_mem() -> GuestMemoryMmap {
|
||||
@@ -317,7 +274,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn segments_and_sregs() {
|
||||
let mut sregs: kvm_sregs = Default::default();
|
||||
let mut sregs: SpecialRegisters = Default::default();
|
||||
let gm = create_guest_mem();
|
||||
configure_segments_and_sregs(&gm, &mut sregs, BootProtocol::LinuxBoot).unwrap();
|
||||
|
||||
@@ -381,7 +338,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn page_tables() {
|
||||
let mut sregs: kvm_sregs = Default::default();
|
||||
let mut sregs: SpecialRegisters = Default::default();
|
||||
let gm = create_guest_mem();
|
||||
setup_page_tables(&gm, &mut sregs).unwrap();
|
||||
|
||||
@@ -408,20 +365,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_setup_fpu() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let kvm = hypervisor::kvm::KvmHyperVisor::new().unwrap();
|
||||
let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||
let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
setup_fpu(&vcpu).unwrap();
|
||||
|
||||
let expected_fpu: kvm_fpu = kvm_fpu {
|
||||
let expected_fpu: FpuState = FpuState {
|
||||
fcw: 0x37f,
|
||||
mxcsr: 0x1f80,
|
||||
..Default::default()
|
||||
};
|
||||
let actual_fpu: kvm_fpu = vcpu.get_fpu().unwrap();
|
||||
let actual_fpu: FpuState = vcpu.get_fpu().unwrap();
|
||||
// TODO: auto-generate kvm related structures with PartialEq on.
|
||||
assert_eq!(expected_fpu.fcw, actual_fpu.fcw);
|
||||
// Setting the mxcsr register from kvm_fpu inside setup_fpu does not influence anything.
|
||||
// Setting the mxcsr register from FpuState inside setup_fpu does not influence anything.
|
||||
// See 'kvm_arch_vcpu_ioctl_set_fpu' from arch/x86/kvm/x86.c.
|
||||
// The mxcsr will stay 0 and the assert below fails. Decide whether or not we should
|
||||
// remove it at all.
|
||||
@@ -430,14 +388,18 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_setup_msrs() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
use hypervisor::arch::x86::msr_index;
|
||||
use hypervisor::x86_64::{MsrEntries, MsrEntry};
|
||||
|
||||
let kvm = hypervisor::kvm::KvmHyperVisor::new().unwrap();
|
||||
let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||
let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
setup_msrs(&vcpu).unwrap();
|
||||
|
||||
// This test will check against the last MSR entry configured (the tenth one).
|
||||
// See create_msr_entries for details.
|
||||
let mut msrs = Msrs::from_entries(&[kvm_msr_entry {
|
||||
let mut msrs = MsrEntries::from_entries(&[MsrEntry {
|
||||
index: msr_index::MSR_IA32_MISC_ENABLE,
|
||||
..Default::default()
|
||||
}]);
|
||||
@@ -450,17 +412,18 @@ mod tests {
|
||||
// Official entries that were setup when we did setup_msrs. We need to assert that the
|
||||
// tenth one (i.e the one with index msr_index::MSR_IA32_MISC_ENABLE has the data we
|
||||
// expect.
|
||||
let entry_vec = boot_msr_entries();
|
||||
let entry_vec = hypervisor::x86_64::boot_msr_entries();
|
||||
assert_eq!(entry_vec.as_slice()[9], msrs.as_slice()[0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_setup_regs() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let kvm = hypervisor::kvm::KvmHyperVisor::new().unwrap();
|
||||
let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||
let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
|
||||
let expected_regs: kvm_regs = kvm_regs {
|
||||
let expected_regs: StandardRegisters = StandardRegisters {
|
||||
rflags: 0x0000000000000002u64,
|
||||
rip: 1,
|
||||
rsp: 2,
|
||||
@@ -478,23 +441,24 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let actual_regs: kvm_regs = vcpu.get_regs().unwrap();
|
||||
let actual_regs: StandardRegisters = vcpu.get_regs().unwrap();
|
||||
assert_eq!(actual_regs, expected_regs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_setup_sregs() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let vm = kvm.create_vm().unwrap();
|
||||
let kvm = hypervisor::kvm::KvmHyperVisor::new().unwrap();
|
||||
let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||
let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||
let vcpu = vm.create_vcpu(0).unwrap();
|
||||
|
||||
let mut expected_sregs: kvm_sregs = vcpu.get_sregs().unwrap();
|
||||
let mut expected_sregs: SpecialRegisters = vcpu.get_sregs().unwrap();
|
||||
let gm = create_guest_mem();
|
||||
configure_segments_and_sregs(&gm, &mut expected_sregs, BootProtocol::LinuxBoot).unwrap();
|
||||
setup_page_tables(&gm, &mut expected_sregs).unwrap();
|
||||
|
||||
setup_sregs(&gm, &vcpu, BootProtocol::LinuxBoot).unwrap();
|
||||
let actual_sregs: kvm_sregs = vcpu.get_sregs().unwrap();
|
||||
let actual_sregs: SpecialRegisters = vcpu.get_sregs().unwrap();
|
||||
assert_eq!(expected_sregs, actual_sregs);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user