use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
use crate::sources::helpers::{extract_timing_entropy, mach_time};
static SEV_EVENT_TIMING_INFO: SourceInfo = SourceInfo {
name: "sev_event_timing",
description: "ARM64 SEV/SEVL broadcast event timing via ICC fabric load",
physics: "Times `SEV` (Send Event, all-core broadcast) and `SEVL` (Send Event Local). \
SEV traverses the ICC fabric to wake WFE-waiting cores; timing reflects ICC \
bus load from all concurrent WFE/SEV operations across all processes. \
Paradox: SEVL is 3\u{00d7} slower than SEV (local event register write is less \
optimized than ICC broadcast path). Measured: SEV CV=240.4%, mean=6.1 ticks; \
SEVL CV=103.5%, mean=20.1 ticks, range=0\u{2013}58 ticks. XOR-mixed to combine \
ICC fabric state (SEV) with local core pipeline state (SEVL).",
category: SourceCategory::Microarch,
platform: Platform::MacOS,
requirements: &[],
entropy_rate_estimate: 3.0,
composite: false,
is_fast: false,
};
pub struct SEVEventTimingSource;
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
impl EntropySource for SEVEventTimingSource {
fn info(&self) -> &SourceInfo {
&SEV_EVENT_TIMING_INFO
}
fn is_available(&self) -> bool {
true
}
fn collect(&self, n_samples: usize) -> Vec<u8> {
let raw = n_samples * 4 + 32;
let mut sev_times = Vec::with_capacity(raw);
let mut sevl_times = Vec::with_capacity(raw);
for _ in 0..16 {
unsafe {
core::arch::asm!("sev", options(nostack, nomem));
core::arch::asm!("sevl", options(nostack, nomem));
}
}
for _ in 0..raw {
let t0 = mach_time();
unsafe {
core::arch::asm!("sev", options(nostack, nomem));
}
let sev_t = mach_time().wrapping_sub(t0);
let t1 = mach_time();
unsafe {
core::arch::asm!("sevl", options(nostack, nomem));
}
let sevl_t = mach_time().wrapping_sub(t1);
if sev_t < 48_000 && sevl_t < 48_000 {
sev_times.push(sev_t);
sevl_times.push(sevl_t ^ (sev_t.wrapping_shl(7)));
}
}
let min = sev_times.len().min(sevl_times.len());
let mut combined = Vec::with_capacity(min * 2);
for i in 0..min {
combined.push(sev_times[i]);
combined.push(sevl_times[i]);
}
extract_timing_entropy(&combined, n_samples)
}
}
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
impl EntropySource for SEVEventTimingSource {
fn info(&self) -> &SourceInfo {
&SEV_EVENT_TIMING_INFO
}
fn is_available(&self) -> bool {
false
}
fn collect(&self, _n_samples: usize) -> Vec<u8> {
Vec::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn info() {
let src = SEVEventTimingSource;
assert_eq!(src.info().name, "sev_event_timing");
assert!(matches!(src.info().category, SourceCategory::Microarch));
assert_eq!(src.info().platform, Platform::MacOS);
assert!(!src.info().composite);
}
#[test]
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
fn is_available_on_apple_silicon() {
assert!(SEVEventTimingSource.is_available());
}
#[test]
#[ignore]
fn collects_with_variation() {
let data = SEVEventTimingSource.collect(32);
assert!(!data.is_empty());
}
}