summary refs log tree commit diff
path: root/devices
diff options
context:
space:
mode:
authorZhuocheng Ding <zhuocheng.ding@intel.corp-partner.google.com>2019-12-02 15:50:28 +0800
committerCommit Bot <commit-bot@chromium.org>2020-03-05 13:12:23 +0000
commitb9f4c9bca30e65eacfb055951fa994ad5127a8f0 (patch)
tree8c8c886824f819620cf6d5c8b39ee4571ed7fb9b /devices
parent2f7dabbd6a0d8620e4b19b92cdae24c08e4c7ccc (diff)
downloadcrosvm-b9f4c9bca30e65eacfb055951fa994ad5127a8f0.tar
crosvm-b9f4c9bca30e65eacfb055951fa994ad5127a8f0.tar.gz
crosvm-b9f4c9bca30e65eacfb055951fa994ad5127a8f0.tar.bz2
crosvm-b9f4c9bca30e65eacfb055951fa994ad5127a8f0.tar.lz
crosvm-b9f4c9bca30e65eacfb055951fa994ad5127a8f0.tar.xz
crosvm-b9f4c9bca30e65eacfb055951fa994ad5127a8f0.tar.zst
crosvm-b9f4c9bca30e65eacfb055951fa994ad5127a8f0.zip
crosvm: Add plumbing for split-irqchip interrupts
Devices use irqfd to inject interrupts, we listen to them in the main
thread and activate userspace pic/ioapic accordingly.

BUG=chromium:908689
TEST=lanuch linux guest with `--split-irqchip` flag

Change-Id: If30d17ce7ec9e26dba782c89cc1b9b2ff897a70d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1945798
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Stephen Barber <smbarber@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Zhuocheng Ding <zhuocheng.ding@intel.corp-partner.google.com>
Diffstat (limited to 'devices')
-rw-r--r--devices/src/ioapic.rs13
-rw-r--r--devices/src/pic.rs37
-rw-r--r--devices/src/split_irqchip_common.rs34
3 files changed, 63 insertions, 21 deletions
diff --git a/devices/src/ioapic.rs b/devices/src/ioapic.rs
index e1fbfb8..09ccb89 100644
--- a/devices/src/ioapic.rs
+++ b/devices/src/ioapic.rs
@@ -10,6 +10,7 @@ use crate::BusDevice;
 use bit_field::*;
 use kvm::Vm;
 use msg_socket::{MsgReceiver, MsgSender};
+use std::sync::Arc;
 use sys_util::{error, warn, EventFd, Result};
 use vm_control::{VmIrqRequest, VmIrqRequestSocket, VmIrqResponse};
 
@@ -89,6 +90,7 @@ pub struct Ioapic {
     redirect_table: [RedirectionTableEntry; kvm::NUM_IOAPIC_PINS],
     // IOREGSEL is technically 32 bits, but only bottom 8 are writable: all others are fixed to 0.
     ioregsel: u8,
+    relay: Arc<GsiRelay>,
     irqfd: Vec<EventFd>,
     socket: VmIrqRequestSocket,
 }
@@ -166,11 +168,16 @@ impl Ioapic {
             current_interrupt_level_bitmap: 0,
             redirect_table: entries,
             ioregsel: 0,
+            relay: Default::default(),
             irqfd,
             socket,
         })
     }
 
+    pub fn register_relay(&mut self, relay: Arc<GsiRelay>) {
+        self.relay = relay;
+    }
+
     // The ioapic must be informed about EOIs in order to avoid sending multiple interrupts of the
     // same type at the same time.
     pub fn end_of_interrupt(&mut self, vector: u8) {
@@ -183,6 +190,12 @@ impl Ioapic {
             if self.redirect_table[i].get_vector() == vector
                 && self.redirect_table[i].get_trigger_mode() == TriggerMode::Level
             {
+                if self.relay.irqfd_resample[i].is_some() {
+                    self.service_irq(i, false);
+                }
+                if let Some(resample_evt) = &self.relay.irqfd_resample[i] {
+                    resample_evt.write(1).unwrap();
+                }
                 self.redirect_table[i].set_remote_irr(false);
             }
             // There is an inherent race condition in hardware if the OS is finished processing an
diff --git a/devices/src/pic.rs b/devices/src/pic.rs
index c18abef..f562be6 100644
--- a/devices/src/pic.rs
+++ b/devices/src/pic.rs
@@ -12,7 +12,9 @@
 // For the purposes of both using more descriptive terms and avoiding terms with lots of charged
 // emotional context, this file refers to them instead as "primary" and "secondary" PICs.
 
+use crate::split_irqchip_common::GsiRelay;
 use crate::BusDevice;
+use std::sync::Arc;
 use sys_util::{debug, warn};
 
 #[repr(usize)]
@@ -30,7 +32,7 @@ enum PicInitState {
     Icw4 = 3,
 }
 
-#[derive(Debug, Default, Clone, Copy, PartialEq)]
+#[derive(Default)]
 struct PicState {
     last_irr: u8,     // Edge detection.
     irr: u8,          // Interrupt Request Register.
@@ -53,6 +55,8 @@ struct PicState {
     elcr: u8,
     elcr_mask: u8,
     init_state: Option<PicInitState>,
+    is_primary: bool,
+    relay: Arc<GsiRelay>,
 }
 
 pub struct Pic {
@@ -176,12 +180,18 @@ impl Pic {
         // that should be masked here. In this case, bits 8 - 8 = 0 and 13 - 8 = 5.
         secondary_pic.elcr_mask = !((1 << 0) | (1 << 5));
 
+        primary_pic.is_primary = true;
         Pic {
             interrupt_request: false,
             pics: [primary_pic, secondary_pic],
         }
     }
 
+    pub fn register_relay(&mut self, relay: Arc<GsiRelay>) {
+        self.pics[0].relay = relay.clone();
+        self.pics[1].relay = relay;
+    }
+
     pub fn service_irq(&mut self, irq: u8, level: bool) -> bool {
         assert!(irq <= 15, "Unexpectedly high value irq: {} vs 15", irq);
 
@@ -391,6 +401,11 @@ impl Pic {
     fn clear_isr(pic: &mut PicState, irq: u8) {
         assert!(irq <= 7, "Unexpectedly high value for irq: {} vs 7", irq);
         pic.isr &= !(1 << irq);
+        Pic::set_irq_internal(pic, irq, false);
+        let irq = if pic.is_primary { irq } else { irq + 8 };
+        if let Some(resample_evt) = &pic.relay.irqfd_resample[irq as usize] {
+            resample_evt.write(1).unwrap();
+        }
     }
 
     fn update_irq(&mut self) -> bool {
@@ -1088,26 +1103,6 @@ mod tests {
         assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
     }
 
-    /// Verify that no-op doesn't change state.
-    #[test]
-    fn no_op_ocw2() {
-        let mut data = set_up();
-        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
-
-        // TODO(mutexlox): Verify APIC interaction when it is implemented.
-        data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
-        assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
-        data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
-
-        let orig = data.pic.pics[PicSelect::Primary as usize].clone();
-
-        // Run a no-op.
-        data.pic.write(PIC_PRIMARY_COMMAND, &[0x40]);
-
-        // Nothing should have changed.
-        assert_eq!(orig, data.pic.pics[PicSelect::Primary as usize]);
-    }
-
     /// Tests cascade IRQ that happens on secondary PIC.
     #[test]
     fn cascade_irq() {
diff --git a/devices/src/split_irqchip_common.rs b/devices/src/split_irqchip_common.rs
index b54c35a..1e513f2 100644
--- a/devices/src/split_irqchip_common.rs
+++ b/devices/src/split_irqchip_common.rs
@@ -5,6 +5,7 @@
 // Common constants and types used for Split IRQ chip devices (e.g. PIC, PIT, IOAPIC).
 
 use bit_field::*;
+use sys_util::EventFd;
 
 #[bitfield]
 #[derive(Clone, Copy, Debug, PartialEq)]
@@ -58,3 +59,36 @@ pub struct MsiDataMessage {
     trigger: TriggerMode,
     reserved2: BitField16,
 }
+
+/// Acts as a relay of interrupt signals between devices and IRQ chips.
+#[derive(Default)]
+pub struct GsiRelay {
+    pub irqfd: [Option<EventFd>; kvm::NUM_IOAPIC_PINS],
+    pub irqfd_resample: [Option<EventFd>; kvm::NUM_IOAPIC_PINS],
+}
+
+impl GsiRelay {
+    pub fn new() -> GsiRelay {
+        GsiRelay {
+            irqfd: Default::default(),
+            irqfd_resample: Default::default(),
+        }
+    }
+
+    pub fn register_irqfd(&mut self, evt: EventFd, gsi: usize) {
+        if gsi >= kvm::NUM_IOAPIC_PINS {
+            // Invalid gsi; ignore.
+            return;
+        }
+        self.irqfd[gsi] = Some(evt);
+    }
+
+    pub fn register_irqfd_resample(&mut self, evt: EventFd, resample_evt: EventFd, gsi: usize) {
+        if gsi >= kvm::NUM_IOAPIC_PINS {
+            // Invalid gsi; ignore.
+            return;
+        }
+        self.irqfd[gsi] = Some(evt);
+        self.irqfd_resample[gsi] = Some(resample_evt);
+    }
+}