aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/i386/cpu.c2
-rw-r--r--target/i386/cpu.h22
-rw-r--r--target/i386/kvm.c42
-rw-r--r--target/i386/kvm_i386.h1
-rw-r--r--target/i386/machine.c45
-rw-r--r--target/i386/sev.c4
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;