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