Skip to main content

arcbox_hypervisor/linux/
hypervisor.rs

1//! Linux KVM hypervisor implementation.
2
3use std::sync::Arc;
4
5use crate::{
6    config::VmConfig,
7    error::HypervisorError,
8    traits::Hypervisor,
9    types::{CpuArch, PlatformCapabilities},
10};
11
12use super::ffi::{self, KVM_CAP_MAX_VCPUS, KVM_CAP_NR_MEMSLOTS, KvmSystem};
13use super::vm::KvmVm;
14
15/// Linux hypervisor implementation using KVM.
16///
17/// This is the main entry point for creating VMs on Linux.
18///
19/// # Example
20///
21/// ```ignore
22/// use arcbox_hypervisor::linux::KvmHypervisor;
23///
24/// let hypervisor = KvmHypervisor::new()?;
25/// let caps = hypervisor.capabilities();
26/// println!("Max vCPUs: {}", caps.max_vcpus);
27/// ```
28pub struct KvmHypervisor {
29    /// KVM system handle.
30    kvm: Arc<KvmSystem>,
31    /// Platform capabilities.
32    capabilities: PlatformCapabilities,
33    /// Size of the vCPU mmap region.
34    vcpu_mmap_size: usize,
35}
36
37impl KvmHypervisor {
38    /// Creates a new KVM hypervisor instance.
39    ///
40    /// # Errors
41    ///
42    /// Returns an error if:
43    /// - `/dev/kvm` cannot be opened
44    /// - KVM API version is not supported
45    /// - Required KVM capabilities are not available
46    pub fn new() -> Result<Self, HypervisorError> {
47        // Open /dev/kvm
48        let kvm = KvmSystem::open().map_err(|e| {
49            HypervisorError::InitializationFailed(format!("Failed to open /dev/kvm: {}", e))
50        })?;
51
52        // Check API version
53        let api_version = kvm.api_version().map_err(|e| {
54            HypervisorError::InitializationFailed(format!("Failed to get KVM API version: {}", e))
55        })?;
56
57        if api_version != 12 {
58            return Err(HypervisorError::InitializationFailed(format!(
59                "Unsupported KVM API version: {} (expected 12)",
60                api_version
61            )));
62        }
63
64        // Get vCPU mmap size
65        let vcpu_mmap_size = kvm.vcpu_mmap_size().map_err(|e| {
66            HypervisorError::InitializationFailed(format!("Failed to get vCPU mmap size: {}", e))
67        })?;
68
69        // Detect capabilities
70        let capabilities = Self::detect_capabilities(&kvm)?;
71
72        tracing::info!(
73            "KVM hypervisor initialized: max_vcpus={}, max_memory={}GB, nested_virt={}",
74            capabilities.max_vcpus,
75            capabilities.max_memory / (1024 * 1024 * 1024),
76            capabilities.nested_virt
77        );
78
79        Ok(Self {
80            kvm: Arc::new(kvm),
81            capabilities,
82            vcpu_mmap_size,
83        })
84    }
85
86    /// Detects platform capabilities from KVM.
87    fn detect_capabilities(kvm: &KvmSystem) -> Result<PlatformCapabilities, HypervisorError> {
88        // Get max vCPUs
89        let max_vcpus = kvm.check_extension(KVM_CAP_MAX_VCPUS).unwrap_or(1) as u32;
90
91        // Get max memory slots (estimate max memory from this)
92        let _max_memslots = kvm.check_extension(KVM_CAP_NR_MEMSLOTS).unwrap_or(32);
93
94        // Calculate max memory (conservative estimate: 512GB)
95        let max_memory = 512 * 1024 * 1024 * 1024_u64;
96
97        // Determine supported architectures
98        let supported_archs = vec![CpuArch::native()];
99
100        Ok(PlatformCapabilities {
101            supported_archs,
102            max_vcpus,
103            max_memory,
104            nested_virt: crate::capability::host_nested_virt().supported,
105            rosetta: false, // Not applicable on Linux
106        })
107    }
108
109    /// Returns the KVM system handle.
110    pub(crate) fn kvm(&self) -> &Arc<KvmSystem> {
111        &self.kvm
112    }
113
114    /// Returns the vCPU mmap size.
115    pub(crate) fn vcpu_mmap_size(&self) -> usize {
116        self.vcpu_mmap_size
117    }
118
119    /// Checks if the given architecture is supported.
120    #[must_use]
121    pub fn supports_arch(&self, arch: CpuArch) -> bool {
122        self.capabilities.supported_archs.contains(&arch)
123    }
124
125    /// Validates VM configuration against platform capabilities.
126    fn validate_config(&self, config: &VmConfig) -> Result<(), HypervisorError> {
127        // Check vCPU count
128        if config.vcpu_count == 0 {
129            return Err(HypervisorError::invalid_config(
130                "vCPU count must be > 0".to_string(),
131            ));
132        }
133
134        if config.vcpu_count > self.capabilities.max_vcpus {
135            return Err(HypervisorError::invalid_config(format!(
136                "vCPU count {} exceeds maximum {}",
137                config.vcpu_count, self.capabilities.max_vcpus
138            )));
139        }
140
141        // Check memory size
142        const MIN_MEMORY: u64 = 16 * 1024 * 1024; // 16MB minimum
143        if config.memory_size < MIN_MEMORY {
144            return Err(HypervisorError::invalid_config(format!(
145                "Memory size {} is below minimum {}",
146                config.memory_size, MIN_MEMORY
147            )));
148        }
149
150        if config.memory_size > self.capabilities.max_memory {
151            return Err(HypervisorError::invalid_config(format!(
152                "Memory size {} exceeds maximum {}",
153                config.memory_size, self.capabilities.max_memory
154            )));
155        }
156
157        crate::types::warn_memory_exceeds_host_half(config.memory_size);
158
159        // Check architecture
160        if !self.supports_arch(config.arch) {
161            return Err(HypervisorError::invalid_config(format!(
162                "Architecture {:?} is not supported",
163                config.arch
164            )));
165        }
166
167        Ok(())
168    }
169}
170
171impl Hypervisor for KvmHypervisor {
172    type Vm = KvmVm;
173
174    fn capabilities(&self) -> &PlatformCapabilities {
175        &self.capabilities
176    }
177
178    fn create_vm(&self, config: VmConfig) -> Result<Self::Vm, HypervisorError> {
179        // Validate configuration
180        self.validate_config(&config)?;
181
182        // Create the VM
183        KvmVm::new(Arc::clone(&self.kvm), self.vcpu_mmap_size, config)
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190
191    #[test]
192    #[ignore] // Requires /dev/kvm
193    fn test_hypervisor_creation() {
194        let result = KvmHypervisor::new();
195        assert!(result.is_ok());
196
197        let hypervisor = result.unwrap();
198        assert!(hypervisor.capabilities().max_vcpus >= 1);
199    }
200
201    #[test]
202    #[ignore] // Requires /dev/kvm
203    fn test_config_validation() {
204        let hypervisor = KvmHypervisor::new().unwrap();
205
206        // Valid config
207        let config = VmConfig {
208            vcpu_count: 2,
209            memory_size: 512 * 1024 * 1024,
210            ..Default::default()
211        };
212        assert!(hypervisor.validate_config(&config).is_ok());
213
214        // Invalid: 0 vCPUs
215        let config = VmConfig {
216            vcpu_count: 0,
217            ..Default::default()
218        };
219        assert!(hypervisor.validate_config(&config).is_err());
220
221        // Invalid: too little memory
222        let config = VmConfig {
223            memory_size: 1024, // 1KB
224            ..Default::default()
225        };
226        assert!(hypervisor.validate_config(&config).is_err());
227    }
228
229    #[test]
230    #[ignore] // Requires /dev/kvm
231    fn test_create_vm() {
232        let hypervisor = KvmHypervisor::new().unwrap();
233
234        let config = VmConfig {
235            vcpu_count: 1,
236            memory_size: 128 * 1024 * 1024,
237            ..Default::default()
238        };
239
240        let vm = hypervisor.create_vm(config);
241        assert!(vm.is_ok());
242    }
243}