use crate::source::{EntropySource, Platform, Requirement, SourceCategory, SourceInfo};
static GXF_REGISTER_TIMING_INFO: SourceInfo = SourceInfo {
name: "gxf_register_timing",
description: "Apple GXF EL0-accessible register trap-path timing entropy",
physics: "S3_6_c15_c1_5 (GXF namespace) is readable from EL0 via JIT-generated MRS, \
producing a multimodal timing distribution: 0/41/42/83/84 ticks, CV=35.2%. \
Modes reflect the GXF trap-and-emulate path: 0=pipeline shortcut, \
41=single trap cycle, 42=trap+hazard, 83=double trap (GXF monitor busy). \
41-tick trap latency matches APRR toggle latency, confirming Apple EL1 \
security monitor round-trip time. Entropy encodes GXF monitor scheduling \
state. Register value is static (0x2010002030100000, capability bitmask); \
entropy comes solely from trap-path timing variation. Novel finding: first \
EL0-accessible GXF namespace register characterised as entropy source.",
category: SourceCategory::Microarch,
platform: Platform::MacOS,
requirements: &[Requirement::AppleSilicon],
entropy_rate_estimate: 0.7,
composite: false,
is_fast: false,
};
pub struct GxfRegisterTimingSource;
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
mod imp {
use super::*;
use crate::sources::helpers::extract_timing_entropy_debiased;
use crate::sources::helpers::mach_time;
use std::sync::Once;
use std::sync::atomic::{AtomicBool, Ordering};
const GXF_MRS_X0: u32 = 0xD5380000u32
| (6u32 << 16) | (15u32 << 12) | (1u32 << 8) | (5u32 << 5); const RET: u32 = 0xD65F03C0u32;
type FnPtr = unsafe extern "C" fn() -> u64;
struct JitPage(*mut libc::c_void);
impl Drop for JitPage {
fn drop(&mut self) {
unsafe {
libc::munmap(self.0, 4096);
}
}
}
static CHECKED: Once = Once::new();
static AVAILABLE: AtomicBool = AtomicBool::new(false);
unsafe fn build_jit() -> Option<(FnPtr, JitPage)> {
let page = unsafe {
libc::mmap(
std::ptr::null_mut(),
4096,
libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | 0x0800, -1,
0,
)
};
if page == libc::MAP_FAILED {
return None;
}
unsafe {
libc::pthread_jit_write_protect_np(0);
let code = page as *mut u32;
code.write(GXF_MRS_X0);
code.add(1).write(RET);
libc::pthread_jit_write_protect_np(1);
core::arch::asm!("dc cvau, {p}", "ic ivau, {p}", p = in(reg) page, options(nostack));
core::arch::asm!("dsb ish", "isb", options(nostack));
}
let fn_ptr: FnPtr = unsafe { std::mem::transmute(page) };
Some((fn_ptr, JitPage(page)))
}
#[inline]
unsafe fn time_gxf(fn_ptr: FnPtr) -> u64 {
core::sync::atomic::fence(Ordering::SeqCst);
let t0 = mach_time();
let _v = unsafe { fn_ptr() };
let t1 = mach_time();
core::sync::atomic::fence(Ordering::SeqCst);
t1.wrapping_sub(t0)
}
impl EntropySource for GxfRegisterTimingSource {
fn info(&self) -> &SourceInfo {
&GXF_REGISTER_TIMING_INFO
}
fn is_available(&self) -> bool {
CHECKED.call_once(|| {
let ok = crate::sources::helpers::probe_jit_instruction_safe(GXF_MRS_X0);
AVAILABLE.store(ok, Ordering::SeqCst);
});
AVAILABLE.load(Ordering::SeqCst)
}
fn collect(&self, n_samples: usize) -> Vec<u8> {
unsafe {
let Some((fn_ptr, _page_guard)) = build_jit() else {
return Vec::new();
};
for _ in 0..32 {
let _ = time_gxf(fn_ptr);
}
let raw_count = n_samples * 8 + 256;
let mut timings = Vec::with_capacity(raw_count);
for _ in 0..raw_count {
let t = time_gxf(fn_ptr);
if t <= 200 {
timings.push(t);
}
}
extract_timing_entropy_debiased(&timings, n_samples)
}
}
}
}
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
impl EntropySource for GxfRegisterTimingSource {
fn info(&self) -> &SourceInfo {
&GXF_REGISTER_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 = GxfRegisterTimingSource;
assert_eq!(src.info().name, "gxf_register_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 availability_probe_does_not_crash() {
let src = GxfRegisterTimingSource;
let _ = src.is_available();
}
#[test]
#[ignore] fn collects_multimodal_timing() {
let src = GxfRegisterTimingSource;
if !src.is_available() {
return;
}
let data = src.collect(32);
assert!(!data.is_empty());
let unique: std::collections::HashSet<u8> = data.iter().copied().collect();
assert!(unique.len() >= 2, "expected GXF trap-path timing variation");
}
}