summary refs log tree commit diff
diff options
context:
space:
mode:
authorColin Downs-Razouk <colindr@google.com>2020-04-30 15:48:47 -0700
committerCommit Bot <commit-bot@chromium.org>2020-05-18 23:56:23 +0000
commit43b1bc8f8ff7ff04b054374538ecad4a4525d283 (patch)
tree221f4deb7e88cf746f8af1c091660657ce48406f
parent73665866a047c4602ab25ae6e20ce1f98f1f8e6e (diff)
downloadcrosvm-43b1bc8f8ff7ff04b054374538ecad4a4525d283.tar
crosvm-43b1bc8f8ff7ff04b054374538ecad4a4525d283.tar.gz
crosvm-43b1bc8f8ff7ff04b054374538ecad4a4525d283.tar.bz2
crosvm-43b1bc8f8ff7ff04b054374538ecad4a4525d283.tar.lz
crosvm-43b1bc8f8ff7ff04b054374538ecad4a4525d283.tar.xz
crosvm-43b1bc8f8ff7ff04b054374538ecad4a4525d283.tar.zst
crosvm-43b1bc8f8ff7ff04b054374538ecad4a4525d283.zip
devices: irqchip: new irqchip module
This new module contains the irqchip trait and it's implementations. The
irqchips will work with the new hypervisor crate to abstract the
interaction between crosvm and kvm.

This just defines the irqchip trait and an empty implementation of the
KvmKernelIrqChip.

BUG=chromium:1077058
TEST=added test for creating a KvmKernelIrqChip and adding a Vcpu to it

Change-Id: Ic1609c965e0a057f5a9d4d74f1cae46edb46dcb4
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2197398
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Stephen Barber <smbarber@chromium.org>
Commit-Queue: Colin Downs-Razouk <colindr@google.com>
-rw-r--r--Cargo.lock10
-rw-r--r--devices/Cargo.toml1
-rw-r--r--devices/src/irqchip/kvm/mod.rs122
-rw-r--r--devices/src/irqchip/mod.rs64
-rw-r--r--devices/src/lib.rs2
-rw-r--r--hypervisor/src/aarch64.rs2
-rw-r--r--hypervisor/src/kvm/aarch64.rs2
-rw-r--r--hypervisor/src/lib.rs2
8 files changed, 203 insertions, 2 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 88a1881..46e186f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -177,6 +177,7 @@ dependencies = [
  "gpu_buffer 0.1.0",
  "gpu_display 0.1.0",
  "gpu_renderer 0.1.0",
+ "hypervisor 0.1.0",
  "io_jail 0.1.0",
  "kvm 0.1.0",
  "kvm_sys 0.1.0",
@@ -343,6 +344,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "hypervisor"
+version = "0.1.0"
+dependencies = [
+ "kvm_sys 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
+]
+
+[[package]]
 name = "io_jail"
 version = "0.1.0"
 dependencies = [
diff --git a/devices/Cargo.toml b/devices/Cargo.toml
index 0ffab50..931ea36 100644
--- a/devices/Cargo.toml
+++ b/devices/Cargo.toml
@@ -22,6 +22,7 @@ enumn = { path = "../enumn" }
 gpu_buffer = { path = "../gpu_buffer", optional = true }
 gpu_display = { path = "../gpu_display", optional = true }
 gpu_renderer = { path = "../gpu_renderer", optional = true }
+hypervisor = { path = "../hypervisor" }
 io_jail = { path = "../io_jail" }
 kvm = { path = "../kvm" }
 kvm_sys = { path = "../kvm_sys" }
diff --git a/devices/src/irqchip/kvm/mod.rs b/devices/src/irqchip/kvm/mod.rs
new file mode 100644
index 0000000..56794a3
--- /dev/null
+++ b/devices/src/irqchip/kvm/mod.rs
@@ -0,0 +1,122 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use hypervisor::kvm::{KvmVcpu, KvmVm};
+use hypervisor::IrqRoute;
+use std::sync::Arc;
+use sync::Mutex;
+use sys_util::{EventFd, Result};
+
+use crate::IrqChip;
+
+/// IrqChip implementation where the entire IrqChip is emulated by KVM.
+///
+/// This implementation will use the KVM API to create and configure the in-kernel irqchip.
+pub struct KvmKernelIrqChip {
+    _vm: KvmVm,
+    vcpus: Arc<Mutex<Vec<Option<KvmVcpu>>>>,
+}
+
+impl KvmKernelIrqChip {
+    /// Construct a new KvmKernelIrqchip.
+    pub fn new(vm: KvmVm, num_vcpus: usize) -> Result<KvmKernelIrqChip> {
+        Ok(KvmKernelIrqChip {
+            _vm: vm,
+            vcpus: Arc::new(Mutex::new((0..num_vcpus).map(|_| None).collect())),
+        })
+    }
+}
+
+/// This IrqChip only works with Kvm so we only implement it for KvmVcpu.
+impl IrqChip<KvmVcpu> for KvmKernelIrqChip {
+    /// Add a vcpu to the irq chip.
+    fn add_vcpu(&mut self, vcpu_id: usize, vcpu: KvmVcpu) -> Result<()> {
+        self.vcpus.lock().insert(vcpu_id, Some(vcpu));
+        Ok(())
+    }
+
+    /// Register an event that can trigger an interrupt for a particular GSI.
+    fn register_irq_event(
+        &mut self,
+        _irq: u32,
+        _irq_event: &EventFd,
+        _resample_event: Option<&EventFd>,
+    ) -> Result<()> {
+        unimplemented!("register_irq_event for KvmKernelIrqChip is not yet implemented");
+    }
+
+    /// Unregister an event for a particular GSI.
+    fn unregister_irq_event(
+        &mut self,
+        _irq: u32,
+        _irq_event: &EventFd,
+        _resample_event: Option<&EventFd>,
+    ) -> Result<()> {
+        unimplemented!("unregister_irq_event for KvmKernelIrqChip is not yet implemented");
+    }
+
+    /// Route an IRQ line to an interrupt controller, or to a particular MSI vector.
+    fn route_irq(&mut self, _route: IrqRoute) -> Result<()> {
+        unimplemented!("route_irq for KvmKernelIrqChip is not yet implemented");
+    }
+
+    /// Return a vector of all registered irq numbers and their associated events.  To be used by
+    /// the main thread to wait for irq events to be triggered.
+    fn irq_event_tokens(&self) -> Result<Vec<(u32, EventFd)>> {
+        unimplemented!("irq_event_tokens for KvmKernelIrqChip is not yet implemented");
+    }
+
+    /// Either assert or deassert an IRQ line.  Sends to either an interrupt controller, or does
+    /// a send_msi if the irq is associated with an MSI.
+    fn service_irq(&mut self, _irq: u32, _level: bool) -> Result<()> {
+        unimplemented!("service_irq for KvmKernelIrqChip is not yet implemented");
+    }
+
+    /// Broadcast an end of interrupt.
+    fn broadcast_eoi(&mut self, _vector: u8) -> Result<()> {
+        unimplemented!("broadcast_eoi for KvmKernelIrqChip is not yet implemented");
+    }
+
+    /// Return true if there is a pending interrupt for the specified vcpu.
+    fn interrupt_requested(&self, _vcpu_id: usize) -> bool {
+        unimplemented!("interrupt_requested for KvmKernelIrqChip is not yet implemented");
+    }
+
+    /// Check if the specified vcpu has any pending interrupts. Returns None for no interrupts,
+    /// otherwise Some(u32) should be the injected interrupt vector.
+    fn get_external_interrupt(&mut self, _vcpu_id: usize) -> Result<Option<u32>> {
+        unimplemented!("get_external_interrupt for KvmKernelIrqChip is not yet implemented");
+    }
+
+    /// Attempt to clone this IrqChip instance.
+    fn try_clone(&self) -> Result<Self> {
+        unimplemented!("try_clone for KvmKernelIrqChip is not yet implemented");
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use hypervisor::kvm::{Kvm, KvmVm};
+    use sys_util::GuestMemory;
+
+    use crate::irqchip::{IrqChip, KvmKernelIrqChip};
+    #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+    use hypervisor::VmAarch64;
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    use hypervisor::VmX86_64;
+
+    #[test]
+    fn create_kvm_kernel_irqchip() {
+        let kvm = Kvm::new().expect("failed to instantiate Kvm");
+        let mem = GuestMemory::new(&[]).unwrap();
+        let vm = KvmVm::new(&kvm, mem).expect("failed to instantiate vm");
+        let vcpu = vm.create_vcpu(0).expect("failed to instantiate vcpu");
+
+        let mut chip =
+            KvmKernelIrqChip::new(vm, 1).expect("failed to instantiate KvmKernelIrqChip");
+
+        chip.add_vcpu(0, vcpu).expect("failed to add vcpu");
+    }
+}
diff --git a/devices/src/irqchip/mod.rs b/devices/src/irqchip/mod.rs
new file mode 100644
index 0000000..a8023e9
--- /dev/null
+++ b/devices/src/irqchip/mod.rs
@@ -0,0 +1,64 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+mod kvm;
+pub use self::kvm::KvmKernelIrqChip;
+
+use std::marker::{Send, Sized};
+
+use hypervisor::{IrqRoute, Vcpu};
+use sys_util::{EventFd, Result};
+
+/// Trait that abstracts interactions with interrupt controllers.
+///
+/// Each VM will have one IrqChip instance which is responsible for routing IRQ lines and
+/// registering IRQ events. Depending on the implementation, the IrqChip may interact with an
+/// underlying hypervisor API or emulate devices in userspace.
+///
+/// This trait is generic over a Vcpu type because some IrqChip implementations can support
+/// multiple hypervisors with a single implementation.
+pub trait IrqChip<V: Vcpu>: Send + Sized {
+    /// Add a vcpu to the irq chip.
+    fn add_vcpu(&mut self, vcpu_id: usize, vcpu: V) -> Result<()>;
+
+    /// Register an event that can trigger an interrupt for a particular GSI.
+    fn register_irq_event(
+        &mut self,
+        irq: u32,
+        irq_event: &EventFd,
+        resample_event: Option<&EventFd>,
+    ) -> Result<()>;
+
+    /// Unregister an event for a particular GSI.
+    fn unregister_irq_event(
+        &mut self,
+        irq: u32,
+        irq_event: &EventFd,
+        resample_event: Option<&EventFd>,
+    ) -> Result<()>;
+
+    /// Route an IRQ line to an interrupt controller, or to a particular MSI vector.
+    fn route_irq(&mut self, route: IrqRoute) -> Result<()>;
+
+    /// Return a vector of all registered irq numbers and their associated events.  To be used by
+    /// the main thread to wait for irq events to be triggered.
+    fn irq_event_tokens(&self) -> Result<Vec<(u32, EventFd)>>;
+
+    /// Either assert or deassert an IRQ line.  Sends to either an interrupt controller, or does
+    /// a send_msi if the irq is associated with an MSI.
+    fn service_irq(&mut self, irq: u32, level: bool) -> Result<()>;
+
+    /// Broadcast an end of interrupt.
+    fn broadcast_eoi(&mut self, vector: u8) -> Result<()>;
+
+    /// Return true if there is a pending interrupt for the specified vcpu.
+    fn interrupt_requested(&self, vcpu_id: usize) -> bool;
+
+    /// Check if the specified vcpu has any pending interrupts. Returns None for no interrupts,
+    /// otherwise Some(u32) should be the injected interrupt vector.
+    fn get_external_interrupt(&mut self, vcpu_id: usize) -> Result<Option<u32>>;
+
+    /// Attempt to clone this IrqChip instance.
+    fn try_clone(&self) -> Result<Self>;
+}
diff --git a/devices/src/lib.rs b/devices/src/lib.rs
index 01c2b46..c945e00 100644
--- a/devices/src/lib.rs
+++ b/devices/src/lib.rs
@@ -8,6 +8,7 @@ mod bus;
 mod cmos;
 mod i8042;
 mod ioapic;
+mod irqchip;
 mod pci;
 mod pic;
 mod pit;
@@ -30,6 +31,7 @@ pub use self::bus::{Bus, BusDevice, BusRange, BusResumeDevice};
 pub use self::cmos::Cmos;
 pub use self::i8042::I8042Device;
 pub use self::ioapic::{Ioapic, IOAPIC_BASE_ADDRESS, IOAPIC_MEM_LENGTH_BYTES};
+pub use self::irqchip::*;
 pub use self::pci::{
     Ac97Backend, Ac97Dev, Ac97Parameters, PciAddress, PciConfigIo, PciConfigMmio, PciDevice,
     PciDeviceError, PciInterruptPin, PciRoot, VfioPciDevice,
diff --git a/hypervisor/src/aarch64.rs b/hypervisor/src/aarch64.rs
index dc06d8d..875941b 100644
--- a/hypervisor/src/aarch64.rs
+++ b/hypervisor/src/aarch64.rs
@@ -7,7 +7,7 @@ use sys_util::Result;
 
 /// A wrapper for using a VM on aarch64 and getting/setting its state.
 pub trait VmAArch64: Vm {
-    type Vcpu: VcpuArm;
+    type Vcpu: VcpuAArch64;
 
     /// Create a Vcpu with the specified Vcpu ID.
     fn create_vcpu(&self, id: usize) -> Result<Self::Vcpu>;
diff --git a/hypervisor/src/kvm/aarch64.rs b/hypervisor/src/kvm/aarch64.rs
index f674671..6e9f9f7 100644
--- a/hypervisor/src/kvm/aarch64.rs
+++ b/hypervisor/src/kvm/aarch64.rs
@@ -5,7 +5,7 @@
 use sys_util::Result;
 
 use super::{KvmVcpu, KvmVm};
-use crate::{Regs, VcpuAArch64, VmAarch64};
+use crate::{VcpuAArch64, VmAArch64};
 
 impl VmAArch64 for KvmVm {
     type Vcpu = KvmVcpu;
diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs
index 0f4c278..f17528b 100644
--- a/hypervisor/src/lib.rs
+++ b/hypervisor/src/lib.rs
@@ -63,3 +63,5 @@ pub trait RunnableVcpu: Deref<Target = <Self as RunnableVcpu>::Vcpu> + DerefMut
 pub enum VcpuExit {
     Unknown,
 }
+
+pub struct IrqRoute {}