Skip to main content

a3s_box_runtime/tee/
snp.rs

1//! AMD SEV-SNP (Secure Encrypted Virtualization - Secure Nested Paging) support.
2//!
3//! This module provides hardware detection for AMD SEV-SNP capability.
4
5use a3s_box_core::error::{BoxError, Result};
6use std::path::Path;
7
8/// SEV-SNP hardware support status.
9#[derive(Debug, Clone)]
10pub struct SevSnpSupport {
11    /// Whether SEV-SNP is available
12    pub available: bool,
13    /// Reason if not available
14    pub reason: Option<String>,
15}
16
17/// Check if the host supports AMD SEV-SNP.
18///
19/// This function checks:
20/// 1. `/dev/sev` device exists (SEV driver loaded)
21/// 2. CPU supports SEV-SNP (via sysfs)
22///
23/// # Returns
24/// - `Ok(SevSnpSupport)` with availability status
25/// - `Err(BoxError)` if detection fails unexpectedly
26pub fn check_sev_snp_support() -> Result<SevSnpSupport> {
27    // Check if /dev/sev exists (SEV driver loaded)
28    if !Path::new("/dev/sev").exists() {
29        return Ok(SevSnpSupport {
30            available: false,
31            reason: Some("/dev/sev device not found - SEV driver not loaded".to_string()),
32        });
33    }
34
35    // Check if SNP is enabled via sysfs
36    let snp_enabled_path = "/sys/module/kvm_amd/parameters/sev_snp";
37    match std::fs::read_to_string(snp_enabled_path) {
38        Ok(content) => {
39            let enabled = content.trim();
40            if enabled == "Y" || enabled == "1" {
41                Ok(SevSnpSupport {
42                    available: true,
43                    reason: None,
44                })
45            } else {
46                Ok(SevSnpSupport {
47                    available: false,
48                    reason: Some(format!(
49                        "SEV-SNP not enabled in kernel (sev_snp={})",
50                        enabled
51                    )),
52                })
53            }
54        }
55        Err(e) => {
56            // File doesn't exist or can't be read - SNP not available
57            Ok(SevSnpSupport {
58                available: false,
59                reason: Some(format!(
60                    "Cannot read SEV-SNP status from {}: {}",
61                    snp_enabled_path, e
62                )),
63            })
64        }
65    }
66}
67
68/// Verify SEV-SNP is available, returning an error if not.
69///
70/// Use this function when TEE is required and you want to fail early
71/// with a descriptive error message.
72pub fn require_sev_snp_support() -> Result<()> {
73    let support = check_sev_snp_support()?;
74    if !support.available {
75        return Err(BoxError::TeeNotSupported(
76            support
77                .reason
78                .unwrap_or_else(|| "Unknown reason".to_string()),
79        ));
80    }
81    Ok(())
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_check_sev_snp_support_returns_result() {
90        // This test just verifies the function runs without panicking
91        // The actual result depends on the host hardware
92        let result = check_sev_snp_support();
93        assert!(result.is_ok());
94    }
95
96    #[test]
97    fn test_sev_snp_support_struct() {
98        let support = SevSnpSupport {
99            available: true,
100            reason: None,
101        };
102        assert!(support.available);
103        assert!(support.reason.is_none());
104
105        let support_unavailable = SevSnpSupport {
106            available: false,
107            reason: Some("Test reason".to_string()),
108        };
109        assert!(!support_unavailable.available);
110        assert_eq!(support_unavailable.reason, Some("Test reason".to_string()));
111    }
112
113    #[test]
114    fn test_sev_snp_support_debug() {
115        let support = SevSnpSupport {
116            available: true,
117            reason: None,
118        };
119        let debug_str = format!("{:?}", support);
120        assert!(debug_str.contains("available: true"));
121
122        let support_unavailable = SevSnpSupport {
123            available: false,
124            reason: Some("SEV driver not loaded".to_string()),
125        };
126        let debug_str = format!("{:?}", support_unavailable);
127        assert!(debug_str.contains("available: false"));
128        assert!(debug_str.contains("SEV driver not loaded"));
129    }
130
131    #[test]
132    fn test_require_sev_snp_support_when_available() {
133        // This test will pass or fail depending on actual hardware
134        // We just verify it returns a Result
135        let result = require_sev_snp_support();
136        // Don't assert on the actual value since it depends on hardware
137        assert!(result.is_ok() || result.is_err());
138    }
139}