crosvm-direct: enable interrupt passthrough.

Simple command line option to enable host interrupt passthrough.

BUG=b:173824544
TEST=None

Change-Id: I75a0224b8885b4129c64811ac315b995b2120d46
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2734594
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Tomasz Jeznach <tjeznach@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
diff --git a/src/linux.rs b/src/linux.rs
index 4e268c5..493f551 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -117,7 +117,10 @@
     CreateWaitContext(base::Error),
     DeviceJail(minijail::Error),
     DevicePivotRoot(minijail::Error),
+    #[cfg(feature = "direct")]
     DirectIo(io::Error),
+    #[cfg(feature = "direct")]
+    DirectIrq(devices::DirectIrqError),
     Disk(PathBuf, io::Error),
     DiskImageLock(base::Error),
     DropCapabilities(base::Error),
@@ -233,7 +236,10 @@
             CreateWaitContext(e) => write!(f, "failed to create wait context: {}", e),
             DeviceJail(e) => write!(f, "failed to jail device: {}", e),
             DevicePivotRoot(e) => write!(f, "failed to pivot root device: {}", e),
+            #[cfg(feature = "direct")]
             DirectIo(e) => write!(f, "failed to open direct io device: {}", e),
+            #[cfg(feature = "direct")]
+            DirectIrq(e) => write!(f, "failed to enable interrupt forwarding: {}", e),
             Disk(p, e) => write!(f, "failed to load disk image {}: {}", p.display(), e),
             DiskImageLock(e) => write!(f, "failed to lock disk image: {}", e),
             DropCapabilities(e) => write!(f, "failed to drop process capabilities: {}", e),
@@ -2507,6 +2513,41 @@
         }
     };
 
+    #[cfg(feature = "direct")]
+    let mut irqs = Vec::new();
+
+    #[cfg(feature = "direct")]
+    for irq in &cfg.direct_level_irq {
+        if !linux.resources.reserve_irq(*irq) {
+            warn!("irq {} already reserved.", irq);
+        }
+        let trigger = Event::new().map_err(Error::CreateEvent)?;
+        let resample = Event::new().map_err(Error::CreateEvent)?;
+        linux
+            .irq_chip
+            .register_irq_event(*irq, &trigger, Some(&resample))
+            .unwrap();
+        let direct_irq =
+            devices::DirectIrq::new(trigger, Some(resample)).map_err(Error::DirectIrq)?;
+        direct_irq.irq_enable(*irq).map_err(Error::DirectIrq)?;
+        irqs.push(direct_irq);
+    }
+
+    #[cfg(feature = "direct")]
+    for irq in &cfg.direct_edge_irq {
+        if !linux.resources.reserve_irq(*irq) {
+            warn!("irq {} already reserved.", irq);
+        }
+        let trigger = Event::new().map_err(Error::CreateEvent)?;
+        linux
+            .irq_chip
+            .register_irq_event(*irq, &trigger, None)
+            .unwrap();
+        let direct_irq = devices::DirectIrq::new(trigger, None).map_err(Error::DirectIrq)?;
+        direct_irq.irq_enable(*irq).map_err(Error::DirectIrq)?;
+        irqs.push(direct_irq);
+    }
+
     run_control(
         linux,
         control_server_socket,