|
|
b38b0f |
From 05a54f3fc44598f917d72a1f2570c43ec042cdb8 Mon Sep 17 00:00:00 2001
|
|
|
b38b0f |
From: Paolo Bonzini <pbonzini@redhat.com>
|
|
|
b38b0f |
Date: Mon, 22 Jul 2019 18:22:16 +0100
|
|
|
b38b0f |
Subject: [PATCH 35/39] target/i386: kvm: Add support for
|
|
|
b38b0f |
KVM_CAP_EXCEPTION_PAYLOAD
|
|
|
b38b0f |
|
|
|
b38b0f |
RH-Author: Paolo Bonzini <pbonzini@redhat.com>
|
|
|
b38b0f |
Message-id: <20190722182220.19374-15-pbonzini@redhat.com>
|
|
|
b38b0f |
Patchwork-id: 89631
|
|
|
b38b0f |
O-Subject: [RHEL-8.1.0 PATCH qemu-kvm v3 14/18] target/i386: kvm: Add support for KVM_CAP_EXCEPTION_PAYLOAD
|
|
|
b38b0f |
Bugzilla: 1689269
|
|
|
b38b0f |
RH-Acked-by: Peter Xu <zhexu@redhat.com>
|
|
|
b38b0f |
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
|
|
|
b38b0f |
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
|
|
b38b0f |
|
|
|
b38b0f |
From: Liran Alon <liran.alon@oracle.com>
|
|
|
b38b0f |
|
|
|
b38b0f |
Kernel commit c4f55198c7c2 ("kvm: x86: Introduce KVM_CAP_EXCEPTION_PAYLOAD")
|
|
|
b38b0f |
introduced a new KVM capability which allows userspace to correctly
|
|
|
b38b0f |
distinguish between pending and injected exceptions.
|
|
|
b38b0f |
|
|
|
b38b0f |
This distinguish is important in case of nested virtualization scenarios
|
|
|
b38b0f |
because a L2 pending exception can still be intercepted by the L1 hypervisor
|
|
|
b38b0f |
while a L2 injected exception cannot.
|
|
|
b38b0f |
|
|
|
b38b0f |
Furthermore, when an exception is attempted to be injected by QEMU,
|
|
|
b38b0f |
QEMU should specify the exception payload (CR2 in case of #PF or
|
|
|
b38b0f |
DR6 in case of #DB) instead of having the payload already delivered in
|
|
|
b38b0f |
the respective vCPU register. Because in case exception is injected to
|
|
|
b38b0f |
L2 guest and is intercepted by L1 hypervisor, then payload needs to be
|
|
|
b38b0f |
reported to L1 intercept (VMExit handler) while still preserving
|
|
|
b38b0f |
respective vCPU register unchanged.
|
|
|
b38b0f |
|
|
|
b38b0f |
This commit adds support for QEMU to properly utilise this new KVM
|
|
|
b38b0f |
capability (KVM_CAP_EXCEPTION_PAYLOAD).
|
|
|
b38b0f |
|
|
|
b38b0f |
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
|
|
|
b38b0f |
Signed-off-by: Liran Alon <liran.alon@oracle.com>
|
|
|
b38b0f |
Message-Id: <20190619162140.133674-10-liran.alon@oracle.com>
|
|
|
b38b0f |
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
|
|
b38b0f |
(cherry picked from commit fd13f23b8c95311eff74426921557eee592b0ed3)
|
|
|
b38b0f |
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
|
|
|
b38b0f |
---
|
|
|
b38b0f |
target/i386/cpu.c | 6 ++-
|
|
|
b38b0f |
target/i386/cpu.h | 6 ++-
|
|
|
b38b0f |
target/i386/hvf/hvf.c | 10 +++--
|
|
|
b38b0f |
target/i386/hvf/x86hvf.c | 4 +-
|
|
|
b38b0f |
target/i386/kvm.c | 101 +++++++++++++++++++++++++++++++++++++++--------
|
|
|
b38b0f |
target/i386/machine.c | 84 ++++++++++++++++++++++++++++++++++++++-
|
|
|
b38b0f |
6 files changed, 187 insertions(+), 24 deletions(-)
|
|
|
b38b0f |
|
|
|
b38b0f |
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
|
|
|
b38b0f |
index bd0b784..f71b044 100644
|
|
|
b38b0f |
--- a/target/i386/cpu.c
|
|
|
b38b0f |
+++ b/target/i386/cpu.c
|
|
|
b38b0f |
@@ -4645,7 +4645,11 @@ static void x86_cpu_reset(CPUState *s)
|
|
|
b38b0f |
memset(env->mtrr_fixed, 0, sizeof(env->mtrr_fixed));
|
|
|
b38b0f |
|
|
|
b38b0f |
env->interrupt_injected = -1;
|
|
|
b38b0f |
- env->exception_injected = -1;
|
|
|
b38b0f |
+ env->exception_nr = -1;
|
|
|
b38b0f |
+ env->exception_pending = 0;
|
|
|
b38b0f |
+ env->exception_injected = 0;
|
|
|
b38b0f |
+ env->exception_has_payload = false;
|
|
|
b38b0f |
+ env->exception_payload = 0;
|
|
|
b38b0f |
env->nmi_injected = false;
|
|
|
b38b0f |
#if !defined(CONFIG_USER_ONLY)
|
|
|
b38b0f |
/* We hard-wire the BSP to the first CPU. */
|
|
|
b38b0f |
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
|
|
|
b38b0f |
index 86f3d98..d120f62 100644
|
|
|
b38b0f |
--- a/target/i386/cpu.h
|
|
|
b38b0f |
+++ b/target/i386/cpu.h
|
|
|
b38b0f |
@@ -1325,10 +1325,14 @@ typedef struct CPUX86State {
|
|
|
b38b0f |
|
|
|
b38b0f |
/* For KVM */
|
|
|
b38b0f |
uint32_t mp_state;
|
|
|
b38b0f |
- int32_t exception_injected;
|
|
|
b38b0f |
+ int32_t exception_nr;
|
|
|
b38b0f |
int32_t interrupt_injected;
|
|
|
b38b0f |
uint8_t soft_interrupt;
|
|
|
b38b0f |
+ uint8_t exception_pending;
|
|
|
b38b0f |
+ uint8_t exception_injected;
|
|
|
b38b0f |
uint8_t has_error_code;
|
|
|
b38b0f |
+ uint8_t exception_has_payload;
|
|
|
b38b0f |
+ uint64_t exception_payload;
|
|
|
b38b0f |
uint32_t ins_len;
|
|
|
b38b0f |
uint32_t sipi_vector;
|
|
|
b38b0f |
bool tsc_valid;
|
|
|
b38b0f |
diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c
|
|
|
b38b0f |
index c367539..acc0bb9 100644
|
|
|
b38b0f |
--- a/target/i386/hvf/hvf.c
|
|
|
b38b0f |
+++ b/target/i386/hvf/hvf.c
|
|
|
b38b0f |
@@ -617,7 +617,9 @@ static void hvf_store_events(CPUState *cpu, uint32_t ins_len, uint64_t idtvec_in
|
|
|
b38b0f |
X86CPU *x86_cpu = X86_CPU(cpu);
|
|
|
b38b0f |
CPUX86State *env = &x86_cpu->env;
|
|
|
b38b0f |
|
|
|
b38b0f |
- env->exception_injected = -1;
|
|
|
b38b0f |
+ env->exception_nr = -1;
|
|
|
b38b0f |
+ env->exception_pending = 0;
|
|
|
b38b0f |
+ env->exception_injected = 0;
|
|
|
b38b0f |
env->interrupt_injected = -1;
|
|
|
b38b0f |
env->nmi_injected = false;
|
|
|
b38b0f |
if (idtvec_info & VMCS_IDT_VEC_VALID) {
|
|
|
b38b0f |
@@ -631,7 +633,8 @@ static void hvf_store_events(CPUState *cpu, uint32_t ins_len, uint64_t idtvec_in
|
|
|
b38b0f |
break;
|
|
|
b38b0f |
case VMCS_IDT_VEC_HWEXCEPTION:
|
|
|
b38b0f |
case VMCS_IDT_VEC_SWEXCEPTION:
|
|
|
b38b0f |
- env->exception_injected = idtvec_info & VMCS_IDT_VEC_VECNUM;
|
|
|
b38b0f |
+ env->exception_nr = idtvec_info & VMCS_IDT_VEC_VECNUM;
|
|
|
b38b0f |
+ env->exception_injected = 1;
|
|
|
b38b0f |
break;
|
|
|
b38b0f |
case VMCS_IDT_VEC_PRIV_SWEXCEPTION:
|
|
|
b38b0f |
default:
|
|
|
b38b0f |
@@ -925,7 +928,8 @@ int hvf_vcpu_exec(CPUState *cpu)
|
|
|
b38b0f |
macvm_set_rip(cpu, rip + ins_len);
|
|
|
b38b0f |
break;
|
|
|
b38b0f |
case VMX_REASON_VMCALL:
|
|
|
b38b0f |
- env->exception_injected = EXCP0D_GPF;
|
|
|
b38b0f |
+ env->exception_nr = EXCP0D_GPF;
|
|
|
b38b0f |
+ env->exception_injected = 1;
|
|
|
b38b0f |
env->has_error_code = true;
|
|
|
b38b0f |
env->error_code = 0;
|
|
|
b38b0f |
break;
|
|
|
b38b0f |
diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c
|
|
|
b38b0f |
index 6c88939..f0e58a8 100644
|
|
|
b38b0f |
--- a/target/i386/hvf/x86hvf.c
|
|
|
b38b0f |
+++ b/target/i386/hvf/x86hvf.c
|
|
|
b38b0f |
@@ -362,8 +362,8 @@ bool hvf_inject_interrupts(CPUState *cpu_state)
|
|
|
b38b0f |
if (env->interrupt_injected != -1) {
|
|
|
b38b0f |
vector = env->interrupt_injected;
|
|
|
b38b0f |
intr_type = VMCS_INTR_T_SWINTR;
|
|
|
b38b0f |
- } else if (env->exception_injected != -1) {
|
|
|
b38b0f |
- vector = env->exception_injected;
|
|
|
b38b0f |
+ } else if (env->exception_nr != -1) {
|
|
|
b38b0f |
+ vector = env->exception_nr;
|
|
|
b38b0f |
if (vector == EXCP03_INT3 || vector == EXCP04_INTO) {
|
|
|
b38b0f |
intr_type = VMCS_INTR_T_SWEXCEPTION;
|
|
|
b38b0f |
} else {
|
|
|
b38b0f |
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
|
|
|
b38b0f |
index ddceb7d..aa2d589 100644
|
|
|
b38b0f |
--- a/target/i386/kvm.c
|
|
|
b38b0f |
+++ b/target/i386/kvm.c
|
|
|
b38b0f |
@@ -103,6 +103,7 @@ static uint32_t num_architectural_pmu_fixed_counters;
|
|
|
b38b0f |
static int has_xsave;
|
|
|
b38b0f |
static int has_xcrs;
|
|
|
b38b0f |
static int has_pit_state2;
|
|
|
b38b0f |
+static int has_exception_payload;
|
|
|
b38b0f |
|
|
|
b38b0f |
static bool has_msr_mcg_ext_ctl;
|
|
|
b38b0f |
|
|
|
b38b0f |
@@ -569,15 +570,56 @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr)
|
|
|
b38b0f |
/* Hope we are lucky for AO MCE */
|
|
|
b38b0f |
}
|
|
|
b38b0f |
|
|
|
b38b0f |
+static void kvm_reset_exception(CPUX86State *env)
|
|
|
b38b0f |
+{
|
|
|
b38b0f |
+ env->exception_nr = -1;
|
|
|
b38b0f |
+ env->exception_pending = 0;
|
|
|
b38b0f |
+ env->exception_injected = 0;
|
|
|
b38b0f |
+ env->exception_has_payload = false;
|
|
|
b38b0f |
+ env->exception_payload = 0;
|
|
|
b38b0f |
+}
|
|
|
b38b0f |
+
|
|
|
b38b0f |
+static void kvm_queue_exception(CPUX86State *env,
|
|
|
b38b0f |
+ int32_t exception_nr,
|
|
|
b38b0f |
+ uint8_t exception_has_payload,
|
|
|
b38b0f |
+ uint64_t exception_payload)
|
|
|
b38b0f |
+{
|
|
|
b38b0f |
+ assert(env->exception_nr == -1);
|
|
|
b38b0f |
+ assert(!env->exception_pending);
|
|
|
b38b0f |
+ assert(!env->exception_injected);
|
|
|
b38b0f |
+ assert(!env->exception_has_payload);
|
|
|
b38b0f |
+
|
|
|
b38b0f |
+ env->exception_nr = exception_nr;
|
|
|
b38b0f |
+
|
|
|
b38b0f |
+ if (has_exception_payload) {
|
|
|
b38b0f |
+ env->exception_pending = 1;
|
|
|
b38b0f |
+
|
|
|
b38b0f |
+ env->exception_has_payload = exception_has_payload;
|
|
|
b38b0f |
+ env->exception_payload = exception_payload;
|
|
|
b38b0f |
+ } else {
|
|
|
b38b0f |
+ env->exception_injected = 1;
|
|
|
b38b0f |
+
|
|
|
b38b0f |
+ if (exception_nr == EXCP01_DB) {
|
|
|
b38b0f |
+ assert(exception_has_payload);
|
|
|
b38b0f |
+ env->dr[6] = exception_payload;
|
|
|
b38b0f |
+ } else if (exception_nr == EXCP0E_PAGE) {
|
|
|
b38b0f |
+ assert(exception_has_payload);
|
|
|
b38b0f |
+ env->cr[2] = exception_payload;
|
|
|
b38b0f |
+ } else {
|
|
|
b38b0f |
+ assert(!exception_has_payload);
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+}
|
|
|
b38b0f |
+
|
|
|
b38b0f |
static int kvm_inject_mce_oldstyle(X86CPU *cpu)
|
|
|
b38b0f |
{
|
|
|
b38b0f |
CPUX86State *env = &cpu->env;
|
|
|
b38b0f |
|
|
|
b38b0f |
- if (!kvm_has_vcpu_events() && env->exception_injected == EXCP12_MCHK) {
|
|
|
b38b0f |
+ if (!kvm_has_vcpu_events() && env->exception_nr == EXCP12_MCHK) {
|
|
|
b38b0f |
unsigned int bank, bank_num = env->mcg_cap & 0xff;
|
|
|
b38b0f |
struct kvm_x86_mce mce;
|
|
|
b38b0f |
|
|
|
b38b0f |
- env->exception_injected = -1;
|
|
|
b38b0f |
+ kvm_reset_exception(env);
|
|
|
b38b0f |
|
|
|
b38b0f |
/*
|
|
|
b38b0f |
* There must be at least one bank in use if an MCE is pending.
|
|
|
b38b0f |
@@ -1458,6 +1500,16 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
|
|
|
b38b0f |
has_pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2);
|
|
|
b38b0f |
#endif
|
|
|
b38b0f |
|
|
|
b38b0f |
+ has_exception_payload = kvm_check_extension(s, KVM_CAP_EXCEPTION_PAYLOAD);
|
|
|
b38b0f |
+ if (has_exception_payload) {
|
|
|
b38b0f |
+ ret = kvm_vm_enable_cap(s, KVM_CAP_EXCEPTION_PAYLOAD, 0, true);
|
|
|
b38b0f |
+ if (ret < 0) {
|
|
|
b38b0f |
+ error_report("kvm: Failed to enable exception payload cap: %s",
|
|
|
b38b0f |
+ strerror(-ret));
|
|
|
b38b0f |
+ return ret;
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+
|
|
|
b38b0f |
ret = kvm_get_supported_msrs(s);
|
|
|
b38b0f |
if (ret < 0) {
|
|
|
b38b0f |
return ret;
|
|
|
b38b0f |
@@ -2717,8 +2769,16 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level)
|
|
|
b38b0f |
return 0;
|
|
|
b38b0f |
}
|
|
|
b38b0f |
|
|
|
b38b0f |
- events.exception.injected = (env->exception_injected >= 0);
|
|
|
b38b0f |
- events.exception.nr = env->exception_injected;
|
|
|
b38b0f |
+ events.flags = 0;
|
|
|
b38b0f |
+
|
|
|
b38b0f |
+ if (has_exception_payload) {
|
|
|
b38b0f |
+ events.flags |= KVM_VCPUEVENT_VALID_PAYLOAD;
|
|
|
b38b0f |
+ events.exception.pending = env->exception_pending;
|
|
|
b38b0f |
+ events.exception_has_payload = env->exception_has_payload;
|
|
|
b38b0f |
+ events.exception_payload = env->exception_payload;
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+ events.exception.nr = env->exception_nr;
|
|
|
b38b0f |
+ events.exception.injected = env->exception_injected;
|
|
|
b38b0f |
events.exception.has_error_code = env->has_error_code;
|
|
|
b38b0f |
events.exception.error_code = env->error_code;
|
|
|
b38b0f |
|
|
|
b38b0f |
@@ -2731,7 +2791,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level)
|
|
|
b38b0f |
events.nmi.masked = !!(env->hflags2 & HF2_NMI_MASK);
|
|
|
b38b0f |
|
|
|
b38b0f |
events.sipi_vector = env->sipi_vector;
|
|
|
b38b0f |
- events.flags = 0;
|
|
|
b38b0f |
|
|
|
b38b0f |
if (has_msr_smbase) {
|
|
|
b38b0f |
events.smi.smm = !!(env->hflags & HF_SMM_MASK);
|
|
|
b38b0f |
@@ -2781,8 +2840,19 @@ static int kvm_get_vcpu_events(X86CPU *cpu)
|
|
|
b38b0f |
if (ret < 0) {
|
|
|
b38b0f |
return ret;
|
|
|
b38b0f |
}
|
|
|
b38b0f |
- env->exception_injected =
|
|
|
b38b0f |
- events.exception.injected ? events.exception.nr : -1;
|
|
|
b38b0f |
+
|
|
|
b38b0f |
+ if (events.flags & KVM_VCPUEVENT_VALID_PAYLOAD) {
|
|
|
b38b0f |
+ env->exception_pending = events.exception.pending;
|
|
|
b38b0f |
+ env->exception_has_payload = events.exception_has_payload;
|
|
|
b38b0f |
+ env->exception_payload = events.exception_payload;
|
|
|
b38b0f |
+ } else {
|
|
|
b38b0f |
+ env->exception_pending = 0;
|
|
|
b38b0f |
+ env->exception_has_payload = false;
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+ env->exception_injected = events.exception.injected;
|
|
|
b38b0f |
+ env->exception_nr =
|
|
|
b38b0f |
+ (env->exception_pending || env->exception_injected) ?
|
|
|
b38b0f |
+ events.exception.nr : -1;
|
|
|
b38b0f |
env->has_error_code = events.exception.has_error_code;
|
|
|
b38b0f |
env->error_code = events.exception.error_code;
|
|
|
b38b0f |
|
|
|
b38b0f |
@@ -2834,12 +2904,12 @@ static int kvm_guest_debug_workarounds(X86CPU *cpu)
|
|
|
b38b0f |
unsigned long reinject_trap = 0;
|
|
|
b38b0f |
|
|
|
b38b0f |
if (!kvm_has_vcpu_events()) {
|
|
|
b38b0f |
- if (env->exception_injected == EXCP01_DB) {
|
|
|
b38b0f |
+ if (env->exception_nr == EXCP01_DB) {
|
|
|
b38b0f |
reinject_trap = KVM_GUESTDBG_INJECT_DB;
|
|
|
b38b0f |
} else if (env->exception_injected == EXCP03_INT3) {
|
|
|
b38b0f |
reinject_trap = KVM_GUESTDBG_INJECT_BP;
|
|
|
b38b0f |
}
|
|
|
b38b0f |
- env->exception_injected = -1;
|
|
|
b38b0f |
+ kvm_reset_exception(env);
|
|
|
b38b0f |
}
|
|
|
b38b0f |
|
|
|
b38b0f |
/*
|
|
|
b38b0f |
@@ -3215,13 +3285,13 @@ int kvm_arch_process_async_events(CPUState *cs)
|
|
|
b38b0f |
|
|
|
b38b0f |
kvm_cpu_synchronize_state(cs);
|
|
|
b38b0f |
|
|
|
b38b0f |
- if (env->exception_injected == EXCP08_DBLE) {
|
|
|
b38b0f |
+ if (env->exception_nr == EXCP08_DBLE) {
|
|
|
b38b0f |
/* this means triple fault */
|
|
|
b38b0f |
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
|
|
b38b0f |
cs->exit_request = 1;
|
|
|
b38b0f |
return 0;
|
|
|
b38b0f |
}
|
|
|
b38b0f |
- env->exception_injected = EXCP12_MCHK;
|
|
|
b38b0f |
+ kvm_queue_exception(env, EXCP12_MCHK, 0, 0);
|
|
|
b38b0f |
env->has_error_code = 0;
|
|
|
b38b0f |
|
|
|
b38b0f |
cs->halted = 0;
|
|
|
b38b0f |
@@ -3436,14 +3506,13 @@ static int kvm_handle_debug(X86CPU *cpu,
|
|
|
b38b0f |
}
|
|
|
b38b0f |
if (ret == 0) {
|
|
|
b38b0f |
cpu_synchronize_state(cs);
|
|
|
b38b0f |
- assert(env->exception_injected == -1);
|
|
|
b38b0f |
+ assert(env->exception_nr == -1);
|
|
|
b38b0f |
|
|
|
b38b0f |
/* pass to guest */
|
|
|
b38b0f |
- env->exception_injected = arch_info->exception;
|
|
|
b38b0f |
+ kvm_queue_exception(env, arch_info->exception,
|
|
|
b38b0f |
+ arch_info->exception == EXCP01_DB,
|
|
|
b38b0f |
+ arch_info->dr6);
|
|
|
b38b0f |
env->has_error_code = 0;
|
|
|
b38b0f |
- if (arch_info->exception == EXCP01_DB) {
|
|
|
b38b0f |
- env->dr[6] = arch_info->dr6;
|
|
|
b38b0f |
- }
|
|
|
b38b0f |
}
|
|
|
b38b0f |
|
|
|
b38b0f |
return ret;
|
|
|
b38b0f |
diff --git a/target/i386/machine.c b/target/i386/machine.c
|
|
|
b38b0f |
index a2ddbba..5ffee8f 100644
|
|
|
b38b0f |
--- a/target/i386/machine.c
|
|
|
b38b0f |
+++ b/target/i386/machine.c
|
|
|
b38b0f |
@@ -239,6 +239,41 @@ static int cpu_pre_save(void *opaque)
|
|
|
b38b0f |
}
|
|
|
b38b0f |
#endif
|
|
|
b38b0f |
|
|
|
b38b0f |
+ /*
|
|
|
b38b0f |
+ * When vCPU is running L2 and exception is still pending,
|
|
|
b38b0f |
+ * it can potentially be intercepted by L1 hypervisor.
|
|
|
b38b0f |
+ * In contrast to an injected exception which cannot be
|
|
|
b38b0f |
+ * intercepted anymore.
|
|
|
b38b0f |
+ *
|
|
|
b38b0f |
+ * Furthermore, when a L2 exception is intercepted by L1
|
|
|
b38b0f |
+ * hypervisor, it's exception payload (CR2/DR6 on #PF/#DB)
|
|
|
b38b0f |
+ * should not be set yet in the respective vCPU register.
|
|
|
b38b0f |
+ * Thus, in case an exception is pending, it is
|
|
|
b38b0f |
+ * important to save the exception payload seperately.
|
|
|
b38b0f |
+ *
|
|
|
b38b0f |
+ * Therefore, if an exception is not in a pending state
|
|
|
b38b0f |
+ * or vCPU is not in guest-mode, it is not important to
|
|
|
b38b0f |
+ * distinguish between a pending and injected exception
|
|
|
b38b0f |
+ * and we don't need to store seperately the exception payload.
|
|
|
b38b0f |
+ *
|
|
|
b38b0f |
+ * In order to preserve better backwards-compatabile migration,
|
|
|
b38b0f |
+ * convert a pending exception to an injected exception in
|
|
|
b38b0f |
+ * case it is not important to distingiush between them
|
|
|
b38b0f |
+ * as described above.
|
|
|
b38b0f |
+ */
|
|
|
b38b0f |
+ if (env->exception_pending && !(env->hflags & HF_GUEST_MASK)) {
|
|
|
b38b0f |
+ env->exception_pending = 0;
|
|
|
b38b0f |
+ env->exception_injected = 1;
|
|
|
b38b0f |
+
|
|
|
b38b0f |
+ if (env->exception_has_payload) {
|
|
|
b38b0f |
+ if (env->exception_nr == EXCP01_DB) {
|
|
|
b38b0f |
+ env->dr[6] = env->exception_payload;
|
|
|
b38b0f |
+ } else if (env->exception_nr == EXCP0E_PAGE) {
|
|
|
b38b0f |
+ env->cr[2] = env->exception_payload;
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+
|
|
|
b38b0f |
return 0;
|
|
|
b38b0f |
}
|
|
|
b38b0f |
|
|
|
b38b0f |
@@ -296,6 +331,23 @@ static int cpu_post_load(void *opaque, int version_id)
|
|
|
b38b0f |
}
|
|
|
b38b0f |
#endif
|
|
|
b38b0f |
|
|
|
b38b0f |
+ /*
|
|
|
b38b0f |
+ * There are cases that we can get valid exception_nr with both
|
|
|
b38b0f |
+ * exception_pending and exception_injected being cleared.
|
|
|
b38b0f |
+ * This can happen in one of the following scenarios:
|
|
|
b38b0f |
+ * 1) Source is older QEMU without KVM_CAP_EXCEPTION_PAYLOAD support.
|
|
|
b38b0f |
+ * 2) Source is running on kernel without KVM_CAP_EXCEPTION_PAYLOAD support.
|
|
|
b38b0f |
+ * 3) "cpu/exception_info" subsection not sent because there is no exception
|
|
|
b38b0f |
+ * pending or guest wasn't running L2 (See comment in cpu_pre_save()).
|
|
|
b38b0f |
+ *
|
|
|
b38b0f |
+ * In those cases, we can just deduce that a valid exception_nr means
|
|
|
b38b0f |
+ * we can treat the exception as already injected.
|
|
|
b38b0f |
+ */
|
|
|
b38b0f |
+ if ((env->exception_nr != -1) &&
|
|
|
b38b0f |
+ !env->exception_pending && !env->exception_injected) {
|
|
|
b38b0f |
+ env->exception_injected = 1;
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+
|
|
|
b38b0f |
env->fpstt = (env->fpus_vmstate >> 11) & 7;
|
|
|
b38b0f |
env->fpus = env->fpus_vmstate & ~0x3800;
|
|
|
b38b0f |
env->fptag_vmstate ^= 0xff;
|
|
|
b38b0f |
@@ -341,6 +393,35 @@ static bool steal_time_msr_needed(void *opaque)
|
|
|
b38b0f |
return cpu->env.steal_time_msr != 0;
|
|
|
b38b0f |
}
|
|
|
b38b0f |
|
|
|
b38b0f |
+static bool exception_info_needed(void *opaque)
|
|
|
b38b0f |
+{
|
|
|
b38b0f |
+ X86CPU *cpu = opaque;
|
|
|
b38b0f |
+ CPUX86State *env = &cpu->env;
|
|
|
b38b0f |
+
|
|
|
b38b0f |
+ /*
|
|
|
b38b0f |
+ * It is important to save exception-info only in case
|
|
|
b38b0f |
+ * we need to distingiush between a pending and injected
|
|
|
b38b0f |
+ * exception. Which is only required in case there is a
|
|
|
b38b0f |
+ * pending exception and vCPU is running L2.
|
|
|
b38b0f |
+ * For more info, refer to comment in cpu_pre_save().
|
|
|
b38b0f |
+ */
|
|
|
b38b0f |
+ return env->exception_pending && (env->hflags & HF_GUEST_MASK);
|
|
|
b38b0f |
+}
|
|
|
b38b0f |
+
|
|
|
b38b0f |
+static const VMStateDescription vmstate_exception_info = {
|
|
|
b38b0f |
+ .name = "cpu/exception_info",
|
|
|
b38b0f |
+ .version_id = 1,
|
|
|
b38b0f |
+ .minimum_version_id = 1,
|
|
|
b38b0f |
+ .needed = exception_info_needed,
|
|
|
b38b0f |
+ .fields = (VMStateField[]) {
|
|
|
b38b0f |
+ VMSTATE_UINT8(env.exception_pending, X86CPU),
|
|
|
b38b0f |
+ VMSTATE_UINT8(env.exception_injected, X86CPU),
|
|
|
b38b0f |
+ VMSTATE_UINT8(env.exception_has_payload, X86CPU),
|
|
|
b38b0f |
+ VMSTATE_UINT64(env.exception_payload, X86CPU),
|
|
|
b38b0f |
+ VMSTATE_END_OF_LIST()
|
|
|
b38b0f |
+ }
|
|
|
b38b0f |
+};
|
|
|
b38b0f |
+
|
|
|
b38b0f |
static const VMStateDescription vmstate_steal_time_msr = {
|
|
|
b38b0f |
.name = "cpu/steal_time_msr",
|
|
|
b38b0f |
.version_id = 1,
|
|
|
b38b0f |
@@ -1219,7 +1300,7 @@ VMStateDescription vmstate_x86_cpu = {
|
|
|
b38b0f |
VMSTATE_INT32(env.interrupt_injected, X86CPU),
|
|
|
b38b0f |
VMSTATE_UINT32(env.mp_state, X86CPU),
|
|
|
b38b0f |
VMSTATE_UINT64(env.tsc, X86CPU),
|
|
|
b38b0f |
- VMSTATE_INT32(env.exception_injected, X86CPU),
|
|
|
b38b0f |
+ VMSTATE_INT32(env.exception_nr, X86CPU),
|
|
|
b38b0f |
VMSTATE_UINT8(env.soft_interrupt, X86CPU),
|
|
|
b38b0f |
VMSTATE_UINT8(env.nmi_injected, X86CPU),
|
|
|
b38b0f |
VMSTATE_UINT8(env.nmi_pending, X86CPU),
|
|
|
b38b0f |
@@ -1243,6 +1324,7 @@ VMStateDescription vmstate_x86_cpu = {
|
|
|
b38b0f |
/* The above list is not sorted /wrt version numbers, watch out! */
|
|
|
b38b0f |
},
|
|
|
b38b0f |
.subsections = (const VMStateDescription*[]) {
|
|
|
b38b0f |
+ &vmstate_exception_info,
|
|
|
b38b0f |
&vmstate_async_pf_msr,
|
|
|
b38b0f |
&vmstate_pv_eoi_msr,
|
|
|
b38b0f |
&vmstate_steal_time_msr,
|
|
|
b38b0f |
--
|
|
|
b38b0f |
1.8.3.1
|
|
|
b38b0f |
|