a user can never be trusted. it may try to hog your resources, or delete something important, or tons of other stuff. fuck the user. so we say if you want to do anything important, you have to go through the kernel. the kernel offers safe ways to do privileged things and can ensure the user does not overstep. fuck the user.
System Calls
to do something privileged, the program executes a trap instruction to that:
changes privilege to kernel mode
performs the privileged operation under heavy supervision
once done the kernel executes a return-from-trap that changes the privilege back to user mode
the process returns control to the user
gotta make sure kernel mode doesn’t overwrite the context of user mode making return-from-trap impossible. x86 implements a kernel stack for each process to store the user context when entering trap
still gotta do this safely, the user must not have any sort of control over the kernel memory, so it can not be told the address of these syscalls, so it must call them other way. so the os defines some trap handlers which the hardware triggers when these traps are called, the hardware knows the exact address of these handlers, and the user only calls syscalls by their numbers, so user input can not call unsafe addresses2
Calling a Trap
load the syscall number into rax and call the syscall instruction3
main: mov %eax, 1 ; identifier for write() syscall syscall
; void swtch(struct context *old, struct context *new);;; Save current register context in old; and then load register context from new..globl swtchswtch: ; Save old registers movl 4(%esp), %eax ; put old ptr into eax popl 0(%eax) ; save the old IP movl %esp, 4(%eax) ; and stack movl %ebx, 8(%eax) ; and other registers movl %ecx, 12(%eax) movl %edx, 16(%eax) movl %esi, 20(%eax) movl %edi, 24(%eax) movl %ebp, 28(%eax) ; Load new registers movl 4(%esp), %eax ; put new ptr into eax movl 28(%eax), %ebp ; restore other registers movl 24(%eax), %edi movl 20(%eax), %esi movl 16(%eax), %edx movl 12(%eax), %ecx movl 8(%eax), %ebx movl 4(%eax), %esp ; stack is switched here pushl 0(%eax) ; return addr put in place ret ; finally return into new ctxt