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 #[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, })
113 }
114
115 #[cfg(target_arch = "x86_64")]
117 fn check_nested_virt() -> bool {
118 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 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 pub(crate) fn kvm(&self) -> &Arc<KvmSystem> {
137 &self.kvm
138 }
139
140 pub(crate) fn vcpu_mmap_size(&self) -> usize {
142 self.vcpu_mmap_size
143 }
144
145 #[must_use]
147 pub fn supports_arch(&self, arch: CpuArch) -> bool {
148 self.capabilities.supported_archs.contains(&arch)
149 }
150
151 fn validate_config(&self, config: &VmConfig) -> Result<(), HypervisorError> {
153 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 const MIN_MEMORY: u64 = 16 * 1024 * 1024; 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 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 self.validate_config(&config)?;
205
206 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] 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] fn test_config_validation() {
228 let hypervisor = KvmHypervisor::new().unwrap();
229
230 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 let config = VmConfig {
240 vcpu_count: 0,
241 ..Default::default()
242 };
243 assert!(hypervisor.validate_config(&config).is_err());
244
245 let config = VmConfig {
247 memory_size: 1024, ..Default::default()
249 };
250 assert!(hypervisor.validate_config(&config).is_err());
251 }
252
253 #[test]
254 #[ignore] 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}