KVM ioctls are used to initialize MCE simulation and inject MCE. The real MCE simulation is implemented in Linux kernel. ChangeLog: 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 | 21 +++++++++++++++++++++ kvm/include/x86/asm/kvm.h | 1 + libkvm-all.h | 4 ++++ qemu-kvm-x86.c | 22 ++++++++++++++++++++++ qemu-kvm.c | 40 ++++++++++++++++++++++++++++++++++++++++ qemu-kvm.h | 3 +++ target-i386/helper.c | 5 +++++ target-i386/libkvm.c | 33 +++++++++++++++++++++++++++++++++ target-i386/machine.c | 4 ++-- 9 files changed, 131 insertions(+), 2 deletions(-) --- a/target-i386/libkvm.c +++ b/target-i386/libkvm.c @@ -380,6 +380,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, --- a/qemu-kvm-x86.c +++ b/qemu-kvm-x86.c @@ -598,6 +598,28 @@ int kvm_arch_qemu_init_env(CPUState *cen kvm_trim_features(&cenv->cpuid_ext3_features, kvm_arch_get_supported_cpuid(cenv, 0x80000001, R_ECX)); +#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 @@ -245,4 +245,7 @@ static inline int kvm_set_migration_log( return kvm_physical_memory_set_dirty_tracking(enable); } +void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc); + #endif --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -1445,6 +1445,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 #ifdef KVM_CAP_IRQ_ROUTING @@ -503,6 +506,20 @@ 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; + __u16 pad2; + __u32 pad3; +}; +#endif + /* * ioctls for VM fds */ @@ -591,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 @@ -1439,3 +1439,43 @@ void qemu_kvm_cpu_stop(CPUState *env) if (kvm_enabled()) env->kvm_cpu_state.stopped = 1; } + +#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 @@ -163,7 +163,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++) { @@ -393,7 +393,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++) { --- a/libkvm-all.h +++ b/libkvm-all.h @@ -29,6 +29,10 @@ typedef struct kvm_vcpu_context *kvm_vcp 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 /*! -- 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