summary refs log tree commit diff
path: root/devices/src/irqchip/kvm/x86_64.rs
diff options
context:
space:
mode:
Diffstat (limited to 'devices/src/irqchip/kvm/x86_64.rs')
-rw-r--r--devices/src/irqchip/kvm/x86_64.rs216
1 files changed, 216 insertions, 0 deletions
diff --git a/devices/src/irqchip/kvm/x86_64.rs b/devices/src/irqchip/kvm/x86_64.rs
new file mode 100644
index 0000000..acede53
--- /dev/null
+++ b/devices/src/irqchip/kvm/x86_64.rs
@@ -0,0 +1,216 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use hypervisor::kvm::KvmVcpu;
+use hypervisor::{IoapicState, LapicState, PicSelect, PicState, PitState};
+use kvm_sys::*;
+use sys_util::Result;
+
+use crate::{Bus, IrqChipX86_64, KvmKernelIrqChip};
+
+impl IrqChipX86_64<KvmVcpu> for KvmKernelIrqChip {
+    /// Get the current state of the PIC
+    fn get_pic_state(&self, select: PicSelect) -> Result<PicState> {
+        Ok(PicState::from(&self.vm.get_pic_state(select)?))
+    }
+
+    /// Set the current state of the PIC
+    fn set_pic_state(&mut self, select: PicSelect, state: &PicState) -> Result<()> {
+        self.vm.set_pic_state(select, &kvm_pic_state::from(state))
+    }
+
+    /// Get the current state of the IOAPIC
+    fn get_ioapic_state(&self) -> Result<IoapicState> {
+        Ok(IoapicState::from(&self.vm.get_ioapic_state()?))
+    }
+
+    /// Set the current state of the IOAPIC
+    fn set_ioapic_state(&mut self, state: &IoapicState) -> Result<()> {
+        self.vm.set_ioapic_state(&kvm_ioapic_state::from(state))
+    }
+
+    /// Get the current state of the specified VCPU's local APIC
+    fn get_lapic_state(&self, _vcpu_id: usize) -> Result<LapicState> {
+        unimplemented!("get_lapic_state for KvmKernelIrqChip is not yet implemented");
+    }
+
+    /// Set the current state of the specified VCPU's local APIC
+    fn set_lapic_state(&mut self, _vcpu_id: usize, _state: &LapicState) -> Result<()> {
+        unimplemented!("set_lapic_state for KvmKernelIrqChip is not yet implemented");
+    }
+
+    /// Create a PIT (Programmable Interval Timer) for this VM.
+    /// The KvmKernelIrqchip creates the PIT by calling the KVM_CREATE_PIT2 KVM API. The
+    /// io_bus is not used in this case because KVM handles intercepting port-mapped io intended
+    /// for the PIT.
+    fn create_pit(&mut self, _io_bus: &mut Bus) -> Result<()> {
+        self.vm.create_pit()
+    }
+
+    /// Retrieves the state of the PIT. Gets the pit state via the KVM API.
+    fn get_pit(&self) -> Result<PitState> {
+        Ok(PitState::from(&self.vm.get_pit_state()?))
+    }
+
+    /// Sets the state of the PIT. Sets the pit state via the KVM API.
+    fn set_pit(&mut self, state: &PitState) -> Result<()> {
+        self.vm.set_pit_state(&kvm_pit_state2::from(state))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use hypervisor::kvm::{Kvm, KvmVm};
+    use sys_util::GuestMemory;
+
+    use crate::irqchip::{IrqChip, IrqChipX86_64, KvmKernelIrqChip};
+    use crate::Bus;
+
+    use hypervisor::{PicSelect, Vm, VmX86_64};
+
+    fn get_chip() -> (KvmKernelIrqChip, KvmVm) {
+        let kvm = Kvm::new().expect("failed to instantiate Kvm");
+        let mem = GuestMemory::new(&[]).unwrap();
+        let vm = KvmVm::new(&kvm, mem).expect("failed tso instantiate vm");
+        let vcpu = vm.create_vcpu(0).expect("failed to instantiate vcpu");
+
+        let mut chip = KvmKernelIrqChip::new(vm.try_clone().expect("failed to clone vm"), 1)
+            .expect("failed to instantiate KvmKernelIrqChip");
+
+        chip.add_vcpu(0, vcpu).expect("failed to add vcpu");
+
+        (chip, vm)
+    }
+
+    #[test]
+    fn get_pic() {
+        let (chip, vm) = get_chip();
+
+        let state = chip
+            .get_pic_state(PicSelect::Primary)
+            .expect("could not get pic state");
+
+        // Default is that no irq lines are asserted
+        assert_eq!(state.irr, 0);
+
+        // Assert Irq Line 0
+        vm.set_irq_line(0, true).expect("could not set irq line");
+
+        let state = chip
+            .get_pic_state(PicSelect::Primary)
+            .expect("could not get pic state");
+
+        // Bit 0 should now be 1
+        assert_eq!(state.irr, 1);
+    }
+
+    #[test]
+    fn set_pic() {
+        let (mut chip, _) = get_chip();
+
+        let mut state = chip
+            .get_pic_state(PicSelect::Primary)
+            .expect("could not get pic state");
+
+        // set bits 0 and 1
+        state.irr = 3;
+
+        chip.set_pic_state(PicSelect::Primary, &state)
+            .expect("could not set the pic state");
+
+        let state = chip
+            .get_pic_state(PicSelect::Primary)
+            .expect("could not get pic state");
+
+        // Bits 1 and 0 should now be 1
+        assert_eq!(state.irr, 3);
+    }
+
+    #[test]
+    fn get_ioapic() {
+        let (chip, vm) = get_chip();
+
+        let state = chip.get_ioapic_state().expect("could not get ioapic state");
+
+        // Default is that no irq lines are asserted
+        assert_eq!(state.current_interrupt_level_bitmap, 0);
+
+        // Default routing entries has routes 0..24 routed to vectors 0..24
+        for i in 0..24 {
+            // when the ioapic is reset by kvm, it defaults to all zeroes except the
+            // interrupt mask is set to 1, which is bit 16
+            assert_eq!(state.redirect_table[i].get(0, 64), 1 << 16);
+        }
+
+        // Assert Irq Line 1
+        vm.set_irq_line(1, true).expect("could not set irq line");
+
+        let state = chip.get_ioapic_state().expect("could not get ioapic state");
+
+        // Bit 1 should now be 1
+        assert_eq!(state.current_interrupt_level_bitmap, 2);
+    }
+
+    #[test]
+    fn set_ioapic() {
+        let (mut chip, _) = get_chip();
+
+        let mut state = chip.get_ioapic_state().expect("could not get ioapic state");
+
+        // set a vector in the redirect table
+        state.redirect_table[2].set_vector(15);
+        // set the irq line status on that entry
+        state.current_interrupt_level_bitmap = 4;
+
+        chip.set_ioapic_state(&state)
+            .expect("could not set the ioapic state");
+
+        let state = chip.get_ioapic_state().expect("could not get ioapic state");
+
+        // verify that get_ioapic_state returns what we set
+        assert_eq!(state.redirect_table[2].get_vector(), 15);
+        assert_eq!(state.current_interrupt_level_bitmap, 4);
+    }
+
+    #[test]
+    fn get_pit() {
+        let (mut chip, _) = get_chip();
+        let mut io_bus = Bus::new();
+        chip.create_pit(&mut io_bus).expect("failed to create pit");
+
+        let state = chip.get_pit().expect("failed to get pit state");
+
+        assert_eq!(state.flags, 0);
+        // assert reset state of pit
+        for i in 0..3 {
+            // initial count of 0 sets it to 0x10000;
+            assert_eq!(state.channels[i].count, 0x10000);
+            assert_eq!(state.channels[i].mode, 0xff);
+            assert_eq!(state.channels[i].gate, i != 2);
+        }
+    }
+
+    #[test]
+    fn set_pit() {
+        let (mut chip, _) = get_chip();
+        let mut io_bus = Bus::new();
+        chip.create_pit(&mut io_bus).expect("failed to create pit");
+
+        let mut state = chip.get_pit().expect("failed to get pit state");
+
+        // set some values
+        state.channels[0].count = 500;
+        state.channels[0].mode = 1;
+
+        // Setting the pit should initialize the one-shot timer
+        chip.set_pit(&state).expect("failed to set pit state");
+
+        let state = chip.get_pit().expect("failed to get pit state");
+
+        // check the values we set
+        assert_eq!(state.channels[0].count, 500);
+        assert_eq!(state.channels[0].mode, 1);
+    }
+}