* [PATCH 1/5] x86: Add utility to run function in User Mode
2017-12-24 10:07 [kvm-unit-test PATCH 0/5]: x86: Add tests for VMware backdoor Arbel Moshe
@ 2017-12-24 10:07 ` Arbel Moshe
2017-12-24 10:07 ` [PATCH 2/5] x86: Add Test Utility to run in User Mode and catch exceptions Arbel Moshe
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Arbel Moshe @ 2017-12-24 10:07 UTC (permalink / raw)
To: pbonzini, rkrcmar, kvm
Cc: idan.brown, liran.alon, Arbel Moshe, Konrad Rzeszutek Wilk
Add usermode util that enables running a function in user mode.
In addition, it enables catching an exception which is raised from
the user-mode function.
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>
---
lib/x86/usermode.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/x86/usermode.h | 30 ++++++++++++++
x86/Makefile.common | 1 +
3 files changed, 144 insertions(+)
create mode 100644 lib/x86/usermode.c
create mode 100644 lib/x86/usermode.h
diff --git a/lib/x86/usermode.c b/lib/x86/usermode.c
new file mode 100644
index 0000000..323d84d
--- /dev/null
+++ b/lib/x86/usermode.c
@@ -0,0 +1,113 @@
+#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 "libcflat.h"
+#include <stdint.h>
+
+#define USERMODE_STACK_SIZE 0x2000
+#define RET_TO_KERNEL_IRQ 0x20
+
+jmp_buf jmpbuf;
+
+static void restore_exec_to_jmpbuf(void)
+{
+ longjmp(jmpbuf, 1);
+}
+
+static void restore_exec_to_jmpbuf_exception_handler(struct ex_regs *regs)
+{
+ /* longjmp must happen after iret, so do not do it now. */
+ regs->rip = (unsigned long)&restore_exec_to_jmpbuf;
+ regs->cs = KERNEL_CS;
+}
+
+uint64_t run_in_user(usermode_func func, unsigned int fault_vector,
+ uint64_t arg1, uint64_t arg2, uint64_t arg3,
+ uint64_t arg4, bool *raised_vector)
+{
+ extern char ret_to_kernel;
+ uint64_t rax = 0;
+ static unsigned char user_stack[USERMODE_STACK_SIZE];
+
+ *raised_vector = 0;
+ set_idt_entry(RET_TO_KERNEL_IRQ, &ret_to_kernel, 3);
+ handle_exception(fault_vector,
+ restore_exec_to_jmpbuf_exception_handler);
+
+ if (setjmp(jmpbuf) != 0) {
+ *raised_vector = 1;
+ return 0;
+ }
+
+ asm volatile (
+ /* Backing Up Stack in rdi */
+ "mov %%rsp, %%rdi\n\t"
+ /* Load user_ds to DS and ES */
+ "mov %[user_ds], %%ax\n\t"
+ "mov %%ax, %%ds\n\t"
+ "mov %%ax, %%es\n\t"
+ /* IRET into user mode */
+ "pushq %[user_ds]\n\t"
+ "pushq %[user_stack_top]\n\t"
+ "pushfq\n\t"
+ "pushq %[user_cs]\n\t"
+ "pushq $user_mode\n\t"
+ "iretq\n"
+
+ "user_mode:\n\t"
+ /* Back up registers before invoking func */
+ "push %%rbx\n\t"
+ "push %%rcx\n\t"
+ "push %%rdx\n\t"
+ "push %%r8\n\t"
+ "push %%r9\n\t"
+ "push %%r10\n\t"
+ "push %%r11\n\t"
+ "push %%rdi\n\t"
+ "push %%rsi\n\t"
+ /* Call user mode function */
+ "mov %[arg1], %%rdi\n\t"
+ "mov %[arg2], %%rsi\n\t"
+ "mov %[arg3], %%rdx\n\t"
+ "mov %[arg4], %%rcx\n\t"
+ "call %[func]\n\t"
+ /* Restore registers */
+ "pop %%rsi\n\t"
+ "pop %%rdi\n\t"
+ "pop %%r11\n\t"
+ "pop %%r10\n\t"
+ "pop %%r9\n\t"
+ "pop %%r8\n\t"
+ "pop %%rdx\n\t"
+ "pop %%rcx\n\t"
+ "pop %%rbx\n\t"
+ /* Return to kernel via system call */
+ "int %[kernel_entry_vector]\n\t"
+ /* Kernel Mode */
+ "ret_to_kernel:\n\t"
+ "mov %%rdi, %%rsp\n\t"
+ :
+ "+a"(rax)
+ :
+ [arg1]"m"(arg1),
+ [arg2]"m"(arg2),
+ [arg3]"m"(arg3),
+ [arg4]"m"(arg4),
+ [func]"m"(func),
+ [user_ds]"i"(USER_DS),
+ [user_cs]"i"(USER_CS),
+ [user_stack_top]"r"(user_stack +
+ sizeof(user_stack)),
+ [kernel_entry_vector]"i"(RET_TO_KERNEL_IRQ)
+ :
+ "rsi", "rdi", "rcx", "rdx");
+
+ return rax;
+}
diff --git a/lib/x86/usermode.h b/lib/x86/usermode.h
new file mode 100644
index 0000000..4e005e6
--- /dev/null
+++ b/lib/x86/usermode.h
@@ -0,0 +1,30 @@
+#ifndef _USERMODE_H_
+#define _USERMODE_H_
+
+#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 "libcflat.h"
+#include <stdint.h>
+
+typedef uint64_t (*usermode_func)(void);
+
+/*
+ * Run function in user mode
+ * Supports running functions with up to 4 arguments.
+ * fault_vector: exception vector that might get thrown during the function.
+ * raised_vector: outputs true if exception occurred.
+ *
+ * returns: return value returned by function, or 0 if an exception occurred.
+ */
+uint64_t run_in_user(usermode_func func, unsigned int fault_vector,
+ uint64_t arg1, uint64_t arg2, uint64_t arg3,
+ uint64_t arg4, bool *raised_vector);
+
+#endif
diff --git a/x86/Makefile.common b/x86/Makefile.common
index 5f7eac4..8a9d245 100644
--- a/x86/Makefile.common
+++ b/x86/Makefile.common
@@ -19,6 +19,7 @@ cflatobjs += lib/x86/desc.o
cflatobjs += lib/x86/isr.o
cflatobjs += lib/x86/acpi.o
cflatobjs += lib/x86/stack.o
+cflatobjs += lib/x86/usermode.o
OBJDIRS += lib/x86
--
2.14.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/5] x86: Add Test Utility to run in User Mode and catch exceptions
2017-12-24 10:07 [kvm-unit-test PATCH 0/5]: x86: Add tests for VMware backdoor Arbel Moshe
2017-12-24 10:07 ` [PATCH 1/5] x86: Add utility to run function in User Mode Arbel Moshe
@ 2017-12-24 10:07 ` Arbel Moshe
2017-12-24 10:07 ` [PATCH 3/5] x86: Add Definition for PCE bit in CR4 Arbel Moshe
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Arbel Moshe @ 2017-12-24 10:07 UTC (permalink / raw)
To: pbonzini, rkrcmar, kvm
Cc: idan.brown, liran.alon, Arbel Moshe, Konrad Rzeszutek Wilk
Add a test utility that enables:
1. Running test function in User Mode or Kernel Mode.
2. Catching an exception.
3. Running a callback function to test return val.
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>
---
lib/x86/fault_test.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/x86/fault_test.h | 42 ++++++++++++++++++++++++++++++++++++++++++
x86/Makefile.common | 1 +
3 files changed, 95 insertions(+)
create mode 100644 lib/x86/fault_test.c
create mode 100644 lib/x86/fault_test.h
diff --git a/lib/x86/fault_test.c b/lib/x86/fault_test.c
new file mode 100644
index 0000000..71ac982
--- /dev/null
+++ b/lib/x86/fault_test.c
@@ -0,0 +1,52 @@
+#include "fault_test.h"
+
+jmp_buf jmpbuf;
+
+static void restore_exec_to_jmpbuf(void)
+{
+ longjmp(jmpbuf, 1);
+}
+
+static void fault_test_fault(struct ex_regs *regs)
+{
+ regs->rip = (unsigned long)&restore_exec_to_jmpbuf;
+}
+
+static bool fault_test(struct fault_test_arg *arg)
+{
+ uint64_t val;
+ bool raised_vector = false;
+ test_fault_func func = (test_fault_func) arg->func;
+ /* Init as success in case there isn't callback */
+ bool callback_success = true;
+
+ if (arg->usermode) {
+ val = run_in_user((usermode_func) func, arg->fault_vector,
+ arg->arg[0], arg->arg[1], arg->arg[2],
+ arg->arg[3], &raised_vector);
+ } else {
+ handle_exception(arg->fault_vector, fault_test_fault);
+ if (setjmp(jmpbuf) == 0)
+ val = func(arg->arg[0], arg->arg[1], arg->arg[2],
+ arg->arg[3]);
+ else
+ raised_vector = true;
+ }
+
+ if (!raised_vector) {
+ arg->retval = val;
+ if (arg->callback != NULL)
+ callback_success = arg->callback(arg);
+ }
+
+ return arg->should_fault ?
+ raised_vector : (!raised_vector && callback_success);
+}
+
+void test_run(struct fault_test *test)
+{
+ bool passed = fault_test(&(test->arg));
+
+ report("%s", passed, test->name);
+}
+
diff --git a/lib/x86/fault_test.h b/lib/x86/fault_test.h
new file mode 100644
index 0000000..dfa715b
--- /dev/null
+++ b/lib/x86/fault_test.h
@@ -0,0 +1,42 @@
+#ifndef __FAULT_TEST__
+#define __FAULT_TEST__
+
+#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 "libcflat.h"
+#include <stdint.h>
+
+#define FAULT_TEST(nm, a) { .name = nm, .arg = a}
+
+struct fault_test_arg;
+
+typedef uint64_t (*test_fault_func)(uint64_t arg1, uint64_t arg2,
+ uint64_t arg3, uint64_t arg4);
+typedef bool (*test_fault_callback)(struct fault_test_arg *arg);
+
+struct fault_test_arg {
+ bool usermode;
+ unsigned int fault_vector;
+ bool should_fault;
+ uint64_t arg[4];
+ uint64_t retval;
+ test_fault_func func;
+ test_fault_callback callback;
+};
+
+struct fault_test {
+ const char *name;
+ struct fault_test_arg arg;
+};
+
+void test_run(struct fault_test *test);
+
+#endif
diff --git a/x86/Makefile.common b/x86/Makefile.common
index 8a9d245..cbd5847 100644
--- a/x86/Makefile.common
+++ b/x86/Makefile.common
@@ -20,6 +20,7 @@ cflatobjs += lib/x86/isr.o
cflatobjs += lib/x86/acpi.o
cflatobjs += lib/x86/stack.o
cflatobjs += lib/x86/usermode.o
+cflatobjs += lib/x86/fault_test.o
OBJDIRS += lib/x86
--
2.14.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 5/5] x86: Add VMware backdoor unit test
2017-12-24 10:07 [kvm-unit-test PATCH 0/5]: x86: Add tests for VMware backdoor Arbel Moshe
` (3 preceding siblings ...)
2017-12-24 10:08 ` [PATCH 4/5] x86: Add definition for 64 bit Segment descriptor Arbel Moshe
@ 2017-12-24 10:08 ` Arbel Moshe
2018-02-14 12:06 ` [kvm-unit-test PATCH 0/5]: x86: Add tests for VMware backdoor Paolo Bonzini
5 siblings, 0 replies; 7+ messages in thread
From: Arbel Moshe @ 2017-12-24 10:08 UTC (permalink / raw)
To: pbonzini, rkrcmar, kvm
Cc: idan.brown, liran.alon, Arbel Moshe, Konrad Rzeszutek Wilk
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 --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();
+}
--
2.14.1
^ permalink raw reply related [flat|nested] 7+ messages in thread