irq: more flexible interrupt tables

This commit is contained in:
2026-02-03 13:29:49 +02:00
parent 58dbaddf11
commit 6b5dd9f673
13 changed files with 112 additions and 101 deletions
+12 -13
View File
@@ -2,8 +2,7 @@ use alloc::{sync::Arc, vec::Vec};
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
IrqHandle, IrqOptions, IrqVector,
ExternalInterruptController, InterruptHandler, Irq, IrqHandle, IrqOptions, IrqVector,
},
};
use device_tree::{
@@ -13,7 +12,10 @@ use device_tree::{
},
};
use kernel_arch_riscv64::boot_hart_id;
use libk::{arch::Cpu, device::register_external_interrupt_controller};
use libk::{
arch::Cpu,
device::{interrupt::FixedInterruptTable, register_external_interrupt_controller},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
use tock_registers::{
@@ -77,8 +79,7 @@ register_structs! {
struct Context {
enable: IrqSafeRwLock<DeviceMemoryIo<'static, ContextEnableRegs>>,
control: IrqSafeRwLock<DeviceMemoryIo<'static, ContextControlRegs>>,
// TODO scale the table depending on effective MAX_IRQS value
table: IrqSafeRwLock<FixedInterruptTable<128>>,
table: FixedInterruptTable,
}
struct Inner {
@@ -163,13 +164,13 @@ impl ExternalInterruptController for Plic {
.inspect_err(|_| log::error!("plic: no context for hart {bsp_hart_id}"))?
.context
.get();
let mut table = context.table.write();
// let mut table = context.table.write();
log::info!(
"Bind irq #{irq} -> hart {bsp_hart_id}, {:?}",
handler.display_name()
);
table.insert(irq as usize, handler)?;
context.table.insert(irq as usize, handler);
Ok(())
}
@@ -183,7 +184,7 @@ impl ExternalInterruptController for Plic {
let context = context.context.get();
let control = context.control.write();
let table = context.table.read();
// let table = context.table.read();
loop {
let irq = control.CLAIM.get();
@@ -192,9 +193,7 @@ impl ExternalInterruptController for Plic {
}
let vector = IrqVector::Irq(Irq::External(irq));
if let Some(handler) = table.handler(irq as usize) {
handler.clone().handle_irq(vector);
} else {
if !context.table.handle_irq_line(irq as usize, vector) {
log::warn!("plic: no handler for IRQ #{irq}");
}
@@ -242,7 +241,7 @@ impl Device for Plic {
context.context.init(Context {
enable: IrqSafeRwLock::new(enable),
control: IrqSafeRwLock::new(control),
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
table: FixedInterruptTable::new(MAX_IRQS), // table: IrqSafeRwLock::new(FixedInterruptTable::new()),
});
}
+7
View File
@@ -37,6 +37,9 @@ pub trait PortConfig: Sync + Send {
pub trait Access: Sized + Send + 'static {
type Config: Config;
/// # Safety
///
/// Unsafe: allows physical memory access.
unsafe fn map(config: &<Self::Config as Config>::Port) -> Result<Self, Error>;
fn read(&self, reg: u8) -> u8;
@@ -76,6 +79,10 @@ impl<A: Access> Uart8250<A> {
}
}
/// # Safety
///
/// Unsafe: the function is meant only to be called once, directly
/// interacts with the device state.
pub unsafe fn init_inner(self: &Arc<Self>) -> Result<(), Error> {
let ports = self.ports.iter();
let port_configs = self.config.ports().iter();
+1 -1
View File
@@ -109,7 +109,7 @@ impl<A: Access> Port8250<A> {
let regs = inner.output().regs.lock();
let iir = regs.read(REG_IIR) & IIR_MASK;
if iir == IIR_RXDA {
Some(regs.read(REG_RBR) as u8)
Some(regs.read(REG_RBR))
} else {
None
}
-45
View File
@@ -83,10 +83,6 @@ pub trait InterruptHandler: Device {
fn handle_irq(self: Arc<Self>, vector: IrqVector) -> bool;
}
pub trait InterruptTable: Sync {
fn handler(&self, index: usize) -> Option<&Arc<dyn InterruptHandler>>;
}
pub trait ExternalInterruptController: Device {
/// Performs IRQ delivery method configuration and registers a handler to execute when it is
/// fired
@@ -151,10 +147,6 @@ pub trait LocalInterruptController: Device {
fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error>;
}
pub struct FixedInterruptTable<const N: usize> {
rows: [Option<Arc<dyn InterruptHandler>>; N],
}
impl IrqHandle {
pub fn register(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
self.intc.register_irq(self.irq, self.options, handler)
@@ -165,43 +157,6 @@ impl IrqHandle {
}
}
impl<const N: usize> FixedInterruptTable<N> {
pub const fn new() -> Self {
Self {
rows: [const { None }; N],
}
}
pub fn insert(&mut self, index: usize, entry: Arc<dyn InterruptHandler>) -> Result<(), Error> {
let row = self.rows.get_mut(index).ok_or(Error::InvalidArgument)?;
if row.is_some() {
return Err(Error::AlreadyExists);
}
*row = Some(entry);
Ok(())
}
pub fn insert_least_loaded(
&mut self,
handler: Arc<dyn InterruptHandler>,
) -> Result<usize, Error> {
let index = self
.rows
.iter()
.position(|p| p.is_none())
.ok_or(Error::InvalidArgument)?;
self.rows[index].replace(handler);
Ok(index)
}
}
impl<const N: usize> InterruptTable for FixedInterruptTable<N> {
#[inline]
fn handler(&self, index: usize) -> Option<&Arc<dyn InterruptHandler>> {
self.rows.get(index).and_then(|p| p.as_ref())
}
}
impl IrqLevel {
pub fn override_default(self, value: IrqLevel) -> Self {
match self {
@@ -25,6 +25,7 @@ pub macro device_tree_driver(
static __REGISTER_FN: extern "C" fn() = __register_fn;
extern "C" fn __register_fn() {
#[allow(clippy::needless_update)]
let config = $crate::driver::DriverConfig {
$(
$($config_field: $config_value,)+
+11 -8
View File
@@ -107,14 +107,17 @@ impl Node {
.as_str_list()
.find_map(|c| drivers.iter().find(|d| d.matches(c)));
if libk::config::get().device_tree.log_missing && driver.is_none() {
// FIXME don't spam virtio missing stuff
if !name.is_some_and(|n| n.starts_with("virtio_mmio")) {
for (i, compatible) in compatible.as_str_list().enumerate() {
if i == 0 {
log::warn!("No driver for {name:?} ({compatible:?})");
} else {
log::warn!(" also {compatible:?}");
#[cfg(any(not(target_arch = "x86_64"), rust_analyzer))]
{
if libk::config::get().device_tree.log_missing && driver.is_none() {
// FIXME don't spam virtio missing stuff
if !name.is_some_and(|n| n.starts_with("virtio_mmio")) {
for (i, compatible) in compatible.as_str_list().enumerate() {
if i == 0 {
log::warn!("No driver for {name:?} ({compatible:?})");
} else {
log::warn!(" also {compatible:?}");
}
}
}
}
+44
View File
@@ -0,0 +1,44 @@
use alloc::{sync::Arc, vec::Vec};
use device_api::interrupt::{InterruptHandler, IrqVector};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
pub struct FixedInterruptTable {
rows: Vec<IrqSafeRwLock<Vec<Arc<dyn InterruptHandler>>>>,
}
impl FixedInterruptTable {
pub fn new(irq_lines: usize) -> Self {
Self {
rows: (0..irq_lines)
.map(|_| IrqSafeRwLock::new(Vec::new()))
.collect(),
}
}
pub fn insert(&self, line: usize, handler: Arc<dyn InterruptHandler>) {
let mut row = self.rows[line].write();
row.push(handler);
}
pub fn insert_least_loaded(&self, handler: Arc<dyn InterruptHandler>) -> usize {
let min_line = self
.rows
.iter()
.enumerate()
.min_by_key(|(_, row)| row.read().len())
.unwrap()
.0;
self.insert(min_line, handler);
min_line
}
pub fn handle_irq_line(&self, line: usize, vector: IrqVector) -> bool {
let row = self.rows[line].read();
for handler in row.iter() {
if handler.clone().handle_irq(vector) {
return true;
}
}
false
}
}
+1
View File
@@ -7,6 +7,7 @@ use yggdrasil_abi::error::Error;
pub mod block;
pub mod char;
pub mod display;
pub mod interrupt;
pub mod manager;
// TODO get rid of this, this does not work when there are multiple interrupt controllers
+15 -20
View File
@@ -8,9 +8,8 @@ use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
IpiDeliveryTarget, IpiMessage, Irq, IrqHandle, IrqLevel, IrqOptions, IrqTrigger, IrqVector,
LocalInterruptController,
ExternalInterruptController, InterruptHandler, IpiDeliveryTarget, IpiMessage, Irq,
IrqHandle, IrqLevel, IrqOptions, IrqTrigger, IrqVector, LocalInterruptController,
},
};
use device_tree::{
@@ -18,12 +17,15 @@ use device_tree::{
driver::{DeviceTreeInterruptController, Node, ProbeContext, device_tree_driver},
};
use kernel_arch_aarch64::{CPU_COUNT, GicInterface};
use libk::{arch::Cpu, device::register_external_interrupt_controller, task::cpu_index};
use libk::{
arch::Cpu,
device::{interrupt::FixedInterruptTable, register_external_interrupt_controller},
task::cpu_index,
};
use libk_mm::{
address::PhysicalAddress,
device::{DeviceMemoryIo, RawDeviceMemoryMapping},
};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use self::{gicc::Gicc, gicd::Gicd};
@@ -43,7 +45,7 @@ pub mod gicv2m;
pub struct Gic {
gicc: Gicc,
gicd: Gicd,
table: IrqSafeRwLock<FixedInterruptTable<MAX_IRQ>>,
table: FixedInterruptTable,
}
unsafe impl Sync for Gic {}
@@ -72,7 +74,7 @@ impl ExternalInterruptController for Gic {
options: IrqOptions,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let mut table = self.table.write();
// let mut table = self.table.write();
let index = match irq {
Irq::External(i) => i + GIC_SPI_START,
@@ -89,7 +91,7 @@ impl ExternalInterruptController for Gic {
if index >= GIC_SPI_START as usize {
self.gicd.configure_irq(index, options);
}
table.insert(index, handler)?;
self.table.insert(index, handler);
Ok(())
}
@@ -128,17 +130,9 @@ impl ExternalInterruptController for Gic {
Irq::External(irq_number as u32 - GIC_SPI_START)
};
{
let table = self.table.read();
let entry = match table.handler(irq_number) {
Some(handler) => handler.clone(),
None => {
log::warn!("No handler for irq{}", irq_number);
return;
}
};
entry.handle_irq(IrqVector::Irq(irq));
let vector = IrqVector::Irq(irq);
if !self.table.handle_irq_line(irq_number, vector) {
log::warn!("No handler for irq{irq_number}");
}
}
}
@@ -242,11 +236,12 @@ impl Gic {
// self.gicd.init(gicd);
// self.gicc.init(gicc);
let table = FixedInterruptTable::new(MAX_IRQ);
Ok(Self {
gicd,
gicc,
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
table, // table: IrqSafeRwLock::new(FixedInterruptTable::new()),
})
}
}
+9 -11
View File
@@ -5,13 +5,14 @@ use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
IrqLevel, IrqOptions, IrqTrigger, IrqVector,
ExternalInterruptController, InterruptHandler, Irq, IrqLevel, IrqOptions, IrqTrigger,
IrqVector,
},
};
use kernel_arch_x86::ISA_IRQ_OFFSET;
use libk::device::interrupt::FixedInterruptTable;
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock};
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
@@ -64,7 +65,7 @@ struct Inner {
pub struct IoApic {
inner: IrqSafeSpinlock<Inner>,
isa_redirections: [Option<IsaRedirection>; 16],
table: IrqSafeRwLock<FixedInterruptTable<{ POPULATED_EXTERNAL_VECTORS as usize }>>,
table: FixedInterruptTable,
}
impl Regs {
@@ -170,7 +171,8 @@ impl ExternalInterruptController for IoApic {
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let mut inner = self.inner.lock();
let table_vector = self.table.write().insert_least_loaded(handler.clone())?;
let table_vector = self.table.insert_least_loaded(handler.clone());
// let table_vector = self.table.write().insert_least_loaded(handler.clone())?;
let gsi_target_vector = (table_vector as u32) + IO_APIC_VECTOR_OFFSET;
let bsp_apic = *BSP_APIC_ID.get();
@@ -216,12 +218,8 @@ impl ExternalInterruptController for IoApic {
}
fn handle_specific_irq(&self, gsi: usize) {
let table = self.table.read();
let vector = IrqVector::Irq(Irq::External(gsi as u32));
if let Some(handler) = table.handler(gsi) {
handler.clone().handle_irq(vector);
} else {
if !self.table.handle_irq_line(gsi, vector) {
log::warn!("No handler set for GSI #{}", gsi);
}
}
@@ -295,7 +293,7 @@ impl IoApic {
Ok(Arc::new(Self {
isa_redirections,
inner: IrqSafeSpinlock::new(inner),
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
table: FixedInterruptTable::new(POPULATED_EXTERNAL_VECTORS as usize),
}))
}
+1 -1
View File
@@ -57,7 +57,7 @@ impl Device for I2CMux {
}
fn display_name(&self) -> &str {
&self.name
self.name
}
}
+1
View File
@@ -6,6 +6,7 @@ use libk_util::OneTimeInit;
pub mod bus;
pub mod clock;
pub mod display;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
pub mod i2c;
pub mod power;
// pub mod timer;