use core::arch::asm;
#[cfg(feature = "std")]
#[inline(always)]
fn is_feat_dit_implemented() -> bool {
std::arch::is_aarch64_feature_detected!("dit")
}
#[cfg(feature = "std")]
#[inline(always)]
fn is_feat_sb_implemented() -> bool {
std::arch::is_aarch64_feature_detected!("sb")
}
#[cfg(not(feature = "std"))]
#[inline(always)]
fn is_feat_dit_implemented() -> bool {
cfg!(target_feature = "dit")
}
#[cfg(not(feature = "std"))]
#[inline(always)]
fn is_feat_sb_implemented() -> bool {
cfg!(target_feature = "sb")
}
#[inline]
#[target_feature(enable = "dit")]
unsafe fn rsr64_dit() -> u64 {
let mut value;
unsafe {
asm!("mrs {}, dit", lateout(reg) value, options(nomem, preserves_flags, nostack));
}
value
}
#[inline]
#[target_feature(enable = "dit")]
unsafe fn wsr64_dit(value: u64) {
unsafe {
asm!("msr dit, {}", in(reg) value, options(nostack));
}
}
#[inline]
#[target_feature(enable = "dit")]
unsafe fn enable_dit() {
unsafe {
asm!("msr dit, #{}", const 1, options(nostack));
}
}
#[inline]
#[target_feature(enable = "sb")]
unsafe fn speculation_barrier() {
unsafe {
asm!("sb", options(nostack));
}
}
#[inline]
fn synchronization_barrier() {
unsafe {
asm!("dsb nsh", "isb sy", options(nostack));
}
}
#[inline]
#[target_feature(enable = "dit,sb")]
unsafe fn with_feat_dit_sb<T, F>(f: F) -> T
where
F: FnOnce() -> T,
{
struct Guard {
dit: u64,
}
impl Drop for Guard {
#[inline]
fn drop(&mut self) {
unsafe { wsr64_dit(self.dit) };
}
}
let _guard = Guard {
dit: unsafe { rsr64_dit() },
};
unsafe { enable_dit() };
unsafe { speculation_barrier() };
f()
}
#[inline]
#[target_feature(enable = "dit")]
unsafe fn with_feat_dit<T, F>(f: F) -> T
where
F: FnOnce() -> T,
{
struct Guard {
dit: u64,
}
impl Drop for Guard {
#[inline]
fn drop(&mut self) {
unsafe { wsr64_dit(self.dit) };
}
}
let _guard = Guard {
dit: unsafe { rsr64_dit() },
};
unsafe { enable_dit() };
synchronization_barrier();
f()
}
#[inline]
pub(crate) fn with_dit<T, F>(f: F) -> T
where
F: FnOnce() -> T,
{
if is_feat_dit_implemented() {
if is_feat_sb_implemented() {
unsafe { with_feat_dit_sb(f) }
} else {
unsafe { with_feat_dit(f) }
}
} else {
f()
}
}
#[cfg(test)]
mod tests {
use super::{is_feat_dit_implemented, rsr64_dit, with_dit};
#[test]
fn dit_is_restored_after_with_dit() {
if is_feat_dit_implemented() {
unsafe {
let saved = rsr64_dit();
with_dit(|| assert_ne!(rsr64_dit(), 0));
assert_eq!(rsr64_dit(), saved);
}
}
}
}