summary refs log tree commit diff
diff options
context:
space:
mode:
authorZhuocheng Ding <zhuocheng.ding@intel.corp-partner.google.com>2019-12-02 15:50:24 +0800
committerCommit Bot <commit-bot@chromium.org>2020-03-05 01:02:49 +0000
commitdb4c70d2151d054b6b4df58be432e500aeafecbe (patch)
tree20c86cafe4e8ca664f90a853518075d204c49173
parentf2e90bf0b0ca101d2925e91ca50d3e9e5ea2fdb7 (diff)
downloadcrosvm-db4c70d2151d054b6b4df58be432e500aeafecbe.tar
crosvm-db4c70d2151d054b6b4df58be432e500aeafecbe.tar.gz
crosvm-db4c70d2151d054b6b4df58be432e500aeafecbe.tar.bz2
crosvm-db4c70d2151d054b6b4df58be432e500aeafecbe.tar.lz
crosvm-db4c70d2151d054b6b4df58be432e500aeafecbe.tar.xz
crosvm-db4c70d2151d054b6b4df58be432e500aeafecbe.tar.zst
crosvm-db4c70d2151d054b6b4df58be432e500aeafecbe.zip
devices: PIC: implement interrupt injection
TODO: Route irqfd to PIC, and use signal to kick vCPU thread when
interrupt is triggered.

BUG=chromium:908689
TEST=Unit tests in file.

Change-Id: I9a87502da57e725d3bb26d746a337d0ba44ef337
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1945797
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Zhuocheng Ding <zhuocheng.ding@intel.corp-partner.google.com>
-rw-r--r--devices/src/pic.rs20
-rw-r--r--kvm/src/lib.rs20
-rw-r--r--src/linux.rs25
3 files changed, 58 insertions, 7 deletions
diff --git a/devices/src/pic.rs b/devices/src/pic.rs
index 9b8235f..c18abef 100644
--- a/devices/src/pic.rs
+++ b/devices/src/pic.rs
@@ -7,10 +7,10 @@
 // modern OSs that use a legacy BIOS.
 // The PIC is connected to the Local APIC on CPU0.
 
-// Terminology note: The 8259A spec refers to "master" and "slave" PITs; the "slave"s are daisy
+// Terminology note: The 8259A spec refers to "master" and "slave" PICs; the "slave"s are daisy
 // chained to the "master"s.
 // 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" PITs.
+// emotional context, this file refers to them instead as "primary" and "secondary" PICs.
 
 use crate::BusDevice;
 use sys_util::{debug, warn};
@@ -56,9 +56,9 @@ struct PicState {
 }
 
 pub struct Pic {
-    // TODO(mutexlox): Implement an APIC and add necessary state to the Pic.
-
-    // index 0 (aka PicSelect::Primary) is the primary pic, the rest are secondary.
+    // Indicates a pending INTR signal to LINT0 of vCPU, checked by vCPU thread.
+    interrupt_request: bool,
+    // Index 0 (aka PicSelect::Primary) is the primary pic, the rest are secondary.
     pics: [PicState; 2],
 }
 
@@ -175,9 +175,9 @@ impl Pic {
         // The secondary PIC has IRQs 8-15, so we subtract 8 from the IRQ number to get the bit
         // that should be masked here. In this case, bits 8 - 8 = 0 and 13 - 8 = 5.
         secondary_pic.elcr_mask = !((1 << 0) | (1 << 5));
-        // TODO(mutexlox): Add logic to initialize APIC interrupt-related fields.
 
         Pic {
+            interrupt_request: false,
             pics: [primary_pic, secondary_pic],
         }
     }
@@ -205,8 +205,14 @@ impl Pic {
         self.get_irq(PicSelect::Primary).is_some()
     }
 
+    /// Determines whether the PIC has fired an interrupt to LAPIC.
+    pub fn interrupt_requested(&self) -> bool {
+        self.interrupt_request
+    }
+
     /// Determines the external interrupt number that the PIC is prepared to inject, if any.
     pub fn get_external_interrupt(&mut self) -> Option<u8> {
+        self.interrupt_request = false;
         let irq_primary = if let Some(irq) = self.get_irq(PicSelect::Primary) {
             irq
         } else {
@@ -403,7 +409,7 @@ impl Pic {
         }
 
         if self.get_irq(PicSelect::Primary).is_some() {
-            // TODO(mutexlox): Signal local interrupt on APIC bus.
+            self.interrupt_request = true;
             // Note: this does not check if the interrupt is succesfully injected into
             // the CPU, just whether or not one is fired.
             true
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs
index 66ebd20..2d951f2 100644
--- a/kvm/src/lib.rs
+++ b/kvm/src/lib.rs
@@ -1313,6 +1313,26 @@ impl Vcpu {
         });
     }
 
+    /// Request the VCPU to exit when it becomes possible to inject interrupts into the guest.
+    #[allow(clippy::cast_ptr_alignment)]
+    pub fn request_interrupt_window(&self) {
+        // Safe because we know we mapped enough memory to hold the kvm_run struct because the
+        // kernel told us how large it was. The pointer is page aligned so casting to a different
+        // type is well defined, hence the clippy allow attribute.
+        let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut kvm_run) };
+        run.request_interrupt_window = 1;
+    }
+
+    /// Checks if we can inject an interrupt into the VCPU.
+    #[allow(clippy::cast_ptr_alignment)]
+    pub fn ready_for_interrupt(&self) -> bool {
+        // Safe because we know we mapped enough memory to hold the kvm_run struct because the
+        // kernel told us how large it was. The pointer is page aligned so casting to a different
+        // type is well defined, hence the clippy allow attribute.
+        let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut kvm_run) };
+        run.ready_for_interrupt_injection != 0 && run.if_flag != 0
+    }
+
     /// Gets the VCPU registers.
     #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
     pub fn get_regs(&self) -> Result<kvm_regs> {
diff --git a/src/linux.rs b/src/linux.rs
index ad35407..a181e52 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -1349,6 +1349,26 @@ fn runnable_vcpu(vcpu: Vcpu, use_kvm_signals: bool, cpu_id: u32) -> Option<Runna
     }
 }
 
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+fn inject_interrupt(pic: &Arc<Mutex<devices::Pic>>, vcpu: &RunnableVcpu) {
+    let mut pic = pic.lock();
+    if pic.interrupt_requested() && vcpu.ready_for_interrupt() {
+        if let Some(vector) = pic.get_external_interrupt() {
+            if let Err(e) = vcpu.interrupt(vector as u32) {
+                error!("PIC: failed to inject interrupt to vCPU0: {}", e);
+            }
+        }
+        // The second interrupt request should be handled immediately, so ask
+        // vCPU to exit as soon as possible.
+        if pic.interrupt_requested() {
+            vcpu.request_interrupt_window();
+        }
+    }
+}
+
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+fn inject_interrupt(pic: &Arc<Mutex<devices::Pic>>, vcpu: &RunnableVcpu) {}
+
 fn run_vcpu(
     vcpu: Vcpu,
     cpu_id: u32,
@@ -1480,6 +1500,11 @@ fn run_vcpu(
                             run_mode_lock = run_mode_arc.cvar.wait(run_mode_lock);
                         }
                     }
+
+                    if cpu_id != 0 { continue; }
+                    if let Some((pic, _)) = &split_irqchip {
+                        inject_interrupt(pic, &vcpu);
+                    }
                 }
             }
         })