diff mbox

[5/5] x86: Add VMware backdoor unit test

Message ID 20171224100801.145806-6-arbel.moshe@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Arbel Moshe Dec. 24, 2017, 10:08 a.m. UTC
Add VMware backdoors unit test.

VMware backdoors, is a PV interface VMware ESX and Workstation expose
to their running VMs, in order to better operate in a virtualized
environment.

There are 2 main backdoor mechanisms: I/O ports & Pseduo-PMCs.

VMware backdoor I/O ports: VMware expose a special I/O port which
sends/gives ParaVirtualize info. The I/O port is accessible from
User Mode, even if the TSS I/O permission bitmap states otherwise.

VMware backdoor Pseduo-PMCs: VMware exposes 3 Pseduo-PMCs to its
running VMS, that lets the VMs collect information regarding host time.
For more info, see:
www.vmware.com/files/pdf/techpaper/Timekeeping-In-VirtualMachines.pdf

This unit test tests the functionality of these backdoors. They are
tested both from User Mode and Kernel Mode, as they will be accessible
running on ESX.

Signed-off-by: Arbel Moshe <arbel.moshe@oracle.com>
Reviewed-by: Liran Alon <liran.alon@oracle.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
 x86/Makefile.common    |   1 +
 x86/unittests.cfg      |   4 ++
 x86/vmware_backdoors.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 196 insertions(+)
 create mode 100644 x86/vmware_backdoors.c
diff mbox

Patch

diff --git a/x86/Makefile.common b/x86/Makefile.common
index cbd5847..f544636 100644
--- a/x86/Makefile.common
+++ b/x86/Makefile.common
@@ -56,6 +56,7 @@  tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
                $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
                $(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \
                $(TEST_DIR)/hyperv_connections.flat \
+               $(TEST_DIR)/vmware_backdoors.flat\
 
 ifdef API
 tests-api = api/api-sample api/dirty-log api/dirty-log-perf
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 91d1c75..5410177 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -150,6 +150,10 @@  file = pmu.flat
 extra_params = -cpu host
 check = /proc/sys/kernel/nmi_watchdog=0
 
+[vmware_backdoors]
+file = vmware_backdoors.flat
+extra_params = -machine vmport=on
+
 [port80]
 file = port80.flat
 
diff --git a/x86/vmware_backdoors.c b/x86/vmware_backdoors.c
new file mode 100644
index 0000000..f718f16
--- /dev/null
+++ b/x86/vmware_backdoors.c
@@ -0,0 +1,191 @@ 
+
+#include "x86/msr.h"
+#include "x86/processor.h"
+#include "x86/apic-defs.h"
+#include "x86/apic.h"
+#include "x86/desc.h"
+#include "x86/isr.h"
+#include "alloc.h"
+#include "setjmp.h"
+#include "usermode.h"
+#include "fault_test.h"
+
+#include "libcflat.h"
+#include <stdint.h>
+
+#define VMWARE_BACKDOOR_PMC_HOST_TSC           0x10000
+#define VMWARE_BACKDOOR_PMC_REAL_TIME          0x10001
+#define VMWARE_BACKDOOR_PMC_APPARENT_TIME      0x10002
+
+#define VMWARE_BACKDOOR_PORT	0x5658
+#define VMWARE_MAGIC		0x564D5868
+
+#define VMPORT_CMD_GETVERSION	0x0a
+#define VMPORT_CMD_ILLEGAL	0xfff
+
+#define VMPORT_DEFAULT_RETVAL	0xdeadbeef
+
+#define RANDOM_IO_PORT		0x1234
+
+struct backdoor_port_result {
+	uint64_t rax;
+	uint64_t rbx;
+	uint64_t rcx;
+	uint64_t rdx;
+};
+
+static bool vmware_backdoor_port_callback(struct fault_test_arg *arg)
+{
+	struct backdoor_port_result *res =
+		(struct backdoor_port_result *) arg->retval;
+
+	switch (arg->arg[2]) {
+	case VMPORT_CMD_GETVERSION:
+		return (res->rbx == VMWARE_MAGIC);
+	case VMPORT_CMD_ILLEGAL:
+		return (res->rbx == VMPORT_DEFAULT_RETVAL);
+	}
+	return false;
+}
+
+static uint64_t vmware_backdoor_port(uint64_t vmport, uint64_t vmport_magic,
+		uint64_t command)
+{
+	struct backdoor_port_result *res =
+		(struct backdoor_port_result *)
+		malloc(sizeof(struct backdoor_port_result));
+
+	res->rax = VMPORT_DEFAULT_RETVAL;
+	res->rbx = VMPORT_DEFAULT_RETVAL;
+	res->rcx = VMPORT_DEFAULT_RETVAL;
+	res->rdx = VMPORT_DEFAULT_RETVAL;
+
+	asm volatile(
+		"mov %[rax], %%rax\n\t"
+		"mov %[rdx], %%rdx\n\t"
+		"mov %[rcx], %%rcx\n\t"
+		"inl %%dx, %%eax\n\t"
+		:
+		"+a"(res->rax),
+		"+b"(res->rbx),
+		"+c"(res->rcx),
+		"+d"(res->rdx)
+		:
+		[rax]"m"(vmport_magic),
+		[rdx]"m"(vmport),
+		[rcx]"m"(command)
+		);
+
+	return (uint64_t) res;
+}
+
+#define FAULT		true
+#define NO_FAULT	false
+#define USER_MODE	true
+#define KERNEL_MODE	false
+
+#define RDPMC_ARG(n, m, sf) {.usermode = m, \
+	.func =  (test_fault_func) rdpmc, .fault_vector = GP_VECTOR, \
+	.should_fault = sf, .arg = {n, 0, 0, 0}, .callback = NULL}
+
+#define RDPMC_TEST(name, a, m, sf) FAULT_TEST("rdpmc_test: "name, \
+		RDPMC_ARG(a, m, sf))
+
+#define PORT_ARG(a, b, c, m, sf) {.usermode = m, \
+	.func =  (test_fault_func) vmware_backdoor_port, \
+	.fault_vector = GP_VECTOR, .should_fault = sf, .arg = {a, b, c, 0}, \
+	.callback = vmware_backdoor_port_callback}
+
+#define PORT_TEST(name, a, b, c, m, sf) FAULT_TEST("port_test: "name, \
+		PORT_ARG(a, b, c, m, sf))
+
+
+struct fault_test vmware_backdoor_tests[] = {
+	RDPMC_TEST("HOST_TSC kernel", VMWARE_BACKDOOR_PMC_HOST_TSC,
+			KERNEL_MODE, NO_FAULT),
+	RDPMC_TEST("REAL_TIME kernel", VMWARE_BACKDOOR_PMC_REAL_TIME,
+			KERNEL_MODE, NO_FAULT),
+	RDPMC_TEST("APPARENT_TIME kernel", VMWARE_BACKDOOR_PMC_APPARENT_TIME,
+			KERNEL_MODE, NO_FAULT),
+	RDPMC_TEST("HOST_TSC user", VMWARE_BACKDOOR_PMC_HOST_TSC,
+			USER_MODE, NO_FAULT),
+	RDPMC_TEST("REAL_TIME user", VMWARE_BACKDOOR_PMC_REAL_TIME,
+			USER_MODE, NO_FAULT),
+	RDPMC_TEST("APPARENT_TIME user", VMWARE_BACKDOOR_PMC_APPARENT_TIME,
+			USER_MODE, NO_FAULT),
+	RDPMC_TEST("RANDOM PMC user", 0xfff, USER_MODE, FAULT),
+
+	PORT_TEST("CMD_GETVERSION user", VMWARE_BACKDOOR_PORT, VMWARE_MAGIC,
+			VMPORT_CMD_GETVERSION, USER_MODE, NO_FAULT),
+	PORT_TEST("CMD_GETVERSION kernel", VMWARE_BACKDOOR_PORT, VMWARE_MAGIC,
+			VMPORT_CMD_GETVERSION, KERNEL_MODE, NO_FAULT),
+	PORT_TEST("CMD_ILLEGAL user", VMWARE_BACKDOOR_PORT, VMWARE_MAGIC,
+			VMPORT_CMD_ILLEGAL, USER_MODE, NO_FAULT),
+	PORT_TEST("RANDOM port user", RANDOM_IO_PORT, VMWARE_MAGIC, 0xfff,
+			USER_MODE, FAULT),
+	{ NULL },
+};
+
+/*
+ * Set TSS IO Perm to throw GP on RANDOM_IO_PORT and VMWARE_BACKDOOR_PORT
+ * from User Mode
+ */
+static void set_tss_ioperm(void)
+{
+	struct descriptor_table_ptr gdt;
+	struct segment_desc64 *gdt_table;
+	struct segment_desc64 *tss_entry;
+	u16 tr = 0;
+	tss64_t *tss;
+	unsigned char *ioperm_bitmap;
+	uint64_t tss_base;
+
+	sgdt(&gdt);
+	tr = str();
+	gdt_table = (struct segment_desc64 *) gdt.base;
+	tss_entry = &gdt_table[tr / sizeof(struct segment_desc64)];
+	tss_base = ((uint64_t) tss_entry->base1 |
+			((uint64_t) tss_entry->base2 << 16) |
+			((uint64_t) tss_entry->base3 << 24) |
+			((uint64_t) tss_entry->base4 << 32));
+	tss = (tss64_t *)tss_base;
+	tss->iomap_base = sizeof(*tss);
+	ioperm_bitmap = ((unsigned char *)tss+tss->iomap_base);
+
+	/* We want GP on RANDOM_IO_PORT and VMWARE_BACKDOOR_PORT */
+	ioperm_bitmap[RANDOM_IO_PORT / 8] |=
+		1 << (RANDOM_IO_PORT % 8);
+	ioperm_bitmap[VMWARE_BACKDOOR_PORT / 8] |=
+		1 << (VMWARE_BACKDOOR_PORT % 8);
+	*(uint64_t *)tss_entry &= ~DESC_BUSY;
+
+	/* Update TSS */
+	ltr(tr);
+}
+
+static void check_vmware_backdoors(void)
+{
+	int i;
+
+	/* Disable Permissions for IO PORTS */
+	set_tss_ioperm();
+	/* Disable Permission to run rdpmc from user mode */
+	write_cr4(read_cr4() & ~X86_CR4_PCE);
+
+	report_prefix_push("vmware_backdoors");
+
+	for (i = 0; vmware_backdoor_tests[i].name != NULL; i++)
+		test_run(&vmware_backdoor_tests[i]);
+
+	report_prefix_pop();
+}
+
+int main(int ac, char **av)
+{
+	setup_vm();
+	setup_idt();
+
+	check_vmware_backdoors();
+
+	return report_summary();
+}