KVM ioctls are used to initialize MCE simulation and inject MCE. The real MCE simulation is implemented in Linux kernel. The Kernel part has been merged. ChangeLog: v7: - Re-based on qemu-kvm.git/next branch v6: - Re-based on latest qemu-kvm.git v5: - Re-based on latest qemu-kvm.git v3: - Re-based on qemu/tcg MCE support patch v2: - Use new kernel MCE capability exportion interface. Signed-off-by: Huang Ying <ying.huang@xxxxxxxxx> --- kvm/include/linux/kvm.h | 20 ++++++++++++++++ kvm/include/x86/asm/kvm.h | 1 qemu-kvm-x86.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ qemu-kvm.c | 40 +++++++++++++++++++++++++++++++++ qemu-kvm.h | 19 +++++++++++++++ target-i386/helper.c | 5 ++++ target-i386/machine.c | 4 +-- 7 files changed, 142 insertions(+), 2 deletions(-) --- a/qemu-kvm-x86.c +++ b/qemu-kvm-x86.c @@ -432,6 +432,39 @@ int kvm_set_msrs(kvm_vcpu_context_t vcpu return r; } +int kvm_get_mce_cap_supported(kvm_context_t kvm, uint64_t *mce_cap, + int *max_banks) +{ +#ifdef KVM_CAP_MCE + int r; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_MCE); + if (r > 0) { + *max_banks = r; + return ioctl(kvm->fd, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap); + } +#endif + return -ENOSYS; +} + +int kvm_setup_mce(kvm_vcpu_context_t vcpu, uint64_t *mcg_cap) +{ +#ifdef KVM_CAP_MCE + return ioctl(vcpu->fd, KVM_X86_SETUP_MCE, mcg_cap); +#else + return -ENOSYS; +#endif +} + +int kvm_set_mce(kvm_vcpu_context_t vcpu, struct kvm_x86_mce *m) +{ +#ifdef KVM_CAP_MCE + return ioctl(vcpu->fd, KVM_X86_SET_MCE, m); +#else + return -ENOSYS; +#endif +} + static void print_seg(FILE *file, const char *name, struct kvm_segment *seg) { fprintf(stderr, @@ -1285,6 +1318,28 @@ int kvm_arch_qemu_init_env(CPUState *cen kvm_setup_cpuid2(cenv->kvm_cpu_state.vcpu_ctx, cpuid_nent, cpuid_ent); +#ifdef KVM_CAP_MCE + if (((cenv->cpuid_version >> 8)&0xF) >= 6 + && (cenv->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA) + && kvm_check_extension(kvm_context, KVM_CAP_MCE) > 0) { + uint64_t mcg_cap; + int banks; + + if (kvm_get_mce_cap_supported(kvm_context, &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(cenv->kvm_cpu_state.vcpu_ctx, &mcg_cap)) + perror("kvm_setup_mce FAILED"); + else + cenv->mcg_cap = mcg_cap; + } + } +#endif + return 0; } --- a/qemu-kvm.h +++ b/qemu-kvm.h @@ -116,6 +116,10 @@ int try_push_interrupts(kvm_context_t kv struct kvm_msr_list *kvm_get_msr_list(kvm_context_t); int kvm_get_msrs(kvm_vcpu_context_t, struct kvm_msr_entry *msrs, int n); int kvm_set_msrs(kvm_vcpu_context_t, struct kvm_msr_entry *msrs, int n); +int kvm_get_mce_cap_supported(kvm_context_t, uint64_t *mce_cap, int *max_banks); +int kvm_setup_mce(kvm_vcpu_context_t vcpu, uint64_t *mcg_cap); +struct kvm_x86_mce; +int kvm_set_mce(kvm_vcpu_context_t vcpu, struct kvm_x86_mce *mce); #endif /*! @@ -620,6 +624,21 @@ int kvm_inject_nmi(kvm_vcpu_context_t vc #endif /*! + * \brief Simulate an x86 MCE + * + * This allows you to simulate a x86 MCE. + * + * \param cenv Which virtual CPU should get MCE injected + * \param bank Bank number + * \param status MSR_MCI_STATUS + * \param mcg_status MSR_MCG_STATUS + * \param addr MSR_MCI_ADDR + * \param misc MSR_MCI_MISC + */ +void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc); + +/*! * \brief Query wheather in kernel pit is used * * \param kvm Pointer to the current kvm_context --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -1509,6 +1509,11 @@ void cpu_inject_x86_mce(CPUState *cenv, unsigned bank_num = mcg_cap & 0xff; uint64_t *banks = cenv->mce_banks; + if (kvm_enabled()) { + kvm_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc); + return; + } + if (bank >= bank_num || !(status & MCI_STATUS_VAL)) return; --- a/kvm/include/linux/kvm.h +++ b/kvm/include/linux/kvm.h @@ -463,6 +463,9 @@ struct kvm_trace_rec { #define KVM_CAP_ASSIGN_DEV_IRQ 29 /* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30 +#ifdef __KVM_HAVE_MCE +#define KVM_CAP_MCE 31 +#endif #define KVM_CAP_PIT2 33 #define KVM_CAP_PIT_STATE2 35 @@ -504,6 +507,19 @@ struct kvm_irq_routing { #endif +#ifdef KVM_CAP_MCE +/* x86 MCE */ +struct kvm_x86_mce { + __u64 status; + __u64 addr; + __u64 misc; + __u64 mcg_status; + __u8 bank; + __u8 pad1[7]; + __u64 pad2[3]; +}; +#endif + /* * ioctls for VM fds */ @@ -592,6 +608,10 @@ struct kvm_irq_routing { #define KVM_NMI _IO(KVMIO, 0x9a) /* Available with KVM_CAP_SET_GUEST_DEBUG */ #define KVM_SET_GUEST_DEBUG _IOW(KVMIO, 0x9b, struct kvm_guest_debug) +/* MCE for x86 */ +#define KVM_X86_SETUP_MCE _IOW(KVMIO, 0x9c, __u64) +#define KVM_X86_GET_MCE_CAP_SUPPORTED _IOR(KVMIO, 0x9d, __u64) +#define KVM_X86_SET_MCE _IOW(KVMIO, 0x9e, struct kvm_x86_mce) /* * Deprecated interfaces --- a/kvm/include/x86/asm/kvm.h +++ b/kvm/include/x86/asm/kvm.h @@ -57,6 +57,7 @@ #define __KVM_HAVE_USER_NMI #define __KVM_HAVE_GUEST_DEBUG #define __KVM_HAVE_MSIX +#define __KVM_HAVE_MCE /* Architectural interrupt line count. */ #define KVM_NR_INTERRUPTS 256 --- a/qemu-kvm.c +++ b/qemu-kvm.c @@ -2703,3 +2703,43 @@ int kvm_set_boot_cpu_id(uint32_t id) { return kvm_set_boot_vcpu_id(kvm_context, id); } + +#ifdef TARGET_I386 +#ifdef KVM_CAP_MCE +struct kvm_x86_mce_data +{ + CPUState *env; + struct kvm_x86_mce *mce; +}; + +static void kvm_do_inject_x86_mce(void *_data) +{ + struct kvm_x86_mce_data *data = _data; + int r; + + r = kvm_set_mce(data->env->kvm_cpu_state.vcpu_ctx, data->mce); + if (r < 0) + perror("kvm_set_mce FAILED"); +} +#endif + +void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc) +{ +#ifdef KVM_CAP_MCE + struct kvm_x86_mce mce = { + .bank = bank, + .status = status, + .mcg_status = mcg_status, + .addr = addr, + .misc = misc, + }; + struct kvm_x86_mce_data data = { + .env = cenv, + .mce = &mce, + }; + + on_vcpu(cenv, kvm_do_inject_x86_mce, &data); +#endif +} +#endif --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -164,7 +164,7 @@ void cpu_save(QEMUFile *f, void *opaque) /* MCE */ qemu_put_be64s(f, &env->mcg_cap); - if (env->mcg_cap) { + if (env->mcg_cap && !kvm_enabled()) { qemu_put_be64s(f, &env->mcg_status); qemu_put_be64s(f, &env->mcg_ctl); for (i = 0; i < (env->mcg_cap & 0xff); i++) { @@ -367,7 +367,7 @@ int cpu_load(QEMUFile *f, void *opaque, if (version_id >= 10) { qemu_get_be64s(f, &env->mcg_cap); - if (env->mcg_cap) { + if (env->mcg_cap && !kvm_enabled()) { qemu_get_be64s(f, &env->mcg_status); qemu_get_be64s(f, &env->mcg_ctl); for (i = 0; i < (env->mcg_cap & 0xff); i++) { -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html