VMware accepts a structured guest-crash event, but a host configured with
backdoor.coredumpOnCrash may terminate the VM before the hypercall
returns. Sending the event from a panic notifier could prevent later kmsg
dumpers and a loaded crash kernel from running.

Register a second panic-only kmsg dumper with late_initcall_sync(). Kmsg
dumpers run in registration order, placing the report after the early
VMware logger and built-in dumpers registered by then. This leaves no
report before the late initcall, but avoids losing dump output to a
terminating host.

Skip the event when a crash kernel is loaded. The earlier logger can still
write panic text to vmware.log before __crash_kexec(). If kdump later
fails, no structured event is sent; issuing it first could prevent kdump
from running at all.

Issue the event at most once. With CONFIG_PRINTK=n, install a late
INT_MIN panic notifier. An unexpected dumper registration failure with
printk enabled leaves reporting disabled because a notifier fallback
would overtake working dumpers.

Link: 
https://lore.kernel.org/r/[email protected]
Co-developed-by: Brennan Lamoreaux <[email protected]>
Signed-off-by: Brennan Lamoreaux <[email protected]>
Co-developed-by: Alexey Makhalov <[email protected]>
Signed-off-by: Alexey Makhalov <[email protected]>
Signed-off-by: Zack Rusin <[email protected]>
---
 arch/x86/include/asm/vmware.h |  1 +
 arch/x86/kernel/cpu/vmware.c  | 63 +++++++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/arch/x86/include/asm/vmware.h b/arch/x86/include/asm/vmware.h
index 598d4cd448ec..ea2ce5272a00 100644
--- a/arch/x86/include/asm/vmware.h
+++ b/arch/x86/include/asm/vmware.h
@@ -61,6 +61,7 @@
 #define VMWARE_CMD_GETHZ               45
 #define VMWARE_CMD_GETVCPU_INFO                68
 #define VMWARE_CMD_STEALCLOCK          91
+#define VMWARE_CMD_REPORTGUESTCRASH    102
 /*
  * Hypercall command mask:
  *   bits [6:0] command, range [0, 127]
diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c
index 3848811550e9..c21fc835537f 100644
--- a/arch/x86/kernel/cpu/vmware.c
+++ b/arch/x86/kernel/cpu/vmware.c
@@ -22,10 +22,14 @@
  */
 
 #include <linux/dmi.h>
+#include <linux/atomic.h>
+#include <linux/crash_core.h>
 #include <linux/init.h>
 #include <linux/export.h>
 #include <linux/kmsg_dump.h>
+#include <linux/limits.h>
 #include <linux/mm.h>
+#include <linux/panic_notifier.h>
 #include <linux/clocksource.h>
 #include <linux/cpu.h>
 #include <linux/efi.h>
@@ -323,6 +327,65 @@ static int __init vmware_panic_log_init(void)
 }
 early_initcall(vmware_panic_log_init);
 
+static atomic_t vmware_crash_reported = ATOMIC_INIT(0);
+
+static void vmware_report_guest_crash(void)
+{
+       if (kexec_crash_loaded())
+               return;
+       if (atomic_xchg(&vmware_crash_reported, 1))
+               return;
+
+       vmware_hypercall1(VMWARE_CMD_REPORTGUESTCRASH, 0);
+}
+
+static void vmware_crash_report_dump(struct kmsg_dumper *dumper,
+                                    struct kmsg_dump_detail *detail)
+{
+       vmware_report_guest_crash();
+}
+
+static struct kmsg_dumper vmware_crash_report_dumper = {
+       .dump = vmware_crash_report_dump,
+       .max_reason = KMSG_DUMP_PANIC,
+};
+
+static int vmware_crash_report_notify(struct notifier_block *nb,
+                                     unsigned long action, void *data)
+{
+       vmware_report_guest_crash();
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block vmware_crash_report_nb = {
+       .notifier_call = vmware_crash_report_notify,
+       .priority = INT_MIN,
+};
+
+static int __init vmware_crash_report_init(void)
+{
+       int ret;
+
+       if (!hypervisor_is_type(X86_HYPER_VMWARE))
+               return 0;
+
+       ret = kmsg_dump_register(&vmware_crash_report_dumper);
+       if (!ret)
+               return 0;
+       if (IS_ENABLED(CONFIG_PRINTK)) {
+               pr_err("failed to register crash report dumper: %d\n", ret);
+               return 0;
+       }
+
+       ret = atomic_notifier_chain_register(&panic_notifier_list,
+                                            &vmware_crash_report_nb);
+       if (ret)
+               pr_err("failed to register crash report notifier: %d\n", ret);
+
+       return 0;
+}
+late_initcall_sync(vmware_crash_report_init);
+
 #ifdef CONFIG_PARAVIRT
 static struct cyc2ns_data vmware_cyc2ns __ro_after_init;
 static bool vmw_sched_clock __initdata = true;
-- 
2.53.0


Reply via email to