summary refs log tree commit diff
path: root/x86_64
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 /x86_64
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 'x86_64')
-rw-r--r--x86_64/src/lib.rs73
1 files changed, 52 insertions, 21 deletions
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index d9fe8a9..a912edd 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -55,6 +55,7 @@ use std::sync::Arc;
 
 use crate::bootparam::boot_params;
 use arch::{RunnableLinuxVm, VmComponents, VmImage};
+use devices::split_irqchip_common::GsiRelay;
 use devices::{
     get_serial_tty_string, Ioapic, PciConfigIo, PciDevice, PciInterruptPin, Pic, SerialParameters,
     IOAPIC_BASE_ADDRESS, IOAPIC_MEM_LENGTH_BYTES,
@@ -88,6 +89,7 @@ pub enum Error {
     CreateVcpu(sys_util::Error),
     CreateVm(sys_util::Error),
     E820Configuration,
+    EnableSplitIrqchip(sys_util::Error),
     KernelOffsetPastEnd,
     LoadBios(io::Error),
     LoadBzImage(bzimage::Error),
@@ -136,6 +138,7 @@ impl Display for Error {
             CreateVcpu(e) => write!(f, "failed to create VCPU: {}", e),
             CreateVm(e) => write!(f, "failed to create VM: {}", e),
             E820Configuration => write!(f, "invalid e820 setup params"),
+            EnableSplitIrqchip(e) => write!(f, "failed to enable split irqchip: {}", e),
             KernelOffsetPastEnd => write!(f, "the kernel extends past the end of RAM"),
             LoadBios(e) => write!(f, "error loading bios: {}", e),
             LoadBzImage(e) => write!(f, "error loading kernel bzImage: {}", e),
@@ -369,7 +372,8 @@ impl arch::LinuxArch for X8664arch {
 
         let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
 
-        let split_irqchip = if split_irqchip {
+        let (split_irqchip, mut gsi_relay) = if split_irqchip {
+            let gsi_relay = GsiRelay::new();
             let pic = Arc::new(Mutex::new(Pic::new()));
             let ioapic = Arc::new(Mutex::new(
                 Ioapic::new(&mut vm, ioapic_device_socket).map_err(Error::CreateIoapicDevice)?,
@@ -382,15 +386,20 @@ impl arch::LinuxArch for X8664arch {
                     false,
                 )
                 .unwrap();
-            Some((pic, ioapic))
+            (Some((pic, ioapic)), Some(gsi_relay))
         } else {
-            None
+            (None, None)
         };
         let pci_devices = create_devices(&mem, &mut vm, &mut resources, &exit_evt)
             .map_err(|e| Error::CreateDevices(Box::new(e)))?;
-        let (pci, pci_irqs, pid_debug_label_map) =
-            arch::generate_pci_root(pci_devices, &mut mmio_bus, &mut resources, &mut vm)
-                .map_err(Error::CreatePciRoot)?;
+        let (pci, pci_irqs, pid_debug_label_map) = arch::generate_pci_root(
+            pci_devices,
+            &mut gsi_relay,
+            &mut mmio_bus,
+            &mut resources,
+            &mut vm,
+        )
+        .map_err(Error::CreatePciRoot)?;
         let pci_bus = Arc::new(Mutex::new(PciConfigIo::new(pci)));
 
         // Event used to notify crosvm that guest OS is trying to suspend.
@@ -400,15 +409,20 @@ impl arch::LinuxArch for X8664arch {
 
         let mut io_bus = Self::setup_io_bus(
             &mut vm,
-            split_irqchip.is_some(),
+            &mut gsi_relay,
             exit_evt.try_clone().map_err(Error::CloneEventFd)?,
             Some(pci_bus.clone()),
             components.memory_size,
             suspend_evt.try_clone().map_err(Error::CloneEventFd)?,
         )?;
 
-        let stdio_serial_num =
-            Self::setup_serial_devices(&mut vm, &mut io_bus, serial_parameters, serial_jail)?;
+        let stdio_serial_num = Self::setup_serial_devices(
+            &mut vm,
+            &mut io_bus,
+            &mut gsi_relay,
+            serial_parameters,
+            serial_jail,
+        )?;
 
         let ramoops_region = match components.pstore {
             Some(pstore) => Some(
@@ -418,7 +432,7 @@ impl arch::LinuxArch for X8664arch {
             None => None,
         };
 
-        if let Some((pic, _)) = &split_irqchip {
+        let gsi_relay = if let Some((pic, ioapic)) = &split_irqchip {
             io_bus.insert(pic.clone(), 0x20, 0x2, true).unwrap();
             io_bus.insert(pic.clone(), 0xa0, 0x2, true).unwrap();
             io_bus.insert(pic.clone(), 0x4d0, 0x2, true).unwrap();
@@ -427,7 +441,15 @@ impl arch::LinuxArch for X8664arch {
             while irq_num < kvm::NUM_IOAPIC_PINS as u32 {
                 irq_num = resources.allocate_irq().unwrap();
             }
-        }
+
+            // This will never fail because gsi_relay is Some iff split_irqchip is Some.
+            let gsi_relay = Arc::new(gsi_relay.unwrap());
+            pic.lock().register_relay(gsi_relay.clone());
+            ioapic.lock().register_relay(gsi_relay.clone());
+            Some(gsi_relay)
+        } else {
+            None
+        };
 
         match components.vm_image {
             VmImage::Bios(ref mut bios) => Self::load_bios(&mem, bios)?,
@@ -483,6 +505,7 @@ impl arch::LinuxArch for X8664arch {
             vcpu_affinity,
             irq_chip,
             split_irqchip,
+            gsi_relay,
             io_bus,
             mmio_bus,
             pid_debug_label_map,
@@ -638,6 +661,8 @@ impl X8664arch {
             vm.create_pit().map_err(Error::CreatePit)?;
             vm.create_irq_chip().map_err(Error::CreateIrqChip)?;
         } else {
+            vm.enable_split_irqchip()
+                .map_err(Error::EnableSplitIrqchip)?;
             for i in 0..kvm::NUM_IOAPIC_PINS {
                 // Add dummy MSI routes to replace the default IRQChip routes.
                 let route = IrqRoute {
@@ -719,13 +744,13 @@ impl X8664arch {
     /// # Arguments
     ///
     /// * - `vm` the vm object
-    /// * - `split_irqchip`: whether to use a split IRQ chip (i.e. userspace PIT/PIC/IOAPIC)
+    /// * - `gsi_relay`: only valid for split IRQ chip (i.e. userspace PIT/PIC/IOAPIC)
     /// * - `exit_evt` - the event fd object which should receive exit events
     /// * - `mem_size` - the size in bytes of physical ram for the guest
     /// * - `suspend_evt` - the event fd object which used to suspend the vm
     fn setup_io_bus(
-        vm: &mut Vm,
-        split_irqchip: bool,
+        _vm: &mut Vm,
+        gsi_relay: &mut Option<GsiRelay>,
         exit_evt: EventFd,
         pci: Option<Arc<Mutex<devices::PciConfigIo>>>,
         mem_size: u64,
@@ -758,7 +783,7 @@ impl X8664arch {
             exit_evt.try_clone().map_err(Error::CloneEventFd)?,
         )));
 
-        if split_irqchip {
+        if let Some(gsi_relay) = gsi_relay {
             let pit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
             let pit = Arc::new(Mutex::new(
                 devices::Pit::new(
@@ -770,8 +795,7 @@ impl X8664arch {
             io_bus.insert(pit.clone(), 0x040, 0x8, true).unwrap();
             io_bus.insert(pit.clone(), 0x061, 0x1, true).unwrap();
             io_bus.insert(i8042, 0x062, 0x3, true).unwrap();
-            vm.register_irqfd(&pit_evt, 0)
-                .map_err(Error::RegisterIrqfd)?;
+            gsi_relay.register_irqfd(pit_evt, 0);
         } else {
             io_bus
                 .insert(nul_device.clone(), 0x040, 0x8, false)
@@ -816,10 +840,12 @@ impl X8664arch {
     ///
     /// * - `vm` the vm object
     /// * - `io_bus` the I/O bus to add the devices to
+    /// * - `gsi_relay`: only valid for split IRQ chip (i.e. userspace PIT/PIC/IOAPIC)
     /// * - `serial_parmaters` - definitions for how the serial devices should be configured
     fn setup_serial_devices(
         vm: &mut Vm,
         io_bus: &mut devices::Bus,
+        gsi_relay: &mut Option<GsiRelay>,
         serial_parameters: &BTreeMap<u8, SerialParameters>,
         serial_jail: Option<Minijail>,
     ) -> Result<Option<u8>> {
@@ -835,10 +861,15 @@ impl X8664arch {
         )
         .map_err(Error::CreateSerialDevices)?;
 
-        vm.register_irqfd(&com_evt_1_3, X86_64_SERIAL_1_3_IRQ)
-            .map_err(Error::RegisterIrqfd)?;
-        vm.register_irqfd(&com_evt_2_4, X86_64_SERIAL_2_4_IRQ)
-            .map_err(Error::RegisterIrqfd)?;
+        if let Some(gsi_relay) = gsi_relay {
+            gsi_relay.register_irqfd(com_evt_1_3, X86_64_SERIAL_1_3_IRQ as usize);
+            gsi_relay.register_irqfd(com_evt_2_4, X86_64_SERIAL_2_4_IRQ as usize);
+        } else {
+            vm.register_irqfd(&com_evt_1_3, X86_64_SERIAL_1_3_IRQ)
+                .map_err(Error::RegisterIrqfd)?;
+            vm.register_irqfd(&com_evt_2_4, X86_64_SERIAL_2_4_IRQ)
+                .map_err(Error::RegisterIrqfd)?;
+        }
 
         Ok(stdio_serial_num)
     }