#include "hw/pc.h"
#include "hw/apic.h"
#include "ioport.h"
-#include "kvm_x86.h"
#ifdef CONFIG_KVM_PARA
#include <linux/kvm_para.h>
#endif
return features;
}
-#endif
+#endif /* CONFIG_KVM_PARA */
#ifdef KVM_CAP_MCE
static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap,
return kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, mcg_cap);
}
-static int kvm_set_mce(CPUState *env, struct kvm_x86_mce *m)
-{
- return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, m);
-}
-
-static int kvm_get_msr(CPUState *env, struct kvm_msr_entry *msrs, int n)
-{
- struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs);
- int r;
-
- kmsrs->nmsrs = n;
- memcpy(kmsrs->entries, msrs, n * sizeof *msrs);
- r = kvm_vcpu_ioctl(env, KVM_GET_MSRS, kmsrs);
- memcpy(msrs, kmsrs->entries, n * sizeof *msrs);
- free(kmsrs);
- return r;
-}
-
-/* FIXME: kill this and kvm_get_msr, use env->mcg_status instead */
-static int kvm_mce_in_progress(CPUState *env)
+static void kvm_mce_inject(CPUState *env, target_phys_addr_t paddr, int code)
{
- struct kvm_msr_entry msr_mcg_status = {
- .index = MSR_MCG_STATUS,
- };
- int r;
+ uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN |
+ MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S;
+ uint64_t mcg_status = MCG_STATUS_MCIP;
- r = kvm_get_msr(env, &msr_mcg_status, 1);
- if (r == -1 || r == 0) {
- fprintf(stderr, "Failed to get MCE status\n");
- return 0;
+ if (code == BUS_MCEERR_AR) {
+ status |= MCI_STATUS_AR | 0x134;
+ mcg_status |= MCG_STATUS_EIPV;
+ } else {
+ status |= 0xc0;
+ mcg_status |= MCG_STATUS_RIPV;
}
- return !!(msr_mcg_status.data & MCG_STATUS_MCIP);
+ cpu_x86_inject_mce(NULL, env, 9, status, mcg_status, paddr,
+ (MCM_ADDR_PHYS << 6) | 0xc,
+ cpu_x86_support_mca_broadcast(env) ?
+ MCE_INJECT_BROADCAST : 0);
}
+#endif /* KVM_CAP_MCE */
-struct kvm_x86_mce_data
+static void hardware_memory_error(void)
{
- CPUState *env;
- struct kvm_x86_mce *mce;
- int abort_on_error;
-};
+ fprintf(stderr, "Hardware memory error!\n");
+ exit(1);
+}
-static void kvm_do_inject_x86_mce(void *_data)
+int kvm_arch_on_sigbus_vcpu(CPUState *env, int code, void *addr)
{
- struct kvm_x86_mce_data *data = _data;
- int r;
+#ifdef KVM_CAP_MCE
+ ram_addr_t ram_addr;
+ target_phys_addr_t paddr;
- /* If there is an MCE exception being processed, ignore this SRAO MCE */
- if ((data->env->mcg_cap & MCG_SER_P) &&
- !(data->mce->status & MCI_STATUS_AR)) {
- if (kvm_mce_in_progress(data->env)) {
- return;
+ if ((env->mcg_cap & MCG_SER_P) && addr
+ && (code == BUS_MCEERR_AR || code == BUS_MCEERR_AO)) {
+ if (qemu_ram_addr_from_host(addr, &ram_addr) ||
+ !kvm_physical_memory_addr_from_ram(env->kvm_state, ram_addr,
+ &paddr)) {
+ fprintf(stderr, "Hardware memory error for memory used by "
+ "QEMU itself instead of guest system!\n");
+ /* Hope we are lucky for AO MCE */
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else {
+ hardware_memory_error();
+ }
}
- }
-
- r = kvm_set_mce(data->env, data->mce);
- if (r < 0) {
- perror("kvm_set_mce FAILED");
- if (data->abort_on_error) {
- abort();
+ kvm_mce_inject(env, paddr, code);
+ } else
+#endif /* KVM_CAP_MCE */
+ {
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else if (code == BUS_MCEERR_AR) {
+ hardware_memory_error();
+ } else {
+ return 1;
}
}
+ return 0;
}
-static void kvm_inject_x86_mce_on(CPUState *env, struct kvm_x86_mce *mce,
- int flag)
+int kvm_arch_on_sigbus(int code, void *addr)
{
- struct kvm_x86_mce_data data = {
- .env = env,
- .mce = mce,
- .abort_on_error = (flag & ABORT_ON_ERROR),
- };
+#ifdef KVM_CAP_MCE
+ if ((first_cpu->mcg_cap & MCG_SER_P) && addr && code == BUS_MCEERR_AO) {
+ ram_addr_t ram_addr;
+ target_phys_addr_t paddr;
- if (!env->mcg_cap) {
- fprintf(stderr, "MCE support is not enabled!\n");
- return;
+ /* Hope we are lucky for AO MCE */
+ if (qemu_ram_addr_from_host(addr, &ram_addr) ||
+ !kvm_physical_memory_addr_from_ram(first_cpu->kvm_state, ram_addr,
+ &paddr)) {
+ fprintf(stderr, "Hardware memory error for memory used by "
+ "QEMU itself instead of guest system!: %p\n", addr);
+ return 0;
+ }
+ kvm_mce_inject(first_cpu, paddr, code);
+ } else
+#endif /* KVM_CAP_MCE */
+ {
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else if (code == BUS_MCEERR_AR) {
+ hardware_memory_error();
+ } else {
+ return 1;
+ }
}
-
- run_on_cpu(env, kvm_do_inject_x86_mce, &data);
+ return 0;
}
-static void kvm_mce_broadcast_rest(CPUState *env);
-#endif
-
-void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
- uint64_t mcg_status, uint64_t addr, uint64_t misc,
- int flag)
+static int kvm_inject_mce_oldstyle(CPUState *env)
{
#ifdef KVM_CAP_MCE
- struct kvm_x86_mce mce = {
- .bank = bank,
- .status = status,
- .mcg_status = mcg_status,
- .addr = addr,
- .misc = misc,
- };
+ if (!kvm_has_vcpu_events() && env->exception_injected == EXCP12_MCHK) {
+ unsigned int bank, bank_num = env->mcg_cap & 0xff;
+ struct kvm_x86_mce mce;
- if (flag & MCE_BROADCAST) {
- kvm_mce_broadcast_rest(cenv);
- }
+ env->exception_injected = -1;
- kvm_inject_x86_mce_on(cenv, &mce, flag);
-#else
- if (flag & ABORT_ON_ERROR) {
- abort();
+ /*
+ * 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
+#endif /* KVM_CAP_MCE */
+ return 0;
}
static void cpu_update_state(void *opaque, int running, int reason)
if (env->mcg_cap) {
int i;
- if (level == KVM_PUT_RESET_STATE) {
- kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status);
- } else if (level == KVM_PUT_FULL_STATE) {
- kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status);
- kvm_msr_entry_set(&msrs[n++], MSR_MCG_CTL, env->mcg_ctl);
- for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) {
- kvm_msr_entry_set(&msrs[n++], MSR_MC0_CTL + i, env->mce_banks[i]);
- }
+ kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status);
+ kvm_msr_entry_set(&msrs[n++], MSR_MCG_CTL, env->mcg_ctl);
+ for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) {
+ kvm_msr_entry_set(&msrs[n++], MSR_MC0_CTL + i, env->mce_banks[i]);
}
}
#endif
{
int ret;
- assert(cpu_is_stopped(env) || qemu_cpu_self(env));
+ assert(cpu_is_stopped(env) || qemu_cpu_is_self(env));
ret = kvm_getput_regs(env, 1);
if (ret < 0) {
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;
{
int ret;
- assert(cpu_is_stopped(env) || qemu_cpu_self(env));
+ assert(cpu_is_stopped(env) || qemu_cpu_is_self(env));
ret = kvm_getput_regs(env, 0);
if (ret < 0) {
return 0;
}
-int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
+void kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
{
- /* Force the VCPU out of its inner loop to process the INIT request */
- if (env->interrupt_request & CPU_INTERRUPT_INIT) {
- env->exit_request = 1;
- }
+ int ret;
/* Inject NMI */
if (env->interrupt_request & CPU_INTERRUPT_NMI) {
env->interrupt_request &= ~CPU_INTERRUPT_NMI;
DPRINTF("injected NMI\n");
- kvm_vcpu_ioctl(env, KVM_NMI);
- }
-
- /* Try to inject an interrupt if the guest can accept it */
- if (run->ready_for_interrupt_injection &&
- (env->interrupt_request & CPU_INTERRUPT_HARD) &&
- (env->eflags & IF_MASK)) {
- int irq;
-
- env->interrupt_request &= ~CPU_INTERRUPT_HARD;
- irq = cpu_get_pic_interrupt(env);
- if (irq >= 0) {
- struct kvm_interrupt intr;
- intr.irq = irq;
- /* FIXME: errors */
- DPRINTF("injected interrupt %d\n", irq);
- kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr);
+ ret = kvm_vcpu_ioctl(env, KVM_NMI);
+ if (ret < 0) {
+ fprintf(stderr, "KVM: injection failed, NMI lost (%s)\n",
+ strerror(-ret));
}
}
- /* If we have an interrupt but the guest is not ready to receive an
- * interrupt, request an interrupt window exit. This will
- * cause a return to userspace as soon as the guest is ready to
- * receive interrupts. */
- if ((env->interrupt_request & CPU_INTERRUPT_HARD)) {
- run->request_interrupt_window = 1;
- } else {
- run->request_interrupt_window = 0;
- }
+ if (!kvm_irqchip_in_kernel()) {
+ /* Force the VCPU out of its inner loop to process the INIT request */
+ if (env->interrupt_request & CPU_INTERRUPT_INIT) {
+ env->exit_request = 1;
+ }
- DPRINTF("setting tpr\n");
- run->cr8 = cpu_get_apic_tpr(env->apic_state);
+ /* Try to inject an interrupt if the guest can accept it */
+ if (run->ready_for_interrupt_injection &&
+ (env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK)) {
+ int irq;
+
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ irq = cpu_get_pic_interrupt(env);
+ if (irq >= 0) {
+ struct kvm_interrupt intr;
+
+ intr.irq = irq;
+ DPRINTF("injected interrupt %d\n", irq);
+ ret = kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr);
+ if (ret < 0) {
+ fprintf(stderr,
+ "KVM: injection failed, interrupt lost (%s)\n",
+ strerror(-ret));
+ }
+ }
+ }
- return 0;
+ /* If we have an interrupt but the guest is not ready to receive an
+ * interrupt, request an interrupt window exit. This will
+ * cause a return to userspace as soon as the guest is ready to
+ * receive interrupts. */
+ if ((env->interrupt_request & CPU_INTERRUPT_HARD)) {
+ run->request_interrupt_window = 1;
+ } else {
+ run->request_interrupt_window = 0;
+ }
+
+ DPRINTF("setting tpr\n");
+ run->cr8 = cpu_get_apic_tpr(env->apic_state);
+ }
}
-int kvm_arch_post_run(CPUState *env, struct kvm_run *run)
+void kvm_arch_post_run(CPUState *env, struct kvm_run *run)
{
if (run->if_flag) {
env->eflags |= IF_MASK;
}
cpu_set_apic_tpr(env->apic_state, run->cr8);
cpu_set_apic_base(env->apic_state, run->apic_base);
-
- return 0;
}
-int kvm_arch_process_irqchip_events(CPUState *env)
+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;
+ }
+
+ if (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)) {
+ env->halted = 0;
+ }
if (env->interrupt_request & CPU_INTERRUPT_INIT) {
kvm_cpu_synchronize_state(env);
do_cpu_init(env);
- env->exception_index = EXCP_HALTED;
}
-
if (env->interrupt_request & CPU_INTERRUPT_SIPI) {
kvm_cpu_synchronize_state(env);
do_cpu_sipi(env);
(env->eflags & IF_MASK)) &&
!(env->interrupt_request & CPU_INTERRUPT_NMI)) {
env->halted = 1;
- env->exception_index = EXCP_HLT;
return 0;
}
return !(env->cr[0] & CR0_PE_MASK) ||
((env->segs[R_CS].selector & 3) != 3);
}
-
-static void hardware_memory_error(void)
-{
- fprintf(stderr, "Hardware memory error!\n");
- exit(1);
-}
-
-#ifdef KVM_CAP_MCE
-static void kvm_mce_broadcast_rest(CPUState *env)
-{
- struct kvm_x86_mce mce = {
- .bank = 1,
- .status = MCI_STATUS_VAL | MCI_STATUS_UC,
- .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV,
- .addr = 0,
- .misc = 0,
- };
- CPUState *cenv;
-
- /* Broadcast MCA signal for processor version 06H_EH and above */
- if (cpu_x86_support_mca_broadcast(env)) {
- for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) {
- if (cenv == env) {
- continue;
- }
- kvm_inject_x86_mce_on(cenv, &mce, ABORT_ON_ERROR);
- }
- }
-}
-
-static void kvm_mce_inj_srar_dataload(CPUState *env, target_phys_addr_t paddr)
-{
- struct kvm_x86_mce mce = {
- .bank = 9,
- .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
- | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
- | MCI_STATUS_AR | 0x134,
- .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV,
- .addr = paddr,
- .misc = (MCM_ADDR_PHYS << 6) | 0xc,
- };
- int r;
-
- r = kvm_set_mce(env, &mce);
- if (r < 0) {
- fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno));
- abort();
- }
- kvm_mce_broadcast_rest(env);
-}
-
-static void kvm_mce_inj_srao_memscrub(CPUState *env, target_phys_addr_t paddr)
-{
- struct kvm_x86_mce mce = {
- .bank = 9,
- .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
- | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
- | 0xc0,
- .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV,
- .addr = paddr,
- .misc = (MCM_ADDR_PHYS << 6) | 0xc,
- };
- int r;
-
- r = kvm_set_mce(env, &mce);
- if (r < 0) {
- fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno));
- abort();
- }
- kvm_mce_broadcast_rest(env);
-}
-
-static void kvm_mce_inj_srao_memscrub2(CPUState *env, target_phys_addr_t paddr)
-{
- struct kvm_x86_mce mce = {
- .bank = 9,
- .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
- | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
- | 0xc0,
- .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV,
- .addr = paddr,
- .misc = (MCM_ADDR_PHYS << 6) | 0xc,
- };
-
- kvm_inject_x86_mce_on(env, &mce, ABORT_ON_ERROR);
- kvm_mce_broadcast_rest(env);
-}
-
-#endif
-
-int kvm_arch_on_sigbus_vcpu(CPUState *env, int code, void *addr)
-{
-#if defined(KVM_CAP_MCE)
- void *vaddr;
- ram_addr_t ram_addr;
- target_phys_addr_t paddr;
-
- if ((env->mcg_cap & MCG_SER_P) && addr
- && (code == BUS_MCEERR_AR
- || code == BUS_MCEERR_AO)) {
- vaddr = (void *)addr;
- if (qemu_ram_addr_from_host(vaddr, &ram_addr) ||
- !kvm_physical_memory_addr_from_ram(env->kvm_state, ram_addr, &paddr)) {
- fprintf(stderr, "Hardware memory error for memory used by "
- "QEMU itself instead of guest system!\n");
- /* Hope we are lucky for AO MCE */
- if (code == BUS_MCEERR_AO) {
- return 0;
- } else {
- hardware_memory_error();
- }
- }
-
- if (code == BUS_MCEERR_AR) {
- /* Fake an Intel architectural Data Load SRAR UCR */
- kvm_mce_inj_srar_dataload(env, paddr);
- } else {
- /*
- * If there is an MCE excpetion being processed, ignore
- * this SRAO MCE
- */
- if (!kvm_mce_in_progress(env)) {
- /* Fake an Intel architectural Memory scrubbing UCR */
- kvm_mce_inj_srao_memscrub(env, paddr);
- }
- }
- } else
-#endif
- {
- if (code == BUS_MCEERR_AO) {
- return 0;
- } else if (code == BUS_MCEERR_AR) {
- hardware_memory_error();
- } else {
- return 1;
- }
- }
- return 0;
-}
-
-int kvm_arch_on_sigbus(int code, void *addr)
-{
-#if defined(KVM_CAP_MCE)
- if ((first_cpu->mcg_cap & MCG_SER_P) && addr && code == BUS_MCEERR_AO) {
- void *vaddr;
- ram_addr_t ram_addr;
- target_phys_addr_t paddr;
-
- /* Hope we are lucky for AO MCE */
- vaddr = addr;
- if (qemu_ram_addr_from_host(vaddr, &ram_addr) ||
- !kvm_physical_memory_addr_from_ram(first_cpu->kvm_state, ram_addr, &paddr)) {
- fprintf(stderr, "Hardware memory error for memory used by "
- "QEMU itself instead of guest system!: %p\n", addr);
- return 0;
- }
- kvm_mce_inj_srao_memscrub2(first_cpu, paddr);
- } else
-#endif
- {
- if (code == BUS_MCEERR_AO) {
- return 0;
- } else if (code == BUS_MCEERR_AR) {
- hardware_memory_error();
- } else {
- return 1;
- }
- }
- return 0;
-}