summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--aarch64/src/lib.rs6
-rw-r--r--arch/src/lib.rs7
-rw-r--r--src/linux.rs2
-rw-r--r--src/main.rs7
-rw-r--r--x86_64/src/lib.rs48
5 files changed, 58 insertions, 12 deletions
diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs
index 48effe5..6bbc775 100644
--- a/aarch64/src/lib.rs
+++ b/aarch64/src/lib.rs
@@ -190,7 +190,11 @@ fn fdt_offset(mem_size: u64) -> u64 {
 pub struct AArch64;
 
 impl arch::LinuxArch for AArch64 {
-    fn build_vm<F>(mut components: VmComponents, virtio_devs: F) -> Result<RunnableLinuxVm>
+    fn build_vm<F>(
+        mut components: VmComponents,
+        _split_irqchip: bool,
+        virtio_devs: F,
+    ) -> Result<RunnableLinuxVm>
     where
         F: FnOnce(
             &GuestMemory,
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
index 795591a..e6814bc 100644
--- a/arch/src/lib.rs
+++ b/arch/src/lib.rs
@@ -74,8 +74,13 @@ pub trait LinuxArch {
     /// # Arguments
     ///
     /// * `components` - Parts to use to build the VM.
+    /// * `split_irqchip` - whether to use a split IRQ chip (i.e. userspace PIT/PIC/IOAPIC)
     /// * `virtio_devs` - Function to generate a list of virtio devices.
-    fn build_vm<F>(components: VmComponents, virtio_devs: F) -> Result<RunnableLinuxVm>
+    fn build_vm<F>(
+        components: VmComponents,
+        split_irqchip: bool,
+        virtio_devs: F,
+    ) -> Result<RunnableLinuxVm>
     where
         F: FnOnce(
             &GuestMemory,
diff --git a/src/linux.rs b/src/linux.rs
index d2e0610..beb1021 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -1011,7 +1011,7 @@ pub fn run_config(cfg: Config) -> Result<()> {
         disk_host_sockets.push(disk_host_socket);
     }
 
-    let linux = Arch::build_vm(components, |m, e| {
+    let linux = Arch::build_vm(components, cfg.split_irqchip, |m, e| {
         create_virtio_devs(
             cfg,
             m,
diff --git a/src/main.rs b/src/main.rs
index 8e83621..31019f9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -120,6 +120,7 @@ pub struct Config {
     virtio_mouse: Option<PathBuf>,
     virtio_keyboard: Option<PathBuf>,
     virtio_input_evdevs: Vec<PathBuf>,
+    split_irqchip: bool,
 }
 
 impl Default for Config {
@@ -153,6 +154,7 @@ impl Default for Config {
             virtio_mouse: None,
             virtio_keyboard: None,
             virtio_input_evdevs: Vec::new(),
+            split_irqchip: false,
         }
     }
 }
@@ -564,6 +566,9 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             }
             cfg.virtio_input_evdevs.push(dev_path);
         }
+        "split-irqchip" => {
+            cfg.split_irqchip = true;
+        }
         "help" => return Err(argument::Error::PrintHelp),
         _ => unreachable!(),
     }
@@ -627,6 +632,8 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
           Argument::value("trackpad", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read trackpad input events and write status updates to, optionally followed by screen width and height (defaults to 800x1280)."),
           Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
           Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),
+          #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+          Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
           Argument::short_flag('h', "help", "Print help message.")];
 
     let mut cfg = Config::default();
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index ed5637d..b65fe46 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -83,7 +83,7 @@ use io_jail::Minijail;
 use kvm::*;
 use resources::{AddressRanges, SystemAllocator};
 use sync::Mutex;
-use sys_util::{EventFd, GuestAddress, GuestMemory};
+use sys_util::{Clock, EventFd, GuestAddress, GuestMemory};
 
 #[derive(Debug)]
 pub enum Error {
@@ -95,6 +95,8 @@ pub enum Error {
     Cmdline(kernel_cmdline::Error),
     /// Unable to make an EventFd
     CreateEventFd(sys_util::Error),
+    /// Unable to create PIT device.
+    CreatePit(devices::PitError),
     /// Unable to create Kvm.
     CreateKvm(sys_util::Error),
     /// Unable to create a PciRoot hub.
@@ -126,6 +128,7 @@ impl error::Error for Error {
             Error::CloneEventFd(_) => "Unable to clone an EventFd",
             Error::Cmdline(_) => "the given kernel command line was invalid",
             Error::CreateEventFd(_) => "Unable to make an EventFd",
+            Error::CreatePit(_) => "Unable to make Pit device",
             Error::CreateKvm(_) => "failed to open /dev/kvm",
             Error::CreatePciRoot(_) => "failed to create a PCI root hub",
             Error::CreateSocket(_) => "failed to create socket",
@@ -286,7 +289,11 @@ fn arch_memory_regions(size: u64) -> Vec<(GuestAddress, u64)> {
 }
 
 impl arch::LinuxArch for X8664arch {
-    fn build_vm<F>(mut components: VmComponents, virtio_devs: F) -> Result<RunnableLinuxVm>
+    fn build_vm<F>(
+        mut components: VmComponents,
+        split_irqchip: bool,
+        virtio_devs: F,
+    ) -> Result<RunnableLinuxVm>
     where
         F: FnOnce(
             &GuestMemory,
@@ -297,7 +304,7 @@ impl arch::LinuxArch for X8664arch {
             Self::get_resource_allocator(components.memory_mb, components.wayland_dmabuf);
         let mem = Self::setup_memory(components.memory_mb)?;
         let kvm = Kvm::new().map_err(Error::CreateKvm)?;
-        let mut vm = Self::create_vm(&kvm, mem.clone())?;
+        let mut vm = Self::create_vm(&kvm, split_irqchip, mem.clone())?;
 
         let vcpu_count = components.vcpu_count;
         let mut vcpus = Vec::with_capacity(vcpu_count as usize);
@@ -329,6 +336,7 @@ impl arch::LinuxArch for X8664arch {
 
         let (io_bus, stdio_serial) = Self::setup_io_bus(
             &mut vm,
+            split_irqchip,
             exit_evt.try_clone().map_err(Error::CloneEventFd)?,
             Some(pci_bus.clone()),
         )?;
@@ -418,13 +426,16 @@ impl X8664arch {
     /// # Arguments
     ///
     /// * `kvm` - The opened /dev/kvm object.
+    /// * `split_irqchip` - Whether to use a split IRQ chip.
     /// * `mem` - The memory to be used by the guest.
-    fn create_vm(kvm: &Kvm, mem: GuestMemory) -> Result<Vm> {
+    fn create_vm(kvm: &Kvm, split_irqchip: bool, mem: GuestMemory) -> Result<Vm> {
         let vm = Vm::new(&kvm, mem)?;
         let tss_addr = GuestAddress(0xfffbd000);
         vm.set_tss_addr(tss_addr).expect("set tss addr failed");
-        vm.create_pit().expect("create pit failed");
-        vm.create_irq_chip()?;
+        if !split_irqchip {
+            vm.create_pit().expect("create pit failed");
+            vm.create_irq_chip()?;
+        }
         Ok(vm)
     }
 
@@ -490,9 +501,11 @@ impl X8664arch {
     /// # Arguments
     ///
     /// * - `vm` the vm object
+    /// * - `split_irqchip`: whether to use a split IRQ chip (i.e. userspace PIT/PIC/IOAPIC)
     /// * - `exit_evt` - the event fd object which should receive exit events
     fn setup_io_bus(
         vm: &mut Vm,
+        split_irqchip: bool,
         exit_evt: EventFd,
         pci: Option<Arc<Mutex<devices::PciConfigIo>>>,
     ) -> Result<(devices::Bus, Arc<Mutex<devices::Serial>>)> {
@@ -558,9 +571,26 @@ impl X8664arch {
                 false,
             )
             .unwrap();
-        io_bus
-            .insert(nul_device.clone(), 0x040, 0x8, false)
-            .unwrap(); // ignore pit
+
+        if split_irqchip {
+            let pit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
+            let pit = Arc::new(Mutex::new(
+                devices::Pit::new(
+                    pit_evt.try_clone().map_err(Error::CloneEventFd)?,
+                    Arc::new(Mutex::new(Clock::new())),
+                )
+                .map_err(Error::CreatePit)?,
+            ));
+            // Reserve from 0x40 to 0x61 (the speaker).
+            io_bus.insert(pit.clone(), 0x040, 0x22, false).unwrap();
+            vm.register_irqfd(&pit_evt, 0)
+                .map_err(Error::RegisterIrqfd)?;
+        } else {
+            io_bus
+                .insert(nul_device.clone(), 0x040, 0x8, false)
+                .unwrap(); // ignore pit
+        }
+
         io_bus
             .insert(nul_device.clone(), 0x0ed, 0x1, false)
             .unwrap(); // most likely this one does nothing