diff --git a/riscv/CHANGELOG.md b/riscv/CHANGELOG.md index 97233feb..0ec3f138 100644 --- a/riscv/CHANGELOG.md +++ b/riscv/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - New convenience `try_new` and `new` associated functions for `Mtvec` and `Stvec`. +- New methods and functions for enabling core interrupts in the `mie` and `sie` registers + using the `riscv_pac::CoreInterrupt` trait. +- New `riscv::interrupt::{is_interrupt_enabled, disable_interrupt, enable_interrupt}` functions. ### Changed diff --git a/riscv/src/interrupt/machine.rs b/riscv/src/interrupt/machine.rs index d88ceb46..52212f1d 100644 --- a/riscv/src/interrupt/machine.rs +++ b/riscv/src/interrupt/machine.rs @@ -1,6 +1,6 @@ use crate::{ interrupt::Trap, - register::{mcause, mepc, mstatus}, + register::{mcause, mepc, mie, mstatus}, }; use riscv_pac::{ result::{Error, Result}, @@ -96,18 +96,51 @@ unsafe impl ExceptionNumber for Exception { } } -/// Disables all interrupts in the current hart (machine mode). +/// Checks if a specific core interrupt source is enabled in the current hart (machine mode). +#[inline] +pub fn is_interrupt_enabled(interrupt: I) -> bool { + mie::read().is_enabled(interrupt) +} + +/// Disables interrupts for a specific core interrupt source in the current hart (machine mode). +#[inline] +pub fn disable_interrupt(interrupt: I) { + mie::disable(interrupt); +} + +/// Enables interrupts for a specific core interrupt source in the current hart (machine mode). +/// +/// # Note +/// +/// Interrupts will only be triggered if globally enabled in the hart. To do this, use [`enable`]. +/// +/// # Safety +/// +/// Enabling interrupts might break critical sections or other synchronization mechanisms. +/// Ensure that this is called in a safe context where interrupts can be enabled. +#[inline] +pub unsafe fn enable_interrupt(interrupt: I) { + mie::enable(interrupt); +} + +/// Disables interrupts globally in the current hart (machine mode). #[inline] pub fn disable() { // SAFETY: It is safe to disable interrupts unsafe { mstatus::clear_mie() } } -/// Enables all the interrupts in the current hart (machine mode). +/// Enables interrupts globally in the current hart (machine mode). +/// +/// # Note +/// +/// Only enabled interrupt sources will be triggered. +/// To enable specific interrupt sources, use [`enable_interrupt`]. /// /// # Safety /// -/// Do not call this function inside a critical section. +/// Enabling interrupts might break critical sections or other synchronization mechanisms. +/// Ensure that this is called in a safe context where interrupts can be enabled. #[inline] pub unsafe fn enable() { mstatus::set_mie() diff --git a/riscv/src/interrupt/supervisor.rs b/riscv/src/interrupt/supervisor.rs index ddb913c2..8db43f98 100644 --- a/riscv/src/interrupt/supervisor.rs +++ b/riscv/src/interrupt/supervisor.rs @@ -1,6 +1,6 @@ use crate::{ interrupt::Trap, - register::{scause, sepc, sstatus}, + register::{scause, sepc, sie, sstatus}, }; use riscv_pac::{ result::{Error, Result}, @@ -88,18 +88,52 @@ unsafe impl ExceptionNumber for Exception { } } -/// Disables all interrupts in the current hart (supervisor mode). +/// Checks if a specific core interrupt source is enabled in the current hart (supervisor mode). +#[inline] +pub fn is_interrupt_enabled(interrupt: I) -> bool { + sie::read().is_enabled(interrupt) +} + +/// Disables interrupts for a specific core interrupt source in the current hart (supervisor mode). +#[inline] +pub fn disable_interrupt(interrupt: I) { + // SAFETY: it is safe to disable an interrupt source + sie::disable(interrupt); +} + +/// Enables interrupts for a specific core interrupt source in the current hart (supervisor mode). +/// +/// # Note +/// +/// Interrupts will only be triggered if globally enabled in the hart. To do this, use [`enable`]. +/// +/// # Safety +/// +/// Enabling interrupts might break critical sections or other synchronization mechanisms. +/// Ensure that this is called in a safe context where interrupts can be enabled. +#[inline] +pub unsafe fn enable_interrupt(interrupt: I) { + sie::enable(interrupt); +} + +/// Disables interrupts globally in the current hart (supervisor mode). #[inline] pub fn disable() { // SAFETY: It is safe to disable interrupts unsafe { sstatus::clear_sie() } } -/// Enables all the interrupts in the current hart (supervisor mode). +/// Enables interrupts globally in the current hart (supervisor mode). +/// +/// # Note +/// +/// Only enabled interrupt sources will be triggered. +/// To enable specific interrupt sources, use [`enable_interrupt`]. /// /// # Safety /// -/// Do not call this function inside a critical section. +/// Enabling interrupts might break critical sections or other synchronization mechanisms. +/// Ensure that this is called in a safe context where interrupts can be enabled. #[inline] pub unsafe fn enable() { sstatus::set_sie() diff --git a/riscv/src/register/mie.rs b/riscv/src/register/mie.rs index 2c273ecf..0ca87677 100644 --- a/riscv/src/register/mie.rs +++ b/riscv/src/register/mie.rs @@ -1,9 +1,12 @@ //! mie register +use crate::bits::{bf_extract, bf_insert}; +use riscv_pac::CoreInterruptNumber; + read_write_csr! { /// `mie` register Mie: 0x304, - mask: 0xaaa, + mask: usize::MAX, } read_write_csr_field! { @@ -42,6 +45,26 @@ read_write_csr_field! { mext: 11, } +impl Mie { + /// Check if a specific core interrupt source is enabled. + #[inline] + pub fn is_enabled(&self, interrupt: I) -> bool { + bf_extract(self.bits, interrupt.number(), 1) != 0 + } + + /// Enable a specific core interrupt source. + #[inline] + pub fn enable(&mut self, interrupt: I) { + self.bits = bf_insert(self.bits, interrupt.number(), 1, 1); + } + + /// Disable a specific core interrupt source. + #[inline] + pub fn disable(&mut self, interrupt: I) { + self.bits = bf_insert(self.bits, interrupt.number(), 1, 0); + } +} + set!(0x304); clear!(0x304); @@ -64,9 +87,28 @@ set_clear_csr!( /// Machine External Interrupt Enable , set_mext, clear_mext, 1 << 11); +/// Disables a specific core interrupt source. +#[inline] +pub fn disable(interrupt: I) { + // SAFETY: it is safe to disable an interrupt source + unsafe { _clear(1 << interrupt.number()) }; +} + +/// Enables a specific core interrupt source. +/// +/// # Safety +/// +/// Enabling interrupts might break critical sections or other synchronization mechanisms. +/// Ensure that this is called in a safe context where interrupts can be enabled. +#[inline] +pub unsafe fn enable(interrupt: I) { + unsafe { _set(1 << interrupt.number()) }; +} + #[cfg(test)] mod tests { use super::*; + use crate::interrupt::machine::Interrupt; #[test] fn test_mie() { @@ -79,4 +121,39 @@ mod tests { test_csr_field!(m, sext); test_csr_field!(m, mext); } + + #[test] + fn test_mie_interrupt() { + let mut m = Mie::from_bits(0); + + m.enable(Interrupt::SupervisorSoft); + assert!(m.is_enabled(Interrupt::SupervisorSoft)); + m.disable(Interrupt::SupervisorSoft); + assert!(!m.is_enabled(Interrupt::SupervisorSoft)); + + m.enable(Interrupt::MachineSoft); + assert!(m.is_enabled(Interrupt::MachineSoft)); + m.disable(Interrupt::MachineSoft); + assert!(!m.is_enabled(Interrupt::MachineSoft)); + + m.enable(Interrupt::SupervisorTimer); + assert!(m.is_enabled(Interrupt::SupervisorTimer)); + m.disable(Interrupt::SupervisorTimer); + assert!(!m.is_enabled(Interrupt::SupervisorTimer)); + + m.enable(Interrupt::MachineTimer); + assert!(m.is_enabled(Interrupt::MachineTimer)); + m.disable(Interrupt::MachineTimer); + assert!(!m.is_enabled(Interrupt::MachineTimer)); + + m.enable(Interrupt::SupervisorExternal); + assert!(m.is_enabled(Interrupt::SupervisorExternal)); + m.disable(Interrupt::SupervisorExternal); + assert!(!m.is_enabled(Interrupt::SupervisorExternal)); + + m.enable(Interrupt::MachineExternal); + assert!(m.is_enabled(Interrupt::MachineExternal)); + m.disable(Interrupt::MachineExternal); + assert!(!m.is_enabled(Interrupt::MachineExternal)); + } } diff --git a/riscv/src/register/sie.rs b/riscv/src/register/sie.rs index a8ee2812..dfeecdaa 100644 --- a/riscv/src/register/sie.rs +++ b/riscv/src/register/sie.rs @@ -1,9 +1,12 @@ //! sie register +use crate::bits::{bf_extract, bf_insert}; +use riscv_pac::CoreInterruptNumber; + read_write_csr! { /// sie register Sie: 0x104, - mask: 0x222, + mask: usize::MAX, } read_write_csr_field! { @@ -24,6 +27,26 @@ read_write_csr_field! { sext: 9, } +impl Sie { + /// Check if a specific core interrupt source is enabled. + #[inline] + pub fn is_enabled(&self, interrupt: I) -> bool { + bf_extract(self.bits, interrupt.number(), 1) != 0 + } + + /// Enable a specific core interrupt source. + #[inline] + pub fn enable(&mut self, interrupt: I) { + self.bits = bf_insert(self.bits, interrupt.number(), 1, 1); + } + + /// Disable a specific core interrupt source. + #[inline] + pub fn disable(&mut self, interrupt: I) { + self.bits = bf_insert(self.bits, interrupt.number(), 1, 0); + } +} + set!(0x104); clear!(0x104); @@ -37,9 +60,28 @@ set_clear_csr!( /// Supervisor External Interrupt Enable , set_sext, clear_sext, 1 << 9); +/// Disables a specific core interrupt source. +#[inline] +pub fn disable(interrupt: I) { + // SAFETY: it is safe to disable an interrupt source + unsafe { _clear(1 << interrupt.number()) }; +} + +/// Enables a specific core interrupt source. +/// +/// # Safety +/// +/// Enabling interrupts might break critical sections or other synchronization mechanisms. +/// Ensure that this is called in a safe context where interrupts can be enabled. +#[inline] +pub unsafe fn enable(interrupt: I) { + unsafe { _set(1 << interrupt.number()) }; +} + #[cfg(test)] mod tests { use super::*; + use crate::interrupt::supervisor::Interrupt; #[test] fn test_sie() { @@ -49,4 +91,24 @@ mod tests { test_csr_field!(sie, stimer); test_csr_field!(sie, sext); } + + #[test] + fn test_sie_interrupt() { + let mut s = Sie::from_bits(0); + + s.enable(Interrupt::SupervisorSoft); + assert!(s.is_enabled(Interrupt::SupervisorSoft)); + s.disable(Interrupt::SupervisorSoft); + assert!(!s.is_enabled(Interrupt::SupervisorSoft)); + + s.enable(Interrupt::SupervisorTimer); + assert!(s.is_enabled(Interrupt::SupervisorTimer)); + s.disable(Interrupt::SupervisorTimer); + assert!(!s.is_enabled(Interrupt::SupervisorTimer)); + + s.enable(Interrupt::SupervisorExternal); + assert!(s.is_enabled(Interrupt::SupervisorExternal)); + s.disable(Interrupt::SupervisorExternal); + assert!(!s.is_enabled(Interrupt::SupervisorExternal)); + } }