Skip to main content

catalejo_sys/
monitor.rs

1//! Protected hardware monitor instruction support.
2
3#[cfg(all(feature = "stealth-mode", not(test)))]
4use core::hint;
5
6use crate::{exception::backend::Backend, ffi::binding};
7
8/// A hardware implementation for monitoring an address.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum MonitorBackend {
11    /// Intel user monitor and user wait instructions.
12    IntelUmonitor,
13
14    /// AMD extended monitor and extended wait instructions.
15    AmdMonitorx,
16}
17
18/// A failure while arming or waiting with a hardware monitor.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum MonitorError {
21    /// The monitored local address faulted while it was being armed.
22    Fault,
23
24    /// The selected optional instruction is unavailable at runtime.
25    Unsupported,
26}
27
28/// Determine the runtime selected hardware monitor implementation.
29///
30/// A [`None`] result requests a polling fallback from the caller.
31#[inline]
32pub fn backend() -> Option<MonitorBackend> {
33    // SAFETY: CPUID-backed monitor selection does not execute a protected monitor instruction.
34    let target_backend = unsafe { binding::catalejo_monitor_select() };
35
36    match target_backend {
37        binding::CATALEJO_MONITOR_BACKEND_INTEL_UMONITOR => Some(MonitorBackend::IntelUmonitor),
38        binding::CATALEJO_MONITOR_BACKEND_AMD_MONITORX => Some(MonitorBackend::AmdMonitorx),
39        binding::CATALEJO_MONITOR_BACKEND_UNSUPPORTED => None,
40        #[cfg(not(feature = "stealth-mode"))]
41        _ => unreachable!(),
42
43        #[cfg(all(feature = "stealth-mode", test))]
44        _ => std::process::abort(),
45
46        #[cfg(all(feature = "stealth-mode", not(test)))]
47        // SAFETY: The C implementation returns only declared backend values.
48        _ => unsafe { hint::unreachable_unchecked() },
49    }
50}
51
52/// Arm the selected hardware monitor for an address.
53///
54/// # Safety
55///
56/// The address must name suitable local memory and remain live through the matching [`wait`] call.
57/// The fault backend must belong to the current process.
58#[inline]
59pub unsafe fn arm(
60    fault_backend: &Backend,
61    target_address: *const u8,
62) -> Result<MonitorBackend, MonitorError> {
63    if fault_backend.validate().is_err() {
64        return Err(MonitorError::Fault);
65    }
66
67    // SAFETY:
68    // The caller supplies the monitor address contract and the current process backend.
69    let target_outcome = unsafe { binding::catalejo_monitor_arm(target_address) };
70
71    match target_outcome {
72        binding::CATALEJO_MONITOR_ARM_INTEL_UMONITOR => Ok(MonitorBackend::IntelUmonitor),
73        binding::CATALEJO_MONITOR_ARM_AMD_MONITORX => Ok(MonitorBackend::AmdMonitorx),
74        binding::CATALEJO_MONITOR_ARM_FAULT => Err(MonitorError::Fault),
75        binding::CATALEJO_MONITOR_ARM_UNSUPPORTED => Err(MonitorError::Unsupported),
76        #[cfg(not(feature = "stealth-mode"))]
77        _ => unreachable!(),
78
79        #[cfg(all(feature = "stealth-mode", test))]
80        _ => std::process::abort(),
81
82        #[cfg(all(feature = "stealth-mode", not(test)))]
83        // SAFETY: The C implementation returns only declared monitor-arm outcomes.
84        _ => unsafe { hint::unreachable_unchecked() },
85    }
86}
87
88/// Wait for one bounded interval with an armed hardware monitor.
89///
90/// # Safety
91///
92/// The backend must come from the immediately preceding successful [`arm`] call on this thread.
93/// The monitored mapping must remain live. The fault backend must belong to the current process.
94#[inline]
95pub unsafe fn wait(fault_backend: &Backend, backend: MonitorBackend) -> Result<(), MonitorError> {
96    if fault_backend.validate().is_err() {
97        return Err(MonitorError::Fault);
98    }
99
100    let backend = match backend {
101        MonitorBackend::IntelUmonitor => binding::CATALEJO_MONITOR_BACKEND_INTEL_UMONITOR,
102        MonitorBackend::AmdMonitorx => binding::CATALEJO_MONITOR_BACKEND_AMD_MONITORX,
103    };
104
105    // SAFETY:
106    // The caller supplies the same-thread monitor contract and the current process backend.
107    let outcome = unsafe { binding::catalejo_monitor_wait(backend) };
108
109    match outcome {
110        binding::CATALEJO_OUTCOME_SUCCESS => Ok(()),
111        binding::CATALEJO_OUTCOME_ERROR => Err(MonitorError::Fault),
112        binding::CATALEJO_OUTCOME_INVALID_VALUE => Err(MonitorError::Unsupported),
113        #[cfg(not(feature = "stealth-mode"))]
114        _ => unreachable!(),
115
116        #[cfg(all(feature = "stealth-mode", test))]
117        _ => std::process::abort(),
118
119        #[cfg(all(feature = "stealth-mode", not(test)))]
120        // SAFETY: The C implementation returns only declared outcomes.
121        _ => unsafe { hint::unreachable_unchecked() },
122    }
123}