Add support for attempting returns from panic
[akaros.git] / kern / src / trap.c
index 50c4a8c..5e3824d 100644 (file)
 #include <kdebug.h>
 #include <kmalloc.h>
 
+static void print_unhandled_trap(struct proc *p, struct user_context *ctx,
+                                 unsigned int trap_nr, unsigned int err,
+                                 unsigned long aux)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       uint32_t vcoreid = pcpui->owning_vcoreid;
+       struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
+       static spinlock_t print_trap_lock = SPINLOCK_INITIALIZER;
+
+       spin_lock(&print_trap_lock);
+       if (!proc_is_vcctx_ready(p))
+               printk("Unhandled user trap from early SCP\n");
+       else if (vcpd->notif_disabled)
+               printk("Unhandled user trap in vcore context from VC %d\n", vcoreid);
+       print_user_ctx(ctx);
+       printk("err 0x%x (for PFs: User 4, Wr 2, Rd 1), aux %p\n", err, aux);
+       debug_addr_proc(p, get_user_ctx_pc(ctx));
+       print_vmrs(p);
+       backtrace_user_ctx(p, ctx);
+       spin_unlock(&print_trap_lock);
+}
+
+/* Traps that are considered normal operations. */
+static bool benign_trap(unsigned int err)
+{
+       return err & PF_VMR_BACKED;
+}
+
+static void printx_unhandled_trap(struct proc *p, struct user_context *ctx,
+                                  unsigned int trap_nr, unsigned int err,
+                                  unsigned long aux)
+{
+       if (printx_on && !benign_trap(err))
+               print_unhandled_trap(p, ctx, trap_nr, err, aux);
+}
+
+/* Helper, reflects the current context back to the 2LS.  Returns 0 on success,
+ * -1 on failure. */
+int reflect_current_context(void)
+{
+       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];
+
+       assert(pcpui->cur_proc == pcpui->owning_proc);
+       if (!proc_is_vcctx_ready(p))
+               return -1;
+       if (vcpd->notif_disabled)
+               return -1;
+       /* the guts of a __notify */
+       vcpd->notif_disabled = TRUE;
+       copy_current_ctx_to(&vcpd->uthread_ctx);
+       memset(pcpui->cur_ctx, 0, sizeof(struct user_context));
+       proc_init_ctx(pcpui->cur_ctx, vcoreid, vcpd->vcore_entry,
+                     vcpd->vcore_stack, vcpd->vcore_tls_desc);
+       return 0;
+}
+
+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;
+
+       assert(p);
+       assert(pcpui->cur_ctx && (pcpui->cur_ctx->type == ROS_HW_CTX));
+       /* 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(&pcpui->cur_ctx->tf.hw_tf, trap_nr, err, aux);
+       printx_unhandled_trap(p, pcpui->cur_ctx, trap_nr, err, aux);
+       if (reflect_current_context()) {
+               print_unhandled_trap(p, pcpui->cur_ctx, trap_nr, err, aux);
+               proc_destroy(p);
+       }
+}
+
+/* Helper, copies the current context to to_ctx. */
+void copy_current_ctx_to(struct user_context *to_ctx)
+{
+       struct user_context *cur_ctx = current_ctx;
+
+       /* Be sure to finalize into cur_ctx, not the to_ctx.  o/w the arch could get
+        * confused by other calls to finalize. */
+       arch_finalize_ctx(cur_ctx);
+       *to_ctx = *cur_ctx;
+}
+
 struct kmem_cache *kernel_msg_cache;
 
 void kernel_msg_init(void)
 {
        kernel_msg_cache = kmem_cache_create("kernel_msgs",
-                          sizeof(struct kernel_message), HW_CACHE_ALIGN, 0, 0, 0);
+                                            sizeof(struct kernel_message),
+                                            ARCH_CL_SIZE, 0, NULL, 0, 0, NULL);
 }
 
 uint32_t send_kernel_message(uint32_t dst, amr_t pc, long arg0, long arg1,
@@ -56,100 +150,109 @@ uint32_t send_kernel_message(uint32_t dst, amr_t pc, long arg0, long arg1,
        return 0;
 }
 
-/* Helper function.  Returns 0 if the list was empty. */
-static kernel_message_t *get_next_amsg(struct kernel_msg_list *list_head,
-                                       spinlock_t *list_lock)
-{
-       kernel_message_t *k_msg;
-       spin_lock_irqsave(list_lock);
-       k_msg = STAILQ_FIRST(list_head);
-       if (k_msg)
-               STAILQ_REMOVE_HEAD(list_head, link);
-       spin_unlock_irqsave(list_lock);
-       return k_msg;
-}
-
-/* Kernel message handler.  Extensive documentation is in
- * Documentation/kernel_messages.txt.
+/* Kernel message IPI/IRQ handler.
  *
- * In general: this processes immediate messages, then routine messages.
- * Routine messages might not return (__startcore, etc), so we need to be
- * careful about a few things.
+ * This processes immediate messages, and that's it (it used to handle routines
+ * too, if it came in from userspace).  Routine messages will get processed when
+ * the kernel has a chance (right before popping to userspace or in smp_idle
+ * before halting).
  *
  * Note that all of this happens from interrupt context, and interrupts are
- * currently disabled for this gate.  Interrupts need to be disabled so that the
- * self-ipi doesn't preempt the execution of this kernel message. */
-void handle_kmsg_ipi(struct trapframe *tf, void *data)
-{
-
-       per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
-       kernel_message_t msg_cp, *k_msg;
-
-       while (1) { // will break out when there are no more messages
-               /* Try to get an immediate message.  Exec and free it. */
-               k_msg = get_next_amsg(&myinfo->immed_amsgs, &myinfo->immed_amsg_lock);
-               if (k_msg) {
-                       assert(k_msg->pc);
-                       k_msg->pc(tf, k_msg->srcid, k_msg->arg0, k_msg->arg1, k_msg->arg2);
-                       kmem_cache_free(kernel_msg_cache, (void*)k_msg);
-               } else { // no immediate, might be a routine
-                       if (in_kernel(tf))
-                               return; // don't execute routine msgs if we were in the kernel
-                       k_msg = get_next_amsg(&myinfo->routine_amsgs,
-                                             &myinfo->routine_amsg_lock);
-                       if (!k_msg) // no routines either
-                               return;
-                       /* copy in, and then free, in case we don't return */
-                       msg_cp = *k_msg;
-                       kmem_cache_free(kernel_msg_cache, (void*)k_msg);
-                       /* Execute the kernel message */
-                       assert(msg_cp.pc);
-                       assert(msg_cp.dstid == core_id());
-                       /* TODO: when batching syscalls, this should be reread from cur_tf*/
-                       msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
-               }
+ * disabled. */
+void handle_kmsg_ipi(struct hw_trapframe *hw_tf, void *data)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct kernel_message *kmsg_i, *temp;
+       /* Avoid locking if the list appears empty (lockless peek is okay) */
+       if (STAILQ_EMPTY(&pcpui->immed_amsgs))
+               return;
+       /* The lock serves as a cmb to force a re-read of the head of the list */
+       spin_lock_irqsave(&pcpui->immed_amsg_lock);
+       STAILQ_FOREACH_SAFE(kmsg_i, &pcpui->immed_amsgs, link, temp) {
+               pcpui_trace_kmsg(pcpui, (uintptr_t)kmsg_i->pc);
+               kmsg_i->pc(kmsg_i->srcid, kmsg_i->arg0, kmsg_i->arg1, kmsg_i->arg2);
+               STAILQ_REMOVE(&pcpui->immed_amsgs, kmsg_i, kernel_message, link);
+               kmem_cache_free(kernel_msg_cache, (void*)kmsg_i);
        }
+       spin_unlock_irqsave(&pcpui->immed_amsg_lock);
 }
 
-/* Runs any outstanding routine kernel messages from within the kernel.  Will
- * make sure immediates still run first (or when they arrive, if processing a
- * bunch of these messages).  This will disable interrupts, and restore them to
- * whatever state you left them. */
-void process_routine_kmsg(struct trapframe *tf)
-{
-       per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
-       kernel_message_t msg_cp, *k_msg;
-       int8_t irq_state = 0;
-
-       disable_irqsave(&irq_state);
-       /* If we were told what our TF was, use that.  o/w, go with current_tf. */
-       tf = tf ? tf : current_tf;
-       while (1) {
-               /* normally, we want ints disabled, so we don't have an empty self-ipi
-                * for every routine message. (imagine a long list of routines).  But we
-                * do want immediates to run ahead of routines.  This enabling should
-                * work (might not in some shitty VMs).  Also note we can receive an
-                * extra self-ipi for routine messages before we turn off irqs again.
-                * Not a big deal, since we will process it right away. 
-                * TODO: consider calling __kernel_message() here. */
-               if (!STAILQ_EMPTY(&myinfo->immed_amsgs)) {
-                       enable_irq();
-                       cpu_relax();
-                       disable_irq();
-               }
-               k_msg = get_next_amsg(&myinfo->routine_amsgs,
-                                     &myinfo->routine_amsg_lock);
-               if (!k_msg) {
-                       enable_irqsave(&irq_state);
-                       return;
-               }
-               /* copy in, and then free, in case we don't return */
-               msg_cp = *k_msg;
-               kmem_cache_free(kernel_msg_cache, (void*)k_msg);
-               /* Execute the kernel message */
-               assert(msg_cp.pc);
-               assert(msg_cp.dstid == core_id());
-               msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
+bool has_routine_kmsg(void)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       /* lockless peek */
+       return !STAILQ_EMPTY(&pcpui->routine_amsgs);
+}
+
+/* Helper function, gets the next routine KMSG (RKM).  Returns 0 if the list was
+ * empty. */
+static kernel_message_t *get_next_rkmsg(struct per_cpu_info *pcpui)
+{
+       struct kernel_message *kmsg;
+       /* Avoid locking if the list appears empty (lockless peek is okay) */
+       if (STAILQ_EMPTY(&pcpui->routine_amsgs))
+               return 0;
+       /* The lock serves as a cmb to force a re-read of the head of the list.
+        * IRQs are disabled by our caller. */
+       spin_lock(&pcpui->routine_amsg_lock);
+       kmsg = STAILQ_FIRST(&pcpui->routine_amsgs);
+       if (kmsg)
+               STAILQ_REMOVE_HEAD(&pcpui->routine_amsgs, link);
+       spin_unlock(&pcpui->routine_amsg_lock);
+       return kmsg;
+}
+
+/* Runs routine kernel messages.  This might not return.  In the past, this
+ * would also run immediate messages, but this is unnecessary.  Immediates will
+ * run whenever we reenable IRQs.  We could have some sort of ordering or
+ * guarantees between KMSG classes, but that's not particularly useful at this
+ * point.
+ *
+ * Note this runs from normal context, with interruptes disabled.  However, a
+ * particular RKM could enable interrupts - for instance __launch_kthread() will
+ * restore an old kthread that may have had IRQs on. */
+void process_routine_kmsg(void)
+{
+       uint32_t pcoreid = core_id();
+       struct per_cpu_info *pcpui = &per_cpu_info[pcoreid];
+       struct kernel_message msg_cp, *kmsg;
+
+       /* Important that callers have IRQs disabled.  When sending cross-core RKMs,
+        * the IPI is used to keep the core from going to sleep - even though RKMs
+        * aren't handled in the kmsg handler.  Check smp_idle() for more info. */
+       assert(!irq_is_enabled());
+       while ((kmsg = get_next_rkmsg(pcpui))) {
+               /* Copy in, and then free, in case we don't return */
+               msg_cp = *kmsg;
+               kmem_cache_free(kernel_msg_cache, (void*)kmsg);
+               assert(msg_cp.dstid == pcoreid);        /* caught a brutal bug with this */
+               set_rkmsg(pcpui);                                       /* we're now in early RKM ctx */
+               /* The kmsg could block.  If it does, we want the kthread code to know
+                * it's not running on behalf of a process, and we're actually spawning
+                * a kernel task.  While we do have a syscall that does work in an RKM
+                * (change_to), it's not really the rest of the syscall context. */
+               pcpui->cur_kthread->flags = KTH_KTASK_FLAGS;
+               pcpui_trace_kmsg(pcpui, (uintptr_t)msg_cp.pc);
+               msg_cp.pc(msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
+               /* And if we make it back, be sure to restore the default flags.  If we
+                * never return, but the kthread exits via some other way (smp_idle()),
+                * then smp_idle() will deal with the flags.  The default state includes
+                * 'not a ktask'. */
+               pcpui->cur_kthread->flags = KTH_DEFAULT_FLAGS;
+               /* If we aren't still in early RKM, it is because the KMSG blocked
+                * (thus leaving early RKM, finishing in default context) and then
+                * returned.  This is a 'detached' RKM.  Must idle in this scenario,
+                * since we might have migrated or otherwise weren't meant to PRKM
+                * (can't return twice).  Also note that this may involve a core
+                * migration, so we need to reread pcpui.*/
+               cmb();
+               pcpui = &per_cpu_info[core_id()];
+               if (!in_early_rkmsg_ctx(pcpui))
+                       smp_idle();
+               clear_rkmsg(pcpui);
+               /* Some RKMs might turn on interrupts (perhaps in the future) and then
+                * return. */
+               disable_irq();
        }
 }
 
@@ -164,8 +267,9 @@ void print_kmsgs(uint32_t coreid)
                struct kernel_message *kmsg_i;
                STAILQ_FOREACH(kmsg_i, list, link) {
                        fn_name = get_fn_name((long)kmsg_i->pc);
-                       printk("%s KMSG on %d from %d to run %08p(%s)\n", type,
-                              kmsg_i->dstid, kmsg_i->srcid, kmsg_i->pc, fn_name); 
+                       printk("%s KMSG on %d from %d to run %p(%s)(%p, %p, %p)\n", type,
+                              kmsg_i->dstid, kmsg_i->srcid, kmsg_i->pc, fn_name,
+                              kmsg_i->arg0, kmsg_i->arg1, kmsg_i->arg2);
                        kfree(fn_name);
                }
        }
@@ -178,35 +282,63 @@ void kmsg_queue_stat(void)
 {
        struct kernel_message *kmsg;
        bool immed_emp, routine_emp;
-       for (int i = 0; i < num_cpus; i++) {
+       for (int i = 0; i < num_cores; i++) {
                spin_lock_irqsave(&per_cpu_info[i].immed_amsg_lock);
                immed_emp = STAILQ_EMPTY(&per_cpu_info[i].immed_amsgs);
                spin_unlock_irqsave(&per_cpu_info[i].immed_amsg_lock);
                spin_lock_irqsave(&per_cpu_info[i].routine_amsg_lock);
                routine_emp = STAILQ_EMPTY(&per_cpu_info[i].routine_amsgs);
                spin_unlock_irqsave(&per_cpu_info[i].routine_amsg_lock);
-               printk("Core %d's immed_emp: %d, routine_emp %d\n", i, immed_emp, routine_emp);
+               printk("Core %d's immed_emp: %d, routine_emp %d\n", i, immed_emp,
+               routine_emp);
                if (!immed_emp) {
                        kmsg = STAILQ_FIRST(&per_cpu_info[i].immed_amsgs);
                        printk("Immed msg on core %d:\n", i);
                        printk("\tsrc:  %d\n", kmsg->srcid);
                        printk("\tdst:  %d\n", kmsg->dstid);
-                       printk("\tpc:   %08p\n", kmsg->pc);
-                       printk("\targ0: %08p\n", kmsg->arg0);
-                       printk("\targ1: %08p\n", kmsg->arg1);
-                       printk("\targ2: %08p\n", kmsg->arg2);
+                       printk("\tpc:   %p\n", kmsg->pc);
+                       printk("\targ0: %p\n", kmsg->arg0);
+                       printk("\targ1: %p\n", kmsg->arg1);
+                       printk("\targ2: %p\n", kmsg->arg2);
                }
                if (!routine_emp) {
                        kmsg = STAILQ_FIRST(&per_cpu_info[i].routine_amsgs);
                        printk("Routine msg on core %d:\n", i);
                        printk("\tsrc:  %d\n", kmsg->srcid);
                        printk("\tdst:  %d\n", kmsg->dstid);
-                       printk("\tpc:   %08p\n", kmsg->pc);
-                       printk("\targ0: %08p\n", kmsg->arg0);
-                       printk("\targ1: %08p\n", kmsg->arg1);
-                       printk("\targ2: %08p\n", kmsg->arg2);
+                       printk("\tpc:   %p\n", kmsg->pc);
+                       printk("\targ0: %p\n", kmsg->arg0);
+                       printk("\targ1: %p\n", kmsg->arg1);
+                       printk("\targ2: %p\n", kmsg->arg2);
                }
-                       
+
        }
 }
 
+void print_kctx_depths(const char *str)
+{
+       uint32_t coreid = core_id();
+       struct per_cpu_info *pcpui = &per_cpu_info[coreid];
+
+       if (!str)
+               str = "(none)";
+       printk("%s: Core %d, irq depth %d, ktrap depth %d, irqon %d\n", str, coreid,
+              irq_depth(pcpui), ktrap_depth(pcpui), irq_is_enabled());
+}
+
+void print_user_ctx(struct user_context *ctx)
+{
+       switch (ctx->type) {
+       case ROS_HW_CTX:
+               print_trapframe(&ctx->tf.hw_tf);
+               break;
+       case ROS_SW_CTX:
+               print_swtrapframe(&ctx->tf.sw_tf);
+               break;
+       case ROS_VM_CTX:
+               print_vmtrapframe(&ctx->tf.vm_tf);
+               break;
+       default:
+               printk("Bad TF %p type %d!\n", ctx, ctx->type);
+       }
+}