1use super::{
2 Backend, BackendPolicy, CandidateDetectionMode, CtGatePosture, MemoryLockPosture,
3 SecurityPosture, WipePosture,
4};
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub struct BackendPolicyError {
9 pub policy: BackendPolicy,
11 pub report: BackendReport,
13}
14
15impl core::fmt::Display for BackendPolicyError {
16 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17 write!(
18 formatter,
19 "runtime backend policy `{}` was not satisfied ({})",
20 self.policy, self.report,
21 )
22 }
23}
24
25#[cfg(feature = "std")]
26impl std::error::Error for BackendPolicyError {}
27
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub struct BackendReport {
31 pub active: Backend,
37 pub candidate: Backend,
39 pub candidate_detection_mode: CandidateDetectionMode,
42 pub simd_feature_enabled: bool,
44 pub accelerated_backend_active: bool,
46 pub unsafe_boundary_enforced: bool,
53 pub security_posture: SecurityPosture,
55 pub wipe_posture: WipePosture,
57 pub ct_gate_posture: CtGatePosture,
59}
60
61#[derive(Clone, Copy, Debug, Eq, PartialEq)]
63pub struct BackendSnapshot {
64 pub active: &'static str,
69 pub candidate: &'static str,
71 pub candidate_detection_mode: &'static str,
73 pub candidate_required_cpu_features: &'static [&'static str],
75 pub simd_feature_enabled: bool,
77 pub accelerated_backend_active: bool,
79 pub unsafe_boundary_enforced: bool,
85 pub security_posture: &'static str,
87 pub wipe_posture: &'static str,
89 pub ct_gate_posture: &'static str,
91}
92
93impl core::fmt::Display for BackendReport {
94 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
95 write!(
96 formatter,
97 "active={} candidate={} candidate_detection_mode={} candidate_required_cpu_features=",
98 self.active, self.candidate, self.candidate_detection_mode,
99 )?;
100 write_feature_list(formatter, self.candidate_required_cpu_features())?;
101 write!(
102 formatter,
103 " simd_feature_enabled={} accelerated_backend_active={} unsafe_boundary_enforced={} security_posture={} wipe_posture={} ct_gate_posture={}",
104 self.simd_feature_enabled,
105 self.accelerated_backend_active,
106 self.unsafe_boundary_enforced,
107 self.security_posture,
108 self.wipe_posture,
109 self.ct_gate_posture,
110 )
111 }
112}
113
114impl BackendReport {
115 #[must_use]
125 pub const fn satisfies(self, policy: BackendPolicy) -> bool {
126 match policy {
127 BackendPolicy::ScalarExecutionOnly => {
128 matches!(self.active, Backend::Scalar) && !self.accelerated_backend_active
129 }
130 BackendPolicy::SimdFeatureDisabled => !self.simd_feature_enabled,
131 BackendPolicy::NoDetectedSimdCandidate => matches!(self.candidate, Backend::Scalar),
132 BackendPolicy::HighAssuranceScalarOnly => {
133 matches!(self.active, Backend::Scalar)
134 && matches!(self.candidate, Backend::Scalar)
135 && !self.simd_feature_enabled
136 && !self.accelerated_backend_active
137 && self.unsafe_boundary_enforced
138 && matches!(
139 self.ct_gate_posture,
140 CtGatePosture::HardwareSpeculationBarrier
141 | CtGatePosture::HardwareSpeculationBarrierBuildAsserted
142 )
143 }
144 }
145 }
146
147 #[must_use]
158 pub const fn candidate_required_cpu_features(self) -> &'static [&'static str] {
159 self.candidate.required_cpu_features()
160 }
161
162 #[must_use]
170 pub const fn memory_lock_posture(self) -> MemoryLockPosture {
171 let _ = self;
172 MemoryLockPosture::NotProvided
173 }
174
175 #[must_use]
186 pub const fn snapshot(self) -> BackendSnapshot {
187 BackendSnapshot {
188 active: self.active.as_str(),
189 candidate: self.candidate.as_str(),
190 candidate_detection_mode: self.candidate_detection_mode.as_str(),
191 candidate_required_cpu_features: self.candidate_required_cpu_features(),
192 simd_feature_enabled: self.simd_feature_enabled,
193 accelerated_backend_active: self.accelerated_backend_active,
194 unsafe_boundary_enforced: self.unsafe_boundary_enforced,
195 security_posture: self.security_posture.as_str(),
196 wipe_posture: self.wipe_posture.as_str(),
197 ct_gate_posture: self.ct_gate_posture.as_str(),
198 }
199 }
200}
201
202#[must_use]
212pub fn backend_report() -> BackendReport {
213 let active = active_backend();
214 let candidate = detected_candidate();
215 let candidate_detection_mode = candidate_detection_mode();
216 let accelerated_backend_active = active != Backend::Scalar;
217 let unsafe_boundary_enforced = !cfg!(feature = "simd");
218 let security_posture = if accelerated_backend_active {
219 SecurityPosture::Accelerated
220 } else if candidate != Backend::Scalar {
221 SecurityPosture::SimdCandidateScalarActive
222 } else {
223 SecurityPosture::ScalarOnly
224 };
225
226 BackendReport {
227 active,
228 candidate,
229 candidate_detection_mode,
230 simd_feature_enabled: cfg!(feature = "simd"),
231 accelerated_backend_active,
232 unsafe_boundary_enforced,
233 security_posture,
234 wipe_posture: wipe_posture(),
235 ct_gate_posture: ct_gate_posture(),
236 }
237}
238
239const fn wipe_posture() -> WipePosture {
240 if cfg!(any(
241 target_arch = "aarch64",
242 target_arch = "arm",
243 target_arch = "riscv32",
244 target_arch = "riscv64",
245 target_arch = "x86",
246 target_arch = "x86_64",
247 )) {
248 WipePosture::HardwareFence
249 } else {
250 WipePosture::CompilerFenceOnly
251 }
252}
253
254const fn ct_gate_posture() -> CtGatePosture {
255 if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
256 CtGatePosture::HardwareSpeculationBarrier
257 } else if cfg!(all(
258 target_arch = "aarch64",
259 base64_ng_aarch64_csdb_attested
260 )) {
261 CtGatePosture::HardwareSpeculationBarrierBuildAsserted
262 } else if cfg!(target_arch = "aarch64") {
263 CtGatePosture::HardwareSpeculationBarrierUnattested
264 } else if cfg!(any(
265 target_arch = "arm",
266 target_arch = "riscv32",
267 target_arch = "riscv64"
268 )) {
269 CtGatePosture::OrderingFence
270 } else {
271 CtGatePosture::CompilerFenceOnly
272 }
273}
274
275pub fn require_backend_policy(policy: BackendPolicy) -> Result<(), BackendPolicyError> {
289 let report = backend_report();
290 if report.satisfies(policy) {
291 Ok(())
292 } else {
293 Err(BackendPolicyError { policy, report })
294 }
295}
296
297fn write_feature_list(
298 formatter: &mut core::fmt::Formatter<'_>,
299 features: &[&str],
300) -> core::fmt::Result {
301 formatter.write_str("[")?;
302 let mut index = 0;
303 while index < features.len() {
304 if index != 0 {
305 formatter.write_str(",")?;
306 }
307 formatter.write_str(features[index])?;
308 index += 1;
309 }
310 formatter.write_str("]")
311}
312
313#[cfg(feature = "simd")]
314fn active_backend() -> Backend {
315 match crate::simd::active_backend() {
316 crate::simd::ActiveBackend::Scalar => Backend::Scalar,
317 #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
318 crate::simd::ActiveBackend::Avx512Vbmi => Backend::Avx512Vbmi,
319 #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
320 crate::simd::ActiveBackend::Avx2 => Backend::Avx2,
321 #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
322 crate::simd::ActiveBackend::Ssse3Sse41 => Backend::Ssse3Sse41,
323 #[cfg(all(feature = "std", target_arch = "aarch64"))]
324 crate::simd::ActiveBackend::Neon => Backend::Neon,
325 }
326}
327
328#[cfg(not(feature = "simd"))]
329const fn active_backend() -> Backend {
330 Backend::Scalar
331}
332
333#[cfg(feature = "simd")]
334fn detected_candidate() -> Backend {
335 match crate::simd::detected_candidate() {
336 crate::simd::Candidate::Scalar => Backend::Scalar,
337 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
338 crate::simd::Candidate::Avx512Vbmi => Backend::Avx512Vbmi,
339 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
340 crate::simd::Candidate::Avx2 => Backend::Avx2,
341 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
342 crate::simd::Candidate::Ssse3Sse41 => Backend::Ssse3Sse41,
343 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
344 crate::simd::Candidate::Neon => Backend::Neon,
345 #[cfg(target_arch = "wasm32")]
346 crate::simd::Candidate::WasmSimd128 => Backend::WasmSimd128,
347 }
348}
349
350#[cfg(not(feature = "simd"))]
351const fn detected_candidate() -> Backend {
352 Backend::Scalar
353}
354
355#[cfg(all(
356 feature = "simd",
357 feature = "std",
358 any(target_arch = "x86", target_arch = "x86_64")
359))]
360const fn candidate_detection_mode() -> CandidateDetectionMode {
361 CandidateDetectionMode::RuntimeCpuFeatures
362}
363
364#[cfg(all(
365 feature = "simd",
366 not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
367))]
368const fn candidate_detection_mode() -> CandidateDetectionMode {
369 CandidateDetectionMode::CompileTimeTargetFeatures
370}
371
372#[cfg(not(feature = "simd"))]
373const fn candidate_detection_mode() -> CandidateDetectionMode {
374 CandidateDetectionMode::SimdFeatureDisabled
375}