Add panic_hwtf() for kernel faults

When the kernel faults, we would print the HWTF and maybe a backtrace,
and then panic.  These prints were not serialized like other panic
prints.  The new panic() would also print a backtrace of whoever called
panic, which isn't useful for faults (the stack frame was just the
handler).

panic_hwtf() will take a trapframe and print + backtrace that hw_tf
instead of backtracing the calling context.  This output will be
serialized, so the debug output from concurrent faults across multiple
cores won't be interleaved.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/arch/x86/trap.c b/kern/arch/x86/trap.c
index 0e326a2..82089b4 100644
--- a/kern/arch/x86/trap.c
+++ b/kern/arch/x86/trap.c
@@ -273,9 +273,8 @@
 		/* This only runs from test_uaccess(), where it is expected to fail. */
 		if (try_handle_exception_fixup(hw_tf))
 			return TRUE;
-		print_trapframe(hw_tf);
-		backtrace_hwtf(hw_tf);
-		panic("Proc-less Page Fault in the Kernel at %p!", fault_va);
+		panic_hwtf(hw_tf, "Proc-less Page Fault in the Kernel at %p!",
+		           fault_va);
 	}
 	/* TODO - handle kernel page faults.  This is dangerous, since we might be
 	 * holding locks in the kernel and could deadlock when we HPF.  For now, I'm
@@ -300,8 +299,6 @@
 	if (err) {
 		if (try_handle_exception_fixup(hw_tf))
 			return TRUE;
-		print_trapframe(hw_tf);
-		backtrace_hwtf(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,
@@ -309,7 +306,7 @@
 		       *(uintptr_t*)(hw_tf->tf_rsp +  8),
 		       *(uintptr_t*)(hw_tf->tf_rsp + 16),
 		       *(uintptr_t*)(hw_tf->tf_rsp + 24));
-		panic("Proc-ful Page Fault in the Kernel at %p!", fault_va);
+		panic_hwtf(hw_tf, "Proc-ful 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
@@ -537,9 +534,7 @@
 
 void handle_double_fault(struct hw_trapframe *hw_tf)
 {
-	print_trapframe(hw_tf);
-	backtrace_hwtf(hw_tf);
-	panic("Double fault!  Check the kernel stack pointer; you likely ran off the end of the stack.");
+	panic_hwtf(hw_tf, "Double fault!  Check the kernel stack pointer; you likely ran off the end of the stack.");
 }
 
 /* Certain traps want IRQs enabled, such as the syscall.  Others can't handle
@@ -608,10 +603,8 @@
 	}
 
 	if (!handled) {
-		if (in_kernel(hw_tf)) {
-			print_trapframe(hw_tf);
-			panic("Damn Damn!  Unhandled trap in the kernel!");
-		}
+		if (in_kernel(hw_tf))
+			panic_hwtf(hw_tf, "Damn Damn!  Unhandled trap in the kernel!");
 		reflect_unhandled_trap(hw_tf->tf_trapno, hw_tf->tf_err, aux);
 	}
 }
@@ -668,10 +661,8 @@
 	}
 	printd("Incoming TRAP %d on core %d, TF at %p\n", hw_tf->tf_trapno,
 	       core_id(), hw_tf);
-	if ((hw_tf->tf_cs & ~3) != GD_UT && (hw_tf->tf_cs & ~3) != GD_KT) {
-		print_trapframe(hw_tf);
-		panic("Trapframe with invalid CS!");
-	}
+	if ((hw_tf->tf_cs & ~3) != GD_UT && (hw_tf->tf_cs & ~3) != GD_KT)
+		panic_hwtf(hw_tf, "Trapframe with invalid CS!");
 	trap_dispatch(hw_tf);
 	/* Return to the current process, which should be runnable.  If we're the
 	 * kernel, we should just return naturally.  Note that current and tf need
diff --git a/kern/include/assert.h b/kern/include/assert.h
index 2b64c36..a2d9994 100644
--- a/kern/include/assert.h
+++ b/kern/include/assert.h
@@ -4,16 +4,18 @@
 
 #include <compiler.h>
 
-void ( _warn)(const char *, int, const char *, ...);
-void ( _panic)(const char *, int, const char *, ...)
+void _warn(const char *, int, const char *, ...);
+struct hw_trapframe;
+void _panic(struct hw_trapframe *, const char *, int, const char *, ...)
 	__attribute__((noreturn));
 
 #define warn(...) _warn(__FILE__, __LINE__, __VA_ARGS__)
 #define warn_once(...) run_once_racy(warn(__VA_ARGS__))
 #define warn_on(x) do { if (x) warn(#x);} while (0)
 #define warn_on_once(x) do { if (x) warn_once(#x);} while (0)
-#define panic(...) _panic(__FILE__, __LINE__, __VA_ARGS__)
-#define exhausted(...) _panic(__FILE__, __LINE__, __VA_ARGS__)
+#define panic(...) _panic(NULL, __FILE__, __LINE__, __VA_ARGS__)
+#define panic_hwtf(x, ...) _panic(x, __FILE__, __LINE__, __VA_ARGS__)
+#define exhausted(...) _panic(NULL, __FILE__, __LINE__, __VA_ARGS__)
 
 #define assert(x)		\
 	do { if (unlikely(!(x))) panic("assertion failed: %s", #x); } while (0)
diff --git a/kern/include/init.h b/kern/include/init.h
index 789e724..3e32f3f 100644
--- a/kern/include/init.h
+++ b/kern/include/init.h
@@ -31,6 +31,3 @@
  */
 const char *get_boot_option(const char *base, const char *option, char *param,
 							size_t max_param);
-
-void _panic(const char *file, int line, const char *fmt, ...);
-void _warn(const char *file, int line, const char *fmt, ...);
diff --git a/kern/src/init.c b/kern/src/init.c
index 5d27a3c..28b8e06 100644
--- a/kern/src/init.c
+++ b/kern/src/init.c
@@ -241,7 +241,8 @@
  * Panic is called on unresolvable fatal errors.
  * It prints "panic: mesg", and then enters the kernel monitor.
  */
-void _panic(const char *file, int line, const char *fmt,...)
+void _panic(struct hw_trapframe *hw_tf, const char *file, int line,
+            const char *fmt, ...)
 {
 	va_list ap;
 	struct per_cpu_info *pcpui;
@@ -264,11 +265,17 @@
 	va_end(ap);
 	/* Recursive panics are usually backtrace problems.  Possibly printk.
 	 * Locking panics might recurse forever. */
-	if (PERCPU_VAR(panic_depth) == 1)
-		backtrace();
-	else
+	if (PERCPU_VAR(panic_depth) == 1) {
+		if (hw_tf) {
+			print_trapframe(hw_tf);
+			backtrace_hwtf(hw_tf);
+		} else {
+			backtrace();
+		}
+	} else {
 		printk("\tRecursive kernel panic on core %d (depth %d)\n",
 		       core_id_early(), PERCPU_VAR(panic_depth));
+	}
 	printk("\n");
 
 	/* If we're here, we panicked and currently hold the lock.  We might have