aboutsummaryrefslogtreecommitdiff
path: root/target-i386
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2011-03-21 17:42:20 -0500
committerAnthony Liguori <aliguori@us.ibm.com>2011-03-21 17:42:20 -0500
commit31b7c261a207e1e670d737ec78a87dd031bd8f73 (patch)
treeef070dd56c118c8761df7baf0a457fc2c5ff9dfa /target-i386
parent8b06c62ae48b67b320f7420dcd4854c5559e1532 (diff)
parentdc7a09cfe47679d89289101cc9eb387c45e48fe7 (diff)
Merge remote branch 'qemu-kvm/uq/master' into staging
Diffstat (limited to 'target-i386')
-rw-r--r--target-i386/cpu.h13
-rw-r--r--target-i386/cpuid.c1
-rw-r--r--target-i386/exec.h15
-rw-r--r--target-i386/helper.c190
-rw-r--r--target-i386/kvm.c593
-rw-r--r--target-i386/kvm_x86.h25
-rw-r--r--target-i386/machine.c2
7 files changed, 397 insertions, 442 deletions
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 5f1df8b4d3..c7047d5912 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -685,7 +685,7 @@ typedef struct CPUX86State {
uint64_t tsc;
- uint64_t pat;
+ uint64_t mcg_status;
/* exception/interrupt handling */
int error_code;
@@ -705,6 +705,8 @@ typedef struct CPUX86State {
CPU_COMMON
+ uint64_t pat;
+
/* processor features (e.g. for CPUID insn) */
uint32_t cpuid_level;
uint32_t cpuid_vendor1;
@@ -741,7 +743,6 @@ typedef struct CPUX86State {
struct DeviceState *apic_state;
uint64_t mcg_cap;
- uint64_t mcg_status;
uint64_t mcg_ctl;
uint64_t mce_banks[MCE_BANKS_DEF*4];
@@ -985,4 +986,12 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
void do_cpu_init(CPUState *env);
void do_cpu_sipi(CPUState *env);
+
+#define MCE_INJECT_BROADCAST 1
+#define MCE_INJECT_UNCOND_AO 2
+
+void cpu_x86_inject_mce(Monitor *mon, CPUState *cenv, int bank,
+ uint64_t status, uint64_t mcg_status, uint64_t addr,
+ uint64_t misc, int flags);
+
#endif /* CPU_I386_H */
diff --git a/target-i386/cpuid.c b/target-i386/cpuid.c
index 5382a283f5..814d13e767 100644
--- a/target-i386/cpuid.c
+++ b/target-i386/cpuid.c
@@ -847,7 +847,6 @@ int cpu_x86_register (CPUX86State *env, const char *cpu_model)
env->cpuid_version |= ((def->model & 0xf) << 4) | ((def->model >> 4) << 16);
env->cpuid_version |= def->stepping;
env->cpuid_features = def->features;
- env->pat = 0x0007040600070406ULL;
env->cpuid_ext_features = def->ext_features;
env->cpuid_ext2_features = def->ext2_features;
env->cpuid_ext3_features = def->ext3_features;
diff --git a/target-i386/exec.h b/target-i386/exec.h
index 3e7386e91c..6f9f709d8a 100644
--- a/target-i386/exec.h
+++ b/target-i386/exec.h
@@ -293,15 +293,12 @@ static inline void load_eflags(int eflags, int update_mask)
static inline int cpu_has_work(CPUState *env)
{
- int work;
-
- work = (env->interrupt_request & CPU_INTERRUPT_HARD) &&
- (env->eflags & IF_MASK);
- work |= env->interrupt_request & CPU_INTERRUPT_NMI;
- work |= env->interrupt_request & CPU_INTERRUPT_INIT;
- work |= env->interrupt_request & CPU_INTERRUPT_SIPI;
-
- return work;
+ return ((env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK)) ||
+ (env->interrupt_request & (CPU_INTERRUPT_NMI |
+ CPU_INTERRUPT_INIT |
+ CPU_INTERRUPT_SIPI |
+ CPU_INTERRUPT_MCE));
}
/* load efer and update the corresponding hflags. XXX: do consistency
diff --git a/target-i386/helper.c b/target-i386/helper.c
index f0c546df5c..d15fca591e 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -27,7 +27,10 @@
#include "exec-all.h"
#include "qemu-common.h"
#include "kvm.h"
-#include "kvm_x86.h"
+#ifndef CONFIG_USER_ONLY
+#include "sysemu.h"
+#include "monitor.h"
+#endif
//#define DEBUG_MMU
@@ -96,13 +99,13 @@ void cpu_reset(CPUX86State *env)
env->mxcsr = 0x1f80;
+ env->pat = 0x0007040600070406ULL;
+
memset(env->dr, 0, sizeof(env->dr));
env->dr[6] = DR6_FIXED_1;
env->dr[7] = DR7_FIXED_1;
cpu_breakpoint_remove_all(env, BP_CPU);
cpu_watchpoint_remove_all(env, BP_CPU);
-
- env->mcg_status = 0;
}
void cpu_x86_close(CPUX86State *env)
@@ -1065,91 +1068,138 @@ static void breakpoint_handler(CPUState *env)
prev_debug_excp_handler(env);
}
-/* This should come from sysemu.h - if we could include it here... */
-void qemu_system_reset_request(void);
-
-static void qemu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
- uint64_t mcg_status, uint64_t addr, uint64_t misc)
+typedef struct MCEInjectionParams {
+ Monitor *mon;
+ CPUState *env;
+ int bank;
+ uint64_t status;
+ uint64_t mcg_status;
+ uint64_t addr;
+ uint64_t misc;
+ int flags;
+} MCEInjectionParams;
+
+static void do_inject_x86_mce(void *data)
{
- uint64_t mcg_cap = cenv->mcg_cap;
- uint64_t *banks = cenv->mce_banks;
+ MCEInjectionParams *params = data;
+ CPUState *cenv = params->env;
+ uint64_t *banks = cenv->mce_banks + 4 * params->bank;
+
+ cpu_synchronize_state(cenv);
/*
- * if MSR_MCG_CTL is not all 1s, the uncorrected error
- * reporting is disabled
- */
- if ((status & MCI_STATUS_UC) && (mcg_cap & MCG_CTL_P) &&
- cenv->mcg_ctl != ~(uint64_t)0)
- return;
- banks += 4 * bank;
- /*
- * if MSR_MCi_CTL is not all 1s, the uncorrected error
- * reporting is disabled for the bank
+ * If there is an MCE exception being processed, ignore this SRAO MCE
+ * unless unconditional injection was requested.
*/
- if ((status & MCI_STATUS_UC) && banks[0] != ~(uint64_t)0)
+ if (!(params->flags & MCE_INJECT_UNCOND_AO)
+ && !(params->status & MCI_STATUS_AR)
+ && (cenv->mcg_status & MCG_STATUS_MCIP)) {
return;
- if (status & MCI_STATUS_UC) {
+ }
+
+ if (params->status & MCI_STATUS_UC) {
+ /*
+ * if MSR_MCG_CTL is not all 1s, the uncorrected error
+ * reporting is disabled
+ */
+ if ((cenv->mcg_cap & MCG_CTL_P) && cenv->mcg_ctl != ~(uint64_t)0) {
+ monitor_printf(params->mon,
+ "CPU %d: Uncorrected error reporting disabled\n",
+ cenv->cpu_index);
+ return;
+ }
+
+ /*
+ * if MSR_MCi_CTL is not all 1s, the uncorrected error
+ * reporting is disabled for the bank
+ */
+ if (banks[0] != ~(uint64_t)0) {
+ monitor_printf(params->mon,
+ "CPU %d: Uncorrected error reporting disabled for"
+ " bank %d\n",
+ cenv->cpu_index, params->bank);
+ return;
+ }
+
if ((cenv->mcg_status & MCG_STATUS_MCIP) ||
!(cenv->cr[4] & CR4_MCE_MASK)) {
- fprintf(stderr, "injects mce exception while previous "
- "one is in progress!\n");
+ monitor_printf(params->mon,
+ "CPU %d: Previous MCE still in progress, raising"
+ " triple fault\n",
+ cenv->cpu_index);
qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
qemu_system_reset_request();
return;
}
- if (banks[1] & MCI_STATUS_VAL)
- status |= MCI_STATUS_OVER;
- banks[2] = addr;
- banks[3] = misc;
- cenv->mcg_status = mcg_status;
- banks[1] = status;
+ if (banks[1] & MCI_STATUS_VAL) {
+ params->status |= MCI_STATUS_OVER;
+ }
+ banks[2] = params->addr;
+ banks[3] = params->misc;
+ cenv->mcg_status = params->mcg_status;
+ banks[1] = params->status;
cpu_interrupt(cenv, CPU_INTERRUPT_MCE);
} else if (!(banks[1] & MCI_STATUS_VAL)
|| !(banks[1] & MCI_STATUS_UC)) {
- if (banks[1] & MCI_STATUS_VAL)
- status |= MCI_STATUS_OVER;
- banks[2] = addr;
- banks[3] = misc;
- banks[1] = status;
- } else
+ if (banks[1] & MCI_STATUS_VAL) {
+ params->status |= MCI_STATUS_OVER;
+ }
+ banks[2] = params->addr;
+ banks[3] = params->misc;
+ banks[1] = params->status;
+ } else {
banks[1] |= MCI_STATUS_OVER;
+ }
}
-void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
- uint64_t mcg_status, uint64_t addr, uint64_t misc,
- int broadcast)
+void cpu_x86_inject_mce(Monitor *mon, CPUState *cenv, int bank,
+ uint64_t status, uint64_t mcg_status, uint64_t addr,
+ uint64_t misc, int flags)
{
+ MCEInjectionParams params = {
+ .mon = mon,
+ .env = cenv,
+ .bank = bank,
+ .status = status,
+ .mcg_status = mcg_status,
+ .addr = addr,
+ .misc = misc,
+ .flags = flags,
+ };
unsigned bank_num = cenv->mcg_cap & 0xff;
CPUState *env;
- int flag = 0;
- if (bank >= bank_num || !(status & MCI_STATUS_VAL)) {
+ if (!cenv->mcg_cap) {
+ monitor_printf(mon, "MCE injection not supported\n");
return;
}
-
- if (broadcast) {
- if (!cpu_x86_support_mca_broadcast(cenv)) {
- fprintf(stderr, "Current CPU does not support broadcast\n");
- return;
- }
+ if (bank >= bank_num) {
+ monitor_printf(mon, "Invalid MCE bank number\n");
+ return;
+ }
+ if (!(status & MCI_STATUS_VAL)) {
+ monitor_printf(mon, "Invalid MCE status code\n");
+ return;
+ }
+ if ((flags & MCE_INJECT_BROADCAST)
+ && !cpu_x86_support_mca_broadcast(cenv)) {
+ monitor_printf(mon, "Guest CPU does not support MCA broadcast\n");
+ return;
}
- if (kvm_enabled()) {
- if (broadcast) {
- flag |= MCE_BROADCAST;
- }
-
- kvm_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc, flag);
- } else {
- qemu_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc);
- if (broadcast) {
- for (env = first_cpu; env != NULL; env = env->next_cpu) {
- if (cenv == env) {
- continue;
- }
- qemu_inject_x86_mce(env, 1, MCI_STATUS_VAL | MCI_STATUS_UC,
- MCG_STATUS_MCIP | MCG_STATUS_RIPV, 0, 0);
+ run_on_cpu(cenv, do_inject_x86_mce, &params);
+ if (flags & MCE_INJECT_BROADCAST) {
+ params.bank = 1;
+ params.status = MCI_STATUS_VAL | MCI_STATUS_UC;
+ params.mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV;
+ params.addr = 0;
+ params.misc = 0;
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (cenv == env) {
+ continue;
}
+ params.env = env;
+ run_on_cpu(cenv, do_inject_x86_mce, &params);
}
}
}
@@ -1157,15 +1207,16 @@ void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
static void mce_init(CPUX86State *cenv)
{
- unsigned int bank, bank_num;
+ unsigned int bank;
- if (((cenv->cpuid_version >> 8)&0xf) >= 6
- && (cenv->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA)) {
+ if (((cenv->cpuid_version >> 8) & 0xf) >= 6
+ && (cenv->cpuid_features & (CPUID_MCE | CPUID_MCA)) ==
+ (CPUID_MCE | CPUID_MCA)) {
cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF;
cenv->mcg_ctl = ~(uint64_t)0;
- bank_num = MCE_BANKS_DEF;
- for (bank = 0; bank < bank_num; bank++)
- cenv->mce_banks[bank*4] = ~(uint64_t)0;
+ for (bank = 0; bank < MCE_BANKS_DEF; bank++) {
+ cenv->mce_banks[bank * 4] = ~(uint64_t)0;
+ }
}
}
@@ -1231,8 +1282,11 @@ CPUX86State *cpu_x86_init(const char *cpu_model)
void do_cpu_init(CPUState *env)
{
int sipi = env->interrupt_request & CPU_INTERRUPT_SIPI;
+ uint64_t pat = env->pat;
+
cpu_reset(env);
env->interrupt_request = sipi;
+ env->pat = pat;
apic_init_reset(env->apic_state);
env->halted = !cpu_is_bsp(env);
}
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 1f12cbf19e..a13599db81 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -28,7 +28,6 @@
#include "hw/pc.h"
#include "hw/apic.h"
#include "ioport.h"
-#include "kvm_x86.h"
#ifdef CONFIG_KVM_PARA
#include <linux/kvm_para.h>
@@ -172,9 +171,42 @@ static int get_para_features(CPUState *env)
#endif
return features;
}
-#endif
+#endif /* CONFIG_KVM_PARA */
+
+typedef struct HWPoisonPage {
+ ram_addr_t ram_addr;
+ QLIST_ENTRY(HWPoisonPage) list;
+} HWPoisonPage;
+
+static QLIST_HEAD(, HWPoisonPage) hwpoison_page_list =
+ QLIST_HEAD_INITIALIZER(hwpoison_page_list);
+
+static void kvm_unpoison_all(void *param)
+{
+ HWPoisonPage *page, *next_page;
+
+ QLIST_FOREACH_SAFE(page, &hwpoison_page_list, list, next_page) {
+ QLIST_REMOVE(page, list);
+ qemu_ram_remap(page->ram_addr, TARGET_PAGE_SIZE);
+ qemu_free(page);
+ }
+}
#ifdef KVM_CAP_MCE
+static void kvm_hwpoison_page_add(ram_addr_t ram_addr)
+{
+ HWPoisonPage *page;
+
+ QLIST_FOREACH(page, &hwpoison_page_list, list) {
+ if (page->ram_addr == ram_addr) {
+ return;
+ }
+ }
+ page = qemu_malloc(sizeof(HWPoisonPage));
+ page->ram_addr = ram_addr;
+ QLIST_INSERT_HEAD(&hwpoison_page_list, page, list);
+}
+
static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap,
int *max_banks)
{
@@ -188,117 +220,129 @@ static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap,
return -ENOSYS;
}
-static int kvm_setup_mce(CPUState *env, uint64_t *mcg_cap)
+static void kvm_mce_inject(CPUState *env, target_phys_addr_t paddr, int code)
{
- return kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, mcg_cap);
-}
+ 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;
-static int kvm_set_mce(CPUState *env, struct kvm_x86_mce *m)
-{
- return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, m);
+ if (code == BUS_MCEERR_AR) {
+ status |= MCI_STATUS_AR | 0x134;
+ mcg_status |= MCG_STATUS_EIPV;
+ } else {
+ status |= 0xc0;
+ mcg_status |= MCG_STATUS_RIPV;
+ }
+ 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 */
-static int kvm_get_msr(CPUState *env, struct kvm_msr_entry *msrs, int n)
+static void hardware_memory_error(void)
{
- 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;
+ fprintf(stderr, "Hardware memory error!\n");
+ exit(1);
}
-/* FIXME: kill this and kvm_get_msr, use env->mcg_status instead */
-static int kvm_mce_in_progress(CPUState *env)
+int kvm_arch_on_sigbus_vcpu(CPUState *env, int code, void *addr)
{
- struct kvm_msr_entry msr_mcg_status = {
- .index = MSR_MCG_STATUS,
- };
- int r;
+#ifdef KVM_CAP_MCE
+ ram_addr_t ram_addr;
+ target_phys_addr_t paddr;
- 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 ((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();
+ }
+ }
+ kvm_hwpoison_page_add(ram_addr);
+ 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 !!(msr_mcg_status.data & MCG_STATUS_MCIP);
+ return 0;
}
-struct kvm_x86_mce_data
-{
- CPUState *env;
- struct kvm_x86_mce *mce;
- int abort_on_error;
-};
-
-static void kvm_do_inject_x86_mce(void *_data)
+int kvm_arch_on_sigbus(int code, void *addr)
{
- struct kvm_x86_mce_data *data = _data;
- int r;
+#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 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;
+ /* 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;
}
- }
-
- r = kvm_set_mce(data->env, data->mce);
- if (r < 0) {
- perror("kvm_set_mce FAILED");
- if (data->abort_on_error) {
- abort();
+ kvm_hwpoison_page_add(ram_addr);
+ 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;
}
}
+ return 0;
}
-static void kvm_inject_x86_mce_on(CPUState *env, struct kvm_x86_mce *mce,
- int flag)
+static int kvm_inject_mce_oldstyle(CPUState *env)
{
- struct kvm_x86_mce_data data = {
- .env = env,
- .mce = mce,
- .abort_on_error = (flag & ABORT_ON_ERROR),
- };
-
- if (!env->mcg_cap) {
- fprintf(stderr, "MCE support is not enabled!\n");
- return;
- }
-
- run_on_cpu(env, kvm_do_inject_x86_mce, &data);
-}
+#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;
-static void kvm_mce_broadcast_rest(CPUState *env);
-#endif
+ env->exception_injected = -1;
-void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
- uint64_t mcg_status, uint64_t addr, uint64_t misc,
- int flag)
-{
-#ifdef KVM_CAP_MCE
- struct kvm_x86_mce mce = {
- .bank = bank,
- .status = status,
- .mcg_status = mcg_status,
- .addr = addr,
- .misc = misc,
- };
+ /*
+ * 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);
- if (flag & MCE_BROADCAST) {
- kvm_mce_broadcast_rest(cenv);
- }
+ 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];
- kvm_inject_x86_mce_on(cenv, &mce, flag);
-#else
- if (flag & ABORT_ON_ERROR) {
- abort();
+ 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)
@@ -426,20 +470,26 @@ int kvm_arch_init_vcpu(CPUState *env)
&& kvm_check_extension(env->kvm_state, KVM_CAP_MCE) > 0) {
uint64_t mcg_cap;
int banks;
+ int ret;
- if (kvm_get_mce_cap_supported(env->kvm_state, &mcg_cap, &banks)) {
- perror("kvm_get_mce_cap_supported FAILED");
- } else {
- if (banks > MCE_BANKS_DEF)
- banks = MCE_BANKS_DEF;
- mcg_cap &= MCE_CAP_DEF;
- mcg_cap |= banks;
- if (kvm_setup_mce(env, &mcg_cap)) {
- perror("kvm_setup_mce FAILED");
- } else {
- env->mcg_cap = mcg_cap;
- }
+ ret = kvm_get_mce_cap_supported(env->kvm_state, &mcg_cap, &banks);
+ if (ret < 0) {
+ fprintf(stderr, "kvm_get_mce_cap_supported: %s", strerror(-ret));
+ return ret;
+ }
+
+ if (banks > MCE_BANKS_DEF) {
+ banks = MCE_BANKS_DEF;
}
+ mcg_cap &= MCE_CAP_DEF;
+ mcg_cap |= banks;
+ ret = kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, &mcg_cap);
+ if (ret < 0) {
+ fprintf(stderr, "KVM_X86_SETUP_MCE: %s", strerror(-ret));
+ return ret;
+ }
+
+ env->mcg_cap = mcg_cap;
}
#endif
@@ -556,6 +606,7 @@ int kvm_arch_init(KVMState *s)
fprintf(stderr, "e820_add_entry() table is full\n");
return ret;
}
+ qemu_register_reset(kvm_unpoison_all, NULL);
return 0;
}
@@ -810,6 +861,7 @@ static int kvm_put_msrs(CPUState *env, int level)
kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs);
kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp);
kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip);
+ kvm_msr_entry_set(&msrs[n++], MSR_PAT, env->pat);
if (has_msr_star) {
kvm_msr_entry_set(&msrs[n++], MSR_STAR, env->star);
}
@@ -855,14 +907,10 @@ static int kvm_put_msrs(CPUState *env, int level)
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
@@ -1066,6 +1114,7 @@ static int kvm_get_msrs(CPUState *env)
msrs[n++].index = MSR_IA32_SYSENTER_CS;
msrs[n++].index = MSR_IA32_SYSENTER_ESP;
msrs[n++].index = MSR_IA32_SYSENTER_EIP;
+ msrs[n++].index = MSR_PAT;
if (has_msr_star) {
msrs[n++].index = MSR_STAR;
}
@@ -1121,6 +1170,9 @@ static int kvm_get_msrs(CPUState *env)
case MSR_IA32_SYSENTER_EIP:
env->sysenter_eip = msrs[i].data;
break;
+ case MSR_PAT:
+ env->pat = msrs[i].data;
+ break;
case MSR_STAR:
env->star = msrs[i].data;
break;
@@ -1373,6 +1425,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;
@@ -1509,13 +1566,38 @@ void kvm_arch_post_run(CPUState *env, struct kvm_run *run)
cpu_set_apic_base(env->apic_state, run->apic_base);
}
-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)) {
+ if (((env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK)) ||
+ (env->interrupt_request & CPU_INTERRUPT_NMI)) {
env->halted = 0;
}
if (env->interrupt_request & CPU_INTERRUPT_INIT) {
@@ -1536,64 +1618,10 @@ static int kvm_handle_halt(CPUState *env)
(env->eflags & IF_MASK)) &&
!(env->interrupt_request & CPU_INTERRUPT_NMI)) {
env->halted = 1;
- return 0;
+ return EXCP_HLT;
}
- return 1;
-}
-
-static bool host_supports_vmx(void)
-{
- uint32_t ecx, unused;
-
- host_cpuid(1, 0, &unused, &unused, &ecx, &unused);
- return ecx & CPUID_EXT_VMX;
-}
-
-#define VMX_INVALID_GUEST_STATE 0x80000021
-
-int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run)
-{
- uint64_t code;
- int ret = 0;
-
- switch (run->exit_reason) {
- case KVM_EXIT_HLT:
- DPRINTF("handle_hlt\n");
- ret = kvm_handle_halt(env);
- break;
- case KVM_EXIT_SET_TPR:
- ret = 1;
- break;
- case KVM_EXIT_FAIL_ENTRY:
- code = run->fail_entry.hardware_entry_failure_reason;
- fprintf(stderr, "KVM: entry failed, hardware error 0x%" PRIx64 "\n",
- code);
- if (host_supports_vmx() && code == VMX_INVALID_GUEST_STATE) {
- fprintf(stderr,
- "\nIf you're runnning a guest on an Intel machine without "
- "unrestricted mode\n"
- "support, the failure can be most likely due to the guest "
- "entering an invalid\n"
- "state for Intel VT. For example, the guest maybe running "
- "in big real mode\n"
- "which is not supported on less recent Intel processors."
- "\n\n");
- }
- ret = -1;
- break;
- case KVM_EXIT_EXCEPTION:
- fprintf(stderr, "KVM: exception %d exit (error code 0x%x)\n",
- run->ex.exception, run->ex.error_code);
- ret = -1;
- break;
- default:
- fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
- ret = -1;
- break;
- }
-
- return ret;
+ return 0;
}
#ifdef KVM_CAP_SET_GUEST_DEBUG
@@ -1703,31 +1731,31 @@ void kvm_arch_remove_all_hw_breakpoints(void)
static CPUWatchpoint hw_watchpoint;
-int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+static int kvm_handle_debug(struct kvm_debug_exit_arch *arch_info)
{
- int handle = 0;
+ int ret = 0;
int n;
if (arch_info->exception == 1) {
if (arch_info->dr6 & (1 << 14)) {
if (cpu_single_env->singlestep_enabled) {
- handle = 1;
+ ret = EXCP_DEBUG;
}
} else {
for (n = 0; n < 4; n++) {
if (arch_info->dr6 & (1 << n)) {
switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) {
case 0x0:
- handle = 1;
+ ret = EXCP_DEBUG;
break;
case 0x1:
- handle = 1;
+ ret = EXCP_DEBUG;
cpu_single_env->watchpoint_hit = &hw_watchpoint;
hw_watchpoint.vaddr = hw_breakpoint[n].addr;
hw_watchpoint.flags = BP_MEM_WRITE;
break;
case 0x3:
- handle = 1;
+ ret = EXCP_DEBUG;
cpu_single_env->watchpoint_hit = &hw_watchpoint;
hw_watchpoint.vaddr = hw_breakpoint[n].addr;
hw_watchpoint.flags = BP_MEM_ACCESS;
@@ -1737,17 +1765,18 @@ int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
}
}
} else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) {
- handle = 1;
+ ret = EXCP_DEBUG;
}
- if (!handle) {
+ if (ret == 0) {
cpu_synchronize_state(cpu_single_env);
assert(cpu_single_env->exception_injected == -1);
+ /* pass to guest */
cpu_single_env->exception_injected = arch_info->exception;
cpu_single_env->has_error_code = 0;
}
- return handle;
+ return ret;
}
void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
@@ -1778,178 +1807,68 @@ void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
}
#endif /* KVM_CAP_SET_GUEST_DEBUG */
-bool kvm_arch_stop_on_emulation_error(CPUState *env)
-{
- 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)
+static bool host_supports_vmx(void)
{
- 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,
- };
+ uint32_t ecx, unused;
- kvm_inject_x86_mce_on(env, &mce, ABORT_ON_ERROR);
- kvm_mce_broadcast_rest(env);
+ host_cpuid(1, 0, &unused, &unused, &ecx, &unused);
+ return ecx & CPUID_EXT_VMX;
}
-#endif
+#define VMX_INVALID_GUEST_STATE 0x80000021
-int kvm_arch_on_sigbus_vcpu(CPUState *env, int code, void *addr)
+int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run)
{
-#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();
- }
- }
+ uint64_t code;
+ int ret;
- 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;
+ switch (run->exit_reason) {
+ case KVM_EXIT_HLT:
+ DPRINTF("handle_hlt\n");
+ ret = kvm_handle_halt(env);
+ break;
+ case KVM_EXIT_SET_TPR:
+ ret = 0;
+ break;
+ case KVM_EXIT_FAIL_ENTRY:
+ code = run->fail_entry.hardware_entry_failure_reason;
+ fprintf(stderr, "KVM: entry failed, hardware error 0x%" PRIx64 "\n",
+ code);
+ if (host_supports_vmx() && code == VMX_INVALID_GUEST_STATE) {
+ fprintf(stderr,
+ "\nIf you're runnning a guest on an Intel machine without "
+ "unrestricted mode\n"
+ "support, the failure can be most likely due to the guest "
+ "entering an invalid\n"
+ "state for Intel VT. For example, the guest maybe running "
+ "in big real mode\n"
+ "which is not supported on less recent Intel processors."
+ "\n\n");
}
+ ret = -1;
+ break;
+ case KVM_EXIT_EXCEPTION:
+ fprintf(stderr, "KVM: exception %d exit (error code 0x%x)\n",
+ run->ex.exception, run->ex.error_code);
+ ret = -1;
+ break;
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ case KVM_EXIT_DEBUG:
+ DPRINTF("kvm_exit_debug\n");
+ ret = kvm_handle_debug(&run->debug.arch);
+ break;
+#endif /* KVM_CAP_SET_GUEST_DEBUG */
+ default:
+ fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
+ ret = -1;
+ break;
}
- return 0;
+
+ return ret;
}
-int kvm_arch_on_sigbus(int code, void *addr)
+bool kvm_arch_stop_on_emulation_error(CPUState *env)
{
-#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;
+ return !(env->cr[0] & CR0_PE_MASK) ||
+ ((env->segs[R_CS].selector & 3) != 3);
}
diff --git a/target-i386/kvm_x86.h b/target-i386/kvm_x86.h
deleted file mode 100644
index 9d7b584267..0000000000
--- a/target-i386/kvm_x86.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * QEMU KVM support
- *
- * Copyright (C) 2009 Red Hat Inc.
- * Copyright IBM, Corp. 2008
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#ifndef __KVM_X86_H__
-#define __KVM_X86_H__
-
-#define ABORT_ON_ERROR 0x01
-#define MCE_BROADCAST 0x02
-
-void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
- uint64_t mcg_status, uint64_t addr, uint64_t misc,
- int flag);
-
-#endif
diff --git a/target-i386/machine.c b/target-i386/machine.c
index d78eceb779..6384f54b95 100644
--- a/target-i386/machine.c
+++ b/target-i386/machine.c
@@ -491,6 +491,8 @@ static const VMStateDescription vmstate_cpu = {
VMSTATE_UINT64_V(xcr0, CPUState, 12),
VMSTATE_UINT64_V(xstate_bv, CPUState, 12),
VMSTATE_YMMH_REGS_VARS(ymmh_regs, CPUState, CPU_NB_REGS, 12),
+
+ VMSTATE_UINT64_V(pat, CPUState, 13),
VMSTATE_END_OF_LIST()
/* The above list is not sorted /wrt version numbers, watch out! */
},