use kvm_bindings::KVM_IRQ_ROUTING_IRQCHIP;
#[cfg(target_arch = "x86_64")]
use kvm_bindings::{KVM_IRQCHIP_IOAPIC, KVM_IRQCHIP_PIC_MASTER, KVM_IRQCHIP_PIC_SLAVE};
use vmm_sys_util::eventfd::EFD_NONBLOCK;
use super::*;
#[cfg(target_arch = "x86_64")]
pub const MAX_LEGACY_IRQS: u32 = 24;
#[cfg(target_arch = "aarch64")]
pub const MAX_LEGACY_IRQS: u32 = 128;
pub(super) struct LegacyIrq {
base: u32,
vmfd: Arc<VmFd>,
irqfd: EventFd,
}
impl LegacyIrq {
pub(super) fn new(
base: InterruptIndex,
count: InterruptIndex,
vmfd: Arc<VmFd>,
_routes: Arc<KvmIrqRouting>,
) -> Result<Self> {
if count != 1 {
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
}
if base >= MAX_LEGACY_IRQS {
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
}
Ok(LegacyIrq {
base,
vmfd,
irqfd: EventFd::new(EFD_NONBLOCK)?,
})
}
#[cfg(target_arch = "x86_64")]
fn add_legacy_entry(
gsi: u32,
chip: u32,
pin: u32,
routes: &mut HashMap<u64, kvm_irq_routing_entry>,
) -> Result<()> {
let mut entry = kvm_irq_routing_entry {
gsi,
type_: KVM_IRQ_ROUTING_IRQCHIP,
..Default::default()
};
entry.u.irqchip.irqchip = chip;
entry.u.irqchip.pin = pin;
routes.insert(hash_key(&entry), entry);
Ok(())
}
#[cfg(target_arch = "x86_64")]
pub(super) fn initialize_legacy(
routes: &mut HashMap<u64, kvm_irq_routing_entry>,
) -> Result<()> {
for i in 0..8 {
if i != 2 {
Self::add_legacy_entry(i, KVM_IRQCHIP_PIC_MASTER, i, routes)?;
}
}
for i in 8..16 {
Self::add_legacy_entry(i, KVM_IRQCHIP_PIC_SLAVE, i - 8, routes)?;
}
for i in 0..MAX_LEGACY_IRQS {
if i == 0 {
Self::add_legacy_entry(i, KVM_IRQCHIP_IOAPIC, 2, routes)?;
} else if i != 2 {
Self::add_legacy_entry(i, KVM_IRQCHIP_IOAPIC, i, routes)?;
};
}
Ok(())
}
#[cfg(target_arch = "aarch64")]
pub(super) fn initialize_legacy(
routes: &mut HashMap<u64, kvm_irq_routing_entry>,
) -> Result<()> {
for i in 0..MAX_LEGACY_IRQS {
let mut entry = kvm_irq_routing_entry {
gsi: i,
type_: KVM_IRQ_ROUTING_IRQCHIP,
..Default::default()
};
entry.u.irqchip.irqchip = 0;
entry.u.irqchip.pin = i;
routes.insert(hash_key(&entry), entry);
}
Ok(())
}
}
impl InterruptSourceGroup for LegacyIrq {
fn interrupt_type(&self) -> InterruptSourceType {
InterruptSourceType::LegacyIrq
}
fn len(&self) -> u32 {
1
}
fn base(&self) -> u32 {
self.base
}
fn enable(&self, configs: &[InterruptSourceConfig]) -> Result<()> {
if configs.len() != 1 {
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
}
self.vmfd
.register_irqfd(&self.irqfd, self.base)
.map_err(from_sys_util_errno)
}
fn disable(&self) -> Result<()> {
self.vmfd
.unregister_irqfd(&self.irqfd, self.base)
.map_err(from_sys_util_errno)
}
fn update(&self, index: InterruptIndex, _config: &InterruptSourceConfig) -> Result<()> {
if index != 0 {
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
}
Ok(())
}
fn notifier(&self, index: InterruptIndex) -> Option<&EventFd> {
if index != 0 {
None
} else {
Some(&self.irqfd)
}
}
fn trigger(&self, index: InterruptIndex) -> Result<()> {
if index != 0 {
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
}
self.irqfd.write(1)
}
fn mask(&self, index: InterruptIndex) -> Result<()> {
if index > 1 {
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
}
self.vmfd
.unregister_irqfd(&self.irqfd, self.base + index)
.map_err(from_sys_util_errno)?;
Ok(())
}
fn unmask(&self, index: InterruptIndex) -> Result<()> {
if index > 1 {
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
}
self.vmfd
.register_irqfd(&self.irqfd, self.base + index)
.map_err(from_sys_util_errno)?;
Ok(())
}
fn get_pending_state(&self, index: InterruptIndex) -> bool {
if index > 1 {
return false;
}
match self.irqfd.read() {
Err(_) => false,
Ok(count) => {
if count != 0 && self.irqfd.write(count).is_err() {
}
count != 0
}
}
}
}
#[cfg(test)]
#[cfg(target_arch = "x86_64")]
mod test {
use super::*;
use crate::manager::tests::create_vm_fd;
const MASTER_PIC: usize = 7;
const SLAVE_PIC: usize = 8;
const IOAPIC: usize = 23;
#[test]
#[allow(unreachable_patterns)]
fn test_legacy_interrupt_group() {
let vmfd = Arc::new(create_vm_fd());
let rounting = Arc::new(KvmIrqRouting::new(vmfd.clone()));
let base = 0;
let count = 1;
let group = LegacyIrq::new(base, count, vmfd.clone(), rounting.clone()).unwrap();
let legacy_fds = vec![InterruptSourceConfig::LegacyIrq(LegacyIrqSourceConfig {})];
match group.interrupt_type() {
InterruptSourceType::LegacyIrq => {}
_ => {
panic!();
}
}
vmfd.create_irq_chip().unwrap();
assert_eq!(group.len(), 1);
assert_eq!(group.base(), base);
group.enable(&legacy_fds).unwrap();
group.notifier(0).unwrap().write(1).unwrap();
group.trigger(0).unwrap();
assert!(group.trigger(1).is_err());
group
.update(
0,
&InterruptSourceConfig::LegacyIrq(LegacyIrqSourceConfig {}),
)
.unwrap();
group.disable().unwrap();
assert!(LegacyIrq::new(base, 2, vmfd.clone(), rounting.clone()).is_err());
assert!(LegacyIrq::new(110, 1, vmfd, rounting).is_err());
}
#[test]
fn test_irq_routing_initialize_legacy() {
let vmfd = Arc::new(create_vm_fd());
let routing = KvmIrqRouting::new(vmfd.clone());
assert!(routing.initialize().is_err());
vmfd.create_irq_chip().unwrap();
routing.initialize().unwrap();
let routes = &routing.routes.lock().unwrap();
assert_eq!(routes.len(), MASTER_PIC + SLAVE_PIC + IOAPIC);
}
#[test]
fn test_routing_opt() {
let vmfd = Arc::new(create_vm_fd());
let routing = KvmIrqRouting::new(vmfd.clone());
assert!(routing.initialize().is_err());
vmfd.create_irq_chip().unwrap();
routing.initialize().unwrap();
let mut entry = kvm_irq_routing_entry {
gsi: 8,
type_: kvm_bindings::KVM_IRQ_ROUTING_IRQCHIP,
..Default::default()
};
entry.u.irqchip.irqchip = 0;
entry.u.irqchip.pin = 3;
let entrys = vec![entry];
assert!(routing.modify(&entry).is_err());
routing.add(&entrys).unwrap();
entry.u.irqchip.pin = 4;
routing.modify(&entry).unwrap();
routing.remove(&entrys).unwrap();
assert!(routing.modify(&entry).is_err());
}
#[test]
fn test_routing_set_routing() {
let vmfd = Arc::new(create_vm_fd());
let routing = KvmIrqRouting::new(vmfd.clone());
assert!(routing.initialize().is_err());
vmfd.create_irq_chip().unwrap();
routing.initialize().unwrap();
let mut entry = kvm_irq_routing_entry {
gsi: 8,
type_: kvm_bindings::KVM_IRQ_ROUTING_IRQCHIP,
..Default::default()
};
entry.u.irqchip.irqchip = 0;
entry.u.irqchip.pin = 3;
routing
.routes
.lock()
.unwrap()
.insert(hash_key(&entry), entry);
let routes = routing.routes.lock().unwrap();
routing.set_routing(&routes).unwrap();
}
#[test]
fn test_has_key() {
let gsi = 4;
let mut entry = kvm_irq_routing_entry {
gsi,
type_: kvm_bindings::KVM_IRQ_ROUTING_IRQCHIP,
..Default::default()
};
entry.u.irqchip.irqchip = kvm_bindings::KVM_IRQCHIP_PIC_MASTER;
entry.u.irqchip.pin = gsi;
assert_eq!(hash_key(&entry), 0x0001_0000_0004);
}
}