kvm: x86: Inject pending MCE events on state writeback

The current way of injecting MCE events without updating of and
synchronizing with the CPUState is broken and causes spurious
corruptions of the MCE-related parts of the CPUState.

As a first step towards a fix, enhance the state writeback code with
support for injecting events that are pending in the CPUState. A pending
exception will then be signaled via cpu_interrupt(CPU_INTERRUPT_MCE).
And, just like for TCG, we need to leave the halt state when
CPU_INTERRUPT_MCE is pending (left broken for the to-be-removed old KVM
code).

This will also allow to unify TCG and KVM injection code.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
CC: Huang Ying <ying.huang@intel.com>
CC: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
CC: Jin Dongming <jin.dongming@np.css.fujitsu.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
This commit is contained in:
Jan Kiszka 2011-03-02 08:56:14 +01:00 committed by Marcelo Tosatti
parent 990368650f
commit ab443475c9

View File

@ -467,6 +467,38 @@ void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
#endif /* !KVM_CAP_MCE*/
}
static int kvm_inject_mce_oldstyle(CPUState *env)
{
#ifdef KVM_CAP_MCE
if (!kvm_has_vcpu_events() && env->exception_injected == EXCP12_MCHK) {
unsigned int bank, bank_num = env->mcg_cap & 0xff;
struct kvm_x86_mce mce;
env->exception_injected = -1;
/*
* There must be at least one bank in use if an MCE is pending.
* Find it and use its values for the event injection.
*/
for (bank = 0; bank < bank_num; bank++) {
if (env->mce_banks[bank * 4 + 1] & MCI_STATUS_VAL) {
break;
}
}
assert(bank < bank_num);
mce.bank = bank;
mce.status = env->mce_banks[bank * 4 + 1];
mce.mcg_status = env->mcg_status;
mce.addr = env->mce_banks[bank * 4 + 2];
mce.misc = env->mce_banks[bank * 4 + 3];
return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, &mce);
}
#endif /* KVM_CAP_MCE */
return 0;
}
static void cpu_update_state(void *opaque, int running, int reason)
{
CPUState *env = opaque;
@ -1539,6 +1571,11 @@ int kvm_arch_put_registers(CPUState *env, int level)
if (ret < 0) {
return ret;
}
/* must be before kvm_put_msrs */
ret = kvm_inject_mce_oldstyle(env);
if (ret < 0) {
return ret;
}
ret = kvm_put_msrs(env, level);
if (ret < 0) {
return ret;
@ -1677,6 +1714,29 @@ void kvm_arch_post_run(CPUState *env, struct kvm_run *run)
int kvm_arch_process_async_events(CPUState *env)
{
if (env->interrupt_request & CPU_INTERRUPT_MCE) {
/* We must not raise CPU_INTERRUPT_MCE if it's not supported. */
assert(env->mcg_cap);
env->interrupt_request &= ~CPU_INTERRUPT_MCE;
kvm_cpu_synchronize_state(env);
if (env->exception_injected == EXCP08_DBLE) {
/* this means triple fault */
qemu_system_reset_request();
env->exit_request = 1;
return 0;
}
env->exception_injected = EXCP12_MCHK;
env->has_error_code = 0;
env->halted = 0;
if (kvm_irqchip_in_kernel() && env->mp_state == KVM_MP_STATE_HALTED) {
env->mp_state = KVM_MP_STATE_RUNNABLE;
}
}
if (kvm_irqchip_in_kernel()) {
return 0;
}