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. Note that it's usually impossible to understand from Hyper-V crash msr's that crash happened because ctl msr always contains the same value HV_X64_MSR_CRASH_CTL_NOTIFY. To solve it add a particular value - hv_crash_occurred inside CPU state and migrate this value with crash msr's. 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> --- hw/misc/pvpanic.c | 3 +-- include/sysemu/kvm.h | 2 ++ include/sysemu/sysemu.h | 1 + kvm-all.c | 5 ++++ 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 | 4 ++++ target-i386/kvm.c | 53 ++++++++++++++++++++++++++++++++++++++++++ target-i386/machine.c | 24 +++++++++++++++++++ target-mips/kvm.c | 5 ++++ target-ppc/kvm.c | 5 ++++ target-s390x/kvm.c | 16 ++++++------- vl.c | 6 +++++ 16 files changed, 138 insertions(+), 11 deletions(-) diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 994f8af..3709488 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -41,8 +41,7 @@ static void handle_event(int event) } if (event & PVPANIC_PANICKED) { - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); - vm_stop(RUN_STATE_GUEST_PANICKED); + qemu_system_guest_panicked(); return; } } diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index f459fbd..c62fd0c 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_crash(CPUState *cpu, struct kvm_run *run); + 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..d35dc1e 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -1844,6 +1844,11 @@ int kvm_cpu_exec(CPUState *cpu) qemu_system_reset_request(); ret = EXCP_INTERRUPT; break; + case KVM_SYSTEM_EVENT_CRASH: + kvm_arch_handle_crash(cpu, run); + 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..8cc5571 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_crash(CPUState *cs, struct kvm_run *run) +{ + 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 36b07f9..04a8408 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..2958cdc 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,9 @@ 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; + uint8_t hv_crash_occurred; /* exception/interrupt handling */ int error_code; diff --git a/target-i386/kvm.c b/target-i386/kvm.c index daced5c..1f887cb 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -79,6 +79,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; @@ -515,6 +516,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) { @@ -761,6 +767,10 @@ 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_NOTIFY; + env->hv_crash_occurred = 0; + } } void kvm_arch_do_init_vcpu(X86CPU *cpu) @@ -1321,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++], @@ -1673,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; @@ -1817,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; @@ -2539,6 +2573,25 @@ static bool host_supports_vmx(void) return ecx & CPUID_EXT_VMX; } +static int kvm_arch_handle_hv_crash(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + /* Mark that Hyper-v guest crash occurred */ + env->hv_crash_occurred = 1; + + return 0; +} + +int kvm_arch_handle_crash(CPUState *cs, struct kvm_run *run) +{ + if (run->system_event.flags & KVM_SYSTEM_EVENT_FL_HV_CRASH) { + return kvm_arch_handle_hv_crash(cs); + } + 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..4f72ba8 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -661,6 +661,29 @@ 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_UINT8(env.hv_crash_occurred, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + static bool avx512_needed(void *opaque) { X86CPU *cpu = opaque; @@ -842,6 +865,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..01a6350 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_crash(CPUState *cs, struct kvm_run *run) +{ + 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..6b53a85 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_crash(CPUState *cs, struct kvm_run *run) +{ + 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 135111a..79317c8 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -1796,13 +1796,6 @@ static bool is_special_wait_psw(CPUState *cs) return cs->kvm_run->psw_addr == 0xfffUL; } -static void guest_panicked(void) -{ - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, - &error_abort); - vm_stop(RUN_STATE_GUEST_PANICKED); -} - static void unmanageable_intercept(S390CPU *cpu, const char *str, int pswoffset) { CPUState *cs = CPU(cpu); @@ -1811,7 +1804,7 @@ static void unmanageable_intercept(S390CPU *cpu, const char *str, int pswoffset) str, cs->cpu_index, ldq_phys(cs->as, cpu->env.psa + pswoffset), ldq_phys(cs->as, cpu->env.psa + pswoffset + 8)); s390_cpu_halt(cpu); - guest_panicked(); + qemu_system_guest_panicked(); } static int handle_intercept(S390CPU *cpu) @@ -1844,7 +1837,7 @@ static int handle_intercept(S390CPU *cpu) if (is_special_wait_psw(cs)) { qemu_system_shutdown_request(); } else { - guest_panicked(); + qemu_system_guest_panicked(); } } r = EXCP_HALTED; @@ -2002,6 +1995,11 @@ static int kvm_arch_handle_debug_exit(S390CPU *cpu) return ret; } +int kvm_arch_handle_crash(CPUState *cs, struct kvm_run *run) +{ + 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 69ad90c..38eee1f 100644 --- a/vl.c +++ b/vl.c @@ -1721,6 +1721,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) { -- 2.1.4 -- 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