1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ExecutionEnvironment {
9 Baremetal,
11 Nested,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum Vendor {
18 Amd,
20 Intel,
22 Arm,
24}
25
26#[derive(Clone, Copy, Debug, PartialEq)]
28pub enum IsolationType {
29 Vbs,
31 Snp,
33 Tdx,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum VmmType {
40 OpenVmm,
42 HyperV,
44}
45
46#[derive(Debug, Clone)]
48pub struct VmHostInfo {
49 pub vbs_supported: bool,
51 pub snp_status: bool,
53 pub tdx_status: bool,
55}
56
57#[derive(Debug, Clone)]
59pub struct HostContext {
60 pub vm_host_info: Option<VmHostInfo>,
62 pub vendor: Vendor,
64 pub execution_environment: ExecutionEnvironment,
66}
67
68impl HostContext {
69 pub async fn new() -> Self {
71 let is_nested = {
72 #[cfg(target_arch = "x86_64")]
74 {
75 let result = safe_intrinsics::cpuid(
76 hvdef::HV_CPUID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION,
77 0,
78 );
79 hvdef::HvEnlightenmentInformation::from(
80 result.eax as u128
81 | (result.ebx as u128) << 32
82 | (result.ecx as u128) << 64
83 | (result.edx as u128) << 96,
84 )
85 .nested()
86 }
87 #[cfg(not(target_arch = "x86_64"))]
89 {
90 false
91 }
92 };
93
94 let vendor = {
95 #[cfg(target_arch = "x86_64")]
97 {
98 let result = safe_intrinsics::cpuid(
99 x86defs::cpuid::CpuidFunction::VendorAndMaxFunction.0,
100 0,
101 );
102 if x86defs::cpuid::Vendor::from_ebx_ecx_edx(result.ebx, result.ecx, result.edx)
103 .is_amd_compatible()
104 {
105 Vendor::Amd
106 } else {
107 assert!(
108 x86defs::cpuid::Vendor::from_ebx_ecx_edx(
109 result.ebx, result.ecx, result.edx
110 )
111 .is_intel_compatible()
112 );
113 Vendor::Intel
114 }
115 }
116 #[cfg(not(target_arch = "x86_64"))]
118 {
119 Vendor::Arm
120 }
121 };
122
123 let vm_host_info = {
124 #[cfg(windows)]
125 {
126 crate::vm::hyperv::powershell::run_get_vm_host()
127 .await
128 .ok()
129 .map(|info| VmHostInfo {
130 vbs_supported: info.guest_isolation_types.contains(
131 &crate::vm::hyperv::powershell::HyperVGuestStateIsolationType::Vbs,
132 ),
133 snp_status: info.snp_status,
134 tdx_status: info.tdx_status,
135 })
136 }
137 #[cfg(not(windows))]
138 {
139 None
140 }
141 };
142
143 Self {
144 vm_host_info,
145 vendor,
146 execution_environment: if is_nested {
147 ExecutionEnvironment::Nested
148 } else {
149 ExecutionEnvironment::Baremetal
150 },
151 }
152 }
153}
154
155pub enum TestRequirement {
157 ExecutionEnvironment(ExecutionEnvironment),
159 Vendor(Vendor),
161 Isolation(IsolationType),
163 And(Box<TestRequirement>, Box<TestRequirement>),
165 Or(Box<TestRequirement>, Box<TestRequirement>),
167 Not(Box<TestRequirement>),
169}
170
171impl TestRequirement {
172 pub fn is_satisfied(&self, context: &HostContext) -> bool {
174 match self {
175 TestRequirement::ExecutionEnvironment(env) => context.execution_environment == *env,
176 TestRequirement::Vendor(vendor) => context.vendor == *vendor,
177 TestRequirement::Isolation(isolation_type) => {
178 if let Some(vm_host_info) = &context.vm_host_info {
179 match isolation_type {
180 IsolationType::Vbs => vm_host_info.vbs_supported,
181 IsolationType::Snp => vm_host_info.snp_status,
182 IsolationType::Tdx => vm_host_info.tdx_status,
183 }
184 } else {
185 false
186 }
187 }
188 TestRequirement::And(req1, req2) => {
189 req1.is_satisfied(context) && req2.is_satisfied(context)
190 }
191 TestRequirement::Or(req1, req2) => {
192 req1.is_satisfied(context) || req2.is_satisfied(context)
193 }
194 TestRequirement::Not(req) => !req.is_satisfied(context),
195 }
196 }
197}
198
199#[derive(Debug, Clone)]
201pub struct TestEvaluationResult {
202 pub test_name: String,
204 pub can_run: bool,
206}
207
208impl TestEvaluationResult {
209 pub fn new(test_name: &str) -> Self {
211 Self {
212 test_name: test_name.to_string(),
213 can_run: true,
214 }
215 }
216}
217
218pub struct TestCaseRequirements {
220 requirements: TestRequirement,
221}
222
223impl TestCaseRequirements {
224 pub fn new(requirements: TestRequirement) -> Self {
226 Self { requirements }
227 }
228}
229
230pub fn can_run_test_with_context(
232 config: Option<&TestCaseRequirements>,
233 context: &HostContext,
234) -> bool {
235 if let Some(config) = config {
236 config.requirements.is_satisfied(context)
237 } else {
238 true
239 }
240}