Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve syscall implementation #284

Merged
merged 8 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 63 additions & 5 deletions arch/x86/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include <segment.h>
#include <page.h>
#include <traps.h>
#include <usermode.h>
#include <errno.h>

.macro _handle_usermode cr3
testb $0x3, cpu_exc_cs(%_ASM_SP)
Expand All @@ -46,6 +48,18 @@
_handle_usermode user_cr3
.endm

.macro syscall_from_usermode
SET_CR3 cr3
swapgs
SWITCH_STACK
.endm

.macro syscall_to_usermode
SWITCH_STACK
swapgs
SET_CR3 user_cr3
.endm

.macro exception_handler sym vec has_error_code
ENTRY(entry_\sym)
enter_from_usermode
Expand All @@ -54,7 +68,12 @@ ENTRY(entry_\sym)
push $0
.endif

#if defined(__x86_64__)
movl $\vec, cpu_exc_vector(%_ASM_SP)
#else
push $\vec
#endif

jmp handle_exception
END_FUNC(entry_\sym)
.endm
Expand Down Expand Up @@ -105,11 +124,7 @@ ENTRY(handle_exception)

RESTORE_ALL_REGS

#if defined(__x86_64__)
add $8, %_ASM_SP
#else
add $4, %_ASM_SP
#endif

enter_to_usermode
IRET
Expand Down Expand Up @@ -152,6 +167,49 @@ ENTRY(enter_usermode)
IRET
END_FUNC(enter_usermode)

ENTRY(syscall_exit)
POPF

/* Save exit code to return value register (AX) */
mov %_ASM_SI, cpu_regs_ax(%_ASM_SP)
RESTORE_ALL_REGS

ret
minipli-oss marked this conversation as resolved.
Show resolved Hide resolved
END_FUNC(syscall_exit)

ENTRY(terminate_user_task)
SWITCH_STACK
POPF

movl $-EFAULT, cpu_regs_ax(%_ASM_SP)
RESTORE_ALL_REGS

ret
minipli-oss marked this conversation as resolved.
Show resolved Hide resolved
END_FUNC(terminate_user_task)

.align PAGE_SIZE
ENTRY(syscall_handler_entry)
SAVE_CALLEE_SAVED_REGS
syscall_from_usermode

cmp $SYSCALL_EXIT, %_ASM_AX
jz syscall_exit

push %_ASM_CX
push %r11

mov %_ASM_DI, %_ASM_CX
mov %_ASM_AX, %_ASM_DI
call syscall_handler

pop %r11
pop %_ASM_CX

syscall_to_usermode
RESTORE_CALLEE_SAVED_REGS
SYSRET
minipli-oss marked this conversation as resolved.
Show resolved Hide resolved
END_FUNC(syscall_handler_entry)

SECTION(.text.user, "ax", 16)
ENTRY(usermode_stub)
/* DI: User function to be called
Expand All @@ -163,7 +221,7 @@ ENTRY(usermode_stub)

/* sys_exit */
mov %_ASM_AX, %_ASM_DI
xor %_ASM_AX, %_ASM_AX
mov $SYSCALL_EXIT, %_ASM_AX
syscall
END_FUNC(usermode_stub)

Expand Down
4 changes: 3 additions & 1 deletion arch/x86/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ extern void asm_interrupt_handler_uart(void);
extern void asm_interrupt_handler_keyboard(void);
extern void asm_interrupt_handler_timer(void);

extern void terminate_user_task(void);

static void init_tss(percpu_t *percpu) {
#if defined(__i386__)
percpu->tss_df.iopb = sizeof(percpu->tss_df);
Expand Down Expand Up @@ -316,7 +318,7 @@ void do_exception(struct cpu_regs *regs) {
/* Handle user tasks' exceptions */
if (enter_from_usermode(regs->exc.cs)) {
printk("Task exception: %s\n", panic_str);
goto_syscall_exit(-EFAULT);
terminate_user_task();
}

panic(panic_str);
Expand Down
2 changes: 1 addition & 1 deletion common/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
static console_callback_entry_t console_callbacks[2];
static unsigned int num_console_callbacks;

static void vprintk(const char *fmt, va_list args) {
void vprintk(const char *fmt, va_list args) {
static char buf[VPRINTK_BUF_SIZE];
static spinlock_t lock = SPINLOCK_INIT;
unsigned int i;
Expand Down
189 changes: 48 additions & 141 deletions common/usermode.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,98 +28,20 @@
#include <processor.h>
#include <usermode.h>

static inline void syscall_save(void) {
/* clang-format off */
asm volatile(
"push %%" STR(_ASM_CX) "\n"
"push %%r11"
::: "memory"
);
/* clang-format on */
}

static inline void syscall_restore(void) {
/* clang-format off */
asm volatile(
"pop %%r11\n"
"pop %%" STR(_ASM_CX)
::: "memory"
);
/* clang-format on */
}

static inline long syscall_return(long return_code) {
asm volatile("" ::"a"(return_code));
return return_code;
}

static inline void stack_switch(void) {
/* clang-format off */
asm volatile(
"xchg %%gs:%[private], %%" STR(_ASM_SP) "\n"
::[private] "m"(ACCESS_ONCE(PERCPU_VAR(usermode_private)))
);
/* clang-format on */
}

static inline void switch_address_space(const cr3_t *cr3) {
/* clang-format off */
asm volatile(
"push %%" STR(_ASM_AX) "\n"
"mov %[cr3], %%" STR(_ASM_AX) "\n"
"mov %%" STR(_ASM_AX) ", %%cr3\n"
"pop %%" STR(_ASM_AX) "\n"
:: [cr3] "m" (*cr3)
: STR(_ASM_AX)
);
/* clang-format on */
}

static inline void _sys_exit(void) {
/* clang-format off */
asm volatile (
"mov %%" STR(_ASM_DI)", %%gs:%[private]\n"
POPF()
RESTORE_ALL_REGS()
"mov %%gs:%[private], %%" STR(_ASM_AX) "\n"
"ret\n"
:: [ private ] "m"(ACCESS_ONCE(PERCPU_VAR(usermode_private)))
: STR(_ASM_AX)
);
/* clang-format on */
}

void __naked syscall_handler(void) {
register unsigned long syscall_nr asm(STR(_ASM_AX));
register unsigned long param1 asm(STR(_ASM_DI));
(void) param1;
register unsigned long param2 asm(STR(_ASM_SI));
(void) param2;
register unsigned long param3 asm(STR(_ASM_BX));
(void) param3;
register unsigned long param4 asm(STR(_ASM_DX));
(void) param4;

SAVE_CLOBBERED_REGS();
switch_address_space(&cr3);
swapgs();
stack_switch();
syscall_save();

long syscall_handler(long syscall_nr, long arg1, long arg2, long arg3, long arg4,
long arg5) {
switch (syscall_nr) {
case SYSCALL_EXIT:
syscall_restore();
_sys_exit();
/* SYSCALL_EXIT is handled by asm routine syscall_exit() */
UNREACHABLE();
wipawel marked this conversation as resolved.
Show resolved Hide resolved

case SYSCALL_PRINTF:
printk(_ptr(param1), param2, param3, param4);
syscall_return(0);
break;
vprintk(_ptr(arg1), _ptr(arg2));
return 0;

case SYSCALL_MMAP: {
void *va = _ptr(param1);
unsigned int order = _u(param2);
void *va = _ptr(arg1);
unsigned int order = _u(arg2);
frame_t *frame;

frame = get_free_frames(order);
Expand All @@ -128,41 +50,32 @@ void __naked syscall_handler(void) {

va = vmap_user(va, frame->mfn, order, L4_PROT_USER, L3_PROT_USER, L2_PROT_USER,
L1_PROT_USER);
syscall_return(_ul(va));
} break;
return _ul(va);
}

case SYSCALL_MUNMAP: {
void *va = _ptr(param1);
unsigned int order = _u(param2);
void *va = _ptr(arg1);
unsigned int order = _u(arg2);

vunmap_user(va, order);
} break;
return 0;
}

default:
printk("Unknown syscall: %lu\n", syscall_nr);
syscall_return(-1L);
break;
return -1;
}

syscall_restore();
stack_switch();
swapgs();
switch_address_space(&user_cr3);

RESTORE_CLOBBERED_REGS();

sysret();
}

static void init_syscall(void) {
msr_star_t star;

star.eip = _u(_ul(&syscall_handler));
star.eip = _u(_ul(&syscall_handler_entry));
star.kern_cs = __KERN_CS64;
star.user_cs = __USER_CS64;

wrmsr(MSR_STAR, star.reg);
wrmsr(MSR_LSTAR, _ul(&syscall_handler));
wrmsr(MSR_LSTAR, _ul(&syscall_handler_entry));
/* FIXME: Add compat support */
wrmsr(MSR_CSTAR, _ul(NULL));

Expand All @@ -177,67 +90,61 @@ static void init_syscall(void) {
void init_usermode(percpu_t *percpu) {
vmap_user_4k(&cr3, virt_to_mfn(&cr3), L1_PROT);
vmap_user_4k(&enter_usermode, virt_to_mfn(&enter_usermode), L1_PROT);
vmap_user_4k(&syscall_handler, virt_to_mfn(&syscall_handler), L1_PROT);

vmap_user_4k(&syscall_handler_entry, virt_to_mfn(&syscall_handler_entry), L1_PROT);
init_syscall();
}

static inline void __user_text sys_exit(unsigned long exit_code) {
asm volatile("syscall" ::"A"(SYSCALL_EXIT), "D"(exit_code) : STR(_ASM_CX), "r11");
}

static inline long __user_text sys_printf(const char *fmt, unsigned long arg1,
unsigned long arg2, unsigned long arg3) {
register unsigned long rax asm(STR(_ASM_AX));
static inline long __user_text syscall(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;
wipawel marked this conversation as resolved.
Show resolved Hide resolved

/* clang-format off */
asm volatile(
"syscall"
: "=A"(rax)
: "0"(SYSCALL_PRINTF), "D"(fmt), "S"(arg1), "b"(arg2), "d"(arg3)
"syscall\n"
: "=a"(return_code)
wipawel marked this conversation as resolved.
Show resolved Hide resolved
: "0"(syscall_nr), "S"(arg1), "d"(arg2), "D" (arg3), "r"(_arg4), "r"(_arg5)
: STR(_ASM_CX), "r11"
);
/* clang-format on */

return rax;
return return_code;
}

static inline long __user_text sys_mmap(void *va, unsigned long order) {
register unsigned long rax asm(STR(_ASM_AX));
#define syscall0(nr) syscall((nr), 0, 0, 0, 0, 0)
#define syscall1(nr, a1) syscall((nr), (a1), 0, 0, 0, 0)
#define syscall2(nr, a1, a2) syscall((nr), (a1), (a2), 0, 0, 0)
#define syscall3(nr, a1, a2, a3) syscall((nr), (a1), (a2), (a3), 0, 0)
#define syscall4(nr, a1, a2, a3, a4) syscall((nr), (a1), (a2), (a3), (a4), 0)
#define syscall5(nr, a1, a2, a3, a4, a5) syscall((nr), (a1), (a2), (a3), (a4), (a5))

/* clang-format off */
asm volatile(
"syscall"
: "=A"(rax)
: "0"(SYSCALL_MMAP), "D"(va), "S"(order)
: STR(_ASM_CX), "r11"
);
/* clang-format on */

return rax;
static inline void __user_text sys_exit(unsigned long exit_code) {
syscall1(SYSCALL_EXIT, exit_code);
}

static inline long __user_text sys_munmap(void *va, unsigned long order) {
register unsigned long rax asm(STR(_ASM_AX));
static inline long __user_text sys_printf(const char *fmt, va_list args) {
return syscall2(SYSCALL_PRINTF, _ul(fmt), _ul(args));
}

/* clang-format off */
asm volatile(
"syscall"
::"A"(SYSCALL_MUNMAP), "D"(va), "S"(order)
: STR(_ASM_CX), "r11"
);
/* clang-format on */
static inline long __user_text sys_mmap(void *va, unsigned long order) {
return syscall2(SYSCALL_MMAP, _ul(va), order);
}

return rax;
static inline long __user_text sys_munmap(void *va, unsigned long order) {
return syscall2(SYSCALL_MUNMAP, _ul(va), order);
}

void __user_text exit(unsigned long exit_code) {
sys_exit(exit_code);
}

void __user_text printf(const char *fmt, unsigned long arg1, unsigned long arg2,
unsigned long arg3) {
sys_printf(fmt, arg1, arg2, arg3);
void __user_text printf(const char *fmt, ...) {
va_list args;

va_start(args, fmt);
sys_printf(fmt, args);
va_end(args);
}

void *__user_text mmap(void *va, unsigned long order) {
Expand Down
Loading