diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c
index 628a059..a964013 100644
--- a/arch/x86/kernel/cpu/vmware.c
+++ b/arch/x86/kernel/cpu/vmware.c
@@ -26,16 +26,20 @@
#include <asm/div64.h>
#include <asm/x86_init.h>
#include <asm/hypervisor.h>
+#include <linux/kmsg_dump.h>
-#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)" : \
@@ -75,6 +79,13 @@ static unsigned long vmware_get_tsc_khz(void)
return tsc_hz;
}
+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
+};
+
static void __init vmware_platform_setup(void)
{
uint32_t eax, ebx, ecx, edx;
@@ -86,6 +97,8 @@ static void __init vmware_platform_setup(void)
else
printk(KERN_WARNING
"Failed to get TSC freq from the hypervisor\n");
+
+ kmsg_dump_register(&kmsg_dumper);
}
/*
@@ -145,3 +158,127 @@ 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;
+}
+
+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;
+ }
+}