use alloc::sync::Arc;
use axdevice::{DeviceManagerResult, IrqResolver};
use axdevice_base::{InterruptTriggerMode, IrqError, IrqLine, IrqLineId, IrqResult, IrqSink};
use axvm_types::VMInterruptMode;
use crate::{AxVmResult, ax_err};
#[ax_crate_interface::def_interface]
pub trait RiscvPlatformIrqInjectorIf {
fn register_virtual_irq_injector(injector: fn(usize) -> bool);
fn set_virtual_irq_targets(cpu_id: usize, irq_sources: &[u32]);
}
#[expect(
dead_code,
reason = "the RISC-V architecture backend is not compiled for this target"
)]
pub(crate) fn register_riscv_virtual_irq_injector(injector: fn(usize) -> bool) {
ax_crate_interface::call_interface!(RiscvPlatformIrqInjectorIf::register_virtual_irq_injector(
injector
));
}
#[expect(
dead_code,
reason = "the RISC-V architecture backend is not compiled for this target"
)]
pub(crate) fn set_riscv_virtual_irq_targets(cpu_id: usize, irq_sources: &[u32]) {
ax_crate_interface::call_interface!(RiscvPlatformIrqInjectorIf::set_virtual_irq_targets(
cpu_id,
irq_sources
));
}
pub struct InterruptFabric {
mode: VMInterruptMode,
sink: Option<Arc<dyn IrqSink>>,
}
impl InterruptFabric {
pub const fn new(mode: VMInterruptMode) -> Self {
Self { mode, sink: None }
}
pub fn with_sink(mode: VMInterruptMode, sink: Arc<dyn IrqSink>) -> AxVmResult<Self> {
if mode == VMInterruptMode::NoIrq {
return ax_err!(
InvalidInput,
"a VM configured with interrupt_mode=no_irq cannot install an IRQ backend"
);
}
Ok(Self {
mode,
sink: Some(sink),
})
}
pub const fn mode(&self) -> VMInterruptMode {
self.mode
}
pub const fn has_backend(&self) -> bool {
self.sink.is_some()
}
fn sink_for_line(&self, line: usize) -> IrqResult<&Arc<dyn IrqSink>> {
let Some(sink) = &self.sink else {
if self.mode == VMInterruptMode::NoIrq {
return Err(IrqError::InvalidLine {
line: IrqLineId(line),
operation: "resolve",
detail: "the VM is configured without interrupt delivery".into(),
});
}
return Err(IrqError::Unsupported {
line: IrqLineId(line),
operation: "resolve",
detail: "no interrupt backend is installed".into(),
});
};
Ok(sink)
}
pub fn set_level(&self, line: usize, asserted: bool) -> AxVmResult {
self.sink_for_line(line)?
.set_level(IrqLineId(line), asserted)?;
Ok(())
}
pub fn pulse(&self, line: usize) -> AxVmResult {
self.sink_for_line(line)?.pulse(IrqLineId(line))?;
Ok(())
}
pub(crate) fn validate_mode(&self, mode: VMInterruptMode) -> AxVmResult {
if self.mode != mode {
return ax_err!(
InvalidInput,
format_args!(
"interrupt fabric mode {:?} does not match VM interrupt mode {:?}",
self.mode, mode
)
);
}
Ok(())
}
}
impl Default for InterruptFabric {
fn default() -> Self {
Self::new(VMInterruptMode::NoIrq)
}
}
impl IrqResolver for InterruptFabric {
fn resolve_irq(
&self,
line: usize,
trigger: InterruptTriggerMode,
) -> DeviceManagerResult<IrqLine> {
Ok(IrqLine::new(
IrqLineId(line),
trigger,
self.sink_for_line(line)?.clone(),
))
}
}
pub(crate) mod model;