irq: more flexible interrupt tables
This commit is contained in:
@@ -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()),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,)+
|
||||
|
||||
@@ -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:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ impl Device for I2CMux {
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
&self.name
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user