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