Fault reflection and blocking page faults (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 14 Feb 2014 23:21:43 +0000 (15:21 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 18 Feb 2014 03:49:30 +0000 (19:49 -0800)
Unhandled traps/faults will now be reflected back to userspace for MCPs
in uthread context.  SCPs and MCPs in vcore context retain the existing
behavior (death).

Hard page faults (that can't be serviced without blocking) for
file-backed VMRs will be reflected as unhandled PFs.  Userspace can tell
if the PF is on a valid VMR or not by checking the error code.

The RISCV stuff is all just stubs.  If I made any x86-like assumptions
that are unreasonable, let me know.

You need to reinstall your kernel headers.  Easiest way is to rebuild
your toolchain.

15 files changed:
kern/arch/riscv/ros/trapframe.h
kern/arch/riscv/trap.c
kern/arch/x86/ros/trapframe.h
kern/arch/x86/trap.c
kern/arch/x86/trap.h
kern/arch/x86/trap32.c
kern/arch/x86/trap64.c
kern/include/trap.h
kern/src/mm.c
kern/src/trap.c
tests/mmap.c [new file with mode: 0644]
user/parlib/include/riscv/vcore.h
user/parlib/include/x86/vcore32.h
user/parlib/include/x86/vcore64.h
user/parlib/uthread.c

index d9a302e..57a4e9c 100644 (file)
@@ -26,6 +26,11 @@ struct sw_trapframe {
 #define GPR_A0 18
 #define GPR_A1 19
 
+#error "fix this #define"
+/* this is an error flag, reflected back in faults.  similar to flags that say
+ * if a PF was a write fault, read fault, or user fault. */
+#define PF_VMR_BACKED (1 << 31)
+
 typedef struct ancillary_state
 {
        uint64_t fpr[32];
index 39fe97c..78efb30 100644 (file)
@@ -214,6 +214,7 @@ handle_fault_fetch(struct hw_trapframe *state)
 
        set_current_ctx_hw(&per_cpu_info[core_id()], state);
 
+#warning "returns EAGAIN if you should reflect the fault"
        if(handle_page_fault(current, state->epc, PROT_EXEC))
                unhandled_trap(state, "Instruction Page Fault");
 }
@@ -229,6 +230,7 @@ handle_fault_load(struct hw_trapframe *state)
 
        set_current_ctx_hw(&per_cpu_info[core_id()], state);
 
+#warning "returns EAGAIN if you should reflect the fault"
        if(handle_page_fault(current, state->badvaddr, PROT_READ))
                unhandled_trap(state, "Load Page Fault");
 }
@@ -332,6 +334,8 @@ handle_trap(struct hw_trapframe *hw_tf)
                } else {
                        trap_handlers[hw_tf->cause](hw_tf);
                }
+               #warning "if a trap wasn't handled fully, like an MCP pf, reflect it
+               reflect_unhandled_trap(hw_tf->tf_trapno, hw_tf->tf_err, aux);
        }
        
        extern void pop_hw_tf(struct hw_trapframe *tf); /* in asm */
@@ -364,4 +368,11 @@ int register_dev_irq(int irq, void (*handler)(struct hw_trapframe *, void *),
                      void *irq_arg)
 {
        printk("%s not implemented\n", __FUNCTION);
+       return -1;
+}
+
+void __arch_reflect_trap_hwtf(struct hw_trapframe *hw_tf, unsigned int trap_nr,
+                              unsigned int err, unsigned long aux)
+{
+       printk("%s not implemented\n", __FUNCTION);
 }
index 3c15c99..53083c3 100644 (file)
@@ -7,6 +7,14 @@
 
 #include <ros/common.h>
 
+#define ROS_ARCH_REFL_ID 0x1234
+
+/* Page faults return the nature of the fault in the bits of the error code: */
+#define PF_ERROR_PRESENT               0x01
+#define PF_ERROR_WRITE                         0x02
+#define PF_ERROR_USER                  0x04
+#define PF_VMR_BACKED                  (1 << 31)
+
 #ifdef __x86_64__
 #include <ros/arch/trapframe64.h>
 #else
index bd1da28..bd8a35a 100644 (file)
@@ -220,11 +220,60 @@ void backtrace_kframe(struct hw_trapframe *hw_tf)
        pcpui->__lock_checking_enabled++;
 }
 
+static bool __handle_page_fault(struct hw_trapframe *hw_tf, unsigned long *aux)
+{
+       uintptr_t fault_va = rcr2();
+       int prot = hw_tf->tf_err & PF_ERROR_WRITE ? PROT_WRITE : PROT_READ;
+       int err;
+
+       /* TODO - handle kernel page faults */
+       if ((hw_tf->tf_cs & 3) == 0) {
+               print_trapframe(hw_tf);
+               backtrace_kframe(hw_tf);
+               panic("Page Fault in the Kernel at %p!", fault_va);
+               /* if we want to do something like kill a process or other code, be
+                * aware we are in a sort of irq-like context, meaning the main kernel
+                * code we 'interrupted' could be holding locks - even irqsave locks. */
+       }
+       /* safe to reenable after rcr2 */
+       enable_irq();
+       if ((err = handle_page_fault(current, fault_va, prot))) {
+               if (err == -EAGAIN)
+                       hw_tf->tf_err |= PF_VMR_BACKED;
+               *aux = fault_va;
+               return FALSE;
+               /* useful debugging */
+               printk("[%08x] user %s fault va %p ip %p on core %d with err %d\n",
+                      current->pid, prot & PROT_READ ? "READ" : "WRITE", fault_va,
+                      hw_tf->tf_rip, core_id(), err);
+               print_trapframe(hw_tf);
+               /* Turn this on to help debug bad function pointers */
+#ifdef CONFIG_X86_64
+               printd("rsp %p\n\t 0(rsp): %p\n\t 8(rsp): %p\n\t 16(rsp): %p\n"
+                      "\t24(rsp): %p\n", hw_tf->tf_rsp,
+                      *(uintptr_t*)(hw_tf->tf_rsp +  0),
+                      *(uintptr_t*)(hw_tf->tf_rsp +  8),
+                      *(uintptr_t*)(hw_tf->tf_rsp + 16),
+                      *(uintptr_t*)(hw_tf->tf_rsp + 24));
+#else
+               printd("esp %p\n\t 0(esp): %p\n\t 4(esp): %p\n\t 8(esp): %p\n"
+                      "\t12(esp): %p\n", hw_tf->tf_esp,
+                      *(uintptr_t*)(hw_tf->tf_esp +  0),
+                      *(uintptr_t*)(hw_tf->tf_esp +  4),
+                      *(uintptr_t*)(hw_tf->tf_esp +  8),
+                      *(uintptr_t*)(hw_tf->tf_esp + 12));
+#endif
+       }
+       return TRUE;
+}
+
 /* Certain traps want IRQs enabled, such as the syscall.  Others can't handle
  * it, like the page fault handler.  Turn them on on a case-by-case basis. */
 static void trap_dispatch(struct hw_trapframe *hw_tf)
 {
        struct per_cpu_info *pcpui;
+       bool handled = TRUE;
+       unsigned long aux = 0;
        // Handle processor exceptions.
        switch(hw_tf->tf_trapno) {
                case T_NMI:
@@ -279,7 +328,7 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
                        break;
                }
                case T_PGFLT:
-                       page_fault_handler(hw_tf);
+                       handled = __handle_page_fault(hw_tf, &aux);
                        break;
                case T_FPERR:
                        handle_fperr(hw_tf);
@@ -295,17 +344,15 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
                                                  (unsigned int)x86_get_systrap_arg1(hw_tf));
                        break;
                default:
-                       // Unexpected trap: The user process or the kernel has a bug.
-                       print_trapframe(hw_tf);
-                       if (hw_tf->tf_cs == GD_KT)
+                       if (hw_tf->tf_cs == GD_KT) {
+                               print_trapframe(hw_tf);
                                panic("Damn Damn!  Unhandled trap in the kernel!");
-                       else {
-                               warn("Unexpected trap from userspace");
-                               enable_irq();
-                               proc_destroy(current);
+                       } else {
+                               handled = FALSE;
                        }
        }
-       return;
+       if (!handled)
+               reflect_unhandled_trap(hw_tf->tf_trapno, hw_tf->tf_err, aux);
 }
 
 /* Helper.  For now, this copies out the TF to pcpui.  Eventually, we should
index 01e1a20..c3ff7f0 100644 (file)
 
 #define T_DEFAULT   0x0000beef         // catchall
 
-/* Page faults return the nature of the fault in the bits of the error code: */
-#define PF_ERROR_PRESENT               0x01
-#define PF_ERROR_WRITE                         0x02
-#define PF_ERROR_USER                  0x04
-
 /* Floating point constants */
 #define FP_EXCP_IE                             (1 << 0)        /* invalid op */
 #define FP_EXCP_DE                             (1 << 1)        /* denormalized op */
index 3638833..0ffa700 100644 (file)
@@ -86,36 +86,13 @@ void print_trapframe(struct hw_trapframe *hw_tf)
        pcpui->__lock_checking_enabled++;
 }
 
-void page_fault_handler(struct hw_trapframe *hw_tf)
+void __arch_reflect_trap_hwtf(struct hw_trapframe *hw_tf, unsigned int trap_nr,
+                              unsigned int err, unsigned long aux)
 {
-       uint32_t fault_va = rcr2();
-       int prot = hw_tf->tf_err & PF_ERROR_WRITE ? PROT_WRITE : PROT_READ;
-       int err;
-
-       /* TODO - handle kernel page faults */
-       if ((hw_tf->tf_cs & 3) == 0) {
-               print_trapframe(hw_tf);
-               backtrace_kframe(hw_tf);
-               panic("Page Fault in the Kernel at 0x%08x!", fault_va);
-               /* if we want to do something like kill a process or other code, be
-                * aware we are in a sort of irq-like context, meaning the main kernel
-                * code we 'interrupted' could be holding locks - even irqsave locks. */
-       }
-       /* safe to reenable after rcr2 */
-       enable_irq();
-       if ((err = handle_page_fault(current, fault_va, prot))) {
-               /* Destroy the faulting process */
-               printk("[%08x] user %s fault va %08x ip %08x on core %d with err %d\n",
-                      current->pid, prot & PROT_READ ? "READ" : "WRITE", fault_va,
-                      hw_tf->tf_eip, core_id(), err);
-               print_trapframe(hw_tf);
-               /* Turn this on to help debug bad function pointers */
-               printd("esp %p\n\t 0(esp): %p\n\t 4(esp): %p\n\t 8(esp): %p\n"
-                      "\t12(esp): %p\n", hw_tf->tf_esp,
-                      *(uintptr_t*)(hw_tf->tf_esp +  0),
-                      *(uintptr_t*)(hw_tf->tf_esp +  4),
-                      *(uintptr_t*)(hw_tf->tf_esp +  8),
-                      *(uintptr_t*)(hw_tf->tf_esp + 12));
-               proc_destroy(current);
-       }
+       hw_tf->tf_trapno = trap_nr;
+       /* this can be necessary, since hw_tf is the pcpui one, and the err that
+        * came in probably came from the kernel stack's hw_tf. */
+       hw_tf->tf_err = err;
+       hw_tf->tf_regs.reg_oesp = aux;
+       hw_tf->tf_padding3 = ROS_ARCH_REFL_ID;
 }
index 30a397d..3dc40aa 100644 (file)
@@ -115,36 +115,14 @@ void print_swtrapframe(struct sw_trapframe *sw_tf)
        pcpui->__lock_checking_enabled++;
 }
 
-void page_fault_handler(struct hw_trapframe *hw_tf)
+void __arch_reflect_trap_hwtf(struct hw_trapframe *hw_tf, unsigned int trap_nr,
+                              unsigned int err, unsigned long aux)
 {
-       uintptr_t fault_va = rcr2();
-       int prot = hw_tf->tf_err & PF_ERROR_WRITE ? PROT_WRITE : PROT_READ;
-       int err;
-
-       /* TODO - handle kernel page faults */
-       if ((hw_tf->tf_cs & 3) == 0) {
-               print_trapframe(hw_tf);
-               backtrace_kframe(hw_tf);
-               panic("Page Fault in the Kernel at %p!", fault_va);
-               /* if we want to do something like kill a process or other code, be
-                * aware we are in a sort of irq-like context, meaning the main kernel
-                * code we 'interrupted' could be holding locks - even irqsave locks. */
-       }
-       /* safe to reenable after rcr2 */
-       enable_irq();
-       if ((err = handle_page_fault(current, fault_va, prot))) {
-               /* Destroy the faulting process */
-               printk("[%08x] user %s fault va %p ip %p on core %d with err %d\n",
-                      current->pid, prot & PROT_READ ? "READ" : "WRITE", fault_va,
-                      hw_tf->tf_rip, core_id(), err);
-               print_trapframe(hw_tf);
-               /* Turn this on to help debug bad function pointers */
-               printd("rsp %p\n\t 0(rsp): %p\n\t 8(rsp): %p\n\t 16(rsp): %p\n"
-                      "\t24(rsp): %p\n", hw_tf->tf_rsp,
-                      *(uintptr_t*)(hw_tf->tf_rsp +  0),
-                      *(uintptr_t*)(hw_tf->tf_rsp +  8),
-                      *(uintptr_t*)(hw_tf->tf_rsp + 16),
-                      *(uintptr_t*)(hw_tf->tf_rsp + 24));
-               proc_destroy(current);
-       }
+       hw_tf->tf_trapno = trap_nr;
+       /* this can be necessary, since hw_tf is the pcpui one, and the err that
+        * came in probably came from the kernel stack's hw_tf. */
+       hw_tf->tf_err = err;
+       hw_tf->tf_padding4 = (uint32_t)(aux);
+       hw_tf->tf_padding5 = (uint32_t)(aux >> 32);
+       hw_tf->tf_padding3 = ROS_ARCH_REFL_ID;
 }
index 4f3d223..0b7b0c7 100644 (file)
@@ -23,7 +23,6 @@ void unregister_raw_irq(unsigned int vector, isr_t handler, void *data);
 int register_dev_irq(int irq, isr_t handler, void *irq_arg);
 void print_trapframe(struct hw_trapframe *hw_tf);
 void print_user_ctx(struct user_context *ctx);
-void page_fault_handler(struct hw_trapframe *hw_tf);
 /* Generic per-core timer interrupt handler.  set_percore_timer() will fire the
  * timer_interrupt(). */
 void set_core_timer(uint32_t usec, bool periodic);
@@ -41,9 +40,11 @@ uintptr_t get_stack_top(void);
 static inline void save_kernel_ctx(struct kernel_ctx *ctx)
                    __attribute__((always_inline, returns_twice));
 void pop_kernel_ctx(struct kernel_ctx *ctx) __attribute__((noreturn));
-
-/* Sends a non-maskable interrupt, which we have print a trapframe. */
 void send_nmi(uint32_t os_coreid);
+void reflect_unhandled_trap(unsigned int trap_nr, unsigned int err,
+                            unsigned long aux);
+void __arch_reflect_trap_hwtf(struct hw_trapframe *hw_tf, unsigned int trap_nr,
+                              unsigned int err, unsigned long aux);
 
 /* Kernel messages.  This is an in-order 'active message' style messaging
  * subsystem, where you can instruct other cores (including your own) to execute
index 8ccf1b3..13cb144 100644 (file)
@@ -854,12 +854,14 @@ static int __hpf_load_page(struct proc *p, struct page_map *pm,
                        break;
                case (PROC_RUNNABLE_M):
                case (PROC_RUNNING_M):
-                       printk("MCP pagefault, not supported yet!\n");
                        spin_unlock(&p->proc_lock);
-                       /* this might end up not returning or something. */
+                       return -EAGAIN; /* will get reflected back to userspace */
+               case (PROC_DYING):
+                       spin_unlock(&p->proc_lock);
                        return -EINVAL;
                default:
-                       /* TODO: could be dying, can just error out */
+                       /* shouldn't have any waitings, under the current yield style.  if
+                        * this becomes an issue, we can branch on is_mcp(). */
                        printk("HPF unexpectecd state(%s)", procstate2str(p->state));
                        spin_unlock(&p->proc_lock);
                        return -EINVAL;
index c8714e7..c097b9c 100644 (file)
 #include <kdebug.h>
 #include <kmalloc.h>
 
+void reflect_unhandled_trap(unsigned int trap_nr, unsigned int err,
+                            unsigned long aux)
+{
+       uint32_t coreid = core_id();
+       struct per_cpu_info *pcpui = &per_cpu_info[coreid];
+       struct proc *p = pcpui->cur_proc;
+       uint32_t vcoreid = pcpui->owning_vcoreid;
+       struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
+       struct hw_trapframe *hw_tf = &pcpui->cur_ctx->tf.hw_tf;
+       assert(p);
+       assert(pcpui->cur_ctx && (pcpui->cur_ctx->type == ROS_HW_CTX));
+       if (!(p->procinfo->is_mcp)) {
+               printk("Unhandled SCP trap\n");
+               goto error_out;
+       }
+       if (vcpd->notif_disabled) {
+               printk("Unhandled MCP trap in vcore context\n");
+               goto error_out;
+       }
+       /* need to store trap_nr, err code, and aux into the tf so that it can get
+        * extracted on the other end, and we need to flag the TF in some way so we
+        * can tell it was reflected.  for example, on a PF, we need some number (14
+        * on x86), the prot violation (write, read, etc), and the virt addr (aux).
+        * parlib will know how to extract this info. */
+       __arch_reflect_trap_hwtf(hw_tf, trap_nr, err, aux);
+       /* the guts of a __notify */
+       vcpd->notif_disabled = TRUE;
+       vcpd->uthread_ctx = *pcpui->cur_ctx;
+       memset(pcpui->cur_ctx, 0, sizeof(struct user_context));
+       proc_init_ctx(pcpui->cur_ctx, vcoreid, p->env_entry,
+                     vcpd->transition_stack, vcpd->vcore_tls_desc);
+       return;
+error_out:
+       print_trapframe(hw_tf);
+       enable_irq();
+       proc_destroy(p);
+}
+
 struct kmem_cache *kernel_msg_cache;
 
 void kernel_msg_init(void)
diff --git a/tests/mmap.c b/tests/mmap.c
new file mode 100644 (file)
index 0000000..5c1aa76
--- /dev/null
@@ -0,0 +1,80 @@
+/* Copyright (c) 2014 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * mmap_test: dumping ground for various tests, such as PFs on files. */
+
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <parlib.h>
+#include <timing.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+       
+void *addr = 0;
+size_t nr_pgs = 1;
+#define STRIDE 256
+
+static void mmap_test(void)
+{
+       assert(addr);
+       for (int *i = addr; (void*)i < addr + nr_pgs * PGSIZE; i += STRIDE) {
+               *i += 1;
+       }
+}
+
+void *worker_thread(void* arg)
+{      
+       while (1) {
+               mmap_test();
+               uthread_sleep(1);
+       }
+       return 0;
+}
+
+int main(int argc, char** argv) 
+{
+       pthread_t child;
+       void *child_ret;
+       int fd;
+       struct stat statbuf;
+
+       if (argc < 2) {
+               printf("Usage: %s FILENAME [NR_PGS]\n", argv[0]);
+               exit(-1);
+       }
+       /* if you're going to create, you'll need to seek too */
+       //fd = open(argv[1], O_RDWR | O_CREAT, 0666);
+       fd = open(argv[1], O_RDWR, 0666);
+       if (fd < 0) {
+               perror("Unable to open file");
+               exit(-1);
+       }
+       if (argc < 3)
+               nr_pgs = 1;
+       else
+               nr_pgs = atoi(argv[2]);
+       if (fstat(fd, &statbuf)) {
+               perror("Stat failed");
+               exit(-1);
+       }
+       nr_pgs = MIN(nr_pgs, (ROUNDUP(statbuf.st_size, PGSIZE) >> PGSHIFT));
+       addr = mmap(0, nr_pgs * PGSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+       if (addr == MAP_FAILED) {
+               perror("mmap failed");
+               exit(-1);
+       }
+       printf("Running as an SCP\n");
+       mmap_test();
+       printf("Spawning worker thread, etc...\n");
+       pthread_create(&child, NULL, &worker_thread, NULL);
+       pthread_join(child, &child_ret);
+} 
index f0fb959..a6605d6 100644 (file)
@@ -111,4 +111,29 @@ static inline void init_user_ctx(struct user_context *ctx, uint32_t entry_pt,
        temp; \
 })
 
+#error implement these
+static bool has_refl_fault(struct user_context *ctx)
+{
+       return 0;
+}
+
+static void clear_refl_fault(struct user_context *ctx)
+{
+}
+
+static unsigned int __arch_refl_get_nr(struct user_context *ctx)
+{
+       return 0;
+}
+
+static unsigned int __arch_refl_get_err(struct user_context *ctx)
+{
+       return 0;
+}
+
+static unsigned long __arch_refl_get_aux(struct user_context *ctx)
+{
+       return 0;
+}
+
 #endif /* PARLIB_ARCH_VCORE_H */
index 400f475..d3a3e3d 100644 (file)
@@ -369,4 +369,29 @@ static void print_user_context(struct user_context *ctx)
                printf("Unknown context type %d\n", ctx->type);
 }
 
+static bool has_refl_fault(struct user_context *ctx)
+{
+       return ctx->tf.hw_tf.tf_padding3 == ROS_ARCH_REFL_ID;
+}
+
+static void clear_refl_fault(struct user_context *ctx)
+{
+       ctx->tf.hw_tf.tf_padding3 = 0;
+}
+
+static unsigned int __arch_refl_get_nr(struct user_context *ctx)
+{
+       return ctx->tf.hw_tf.tf_trapno;
+}
+
+static unsigned int __arch_refl_get_err(struct user_context *ctx)
+{
+       return ctx->tf.hw_tf.tf_err;
+}
+
+static unsigned long __arch_refl_get_aux(struct user_context *ctx)
+{
+       return ctx->tf.hw_tf.tf_regs.reg_oesp;
+}
+
 #endif /* PARLIB_ARCH_VCORE32_H */
index 93dcd32..9ae612d 100644 (file)
@@ -417,4 +417,30 @@ static void print_user_context(struct user_context *ctx)
                printf("Unknown context type %d\n", ctx->type);
 }
 
+static bool has_refl_fault(struct user_context *ctx)
+{
+       return ctx->tf.hw_tf.tf_padding3 == ROS_ARCH_REFL_ID;
+}
+
+static void clear_refl_fault(struct user_context *ctx)
+{
+       ctx->tf.hw_tf.tf_padding3 = 0;
+}
+
+static unsigned int __arch_refl_get_nr(struct user_context *ctx)
+{
+       return ctx->tf.hw_tf.tf_trapno;
+}
+
+static unsigned int __arch_refl_get_err(struct user_context *ctx)
+{
+       return ctx->tf.hw_tf.tf_err;
+}
+
+static unsigned long __arch_refl_get_aux(struct user_context *ctx)
+{
+       return ((unsigned long)ctx->tf.hw_tf.tf_padding5 << 32) |
+              ctx->tf.hw_tf.tf_padding4;
+}
+
 #endif /* PARLIB_ARCH_VCORE64_H */
index 44093b9..aaa3231 100644 (file)
@@ -458,12 +458,28 @@ static void set_uthread_tls(struct uthread *uthread, uint32_t vcoreid)
        }
 }
 
+/* Attempts to handle a fault for uth, etc */
+static void handle_refl_fault(struct uthread *uth, struct user_context *ctx)
+{
+       unsigned int trap_nr = __arch_refl_get_nr(ctx);
+       unsigned int err = __arch_refl_get_err(ctx);
+       unsigned long aux = __arch_refl_get_aux(ctx);
+
+       printf("detected faulted uthread, nr %d, err %08x, aux %p\n", trap_nr, err,
+              aux);
+       if (err & PF_VMR_BACKED)
+               printf("and it's VMR backed\n");
+       /* TODO: call 2LS op, which needs to handle list membership and resched */
+       exit(-1);
+}
+
 /* Run the thread that was current_uthread, from a previous run.  Should be
  * called only when the uthread already was running, and we were interrupted by
  * the kernel (event, etc).  Do not call this to run a fresh uthread, even if
  * you've set it to be current. */
 void run_current_uthread(void)
 {
+       struct uthread *uth;
        uint32_t vcoreid = vcore_id();
        struct preempt_data *vcpd = vcpd_of(vcoreid);
        assert(current_uthread);
@@ -473,6 +489,22 @@ void run_current_uthread(void)
        assert(!(current_uthread->flags & UTHREAD_FPSAVED));
        printd("[U] Vcore %d is restarting uthread %08p\n", vcoreid,
               current_uthread);
+       if (has_refl_fault(&vcpd->uthread_ctx)) {
+               clear_refl_fault(&vcpd->uthread_ctx);
+               /* we preemptively copy out and make non-running, so that there is a
+                * consistent state for the handler.  it can then block the uth or
+                * whatever. */
+               uth = current_uthread;
+               current_uthread = 0;
+               uth->u_ctx = vcpd->uthread_ctx;
+               save_fp_state(&uth->as);
+               uth->state == UT_NOT_RUNNING;
+               uth->flags |= UTHREAD_SAVED | UTHREAD_FPSAVED;
+               handle_refl_fault(uth, &vcpd->uthread_ctx);
+               /* we abort no matter what.  up to the 2LS to reschedule the thread */
+               set_stack_pointer((void*)vcpd->transition_stack);
+               vcore_entry();
+       }
        /* Go ahead and start the uthread */
        set_uthread_tls(current_uthread, vcoreid);
        /* Run, using the TF in the VCPD.  FP state should already be loaded */
@@ -507,6 +539,13 @@ void run_uthread(struct uthread *uthread)
                assert(uthread->flags & UTHREAD_FPSAVED);
        else
                assert(!(uthread->flags & UTHREAD_FPSAVED));
+       if (has_refl_fault(&uthread->u_ctx)) {
+               clear_refl_fault(&uthread->u_ctx);
+               handle_refl_fault(uthread, &uthread->u_ctx);
+               /* we abort no matter what.  up to the 2LS to reschedule the thread */
+               set_stack_pointer((void*)vcpd->transition_stack);
+               vcore_entry();
+       }
        uthread->state = UT_RUNNING;
        /* Save a ptr to the uthread we'll run in the transition context's TLS */
        current_uthread = uthread;
@@ -528,6 +567,10 @@ static void __run_current_uthread_raw(void)
 {
        uint32_t vcoreid = vcore_id();
        struct preempt_data *vcpd = vcpd_of(vcoreid);
+       if (has_refl_fault(&vcpd->uthread_ctx)) {
+               printf("Raw / DONT_MIGRATE uthread took a fault, exiting.\n");
+               exit(-1);
+       }
        /* We need to manually say we have a notif pending, so we eventually return
         * to vcore context.  (note the kernel turned it off for us) */
        vcpd->notif_pending = TRUE;