| // Copyright 2025 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| use core::cell::UnsafeCell; |
| |
| use crate::scheduler::{PreemptDisableGuard, RescheduleReason}; |
| use crate::{Kernel, scheduler}; |
| |
| /// This trait provides a generic interface to an architecture's interrupt |
| /// controller, such as a RISC-V PLIC or an Arm NVIC. |
| pub trait InterruptController { |
| type InterruptHandler: Copy + Sync + Send + 'static; |
| |
| /// Called early in the kernel::main() function. |
| fn early_init(&self) {} |
| |
| /// Enable a specific interrupt by its IRQ number. |
| fn enable_interrupt(irq: u32); |
| |
| /// Disable a specific interrupt by its IRQ number. |
| fn disable_interrupt(irq: u32); |
| |
| /// Userspace handling of the interrupt is complete. Called |
| /// from [`syscall_defs::interrupt_ack(object_handle: u32, signal_mask: Signals)`] |
| fn userspace_interrupt_ack(irq: u32); |
| |
| /// Userspace interrupt handlers call [`userspace_interrupt_handler_enter(kernel: K, irq: u32)`] |
| /// to mask the interrupt prior to the interrupt object being signalled. The interrupt |
| /// is unmasked from userspace by [`userspace_interrupt_ack(irq: u32)`]. |
| fn userspace_interrupt_handler_enter<K: Kernel>(kernel: K, irq: u32); |
| |
| /// Userspace interrupt handler is complete. |
| /// |
| /// This consumes the `InterruptGuard` by value. When it goes out of scope, |
| /// its `Drop` implementation will handle thread termination checks and |
| /// deferred rescheduling. |
| fn userspace_interrupt_handler_exit<K: Kernel>(kernel: K, irq: u32, guard: InterruptGuard<K>); |
| |
| /// Kernel interrupt handler has started. |
| fn kernel_interrupt_handler_enter<K: Kernel>(kernel: K, irq: u32); |
| |
| /// Kernel interrupt handler is complete. |
| /// |
| /// This consumes the `InterruptGuard` by value. When it goes out of scope, |
| /// its `Drop` implementation will handle thread termination checks and |
| /// deferred rescheduling. |
| fn kernel_interrupt_handler_exit<K: Kernel>(kernel: K, irq: u32, guard: InterruptGuard<K>); |
| |
| /// Globally enable interrupts. |
| fn enable_interrupts(); |
| |
| /// Globally disable interrupts. |
| fn disable_interrupts(); |
| |
| /// Returns `true` if interrupts are globally enabled. |
| fn interrupts_enabled() -> bool; |
| |
| /// Trigger an interrupt by IRQ. This may not be supported |
| /// on all interrupt controllers. |
| fn trigger_interrupt(irq: u32); |
| } |
| |
| pub struct InterruptGuard<K: Kernel> { |
| kernel: K, |
| preempt_guard: Option<PreemptDisableGuard<K>>, |
| from_userspace: bool, |
| reason: RescheduleReason, |
| } |
| |
| impl<K: Kernel> InterruptGuard<K> { |
| pub fn new(kernel: K, from_userspace: bool) -> Self { |
| Self { |
| kernel, |
| preempt_guard: Some(crate::scheduler::PreemptDisableGuard::new(kernel)), |
| from_userspace, |
| reason: RescheduleReason::Preempted, |
| } |
| } |
| |
| pub fn set_reschedule_reason(&mut self, reason: RescheduleReason) { |
| self.reason = reason; |
| } |
| |
| pub fn from_userspace(&self) -> bool { |
| self.from_userspace |
| } |
| } |
| |
| impl<K: Kernel> Drop for InterruptGuard<K> { |
| fn drop(&mut self) { |
| if let Some(guard) = self.preempt_guard.take() { |
| drop(guard); |
| } |
| handle_thread_termination(self.kernel, self.from_userspace); |
| scheduler::try_deferred_reschedule(self.kernel, self.reason); |
| } |
| } |
| |
| /// Handle conditional termination of interrupted thread |
| /// |
| /// Checks if the current thread is terminating and was interrupted from userspace. |
| /// If so, exit the thread. |
| /// |
| /// This should be called when returning to interrupt or exception. |
| pub fn handle_thread_termination<K: Kernel>(kernel: K, from_userspace: bool) { |
| if !from_userspace { |
| return; |
| } |
| |
| let sched = kernel.get_scheduler().lock(kernel); |
| if sched.current_thread().is_terminating() { |
| sched.thread_exit(kernel); |
| } |
| } |
| |
| /// StaticContext provides a context with Send & Sync for |
| /// use with static instances of ForeignRc. |
| pub struct StaticContext<T> { |
| inner: UnsafeCell<Option<T>>, |
| } |
| |
| impl<T: Clone> StaticContext<T> { |
| pub const fn new() -> Self { |
| Self { |
| inner: UnsafeCell::new(None), |
| } |
| } |
| |
| /// # Safety |
| /// Users of [`StaticContext::set()`] must ensure that |
| /// their calls into them are not done concurrently. |
| pub unsafe fn set(&self, val: T) { |
| unsafe { *self.inner.get() = Some(val) }; |
| } |
| |
| /// # Safety |
| /// Users of [`StaticContext::get()`] must ensure that |
| /// their calls into them are not done concurrently. |
| pub unsafe fn get(&self) -> Option<T> { |
| unsafe { (*self.inner.get()).clone() } |
| } |
| } |
| |
| unsafe impl<T> Sync for StaticContext<T> {} |
| unsafe impl<T> Send for StaticContext<T> {} |
| |
| pub type InterruptTableEntry<I> = Option<<I as InterruptController>::InterruptHandler>; |