blob: f69bd968fcca389e27d86f556c88e1d45e0ebe82 [file] [log] [blame]
Paul Mundt5a4f7c62007-11-20 18:08:06 +09001#include <linux/bug.h>
2#include <linux/io.h>
3#include <linux/types.h>
4#include <linux/kdebug.h>
Paul Mundt47a3eb92007-11-26 18:17:51 +09005#include <linux/signal.h>
6#include <linux/sched.h>
Paul Mundt9a33fc22008-05-19 19:32:07 +09007#include <linux/uaccess.h>
Paul Mundte115f2c2009-08-22 05:28:25 +09008#include <asm/unwinder.h>
Paul Mundt5a4f7c62007-11-20 18:08:06 +09009#include <asm/system.h>
10
11#ifdef CONFIG_BUG
Matt Flemingb344e24a2009-08-16 21:54:48 +010012void handle_BUG(struct pt_regs *regs)
Paul Mundt5a4f7c62007-11-20 18:08:06 +090013{
Paul Mundte115f2c2009-08-22 05:28:25 +090014 const struct bug_entry *bug;
15 unsigned long bugaddr = regs->pc;
Paul Mundt5a4f7c62007-11-20 18:08:06 +090016 enum bug_trap_type tt;
Paul Mundte115f2c2009-08-22 05:28:25 +090017
18 if (!is_valid_bugaddr(bugaddr))
19 goto invalid;
20
21 bug = find_bug(bugaddr);
22
23 /* Switch unwinders when unwind_stack() is called */
24 if (bug->flags & BUGFLAG_UNWINDER)
25 unwinder_faulted = 1;
26
27 tt = report_bug(bugaddr, regs);
Paul Mundt5a4f7c62007-11-20 18:08:06 +090028 if (tt == BUG_TRAP_TYPE_WARN) {
Paul Mundte115f2c2009-08-22 05:28:25 +090029 regs->pc += instruction_size(bugaddr);
Paul Mundt5a4f7c62007-11-20 18:08:06 +090030 return;
31 }
32
Paul Mundte115f2c2009-08-22 05:28:25 +090033invalid:
Paul Mundt5a4f7c62007-11-20 18:08:06 +090034 die("Kernel BUG", regs, TRAPA_BUG_OPCODE & 0xff);
35}
36
37int is_valid_bugaddr(unsigned long addr)
38{
Paul Mundt2bcfffa2009-05-09 16:02:08 +090039 insn_size_t opcode;
Paul Mundt9a33fc22008-05-19 19:32:07 +090040
41 if (addr < PAGE_OFFSET)
42 return 0;
Paul Mundt2bcfffa2009-05-09 16:02:08 +090043 if (probe_kernel_address((insn_size_t *)addr, opcode))
Paul Mundt9a33fc22008-05-19 19:32:07 +090044 return 0;
Paul Mundte115f2c2009-08-22 05:28:25 +090045 if (opcode == TRAPA_BUG_OPCODE)
Matt Flemingb344e24a2009-08-16 21:54:48 +010046 return 1;
47
48 return 0;
Paul Mundt5a4f7c62007-11-20 18:08:06 +090049}
50#endif
51
52/*
53 * Generic trap handler.
54 */
55BUILD_TRAP_HANDLER(debug)
56{
57 TRAP_HANDLER_DECL;
58
59 /* Rewind */
60 regs->pc -= instruction_size(ctrl_inw(regs->pc - 4));
61
62 if (notify_die(DIE_TRAP, "debug trap", regs, 0, vec & 0xff,
63 SIGTRAP) == NOTIFY_STOP)
64 return;
65
66 force_sig(SIGTRAP, current);
67}
68
69/*
70 * Special handler for BUG() traps.
71 */
72BUILD_TRAP_HANDLER(bug)
73{
74 TRAP_HANDLER_DECL;
75
76 /* Rewind */
77 regs->pc -= instruction_size(ctrl_inw(regs->pc - 4));
78
79 if (notify_die(DIE_TRAP, "bug trap", regs, 0, TRAPA_BUG_OPCODE & 0xff,
80 SIGTRAP) == NOTIFY_STOP)
81 return;
82
83#ifdef CONFIG_BUG
84 if (__kernel_text_address(instruction_pointer(regs))) {
Paul Mundt2bcfffa2009-05-09 16:02:08 +090085 insn_size_t insn = *(insn_size_t *)instruction_pointer(regs);
Paul Mundt5a4f7c62007-11-20 18:08:06 +090086 if (insn == TRAPA_BUG_OPCODE)
87 handle_BUG(regs);
Magnus Damm0ec39882009-06-17 04:48:20 +000088 return;
Paul Mundt5a4f7c62007-11-20 18:08:06 +090089 }
90#endif
91
92 force_sig(SIGTRAP, current);
93}