diff mbox

[1/5] x86: Add utility to run function in User Mode

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

Commit Message

Arbel Moshe Dec. 24, 2017, 10:07 a.m. UTC
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 mbox

Patch

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