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: 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 libkvm-all.h | 4 +++ qemu-kvm-x86.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ qemu-kvm.c | 40 +++++++++++++++++++++++++++++++++ qemu-kvm.h | 3 ++ target-i386/helper.c | 5 ++++ target-i386/machine.c | 4 +-- 8 files changed, 130 insertions(+), 2 deletions(-) --- a/qemu-kvm-x86.c +++ b/qemu-kvm-x86.c @@ -444,6 +444,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, @@ -1301,6 +1334,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 @@ -241,4 +241,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 @@ -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 @@ -2845,3 +2845,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++) { --- a/libkvm-all.h +++ b/libkvm-all.h @@ -109,6 +109,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 /*! -- 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