diff options
Diffstat (limited to 'hypervisor/src/kvm/x86_64.rs')
-rw-r--r-- | hypervisor/src/kvm/x86_64.rs | 238 |
1 files changed, 209 insertions, 29 deletions
diff --git a/hypervisor/src/kvm/x86_64.rs b/hypervisor/src/kvm/x86_64.rs index 06774f4..eaa34cf 100644 --- a/hypervisor/src/kvm/x86_64.rs +++ b/hypervisor/src/kvm/x86_64.rs @@ -4,14 +4,17 @@ use std::convert::TryInto; -use kvm_sys::*; use libc::E2BIG; -use sys_util::{ioctl_with_mut_ptr, Error, Result}; + +use kvm_sys::*; +use sys_util::{ + errno_result, ioctl_with_mut_ptr, ioctl_with_mut_ref, ioctl_with_ref, Error, Result, +}; use super::{Kvm, KvmVcpu, KvmVm}; use crate::{ - CpuId, CpuIdEntry, HypervisorX86_64, IoapicRedirectionTableEntry, IoapicState, LapicState, - PicState, PitChannelState, PitState, Regs, VcpuX86_64, VmX86_64, + ClockState, CpuId, CpuIdEntry, HypervisorX86_64, IoapicRedirectionTableEntry, IoapicState, + LapicState, PicSelect, PicState, PitChannelState, PitState, Regs, VcpuX86_64, VmX86_64, }; type KvmCpuId = kvm::CpuId; @@ -54,6 +57,180 @@ impl Kvm { } } +impl HypervisorX86_64 for Kvm { + fn get_supported_cpuid(&self) -> Result<CpuId> { + self.get_cpuid(KVM_GET_SUPPORTED_CPUID()) + } + + fn get_emulated_cpuid(&self) -> Result<CpuId> { + self.get_cpuid(KVM_GET_EMULATED_CPUID()) + } +} + +impl KvmVm { + /// Arch-specific implementation of `Vm::get_pvclock`. + pub fn get_pvclock_arch(&self) -> Result<ClockState> { + // Safe because we know that our file is a VM fd, we know the kernel will only write correct + // amount of memory to our pointer, and we verify the return result. + let mut clock_data: kvm_clock_data = unsafe { std::mem::zeroed() }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_CLOCK(), &mut clock_data) }; + if ret == 0 { + Ok(ClockState::from(clock_data)) + } else { + errno_result() + } + } + + /// Arch-specific implementation of `Vm::set_pvclock`. + pub fn set_pvclock_arch(&self, state: &ClockState) -> Result<()> { + let clock_data = kvm_clock_data::from(*state); + // Safe because we know that our file is a VM fd, we know the kernel will only read correct + // amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_CLOCK(), &clock_data) }; + if ret == 0 { + Ok(()) + } else { + errno_result() + } + } + + /// Retrieves the state of given interrupt controller by issuing KVM_GET_IRQCHIP ioctl. + /// + /// Note that this call can only succeed after a call to `Vm::create_irq_chip`. + pub fn get_pic_state(&self, id: PicSelect) -> Result<kvm_pic_state> { + let mut irqchip_state = kvm_irqchip::default(); + irqchip_state.chip_id = id as u32; + let ret = unsafe { + // Safe because we know our file is a VM fd, we know the kernel will only write + // correct amount of memory to our pointer, and we verify the return result. + ioctl_with_mut_ref(self, KVM_GET_IRQCHIP(), &mut irqchip_state) + }; + if ret == 0 { + Ok(unsafe { + // Safe as we know that we are retrieving data related to the + // PIC (primary or secondary) and not IOAPIC. + irqchip_state.chip.pic + }) + } else { + errno_result() + } + } + + /// Sets the state of given interrupt controller by issuing KVM_SET_IRQCHIP ioctl. + /// + /// Note that this call can only succeed after a call to `Vm::create_irq_chip`. + pub fn set_pic_state(&self, id: PicSelect, state: &kvm_pic_state) -> Result<()> { + let mut irqchip_state = kvm_irqchip::default(); + irqchip_state.chip_id = id as u32; + irqchip_state.chip.pic = *state; + // Safe because we know that our file is a VM fd, we know the kernel will only read + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_IRQCHIP(), &irqchip_state) }; + if ret == 0 { + Ok(()) + } else { + errno_result() + } + } + + /// Retrieves the state of IOAPIC by issuing KVM_GET_IRQCHIP ioctl. + /// + /// Note that this call can only succeed after a call to `Vm::create_irq_chip`. + pub fn get_ioapic_state(&self) -> Result<kvm_ioapic_state> { + let mut irqchip_state = kvm_irqchip::default(); + irqchip_state.chip_id = 2; + let ret = unsafe { + // Safe because we know our file is a VM fd, we know the kernel will only write + // correct amount of memory to our pointer, and we verify the return result. + ioctl_with_mut_ref(self, KVM_GET_IRQCHIP(), &mut irqchip_state) + }; + if ret == 0 { + Ok(unsafe { + // Safe as we know that we are retrieving data related to the + // IOAPIC and not PIC. + irqchip_state.chip.ioapic + }) + } else { + errno_result() + } + } + + /// Sets the state of IOAPIC by issuing KVM_SET_IRQCHIP ioctl. + /// + /// Note that this call can only succeed after a call to `Vm::create_irq_chip`. + pub fn set_ioapic_state(&self, state: &kvm_ioapic_state) -> Result<()> { + let mut irqchip_state = kvm_irqchip::default(); + irqchip_state.chip_id = 2; + irqchip_state.chip.ioapic = *state; + // Safe because we know that our file is a VM fd, we know the kernel will only read + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_IRQCHIP(), &irqchip_state) }; + if ret == 0 { + Ok(()) + } else { + errno_result() + } + } + + /// Creates a PIT as per the KVM_CREATE_PIT2 ioctl. + /// + /// Note that this call can only succeed after a call to `Vm::create_irq_chip`. + pub fn create_pit(&self) -> Result<()> { + let pit_config = kvm_pit_config::default(); + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_PIT2(), &pit_config) }; + if ret == 0 { + Ok(()) + } else { + errno_result() + } + } + + /// Retrieves the state of PIT by issuing KVM_GET_PIT2 ioctl. + /// + /// Note that this call can only succeed after a call to `Vm::create_pit`. + pub fn get_pit_state(&self) -> Result<kvm_pit_state2> { + // Safe because we know that our file is a VM fd, we know the kernel will only write + // correct amount of memory to our pointer, and we verify the return result. + let mut pit_state = unsafe { std::mem::zeroed() }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_PIT2(), &mut pit_state) }; + if ret == 0 { + Ok(pit_state) + } else { + errno_result() + } + } + + /// Sets the state of PIT by issuing KVM_SET_PIT2 ioctl. + /// + /// Note that this call can only succeed after a call to `Vm::create_pit`. + pub fn set_pit_state(&self, pit_state: &kvm_pit_state2) -> Result<()> { + // Safe because we know that our file is a VM fd, we know the kernel will only read + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_PIT2(), pit_state) }; + if ret == 0 { + Ok(()) + } else { + errno_result() + } + } +} + +impl VmX86_64 for KvmVm { + type Vcpu = KvmVcpu; + + fn create_vcpu(&self, id: usize) -> Result<Self::Vcpu> { + self.create_kvm_vcpu(id) + } +} + +impl VcpuX86_64 for KvmVcpu { + fn get_regs(&self) -> Result<Regs> { + Ok(Regs {}) + } +} + impl<'a> From<&'a KvmCpuId> for CpuId { fn from(kvm_cpuid: &'a KvmCpuId) -> CpuId { let kvm_entries = kvm_cpuid.entries_slice(); @@ -74,27 +251,22 @@ impl<'a> From<&'a KvmCpuId> for CpuId { } } -impl HypervisorX86_64 for Kvm { - fn get_supported_cpuid(&self) -> Result<CpuId> { - self.get_cpuid(KVM_GET_SUPPORTED_CPUID()) - } - - fn get_emulated_cpuid(&self) -> Result<CpuId> { - self.get_cpuid(KVM_GET_EMULATED_CPUID()) - } -} - -impl VmX86_64 for KvmVm { - type Vcpu = KvmVcpu; - - fn create_vcpu(&self, id: usize) -> Result<Self::Vcpu> { - self.create_kvm_vcpu(id) +impl From<ClockState> for kvm_clock_data { + fn from(state: ClockState) -> Self { + kvm_clock_data { + clock: state.clock, + flags: state.flags, + ..Default::default() + } } } -impl VcpuX86_64 for KvmVcpu { - fn get_regs(&self) -> Result<Regs> { - Ok(Regs {}) +impl From<kvm_clock_data> for ClockState { + fn from(clock_data: kvm_clock_data) -> Self { + ClockState { + clock: clock_data.clock, + flags: clock_data.flags, + } } } @@ -305,15 +477,13 @@ impl From<&kvm_pit_channel_state> for PitChannelState { #[cfg(test)] mod tests { + use super::*; use crate::{ - DeliveryMode, DeliveryStatus, DestinationMode, IoapicRedirectionTableEntry, IoapicState, - LapicState, PicInitState, PicState, PitChannelState, PitRWMode, PitRWState, PitState, - TriggerMode, + DeliveryMode, DeliveryStatus, DestinationMode, HypervisorX86_64, + IoapicRedirectionTableEntry, IoapicState, LapicState, PicInitState, PicState, + PitChannelState, PitRWMode, PitRWState, PitState, TriggerMode, Vm, }; - use kvm_sys::*; - - use super::Kvm; - use crate::HypervisorX86_64; + use sys_util::{GuestAddress, GuestMemory}; #[test] fn get_supported_cpuid() { @@ -501,4 +671,14 @@ mod tests { // convert back and compare assert_eq!(state, PitState::from(&kvm_state)); } + + #[test] + fn clock_handling() { + let kvm = Kvm::new().unwrap(); + let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap(); + let vm = KvmVm::new(&kvm, gm).unwrap(); + let mut clock_data = vm.get_pvclock().unwrap(); + clock_data.clock += 1000; + vm.set_pvclock(&clock_data).unwrap(); + } } |