use std::sync::Arc;
use crate::{
config::VmConfig,
error::HypervisorError,
traits::Hypervisor,
types::{CpuArch, PlatformCapabilities},
};
use super::ffi::{self, KVM_CAP_MAX_VCPUS, KVM_CAP_NR_MEMSLOTS, KvmSystem};
use super::vm::KvmVm;
pub struct KvmHypervisor {
kvm: Arc<KvmSystem>,
capabilities: PlatformCapabilities,
vcpu_mmap_size: usize,
}
impl KvmHypervisor {
pub fn new() -> Result<Self, HypervisorError> {
let kvm = KvmSystem::open().map_err(|e| {
HypervisorError::InitializationFailed(format!("Failed to open /dev/kvm: {}", e))
})?;
let api_version = kvm.api_version().map_err(|e| {
HypervisorError::InitializationFailed(format!("Failed to get KVM API version: {}", e))
})?;
if api_version != 12 {
return Err(HypervisorError::InitializationFailed(format!(
"Unsupported KVM API version: {} (expected 12)",
api_version
)));
}
let vcpu_mmap_size = kvm.vcpu_mmap_size().map_err(|e| {
HypervisorError::InitializationFailed(format!("Failed to get vCPU mmap size: {}", e))
})?;
let capabilities = Self::detect_capabilities(&kvm)?;
tracing::info!(
"KVM hypervisor initialized: max_vcpus={}, max_memory={}GB, nested_virt={}",
capabilities.max_vcpus,
capabilities.max_memory / (1024 * 1024 * 1024),
capabilities.nested_virt
);
Ok(Self {
kvm: Arc::new(kvm),
capabilities,
vcpu_mmap_size,
})
}
fn detect_capabilities(kvm: &KvmSystem) -> Result<PlatformCapabilities, HypervisorError> {
let max_vcpus = kvm.check_extension(KVM_CAP_MAX_VCPUS).unwrap_or(1) as u32;
let _max_memslots = kvm.check_extension(KVM_CAP_NR_MEMSLOTS).unwrap_or(32);
let max_memory = 512 * 1024 * 1024 * 1024_u64;
let supported_archs = vec![CpuArch::native()];
#[cfg(target_arch = "x86_64")]
let nested_virt = Self::check_nested_virt();
#[cfg(not(target_arch = "x86_64"))]
let nested_virt = false;
Ok(PlatformCapabilities {
supported_archs,
max_vcpus,
max_memory,
nested_virt,
rosetta: false, })
}
#[cfg(target_arch = "x86_64")]
fn check_nested_virt() -> bool {
if let Ok(content) = std::fs::read_to_string("/sys/module/kvm_intel/parameters/nested") {
if content.trim() == "Y" || content.trim() == "1" {
return true;
}
}
if let Ok(content) = std::fs::read_to_string("/sys/module/kvm_amd/parameters/nested") {
if content.trim() == "Y" || content.trim() == "1" {
return true;
}
}
false
}
pub(crate) fn kvm(&self) -> &Arc<KvmSystem> {
&self.kvm
}
pub(crate) fn vcpu_mmap_size(&self) -> usize {
self.vcpu_mmap_size
}
#[must_use]
pub fn supports_arch(&self, arch: CpuArch) -> bool {
self.capabilities.supported_archs.contains(&arch)
}
fn validate_config(&self, config: &VmConfig) -> Result<(), HypervisorError> {
if config.vcpu_count == 0 {
return Err(HypervisorError::invalid_config(
"vCPU count must be > 0".to_string(),
));
}
if config.vcpu_count > self.capabilities.max_vcpus {
return Err(HypervisorError::invalid_config(format!(
"vCPU count {} exceeds maximum {}",
config.vcpu_count, self.capabilities.max_vcpus
)));
}
const MIN_MEMORY: u64 = 16 * 1024 * 1024; if config.memory_size < MIN_MEMORY {
return Err(HypervisorError::invalid_config(format!(
"Memory size {} is below minimum {}",
config.memory_size, MIN_MEMORY
)));
}
if config.memory_size > self.capabilities.max_memory {
return Err(HypervisorError::invalid_config(format!(
"Memory size {} exceeds maximum {}",
config.memory_size, self.capabilities.max_memory
)));
}
crate::types::warn_memory_exceeds_host_half(config.memory_size);
if !self.supports_arch(config.arch) {
return Err(HypervisorError::invalid_config(format!(
"Architecture {:?} is not supported",
config.arch
)));
}
Ok(())
}
}
impl Hypervisor for KvmHypervisor {
type Vm = KvmVm;
fn capabilities(&self) -> &PlatformCapabilities {
&self.capabilities
}
fn create_vm(&self, config: VmConfig) -> Result<Self::Vm, HypervisorError> {
self.validate_config(&config)?;
KvmVm::new(Arc::clone(&self.kvm), self.vcpu_mmap_size, config)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore] fn test_hypervisor_creation() {
let result = KvmHypervisor::new();
assert!(result.is_ok());
let hypervisor = result.unwrap();
assert!(hypervisor.capabilities().max_vcpus >= 1);
}
#[test]
#[ignore] fn test_config_validation() {
let hypervisor = KvmHypervisor::new().unwrap();
let config = VmConfig {
vcpu_count: 2,
memory_size: 512 * 1024 * 1024,
..Default::default()
};
assert!(hypervisor.validate_config(&config).is_ok());
let config = VmConfig {
vcpu_count: 0,
..Default::default()
};
assert!(hypervisor.validate_config(&config).is_err());
let config = VmConfig {
memory_size: 1024, ..Default::default()
};
assert!(hypervisor.validate_config(&config).is_err());
}
#[test]
#[ignore] fn test_create_vm() {
let hypervisor = KvmHypervisor::new().unwrap();
let config = VmConfig {
vcpu_count: 1,
memory_size: 128 * 1024 * 1024,
..Default::default()
};
let vm = hypervisor.create_vm(config);
assert!(vm.is_ok());
}
}