From: Andrey Smetanin <asmetanin@xxxxxxxxxxxxx> KVM Hyper-V based guests can notify hypervisor about occurred guest crash. This patch does handling of KVM crash event by sending to libvirt guest panic event that allows to gather guest crash dump by QEMU/LIBVIRT. Add support of HV_X64_MSR_CRASH_P0-P4, HV_X64_MSR_CRASH_CTL msrs. The idea is to provide functionality equal to pvpanic device without QEMU guest agent for Windows. The idea is borrowed from Linux HyperV bus driver and validated against Windows 2k12. Signed-off-by: Andrey Smetanin <asmetanin@xxxxxxxxxxxxx> Signed-off-by: Denis V. Lunev <den@xxxxxxxxxx> CC: Paolo Bonzini <pbonzini@xxxxxxxxxx> CC: Andreas Färber <afaerber@xxxxxxx> --- include/sysemu/kvm.h | 2 ++ include/sysemu/sysemu.h | 1 + kvm-all.c | 7 ++++ linux-headers/asm-x86/hyperv.h | 16 +++++++++ linux-headers/linux/kvm.h | 2 ++ target-arm/kvm.c | 5 +++ target-i386/cpu-qom.h | 1 + target-i386/cpu.c | 1 + target-i386/cpu.h | 3 ++ target-i386/kvm.c | 80 ++++++++++++++++++++++++++++++++++++++++++ target-i386/machine.c | 23 ++++++++++++ target-mips/kvm.c | 5 +++ target-ppc/kvm.c | 5 +++ target-s390x/kvm.c | 5 +++ vl.c | 6 ++++ 15 files changed, 162 insertions(+) diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index f459fbd..3c0fa02 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -257,6 +257,8 @@ extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run); MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run); +int kvm_arch_handle_hv_crash(CPUState *cpu); + int kvm_arch_handle_exit(CPUState *cpu, struct kvm_run *run); int kvm_arch_process_async_events(CPUState *cpu); diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index df80951..70164c9 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -68,6 +68,7 @@ int qemu_reset_requested_get(void); void qemu_system_killed(int signal, pid_t pid); void qemu_devices_reset(void); void qemu_system_reset(bool report); +void qemu_system_guest_panicked(void); void qemu_add_exit_notifier(Notifier *notify); void qemu_remove_exit_notifier(Notifier *notify); diff --git a/kvm-all.c b/kvm-all.c index 53e01d4..1528fb5 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -1844,6 +1844,13 @@ int kvm_cpu_exec(CPUState *cpu) qemu_system_reset_request(); ret = EXCP_INTERRUPT; break; + case KVM_SYSTEM_EVENT_CRASH: + if (run->system_event.flags & KVM_SYSTEM_EVENT_FL_HV_CRASH) { + kvm_arch_handle_hv_crash(cpu); + } + qemu_system_guest_panicked(); + ret = 0; + break; default: DPRINTF("kvm_arch_handle_exit\n"); ret = kvm_arch_handle_exit(cpu, run); diff --git a/linux-headers/asm-x86/hyperv.h b/linux-headers/asm-x86/hyperv.h index ce6068d..aec7d27 100644 --- a/linux-headers/asm-x86/hyperv.h +++ b/linux-headers/asm-x86/hyperv.h @@ -108,6 +108,8 @@ #define HV_X64_HYPERCALL_PARAMS_XMM_AVAILABLE (1 << 4) /* Support for a virtual guest idle state is available */ #define HV_X64_GUEST_IDLE_STATE_AVAILABLE (1 << 5) +/* Guest crash data handler available */ +#define HV_X64_GUEST_CRASH_MSR_AVAILABLE (1 << 10) /* * Implementation recommendations. Indicates which behaviors the hypervisor @@ -199,6 +201,20 @@ #define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 #define HV_X64_MSR_STIMER3_COUNT 0x400000B7 +/* Hypev-V guest crash notification MSR's */ +#define HV_X64_MSR_CRASH_P0 0x40000100 +#define HV_X64_MSR_CRASH_P1 0x40000101 +#define HV_X64_MSR_CRASH_P2 0x40000102 +#define HV_X64_MSR_CRASH_P3 0x40000103 +#define HV_X64_MSR_CRASH_P4 0x40000104 +#define HV_X64_MSR_CRASH_CTL 0x40000105 +#define HV_X64_MSR_CRASH_CTL_NOTIFY (1ULL << 63) +#define HV_X64_MSR_CRASH_CTL_CONTENTS \ + (HV_X64_MSR_CRASH_CTL_NOTIFY) + +#define HV_X64_MSR_CRASH_PARAMS \ + (1 + (HV_X64_MSR_CRASH_P4 - HV_X64_MSR_CRASH_P0)) + #define HV_X64_MSR_HYPERCALL_ENABLE 0x00000001 #define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT 12 #define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_MASK \ diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index fad9e5c..46cb7e0 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -317,6 +317,8 @@ struct kvm_run { struct { #define KVM_SYSTEM_EVENT_SHUTDOWN 1 #define KVM_SYSTEM_EVENT_RESET 2 +#define KVM_SYSTEM_EVENT_CRASH 3 +#define KVM_SYSTEM_EVENT_FL_HV_CRASH (1ULL << 0) __u32 type; __u64 flags; } system_event; diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 548bfd7..c19899e 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -512,6 +512,11 @@ MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) return MEMTXATTRS_UNSPECIFIED; } +int kvm_arch_handle_hv_crash(CPUState *cs) +{ + return 0; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { return 0; diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index 7a4fddd..c35b624 100644 --- a/target-i386/cpu-qom.h +++ b/target-i386/cpu-qom.h @@ -89,6 +89,7 @@ typedef struct X86CPU { bool hyperv_relaxed_timing; int hyperv_spinlock_attempts; bool hyperv_time; + bool hyperv_crash; bool check_cpuid; bool enforce_cpuid; bool expose_kvm; diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 4e7cdaa..af0552a 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -3117,6 +3117,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("hv-relaxed", X86CPU, hyperv_relaxed_timing, false), DEFINE_PROP_BOOL("hv-vapic", X86CPU, hyperv_vapic, false), DEFINE_PROP_BOOL("hv-time", X86CPU, hyperv_time, false), + DEFINE_PROP_BOOL("hv-crash", X86CPU, hyperv_crash, false), DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, false), DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false), DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true), diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 603aaf0..474a93e 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -21,6 +21,7 @@ #include "config.h" #include "qemu-common.h" +#include <asm/hyperv.h> #ifdef TARGET_X86_64 #define TARGET_LONG_BITS 64 @@ -904,6 +905,8 @@ typedef struct CPUX86State { uint64_t msr_hv_guest_os_id; uint64_t msr_hv_vapic; uint64_t msr_hv_tsc; + uint64_t msr_hv_crash_prm[HV_X64_MSR_CRASH_PARAMS]; + uint64_t msr_hv_crash_ctl; /* exception/interrupt handling */ int error_code; diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 5a236e3..690677b 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -80,6 +80,7 @@ static int lm_capable_kernel; static bool has_msr_hv_hypercall; static bool has_msr_hv_vapic; static bool has_msr_hv_tsc; +static bool has_msr_hv_crash; static bool has_msr_mtrr; static bool has_msr_xss; @@ -516,6 +517,11 @@ int kvm_arch_init_vcpu(CPUState *cs) c->eax |= 0x200; has_msr_hv_tsc = true; } + if (cpu->hyperv_crash) { + c->edx |= HV_X64_GUEST_CRASH_MSR_AVAILABLE; + has_msr_hv_crash = true; + } + c = &cpuid_data.entries[cpuid_i++]; c->function = HYPERV_CPUID_ENLIGHTMENT_INFO; if (cpu->hyperv_relaxed_timing) { @@ -762,6 +768,9 @@ void kvm_arch_reset_vcpu(X86CPU *cpu) } else { env->mp_state = KVM_MP_STATE_RUNNABLE; } + if (has_msr_hv_crash) { + env->msr_hv_crash_ctl = HV_X64_MSR_CRASH_CTL_CONTENTS; + } } void kvm_arch_do_init_vcpu(X86CPU *cpu) @@ -1322,6 +1331,16 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_REFERENCE_TSC, env->msr_hv_tsc); } + if (has_msr_hv_crash) { + int j; + + for (j = 0; j < HV_X64_MSR_CRASH_PARAMS; j++) + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_CRASH_P0 + j, + env->msr_hv_crash_prm[j]); + + kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_CRASH_CTL, + env->msr_hv_crash_ctl); + } if (has_msr_mtrr) { kvm_msr_entry_set(&msrs[n++], MSR_MTRRdefType, env->mtrr_deftype); kvm_msr_entry_set(&msrs[n++], @@ -1674,6 +1693,14 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_hv_tsc) { msrs[n++].index = HV_X64_MSR_REFERENCE_TSC; } + if (has_msr_hv_crash) { + int j; + + for (j = 0; j < HV_X64_MSR_CRASH_PARAMS; j++) { + msrs[n++].index = HV_X64_MSR_CRASH_P0 + j; + } + msrs[n++].index = HV_X64_MSR_CRASH_CTL; + } if (has_msr_mtrr) { msrs[n++].index = MSR_MTRRdefType; msrs[n++].index = MSR_MTRRfix64K_00000; @@ -1818,6 +1845,12 @@ static int kvm_get_msrs(X86CPU *cpu) case HV_X64_MSR_REFERENCE_TSC: env->msr_hv_tsc = msrs[i].data; break; + case HV_X64_MSR_CRASH_CTL: + env->msr_hv_crash_ctl = msrs[i].data; + break; + case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4: + env->msr_hv_crash_prm[index - HV_X64_MSR_CRASH_P0] = msrs[i].data; + break; case MSR_MTRRdefType: env->mtrr_deftype = msrs[i].data; break; @@ -2540,6 +2573,53 @@ static bool host_supports_vmx(void) return ecx & CPUID_EXT_VMX; } +int kvm_arch_handle_hv_crash(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + struct { + struct kvm_msrs info; + struct kvm_msr_entry entries[HV_X64_MSR_CRASH_PARAMS + 1]; + } msr_data; + struct kvm_msr_entry *msrs = msr_data.entries; + int ret, n, i; + + if (!has_msr_hv_crash) { + return -EINVAL; + } + + for (n = 0; n < HV_X64_MSR_CRASH_PARAMS; n++) { + msrs[n].index = HV_X64_MSR_CRASH_P0 + n; + } + + msrs[n++].index = HV_X64_MSR_CRASH_CTL; + msr_data.info = (struct kvm_msrs) { + .nmsrs = n, + }; + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, &msr_data); + if (ret < 0) { + return ret; + } + + for (i = 0; i < ret; i++) { + uint32_t index = msrs[i].index; + + switch (index) { + case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4: + env->msr_hv_crash_prm[index - HV_X64_MSR_CRASH_P0] = msrs[i].data; + break; + case HV_X64_MSR_CRASH_CTL: + env->msr_hv_crash_ctl = msrs[i].data; + break; + default: + break; + } + } + + return 0; +} + #define VMX_INVALID_GUEST_STATE 0x80000021 int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) diff --git a/target-i386/machine.c b/target-i386/machine.c index a0df64b..15b3f31 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -661,6 +661,28 @@ static const VMStateDescription vmstate_msr_hyperv_time = { } }; +static bool hyperv_crash_enable_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return (env->msr_hv_crash_ctl & HV_X64_MSR_CRASH_CTL_CONTENTS) ? + true : false; +} + +static const VMStateDescription vmstate_msr_hyperv_crash = { + .name = "cpu/msr_hyperv_crash", + .version_id = 1, + .minimum_version_id = 1, + .needed = hyperv_crash_enable_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.msr_hv_crash_ctl, X86CPU), + VMSTATE_UINT64_ARRAY(env.msr_hv_crash_prm, + X86CPU, HV_X64_MSR_CRASH_PARAMS), + VMSTATE_END_OF_LIST() + } +}; + static bool avx512_needed(void *opaque) { X86CPU *cpu = opaque; @@ -842,6 +864,7 @@ VMStateDescription vmstate_x86_cpu = { &vmstate_msr_hypercall_hypercall, &vmstate_msr_hyperv_vapic, &vmstate_msr_hyperv_time, + &vmstate_msr_hyperv_crash, &vmstate_avx512, &vmstate_xss, NULL diff --git a/target-mips/kvm.c b/target-mips/kvm.c index 948619f..1ac7732 100644 --- a/target-mips/kvm.c +++ b/target-mips/kvm.c @@ -122,6 +122,11 @@ int kvm_arch_process_async_events(CPUState *cs) return cs->halted; } +int kvm_arch_handle_hv_crash(CPUState *cs) +{ + return 0; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { int ret; diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index afb4696..af34198 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -1564,6 +1564,11 @@ static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run) return handle; } +int kvm_arch_handle_hv_crash(CPUState *cs) +{ + return 0; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { PowerPCCPU *cpu = POWERPC_CPU(cs); diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index b02ff8d..07d0914 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -2002,6 +2002,11 @@ static int kvm_arch_handle_debug_exit(S390CPU *cpu) return ret; } +int kvm_arch_handle_hv_crash(CPUState *cs) +{ + return 0; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { S390CPU *cpu = S390_CPU(cs); diff --git a/vl.c b/vl.c index 1a920e6..9b60f95 100644 --- a/vl.c +++ b/vl.c @@ -1725,6 +1725,12 @@ void qemu_system_reset(bool report) cpu_synchronize_all_post_reset(); } +void qemu_system_guest_panicked(void) +{ + qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); + vm_stop(RUN_STATE_GUEST_PANICKED); +} + void qemu_system_reset_request(void) { if (no_reboot) { -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in