kvm: add set_irq_routing method

This is used by the plugin process API, which may register an IRQ
routing table.

TEST=./build_test
BUG=chromium:800626

Change-Id: If40965e8abfb0c9074c90b5fc77f9042f06499e0
Reviewed-on: https://chromium-review.googlesource.com/857910
Commit-Ready: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
Zach Reizner 2018-01-09 10:41:10 -08:00 committed by chrome-bot
parent 0ba70d8d3c
commit 086922c222
2 changed files with 101 additions and 0 deletions

View file

@ -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<u64> 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::<kvm_irq_routing>() +
(routes.len() * size_of::<kvm_irq_routing_entry>());
let bytes: Vec<u8> = 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();

View file

@ -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);