11c93387eSArbel Moshe #include "x86/msr.h"
21c93387eSArbel Moshe #include "x86/processor.h"
31c93387eSArbel Moshe #include "x86/apic-defs.h"
41c93387eSArbel Moshe #include "x86/apic.h"
51c93387eSArbel Moshe #include "x86/desc.h"
61c93387eSArbel Moshe #include "x86/isr.h"
71c93387eSArbel Moshe #include "alloc.h"
81c93387eSArbel Moshe #include "setjmp.h"
91c93387eSArbel Moshe #include "usermode.h"
101c93387eSArbel Moshe
111c93387eSArbel Moshe #include "libcflat.h"
121c93387eSArbel Moshe #include <stdint.h>
131c93387eSArbel Moshe
141c93387eSArbel Moshe #define USERMODE_STACK_SIZE 0x2000
151c93387eSArbel Moshe #define RET_TO_KERNEL_IRQ 0x20
161c93387eSArbel Moshe
17e743729cSVitaly Kuznetsov static jmp_buf jmpbuf;
181c93387eSArbel Moshe
restore_exec_to_jmpbuf(void)191c93387eSArbel Moshe static void restore_exec_to_jmpbuf(void)
201c93387eSArbel Moshe {
211c93387eSArbel Moshe longjmp(jmpbuf, 1);
221c93387eSArbel Moshe }
231c93387eSArbel Moshe
restore_exec_to_jmpbuf_exception_handler(struct ex_regs * regs)241c93387eSArbel Moshe static void restore_exec_to_jmpbuf_exception_handler(struct ex_regs *regs)
251c93387eSArbel Moshe {
261c93387eSArbel Moshe /* longjmp must happen after iret, so do not do it now. */
271c93387eSArbel Moshe regs->rip = (unsigned long)&restore_exec_to_jmpbuf;
281c93387eSArbel Moshe regs->cs = KERNEL_CS;
29663f9e44SAaron Lewis #ifdef __x86_64__
30663f9e44SAaron Lewis regs->ss = KERNEL_DS;
31663f9e44SAaron Lewis #endif
321c93387eSArbel Moshe }
331c93387eSArbel Moshe
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)341c93387eSArbel Moshe uint64_t run_in_user(usermode_func func, unsigned int fault_vector,
351c93387eSArbel Moshe uint64_t arg1, uint64_t arg2, uint64_t arg3,
361c93387eSArbel Moshe uint64_t arg4, bool *raised_vector)
371c93387eSArbel Moshe {
381c93387eSArbel Moshe extern char ret_to_kernel;
39dd884ff3SMathias Krause volatile uint64_t rax = 0;
401c93387eSArbel Moshe static unsigned char user_stack[USERMODE_STACK_SIZE];
41dd884ff3SMathias Krause handler old_ex;
421c93387eSArbel Moshe
431c93387eSArbel Moshe *raised_vector = 0;
441c93387eSArbel Moshe set_idt_entry(RET_TO_KERNEL_IRQ, &ret_to_kernel, 3);
45dd884ff3SMathias Krause old_ex = handle_exception(fault_vector,
461c93387eSArbel Moshe restore_exec_to_jmpbuf_exception_handler);
471c93387eSArbel Moshe
481c93387eSArbel Moshe if (setjmp(jmpbuf) != 0) {
49dd884ff3SMathias Krause handle_exception(fault_vector, old_ex);
501c93387eSArbel Moshe *raised_vector = 1;
511c93387eSArbel Moshe return 0;
521c93387eSArbel Moshe }
531c93387eSArbel Moshe
541c93387eSArbel Moshe asm volatile (
558cd86535SPaolo Bonzini /* Prepare kernel SP for exception handlers */
568cd86535SPaolo Bonzini "mov %%rsp, %[rsp0]\n\t"
571c93387eSArbel Moshe /* Load user_ds to DS and ES */
581c93387eSArbel Moshe "mov %[user_ds], %%ax\n\t"
591c93387eSArbel Moshe "mov %%ax, %%ds\n\t"
601c93387eSArbel Moshe "mov %%ax, %%es\n\t"
611c93387eSArbel Moshe /* IRET into user mode */
621c93387eSArbel Moshe "pushq %[user_ds]\n\t"
631c93387eSArbel Moshe "pushq %[user_stack_top]\n\t"
641c93387eSArbel Moshe "pushfq\n\t"
651c93387eSArbel Moshe "pushq %[user_cs]\n\t"
66318f89e7SMathias Krause "lea user_mode(%%rip), %%rax\n\t"
67318f89e7SMathias Krause "pushq %%rax\n\t"
681c93387eSArbel Moshe "iretq\n"
691c93387eSArbel Moshe
701c93387eSArbel Moshe "user_mode:\n\t"
71318f89e7SMathias Krause /* Back up volatile registers before invoking func */
721c93387eSArbel Moshe "push %%rcx\n\t"
731c93387eSArbel Moshe "push %%rdx\n\t"
74318f89e7SMathias Krause "push %%rdi\n\t"
75318f89e7SMathias Krause "push %%rsi\n\t"
761c93387eSArbel Moshe "push %%r8\n\t"
771c93387eSArbel Moshe "push %%r9\n\t"
781c93387eSArbel Moshe "push %%r10\n\t"
791c93387eSArbel Moshe "push %%r11\n\t"
801c93387eSArbel Moshe /* Call user mode function */
811c93387eSArbel Moshe "mov %[arg1], %%rdi\n\t"
821c93387eSArbel Moshe "mov %[arg2], %%rsi\n\t"
831c93387eSArbel Moshe "mov %[arg3], %%rdx\n\t"
841c93387eSArbel Moshe "mov %[arg4], %%rcx\n\t"
85ebc1b903SPaolo Bonzini "call *%[func]\n\t"
861c93387eSArbel Moshe /* Restore registers */
871c93387eSArbel Moshe "pop %%r11\n\t"
881c93387eSArbel Moshe "pop %%r10\n\t"
891c93387eSArbel Moshe "pop %%r9\n\t"
901c93387eSArbel Moshe "pop %%r8\n\t"
91318f89e7SMathias Krause "pop %%rsi\n\t"
92318f89e7SMathias Krause "pop %%rdi\n\t"
931c93387eSArbel Moshe "pop %%rdx\n\t"
941c93387eSArbel Moshe "pop %%rcx\n\t"
951c93387eSArbel Moshe /* Return to kernel via system call */
961c93387eSArbel Moshe "int %[kernel_entry_vector]\n\t"
971c93387eSArbel Moshe /* Kernel Mode */
981c93387eSArbel Moshe "ret_to_kernel:\n\t"
998cd86535SPaolo Bonzini "mov %[rsp0], %%rsp\n\t"
100*47a84f27SMathias Krause #ifdef __x86_64__
101*47a84f27SMathias Krause /*
102*47a84f27SMathias Krause * Restore SS, as the CPU loads SS with a NULL segment
103*47a84f27SMathias Krause * if handling an interrupt/exception changes the CPL.
104*47a84f27SMathias Krause */
105*47a84f27SMathias Krause "mov %[kernel_ds], %%ss\n\t"
106*47a84f27SMathias Krause #endif
1071c93387eSArbel Moshe :
1088cd86535SPaolo Bonzini "+a"(rax),
109dbd38004SZixuan Wang [rsp0]"=m"(tss[0].rsp0)
1101c93387eSArbel Moshe :
1111c93387eSArbel Moshe [arg1]"m"(arg1),
1121c93387eSArbel Moshe [arg2]"m"(arg2),
1131c93387eSArbel Moshe [arg3]"m"(arg3),
1141c93387eSArbel Moshe [arg4]"m"(arg4),
1151c93387eSArbel Moshe [func]"m"(func),
1161c93387eSArbel Moshe [user_ds]"i"(USER_DS),
1171c93387eSArbel Moshe [user_cs]"i"(USER_CS),
118*47a84f27SMathias Krause [kernel_ds]"rm"(KERNEL_DS),
1191c93387eSArbel Moshe [user_stack_top]"r"(user_stack +
1201c93387eSArbel Moshe sizeof(user_stack)),
121318f89e7SMathias Krause [kernel_entry_vector]"i"(RET_TO_KERNEL_IRQ));
1221c93387eSArbel Moshe
123dd884ff3SMathias Krause handle_exception(fault_vector, old_ex);
124dd884ff3SMathias Krause
1251c93387eSArbel Moshe return rax;
1261c93387eSArbel Moshe }
127