Skip to main content

mvm_core/
platform.rs

1use std::path::Path;
2use std::sync::OnceLock;
3
4/// The execution environment for running Firecracker workloads.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum Platform {
7    /// macOS — requires Lima VM for nested virtualization
8    MacOS,
9    /// Native Linux with /dev/kvm available — run directly on host
10    LinuxNative,
11    /// Linux without /dev/kvm — requires Lima VM (e.g., WSL2 without KVM)
12    LinuxNoKvm,
13}
14
15impl Platform {
16    /// Whether this platform needs Lima to run Firecracker.
17    pub fn needs_lima(self) -> bool {
18        match self {
19            Platform::MacOS | Platform::LinuxNoKvm => true,
20            Platform::LinuxNative => false,
21        }
22    }
23
24    /// Whether this platform can run Firecracker directly.
25    pub fn has_kvm(self) -> bool {
26        matches!(self, Platform::LinuxNative)
27    }
28}
29
30impl std::fmt::Display for Platform {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            Platform::MacOS => write!(f, "macOS (via Lima)"),
34            Platform::LinuxNative => write!(f, "Linux (native KVM)"),
35            Platform::LinuxNoKvm => write!(f, "Linux (via Lima, no KVM)"),
36        }
37    }
38}
39
40/// Cached platform detection result.
41static DETECTED: OnceLock<Platform> = OnceLock::new();
42
43/// Detect the current platform. Result is cached after the first call.
44pub fn current() -> Platform {
45    *DETECTED.get_or_init(detect)
46}
47
48fn detect() -> Platform {
49    if cfg!(target_os = "macos") {
50        Platform::MacOS
51    } else if cfg!(target_os = "linux") {
52        if Path::new("/dev/kvm").exists() {
53            Platform::LinuxNative
54        } else {
55            Platform::LinuxNoKvm
56        }
57    } else {
58        // Unsupported OS — fall back to Lima-based approach
59        Platform::MacOS
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn test_detect_returns_consistent_result() {
69        let a = current();
70        let b = current();
71        assert_eq!(a, b);
72    }
73
74    #[test]
75    fn test_platform_display() {
76        assert_eq!(Platform::MacOS.to_string(), "macOS (via Lima)");
77        assert_eq!(Platform::LinuxNative.to_string(), "Linux (native KVM)");
78        assert_eq!(Platform::LinuxNoKvm.to_string(), "Linux (via Lima, no KVM)");
79    }
80
81    #[test]
82    fn test_needs_lima() {
83        assert!(Platform::MacOS.needs_lima());
84        assert!(!Platform::LinuxNative.needs_lima());
85        assert!(Platform::LinuxNoKvm.needs_lima());
86    }
87
88    #[test]
89    fn test_has_kvm() {
90        assert!(!Platform::MacOS.has_kvm());
91        assert!(Platform::LinuxNative.has_kvm());
92        assert!(!Platform::LinuxNoKvm.has_kvm());
93    }
94
95    #[test]
96    fn test_current_platform_valid() {
97        let p = current();
98        // On any platform, we should get a valid result
99        let _ = p.needs_lima();
100        let _ = p.has_kvm();
101    }
102}