a3s_box_runtime/
host_check.rs1use a3s_box_core::error::{BoxError, Result};
9
10#[derive(Debug, Clone)]
12pub struct VirtualizationSupport {
13 pub backend: String,
15 pub details: String,
17}
18
19pub fn check_virtualization_support() -> Result<VirtualizationSupport> {
23 #[cfg(target_os = "macos")]
24 {
25 check_macos_hypervisor()
26 }
27
28 #[cfg(target_os = "linux")]
29 {
30 check_linux_kvm()
31 }
32
33 #[cfg(target_os = "windows")]
34 {
35 check_windows_whpx()
36 }
37
38 #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
39 {
40 Err(BoxError::ConfigError(
41 "Unsupported platform: A3S Box requires macOS (Apple Silicon), Linux with KVM, or Windows with WHPX"
42 .to_string(),
43 ))
44 }
45}
46
47#[cfg(target_os = "macos")]
49fn check_macos_hypervisor() -> Result<VirtualizationSupport> {
50 #[cfg(target_arch = "aarch64")]
51 {
52 let output = std::process::Command::new("sysctl")
54 .arg("kern.hv_support")
55 .output()
56 .map_err(|e| BoxError::ExecError(format!("Failed to run sysctl: {}", e)))?;
57
58 if !output.status.success() {
59 return Err(BoxError::ConfigError(
60 "Failed to query Hypervisor.framework support via sysctl".to_string(),
61 ));
62 }
63
64 let stdout = String::from_utf8_lossy(&output.stdout);
65 let value = stdout.split(':').nth(1).map(|s| s.trim()).unwrap_or("0");
67
68 if value == "1" {
69 Ok(VirtualizationSupport {
70 backend: "Hypervisor.framework".to_string(),
71 details: "Apple Silicon hardware virtualization is available".to_string(),
72 })
73 } else {
74 Err(BoxError::ConfigError(
75 "Hypervisor.framework is not available on this system. \
76 Ensure you are running on Apple Silicon and have the necessary entitlements."
77 .to_string(),
78 ))
79 }
80 }
81
82 #[cfg(not(target_arch = "aarch64"))]
83 {
84 Err(BoxError::ConfigError(
85 "A3S Box on macOS requires Apple Silicon (ARM64). Intel Macs are not supported."
86 .to_string(),
87 ))
88 }
89}
90
91#[cfg(target_os = "linux")]
93fn check_linux_kvm() -> Result<VirtualizationSupport> {
94 use std::path::Path;
95
96 let kvm_path = Path::new("/dev/kvm");
97
98 if !kvm_path.exists() {
99 return Err(BoxError::ConfigError(
100 "KVM is not available: /dev/kvm not found. \
101 Ensure KVM kernel modules are loaded (modprobe kvm kvm_intel or kvm_amd)."
102 .to_string(),
103 ));
104 }
105
106 match std::fs::OpenOptions::new()
108 .read(true)
109 .write(true)
110 .open(kvm_path)
111 {
112 Ok(_) => Ok(VirtualizationSupport {
113 backend: "KVM".to_string(),
114 details: "Linux KVM hardware virtualization is available".to_string(),
115 }),
116 Err(e) => {
117 if e.kind() == std::io::ErrorKind::PermissionDenied {
118 Err(BoxError::ConfigError(format!(
119 "KVM access denied: {}. Add your user to the 'kvm' group: \
120 sudo usermod -aG kvm $USER",
121 e
122 )))
123 } else {
124 Err(BoxError::ConfigError(format!(
125 "Failed to access /dev/kvm: {}",
126 e
127 )))
128 }
129 }
130 }
131}
132
133#[cfg(target_os = "windows")]
135fn check_windows_whpx() -> Result<VirtualizationSupport> {
136 #[cfg(not(target_arch = "x86_64"))]
137 {
138 return Err(BoxError::ConfigError(
139 "A3S Box on Windows currently requires x86_64 for the WHPX backend.".to_string(),
140 ));
141 }
142
143 let output = std::process::Command::new("powershell.exe")
144 .args([
145 "-NoProfile",
146 "-NonInteractive",
147 "-Command",
148 "(Get-WindowsOptionalFeature -Online -FeatureName HypervisorPlatform).State",
149 ])
150 .output()
151 .map_err(|e| BoxError::ExecError(format!("Failed to query HypervisorPlatform: {}", e)))?;
152
153 if !output.status.success() {
154 return Err(BoxError::ConfigError(format!(
155 "Failed to query Windows Hypervisor Platform state (exit code {:?})",
156 output.status.code()
157 )));
158 }
159
160 let state = String::from_utf8_lossy(&output.stdout)
161 .trim()
162 .to_ascii_lowercase();
163 if state == "enabled" {
164 Ok(VirtualizationSupport {
165 backend: "WHPX".to_string(),
166 details: "Windows Hypervisor Platform is enabled".to_string(),
167 })
168 } else {
169 Err(BoxError::ConfigError(format!(
170 "Windows Hypervisor Platform is not enabled (current state: {}). Enable the 'Windows Hypervisor Platform' optional feature and reboot.",
171 if state.is_empty() { "unknown" } else { &state }
172 )))
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_check_virtualization_support() {
182 match check_virtualization_support() {
185 Ok(support) => {
186 println!("Virtualization supported:");
187 println!(" Backend: {}", support.backend);
188 println!(" Details: {}", support.details);
189 }
190 Err(e) => {
191 println!("Virtualization not supported: {}", e);
192 }
193 }
194 }
195}