KVM ioctls are used to initialize MCE simulation and inject MCE. The real MCE simulation is implemented in Linux kernel. ChangeLog: 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/kernel/arch/x86/include/asm/kvm.h | 1 kvm/kernel/include/linux/kvm.h | 21 +++++++++++++++++ kvm/libkvm/libkvm-x86.c | 39 +++++++++++++++++++++++++++++++++ kvm/libkvm/libkvm.h | 4 +++ qemu-kvm-x86.c | 23 +++++++++++++++++++ qemu-kvm.c | 40 ++++++++++++++++++++++++++++++++++ qemu-kvm.h | 3 ++ target-i386/helper.c | 5 ++++ 8 files changed, 136 insertions(+) --- a/kvm/libkvm/libkvm-x86.c +++ b/kvm/libkvm/libkvm-x86.c @@ -378,6 +378,45 @@ int kvm_set_msrs(kvm_context_t kvm, int 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_context_t kvm, int vcpu, uint64_t *mcg_cap) +{ +#ifdef KVM_CAP_MCE + int r; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_MCE); + if (r > 0) + return ioctl(kvm->vcpu_fd[vcpu], KVM_X86_SETUP_MCE, mcg_cap); +#endif + return -ENOSYS; +} + +int kvm_set_mce(kvm_context_t kvm, int vcpu, struct kvm_x86_mce *m) +{ +#ifdef KVM_CAP_MCE + int r; + + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_MCE); + if (r > 0) + return ioctl(kvm->vcpu_fd[vcpu], KVM_X86_SET_MCE, m); +#endif + return -ENOSYS; +} + static void print_seg(FILE *file, const char *name, struct kvm_segment *seg) { fprintf(stderr, --- a/kvm/libkvm/libkvm.h +++ b/kvm/libkvm/libkvm.h @@ -27,6 +27,10 @@ typedef struct kvm_context *kvm_context_ struct kvm_msr_list *kvm_get_msr_list(kvm_context_t); int kvm_get_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n); int kvm_set_msrs(kvm_context_t, int vcpu, 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_context_t, int vcpu, uint64_t *mcg_cap); +struct kvm_x86_mce; +int kvm_set_mce(kvm_context_t, int vcpu, struct kvm_x86_mce *mce); #endif /*! --- a/qemu-kvm-x86.c +++ b/qemu-kvm-x86.c @@ -566,6 +566,29 @@ int kvm_arch_qemu_init_env(CPUState *cen do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©); kvm_setup_cpuid2(kvm_context, cenv->cpu_index, 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(kvm_context, cenv->cpu_index, &mcg_cap)) + perror("kvm_setup_mce FAILED"); + else + cenv->mcg_cap = mcg_cap; + } + } +#endif + return 0; } --- a/qemu-kvm.h +++ b/qemu-kvm.h @@ -233,4 +233,7 @@ static inline void cpu_synchronize_state } } +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 @@ -1442,6 +1442,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/kernel/include/linux/kvm.h +++ b/kvm/kernel/include/linux/kvm.h @@ -455,6 +455,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 #ifdef KVM_CAP_IRQ_ROUTING @@ -494,6 +497,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 */ @@ -581,6 +598,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/kernel/arch/x86/include/asm/kvm.h +++ b/kvm/kernel/arch/x86/include/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 @@ -1425,3 +1425,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(kvm_context, data->env->cpu_index, 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
Attachment:
signature.asc
Description: This is a digitally signed message part