use core::arch::asm;
#[repr(u8)]
#[derive(Clone, Copy)]
enum Features {
#[allow(dead_code)]
Neither = 1,
#[allow(dead_code)]
DitOnly = 2,
DitSb = 3,
}
#[cfg(not(all(target_feature = "dit", target_feature = "sb")))]
mod detect {
use super::Features;
use core::mem::transmute;
use core::sync::atomic::{AtomicU8, Ordering};
static FEATURES: AtomicU8 = AtomicU8::new(0);
#[inline]
pub fn get_aarch64_dit_sb_features() -> Features {
let features = FEATURES.load(Ordering::Relaxed);
if features > 0 {
unsafe { transmute::<u8, Features>(features) }
} else {
detect_aarch64_dit_sb_features()
}
}
pub unsafe fn set_aarch64_dit_sb_features(dit: bool, sb: bool) -> Features {
let dit = dit || cfg!(target_feature = "dit");
let sb = sb || cfg!(target_feature = "sb");
let features = match (dit, sb) {
(true, true) => Features::DitSb,
(true, false) => Features::DitOnly,
_ => Features::Neither,
};
let _ = FEATURES.compare_exchange(0, features as u8, Ordering::Relaxed, Ordering::Relaxed);
features
}
#[cfg(feature = "std")]
#[cold]
fn detect_aarch64_dit_sb_features() -> Features {
use std::arch::is_aarch64_feature_detected;
unsafe {
set_aarch64_dit_sb_features(
is_aarch64_feature_detected!("dit"),
is_aarch64_feature_detected!("sb"),
)
}
}
#[cfg(not(feature = "std"))]
#[cold]
fn detect_aarch64_dit_sb_features() -> Features {
unsafe {
set_aarch64_dit_sb_features(cfg!(target_feature = "dit"), cfg!(target_feature = "sb"))
}
}
}
pub unsafe fn set_aarch64_dit_sb_features(_dit: bool, _sb: bool) {
#[cfg(not(all(target_feature = "dit", target_feature = "sb")))]
unsafe {
detect::set_aarch64_dit_sb_features(_dit, _sb);
}
}
#[cfg(not(all(target_feature = "dit", target_feature = "sb")))]
use detect::get_aarch64_dit_sb_features;
#[cfg(all(target_feature = "dit", target_feature = "sb"))]
#[inline(always)]
fn get_aarch64_dit_sb_features() -> Features {
Features::DitSb
}
#[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,
{
match get_aarch64_dit_sb_features() {
Features::DitSb => {
unsafe { with_feat_dit_sb(f) }
}
Features::DitOnly => {
unsafe { with_feat_dit(f) }
}
Features::Neither => f(),
}
}
#[cfg(test)]
mod tests {
extern crate std;
use super::{rsr64_dit, with_dit};
use std::arch::is_aarch64_feature_detected;
#[test]
fn dit_is_restored_after_with_dit() {
if is_aarch64_feature_detected!("dit") {
unsafe {
let saved = rsr64_dit();
with_dit(|| assert_ne!(rsr64_dit(), 0));
assert_eq!(rsr64_dit(), saved);
}
}
}
}