use core::arch::asm;
const MSTATUS_MIE: usize = 1 << 3;
const MIE_MEIE: usize = 1 << 11;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct RestoreState {
bits: usize,
}
impl RestoreState {
#[inline]
pub const fn machine_interrupts_enabled(self) -> bool {
self.bits & MSTATUS_MIE != 0
}
#[inline]
pub const fn machine_external_interrupt_enabled(self) -> bool {
self.bits & MIE_MEIE != 0
}
#[inline]
const fn from_raw_parts(mstatus: usize, mie: usize) -> Self {
Self {
bits: (mstatus & MSTATUS_MIE) | (mie & MIE_MEIE),
}
}
}
#[inline]
pub fn disable() -> RestoreState {
let mstatus: usize;
let mie: usize;
unsafe {
asm!(
"csrrc {mstatus}, mstatus, {mstatus_mie}",
"csrrc {mie}, mie, {mie_meie}",
mstatus = out(reg) mstatus,
mie = out(reg) mie,
mstatus_mie = in(reg) MSTATUS_MIE,
mie_meie = in(reg) MIE_MEIE,
options(nostack),
);
}
RestoreState::from_raw_parts(mstatus, mie)
}
#[inline]
pub unsafe fn enable() {
unsafe {
asm!(
"csrrs zero, mie, {mie_meie}",
"csrrs zero, mstatus, {mstatus_mie}",
mstatus_mie = in(reg) MSTATUS_MIE,
mie_meie = in(reg) MIE_MEIE,
options(nostack),
);
}
}
#[inline]
pub unsafe fn restore(state: RestoreState) {
if state.machine_external_interrupt_enabled() {
unsafe {
asm!(
"csrrs zero, mie, {mask}",
mask = in(reg) MIE_MEIE,
options(nostack),
);
}
} else {
unsafe {
asm!(
"csrrc zero, mie, {mask}",
mask = in(reg) MIE_MEIE,
options(nostack),
);
}
}
if state.machine_interrupts_enabled() {
unsafe {
asm!(
"csrrs zero, mstatus, {mask}",
mask = in(reg) MSTATUS_MIE,
options(nostack),
);
}
} else {
unsafe {
asm!(
"csrrc zero, mstatus, {mask}",
mask = in(reg) MSTATUS_MIE,
options(nostack),
);
}
}
}
#[inline]
pub fn free<R>(f: impl FnOnce() -> R) -> R {
let state = disable();
let result = f();
unsafe {
restore(state);
}
result
}
#[cfg(feature = "critical-section")]
mod critical_section_impl {
use super::MSTATUS_MIE;
use core::arch::asm;
use critical_section::{Impl, RawRestoreState, set_impl};
struct SingleHartCriticalSection;
set_impl!(SingleHartCriticalSection);
#[inline]
fn disable_global_interrupts() -> bool {
let mstatus: usize;
unsafe {
asm!(
"csrrc {mstatus}, mstatus, {mask}",
mstatus = out(reg) mstatus,
mask = in(reg) MSTATUS_MIE,
options(nostack),
);
}
mstatus & MSTATUS_MIE != 0
}
#[inline]
unsafe fn restore_global_interrupts(was_enabled: bool) {
if was_enabled {
unsafe {
asm!(
"csrrs zero, mstatus, {mask}",
mask = in(reg) MSTATUS_MIE,
options(nostack),
);
}
}
}
unsafe impl Impl for SingleHartCriticalSection {
unsafe fn acquire() -> RawRestoreState {
disable_global_interrupts()
}
unsafe fn release(state: RawRestoreState) {
unsafe {
restore_global_interrupts(state);
}
}
}
}