diff options
Diffstat (limited to 'devices/src/irqchip/kvm/x86_64.rs')
-rw-r--r-- | devices/src/irqchip/kvm/x86_64.rs | 216 |
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); + } +} |