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        // Check for nested virtualization support
101        #[cfg(target_arch = "x86_64")]
102        let nested_virt = Self::check_nested_virt();
103        #[cfg(not(target_arch = "x86_64"))]
104        let nested_virt = false;
105
106        Ok(PlatformCapabilities {
107            supported_archs,
108            max_vcpus,
109            max_memory,
110            nested_virt,
111            rosetta: false, // Not applicable on Linux
112        })
113    }
114
115    /// Checks if nested virtualization is supported (x86 only).
116    #[cfg(target_arch = "x86_64")]
117    fn check_nested_virt() -> bool {
118        // Check Intel VMX nested support
119        if let Ok(content) = std::fs::read_to_string("/sys/module/kvm_intel/parameters/nested") {
120            if content.trim() == "Y" || content.trim() == "1" {
121                return true;
122            }
123        }
124
125        // Check AMD SVM nested support
126        if let Ok(content) = std::fs::read_to_string("/sys/module/kvm_amd/parameters/nested") {
127            if content.trim() == "Y" || content.trim() == "1" {
128                return true;
129            }
130        }
131
132        false
133    }
134
135    /// Returns the KVM system handle.
136    pub(crate) fn kvm(&self) -> &Arc<KvmSystem> {
137        &self.kvm
138    }
139
140    /// Returns the vCPU mmap size.
141    pub(crate) fn vcpu_mmap_size(&self) -> usize {
142        self.vcpu_mmap_size
143    }
144
145    /// Checks if the given architecture is supported.
146    #[must_use]
147    pub fn supports_arch(&self, arch: CpuArch) -> bool {
148        self.capabilities.supported_archs.contains(&arch)
149    }
150
151    /// Validates VM configuration against platform capabilities.
152    fn validate_config(&self, config: &VmConfig) -> Result<(), HypervisorError> {
153        // Check vCPU count
154        if config.vcpu_count == 0 {
155            return Err(HypervisorError::invalid_config(
156                "vCPU count must be > 0".to_string(),
157            ));
158        }
159
160        if config.vcpu_count > self.capabilities.max_vcpus {
161            return Err(HypervisorError::invalid_config(format!(
162                "vCPU count {} exceeds maximum {}",
163                config.vcpu_count, self.capabilities.max_vcpus
164            )));
165        }
166
167        // Check memory size
168        const MIN_MEMORY: u64 = 16 * 1024 * 1024; // 16MB minimum
169        if config.memory_size < MIN_MEMORY {
170            return Err(HypervisorError::invalid_config(format!(
171                "Memory size {} is below minimum {}",
172                config.memory_size, MIN_MEMORY
173            )));
174        }
175
176        if config.memory_size > self.capabilities.max_memory {
177            return Err(HypervisorError::invalid_config(format!(
178                "Memory size {} exceeds maximum {}",
179                config.memory_size, self.capabilities.max_memory
180            )));
181        }
182
183        // Check architecture
184        if !self.supports_arch(config.arch) {
185            return Err(HypervisorError::invalid_config(format!(
186                "Architecture {:?} is not supported",
187                config.arch
188            )));
189        }
190
191        Ok(())
192    }
193}
194
195impl Hypervisor for KvmHypervisor {
196    type Vm = KvmVm;
197
198    fn capabilities(&self) -> &PlatformCapabilities {
199        &self.capabilities
200    }
201
202    fn create_vm(&self, config: VmConfig) -> Result<Self::Vm, HypervisorError> {
203        // Validate configuration
204        self.validate_config(&config)?;
205
206        // Create the VM
207        KvmVm::new(Arc::clone(&self.kvm), self.vcpu_mmap_size, config)
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214
215    #[test]
216    #[ignore] // Requires /dev/kvm
217    fn test_hypervisor_creation() {
218        let result = KvmHypervisor::new();
219        assert!(result.is_ok());
220
221        let hypervisor = result.unwrap();
222        assert!(hypervisor.capabilities().max_vcpus >= 1);
223    }
224
225    #[test]
226    #[ignore] // Requires /dev/kvm
227    fn test_config_validation() {
228        let hypervisor = KvmHypervisor::new().unwrap();
229
230        // Valid config
231        let config = VmConfig {
232            vcpu_count: 2,
233            memory_size: 512 * 1024 * 1024,
234            ..Default::default()
235        };
236        assert!(hypervisor.validate_config(&config).is_ok());
237
238        // Invalid: 0 vCPUs
239        let config = VmConfig {
240            vcpu_count: 0,
241            ..Default::default()
242        };
243        assert!(hypervisor.validate_config(&config).is_err());
244
245        // Invalid: too little memory
246        let config = VmConfig {
247            memory_size: 1024, // 1KB
248            ..Default::default()
249        };
250        assert!(hypervisor.validate_config(&config).is_err());
251    }
252
253    #[test]
254    #[ignore] // Requires /dev/kvm
255    fn test_create_vm() {
256        let hypervisor = KvmHypervisor::new().unwrap();
257
258        let config = VmConfig {
259            vcpu_count: 1,
260            memory_size: 128 * 1024 * 1024,
261            ..Default::default()
262        };
263
264        let vm = hypervisor.create_vm(config);
265        assert!(vm.is_ok());
266    }
267}