diff --git a/Cargo.lock b/Cargo.lock index 17edc59fa..bf6477b22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,7 +45,7 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -104,6 +104,11 @@ name = "byteorder" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cast" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cc" version = "1.0.37" @@ -167,7 +172,10 @@ version = "0.1.0" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "epoll 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kvm-ioctls 0.1.0 (git+https://github.com/rust-vmm/kvm-ioctls)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)", "vmm-sys-util 0.1.0 (git+https://github.com/rust-vmm/vmm-sys-util)", ] @@ -217,7 +225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -234,7 +242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "kvm-ioctls" version = "0.1.0" -source = "git+https://github.com/rust-vmm/kvm-ioctls#e7a0ccc463ab8d311aec2f7b2560fa18ed47a0eb" +source = "git+https://github.com/rust-vmm/kvm-ioctls#b7e33cfa4a1d039df35c5ee24e66d06f32c94602" dependencies = [ "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", @@ -507,12 +515,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "remove_dir_all" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -550,7 +558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.35" +version = "0.15.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -565,7 +573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -578,13 +586,13 @@ dependencies = [ "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "termion" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", @@ -636,8 +644,9 @@ dependencies = [ [[package]] name = "vm-memory" version = "0.1.0" -source = "git+https://github.com/rust-vmm/vm-memory#682f10b36d6a48e656d45143fb64621bc8fe4b8e" +source = "git+https://github.com/rust-vmm/vm-memory#8816aa2dbbb48ac15db2d8396fa3075980ccaa3e" dependencies = [ + "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -721,6 +730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" +"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" "checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180" @@ -761,16 +771,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828" "checksum remain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "725c14cd936029b13547500781bd45284848e8e3970b7ef4179ecb202a150d64" -"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "32746bf0f26eab52f06af0d0aa1984f641341d06d8d673c693871da2d188c9be" "checksum ssh2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dee822d619a700f98c4de3b5931f272ecc7cf2e924ceb2df47b61df4ae033a0c" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" -"checksum syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)" = "641e117d55514d6d918490e47102f7e08d096fdde360247e4a10f7a91a8478d3" +"checksum syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)" = "8b4f551a91e2e3848aeef8751d0d4eec9489b6474c720fd4c55958d8d31a430c" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum tempfile 3.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dc4738f2e68ed2855de5ac9cdbe05c9216773ecde4739b2f095002ab03a13ef" -"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" +"checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index b38c71d54..0b412fcf5 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -22,7 +22,11 @@ extern crate vm_virtio; extern crate vmm_sys_util; use crate::config::VmConfig; -use kvm_bindings::{kvm_msi, kvm_pit_config, kvm_userspace_memory_region, KVM_PIT_SPEAKER_DUMMY}; +use devices::ioapic; +use kvm_bindings::{ + kvm_enable_cap, kvm_msi, kvm_pit_config, kvm_userspace_memory_region, KVM_CAP_SPLIT_IRQCHIP, + KVM_PIT_SPEAKER_DUMMY, +}; use kvm_ioctls::*; use libc::{c_void, siginfo_t, EFD_NONBLOCK}; use linux_loader::loader::KernelLoader; @@ -278,6 +282,7 @@ pub struct Vcpu { id: u8, io_bus: devices::Bus, mmio_bus: devices::Bus, + ioapic: Option>>, } impl Vcpu { @@ -287,7 +292,13 @@ impl Vcpu { /// /// * `id` - Represents the CPU number between [0, max vcpus). /// * `vm` - The virtual machine this vcpu will get attached to. - pub fn new(id: u8, vm: &Vm, io_bus: devices::Bus, mmio_bus: devices::Bus) -> Result { + pub fn new( + id: u8, + vm: &Vm, + io_bus: devices::Bus, + mmio_bus: devices::Bus, + ioapic: Option>>, + ) -> Result { let kvm_vcpu = vm.fd.create_vcpu(id).map_err(Error::VcpuFd)?; // Initially the cpuid per vCPU is the one supported by this VM. Ok(Vcpu { @@ -295,6 +306,7 @@ impl Vcpu { id, io_bus, mmio_bus, + ioapic, }) } @@ -351,6 +363,12 @@ impl Vcpu { self.mmio_bus.write(addr as u64, data); Ok(()) } + VcpuExit::IoapicEoi(vector) => { + if let Some(ioapic) = &self.ioapic { + ioapic.lock().unwrap().end_of_interrupt(vector); + } + Ok(()) + } r => { error!("Unexpected exit reason on vcpu run: {:?}", r); Err(Error::VcpuUnhandledKvmExit) @@ -368,6 +386,11 @@ impl Vcpu { } } +struct InterruptInfo<'a> { + msi_capable: bool, + ioapic: &'a Option>>, +} + struct KernelIoapicIrq { evt: EventFd, } @@ -384,6 +407,32 @@ impl devices::Interrupt for KernelIoapicIrq { } } +struct UserIoapicIrq { + ioapic: Arc>, + irq: usize, +} + +impl UserIoapicIrq { + fn new(ioapic: Arc>, irq: usize) -> Self { + UserIoapicIrq { ioapic, irq } + } +} + +impl devices::Interrupt for UserIoapicIrq { + fn deliver(&self) -> result::Result<(), io::Error> { + self.ioapic + .lock() + .unwrap() + .service_irq(self.irq) + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("failed to inject IRQ #{}: {:?}", self.irq, e), + ) + }) + } +} + struct DeviceManager { io_bus: devices::Bus, mmio_bus: devices::Bus, @@ -395,6 +444,9 @@ struct DeviceManager { i8042: Arc>, exit_evt: EventFd, + // IOAPIC + ioapic: Option>>, + // PCI root pci: Arc>, } @@ -406,20 +458,39 @@ impl DeviceManager { vm_fd: &Arc, vm_cfg: &VmConfig, msi_capable: bool, + userspace_ioapic: bool, ) -> DeviceManagerResult { let io_bus = devices::Bus::new(); let mut mmio_bus = devices::Bus::new(); + let ioapic = if userspace_ioapic { + // Create IOAPIC + Some(Arc::new(Mutex::new(ioapic::Ioapic::new(vm_fd.clone())))) + } else { + None + }; + + let interrupt_info = InterruptInfo { + msi_capable, + ioapic: &ioapic, + }; + // Serial is tied to IRQ #4 let serial_irq = 4; - let serial_evt = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?; - vm_fd - .register_irqfd(serial_evt.as_raw_fd(), serial_irq as u32) - .map_err(DeviceManagerError::Irq)?; + let interrupt: Box = if let Some(ioapic) = &ioapic { + Box::new(UserIoapicIrq::new(ioapic.clone(), serial_irq)) + } else { + let serial_evt = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?; + vm_fd + .register_irqfd(serial_evt.as_raw_fd(), serial_irq as u32) + .map_err(DeviceManagerError::Irq)?; + + Box::new(KernelIoapicIrq::new(serial_evt)) + }; // Add serial device let serial = Arc::new(Mutex::new(devices::legacy::Serial::new_out( - Box::new(KernelIoapicIrq::new(serial_evt)), + interrupt, Box::new(stdout()), ))); @@ -465,7 +536,7 @@ impl DeviceManager { vm_fd, &mut pci_root, &mut mmio_bus, - msi_capable, + &interrupt_info, )?; } @@ -490,7 +561,7 @@ impl DeviceManager { vm_fd, &mut pci_root, &mut mmio_bus, - msi_capable, + &interrupt_info, )?; } @@ -507,7 +578,7 @@ impl DeviceManager { vm_fd, &mut pci_root, &mut mmio_bus, - msi_capable, + &interrupt_info, )?; } @@ -519,6 +590,7 @@ impl DeviceManager { serial, i8042, exit_evt, + ioapic, pci, }) } @@ -530,9 +602,13 @@ impl DeviceManager { vm_fd: &Arc, pci_root: &mut PciRoot, mmio_bus: &mut devices::Bus, - msi_capable: bool, + interrupt_info: &InterruptInfo, ) -> DeviceManagerResult<()> { - let msix_num = if msi_capable { DEFAULT_MSIX_VEC_NUM } else { 0 }; + let msix_num = if interrupt_info.msi_capable { + DEFAULT_MSIX_VEC_NUM + } else { + 0 + }; let mut virtio_pci_device = VirtioPciDevice::new(memory, virtio_device, msix_num) .map_err(DeviceManagerError::VirtioDevice)?; @@ -548,7 +624,7 @@ impl DeviceManager { .map_err(DeviceManagerError::RegisterIoevent)?; } - if msi_capable { + if interrupt_info.msi_capable { let vm_fd_clone = vm_fd.clone(); let msi_cb = Arc::new(Box::new(move |p: InterruptParameters| { @@ -579,19 +655,38 @@ impl DeviceManager { virtio_pci_device.assign_msix(msi_cb); } else { - let irqfd = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?; - let irq_num = allocator .allocate_irq() .ok_or(DeviceManagerError::AllocateIrq)?; - vm_fd - .register_irqfd(irqfd.as_raw_fd(), irq_num) - .map_err(DeviceManagerError::Irq)?; - let irq_cb = Arc::new( + let irq_cb = if let Some(ioapic) = interrupt_info.ioapic { + let ioapic_clone = ioapic.clone(); + Box::new(move |_p: InterruptParameters| { + ioapic_clone + .lock() + .unwrap() + .service_irq(irq_num as usize) + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("failed to inject IRQ #{}: {:?}", irq_num, e), + ) + }) + }) as InterruptDelivery + } else { + let irqfd = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?; + vm_fd + .register_irqfd(irqfd.as_raw_fd(), irq_num) + .map_err(DeviceManagerError::Irq)?; + Box::new(move |_p: InterruptParameters| irqfd.write(1)) as InterruptDelivery + }; + + virtio_pci_device.assign_pin_irq( + Arc::new(irq_cb), + irq_num as u32, + PciInterruptPin::IntA, ); - virtio_pci_device.assign_pin_irq(irq_cb, irq_num as u32, PciInterruptPin::IntA); } let virtio_pci_device = Arc::new(Mutex::new(virtio_pci_device)); @@ -618,6 +713,14 @@ impl DeviceManager { self.io_bus .insert(self.pci.clone(), 0xcf8, 0x8) .map_err(Error::BusError)?; + + if let Some(ioapic) = &self.ioapic { + // Insert IOAPIC + self.mmio_bus + .insert(ioapic.clone(), 0xfec0_0000, 0x20) + .map_err(Error::BusError)?; + } + Ok(()) } } @@ -727,16 +830,33 @@ impl<'a> Vm<'a> { fd.set_tss_address(arch::x86_64::layout::KVM_TSS_ADDRESS.raw_value() as usize) .map_err(Error::VmSetup)?; - // Create IRQ chip - fd.create_irq_chip().map_err(Error::VmSetup)?; - // Supported CPUID let mut cpuid = kvm .get_supported_cpuid(MAX_KVM_CPUID_ENTRIES) .map_err(Error::VmSetup)?; + let msi_capable = kvm.check_extension(Cap::SignalMsi); + let mut cpuid_patches = Vec::new(); + let mut userspace_ioapic = false; if kvm.check_extension(Cap::TscDeadlineTimer) { + if kvm.check_extension(Cap::SplitIrqchip) && msi_capable { + // Create split irqchip + // Only the local APIC is emulated in kernel, both PICs and IOAPIC + // are not. + let mut cap: kvm_enable_cap = Default::default(); + cap.cap = KVM_CAP_SPLIT_IRQCHIP; + cap.args[0] = ioapic::NUM_IOAPIC_PINS as u64; + fd.enable_cap(&cap).map_err(Error::VmSetup)?; + + // Because of the split irqchip, we need a userspace IOAPIC. + userspace_ioapic = true; + } else { + // Create irqchip + // A local APIC, 2 PICs and an IOAPIC are emulated in kernel. + fd.create_irq_chip().map_err(Error::VmSetup)?; + } + // Patch tsc deadline timer bit cpuid_patches.push(CpuidPatch { function: 1, @@ -748,6 +868,9 @@ impl<'a> Vm<'a> { edx_bit: None, }); } else { + // Create irqchip + // A local APIC, 2 PICs and an IOAPIC are emulated in kernel. + fd.create_irq_chip().map_err(Error::VmSetup)?; // Creates an in-kernel device model for the PIT. let mut pit_config = kvm_pit_config::default(); // We need to enable the emulation of a dummy speaker port stub so that writing to port 0x61 @@ -784,7 +907,8 @@ impl<'a> Vm<'a> { &mut allocator, &fd, &config, - kvm.check_extension(Cap::SignalMsi), + msi_capable, + userspace_ioapic, ) .map_err(Error::DeviceManager)?; @@ -952,7 +1076,13 @@ impl<'a> Vm<'a> { for cpu_id in 0..vcpu_count { let io_bus = self.devices.io_bus.clone(); let mmio_bus = self.devices.mmio_bus.clone(); - let mut vcpu = Vcpu::new(cpu_id, &self, io_bus, mmio_bus)?; + let ioapic = if let Some(ioapic) = &self.devices.ioapic { + Some(ioapic.clone()) + } else { + None + }; + + let mut vcpu = Vcpu::new(cpu_id, &self, io_bus, mmio_bus, ioapic)?; vcpu.configure(entry_addr, &self)?; let vcpu_thread_barrier = vcpu_thread_barrier.clone(); @@ -1094,7 +1224,7 @@ pub fn test_vm() { VcpuExit::Epr => {} VcpuExit::SystemEvent => {} VcpuExit::S390Stsi => {} - VcpuExit::IoapicEoi => {} + VcpuExit::IoapicEoi(_vector) => {} VcpuExit::Hyperv => {} } // r => panic!("unexpected exit reason: {:?}", r),