diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2011-03-21 17:42:20 -0500 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2011-03-21 17:42:20 -0500 |
commit | 31b7c261a207e1e670d737ec78a87dd031bd8f73 (patch) | |
tree | ef070dd56c118c8761df7baf0a457fc2c5ff9dfa /target-i386 | |
parent | 8b06c62ae48b67b320f7420dcd4854c5559e1532 (diff) | |
parent | dc7a09cfe47679d89289101cc9eb387c45e48fe7 (diff) |
Merge remote branch 'qemu-kvm/uq/master' into staging
Diffstat (limited to 'target-i386')
-rw-r--r-- | target-i386/cpu.h | 13 | ||||
-rw-r--r-- | target-i386/cpuid.c | 1 | ||||
-rw-r--r-- | target-i386/exec.h | 15 | ||||
-rw-r--r-- | target-i386/helper.c | 190 | ||||
-rw-r--r-- | target-i386/kvm.c | 593 | ||||
-rw-r--r-- | target-i386/kvm_x86.h | 25 | ||||
-rw-r--r-- | target-i386/machine.c | 2 |
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, ¶ms); + 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, ¶ms); } } } @@ -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! */ }, |