diff options
Diffstat (limited to 'target')
-rw-r--r-- | target/i386/cpu.c | 2 | ||||
-rw-r--r-- | target/i386/cpu.h | 22 | ||||
-rw-r--r-- | target/i386/kvm.c | 42 | ||||
-rw-r--r-- | target/i386/kvm_i386.h | 1 | ||||
-rw-r--r-- | target/i386/machine.c | 45 | ||||
-rw-r--r-- | target/i386/sev.c | 4 |
6 files changed, 61 insertions, 55 deletions
diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 805ce95..e3320f5 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1083,7 +1083,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, "md-clear", NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + NULL, NULL, NULL /* pconfig */, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "spec-ctrl", "stibp", NULL, "arch-capabilities", "core-capability", "ssbd", diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 05393cf..8b3dc55 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1877,6 +1877,28 @@ static inline bool cpu_has_vmx(CPUX86State *env) return env->features[FEAT_1_ECX] & CPUID_EXT_VMX; } +/* + * In order for a vCPU to enter VMX operation it must have CR4.VMXE set. + * Since it was set, CR4.VMXE must remain set as long as vCPU is in + * VMX operation. This is because CR4.VMXE is one of the bits set + * in MSR_IA32_VMX_CR4_FIXED1. + * + * There is one exception to above statement when vCPU enters SMM mode. + * When a vCPU enters SMM mode, it temporarily exit VMX operation and + * may also reset CR4.VMXE during execution in SMM mode. + * When vCPU exits SMM mode, vCPU state is restored to be in VMX operation + * and CR4.VMXE is restored to it's original value of being set. + * + * Therefore, when vCPU is not in SMM mode, we can infer whether + * VMX is being used by examining CR4.VMXE. Otherwise, we cannot + * know for certain. + */ +static inline bool cpu_vmx_maybe_enabled(CPUX86State *env) +{ + return cpu_has_vmx(env) && + ((env->cr[4] & CR4_VMXE_MASK) || (env->hflags & HF_SMM_MASK)); +} + /* fpu_helper.c */ void update_fp_status(CPUX86State *env); void update_mxcsr_status(CPUX86State *env); diff --git a/target/i386/kvm.c b/target/i386/kvm.c index ec7870c..ada89d2 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -128,6 +128,11 @@ bool kvm_has_adjust_clock_stable(void) return (ret == KVM_CLOCK_TSC_STABLE); } +bool kvm_has_exception_payload(void) +{ + return has_exception_payload; +} + bool kvm_allows_irq0_override(void) { return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing(); @@ -1342,7 +1347,6 @@ static int hyperv_init_vcpu(X86CPU *cpu) } static Error *invtsc_mig_blocker; -static Error *nested_virt_mig_blocker; #define KVM_MAX_CPUID_ENTRIES 100 @@ -1653,22 +1657,6 @@ int kvm_arch_init_vcpu(CPUState *cs) !!(c->ecx & CPUID_EXT_SMX); } - if (cpu_has_vmx(env) && !nested_virt_mig_blocker && - ((kvm_max_nested_state_length() <= 0) || !has_exception_payload)) { - error_setg(&nested_virt_mig_blocker, - "Kernel do not provide required capabilities for " - "nested virtualization migration. " - "(CAP_NESTED_STATE=%d, CAP_EXCEPTION_PAYLOAD=%d)", - kvm_max_nested_state_length() > 0, - has_exception_payload); - r = migrate_add_blocker(nested_virt_mig_blocker, &local_err); - if (local_err) { - error_report_err(local_err); - error_free(nested_virt_mig_blocker); - return r; - } - } - if (env->mcg_cap & MCG_LMCE_P) { has_msr_mcg_ext_ctl = has_msr_feature_control = true; } @@ -1683,7 +1671,7 @@ int kvm_arch_init_vcpu(CPUState *cs) if (local_err) { error_report_err(local_err); error_free(invtsc_mig_blocker); - goto fail2; + return r; } } } @@ -1723,15 +1711,15 @@ int kvm_arch_init_vcpu(CPUState *cs) max_nested_state_len = kvm_max_nested_state_length(); if (max_nested_state_len > 0) { assert(max_nested_state_len >= offsetof(struct kvm_nested_state, data)); - env->nested_state = g_malloc0(max_nested_state_len); - env->nested_state->size = max_nested_state_len; - - if (IS_INTEL_CPU(env)) { - struct kvm_vmx_nested_state_hdr *vmx_hdr = - &env->nested_state->hdr.vmx; + if (cpu_has_vmx(env)) { + struct kvm_vmx_nested_state_hdr *vmx_hdr; + env->nested_state = g_malloc0(max_nested_state_len); + env->nested_state->size = max_nested_state_len; env->nested_state->format = KVM_STATE_NESTED_FORMAT_VMX; + + vmx_hdr = &env->nested_state->hdr.vmx; vmx_hdr->vmxon_pa = -1ull; vmx_hdr->vmcs12_pa = -1ull; } @@ -1752,8 +1740,6 @@ int kvm_arch_init_vcpu(CPUState *cs) fail: migrate_del_blocker(invtsc_mig_blocker); - fail2: - migrate_del_blocker(nested_virt_mig_blocker); return r; } @@ -3529,7 +3515,7 @@ static int kvm_put_nested_state(X86CPU *cpu) CPUX86State *env = &cpu->env; int max_nested_state_len = kvm_max_nested_state_length(); - if (max_nested_state_len <= 0) { + if (!env->nested_state) { return 0; } @@ -3543,7 +3529,7 @@ static int kvm_get_nested_state(X86CPU *cpu) int max_nested_state_len = kvm_max_nested_state_length(); int ret; - if (max_nested_state_len <= 0) { + if (!env->nested_state) { return 0; } diff --git a/target/i386/kvm_i386.h b/target/i386/kvm_i386.h index 3057ba4..06fe06b 100644 --- a/target/i386/kvm_i386.h +++ b/target/i386/kvm_i386.h @@ -35,6 +35,7 @@ bool kvm_allows_irq0_override(void); bool kvm_has_smm(void); bool kvm_has_adjust_clock_stable(void); +bool kvm_has_exception_payload(void); void kvm_synchronize_all_tsc(void); void kvm_arch_reset_vcpu(X86CPU *cs); void kvm_arch_do_init_vcpu(X86CPU *cs); diff --git a/target/i386/machine.c b/target/i386/machine.c index 704ba6d..b114609 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -7,6 +7,7 @@ #include "hw/isa/isa.h" #include "migration/cpu.h" #include "hyperv.h" +#include "kvm_i386.h" #include "sysemu/kvm.h" #include "sysemu/tcg.h" @@ -232,10 +233,25 @@ static int cpu_pre_save(void *opaque) } #ifdef CONFIG_KVM - /* Verify we have nested virtualization state from kernel if required */ - if (kvm_enabled() && cpu_has_vmx(env) && !env->nested_state) { - error_report("Guest enabled nested virtualization but kernel " - "does not support saving of nested state"); + /* + * In case vCPU may have enabled VMX, we need to make sure kernel have + * required capabilities in order to perform migration correctly: + * + * 1) We must be able to extract vCPU nested-state from KVM. + * + * 2) In case vCPU is running in guest-mode and it has a pending exception, + * we must be able to determine if it's in a pending or injected state. + * Note that in case KVM don't have required capability to do so, + * a pending/injected exception will always appear as an + * injected exception. + */ + if (kvm_enabled() && cpu_vmx_maybe_enabled(env) && + (!env->nested_state || + (!kvm_has_exception_payload() && (env->hflags & HF_GUEST_MASK) && + env->exception_injected))) { + error_report("Guest maybe enabled nested virtualization but kernel " + "does not support required capabilities to save vCPU " + "nested state"); return -EINVAL; } #endif @@ -1019,31 +1035,13 @@ static const VMStateDescription vmstate_vmx_nested_state = { } }; -static bool svm_nested_state_needed(void *opaque) -{ - struct kvm_nested_state *nested_state = opaque; - - return (nested_state->format == KVM_STATE_NESTED_FORMAT_SVM); -} - -static const VMStateDescription vmstate_svm_nested_state = { - .name = "cpu/kvm_nested_state/svm", - .version_id = 1, - .minimum_version_id = 1, - .needed = svm_nested_state_needed, - .fields = (VMStateField[]) { - VMSTATE_END_OF_LIST() - } -}; - static bool nested_state_needed(void *opaque) { X86CPU *cpu = opaque; CPUX86State *env = &cpu->env; return (env->nested_state && - (vmx_nested_state_needed(env->nested_state) || - svm_nested_state_needed(env->nested_state))); + vmx_nested_state_needed(env->nested_state)); } static int nested_state_post_load(void *opaque, int version_id) @@ -1105,7 +1103,6 @@ static const VMStateDescription vmstate_kvm_nested_state = { }, .subsections = (const VMStateDescription*[]) { &vmstate_vmx_nested_state, - &vmstate_svm_nested_state, NULL } }; diff --git a/target/i386/sev.c b/target/i386/sev.c index 5ba1384..f1423cb 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -763,7 +763,7 @@ sev_guest_init(const char *id) "reduced-phys-bits", NULL); if (s->reduced_phys_bits < 1) { error_report("%s: reduced_phys_bits check failed, it should be >=1," - "' requested '%d'", __func__, s->reduced_phys_bits); + " requested '%d'", __func__, s->reduced_phys_bits); goto err; } @@ -783,7 +783,7 @@ sev_guest_init(const char *id) ret = sev_platform_ioctl(s->sev_fd, SEV_PLATFORM_STATUS, &status, &fw_error); if (ret) { - error_report("%s: failed to get platform status ret=%d" + error_report("%s: failed to get platform status ret=%d " "fw_error='%d: %s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); goto err; |