diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs index 3dee94cf08..be1feecdd0 100644 --- a/kvm/src/lib.rs +++ b/kvm/src/lib.rs @@ -14,6 +14,7 @@ mod cap; use std::fs::File; use std::collections::{BinaryHeap, HashMap}; use std::collections::hash_map::Entry; +use std::mem::size_of; use std::os::raw::*; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; @@ -168,6 +169,18 @@ impl Into for NoDatamatch { } } +/// A source of IRQs in an `IrqRoute`. +pub enum IrqSource { + Irqchip { chip: u32, pin: u32 }, + Msi { address: u64, data: u32 }, +} + +/// A single route for an IRQ. +pub struct IrqRoute { + pub gsi: u32, + pub source: IrqSource, +} + /// A wrapper around creating and using a VM. pub struct Vm { vm: File, @@ -473,6 +486,55 @@ impl Vm { errno_result() } } + + /// Sets the GSI routing table, replacing any table set with previous calls to + /// `set_gsi_routing`. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_gsi_routing(&self, routes: &[IrqRoute]) -> Result<()> { + let vec_size_bytes = size_of::() + + (routes.len() * size_of::()); + let bytes: Vec = vec![0; vec_size_bytes]; + let irq_routing: &mut kvm_irq_routing = unsafe { + // We have ensured in new that there is enough space for the structure so this + // conversion is safe. + &mut *(bytes.as_ptr() as *mut kvm_irq_routing) + }; + irq_routing.nr = routes.len() as u32; + + { + // Safe because we ensured there is enough space in irq_routing to hold the number of + // route entries. + let irq_routes = unsafe { irq_routing.entries.as_mut_slice(routes.len()) }; + for (route, irq_route) in routes.iter().zip(irq_routes.iter_mut()) { + irq_route.gsi = route.gsi; + match route.source { + IrqSource::Irqchip { chip, pin } => { + irq_route.type_ = KVM_IRQ_ROUTING_IRQCHIP; + irq_route.u.irqchip = kvm_irq_routing_irqchip { + irqchip: chip, + pin, + } + } + IrqSource::Msi { address, data } => { + irq_route.type_ = KVM_IRQ_ROUTING_MSI; + irq_route.u.msi = kvm_irq_routing_msi { + address_lo: address as u32, + address_hi: (address >> 32) as u32, + data: data, + ..Default::default() + } + } + } + } + } + + let ret = unsafe { ioctl_with_ref(self, KVM_SET_GSI_ROUTING(), irq_routing) }; + if ret == 0 { + Ok(()) + } else { + errno_result() + } + } } impl AsRawFd for Vm { @@ -981,6 +1043,44 @@ mod tests { vm.unregister_irqfd(&evtfd3, 4).unwrap(); } + #[test] + fn set_gsi_routing() { + let kvm = Kvm::new().unwrap(); + let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap(); + let vm = Vm::new(&kvm, gm).unwrap(); + vm.set_gsi_routing(&[]).unwrap(); + vm.set_gsi_routing(&[IrqRoute { + gsi: 1, + source: IrqSource::Irqchip { + chip: KVM_IRQCHIP_IOAPIC, + pin: 3, + }, + }]).unwrap(); + vm.set_gsi_routing(&[IrqRoute { + gsi: 1, + source: IrqSource::Msi { + address: 0xf000000, + data: 0xa0, + }, + }]).unwrap(); + vm.set_gsi_routing(&[ + IrqRoute { + gsi: 1, + source: IrqSource::Irqchip { + chip: KVM_IRQCHIP_IOAPIC, + pin: 3, + }, + }, + IrqRoute { + gsi: 2, + source: IrqSource::Msi { + address: 0xf000000, + data: 0xa0, + }, + }, + ]).unwrap(); + } + #[test] fn create_vcpu() { let kvm = Kvm::new().unwrap(); diff --git a/kvm_sys/src/lib.rs b/kvm_sys/src/lib.rs index 58269c7ed0..0d6f8491b3 100644 --- a/kvm_sys/src/lib.rs +++ b/kvm_sys/src/lib.rs @@ -20,6 +20,7 @@ pub mod x86 { pub mod bindings; pub use bindings::*; + ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x02, kvm_msr_list); ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); ioctl_iowr_nr!(KVM_GET_EMULATED_CPUID, KVMIO, 0x09, kvm_cpuid2);