xous-core/kernel/src/irq.rs
bunnie c23bed360f implement extraction of kernel analytics
this is gated behind the feature "debug-proc" because this could
leak attacker-useful information, and also it adds a primitive that
causes the kernel to write the debug information into any page
that the userspace already has access to. There are some checks
to ensure that the page is actually in RAM (so you can't use this
to trigger weird peripheral states, etc.) so hypothetically this
should just cause corruption in the userspace process if it is
abused, but still - more attack surface, more problems.

however, the feature is useful enough and the risk is small enough
that I think I'm going to leave it enabled for the foreseeable future.
It's definitely helpful to be able to see how much free memory
you have, and other resource allocation trackers like connection
limits and interrupt handler allocations.
2026-01-08 02:56:33 +08:00

121 lines
4.5 KiB
Rust

// SPDX-FileCopyrightText: 2020 Sean Cross <sean@xobs.io>
// SPDX-License-Identifier: Apache-2.0
use xous_kernel::{MemoryAddress, PID};
use crate::arch;
static mut IRQ_HANDLERS: [Option<(PID, MemoryAddress, Option<MemoryAddress>)>; 32] = [None; 32];
#[cfg(baremetal)]
pub fn handle(irqs_pending: usize) -> Result<xous_kernel::Result, xous_kernel::Error> {
use crate::services::SystemServices;
// Unsafe is required here because we're accessing a static
// mutable value, and it could be modified from various threads.
// However, this is fine because this is run from an IRQ context
// with interrupts disabled.
// NOTE: This will become an issue when running with multiple cores,
// so this should be protected by a mutex.
unsafe {
for (irq_no, handler) in (&mut *(&raw mut IRQ_HANDLERS)).iter().enumerate() {
if irqs_pending & (1 << irq_no) != 0 {
if let Some((pid, f, arg)) = handler {
return SystemServices::with_mut(|ss| {
// Disable all other IRQs and redirect into userspace
arch::irq::disable_all_irqs();
klog!(
"Making a callback to PID{}: {:x?} ({:08x}, {:x?})",
pid,
f,
irq_no as usize,
arg
);
ss.make_callback_to(
*pid,
f.get() as *mut usize,
crate::services::CallbackType::Interrupt(
irq_no,
arg.map(|x| x.get() as *mut usize).unwrap_or(core::ptr::null_mut::<usize>()),
),
)
.map(|_| xous_kernel::Result::ResumeProcess)
});
} else {
klog!("[!] Masked an unhandled IRQ #{:?}", irq_no);
// If there is no handler, mask this interrupt
// to prevent an IRQ storm. This is considered
// an error.
arch::irq::disable_irq(irq_no);
}
}
}
}
Ok(xous_kernel::Result::ResumeProcess)
}
#[allow(dead_code)] // needed to silence a hosted mode warning
pub fn for_each_irq<F>(mut op: F)
where
F: FnMut(usize, &PID, MemoryAddress, Option<MemoryAddress>),
{
unsafe {
for (idx, handler) in (&mut *(&raw mut IRQ_HANDLERS)).iter().enumerate() {
// Ignore threads that have no PC, and ignore the ISR thread
if let Some(handler) = handler {
op(idx, &handler.0, handler.1, handler.2);
}
}
}
}
pub fn interrupt_claim(
irq: usize,
pid: PID,
f: MemoryAddress,
arg: Option<MemoryAddress>,
) -> Result<(), xous_kernel::Error> {
// Unsafe is required since we're accessing a static mut array.
// However, we disable interrupts to prevent contention on this array.
unsafe {
if irq > (&mut *(&raw mut IRQ_HANDLERS)).len() {
Err(xous_kernel::Error::InterruptNotFound)
} else if (&mut *(&raw mut IRQ_HANDLERS))[irq].is_some() {
Err(xous_kernel::Error::InterruptInUse)
} else {
(&mut *(&raw mut IRQ_HANDLERS))[irq] = Some((pid, f, arg));
arch::irq::enable_irq(irq);
Ok(())
}
}
}
pub fn interrupt_free(irq: usize, pid: PID) -> Result<(), xous_kernel::Error> {
// Unsafe is required since we're accessing a static mut array.
// However, we disable interrupts to prevent contention on this array.
unsafe {
if irq > (&mut *(&raw mut IRQ_HANDLERS)).len() {
Err(xous_kernel::Error::InterruptNotFound)
} else if !(&mut *(&raw mut IRQ_HANDLERS))[irq].map(|f| f.0 == pid).unwrap_or(false) {
Err(xous_kernel::Error::InterruptNotFound)
} else {
arch::irq::disable_irq(irq);
(&mut *(&raw mut IRQ_HANDLERS))[irq] = None;
Ok(())
}
}
}
/// Iterate through the IRQ handlers and remove any handler that exists
/// for the given PID.
pub fn release_interrupts_for_pid(pid: PID) {
unsafe {
for (irq, handler) in (&mut *(&raw mut IRQ_HANDLERS)).iter_mut().enumerate() {
if let Some(h) = handler {
if h.0 == pid {
arch::irq::disable_irq(irq);
*handler = None;
}
}
}
}
}