use core::arch::x86_64::_rdtsc;
use x86_intr::{
arch::{_mm_monitor, _mm_mwait, _monitorx, _mwaitx, _umonitor, _umwait, mwaitx_no_timeout},
is_cpuid_feature_detected,
};
#[derive(Debug, Clone, Copy)]
pub struct HardwareMonitor(());
impl HardwareMonitor {
#[inline]
pub fn new<T>(addr: *const T, support: MonitorSupport) -> Self {
match support.inner {
MonitorSupportBackend::Monitor => unsafe {
_mm_monitor(addr.cast(), 0, 0);
},
MonitorSupportBackend::Monitorx => unsafe {
_monitorx(addr.cast(), 0, 0);
},
MonitorSupportBackend::Waitpkg => unsafe {
_umonitor(addr.cast());
},
MonitorSupportBackend::None => panic!("hardware monitors not supported on platform"),
}
Self(())
}
#[inline]
pub fn wait(self, control: MonitorControl, support: MonitorSupport) {
const DEFAULT_TIMEOUT: u64 = 65536;
match support.inner {
MonitorSupportBackend::Monitor => unsafe {
_mm_mwait(0, control as u32);
},
MonitorSupportBackend::Monitorx => unsafe {
mwaitx_no_timeout(control as u32);
},
MonitorSupportBackend::Waitpkg => unsafe {
let deadline = _rdtsc() + DEFAULT_TIMEOUT;
_umwait(control as u32, deadline);
},
MonitorSupportBackend::None => panic!("hardware monitors not supported on platform"),
}
}
pub fn wait_timeout(self, timeout: u64, control: MonitorControl, support: MonitorSupport) {
match support.inner {
MonitorSupportBackend::Monitor => unsafe {
_mm_mwait(0, control as u32);
},
MonitorSupportBackend::Monitorx => unsafe {
_mwaitx(0, control as u32, timeout as u32);
},
MonitorSupportBackend::Waitpkg => unsafe {
let deadline = _rdtsc() + timeout;
_umwait(control as u32, deadline);
},
MonitorSupportBackend::None => panic!("hardware monitors not supported on platform"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum MonitorControl {
Power = 0,
Performance = 1,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum MonitorSupportBackend {
#[default]
None,
Waitpkg,
Monitorx,
Monitor,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct MonitorSupport {
pub(crate) inner: MonitorSupportBackend,
}
impl MonitorSupport {
pub fn platform() -> Self {
let inner = if is_cpuid_feature_detected!("waitpkg") {
MonitorSupportBackend::Waitpkg
} else if is_cpuid_feature_detected!("monitorx") {
MonitorSupportBackend::Monitorx
} else {
MonitorSupportBackend::None
};
Self { inner }
}
pub const unsafe fn new_unchecked(inner: MonitorSupportBackend) -> Self {
Self { inner }
}
pub const fn backend(self) -> MonitorSupportBackend {
self.inner
}
}