arcbox_hypervisor/linux/
hypervisor.rs1use 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
15pub struct KvmHypervisor {
29 kvm: Arc<KvmSystem>,
31 capabilities: PlatformCapabilities,
33 vcpu_mmap_size: usize,
35}
36
37impl KvmHypervisor {
38 pub fn new() -> Result<Self, HypervisorError> {
47 let kvm = KvmSystem::open().map_err(|e| {
49 HypervisorError::InitializationFailed(format!("Failed to open /dev/kvm: {}", e))
50 })?;
51
52 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 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 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 fn detect_capabilities(kvm: &KvmSystem) -> Result<PlatformCapabilities, HypervisorError> {
88 let max_vcpus = kvm.check_extension(KVM_CAP_MAX_VCPUS).unwrap_or(1) as u32;
90
91 let _max_memslots = kvm.check_extension(KVM_CAP_NR_MEMSLOTS).unwrap_or(32);
93
94 let max_memory = 512 * 1024 * 1024 * 1024_u64;
96
97 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, })
107 }
108
109 pub(crate) fn kvm(&self) -> &Arc<KvmSystem> {
111 &self.kvm
112 }
113
114 pub(crate) fn vcpu_mmap_size(&self) -> usize {
116 self.vcpu_mmap_size
117 }
118
119 #[must_use]
121 pub fn supports_arch(&self, arch: CpuArch) -> bool {
122 self.capabilities.supported_archs.contains(&arch)
123 }
124
125 fn validate_config(&self, config: &VmConfig) -> Result<(), HypervisorError> {
127 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 const MIN_MEMORY: u64 = 16 * 1024 * 1024; 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 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 self.validate_config(&config)?;
181
182 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] 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] fn test_config_validation() {
204 let hypervisor = KvmHypervisor::new().unwrap();
205
206 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 let config = VmConfig {
216 vcpu_count: 0,
217 ..Default::default()
218 };
219 assert!(hypervisor.validate_config(&config).is_err());
220
221 let config = VmConfig {
223 memory_size: 1024, ..Default::default()
225 };
226 assert!(hypervisor.validate_config(&config).is_err());
227 }
228
229 #[test]
230 #[ignore] 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}