An x86-64 operating system built from xv6, now largely rewritten: SMP, a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF rasteriser, and a Linux-numbered syscall ABI. No history before this point -- the tree was never under version control. This commit exists so the rename that follows has something to undo to.
141 lines
3.6 KiB
C
141 lines
3.6 KiB
C
#include "types.h"
|
|
#include "defs.h"
|
|
#include "param.h"
|
|
#include "memlayout.h"
|
|
#include "mmu.h"
|
|
#include "traps.h"
|
|
#include "proc.h"
|
|
#include "x86.h"
|
|
#include "syscall.h"
|
|
#include "uaccess.h"
|
|
#include "errno.h"
|
|
#include "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");
|
|
}
|