From 5da729255406ae4da5bc539569b8d7a10407dcbf Mon Sep 17 00:00:00 2001
From: Alexey Makhalov <amakhalov@vmware.com>
Date: Fri, 22 Dec 2017 14:59:28 -0800
Subject: [PATCH] Log kmsg dump on panic

In case of panic kmsg will be dumped to vmware.log file
---
 arch/x86/kernel/cpu/vmware.c | 145 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 142 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c
index 40ed2685..50ed5769 100644
--- a/arch/x86/kernel/cpu/vmware.c
+++ b/arch/x86/kernel/cpu/vmware.c
@@ -30,19 +30,24 @@
 #include <asm/hypervisor.h>
 #include <asm/timer.h>
 #include <asm/apic.h>
+#include <linux/kmsg_dump.h>
+#include <linux/frame.h>
 
 #undef pr_fmt
 #define pr_fmt(fmt)	"vmware: " fmt
 
-#define CPUID_VMWARE_INFO_LEAF	0x40000000
-#define VMWARE_HYPERVISOR_MAGIC	0x564D5868
-#define VMWARE_HYPERVISOR_PORT	0x5658
+#define CPUID_VMWARE_INFO_LEAF		0x40000000
+#define VMWARE_HYPERVISOR_MAGIC		0x564D5868
+#define VMWARE_HYPERVISOR_PORT		0x5658
+#define VMWARE_HYPERVISOR_HB_PORT	0x5659
 
 #define VMWARE_PORT_CMD_GETVERSION	10
 #define VMWARE_PORT_CMD_GETHZ		45
 #define VMWARE_PORT_CMD_GETVCPU_INFO	68
 #define VMWARE_PORT_CMD_LEGACY_X2APIC	3
 #define VMWARE_PORT_CMD_VCPU_RESERVED	31
+#define VMWARE_PORT_CMD_MESSAGE		30
+#define VMWARE_HB_PORT_CMD_MESSAGE	0
 
 #define VMWARE_PORT(cmd, eax, ebx, ecx, edx)				\
 	__asm__("inl (%%dx)" :						\
@@ -66,6 +71,13 @@ static unsigned long vmware_get_tsc_khz(void)
 	return vmware_tsc_khz;
 }
 
+static void kmsg_dumper_vmware_log(struct kmsg_dumper *dumper,
+				enum kmsg_dump_reason reason);
+
+static struct kmsg_dumper kmsg_dumper = {
+	.dump = kmsg_dumper_vmware_log
+};
+
 #ifdef CONFIG_PARAVIRT
 static struct cyc2ns_data vmware_cyc2ns __ro_after_init;
 static int vmw_sched_clock __initdata = 1;
@@ -172,6 +184,8 @@ static void __init vmware_platform_setup(void)
 #endif
 
 	vmware_set_capabilities();
+
+	kmsg_dump_register(&kmsg_dumper);
 }
 
 /*
@@ -212,3 +226,128 @@ const __refconst struct hypervisor_x86 x86_hyper_vmware = {
 	.x2apic_available	= vmware_legacy_x2apic_available,
 };
 EXPORT_SYMBOL(x86_hyper_vmware);
+
+#define MESSAGE_STATUS_SUCCESS  (0x01 << 16)
+#define MESSAGE_STATUS_CPT      (0x10 << 16)
+#define MESSAGE_STATUS_HB       (0x80 << 16)
+
+#define RPCI_PROTOCOL_NUM       0x49435052 /* 'RPCI' */
+#define GUESTMSG_FLAG_COOKIE    0x80000000
+
+#define MESSAGE_TYPE_OPEN	(0 << 16)
+#define MESSAGE_TYPE_SENDSIZE	(1 << 16)
+#define MESSAGE_TYPE_CLOSE	(6 << 16)
+
+typedef struct {
+	uint32_t id;
+	uint32_t cookieHigh;
+	uint32_t cookieLow;
+} vmw_msg;
+
+static int
+vmware_log_open(vmw_msg *msg) {
+	uint32_t result, info, dx, si, di;
+	__asm__ __volatile__ ("inl (%%dx)"
+		:	"=a" (result),
+			"=c" (info),
+			"=d" (dx),
+			"=S" (si),
+			"=D" (di)
+		:       "a"  (VMWARE_HYPERVISOR_MAGIC),
+			"c"  (VMWARE_PORT_CMD_MESSAGE | MESSAGE_TYPE_OPEN),
+			"d"  (VMWARE_HYPERVISOR_PORT),
+			"b"  (RPCI_PROTOCOL_NUM | GUESTMSG_FLAG_COOKIE));
+
+	if ((info & MESSAGE_STATUS_SUCCESS) == 0)
+		return 1;
+
+	msg->id = dx & 0xffff0000;
+	msg->cookieHigh = si;
+	msg->cookieLow = di;
+	return 0;
+}
+
+static int
+vmware_log_close(vmw_msg *msg) {
+	uint32_t result, info;
+	__asm__ __volatile__ ("inl (%%dx)"
+		:	"=a" (result),
+			"=c" (info)
+		:       "a"  (VMWARE_HYPERVISOR_MAGIC),
+			"c"  (VMWARE_PORT_CMD_MESSAGE | MESSAGE_TYPE_CLOSE),
+			"d"  (VMWARE_HYPERVISOR_PORT | msg->id),
+			"b"  (0),
+			"S"  (msg->cookieHigh),
+			"D"  (msg->cookieLow));
+
+	if ((info & MESSAGE_STATUS_SUCCESS) == 0)
+		return 1;
+	return 0;
+}
+
+static int
+vmware_log_send(vmw_msg *msg, const char *string) {
+	uint32_t result, info;
+	uint32_t len = strlen(string);
+
+retry:
+	__asm__ __volatile__ ("inl (%%dx)"
+		:	"=a" (result),
+			"=c" (info)
+		:       "a"  (VMWARE_HYPERVISOR_MAGIC),
+			"c"  (VMWARE_PORT_CMD_MESSAGE | MESSAGE_TYPE_SENDSIZE),
+			"d"  (VMWARE_HYPERVISOR_PORT | msg->id),
+			"b"  (len),
+			"S"  (msg->cookieHigh),
+			"D"  (msg->cookieLow));
+
+	if ((info & MESSAGE_STATUS_SUCCESS) == 0 ||
+	    (info & MESSAGE_STATUS_HB) == 0)
+		/* Expected success + high-bandwidth. Give up. */
+		return 1;
+
+	__asm__ __volatile__ ("pushq %%rbp\n\t"
+			      "movl %[rbp], %%ebp\n\t"
+			      "cld\n\t"
+			      "rep; outsb\n\t"
+			      "popq %%rbp\n\t"
+		:	"=a" (result),
+			"=b" (info)
+		:       "a"  (VMWARE_HYPERVISOR_MAGIC),
+			"c"  (len),
+			"d"  (VMWARE_HYPERVISOR_HB_PORT | msg->id),
+			"b"  (VMWARE_HB_PORT_CMD_MESSAGE | MESSAGE_STATUS_SUCCESS),
+			"S"  (string),
+		  [rbp] "r"  (msg->cookieHigh),
+			"D"  (msg->cookieLow));
+
+	if ((info & MESSAGE_STATUS_SUCCESS) == 0) {
+		if (info & MESSAGE_STATUS_CPT)
+			/* A checkpoint occurred. Retry. */
+			goto retry;
+		return 1;
+	}
+	return 0;
+}
+STACK_FRAME_NON_STANDARD(vmware_log_send);
+
+static void kmsg_dumper_vmware_log(struct kmsg_dumper *dumper,
+				enum kmsg_dump_reason reason)
+{
+	vmw_msg msg;
+	static char line[1024];
+	size_t len = 0;
+
+	line[0] = 'l';
+	line[1] = 'o';
+	line[2] = 'g';
+	line[3] = ' ';
+
+	while (kmsg_dump_get_line(dumper, true, line + 4, sizeof(line) - 4, &len)) {
+		line[len + 4] = '\0';
+		if (vmware_log_open(&msg) ||
+		    vmware_log_send(&msg, line) ||
+		    vmware_log_close(&msg))
+			break;
+	}
+}
-- 
2.11.0