#include "core/types.h" #include "core/defs.h" #include "core/param.h" #include "arch/memlayout.h" #include "arch/mmu.h" #include "arch/traps.h" #include "core/proc.h" #include "arch/x86.h" #include "core/syscall.h" #include "core/uaccess.h" #include "uapi/errno.h" #include "uapi/posix.h" // ----------------------------------------------------------------------- // Argument helpers — read syscall args from the saved register frame. // x86-64 SysV ABI: rdi, rsi, rdx, r10, r8, r9 // ----------------------------------------------------------------------- // Note: address validation goes through uaccess.h (page-table presence + // user bit on every page of the range). There is deliberately no // `addr < myproc()->sz` check: that would assume every valid user pointer // lives in the heap — false for stack addresses and mmap()'d buffers // (both live above myproc()->sz), which made syscalls fail whenever a // Linux-ABI program passed a string/pointer from either region. // // Validated pointers are returned as raw user VAs — the process page // table is live in kernel mode, so they dereference correctly across // page boundaries (unlike a uva2ka() alias, which is only good within // the one page it translated). int fetchint(addr_t addr, int *ip) { return copy_from_user(ip, addr, sizeof(*ip)) ? -1 : 0; } int fetchaddr(addr_t addr, addr_t *ip) { return copy_from_user(ip, addr, sizeof(*ip)) ? -1 : 0; } int fetchstr(addr_t addr, char **pp) { long len = strlen_user(addr); if (len < 0) return -1; *pp = (char*)addr; return len; } static addr_t fetcharg(int n) { switch (n) { case 0: return myproc()->tf->rdi; case 1: return myproc()->tf->rsi; case 2: return myproc()->tf->rdx; case 3: return myproc()->tf->r10; case 4: return myproc()->tf->r8; case 5: return myproc()->tf->r9; } panic("fetcharg: bad n"); return 0; } int argint(int n, int *ip) { *ip = (int)fetcharg(n); return 0; } int argaddr(int n, addr_t *ip) { *ip = fetcharg(n); return 0; } int argptr(int n, char **pp, int size) { addr_t i; if (argaddr(n, &i) < 0) return -1; if (size < 0) return -1; char *p = user_ptr(i, (uint64)size); if (!p) return -1; *pp = p; return 0; } int argstr(int n, char **pp) { addr_t addr; if (argaddr(n, &addr) < 0) return -1; return fetchstr(addr, pp); } // ----------------------------------------------------------------------- // Dispatch table — auto-generated from table.inc // ----------------------------------------------------------------------- #define SYSCALL(nr, lnr, name) extern addr_t sys_##name(void); #define SYSCALL_ALIAS(lnr, name) #include "table.inc" #undef SYSCALL #undef SYSCALL_ALIAS static addr_t (*syscalls[NSYSCALLS])(void) = { #define SYSCALL(nr, lnr, name) [SYS_##name] = sys_##name, #define SYSCALL_ALIAS(lnr, name) #include "table.inc" #undef SYSCALL #undef SYSCALL_ALIAS }; // ----------------------------------------------------------------------- // Main entry point called from trap.c // ----------------------------------------------------------------------- void syscall(struct trapframe *tf) { kstack_check_canary("syscall entry"); myproc()->tf = tf; uint64 orig = myproc()->tf->rax; uint64 num = orig; if (myproc()->syscall_map) { num = (num < (uint64)myproc()->map_size) ? myproc()->syscall_map[num] : 0; } if (num > 0 && num < NSYSCALLS && syscalls[num]) { tf->rax = syscalls[num](); } else { tf->rax = -ENOSYS; } if (myproc()->killed) exit(W_EXITCODE(0, myproc()->killsig ? myproc()->killsig : SIGKILL)); deliver_signals(tf); kstack_check_canary("syscall exit"); }