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 crate::types::warn_memory_exceeds_host_half(config.memory_size);
184
185 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 self.validate_config(&config)?;
207
208 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] 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] fn test_config_validation() {
230 let hypervisor = KvmHypervisor::new().unwrap();
231
232 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 let config = VmConfig {
242 vcpu_count: 0,
243 ..Default::default()
244 };
245 assert!(hypervisor.validate_config(&config).is_err());
246
247 let config = VmConfig {
249 memory_size: 1024, ..Default::default()
251 };
252 assert!(hypervisor.validate_config(&config).is_err());
253 }
254
255 #[test]
256 #[ignore] 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}