dragonball: add kvm context

KVM operation context for virtual machines.

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
Signed-off-by: jingshan <jingshan@linux.alibaba.com>
Signed-off-by: Chao Wu <chaowu@linux.alibaba.com>
Signed-off-by: wllenyj <wllenyj@linux.alibaba.com>
This commit is contained in:
wllenyj 2022-05-14 23:49:52 +08:00 committed by Chao Wu
parent e89e6507a4
commit 468c73b3cb
3 changed files with 286 additions and 3 deletions

View File

@ -14,6 +14,37 @@ use dbs_virtio_devices::Error as VirtIoError;
use crate::device_manager;
/// Shorthand result type for internal VMM commands.
pub type Result<T> = std::result::Result<T, Error>;
/// Errors associated with the VMM internal logic.
///
/// These errors cannot be generated by direct user input, but can result from bad configuration
/// of the host (for example if Dragonball doesn't have permissions to open the KVM fd).
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Failure occurs in issuing KVM ioctls and errors will be returned from kvm_ioctls lib.
#[error("failure in issuing KVM ioctl command")]
Kvm(#[source] kvm_ioctls::Error),
/// The host kernel reports an unsupported KVM API version.
#[error("unsupported KVM version {0}")]
KvmApiVersion(i32),
/// Cannot initialize the KVM context due to missing capabilities.
#[error("missing KVM capability")]
KvmCap(kvm_ioctls::Cap),
#[cfg(target_arch = "x86_64")]
#[error("failed to configure MSRs")]
/// Cannot configure MSRs
GuestMSRs(dbs_arch::msr::Error),
/// MSR inner error
#[error("MSR inner error")]
Msr(vmm_sys_util::fam::Error),
}
/// Errors associated with starting the instance.
#[derive(Debug, thiserror::Error)]
pub enum StartMicrovmError {

View File

@ -0,0 +1,251 @@
// Copyright (C) 2022 Alibaba Cloud. All rights reserved.
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// 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 THIRD-PARTY file.
#![allow(dead_code)]
use kvm_bindings::KVM_API_VERSION;
use kvm_ioctls::{Cap, Kvm, VmFd};
use std::os::unix::io::{FromRawFd, RawFd};
use crate::error::{Error, Result};
/// Describes a KVM context that gets attached to the micro VM instance.
/// It gives access to the functionality of the KVM wrapper as long as every required
/// KVM capability is present on the host.
pub struct KvmContext {
kvm: Kvm,
max_memslots: usize,
#[cfg(target_arch = "x86_64")]
supported_msrs: kvm_bindings::MsrList,
}
impl KvmContext {
/// Create a new KVM context object, using the provided `kvm_fd` if one is presented.
pub fn new(kvm_fd: Option<RawFd>) -> Result<Self> {
let kvm = if let Some(fd) = kvm_fd {
// Safe because we expect kvm_fd to contain a valid fd number when is_some() == true.
unsafe { Kvm::from_raw_fd(fd) }
} else {
Kvm::new().map_err(Error::Kvm)?
};
if kvm.get_api_version() != KVM_API_VERSION as i32 {
return Err(Error::KvmApiVersion(kvm.get_api_version()));
}
Self::check_cap(&kvm, Cap::Irqchip)?;
Self::check_cap(&kvm, Cap::Irqfd)?;
Self::check_cap(&kvm, Cap::Ioeventfd)?;
Self::check_cap(&kvm, Cap::UserMemory)?;
#[cfg(target_arch = "x86_64")]
Self::check_cap(&kvm, Cap::SetTssAddr)?;
#[cfg(target_arch = "x86_64")]
let supported_msrs = dbs_arch::msr::supported_guest_msrs(&kvm).map_err(Error::GuestMSRs)?;
let max_memslots = kvm.get_nr_memslots();
Ok(KvmContext {
kvm,
max_memslots,
#[cfg(target_arch = "x86_64")]
supported_msrs,
})
}
/// Get underlying KVM object to access kvm-ioctls interfaces.
pub fn kvm(&self) -> &Kvm {
&self.kvm
}
/// Get the maximum number of memory slots reported by this KVM context.
pub fn max_memslots(&self) -> usize {
self.max_memslots
}
/// Create a virtual machine object.
pub fn create_vm(&self) -> Result<VmFd> {
self.kvm.create_vm().map_err(Error::Kvm)
}
/// Get the max vcpu count supported by kvm
pub fn get_max_vcpus(&self) -> usize {
self.kvm.get_max_vcpus()
}
fn check_cap(kvm: &Kvm, cap: Cap) -> std::result::Result<(), Error> {
if !kvm.check_extension(cap) {
return Err(Error::KvmCap(cap));
}
Ok(())
}
}
#[cfg(target_arch = "x86_64")]
mod x86_64 {
use super::*;
use dbs_arch::msr::*;
use kvm_bindings::{kvm_msr_entry, CpuId, MsrList, Msrs};
use std::collections::HashSet;
impl KvmContext {
/// Get information about supported CPUID of x86 processor.
pub fn supported_cpuid(
&self,
max_entries_count: usize,
) -> std::result::Result<CpuId, kvm_ioctls::Error> {
self.kvm.get_supported_cpuid(max_entries_count)
}
/// Get information about supported MSRs of x86 processor.
pub fn supported_msrs(
&self,
_max_entries_count: usize,
) -> std::result::Result<MsrList, kvm_ioctls::Error> {
Ok(self.supported_msrs.clone())
}
// It's very sensible to manipulate MSRs, so please be careful to change code below.
fn build_msrs_list(kvm: &Kvm) -> Result<Msrs> {
let mut mset: HashSet<u32> = HashSet::new();
let supported_msr_list = kvm.get_msr_index_list().map_err(super::Error::Kvm)?;
for msr in supported_msr_list.as_slice() {
mset.insert(*msr);
}
let mut msrs = vec![
MSR_IA32_APICBASE,
MSR_IA32_SYSENTER_CS,
MSR_IA32_SYSENTER_ESP,
MSR_IA32_SYSENTER_EIP,
MSR_IA32_CR_PAT,
];
let filters_list = vec![
MSR_STAR,
MSR_VM_HSAVE_PA,
MSR_TSC_AUX,
MSR_IA32_TSC_ADJUST,
MSR_IA32_TSCDEADLINE,
MSR_IA32_MISC_ENABLE,
MSR_IA32_BNDCFGS,
MSR_IA32_SPEC_CTRL,
];
for msr in filters_list {
if mset.contains(&msr) {
msrs.push(msr);
}
}
// TODO: several msrs are optional.
// TODO: Since our guests don't support nested-vmx, LMCE nor SGX for now.
// msrs.push(MSR_IA32_FEATURE_CONTROL);
msrs.push(MSR_CSTAR);
msrs.push(MSR_KERNEL_GS_BASE);
msrs.push(MSR_SYSCALL_MASK);
msrs.push(MSR_LSTAR);
msrs.push(MSR_IA32_TSC);
msrs.push(MSR_KVM_SYSTEM_TIME_NEW);
msrs.push(MSR_KVM_WALL_CLOCK_NEW);
// FIXME: check if it's supported.
msrs.push(MSR_KVM_ASYNC_PF_EN);
msrs.push(MSR_KVM_PV_EOI_EN);
msrs.push(MSR_KVM_STEAL_TIME);
msrs.push(MSR_CORE_PERF_FIXED_CTR_CTRL);
msrs.push(MSR_CORE_PERF_GLOBAL_CTRL);
msrs.push(MSR_CORE_PERF_GLOBAL_STATUS);
msrs.push(MSR_CORE_PERF_GLOBAL_OVF_CTRL);
const MAX_FIXED_COUNTERS: u32 = 3;
for i in 0..MAX_FIXED_COUNTERS {
msrs.push(MSR_CORE_PERF_FIXED_CTR0 + i);
}
// FIXME: skip MCE for now.
let mtrr_msrs = vec![
MSR_MTRRdefType,
MSR_MTRRfix64K_00000,
MSR_MTRRfix16K_80000,
MSR_MTRRfix16K_A0000,
MSR_MTRRfix4K_C0000,
MSR_MTRRfix4K_C8000,
MSR_MTRRfix4K_D0000,
MSR_MTRRfix4K_D8000,
MSR_MTRRfix4K_E0000,
MSR_MTRRfix4K_E8000,
MSR_MTRRfix4K_F0000,
MSR_MTRRfix4K_F8000,
];
for mtrr in mtrr_msrs {
msrs.push(mtrr);
}
const MSR_MTRRCAP_VCNT: u32 = 8;
for i in 0..MSR_MTRRCAP_VCNT {
msrs.push(0x200 + 2 * i);
msrs.push(0x200 + 2 * i + 1);
}
let msrs: Vec<kvm_msr_entry> = msrs
.iter()
.map(|reg| kvm_msr_entry {
index: *reg,
reserved: 0,
data: 0,
})
.collect();
Msrs::from_entries(&msrs).map_err(super::Error::Msr)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use kvm_ioctls::Kvm;
use std::fs::File;
use std::os::unix::fs::MetadataExt;
use std::os::unix::io::{AsRawFd, FromRawFd};
#[test]
fn test_create_kvm_context() {
let c = KvmContext::new(None).unwrap();
assert!(c.max_memslots >= 32);
let kvm = Kvm::new().unwrap();
let f = unsafe { File::from_raw_fd(kvm.as_raw_fd()) };
let m1 = f.metadata().unwrap();
let m2 = File::open("/dev/kvm").unwrap().metadata().unwrap();
assert_eq!(m1.dev(), m2.dev());
assert_eq!(m1.ino(), m2.ino());
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_get_supported_cpu_id() {
let c = KvmContext::new(None).unwrap();
let _ = c
.supported_cpuid(kvm_bindings::KVM_MAX_CPUID_ENTRIES)
.expect("failed to get supported CPUID");
assert!(c.supported_cpuid(0).is_err());
}
#[test]
fn test_create_vm() {
let c = KvmContext::new(None).unwrap();
let _ = c.create_vm().unwrap();
}
}

View File

@ -6,7 +6,6 @@
//! Machine(KVM) which is optimized for container workloads.
#![warn(missing_docs)]
//TODO: Remove this, after the rest of dragonball has been committed.
#![allow(dead_code)]
@ -18,12 +17,14 @@ pub mod config_manager;
pub mod device_manager;
/// Errors related to Virtual machine manager.
pub mod error;
/// Signal handler for virtual machines.
pub mod signal_handler;
/// KVM operation context for virtual machines.
pub mod kvm_context;
/// Metrics system.
pub mod metric;
/// Resource manager for virtual machines.
pub mod resource_manager;
/// Signal handler for virtual machines.
pub mod signal_handler;
/// Virtual machine manager for virtual machines.
pub mod vm;