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        crate::types::warn_memory_exceeds_host_half(config.memory_size);
184
185        // Check architecture
186        if !self.supports_arch(config.arch) {
187            return Err(HypervisorError::invalid_config(format!(
188                "Architecture {:?} is not supported",
189                config.arch
190            )));
191        }
192
193        Ok(())
194    }
195}
196
197impl Hypervisor for KvmHypervisor {
198    type Vm = KvmVm;
199
200    fn capabilities(&self) -> &PlatformCapabilities {
201        &self.capabilities
202    }
203
204    fn create_vm(&self, config: VmConfig) -> Result<Self::Vm, HypervisorError> {
205        // Validate configuration
206        self.validate_config(&config)?;
207
208        // Create the VM
209        KvmVm::new(Arc::clone(&self.kvm), self.vcpu_mmap_size, config)
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216
217    #[test]
218    #[ignore] // Requires /dev/kvm
219    fn test_hypervisor_creation() {
220        let result = KvmHypervisor::new();
221        assert!(result.is_ok());
222
223        let hypervisor = result.unwrap();
224        assert!(hypervisor.capabilities().max_vcpus >= 1);
225    }
226
227    #[test]
228    #[ignore] // Requires /dev/kvm
229    fn test_config_validation() {
230        let hypervisor = KvmHypervisor::new().unwrap();
231
232        // Valid config
233        let config = VmConfig {
234            vcpu_count: 2,
235            memory_size: 512 * 1024 * 1024,
236            ..Default::default()
237        };
238        assert!(hypervisor.validate_config(&config).is_ok());
239
240        // Invalid: 0 vCPUs
241        let config = VmConfig {
242            vcpu_count: 0,
243            ..Default::default()
244        };
245        assert!(hypervisor.validate_config(&config).is_err());
246
247        // Invalid: too little memory
248        let config = VmConfig {
249            memory_size: 1024, // 1KB
250            ..Default::default()
251        };
252        assert!(hypervisor.validate_config(&config).is_err());
253    }
254
255    #[test]
256    #[ignore] // Requires /dev/kvm
257    fn test_create_vm() {
258        let hypervisor = KvmHypervisor::new().unwrap();
259
260        let config = VmConfig {
261            vcpu_count: 1,
262            memory_size: 128 * 1024 * 1024,
263            ..Default::default()
264        };
265
266        let vm = hypervisor.create_vm(config);
267        assert!(vm.is_ok());
268    }
269}