xref: /kvm-unit-tests/lib/x86/usermode.c (revision 663f9e447b981a51f62ea6a178a70dfe5cfb6842)
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 
191c93387eSArbel Moshe static void restore_exec_to_jmpbuf(void)
201c93387eSArbel Moshe {
211c93387eSArbel Moshe 	longjmp(jmpbuf, 1);
221c93387eSArbel Moshe }
231c93387eSArbel Moshe 
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;
29*663f9e44SAaron Lewis #ifdef __x86_64__
30*663f9e44SAaron Lewis 	regs->ss = KERNEL_DS;
31*663f9e44SAaron Lewis #endif
321c93387eSArbel Moshe }
331c93387eSArbel Moshe 
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;
391c93387eSArbel Moshe 	uint64_t rax = 0;
401c93387eSArbel Moshe 	static unsigned char user_stack[USERMODE_STACK_SIZE];
411c93387eSArbel Moshe 
421c93387eSArbel Moshe 	*raised_vector = 0;
431c93387eSArbel Moshe 	set_idt_entry(RET_TO_KERNEL_IRQ, &ret_to_kernel, 3);
441c93387eSArbel Moshe 	handle_exception(fault_vector,
451c93387eSArbel Moshe 			restore_exec_to_jmpbuf_exception_handler);
461c93387eSArbel Moshe 
471c93387eSArbel Moshe 	if (setjmp(jmpbuf) != 0) {
481c93387eSArbel Moshe 		*raised_vector = 1;
491c93387eSArbel Moshe 		return 0;
501c93387eSArbel Moshe 	}
511c93387eSArbel Moshe 
521c93387eSArbel Moshe 	asm volatile (
538cd86535SPaolo Bonzini 			/* Prepare kernel SP for exception handlers */
548cd86535SPaolo Bonzini 			"mov %%rsp, %[rsp0]\n\t"
551c93387eSArbel Moshe 			/* Load user_ds to DS and ES */
561c93387eSArbel Moshe 			"mov %[user_ds], %%ax\n\t"
571c93387eSArbel Moshe 			"mov %%ax, %%ds\n\t"
581c93387eSArbel Moshe 			"mov %%ax, %%es\n\t"
591c93387eSArbel Moshe 			/* IRET into user mode */
601c93387eSArbel Moshe 			"pushq %[user_ds]\n\t"
611c93387eSArbel Moshe 			"pushq %[user_stack_top]\n\t"
621c93387eSArbel Moshe 			"pushfq\n\t"
631c93387eSArbel Moshe 			"pushq %[user_cs]\n\t"
641c93387eSArbel Moshe 			"pushq $user_mode\n\t"
651c93387eSArbel Moshe 			"iretq\n"
661c93387eSArbel Moshe 
671c93387eSArbel Moshe 			"user_mode:\n\t"
681c93387eSArbel Moshe 			/* Back up registers before invoking func */
691c93387eSArbel Moshe 			"push %%rbx\n\t"
701c93387eSArbel Moshe 			"push %%rcx\n\t"
711c93387eSArbel Moshe 			"push %%rdx\n\t"
721c93387eSArbel Moshe 			"push %%r8\n\t"
731c93387eSArbel Moshe 			"push %%r9\n\t"
741c93387eSArbel Moshe 			"push %%r10\n\t"
751c93387eSArbel Moshe 			"push %%r11\n\t"
761c93387eSArbel Moshe 			"push %%rdi\n\t"
771c93387eSArbel Moshe 			"push %%rsi\n\t"
781c93387eSArbel Moshe 			/* Call user mode function */
791c93387eSArbel Moshe 			"mov %[arg1], %%rdi\n\t"
801c93387eSArbel Moshe 			"mov %[arg2], %%rsi\n\t"
811c93387eSArbel Moshe 			"mov %[arg3], %%rdx\n\t"
821c93387eSArbel Moshe 			"mov %[arg4], %%rcx\n\t"
83ebc1b903SPaolo Bonzini 			"call *%[func]\n\t"
841c93387eSArbel Moshe 			/* Restore registers */
851c93387eSArbel Moshe 			"pop %%rsi\n\t"
861c93387eSArbel Moshe 			"pop %%rdi\n\t"
871c93387eSArbel Moshe 			"pop %%r11\n\t"
881c93387eSArbel Moshe 			"pop %%r10\n\t"
891c93387eSArbel Moshe 			"pop %%r9\n\t"
901c93387eSArbel Moshe 			"pop %%r8\n\t"
911c93387eSArbel Moshe 			"pop %%rdx\n\t"
921c93387eSArbel Moshe 			"pop %%rcx\n\t"
931c93387eSArbel Moshe 			"pop %%rbx\n\t"
941c93387eSArbel Moshe 			/* Return to kernel via system call */
951c93387eSArbel Moshe 			"int %[kernel_entry_vector]\n\t"
961c93387eSArbel Moshe 			/* Kernel Mode */
971c93387eSArbel Moshe 			"ret_to_kernel:\n\t"
988cd86535SPaolo Bonzini 			"mov %[rsp0], %%rsp\n\t"
991c93387eSArbel Moshe 			:
1008cd86535SPaolo Bonzini 			"+a"(rax),
101dbd38004SZixuan Wang 			[rsp0]"=m"(tss[0].rsp0)
1021c93387eSArbel Moshe 			:
1031c93387eSArbel Moshe 			[arg1]"m"(arg1),
1041c93387eSArbel Moshe 			[arg2]"m"(arg2),
1051c93387eSArbel Moshe 			[arg3]"m"(arg3),
1061c93387eSArbel Moshe 			[arg4]"m"(arg4),
1071c93387eSArbel Moshe 			[func]"m"(func),
1081c93387eSArbel Moshe 			[user_ds]"i"(USER_DS),
1091c93387eSArbel Moshe 			[user_cs]"i"(USER_CS),
1101c93387eSArbel Moshe 			[user_stack_top]"r"(user_stack +
1111c93387eSArbel Moshe 					sizeof(user_stack)),
1121c93387eSArbel Moshe 			[kernel_entry_vector]"i"(RET_TO_KERNEL_IRQ)
1131c93387eSArbel Moshe 			:
1141c93387eSArbel Moshe 			"rsi", "rdi", "rcx", "rdx");
1151c93387eSArbel Moshe 
1161c93387eSArbel Moshe 	return rax;
1171c93387eSArbel Moshe }
118