diff options
Diffstat (limited to 'arch/x86_64/kernel/traps.c')
-rw-r--r-- | arch/x86_64/kernel/traps.c | 139 |
1 files changed, 25 insertions, 114 deletions
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 0d65b22f229c..09d2e8a10a49 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c | |||
@@ -30,9 +30,10 @@ | |||
30 | #include <linux/kprobes.h> | 30 | #include <linux/kprobes.h> |
31 | #include <linux/kexec.h> | 31 | #include <linux/kexec.h> |
32 | #include <linux/unwind.h> | 32 | #include <linux/unwind.h> |
33 | #include <linux/uaccess.h> | ||
34 | #include <linux/bug.h> | ||
33 | 35 | ||
34 | #include <asm/system.h> | 36 | #include <asm/system.h> |
35 | #include <asm/uaccess.h> | ||
36 | #include <asm/io.h> | 37 | #include <asm/io.h> |
37 | #include <asm/atomic.h> | 38 | #include <asm/atomic.h> |
38 | #include <asm/debugreg.h> | 39 | #include <asm/debugreg.h> |
@@ -108,12 +109,7 @@ static inline void preempt_conditional_cli(struct pt_regs *regs) | |||
108 | preempt_enable_no_resched(); | 109 | preempt_enable_no_resched(); |
109 | } | 110 | } |
110 | 111 | ||
111 | static int kstack_depth_to_print = 12; | 112 | int kstack_depth_to_print = 12; |
112 | #ifdef CONFIG_STACK_UNWIND | ||
113 | static int call_trace = 1; | ||
114 | #else | ||
115 | #define call_trace (-1) | ||
116 | #endif | ||
117 | 113 | ||
118 | #ifdef CONFIG_KALLSYMS | 114 | #ifdef CONFIG_KALLSYMS |
119 | void printk_address(unsigned long address) | 115 | void printk_address(unsigned long address) |
@@ -216,24 +212,7 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, | |||
216 | return NULL; | 212 | return NULL; |
217 | } | 213 | } |
218 | 214 | ||
219 | struct ops_and_data { | 215 | #define MSG(txt) ops->warning(data, txt) |
220 | struct stacktrace_ops *ops; | ||
221 | void *data; | ||
222 | }; | ||
223 | |||
224 | static int dump_trace_unwind(struct unwind_frame_info *info, void *context) | ||
225 | { | ||
226 | struct ops_and_data *oad = (struct ops_and_data *)context; | ||
227 | int n = 0; | ||
228 | |||
229 | while (unwind(info) == 0 && UNW_PC(info)) { | ||
230 | n++; | ||
231 | oad->ops->address(oad->data, UNW_PC(info)); | ||
232 | if (arch_unw_user_mode(info)) | ||
233 | break; | ||
234 | } | ||
235 | return n; | ||
236 | } | ||
237 | 216 | ||
238 | /* | 217 | /* |
239 | * x86-64 can have upto three kernel stacks: | 218 | * x86-64 can have upto three kernel stacks: |
@@ -248,61 +227,24 @@ static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) | |||
248 | return p > t && p < t + THREAD_SIZE - 3; | 227 | return p > t && p < t + THREAD_SIZE - 3; |
249 | } | 228 | } |
250 | 229 | ||
251 | void dump_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * stack, | 230 | void dump_trace(struct task_struct *tsk, struct pt_regs *regs, |
231 | unsigned long *stack, | ||
252 | struct stacktrace_ops *ops, void *data) | 232 | struct stacktrace_ops *ops, void *data) |
253 | { | 233 | { |
254 | const unsigned cpu = smp_processor_id(); | 234 | const unsigned cpu = get_cpu(); |
255 | unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr; | 235 | unsigned long *irqstack_end = (unsigned long*)cpu_pda(cpu)->irqstackptr; |
256 | unsigned used = 0; | 236 | unsigned used = 0; |
257 | struct thread_info *tinfo; | 237 | struct thread_info *tinfo; |
258 | 238 | ||
259 | if (!tsk) | 239 | if (!tsk) |
260 | tsk = current; | 240 | tsk = current; |
261 | 241 | ||
262 | if (call_trace >= 0) { | ||
263 | int unw_ret = 0; | ||
264 | struct unwind_frame_info info; | ||
265 | struct ops_and_data oad = { .ops = ops, .data = data }; | ||
266 | |||
267 | if (regs) { | ||
268 | if (unwind_init_frame_info(&info, tsk, regs) == 0) | ||
269 | unw_ret = dump_trace_unwind(&info, &oad); | ||
270 | } else if (tsk == current) | ||
271 | unw_ret = unwind_init_running(&info, dump_trace_unwind, &oad); | ||
272 | else { | ||
273 | if (unwind_init_blocked(&info, tsk) == 0) | ||
274 | unw_ret = dump_trace_unwind(&info, &oad); | ||
275 | } | ||
276 | if (unw_ret > 0) { | ||
277 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { | ||
278 | ops->warning_symbol(data, "DWARF2 unwinder stuck at %s\n", | ||
279 | UNW_PC(&info)); | ||
280 | if ((long)UNW_SP(&info) < 0) { | ||
281 | ops->warning(data, "Leftover inexact backtrace:\n"); | ||
282 | stack = (unsigned long *)UNW_SP(&info); | ||
283 | if (!stack) | ||
284 | return; | ||
285 | } else | ||
286 | ops->warning(data, "Full inexact backtrace again:\n"); | ||
287 | } else if (call_trace >= 1) | ||
288 | return; | ||
289 | else | ||
290 | ops->warning(data, "Full inexact backtrace again:\n"); | ||
291 | } else | ||
292 | ops->warning(data, "Inexact backtrace:\n"); | ||
293 | } | ||
294 | if (!stack) { | 242 | if (!stack) { |
295 | unsigned long dummy; | 243 | unsigned long dummy; |
296 | stack = &dummy; | 244 | stack = &dummy; |
297 | if (tsk && tsk != current) | 245 | if (tsk && tsk != current) |
298 | stack = (unsigned long *)tsk->thread.rsp; | 246 | stack = (unsigned long *)tsk->thread.rsp; |
299 | } | 247 | } |
300 | /* | ||
301 | * Align the stack pointer on word boundary, later loops | ||
302 | * rely on that (and corruption / debug info bugs can cause | ||
303 | * unaligned values here): | ||
304 | */ | ||
305 | stack = (unsigned long *)((unsigned long)stack & ~(sizeof(long)-1)); | ||
306 | 248 | ||
307 | /* | 249 | /* |
308 | * Print function call entries within a stack. 'cond' is the | 250 | * Print function call entries within a stack. 'cond' is the |
@@ -312,9 +254,9 @@ void dump_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s | |||
312 | #define HANDLE_STACK(cond) \ | 254 | #define HANDLE_STACK(cond) \ |
313 | do while (cond) { \ | 255 | do while (cond) { \ |
314 | unsigned long addr = *stack++; \ | 256 | unsigned long addr = *stack++; \ |
315 | if (oops_in_progress ? \ | 257 | /* Use unlocked access here because except for NMIs \ |
316 | __kernel_text_address(addr) : \ | 258 | we should be already protected against module unloads */ \ |
317 | kernel_text_address(addr)) { \ | 259 | if (__kernel_text_address(addr)) { \ |
318 | /* \ | 260 | /* \ |
319 | * If the address is either in the text segment of the \ | 261 | * If the address is either in the text segment of the \ |
320 | * kernel, or in the region which contains vmalloc'ed \ | 262 | * kernel, or in the region which contains vmalloc'ed \ |
@@ -377,9 +319,10 @@ void dump_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s | |||
377 | /* | 319 | /* |
378 | * This handles the process stack: | 320 | * This handles the process stack: |
379 | */ | 321 | */ |
380 | tinfo = current_thread_info(); | 322 | tinfo = task_thread_info(tsk); |
381 | HANDLE_STACK (valid_stack_ptr(tinfo, stack)); | 323 | HANDLE_STACK (valid_stack_ptr(tinfo, stack)); |
382 | #undef HANDLE_STACK | 324 | #undef HANDLE_STACK |
325 | put_cpu(); | ||
383 | } | 326 | } |
384 | EXPORT_SYMBOL(dump_trace); | 327 | EXPORT_SYMBOL(dump_trace); |
385 | 328 | ||
@@ -516,30 +459,15 @@ bad: | |||
516 | printk("\n"); | 459 | printk("\n"); |
517 | } | 460 | } |
518 | 461 | ||
519 | void handle_BUG(struct pt_regs *regs) | 462 | int is_valid_bugaddr(unsigned long rip) |
520 | { | 463 | { |
521 | struct bug_frame f; | 464 | unsigned short ud2; |
522 | long len; | ||
523 | const char *prefix = ""; | ||
524 | 465 | ||
525 | if (user_mode(regs)) | 466 | if (__copy_from_user(&ud2, (const void __user *) rip, sizeof(ud2))) |
526 | return; | 467 | return 0; |
527 | if (__copy_from_user(&f, (const void __user *) regs->rip, | 468 | |
528 | sizeof(struct bug_frame))) | 469 | return ud2 == 0x0b0f; |
529 | return; | 470 | } |
530 | if (f.filename >= 0 || | ||
531 | f.ud2[0] != 0x0f || f.ud2[1] != 0x0b) | ||
532 | return; | ||
533 | len = __strnlen_user((char *)(long)f.filename, PATH_MAX) - 1; | ||
534 | if (len < 0 || len >= PATH_MAX) | ||
535 | f.filename = (int)(long)"unmapped filename"; | ||
536 | else if (len > 50) { | ||
537 | f.filename += len - 50; | ||
538 | prefix = "..."; | ||
539 | } | ||
540 | printk("----------- [cut here ] --------- [please bite here ] ---------\n"); | ||
541 | printk(KERN_ALERT "Kernel BUG at %s%.50s:%d\n", prefix, (char *)(long)f.filename, f.line); | ||
542 | } | ||
543 | 471 | ||
544 | #ifdef CONFIG_BUG | 472 | #ifdef CONFIG_BUG |
545 | void out_of_line_bug(void) | 473 | void out_of_line_bug(void) |
@@ -619,7 +547,9 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
619 | { | 547 | { |
620 | unsigned long flags = oops_begin(); | 548 | unsigned long flags = oops_begin(); |
621 | 549 | ||
622 | handle_BUG(regs); | 550 | if (!user_mode(regs)) |
551 | report_bug(regs->rip); | ||
552 | |||
623 | __die(str, regs, err); | 553 | __die(str, regs, err); |
624 | oops_end(flags); | 554 | oops_end(flags); |
625 | do_exit(SIGSEGV); | 555 | do_exit(SIGSEGV); |
@@ -786,8 +716,7 @@ mem_parity_error(unsigned char reason, struct pt_regs * regs) | |||
786 | { | 716 | { |
787 | printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n", | 717 | printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n", |
788 | reason); | 718 | reason); |
789 | printk(KERN_EMERG "You probably have a hardware problem with your " | 719 | printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n"); |
790 | "RAM chips\n"); | ||
791 | 720 | ||
792 | if (panic_on_unrecovered_nmi) | 721 | if (panic_on_unrecovered_nmi) |
793 | panic("NMI: Not continuing"); | 722 | panic("NMI: Not continuing"); |
@@ -1193,21 +1122,3 @@ static int __init kstack_setup(char *s) | |||
1193 | return 0; | 1122 | return 0; |
1194 | } | 1123 | } |
1195 | early_param("kstack", kstack_setup); | 1124 | early_param("kstack", kstack_setup); |
1196 | |||
1197 | #ifdef CONFIG_STACK_UNWIND | ||
1198 | static int __init call_trace_setup(char *s) | ||
1199 | { | ||
1200 | if (!s) | ||
1201 | return -EINVAL; | ||
1202 | if (strcmp(s, "old") == 0) | ||
1203 | call_trace = -1; | ||
1204 | else if (strcmp(s, "both") == 0) | ||
1205 | call_trace = 0; | ||
1206 | else if (strcmp(s, "newfallback") == 0) | ||
1207 | call_trace = 1; | ||
1208 | else if (strcmp(s, "new") == 0) | ||
1209 | call_trace = 2; | ||
1210 | return 0; | ||
1211 | } | ||
1212 | early_param("call_trace", call_trace_setup); | ||
1213 | #endif | ||