diff --git a/arch/x86/entry.S b/arch/x86/entry.S index 7e69b47e..cf5bafed 100644 --- a/arch/x86/entry.S +++ b/arch/x86/entry.S @@ -242,6 +242,24 @@ ENTRY(sysenter_handler_entry) SYSEXIT END_FUNC(sysenter_handler_entry) + +ENTRY(int80_handler_entry) + SAVE_ALL_REGS + syscall_from_usermode + + MASK_USER_FLAGS + + cmp $SYSCALL_EXIT, %_ASM_AX + jz syscall_exit + + mov %_ASM_DI, %_ASM_CX + mov %_ASM_AX, %_ASM_DI + call syscall_handler + + syscall_to_usermode + RESTORE_ALL_REGS + IRET +END_FUNC(int80_handler_entry) GLOBAL(end_usermode_helpers) SECTION(.text.user, "ax", 16) diff --git a/common/usermode.c b/common/usermode.c index aa5d107f..8a0d311e 100644 --- a/common/usermode.c +++ b/common/usermode.c @@ -27,6 +27,7 @@ #include #include #include +#include #include extern char exception_handlers[], end_exception_handlers[]; @@ -111,6 +112,25 @@ void init_usermode(percpu_t *percpu) { init_syscall(); init_sysenter(percpu); + set_intr_gate(&percpu->idt[SYSCALL_INT], __KERN_CS, _ul(int80_handler_entry), + GATE_DPL3, GATE_PRESENT, 0); +} + +static inline long __user_text _int80(long syscall_nr, long arg1, long arg2, long arg3, + long arg4, long arg5) { + register long return_code asm(STR(_ASM_AX)); + register long _arg4 asm("r8") = arg4; + register long _arg5 asm("r9") = arg5; + + /* clang-format off */ + asm volatile( + "int $" STR(SYSCALL_INT) "\n" + : "=a"(return_code) + : "0"(syscall_nr), "S"(arg1), "d"(arg2), "D" (arg3), "r"(_arg4), "r"(_arg5) + ); + /* clang-format on */ + + return return_code; } static inline long __user_text _syscall(long syscall_nr, long arg1, long arg2, long arg3, @@ -156,7 +176,7 @@ static inline long __user_text _sysenter(long syscall_nr, long arg1, long arg2, static __user_data syscall_mode_t sc_mode = SYSCALL_MODE_SYSCALL; bool __user_text syscall_mode(syscall_mode_t mode) { - if (mode > SYSCALL_MODE_INT) { + if (mode > SYSCALL_MODE_INT80) { return false; } sc_mode = mode; @@ -171,8 +191,8 @@ static long __user_text syscall(long syscall_nr, long arg1, long arg2, long arg3 return _syscall(syscall_nr, arg1, arg2, arg3, arg4, arg5); case SYSCALL_MODE_SYSENTER: return _sysenter(syscall_nr, arg1, arg2, arg3, arg4, arg5); - case SYSCALL_MODE_INT: - // unimplemented + case SYSCALL_MODE_INT80: + return _int80(syscall_nr, arg1, arg2, arg3, arg4, arg5); default: UNREACHABLE(); } diff --git a/include/arch/x86/traps.h b/include/arch/x86/traps.h index c731580e..52583bcd 100644 --- a/include/arch/x86/traps.h +++ b/include/arch/x86/traps.h @@ -30,6 +30,8 @@ #define MAX_INT 256 +#define SYSCALL_INT 0x80 + #ifndef __ASSEMBLY__ #include diff --git a/include/usermode.h b/include/usermode.h index a45f33dc..9f41145a 100644 --- a/include/usermode.h +++ b/include/usermode.h @@ -42,7 +42,7 @@ enum syscall_mode { SYSCALL_MODE_SYSCALL, // use SYSCALL SYSCALL_MODE_SYSENTER, // use SYSENTER - SYSCALL_MODE_INT, // TODO: use INT $SYSVEC + SYSCALL_MODE_INT80, // use INT 0x80 }; typedef enum syscall_mode syscall_mode_t; @@ -57,6 +57,7 @@ static inline bool enter_from_usermode(uint16_t cs) { extern unsigned long enter_usermode(task_func_t fn, void *fn_arg, void *user_stack); extern void syscall_handler_entry(void); extern void sysenter_handler_entry(void); +extern void int80_handler_entry(void); extern void init_usermode(percpu_t *percpu);