mik32-runtime 0.4.0

Minimal Rust runtime for the MIK32 Amur microcontroller
Documentation
//! Machine interrupt control for MIK32.
//!
//! The runtime disables and restores the global machine interrupt enable bit
//! (`mstatus.MIE`) together with machine external interrupts (`mie.MEIE`).
//! This matches the interrupt model used by MIK32 applications.

use core::arch::asm;

const MSTATUS_MIE: usize = 1 << 3;
const MIE_MEIE: usize = 1 << 11;

/// Previous interrupt state returned by [`disable`].
///
/// Keep this value and pass it back to [`restore`] when leaving a manual
/// critical section.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct RestoreState {
    bits: usize,
}

impl RestoreState {
    /// Returns `true` if global machine interrupts were enabled.
    #[inline]
    pub const fn machine_interrupts_enabled(self) -> bool {
        self.bits & MSTATUS_MIE != 0
    }

    /// Returns `true` if machine external interrupts were enabled.
    #[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),
        }
    }

}

/// Disables global machine interrupts and machine external interrupts.
///
/// Returns the previous state, which can later be passed to [`restore`].
#[inline]
pub fn disable() -> RestoreState {
    let mstatus: usize;
    let mie: usize;

    // SAFETY: CSR access is a single-hart machine-mode operation. The function
    // only clears interrupt-enable bits and returns the previous values.
    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)
}

/// Enables global machine interrupts and machine external interrupts.
///
/// # Safety
///
/// Enabling interrupts can resume interrupt handlers that access shared state.
/// Call this only when doing so cannot violate aliasing or critical-section
/// invariants.
#[inline]
pub unsafe fn enable() {
    // SAFETY: The caller guarantees that enabling interrupts is sound for the
    // current program state.
    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),
        );
    }
}

/// Restores the interrupt state returned by [`disable`].
///
/// # Safety
///
/// `state` must be the value returned by a matching [`disable`] call for the
/// current critical section. Restoring an unrelated state may enable interrupts
/// while protected data is still borrowed.
#[inline]
pub unsafe fn restore(state: RestoreState) {
    if state.machine_external_interrupt_enabled() {
        // SAFETY: The caller guarantees that restoring this state is sound.
        unsafe {
            asm!(
                "csrrs zero, mie, {mask}",
                mask = in(reg) MIE_MEIE,
                options(nostack),
            );
        }
    } else {
        // SAFETY: Clearing an interrupt-enable bit is safe for memory safety.
        unsafe {
            asm!(
                "csrrc zero, mie, {mask}",
                mask = in(reg) MIE_MEIE,
                options(nostack),
            );
        }
    }

    if state.machine_interrupts_enabled() {
        // SAFETY: The caller guarantees that restoring this state is sound.
        unsafe {
            asm!(
                "csrrs zero, mstatus, {mask}",
                mask = in(reg) MSTATUS_MIE,
                options(nostack),
            );
        }
    } else {
        // SAFETY: Clearing an interrupt-enable bit is safe for memory safety.
        unsafe {
            asm!(
                "csrrc zero, mstatus, {mask}",
                mask = in(reg) MSTATUS_MIE,
                options(nostack),
            );
        }
    }
}

/// Runs `f` with global machine interrupts and machine external interrupts
/// disabled, then restores the previous state.
#[inline]
pub fn free<R>(f: impl FnOnce() -> R) -> R {
    let state = disable();
    let result = f();

    // SAFETY: `state` was obtained from the matching `disable` call above.
    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;

        // SAFETY: CSR access is a single-hart machine-mode operation. Clearing
        // mstatus.MIE masks interrupt handlers without changing mie.MEIE.
        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 {
            // SAFETY: `was_enabled` is the raw value returned by `acquire`.
            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) {
            // SAFETY: `state` is the raw value returned by `acquire`.
            unsafe {
                restore_global_interrupts(state);
            }
        }
    }
}