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,
70 pub candidate: &'static str,
72 pub candidate_detection_mode: &'static str,
74 pub candidate_required_cpu_features: &'static [&'static str],
76 pub simd_feature_enabled: bool,
78 pub accelerated_backend_active: bool,
80 pub unsafe_boundary_enforced: bool,
86 pub security_posture: &'static str,
88 pub wipe_posture: &'static str,
90 pub ct_gate_posture: &'static str,
92}
93
94impl core::fmt::Display for BackendReport {
95 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 write!(
97 formatter,
98 "active={} candidate={} candidate_detection_mode={} candidate_required_cpu_features=",
99 self.active, self.candidate, self.candidate_detection_mode,
100 )?;
101 write_feature_list(formatter, self.candidate_required_cpu_features())?;
102 write!(
103 formatter,
104 " simd_feature_enabled={} accelerated_backend_active={} unsafe_boundary_enforced={} security_posture={} wipe_posture={} ct_gate_posture={}",
105 self.simd_feature_enabled,
106 self.accelerated_backend_active,
107 self.unsafe_boundary_enforced,
108 self.security_posture,
109 self.wipe_posture,
110 self.ct_gate_posture,
111 )
112 }
113}
114
115impl BackendReport {
116 #[must_use]
126 pub const fn satisfies(self, policy: BackendPolicy) -> bool {
127 match policy {
128 BackendPolicy::ScalarExecutionOnly => {
129 matches!(self.active, Backend::Scalar) && !self.accelerated_backend_active
130 }
131 BackendPolicy::SimdFeatureDisabled => !self.simd_feature_enabled,
132 BackendPolicy::NoDetectedSimdCandidate => matches!(self.candidate, Backend::Scalar),
133 BackendPolicy::HighAssuranceScalarOnly => {
134 matches!(self.active, Backend::Scalar)
135 && matches!(self.candidate, Backend::Scalar)
136 && !self.simd_feature_enabled
137 && !self.accelerated_backend_active
138 && self.unsafe_boundary_enforced
139 && matches!(
140 self.ct_gate_posture,
141 CtGatePosture::HardwareSpeculationBarrier
142 | CtGatePosture::HardwareSpeculationBarrierBuildAsserted
143 )
144 }
145 }
146 }
147
148 #[must_use]
159 pub const fn candidate_required_cpu_features(self) -> &'static [&'static str] {
160 self.candidate.required_cpu_features()
161 }
162
163 #[must_use]
170 pub fn active_decode_backend(self) -> Backend {
171 let _ = self;
172 active_decode_backend()
173 }
174
175 #[must_use]
183 pub const fn memory_lock_posture(self) -> MemoryLockPosture {
184 let _ = self;
185 MemoryLockPosture::NotProvided
186 }
187
188 #[must_use]
199 pub const fn snapshot(self) -> BackendSnapshot {
200 BackendSnapshot {
201 active: self.active.as_str(),
202 candidate: self.candidate.as_str(),
203 candidate_detection_mode: self.candidate_detection_mode.as_str(),
204 candidate_required_cpu_features: self.candidate_required_cpu_features(),
205 simd_feature_enabled: self.simd_feature_enabled,
206 accelerated_backend_active: self.accelerated_backend_active,
207 unsafe_boundary_enforced: self.unsafe_boundary_enforced,
208 security_posture: self.security_posture.as_str(),
209 wipe_posture: self.wipe_posture.as_str(),
210 ct_gate_posture: self.ct_gate_posture.as_str(),
211 }
212 }
213}
214
215#[must_use]
225pub fn backend_report() -> BackendReport {
226 let active = active_backend();
227 let candidate = detected_candidate();
228 let candidate_detection_mode = candidate_detection_mode();
229 let accelerated_backend_active = active != Backend::Scalar;
230 let unsafe_boundary_enforced = !cfg!(feature = "simd");
231 let security_posture = if accelerated_backend_active {
232 SecurityPosture::Accelerated
233 } else if candidate != Backend::Scalar {
234 SecurityPosture::SimdCandidateScalarActive
235 } else {
236 SecurityPosture::ScalarOnly
237 };
238
239 BackendReport {
240 active,
241 candidate,
242 candidate_detection_mode,
243 simd_feature_enabled: cfg!(feature = "simd"),
244 accelerated_backend_active,
245 unsafe_boundary_enforced,
246 security_posture,
247 wipe_posture: wipe_posture(),
248 ct_gate_posture: ct_gate_posture(),
249 }
250}
251
252const fn wipe_posture() -> WipePosture {
253 if cfg!(any(
254 target_arch = "aarch64",
255 target_arch = "arm",
256 target_arch = "riscv32",
257 target_arch = "riscv64",
258 target_arch = "x86",
259 target_arch = "x86_64",
260 )) {
261 WipePosture::HardwareFence
262 } else {
263 WipePosture::CompilerFenceOnly
264 }
265}
266
267const fn ct_gate_posture() -> CtGatePosture {
268 if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
269 CtGatePosture::HardwareSpeculationBarrier
270 } else if cfg!(all(
271 target_arch = "aarch64",
272 base64_ng_aarch64_csdb_attested
273 )) {
274 CtGatePosture::HardwareSpeculationBarrierBuildAsserted
275 } else if cfg!(target_arch = "aarch64") {
276 CtGatePosture::HardwareSpeculationBarrierUnattested
277 } else if cfg!(any(
278 target_arch = "arm",
279 target_arch = "riscv32",
280 target_arch = "riscv64"
281 )) {
282 CtGatePosture::OrderingFence
283 } else {
284 CtGatePosture::CompilerFenceOnly
285 }
286}
287
288pub fn require_backend_policy(policy: BackendPolicy) -> Result<(), BackendPolicyError> {
302 let report = backend_report();
303 if report.satisfies(policy) {
304 Ok(())
305 } else {
306 Err(BackendPolicyError { policy, report })
307 }
308}
309
310fn write_feature_list(
311 formatter: &mut core::fmt::Formatter<'_>,
312 features: &[&str],
313) -> core::fmt::Result {
314 formatter.write_str("[")?;
315 let mut index = 0;
316 while index < features.len() {
317 if index != 0 {
318 formatter.write_str(",")?;
319 }
320 formatter.write_str(features[index])?;
321 index += 1;
322 }
323 formatter.write_str("]")
324}
325
326#[cfg(feature = "simd")]
327fn active_backend() -> Backend {
328 match crate::simd::active_backend() {
329 crate::simd::ActiveBackend::Scalar => Backend::Scalar,
330 #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
331 crate::simd::ActiveBackend::Avx512Vbmi => Backend::Avx512Vbmi,
332 #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
333 crate::simd::ActiveBackend::Avx2 => Backend::Avx2,
334 #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
335 crate::simd::ActiveBackend::Ssse3Sse41 => Backend::Ssse3Sse41,
336 #[cfg(all(feature = "std", target_arch = "aarch64", target_endian = "little"))]
337 crate::simd::ActiveBackend::Neon => Backend::Neon,
338 #[cfg(all(feature = "simd", target_arch = "wasm32"))]
339 crate::simd::ActiveBackend::WasmSimd128 => Backend::WasmSimd128,
340 }
341}
342
343#[cfg(not(feature = "simd"))]
344const fn active_backend() -> Backend {
345 Backend::Scalar
346}
347
348#[cfg(feature = "simd")]
349fn active_decode_backend() -> Backend {
350 match crate::decode_backend::active_decode_backend() {
351 crate::decode_backend::DecodeBackend::Scalar => Backend::Scalar,
352 #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
353 crate::decode_backend::DecodeBackend::Avx512Vbmi => Backend::Avx512Vbmi,
354 #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
355 crate::decode_backend::DecodeBackend::Avx2 => Backend::Avx2,
356 #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
357 crate::decode_backend::DecodeBackend::Ssse3Sse41 => Backend::Ssse3Sse41,
358 #[cfg(all(feature = "std", target_arch = "aarch64", target_endian = "little"))]
359 crate::decode_backend::DecodeBackend::Neon => Backend::Neon,
360 #[cfg(all(feature = "simd", target_arch = "wasm32"))]
361 crate::decode_backend::DecodeBackend::WasmSimd128 => Backend::WasmSimd128,
362 }
363}
364
365#[cfg(not(feature = "simd"))]
366const fn active_decode_backend() -> Backend {
367 Backend::Scalar
368}
369
370#[cfg(feature = "simd")]
371fn detected_candidate() -> Backend {
372 match crate::simd::detected_candidate() {
373 crate::simd::Candidate::Scalar => Backend::Scalar,
374 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
375 crate::simd::Candidate::Avx512Vbmi => Backend::Avx512Vbmi,
376 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
377 crate::simd::Candidate::Avx2 => Backend::Avx2,
378 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
379 crate::simd::Candidate::Ssse3Sse41 => Backend::Ssse3Sse41,
380 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
381 crate::simd::Candidate::Neon => Backend::Neon,
382 #[cfg(target_arch = "wasm32")]
383 crate::simd::Candidate::WasmSimd128 => Backend::WasmSimd128,
384 }
385}
386
387#[cfg(not(feature = "simd"))]
388const fn detected_candidate() -> Backend {
389 Backend::Scalar
390}
391
392#[cfg(all(
393 feature = "simd",
394 feature = "std",
395 any(target_arch = "x86", target_arch = "x86_64")
396))]
397const fn candidate_detection_mode() -> CandidateDetectionMode {
398 CandidateDetectionMode::RuntimeCpuFeatures
399}
400
401#[cfg(all(
402 feature = "simd",
403 not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
404))]
405const fn candidate_detection_mode() -> CandidateDetectionMode {
406 CandidateDetectionMode::CompileTimeTargetFeatures
407}
408
409#[cfg(not(feature = "simd"))]
410const fn candidate_detection_mode() -> CandidateDetectionMode {
411 CandidateDetectionMode::SimdFeatureDisabled
412}