mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
As per this kernel documentation:
For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_PAPR, KVM_EXIT_XEN,
KVM_EXIT_EPR, KVM_EXIT_X86_RDMSR and KVM_EXIT_X86_WRMSR the corresponding
operations are complete (and guest state is consistent) only after userspace
has re-entered the kernel with KVM_RUN. The kernel side will first finish
incomplete operations and then check for pending signals.
The pending state of the operation is not preserved in state which is
visible to userspace, thus userspace should ensure that the operation is
completed before performing a live migration. Userspace can re-enter the
guest with an unmasked signal pending or with the immediate_exit field set
to complete pending operations without allowing any further instructions
to be executed.
Since we capture the state as part of the pause and override it as part
of the resume we must ensure the state is consistent otherwise we will
lose the results of the MMIO or PIO operation that caused the exit from
which we paused.
Fixes: #3658
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
151 lines
4.0 KiB
Rust
151 lines
4.0 KiB
Rust
// Copyright © 2019 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
|
//
|
|
// Copyright © 2020, Microsoft Corporation
|
|
//
|
|
// Copyright 2018-2019 CrowdStrike, Inc.
|
|
//
|
|
//
|
|
|
|
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
|
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
|
use serde_derive::{Deserialize, Serialize};
|
|
|
|
///
|
|
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
|
///
|
|
pub use {
|
|
kvm_bindings::kvm_cpuid_entry2 as CpuIdEntry, kvm_bindings::kvm_dtable as DescriptorTable,
|
|
kvm_bindings::kvm_fpu as FpuState, kvm_bindings::kvm_lapic_state as LapicState,
|
|
kvm_bindings::kvm_mp_state as MpState, kvm_bindings::kvm_msr_entry as MsrEntry,
|
|
kvm_bindings::kvm_regs as StandardRegisters, kvm_bindings::kvm_segment as SegmentRegister,
|
|
kvm_bindings::kvm_sregs as SpecialRegisters, kvm_bindings::kvm_vcpu_events as VcpuEvents,
|
|
kvm_bindings::kvm_xcrs as ExtendedControlRegisters, kvm_bindings::kvm_xsave as Xsave,
|
|
kvm_bindings::CpuId, kvm_bindings::MsrList, kvm_bindings::Msrs as MsrEntries,
|
|
kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX as CPUID_FLAG_VALID_INDEX,
|
|
};
|
|
|
|
impl SegmentRegisterOps for SegmentRegister {
|
|
fn segment_type(&self) -> u8 {
|
|
self.type_
|
|
}
|
|
fn set_segment_type(&mut self, val: u8) {
|
|
self.type_ = val;
|
|
}
|
|
|
|
fn dpl(&self) -> u8 {
|
|
self.dpl
|
|
}
|
|
|
|
fn set_dpl(&mut self, val: u8) {
|
|
self.dpl = val;
|
|
}
|
|
|
|
fn present(&self) -> u8 {
|
|
self.present
|
|
}
|
|
|
|
fn set_present(&mut self, val: u8) {
|
|
self.present = val;
|
|
}
|
|
|
|
fn long(&self) -> u8 {
|
|
self.l
|
|
}
|
|
|
|
fn set_long(&mut self, val: u8) {
|
|
self.l = val;
|
|
}
|
|
|
|
fn avl(&self) -> u8 {
|
|
self.avl
|
|
}
|
|
|
|
fn set_avl(&mut self, val: u8) {
|
|
self.avl = val;
|
|
}
|
|
|
|
fn desc_type(&self) -> u8 {
|
|
self.s
|
|
}
|
|
|
|
fn set_desc_type(&mut self, val: u8) {
|
|
self.s = val;
|
|
}
|
|
|
|
fn granularity(&self) -> u8 {
|
|
self.g
|
|
}
|
|
|
|
fn set_granularity(&mut self, val: u8) {
|
|
self.g = val;
|
|
}
|
|
|
|
fn db(&self) -> u8 {
|
|
self.db
|
|
}
|
|
|
|
fn set_db(&mut self, val: u8) {
|
|
self.db = val;
|
|
}
|
|
}
|
|
|
|
pub fn boot_msr_entries() -> MsrEntries {
|
|
MsrEntries::from_entries(&[
|
|
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
|
msr!(msr_index::MSR_IA32_SYSENTER_ESP),
|
|
msr!(msr_index::MSR_IA32_SYSENTER_EIP),
|
|
msr!(msr_index::MSR_STAR),
|
|
msr!(msr_index::MSR_CSTAR),
|
|
msr!(msr_index::MSR_LSTAR),
|
|
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
|
msr!(msr_index::MSR_SYSCALL_MASK),
|
|
msr!(msr_index::MSR_IA32_TSC),
|
|
msr_data!(
|
|
msr_index::MSR_IA32_MISC_ENABLE,
|
|
msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64
|
|
),
|
|
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
|
])
|
|
.unwrap()
|
|
}
|
|
|
|
///
|
|
/// Check KVM extension for Linux
|
|
///
|
|
pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
|
if !kvm.check_extension(Cap::SignalMsi) {
|
|
return Err(KvmError::CapabilityMissing(Cap::SignalMsi));
|
|
}
|
|
if !kvm.check_extension(Cap::TscDeadlineTimer) {
|
|
return Err(KvmError::CapabilityMissing(Cap::TscDeadlineTimer));
|
|
}
|
|
if !kvm.check_extension(Cap::SplitIrqchip) {
|
|
return Err(KvmError::CapabilityMissing(Cap::SplitIrqchip));
|
|
}
|
|
if !kvm.check_extension(Cap::SetIdentityMapAddr) {
|
|
return Err(KvmError::CapabilityMissing(Cap::SetIdentityMapAddr));
|
|
}
|
|
if !kvm.check_extension(Cap::SetTssAddr) {
|
|
return Err(KvmError::CapabilityMissing(Cap::SetTssAddr));
|
|
}
|
|
if !kvm.check_extension(Cap::ImmediateExit) {
|
|
return Err(KvmError::CapabilityMissing(Cap::ImmediateExit));
|
|
}
|
|
Ok(())
|
|
}
|
|
#[derive(Clone, Serialize, Deserialize)]
|
|
pub struct VcpuKvmState {
|
|
pub cpuid: CpuId,
|
|
pub msrs: MsrEntries,
|
|
pub vcpu_events: VcpuEvents,
|
|
pub regs: StandardRegisters,
|
|
pub sregs: SpecialRegisters,
|
|
pub fpu: FpuState,
|
|
pub lapic_state: LapicState,
|
|
pub xsave: Xsave,
|
|
pub xcrs: ExtendedControlRegisters,
|
|
pub mp_state: MpState,
|
|
}
|