1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/// Run a closure with disabled interrupts.
///
/// Run the given closure, disabling interrupts before running it (if they aren't already disabled).
/// Afterwards, interrupts are enabling again if they were enabled before.
///
/// If you have other `enable` and `disable` calls _within_ the closure, things may not work as expected.
///
/// Only has an effect if `target_os = "none"`.
///
/// # Examples
///
/// ```
/// use hermit_sync::without_interrupts;
///
/// // interrupts are enabled
/// without_interrupts(|| {
///     // interrupts are disabled
///     without_interrupts(|| {
///         // interrupts are disabled
///     });
///     // interrupts are still disabled
/// });
/// // interrupts are enabled again
/// ```
// Doc taken from `x86_64::instructions::interrupts::without_interrupts`.
#[inline]
pub fn without_interrupts<F, R>(f: F) -> R
where
    F: FnOnce() -> R,
{
    let flags = read_disable();

    let ret = f();

    restore(flags);

    ret
}

#[inline]
pub(crate) fn read_disable() -> imp::Flags {
    if cfg!(target_os = "none") {
        let flags = imp::get();

        if flags != DISABLE {
            imp::set(DISABLE);
        }

        flags
    } else {
        DISABLE
    }
}

#[inline]
pub(crate) fn restore(flags: imp::Flags) {
    if flags != DISABLE && cfg!(target_os = "none") {
        imp::set(flags);
    }
}

pub(crate) use imp::{AtomicFlags, DISABLE};

#[cfg(target_arch = "x86_64")]
mod imp {
    use x86_64::instructions::interrupts;

    pub type Flags = bool;
    pub type AtomicFlags = core::sync::atomic::AtomicBool;

    pub const DISABLE: bool = false;

    pub use interrupts::are_enabled as get;

    #[inline]
    pub fn set(enable: bool) {
        if enable {
            interrupts::enable();
        } else {
            interrupts::disable();
        }
    }
}

#[cfg(target_arch = "aarch64")]
mod imp {
    use aarch64_cpu::registers::DAIF;
    use tock_registers::interfaces::{Readable, Writeable};

    pub type Flags = u64;
    pub type AtomicFlags = core::sync::atomic::AtomicU64;

    /// Set the `A`, `I`, and `F` bit for _masking_ interrupts.
    pub const DISABLE: u64 = 0b111000000;

    #[inline]
    pub fn get() -> u64 {
        // Return only the relevant bits
        DAIF.get() & DISABLE
    }

    #[inline]
    pub fn set(value: u64) {
        // Set only the relevant bits
        let value = (DAIF.get() & !DISABLE) | value;
        DAIF.set(value);
    }
}

#[cfg(target_arch = "riscv64")]
mod imp {
    use riscv::register::sstatus;

    pub type Flags = bool;
    pub type AtomicFlags = core::sync::atomic::AtomicBool;

    pub const DISABLE: bool = false;

    #[inline]
    pub fn get() -> bool {
        sstatus::read().sie()
    }

    #[inline]
    pub fn set(value: bool) {
        unsafe {
            if value {
                sstatus::set_sie();
            } else {
                sstatus::clear_sie();
            }
        }
    }
}