BNX2X: spatch signed typedefs
[akaros.git] / kern / src / syscall.c
index 0ae92d9..7f9e80a 100644 (file)
@@ -35,6 +35,7 @@
 #include <arsc_server.h>
 #include <event.h>
 #include <termios.h>
+#include <manager.h>
 
 /* Tracing Globals */
 int systrace_flags = 0;
@@ -44,6 +45,9 @@ size_t systrace_bufsize = 0;
 struct proc *systrace_procs[MAX_NUM_TRACED] = {0};
 spinlock_t systrace_lock = SPINLOCK_INITIALIZER_IRQSAVE;
 
+// for now, only want this visible here.
+void kprof_write_sysrecord(char *pretty_buf, size_t len);
+
 /* Not enforcing the packing of systrace_procs yet, but don't rely on that */
 static bool proc_is_traced(struct proc *p)
 {
@@ -53,6 +57,143 @@ static bool proc_is_traced(struct proc *p)
        return false;
 }
 
+static bool __trace_this_proc(struct proc *p)
+{
+       return (systrace_flags & SYSTRACE_ON) &&
+              ((systrace_flags & SYSTRACE_ALLPROC) || (proc_is_traced(p)));
+}
+
+static size_t systrace_fill_pretty_buf(struct systrace_record *trace)
+{
+       size_t len = 0;
+       struct timespec ts_start;
+       struct timespec ts_end;
+       tsc2timespec(trace->start_timestamp, &ts_start);
+       tsc2timespec(trace->end_timestamp, &ts_end);
+
+       len = snprintf(trace->pretty_buf, SYSTR_PRETTY_BUF_SZ - len,
+                  "[%7d.%09d]-[%7d.%09d] Syscall %3d (%12s):(0x%llx, 0x%llx, "
+                  "0x%llx, 0x%llx, 0x%llx, 0x%llx) ret: 0x%llx proc: %d core: %d "
+                  "vcore: %d data: ",
+                  ts_start.tv_sec,
+                  ts_start.tv_nsec,
+                  ts_end.tv_sec,
+                  ts_end.tv_nsec,
+                  trace->syscallno,
+                  syscall_table[trace->syscallno].name,
+                  trace->arg0,
+                  trace->arg1,
+                  trace->arg2,
+                  trace->arg3,
+                  trace->arg4,
+                  trace->arg5,
+                  trace->retval,
+                  trace->pid,
+                  trace->coreid,
+                  trace->vcoreid);
+       /* if we have extra data, print it out on the next line, lined up nicely.
+        * this is only useful for looking at the dump in certain terminals.  if we
+        * have a tool that processes the info, we shouldn't do this. */
+       if (trace->datalen)
+               len += snprintf(trace->pretty_buf + len, SYSTR_PRETTY_BUF_SZ - len,
+                               "\n%67s", "");
+       len += printdump(trace->pretty_buf + len,
+                        MIN(trace->datalen, SYSTR_PRETTY_BUF_SZ - len - 1),
+                        trace->data);
+       len += snprintf(trace->pretty_buf + len, SYSTR_PRETTY_BUF_SZ - len, "\n");
+       return len;
+}
+
+static void systrace_start_trace(struct kthread *kthread, struct syscall *sysc)
+{
+       struct systrace_record *trace;
+       int coreid, vcoreid;
+       struct proc *p = current;
+
+       if (!__trace_this_proc(p))
+               return;
+       assert(!kthread->trace);        /* catch memory leaks */
+       coreid = core_id();
+       vcoreid = proc_get_vcoreid(p);
+       if (systrace_flags & SYSTRACE_LOUD) {
+               printk("ENTER [%16llu] Syscall %3d (%12s):(0x%llx, 0x%llx, 0x%llx, "
+                      "0x%llx, 0x%llx, 0x%llx) proc: %d core: %d vcore: %d\n",
+                      read_tsc(),
+                      sysc->num, syscall_table[sysc->num].name,
+                          sysc->arg0, sysc->arg1, sysc->arg2, sysc->arg3, sysc->arg4,
+                          sysc->arg5, p->pid, coreid, vcoreid);
+       }
+       trace = kmalloc(SYSTR_BUF_SZ, 0);
+       if (!trace)
+               return;
+       kthread->trace = trace;
+       trace->start_timestamp = read_tsc();
+       trace->syscallno = sysc->num;
+       trace->arg0 = sysc->arg0;
+       trace->arg1 = sysc->arg1;
+       trace->arg2 = sysc->arg2;
+       trace->arg3 = sysc->arg3;
+       trace->arg4 = sysc->arg4;
+       trace->arg5 = sysc->arg5;
+       trace->pid = p->pid;
+       trace->coreid = coreid;
+       trace->vcoreid = vcoreid;
+       trace->pretty_buf = (char*)trace + sizeof(struct systrace_record);
+       trace->datalen = 0;
+       trace->data[0] = 0;
+}
+
+static void systrace_finish_trace(struct kthread *kthread, long retval)
+{
+       struct systrace_record *trace = kthread->trace;
+       size_t pretty_len;
+       if (trace) {
+               trace->end_timestamp = read_tsc();
+               trace->retval = retval;
+               kthread->trace = 0;
+               pretty_len = systrace_fill_pretty_buf(trace);
+               kprof_write_sysrecord(trace->pretty_buf, pretty_len);
+               if (systrace_flags & SYSTRACE_LOUD)
+                       printk("EXIT %s", trace->pretty_buf);
+               kfree(trace);
+       }
+}
+
+#ifdef CONFIG_SYSCALL_STRING_SAVING
+
+static void alloc_sysc_str(struct kthread *kth)
+{
+       kth->name = kmalloc(SYSCALL_STRLEN, KMALLOC_WAIT);
+       kth->name[0] = 0;
+}
+
+static void free_sysc_str(struct kthread *kth)
+{
+       char *str = kth->name;
+       kth->name = 0;
+       kfree(str);
+}
+
+#define sysc_save_str(...)                                                     \
+{                                                                              \
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];                     \
+       snprintf(pcpui->cur_kthread->name, SYSCALL_STRLEN, __VA_ARGS__);           \
+}
+
+#else
+
+static void alloc_sysc_str(struct kthread *kth)
+{
+}
+
+static void free_sysc_str(struct kthread *kth)
+{
+}
+
+#define sysc_save_str(...)
+
+#endif /* CONFIG_SYSCALL_STRING_SAVING */
+
 /* Helper to finish a syscall, signalling if appropriate */
 static void finish_sysc(struct syscall *sysc, struct proc *p)
 {
@@ -95,6 +236,18 @@ void set_errno(int errno)
                pcpui->cur_kthread->sysc->err = errno;
 }
 
+/* Callable by any function while executing a syscall (or otherwise, actually).
+ */
+int get_errno(void)
+{
+       /* if there's no errno to get, that's not an error I guess. */
+       int errno = 0;
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       if (pcpui->cur_kthread && pcpui->cur_kthread->sysc)
+               errno = pcpui->cur_kthread->sysc->err;
+       return errno;
+}
+
 void unset_errno(void)
 {
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
@@ -104,7 +257,7 @@ void unset_errno(void)
        pcpui->cur_kthread->sysc->errstr[0] = '\0';
 }
 
-void set_errstr(char *fmt, ...)
+void set_errstr(const char *fmt, ...)
 {
        va_list ap;
        int rc;
@@ -148,6 +301,22 @@ char *get_cur_genbuf(void)
        return pcpui->cur_kthread->generic_buf;
 }
 
+/* Helper, looks up proc* for pid and ensures p controls that proc. 0 o/w */
+static struct proc *get_controllable_proc(struct proc *p, pid_t pid)
+{
+       struct proc *target = pid2proc(pid);
+       if (!target) {
+               set_errno(ESRCH);
+               return 0;
+       }
+       if (!proc_controls(p, target)) {
+               set_errno(EPERM);
+               proc_decref(target);
+               return 0;
+       }
+       return target;
+}
+
 /************** Utility Syscalls **************/
 
 static int sys_null(void)
@@ -159,14 +328,9 @@ static int sys_null(void)
  * async I/O handling. */
 static int sys_block(struct proc *p, unsigned int usec)
 {
-       struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
-       struct alarm_waiter a_waiter;
-       init_awaiter(&a_waiter, 0);
        /* Note printing takes a few ms, so your printds won't be perfect. */
        printd("[kernel] sys_block(), sleeping at %llu\n", read_tsc());
-       set_awaiter_rel(&a_waiter, usec);
-       set_alarm(tchain, &a_waiter);
-       sleep_on_awaiter(&a_waiter);
+       kthread_usleep(usec);
        printd("[kernel] sys_block(), waking up at %llu\n", read_tsc());
        return 0;
 }
@@ -308,7 +472,7 @@ static pid_t sys_getpid(struct proc *p)
  * default, so it needs it's status to be changed so that the next call to
  * schedule() will try to run it.  TODO: take args/envs from userspace. */
 static int sys_proc_create(struct proc *p, char *path, size_t path_l,
-                           struct procinfo *pi)
+                           struct procinfo *pi, int flags)
 {
        int pid = 0;
        char *t_path;
@@ -327,29 +491,42 @@ static int sys_proc_create(struct proc *p, char *path, size_t path_l,
        /* TODO: need to split the proc creation, since you must load after setting
         * args/env, since auxp gets set up there. */
        //new_p = proc_create(program, 0, 0);
-       if (proc_alloc(&new_p, current))
+       if (proc_alloc(&new_p, current, flags)) {
+               set_errstr("Failed to alloc new proc");
                goto mid_error;
+       }
+       /* close the CLOEXEC ones, even though this isn't really an exec */
+       close_9ns_files(new_p, TRUE);
+       close_all_files(&new_p->open_files, TRUE);
        /* Set the argument stuff needed by glibc */
        if (memcpy_from_user_errno(p, new_p->procinfo->argp, pi->argp,
-                                  sizeof(pi->argp)))
+                                  sizeof(pi->argp))) {
+               set_errstr("Failed to memcpy argp");
                goto late_error;
+       }
        if (memcpy_from_user_errno(p, new_p->procinfo->argbuf, pi->argbuf,
-                                  sizeof(pi->argbuf)))
+                                  sizeof(pi->argbuf))) {
+               set_errstr("Failed to memcpy argbuf");
                goto late_error;
-       if (load_elf(new_p, program))
+       }
+       if (load_elf(new_p, program)) {
+               set_errstr("Failed to load elf");
                goto late_error;
+       }
+       /* progname is argv0, which accounts for symlinks */
+       proc_set_progname(p, p->procinfo->argbuf);
        kref_put(&program->f_kref);
-       /* Connect to stdin, stdout, stderr (part of proc_create()) */
-       assert(insert_file(&new_p->open_files, dev_stdin,  0) == 0);
-       assert(insert_file(&new_p->open_files, dev_stdout, 0) == 1);
-       assert(insert_file(&new_p->open_files, dev_stderr, 0) == 2);
        __proc_ready(new_p);
        pid = new_p->pid;
        proc_decref(new_p);     /* give up the reference created in proc_create() */
        return pid;
 late_error:
+       set_errno(EINVAL);
+       /* proc_destroy will decref once, which is for the ref created in
+        * proc_create().  We don't decref again (the usual "+1 for existing"),
+        * since the scheduler, which usually handles that, hasn't heard about the
+        * process (via __proc_ready()). */
        proc_destroy(new_p);
-       proc_decref(new_p);     /* give up the reference created in proc_create() */
 mid_error:
        kref_put(&program->f_kref);
        return -1;
@@ -358,29 +535,20 @@ mid_error:
 /* Makes process PID runnable.  Consider moving the functionality to process.c */
 static error_t sys_proc_run(struct proc *p, unsigned pid)
 {
-       struct proc *target = pid2proc(pid);
        error_t retval = 0;
-
-       if (!target) {
-               set_errno(ESRCH);
+       struct proc *target = get_controllable_proc(p, pid);
+       if (!target)
                return -1;
-       }
-       /* make sure we have access and it's in the right state to be activated */
-       if (!proc_controls(p, target)) {
-               set_errno(EPERM);
-               goto out_error;
-       } else if (target->state != PROC_CREATED) {
+       if (target->state != PROC_CREATED) {
                set_errno(EINVAL);
-               goto out_error;
+               proc_decref(target);
+               return -1;
        }
        /* Note a proc can spam this for someone it controls.  Seems safe - if it
         * isn't we can change it. */
        proc_wakeup(target);
        proc_decref(target);
        return 0;
-out_error:
-       proc_decref(target);
-       return -1;
 }
 
 /* Destroy proc pid.  If this is called by the dying process, it will never
@@ -390,17 +558,9 @@ out_error:
 static error_t sys_proc_destroy(struct proc *p, pid_t pid, int exitcode)
 {
        error_t r;
-       struct proc *p_to_die = pid2proc(pid);
-
-       if (!p_to_die) {
-               set_errno(ESRCH);
-               return -1;
-       }
-       if (!proc_controls(p, p_to_die)) {
-               proc_decref(p_to_die);
-               set_errno(EPERM);
+       struct proc *p_to_die = get_controllable_proc(p, pid);
+       if (!p_to_die)
                return -1;
-       }
        if (p_to_die == p) {
                p->exitcode = exitcode;
                printd("[PID %d] proc exiting gracefully (code %d)\n", p->pid,exitcode);
@@ -420,6 +580,8 @@ static int sys_proc_yield(struct proc *p, bool being_nice)
        /* proc_yield() often doesn't return - we need to set the syscall retval
         * early.  If it doesn't return, it expects to eat our reference (for now).
         */
+       free_sysc_str(pcpui->cur_kthread);
+       systrace_finish_trace(pcpui->cur_kthread, 0);
        finish_sysc(pcpui->cur_kthread->sysc, pcpui->cur_proc);
        pcpui->cur_kthread->sysc = 0;   /* don't touch sysc again */
        proc_incref(p, 1);
@@ -450,14 +612,18 @@ static ssize_t sys_fork(env_t* e)
                return -1;
        }
        env_t* env;
-       assert(!proc_alloc(&env, current));
+       ret = proc_alloc(&env, current, PROC_DUP_FGRP);
+       assert(!ret);
        assert(env != NULL);
+       proc_set_progname(env, e->progname);
 
        env->heap_top = e->heap_top;
        env->ppid = e->pid;
        disable_irqsave(&state);        /* protect cur_ctx */
        /* Can't really fork if we don't have a current_ctx to fork */
        if (!current_ctx) {
+               proc_destroy(env);
+               proc_decref(env);
                set_errno(EINVAL);
                return -1;
        }
@@ -496,7 +662,6 @@ static ssize_t sys_fork(env_t* e)
        env->procdata->ldt = e->procdata->ldt;
        #endif
 
-       clone_files(&e->open_files, &env->open_files);
        /* FYI: once we call ready, the proc is open for concurrent usage */
        __proc_ready(env);
        proc_wakeup(env);
@@ -555,8 +720,11 @@ static int sys_exec(struct proc *p, char *path, size_t path_l,
        /* Clear the current_ctx.  We won't be returning the 'normal' way.  Even if
         * we want to return with an error, we need to go back differently in case
         * we succeed.  This needs to be done before we could possibly block, but
-        * unfortunately happens before the point of no return. */
-       pcpui->cur_ctx = 0;
+        * unfortunately happens before the point of no return.
+        *
+        * Note that we will 'hard block' if we block at all.  We can't return to
+        * userspace and then asynchronously finish the exec later. */
+       clear_owning_proc(core_id());
        enable_irqsave(&state);
        /* This could block: */
        /* TODO: 9ns support */
@@ -564,6 +732,10 @@ static int sys_exec(struct proc *p, char *path, size_t path_l,
        user_memdup_free(p, t_path);
        if (!program)
                goto early_error;
+       if (!is_valid_elf(program)) {
+               set_errno(ENOEXEC);
+               goto early_error;
+       }
        /* Set the argument stuff needed by glibc */
        if (memcpy_from_user_errno(p, p->procinfo->argp, pi->argp,
                                   sizeof(pi->argp)))
@@ -572,6 +744,8 @@ static int sys_exec(struct proc *p, char *path, size_t path_l,
                                   sizeof(pi->argbuf)))
                goto mid_error;
        /* This is the point of no return for the process. */
+       /* progname is argv0, which accounts for symlinks */
+       proc_set_progname(p, p->procinfo->argbuf);
        #ifdef CONFIG_X86
        /* clear this, so the new program knows to get an LDT */
        p->procdata->ldt = 0;
@@ -579,6 +753,7 @@ static int sys_exec(struct proc *p, char *path, size_t path_l,
        /* When we destroy our memory regions, accessing cur_sysc would PF */
        pcpui->cur_kthread->sysc = 0;
        unmap_and_destroy_vmrs(p);
+       /* close the CLOEXEC ones */
        close_9ns_files(p, TRUE);
        close_all_files(&p->open_files, TRUE);
        env_user_mem_free(p, 0, UMAPTOP);
@@ -592,6 +767,7 @@ static int sys_exec(struct proc *p, char *path, size_t path_l,
        }
        printd("[PID %d] exec %s\n", p->pid, file_name(program));
        kref_put(&program->f_kref);
+       systrace_finish_trace(pcpui->cur_kthread, 0);
        goto success;
        /* These error and out paths are so we can handle the async interface, both
         * for when we want to error/return to the proc, as well as when we succeed
@@ -602,7 +778,9 @@ mid_error:
        kref_put(&program->f_kref);
 early_error:
        finish_current_sysc(-1);
+       systrace_finish_trace(pcpui->cur_kthread, -1);
 success:
+       free_sysc_str(pcpui->cur_kthread);
        /* Here's how we restart the new (on success) or old (on failure) proc: */
        spin_lock(&p->proc_lock);
        __unmap_vcore(p, 0);    /* VC# keep in sync with proc_run_s */
@@ -614,7 +792,6 @@ all_out:
         * syscall struct (which has been freed and is in the old userspace) (or has
         * already been written to).*/
        disable_irq();                  /* abandon_core/clear_own wants irqs disabled */
-       clear_owning_proc(core_id());
        abandon_core();
        smp_idle();                             /* will reenable interrupts */
 }
@@ -854,16 +1031,9 @@ static int sys_notify(struct proc *p, int target_pid, unsigned int ev_type,
                       struct event_msg *u_msg)
 {
        struct event_msg local_msg = {0};
-       struct proc *target = pid2proc(target_pid);
-       if (!target) {
-               set_errno(ESRCH);
-               return -1;
-       }
-       if (!proc_controls(p, target)) {
-               proc_decref(target);
-               set_errno(EPERM);
+       struct proc *target = get_controllable_proc(p, target_pid);
+       if (!target)
                return -1;
-       }
        /* if the user provided an ev_msg, copy it in and use that */
        if (u_msg) {
                if (memcpy_from_user(p, &local_msg, u_msg, sizeof(struct event_msg))) {
@@ -917,31 +1087,52 @@ static int sys_vc_entry(struct proc *p)
        return 0;
 }
 
-/* This will set a local timer for usec, then shut down the core.  There's a
- * slight race between spinner and halt.  For now, the core will wake up for
- * other interrupts and service them, but will not process routine messages or
- * do anything other than halt until the alarm goes off.  We could just unset
- * the alarm and return early.  On hardware, there are a lot of interrupts that
- * come in.  If we ever use this, we can take a closer look.  */
+/* This will halt the core, waking on an IRQ.  These could be kernel IRQs for
+ * things like timers or devices, or they could be IPIs for RKMs (__notify for
+ * an evq with IPIs for a syscall completion, etc).
+ *
+ * We don't need to finish the syscall early (worried about the syscall struct,
+ * on the vcore's stack).  The syscall will finish before any __preempt RKM
+ * executes, so the vcore will not restart somewhere else before the syscall
+ * completes (unlike with yield, where the syscall itself adjusts the vcore
+ * structures).
+ *
+ * In the future, RKM code might avoid sending IPIs if the core is already in
+ * the kernel.  That code will need to check the CPU's state in some manner, and
+ * send if the core is halted/idle.
+ *
+ * The core must wake up for RKMs, including RKMs that arrive while the kernel
+ * is trying to halt.  The core need not abort the halt for notif_pending for
+ * the vcore, only for a __notify or other RKM.  Anyone setting notif_pending
+ * should then attempt to __notify (o/w it's probably a bug). */
 static int sys_halt_core(struct proc *p, unsigned int usec)
 {
-       struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
-       struct alarm_waiter a_waiter;
-       bool spinner = TRUE;
-       void unblock(struct alarm_waiter *waiter)
-       {
-               spinner = FALSE;
-       }
-       init_awaiter(&a_waiter, unblock);
-       set_awaiter_rel(&a_waiter, MAX(usec, 100));
-       set_alarm(tchain, &a_waiter);
-       enable_irq();
-       /* Could wake up due to another interrupt, but we want to sleep still. */
-       while (spinner) {
-               cpu_halt();     /* slight race between spinner and halt */
-               cpu_relax();
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct preempt_data *vcpd;
+       /* The user can only halt CG cores!  (ones it owns) */
+       if (management_core())
+               return -1;
+       disable_irq();
+       /* both for accounting and possible RKM optimizations */
+       __set_cpu_state(pcpui, CPU_STATE_IDLE);
+       wrmb();
+       if (has_routine_kmsg()) {
+               __set_cpu_state(pcpui, CPU_STATE_KERNEL);
+               enable_irq();
+               return 0;
+       }
+       /* This situation possible, though the check is not necessary.  We can't
+        * assert notif_pending isn't set, since another core may be in the
+        * proc_notify.  Thus we can't tell if this check here caught a bug, or just
+        * aborted early. */
+       vcpd = &p->procdata->vcore_preempt_data[pcpui->owning_vcoreid];
+       if (vcpd->notif_pending) {
+               __set_cpu_state(pcpui, CPU_STATE_KERNEL);
+               enable_irq();
+               return 0;
        }
-       printd("Returning from halting\n");
+       /* CPU_STATE is reset to KERNEL by the IRQ handler that wakes us */
+       cpu_halt();
        return 0;
 }
 
@@ -997,10 +1188,26 @@ static int sys_abort_sysc(struct proc *p, struct syscall *sysc)
        return abort_sysc(p, sysc);
 }
 
+static int sys_abort_sysc_fd(struct proc *p, int fd)
+{
+       /* Consider checking for a bad fd.  Doesn't matter now, since we only look
+        * for actual syscalls blocked that had used fd. */
+       return abort_all_sysc_fd(p, fd);
+}
+
+static unsigned long sys_populate_va(struct proc *p, uintptr_t va,
+                                     unsigned long nr_pgs)
+{
+       return populate_va(p, ROUNDDOWN(va, PGSIZE), nr_pgs);
+}
+
 static intreg_t sys_read(struct proc *p, int fd, void *buf, int len)
 {
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct systrace_record *t = pcpui->cur_kthread->trace;
        ssize_t ret;
        struct file *file = get_file_from_fd(&p->open_files, fd);
+       sysc_save_str("read on fd %d", fd);
        /* VFS */
        if (file) {
                if (!file->f_op->read) {
@@ -1014,17 +1221,26 @@ static intreg_t sys_read(struct proc *p, int fd, void *buf, int len)
                 * it */
                ret = file->f_op->read(file, buf, len, &file->f_pos);
                kref_put(&file->f_kref);
-               return ret;
+       } else {
+               /* plan9, should also handle errors (EBADF) */
+               ret = sysread(fd, buf, len);
+       }
+
+       if ((ret > 0) && t) {
+               t->datalen = MIN(sizeof(t->data), ret);
+               memmove(t->data, buf, t->datalen);
        }
-       /* plan9, should also handle errors (EBADF) */
-    ret = sysread(fd, buf, len);
+
        return ret;
 }
 
 static intreg_t sys_write(struct proc *p, int fd, const void *buf, int len)
 {
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct systrace_record *t = pcpui->cur_kthread->trace;
        ssize_t ret;
        struct file *file = get_file_from_fd(&p->open_files, fd);
+       sysc_save_str("write on fd %d", fd);
        /* VFS */
        if (file) {
                if (!file->f_op->write) {
@@ -1035,11 +1251,17 @@ static intreg_t sys_write(struct proc *p, int fd, const void *buf, int len)
                /* TODO: (UMEM) */
                ret = file->f_op->write(file, buf, len, &file->f_pos);
                kref_put(&file->f_kref);
-               return ret;
+       } else {
+               /* plan9, should also handle errors */
+               ret = syswrite(fd, (void*)buf, len);
+       }
+
+       if (t) {
+               t->datalen = MIN(sizeof(t->data), ret);
+               memmove(t->data, buf, t->datalen);
        }
-       /* plan9, should also handle errors */
-       ret = syswrite(fd, (void*)buf, len);
        return ret;
+
 }
 
 /* Checks args/reads in the path, opens the file, and inserts it into the
@@ -1047,22 +1269,40 @@ static intreg_t sys_write(struct proc *p, int fd, const void *buf, int len)
 static intreg_t sys_open(struct proc *p, const char *path, size_t path_l,
                          int oflag, int mode)
 {
-       int fd;
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct systrace_record *t = pcpui->cur_kthread->trace;
+       int fd = -1;
        struct file *file;
 
        printd("File %s Open attempt oflag %x mode %x\n", path, oflag, mode);
        char *t_path = user_strdup_errno(p, path, path_l);
        if (!t_path)
                return -1;
+       if (t) {
+               t->datalen = MIN(sizeof(t->data), path_l);
+               memmove(t->data, t_path, path_l);
+       }
+
+       /* Make sure only one of O_RDONLY, O_WRONLY, O_RDWR is specified in flag */
+       if (((oflag & (O_RDONLY | O_WRONLY | O_RDWR)) != O_RDONLY) &&
+           ((oflag & (O_RDONLY | O_WRONLY | O_RDWR)) != O_WRONLY) &&
+           ((oflag & (O_RDONLY | O_WRONLY | O_RDWR)) != O_RDWR)) {
+               set_errno(EINVAL);
+               user_memdup_free(p, t_path);
+               return -1;
+       }
+
+       sysc_save_str("open %s", t_path);
        mode &= ~p->fs_env.umask;
        file = do_file_open(t_path, oflag, mode);
        /* VFS */
        if (file) {
-               fd = insert_file(&p->open_files, file, 0);      /* stores the ref to file */
+               /* stores the ref to file */
+               fd = insert_file(&p->open_files, file, 0, FALSE, oflag & O_CLOEXEC);
                kref_put(&file->f_kref);        /* drop our ref */
                if (fd < 0)
                        warn("File insertion failed");
-       } else {
+       } else if (get_errno() == ENOENT) {
                unset_errno();  /* Go can't handle extra errnos */
                fd = sysopen(t_path, oflag);
                /* successful lookup with CREATE and EXCL is an error */
@@ -1074,8 +1314,10 @@ static intreg_t sys_open(struct proc *p, const char *path, size_t path_l,
                                return -1;
                        }
                } else {
-                       if (oflag & O_CREATE)
+                       if (oflag & O_CREATE) {
+                               mode &= S_PMASK;
                                fd = syscreate(t_path, oflag, mode);
+                       }
                }
        }
        user_memdup_free(p, t_path);
@@ -1096,6 +1338,11 @@ static intreg_t sys_close(struct proc *p, int fd)
        }
        /* 9ns, should also handle errors (bad FD, etc) */
        retval = sysclose(fd);
+       if (retval < 0) {
+               /* no one checks their retvals.  a double close will cause problems. */
+               printk("[kernel] sys_close failed: proc %d fd %d.  Check your rets.\n",
+                      p->pid, fd);
+       }
        return retval;
 }
 
@@ -1120,7 +1367,7 @@ static intreg_t sys_fstat(struct proc *p, int fd, struct kstat *u_stat)
                kref_put(&file->f_kref);
        } else {
                unset_errno();  /* Go can't handle extra errnos */
-           if (sysfstat(fd, (uint8_t*)kbuf, sizeof(*kbuf)) < 0) {
+           if (sysfstatakaros(fd, (struct kstat *)kbuf) < 0) {
                        kfree(kbuf);
                        return -1;
                }
@@ -1160,7 +1407,7 @@ static intreg_t stat_helper(struct proc *p, const char *path, size_t path_l,
        } else {
                /* VFS failed, checking 9ns */
                unset_errno();  /* Go can't handle extra errnos */
-               retval = sysstat(t_path, (uint8_t*)kbuf, sizeof(*kbuf));
+               retval = sysstatakaros(t_path, (struct stat *)kbuf);
                printd("sysstat returns %d\n", retval);
                /* both VFS and 9ns failed, bail out */
                if (retval < 0)
@@ -1204,9 +1451,11 @@ intreg_t sys_fcntl(struct proc *p, int fd, int cmd, int arg)
                                return sysdup(fd, -1);
                        case (F_GETFD):
                        case (F_SETFD):
+                               return 0;
                        case (F_GETFL):
+                               return fd_getfl(fd);
                        case (F_SETFL):
-                               return 0;
+                               return fd_setfl(fd, arg);
                        default:
                                warn("Unsupported fcntl cmd %d\n", cmd);
                }
@@ -1215,9 +1464,10 @@ intreg_t sys_fcntl(struct proc *p, int fd, int cmd, int arg)
                return -1;
        }
 
+       /* TODO: these are racy */
        switch (cmd) {
                case (F_DUPFD):
-                       retval = insert_file(&p->open_files, file, arg);
+                       retval = insert_file(&p->open_files, file, arg, FALSE, FALSE);
                        if (retval < 0) {
                                set_errno(-retval);
                                retval = -1;
@@ -1227,8 +1477,13 @@ intreg_t sys_fcntl(struct proc *p, int fd, int cmd, int arg)
                        retval = p->open_files.fd[fd].fd_flags;
                        break;
                case (F_SETFD):
-                       if (arg == FD_CLOEXEC)
-                               file->f_flags |= O_CLOEXEC;
+                       /* I'm considering not supporting this at all.  They must do it at
+                        * open time or fix their buggy/racy code. */
+                       spin_lock(&p->open_files.lock);
+                       if (arg & FD_CLOEXEC)
+                               p->open_files.fd[fd].fd_flags |= FD_CLOEXEC;
+                       retval = p->open_files.fd[fd].fd_flags;
+                       spin_unlock(&p->open_files.lock);
                        break;
                case (F_GETFL):
                        retval = file->f_flags;
@@ -1270,22 +1525,6 @@ intreg_t sys_umask(struct proc *p, int mask)
        return old_mask;
 }
 
-intreg_t sys_chmod(struct proc *p, const char *path, size_t path_l, int mode)
-{
-       int retval;
-       char *t_path = user_strdup_errno(p, path, path_l);
-       if (!t_path)
-               return -1;
-       /* TODO: 9ns support */
-       retval = do_chmod(t_path, mode);
-       user_memdup_free(p, t_path);
-       if (retval < 0) {
-               set_errno(-retval);
-               return -1;
-       }
-       return retval;
-}
-
 /* 64 bit seek, with the off64_t passed in via two (potentially 32 bit) off_ts.
  * We're supporting both 32 and 64 bit kernels/userspaces, but both use the
  * llseek syscall with 64 bit parameters. */
@@ -1295,16 +1534,21 @@ static intreg_t sys_llseek(struct proc *p, int fd, off_t offset_hi,
        off64_t retoff = 0;
        off64_t tempoff = 0;
        int ret = 0;
-       struct file *file = get_file_from_fd(&p->open_files, fd);
-       if (!file) {
-               set_errno(EBADF);
-               return -1;
-       }
+       struct file *file;
        tempoff = offset_hi;
        tempoff <<= 32;
        tempoff |= offset_lo;
-       ret = file->f_op->llseek(file, tempoff, &retoff, whence);
-       kref_put(&file->f_kref);
+       file = get_file_from_fd(&p->open_files, fd);
+       if (file) {
+               ret = file->f_op->llseek(file, tempoff, &retoff, whence);
+               kref_put(&file->f_kref);
+       } else {
+               /* won't return here if error ... */
+               ret = sysseek(fd, tempoff, whence);
+               retoff = ret;
+               ret = 0;
+       }
+
        if (ret)
                return -1;
        if (memcpy_to_user_errno(p, result, &retoff, sizeof(off64_t)))
@@ -1336,8 +1580,11 @@ intreg_t sys_unlink(struct proc *p, const char *path, size_t path_l)
        char *t_path = user_strdup_errno(p, path, path_l);
        if (!t_path)
                return -1;
-       /* TODO: 9ns support */
        retval = do_unlink(t_path);
+       if (retval && (get_errno() == ENOENT)) {
+               unset_errno();
+               retval = sysremove(t_path);
+       }
        user_memdup_free(p, t_path);
        return retval;
 }
@@ -1363,42 +1610,82 @@ intreg_t sys_symlink(struct proc *p, char *old_path, size_t old_l,
 intreg_t sys_readlink(struct proc *p, char *path, size_t path_l,
                       char *u_buf, size_t buf_l)
 {
-       char *symname;
+       char *symname = NULL;
+       uint8_t *buf = NULL;
        ssize_t copy_amt;
+       int ret = -1;
        struct dentry *path_d;
        char *t_path = user_strdup_errno(p, path, path_l);
        if (t_path == NULL)
                return -1;
        /* TODO: 9ns support */
        path_d = lookup_dentry(t_path, 0);
+       if (!path_d){
+               int n = 2048;
+               buf = kmalloc(n*2, KMALLOC_WAIT);
+               struct dir *d = (void *)&buf[n];
+               /* try 9ns. */
+               if (sysstat(t_path, buf, n) > 0) {
+                       printk("sysstat t_path %s\n", t_path);
+                       convM2D(buf, n, d, (char *)&d[1]);
+                       /* will be NULL if things did not work out */
+                       symname = d->muid;
+               }
+       } else
+               symname = path_d->d_inode->i_op->readlink(path_d);
+
        user_memdup_free(p, t_path);
-       if (!path_d)
-               return -1;
-       symname = path_d->d_inode->i_op->readlink(path_d);
-       copy_amt = strnlen(symname, buf_l - 1) + 1;
-       if (memcpy_to_user_errno(p, u_buf, symname, copy_amt)) {
-               kref_put(&path_d->d_kref);
-               return -1;
+
+       if (symname){
+               copy_amt = strnlen(symname, buf_l - 1) + 1;
+               if (! memcpy_to_user_errno(p, u_buf, symname, copy_amt))
+                       ret = copy_amt - 1;
        }
-       kref_put(&path_d->d_kref);
+       if (path_d)
+               kref_put(&path_d->d_kref);
+       if (buf)
+               kfree(buf);
        printd("READLINK returning %s\n", u_buf);
-       return copy_amt;
+       return ret;
 }
 
-intreg_t sys_chdir(struct proc *p, const char *path, size_t path_l)
+static intreg_t sys_chdir(struct proc *p, pid_t pid, const char *path, size_t path_l)
 {
        int retval;
-       char *t_path = user_strdup_errno(p, path, path_l);
-       if (!t_path)
+       char *t_path;
+       struct proc *target = get_controllable_proc(p, pid);
+       if (!target)
                return -1;
+       t_path = user_strdup_errno(p, path, path_l);
+       if (!t_path) {
+               proc_decref(target);
+               return -1;
+       }
        /* TODO: 9ns support */
-       retval = do_chdir(&p->fs_env, t_path);
+       retval = do_chdir(&target->fs_env, t_path);
        user_memdup_free(p, t_path);
-       if (retval) {
-               set_errno(-retval);
+       proc_decref(target);
+       return retval;
+}
+
+static intreg_t sys_fchdir(struct proc *p, pid_t pid, int fd)
+{
+       struct file *file;
+       int retval;
+       struct proc *target = get_controllable_proc(p, pid);
+       if (!target)
+               return -1;
+       file = get_file_from_fd(&p->open_files, fd);
+       if (!file) {
+               /* TODO: 9ns */
+               set_errno(EBADF);
+               proc_decref(target);
                return -1;
        }
-       return 0;
+       retval = do_fchdir(&target->fs_env, file);
+       kref_put(&file->f_kref);
+       proc_decref(target);
+       return retval;
 }
 
 /* Note cwd_l is not a strlen, it's an absolute size */
@@ -1411,6 +1698,7 @@ intreg_t sys_getcwd(struct proc *p, char *u_cwd, size_t cwd_l)
                return -1;              /* errno set by do_getcwd */
        if (memcpy_to_user_errno(p, u_cwd, k_cwd, strnlen(k_cwd, cwd_l - 1) + 1))
                retval = -1;
+       retval = strnlen(k_cwd, cwd_l - 1);
        kfree(kfree_this);
        return retval;
 }
@@ -1421,9 +1709,16 @@ intreg_t sys_mkdir(struct proc *p, const char *path, size_t path_l, int mode)
        char *t_path = user_strdup_errno(p, path, path_l);
        if (!t_path)
                return -1;
+       mode &= S_PMASK;
        mode &= ~p->fs_env.umask;
-       /* TODO: 9ns support */
        retval = do_mkdir(t_path, mode);
+       if (retval && (get_errno() == ENOENT)) {
+               unset_errno();
+               /* mixing plan9 and glibc here, make sure DMDIR doesn't overlap with any
+                * permissions */
+               static_assert(!(S_PMASK & DMDIR));
+               retval = syscreate(t_path, O_RDWR, DMDIR | mode);
+       }
        user_memdup_free(p, t_path);
        return retval;
 }
@@ -1444,39 +1739,16 @@ intreg_t sys_pipe(struct proc *p, int *u_pipefd, int flags)
 {
        int pipefd[2] = {0};
        int retval = syspipe(pipefd);
-       int fd;
-       struct file *pipe_files[2] = {0};
 
        if (retval)
                return -1;
-       fd = insert_file(&p->open_files, pipe_files[0], 0);
-       if (!fd) {
-               set_errno(ENFILE);
-               goto failed_first;
-       }
-       pipefd[0] = fd;
-       fd = insert_file(&p->open_files, pipe_files[1], 0);
-       if (!fd) {
-               set_errno(ENFILE);
-               goto failed_second;
-       }
-       pipefd[1] = fd;
        if (memcpy_to_user_errno(p, u_pipefd, pipefd, sizeof(pipefd))) {
+               sysclose(pipefd[0]);
+               sysclose(pipefd[1]);
                set_errno(EFAULT);
-               goto failed_memcpy;
+               return -1;
        }
-       goto all_out;
-
-failed_memcpy:
-       put_file_from_fd(&p->open_files, pipefd[1]);
-failed_second:
-       put_file_from_fd(&p->open_files, pipefd[0]);
-failed_first:
-       retval = -1;
-all_out:
-       kref_put(&pipe_files[0]->f_kref);
-       kref_put(&pipe_files[1]->f_kref);
-       return retval;
+       return 0;
 }
 
 intreg_t sys_gettimeofday(struct proc *p, int *buf)
@@ -1607,14 +1879,6 @@ intreg_t sys_nbind(struct proc *p,
        return ret;
 }
 
-/* int npipe(int *fd) */
-intreg_t sys_npipe(struct proc *p, int *retfd)
-
-{
-       /* TODO: validate addresses of retfd (UMEM) */
-       return syspipe(retfd);
-}
-
 /* int mount(int fd, int afd, char* onto_path, int flag, char* aname); */
 intreg_t sys_nmount(struct proc *p,
                     int fd,
@@ -1656,7 +1920,7 @@ intreg_t sys_nunmount(struct proc *p, char *name, int name_l, char *old_path, in
        return ret;
 }
 
-static int sys_fd2path(struct proc *p, int fd, void *u_buf, size_t len)
+intreg_t sys_fd2path(struct proc *p, int fd, void *u_buf, size_t len)
 {
        int ret;
        struct chan *ch;
@@ -1673,15 +1937,268 @@ static int sys_fd2path(struct proc *p, int fd, void *u_buf, size_t len)
                return -1;
        }
        ch = fdtochan(current->fgrp, fd, -1, FALSE, TRUE);
-       ret = snprintf(u_buf, len, "%s", "chanpath(ch)");
+       ret = snprintf(u_buf, len, "%s", channame(ch));
        cclose(ch);
        poperror();
        return ret;
 }
 
+/* Helper, interprets the wstat and performs the VFS action.  Returns stat_sz on
+ * success for all ops, -1 or 0 o/w.  If one op fails, it'll skip the remaining
+ * ones. */
+static int vfs_wstat(struct file *file, uint8_t *stat_m, size_t stat_sz,
+                     int flags)
+{
+       struct dir *dir;
+       int m_sz;
+       int retval = 0;
+
+       dir = kzmalloc(sizeof(struct dir) + stat_sz, KMALLOC_WAIT);
+       m_sz = convM2D(stat_m, stat_sz, &dir[0], (char*)&dir[1]);
+       if (m_sz != stat_sz) {
+               set_errstr(Eshortstat);
+               set_errno(EINVAL);
+               kfree(dir);
+               return -1;
+       }
+       if (flags & WSTAT_MODE) {
+               retval = do_file_chmod(file, dir->mode);
+               if (retval < 0)
+                       goto out;
+       }
+       if (flags & WSTAT_LENGTH) {
+               retval = do_truncate(file->f_dentry->d_inode, dir->length);
+               if (retval < 0)
+                       goto out;
+       }
+       if (flags & WSTAT_ATIME) {
+               /* wstat only gives us seconds */
+               file->f_dentry->d_inode->i_atime.tv_sec = dir->atime;
+               file->f_dentry->d_inode->i_atime.tv_nsec = 0;
+       }
+       if (flags & WSTAT_MTIME) {
+               file->f_dentry->d_inode->i_mtime.tv_sec = dir->mtime;
+               file->f_dentry->d_inode->i_mtime.tv_nsec = 0;
+       }
+
+out:
+       kfree(dir);
+       /* convert vfs retval to wstat retval */
+       if (retval >= 0)
+               retval = stat_sz;
+       return retval;
+}
+
+intreg_t sys_wstat(struct proc *p, char *path, size_t path_l,
+                   uint8_t *stat_m, size_t stat_sz, int flags)
+{
+       int retval = 0;
+       char *t_path = user_strdup_errno(p, path, path_l);
+       struct file *file;
+
+       if (!t_path)
+               return -1;
+       retval = syswstat(t_path, stat_m, stat_sz);
+       if (retval == stat_sz) {
+               user_memdup_free(p, t_path);
+               return stat_sz;
+       }
+       /* 9ns failed, we'll need to check the VFS */
+       file = do_file_open(t_path, 0, 0);
+       user_memdup_free(p, t_path);
+       if (!file)
+               return -1;
+       retval = vfs_wstat(file, stat_m, stat_sz, flags);
+       kref_put(&file->f_kref);
+       return retval;
+}
+
+intreg_t sys_fwstat(struct proc *p, int fd, uint8_t *stat_m, size_t stat_sz,
+                    int flags)
+{
+       int retval = 0;
+       struct file *file;
+
+       retval = sysfwstat(fd, stat_m, stat_sz);
+       if (retval == stat_sz)
+               return stat_sz;
+       /* 9ns failed, we'll need to check the VFS */
+       file = get_file_from_fd(&p->open_files, fd);
+       if (!file)
+               return -1;
+       retval = vfs_wstat(file, stat_m, stat_sz, flags);
+       kref_put(&file->f_kref);
+       return retval;
+}
+
+intreg_t sys_rename(struct proc *p, char *old_path, size_t old_path_l,
+                    char *new_path, size_t new_path_l)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct systrace_record *t = pcpui->cur_kthread->trace;
+       ERRSTACK(1);
+       int mountpointlen = 0;
+       char *from_path = user_strdup_errno(p, old_path, old_path_l);
+       char *to_path = user_strdup_errno(p, new_path, new_path_l);
+       struct chan *oldchan = 0, *newchan = NULL;
+       int retval = -1;
+
+       if ((!from_path) || (!to_path))
+               return -1;
+       printd("sys_rename :%s: to :%s: : ", from_path, to_path);
+       if (t) {
+               t->datalen = snprintf((char *)t->data, sizeof(t->data), "Rename :%s: to :%s:", from_path, to_path);
+       }
+
+       /* we need a fid for the wstat. */
+       /* TODO: maybe wrap the 9ns stuff better.  sysrename maybe? */
+
+       /* discard namec error */
+       if (!waserror()) {
+               oldchan = namec(from_path, Aaccess, 0, 0);
+       }
+       poperror();
+       if (!oldchan) {
+               retval = do_rename(from_path, to_path);
+               user_memdup_free(p, from_path);
+               user_memdup_free(p, to_path);
+               return retval;
+       }
+
+       printd("Oldchan: %C\n", oldchan);
+       printd("Oldchan: mchan %C\n", oldchan->mchan);
+
+       /* If walked through a mountpoint, we need to take that
+        * into account for the Twstat.
+        */
+       if (oldchan->mountpoint) {
+               printd("mountpoint: %C\n", oldchan->mountpoint);
+               if (oldchan->mountpoint->name)
+                       mountpointlen = oldchan->mountpoint->name->len;
+       }
+
+       /* This test makes sense even when mountpointlen is 0 */
+       if (strlen(to_path) < mountpointlen) {
+               set_errno(EINVAL);
+               goto done;
+       }
+
+       /* the omode and perm are of no importance. */
+       newchan = namec(to_path, Acreatechan, 0, 0);
+       if (newchan == NULL) {
+               printd("sys_rename %s to %s found no chan\n", from_path, to_path);
+               set_errno(EPERM);
+               goto done;
+       }
+       printd("Newchan: %C\n", newchan);
+       printd("Newchan: mchan %C\n", newchan->mchan);
+
+       if ((newchan->dev != oldchan->dev) || 
+               (newchan->type != oldchan->type)) {
+               printd("Old chan and new chan do not match\n");
+               set_errno(ENODEV);
+               goto done;
+       }
+
+       struct dir dir;
+       size_t mlen;
+       uint8_t mbuf[STATFIXLEN + MAX_PATH_LEN + 1];
+
+       init_empty_dir(&dir);
+       dir.name = to_path;
+       /* absolute paths need the mountpoint name stripped from them.
+        * Once stripped, it still has to be an absolute path.
+        */
+       if (dir.name[0] == '/') {
+               dir.name = to_path + mountpointlen;
+               if (dir.name[0] != '/') {
+                       set_errno(EINVAL);
+                       goto done;
+               }
+       }
+
+       mlen = convD2M(&dir, mbuf, sizeof(mbuf));
+       if (! mlen) {
+               printk("convD2M failed\n");
+               set_errno(EINVAL);
+               goto done;
+       }
+
+       if (waserror()) {
+               printk("validstat failed: %s\n", current_errstr());
+               goto done;
+       }
+
+       validstat(mbuf, mlen, 1);
+       poperror();
+
+       if (waserror()) {
+               //cclose(oldchan);
+               nexterror();
+       }
+
+       retval = devtab[oldchan->type].wstat(oldchan, mbuf, mlen);
+
+       poperror();
+       if (retval == mlen) {
+               retval = mlen;
+       } else {
+               printk("syswstat did not go well\n");
+               set_errno(EXDEV);
+       };
+       printk("syswstat returns %d\n", retval);
+
+done: 
+       user_memdup_free(p, from_path);
+       user_memdup_free(p, to_path);
+       cclose(oldchan);
+       cclose(newchan);
+       return retval;
+}
+
+static intreg_t sys_dup_fds_to(struct proc *p, unsigned int pid,
+                               struct childfdmap *map, unsigned int nentries)
+{
+       ssize_t ret = 0;
+       struct proc *child;
+       int slot;
+       struct file *file;
+
+       if (!is_user_rwaddr(map, sizeof(struct childfdmap) * nentries)) {
+               set_errno(EINVAL);
+               return -1;
+       }
+       child = get_controllable_proc(p, pid);
+       if (!child)
+               return -1;
+       for (int i = 0; i < nentries; i++) {
+               map[i].ok = -1;
+               file = get_file_from_fd(&p->open_files, map[i].parentfd);
+               if (file) {
+                       slot = insert_file(&child->open_files, file, map[i].childfd, TRUE,
+                                          FALSE);
+                       if (slot == map[i].childfd) {
+                               map[i].ok = 0;
+                               ret++;
+                       }
+                       kref_put(&file->f_kref);
+                       continue;
+               }
+               if (!sys_dup_to(p, map[i].parentfd, child, map[i].childfd)) {
+                       map[i].ok = 0;
+                       ret++;
+                       continue;
+               }
+               /* probably a bug, could send EBADF, maybe via 'ok' */
+               printk("[kernel] dup_fds_to: couldn't find %d\n", map[i].parentfd);
+       }
+       proc_decref(child);
+       return ret;
+}
+
 /************** Syscall Invokation **************/
 
-const static struct sys_table_entry syscall_table[] = {
+const struct sys_table_entry syscall_table[] = {
        [SYS_null] = {(syscall_t)sys_null, "null"},
        [SYS_block] = {(syscall_t)sys_block, "block"},
        [SYS_cache_buster] = {(syscall_t)sys_cache_buster, "buster"},
@@ -1716,6 +2233,8 @@ const static struct sys_table_entry syscall_table[] = {
        [SYS_change_to_m] = {(syscall_t)sys_change_to_m, "change_to_m"},
        [SYS_poke_ksched] = {(syscall_t)sys_poke_ksched, "poke_ksched"},
        [SYS_abort_sysc] = {(syscall_t)sys_abort_sysc, "abort_sysc"},
+       [SYS_abort_sysc_fd] = {(syscall_t)sys_abort_sysc_fd, "abort_sysc_fd"},
+       [SYS_populate_va] = {(syscall_t)sys_populate_va, "populate_va"},
 
        [SYS_read] = {(syscall_t)sys_read, "read"},
        [SYS_write] = {(syscall_t)sys_write, "write"},
@@ -1727,15 +2246,15 @@ const static struct sys_table_entry syscall_table[] = {
        [SYS_fcntl] = {(syscall_t)sys_fcntl, "fcntl"},
        [SYS_access] = {(syscall_t)sys_access, "access"},
        [SYS_umask] = {(syscall_t)sys_umask, "umask"},
-       [SYS_chmod] = {(syscall_t)sys_chmod, "chmod"},
        [SYS_llseek] = {(syscall_t)sys_llseek, "llseek"},
        [SYS_link] = {(syscall_t)sys_link, "link"},
        [SYS_unlink] = {(syscall_t)sys_unlink, "unlink"},
        [SYS_symlink] = {(syscall_t)sys_symlink, "symlink"},
        [SYS_readlink] = {(syscall_t)sys_readlink, "readlink"},
        [SYS_chdir] = {(syscall_t)sys_chdir, "chdir"},
+       [SYS_fchdir] = {(syscall_t)sys_fchdir, "fchdir"},
        [SYS_getcwd] = {(syscall_t)sys_getcwd, "getcwd"},
-       [SYS_mkdir] = {(syscall_t)sys_mkdir, "mkdri"},
+       [SYS_mkdir] = {(syscall_t)sys_mkdir, "mkdir"},
        [SYS_rmdir] = {(syscall_t)sys_rmdir, "rmdir"},
        [SYS_pipe] = {(syscall_t)sys_pipe, "pipe"},
        [SYS_gettimeofday] = {(syscall_t)sys_gettimeofday, "gettime"},
@@ -1747,11 +2266,13 @@ const static struct sys_table_entry syscall_table[] = {
        [SYS_nbind] ={(syscall_t)sys_nbind, "nbind"},
        [SYS_nmount] ={(syscall_t)sys_nmount, "nmount"},
        [SYS_nunmount] ={(syscall_t)sys_nunmount, "nunmount"},
-       [SYS_npipe] ={(syscall_t)sys_npipe, "npipe"},
        [SYS_fd2path] ={(syscall_t)sys_fd2path, "fd2path"},
-
+       [SYS_wstat] ={(syscall_t)sys_wstat, "wstat"},
+       [SYS_fwstat] ={(syscall_t)sys_fwstat, "fwstat"},
+       [SYS_rename] ={(syscall_t)sys_rename, "rename"},
+       [SYS_dup_fds_to] = {(syscall_t)sys_dup_fds_to, "dup_fds_to"},
 };
-
+const int max_syscall = sizeof(syscall_table)/sizeof(syscall_table[0]);
 /* Executes the given syscall.
  *
  * Note tf is passed in, which points to the tf of the context on the kernel
@@ -1763,44 +2284,16 @@ const static struct sys_table_entry syscall_table[] = {
 intreg_t syscall(struct proc *p, uintreg_t sc_num, uintreg_t a0, uintreg_t a1,
                  uintreg_t a2, uintreg_t a3, uintreg_t a4, uintreg_t a5)
 {
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
        intreg_t ret = -1;
        ERRSTACK(1);
-       const int max_syscall = sizeof(syscall_table)/sizeof(syscall_table[0]);
-
-       uint32_t coreid, vcoreid;
-       if (systrace_flags & SYSTRACE_ON) {
-               if ((systrace_flags & SYSTRACE_ALLPROC) || (proc_is_traced(p))) {
-                       coreid = core_id();
-                       vcoreid = proc_get_vcoreid(p);
-                       if (systrace_flags & SYSTRACE_LOUD) {
-                               printk("[%16llu] Syscall %3d (%12s):(%p, %p, %p, %p, "
-                                      "%p, %p) proc: %d core: %d vcore: %d\n", read_tsc(),
-                                      sc_num, syscall_table[sc_num].name, a0, a1, a2, a3,
-                                      a4, a5, p->pid, coreid, vcoreid);
-                       } else {
-                               struct systrace_record *trace;
-                               uintptr_t idx, new_idx;
-                               do {
-                                       idx = systrace_bufidx;
-                                       new_idx = (idx + 1) % systrace_bufsize;
-                               } while (!atomic_cas_u32(&systrace_bufidx, idx, new_idx));
-                               trace = &systrace_buffer[idx];
-                               trace->timestamp = read_tsc();
-                               trace->syscallno = sc_num;
-                               trace->arg0 = a0;
-                               trace->arg1 = a1;
-                               trace->arg2 = a2;
-                               trace->arg3 = a3;
-                               trace->arg4 = a4;
-                               trace->arg5 = a5;
-                               trace->pid = p->pid;
-                               trace->coreid = coreid;
-                               trace->vcoreid = vcoreid;
-                       }
-               }
+
+       if (sc_num > max_syscall || syscall_table[sc_num].call == NULL) {
+               printk("[kernel] Invalid syscall %d for proc %d\n", sc_num, p->pid);
+               printk("\tArgs: %p, %p, %p, %p, %p, %p\n", a0, a1, a2, a3, a4, a5);
+               print_user_ctx(per_cpu_info[core_id()].cur_ctx);
+               return -1;
        }
-       if (sc_num > max_syscall || syscall_table[sc_num].call == NULL)
-               panic("Invalid syscall number %d for proc %x!", sc_num, p);
 
        /* N.B. This is going away. */
        if (waserror()){
@@ -1816,12 +2309,11 @@ intreg_t syscall(struct proc *p, uintreg_t sc_num, uintreg_t a0, uintreg_t a1,
        ret = syscall_table[sc_num].call(p, a0, a1, a2, a3, a4, a5);
        //printd("after syscall errstack base %p\n", get_cur_errbuf());
        if (get_cur_errbuf() != &errstack[0]) {
-               coreid = core_id();
-               vcoreid = proc_get_vcoreid(p);
+               /* Can't trust coreid and vcoreid anymore, need to check the trace */
                printk("[%16llu] Syscall %3d (%12s):(%p, %p, %p, %p, "
-                      "%p, %p) proc: %d core: %d vcore: %d\n", read_tsc(),
+                      "%p, %p) proc: %d\n", read_tsc(),
                       sc_num, syscall_table[sc_num].name, a0, a1, a2, a3,
-                      a4, a5, p->pid, coreid, vcoreid);
+                      a4, a5, p->pid);
                if (sc_num != SYS_fork)
                        printk("YOU SHOULD PANIC: errstack mismatch");
        }
@@ -1833,23 +2325,30 @@ void run_local_syscall(struct syscall *sysc)
 {
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
 
-       /* TODO: (UMEM) assert / pin the memory for the sysc */
        assert(irq_is_enabled());       /* in case we proc destroy */
-       /* Abort on mem check failure, for now */
-       if (!user_mem_check(pcpui->cur_proc, sysc, sizeof(struct syscall),
-                           sizeof(uintptr_t), PTE_USER_RW))
+       /* In lieu of pinning, we just check the sysc and will PF on the user addr
+        * later (if the addr was unmapped).  Which is the plan for all UMEM. */
+       if (!is_user_rwaddr(sysc, sizeof(struct syscall))) {
+               printk("[kernel] bad user addr %p (+%p) in %s (user bug)\n", sysc,
+                      sizeof(struct syscall), __FUNCTION__);
                return;
+       }
        pcpui->cur_kthread->sysc = sysc;        /* let the core know which sysc it is */
+       systrace_start_trace(pcpui->cur_kthread, sysc);
+       alloc_sysc_str(pcpui->cur_kthread);
+       /* syscall() does not return for exec and yield, so put any cleanup in there
+        * too. */
        sysc->retval = syscall(pcpui->cur_proc, sysc->num, sysc->arg0, sysc->arg1,
                               sysc->arg2, sysc->arg3, sysc->arg4, sysc->arg5);
        /* Need to re-load pcpui, in case we migrated */
        pcpui = &per_cpu_info[core_id()];
+       free_sysc_str(pcpui->cur_kthread);
+       systrace_finish_trace(pcpui->cur_kthread, sysc->retval);
        /* Some 9ns paths set errstr, but not errno.  glibc will ignore errstr.
         * this is somewhat hacky, since errno might get set unnecessarily */
        if ((current_errstr()[0] != 0) && (!sysc->err))
                sysc->err = EUNSPECIFIED;
        finish_sysc(sysc, pcpui->cur_proc);
-       /* Can unpin (UMEM) at this point */
        pcpui->cur_kthread->sysc = 0;   /* no longer working on sysc */
 }
 
@@ -1860,8 +2359,10 @@ void prep_syscalls(struct proc *p, struct syscall *sysc, unsigned int nr_syscs)
 {
        int retval;
        /* Careful with pcpui here, we could have migrated */
-       if (!nr_syscs)
+       if (!nr_syscs) {
+               printk("[kernel] No nr_sysc, probably a bug, user!\n");
                return;
+       }
        /* For all after the first call, send ourselves a KMSG (TODO). */
        if (nr_syscs != 1)
                warn("Only one supported (Debutante calls: %d)\n", nr_syscs);
@@ -1939,6 +2440,14 @@ int systrace_reg(bool all, struct proc *p)
        return retval;
 }
 
+int systrace_trace_pid(struct proc *p)
+{
+       if (systrace_reg(false, p))
+               error("no more processes");
+       systrace_start(true);
+       return 0;
+}
+
 void systrace_stop(void)
 {
        spin_lock_irqsave(&systrace_lock);
@@ -1975,10 +2484,10 @@ void systrace_print(bool all, struct proc *p)
        /* if you want to be clever, you could make this start from the earliest
         * timestamp and loop around.  Careful of concurrent writes. */
        for (int i = 0; i < systrace_bufsize; i++)
-               if (systrace_buffer[i].timestamp)
+               if (systrace_buffer[i].start_timestamp)
                        printk("[%16llu] Syscall %3d (%12s):(%p, %p, %p, %p, %p,"
                               "%p) proc: %d core: %d vcore: %d\n",
-                              systrace_buffer[i].timestamp,
+                              systrace_buffer[i].start_timestamp,
                               systrace_buffer[i].syscallno,
                               syscall_table[systrace_buffer[i].syscallno].name,
                               systrace_buffer[i].arg0,
@@ -1999,3 +2508,37 @@ void systrace_clear_buffer(void)
        memset(systrace_buffer, 0, sizeof(struct systrace_record) * MAX_SYSTRACES);
        spin_unlock_irqsave(&systrace_lock);
 }
+
+bool syscall_uses_fd(struct syscall *sysc, int fd)
+{
+       switch (sysc->num) {
+               case (SYS_read):
+               case (SYS_write):
+               case (SYS_close):
+               case (SYS_fstat):
+               case (SYS_fcntl):
+               case (SYS_llseek):
+               case (SYS_nmount):
+               case (SYS_fd2path):
+                       if (sysc->arg0 == fd)
+                               return TRUE;
+                       return FALSE;
+               case (SYS_mmap):
+                       /* mmap always has to be special. =) */
+                       if (sysc->arg4 == fd)
+                               return TRUE;
+                       return FALSE;
+               default:
+                       return FALSE;
+       }
+}
+
+void print_sysc(struct proc *p, struct syscall *sysc)
+{
+       struct proc *old_p = switch_to(p);
+       printk("SYS_%d, flags %p, a0 %p, a1 %p, a2 %p, a3 %p, a4 %p, a5 %p\n",
+              sysc->num, atomic_read(&sysc->flags),
+              sysc->arg0, sysc->arg1, sysc->arg2, sysc->arg3, sysc->arg4,
+              sysc->arg5);
+       switch_back(p, old_p);
+}