summary refs log tree commit diff
path: root/hypervisor/src/kvm/x86_64.rs
diff options
context:
space:
mode:
Diffstat (limited to 'hypervisor/src/kvm/x86_64.rs')
-rw-r--r--hypervisor/src/kvm/x86_64.rs238
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();
+    }
 }