pub use x86defs::apic::DeliveryMode;
use hvdef::HvInterruptType;
use hvdef::Vtl;
use inspect::Inspect;
use parking_lot::Mutex;
use std::fmt::Debug;
use std::sync::Arc;
use vm_topology::processor::VpIndex;
use vmcore::line_interrupt::LineSetTarget;
use x86defs::msi::MsiAddress;
use x86defs::msi::MsiData;
pub trait IoApicRouting: Send + Sync {
fn set_irq_route(&self, irq: u8, request: Option<MsiRequest>);
fn assert_irq(&self, irq: u8);
}
pub trait ControlGic: Send + Sync {
fn set_spi_irq(&self, irq_id: u32, high: bool);
}
pub const IRQ_LINES: usize = 24;
#[derive(Debug, Copy, Clone, Inspect)]
pub struct MsiRequest {
#[inspect(hex)]
pub address: u64,
#[inspect(hex)]
pub data: u32,
}
impl MsiRequest {
pub fn new_x86(
mode: DeliveryMode,
destination: u32,
is_logical_destination: bool,
vector: u8,
is_level_triggered: bool,
) -> Self {
let address = MsiAddress::new()
.with_address(x86defs::msi::MSI_ADDRESS)
.with_redirection_hint(mode == DeliveryMode::LOWEST_PRIORITY)
.with_virt_destination(destination as u16)
.with_destination_mode_logical(is_logical_destination);
let data = MsiData::new()
.with_vector(vector)
.with_delivery_mode(mode.0 & 0x7)
.with_assert(is_level_triggered)
.with_trigger_mode_level(is_level_triggered);
Self {
address: u32::from(address).into(),
data: data.into(),
}
}
pub fn as_x86(&self) -> (MsiAddress, MsiData) {
(
MsiAddress::from(self.address as u32),
MsiData::from(self.data),
)
}
pub fn hv_x86_interrupt_control(&self) -> hvdef::HvInterruptControl {
let (address, data) = self.as_x86();
let ty = match DeliveryMode(data.delivery_mode()) {
DeliveryMode::FIXED => HvInterruptType::HvX64InterruptTypeFixed,
DeliveryMode::LOWEST_PRIORITY => HvInterruptType::HvX64InterruptTypeLowestPriority,
DeliveryMode::SMI => HvInterruptType::HvX64InterruptTypeSmi,
DeliveryMode::REMOTE_READ => HvInterruptType::HvX64InterruptTypeRemoteRead,
DeliveryMode::NMI => HvInterruptType::HvX64InterruptTypeNmi,
DeliveryMode::INIT => HvInterruptType::HvX64InterruptTypeInit,
DeliveryMode::SIPI => HvInterruptType::HvX64InterruptTypeSipi,
_ => HvInterruptType(!0),
};
hvdef::HvInterruptControl::new()
.with_interrupt_type(ty)
.with_x86_level_triggered(data.trigger_mode_level())
.with_x86_logical_destination_mode(address.destination_mode_logical())
}
}
#[derive(Debug, Inspect)]
pub struct IrqRoutes {
#[inspect(
with = "|x| inspect::adhoc(|req| inspect::iter_by_index(x.lock().iter()).inspect(req))"
)]
routes: Mutex<Vec<Option<MsiRequest>>>,
}
impl Default for IrqRoutes {
fn default() -> Self {
Self::new()
}
}
impl IrqRoutes {
pub fn new() -> Self {
let routes = vec![None; IRQ_LINES];
Self {
routes: Mutex::new(routes),
}
}
pub fn set_irq_route(&self, irq: u8, request: Option<MsiRequest>) {
self.routes.lock()[irq as usize] = request;
}
pub fn assert_irq(&self, irq: u8, assert: impl FnOnce(MsiRequest)) {
let request = self.routes.lock()[irq as usize];
match request {
Some(request) => {
assert(request);
}
None => {
tracelimit::warn_ratelimited!(irq, "irq for masked interrupt");
}
}
}
}
pub struct ApicLintLineTarget<T> {
partition: Arc<T>,
vtl: Vtl,
}
impl<T: crate::X86Partition> ApicLintLineTarget<T> {
pub fn new(partition: Arc<T>, vtl: Vtl) -> Self {
Self { partition, vtl }
}
}
impl<T: crate::X86Partition> LineSetTarget for ApicLintLineTarget<T> {
fn set_irq(&self, vector: u32, high: bool) {
if !high {
return;
}
let vp_index = VpIndex::new(vector / 2);
let lint = vector % 2;
self.partition.pulse_lint(vp_index, self.vtl, lint as u8);
}
}