xref: /kvm-unit-tests/lib/x86/usermode.c (revision cd5f2fb4ad641c51fe0f1a85264dc3f6ede6e131)
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