diff options
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r-- | arch/sh/kernel/debugtraps.S | 6 | ||||
-rw-r--r-- | arch/sh/kernel/dwarf.c | 38 | ||||
-rw-r--r-- | arch/sh/kernel/traps.c | 7 | ||||
-rw-r--r-- | arch/sh/kernel/traps_32.c | 1 | ||||
-rw-r--r-- | arch/sh/kernel/unwinder.c | 33 |
5 files changed, 59 insertions, 26 deletions
diff --git a/arch/sh/kernel/debugtraps.S b/arch/sh/kernel/debugtraps.S index 591741383ee6..cb00e4a82d4d 100644 --- a/arch/sh/kernel/debugtraps.S +++ b/arch/sh/kernel/debugtraps.S | |||
@@ -21,6 +21,10 @@ | |||
21 | #define sh_bios_handler debug_trap_handler | 21 | #define sh_bios_handler debug_trap_handler |
22 | #endif | 22 | #endif |
23 | 23 | ||
24 | #if !defined(CONFIG_DWARF_UNWINDER) | ||
25 | #define unwinder_trap_handler debug_trap_handler | ||
26 | #endif | ||
27 | |||
24 | .data | 28 | .data |
25 | 29 | ||
26 | ENTRY(debug_trap_table) | 30 | ENTRY(debug_trap_table) |
@@ -35,7 +39,7 @@ ENTRY(debug_trap_table) | |||
35 | .long debug_trap_handler /* 0x38 */ | 39 | .long debug_trap_handler /* 0x38 */ |
36 | .long debug_trap_handler /* 0x39 */ | 40 | .long debug_trap_handler /* 0x39 */ |
37 | .long debug_trap_handler /* 0x3a */ | 41 | .long debug_trap_handler /* 0x3a */ |
38 | .long debug_trap_handler /* 0x3b */ | 42 | .long unwinder_trap_handler /* 0x3b */ |
39 | .long breakpoint_trap_handler /* 0x3c */ | 43 | .long breakpoint_trap_handler /* 0x3c */ |
40 | .long singlestep_trap_handler /* 0x3d */ | 44 | .long singlestep_trap_handler /* 0x3d */ |
41 | .long bug_trap_handler /* 0x3e */ | 45 | .long bug_trap_handler /* 0x3e */ |
diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index d271d04adccd..606ece37eb42 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c | |||
@@ -69,7 +69,7 @@ static struct dwarf_reg *dwarf_frame_alloc_reg(struct dwarf_frame *frame, | |||
69 | * Let's just bomb hard here, we have no way to | 69 | * Let's just bomb hard here, we have no way to |
70 | * gracefully recover. | 70 | * gracefully recover. |
71 | */ | 71 | */ |
72 | BUG(); | 72 | UNWINDER_BUG(); |
73 | } | 73 | } |
74 | 74 | ||
75 | reg->number = reg_num; | 75 | reg->number = reg_num; |
@@ -232,7 +232,7 @@ static int dwarf_read_encoded_value(char *addr, unsigned long *val, | |||
232 | break; | 232 | break; |
233 | default: | 233 | default: |
234 | pr_debug("encoding=0x%x\n", (encoding & 0x70)); | 234 | pr_debug("encoding=0x%x\n", (encoding & 0x70)); |
235 | BUG(); | 235 | UNWINDER_BUG(); |
236 | } | 236 | } |
237 | 237 | ||
238 | if ((encoding & 0x07) == 0x00) | 238 | if ((encoding & 0x07) == 0x00) |
@@ -247,7 +247,7 @@ static int dwarf_read_encoded_value(char *addr, unsigned long *val, | |||
247 | break; | 247 | break; |
248 | default: | 248 | default: |
249 | pr_debug("encoding=0x%x\n", encoding); | 249 | pr_debug("encoding=0x%x\n", encoding); |
250 | BUG(); | 250 | UNWINDER_BUG(); |
251 | } | 251 | } |
252 | 252 | ||
253 | return count; | 253 | return count; |
@@ -519,6 +519,7 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, | |||
519 | break; | 519 | break; |
520 | default: | 520 | default: |
521 | pr_debug("unhandled DWARF instruction 0x%x\n", insn); | 521 | pr_debug("unhandled DWARF instruction 0x%x\n", insn); |
522 | UNWINDER_BUG(); | ||
522 | break; | 523 | break; |
523 | } | 524 | } |
524 | } | 525 | } |
@@ -535,8 +536,8 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, | |||
535 | * on the callstack. Each of the lower (older) stack frames are | 536 | * on the callstack. Each of the lower (older) stack frames are |
536 | * linked via the "prev" member. | 537 | * linked via the "prev" member. |
537 | */ | 538 | */ |
538 | struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, | 539 | struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, |
539 | struct dwarf_frame *prev) | 540 | struct dwarf_frame *prev) |
540 | { | 541 | { |
541 | struct dwarf_frame *frame; | 542 | struct dwarf_frame *frame; |
542 | struct dwarf_cie *cie; | 543 | struct dwarf_cie *cie; |
@@ -558,7 +559,7 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, | |||
558 | frame = mempool_alloc(dwarf_frame_pool, GFP_ATOMIC); | 559 | frame = mempool_alloc(dwarf_frame_pool, GFP_ATOMIC); |
559 | if (!frame) { | 560 | if (!frame) { |
560 | printk(KERN_ERR "Unable to allocate a dwarf frame\n"); | 561 | printk(KERN_ERR "Unable to allocate a dwarf frame\n"); |
561 | BUG(); | 562 | UNWINDER_BUG(); |
562 | } | 563 | } |
563 | 564 | ||
564 | INIT_LIST_HEAD(&frame->reg_list); | 565 | INIT_LIST_HEAD(&frame->reg_list); |
@@ -605,7 +606,8 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, | |||
605 | case DWARF_FRAME_CFA_REG_OFFSET: | 606 | case DWARF_FRAME_CFA_REG_OFFSET: |
606 | if (prev) { | 607 | if (prev) { |
607 | reg = dwarf_frame_reg(prev, frame->cfa_register); | 608 | reg = dwarf_frame_reg(prev, frame->cfa_register); |
608 | BUG_ON(!reg); | 609 | UNWINDER_BUG_ON(!reg); |
610 | UNWINDER_BUG_ON(reg->flags != DWARF_REG_OFFSET); | ||
609 | 611 | ||
610 | addr = prev->cfa + reg->addr; | 612 | addr = prev->cfa + reg->addr; |
611 | frame->cfa = __raw_readl(addr); | 613 | frame->cfa = __raw_readl(addr); |
@@ -624,12 +626,13 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, | |||
624 | frame->cfa += frame->cfa_offset; | 626 | frame->cfa += frame->cfa_offset; |
625 | break; | 627 | break; |
626 | default: | 628 | default: |
627 | BUG(); | 629 | UNWINDER_BUG(); |
628 | } | 630 | } |
629 | 631 | ||
630 | /* If we haven't seen the return address reg, we're screwed. */ | 632 | /* If we haven't seen the return address reg, we're screwed. */ |
631 | reg = dwarf_frame_reg(frame, DWARF_ARCH_RA_REG); | 633 | reg = dwarf_frame_reg(frame, DWARF_ARCH_RA_REG); |
632 | BUG_ON(!reg); | 634 | UNWINDER_BUG_ON(!reg); |
635 | UNWINDER_BUG_ON(reg->flags != DWARF_REG_OFFSET); | ||
633 | 636 | ||
634 | addr = frame->cfa + reg->addr; | 637 | addr = frame->cfa + reg->addr; |
635 | frame->return_addr = __raw_readl(addr); | 638 | frame->return_addr = __raw_readl(addr); |
@@ -664,7 +667,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, | |||
664 | cie->cie_pointer = (unsigned long)entry; | 667 | cie->cie_pointer = (unsigned long)entry; |
665 | 668 | ||
666 | cie->version = *(char *)p++; | 669 | cie->version = *(char *)p++; |
667 | BUG_ON(cie->version != 1); | 670 | UNWINDER_BUG_ON(cie->version != 1); |
668 | 671 | ||
669 | cie->augmentation = p; | 672 | cie->augmentation = p; |
670 | p += strlen(cie->augmentation) + 1; | 673 | p += strlen(cie->augmentation) + 1; |
@@ -694,7 +697,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, | |||
694 | count = dwarf_read_uleb128(p, &length); | 697 | count = dwarf_read_uleb128(p, &length); |
695 | p += count; | 698 | p += count; |
696 | 699 | ||
697 | BUG_ON((unsigned char *)p > end); | 700 | UNWINDER_BUG_ON((unsigned char *)p > end); |
698 | 701 | ||
699 | cie->initial_instructions = p + length; | 702 | cie->initial_instructions = p + length; |
700 | cie->augmentation++; | 703 | cie->augmentation++; |
@@ -722,16 +725,16 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, | |||
722 | * routine in the CIE | 725 | * routine in the CIE |
723 | * augmentation. | 726 | * augmentation. |
724 | */ | 727 | */ |
725 | BUG(); | 728 | UNWINDER_BUG(); |
726 | } else if (*cie->augmentation == 'S') { | 729 | } else if (*cie->augmentation == 'S') { |
727 | BUG(); | 730 | UNWINDER_BUG(); |
728 | } else { | 731 | } else { |
729 | /* | 732 | /* |
730 | * Unknown augmentation. Assume | 733 | * Unknown augmentation. Assume |
731 | * 'z' augmentation. | 734 | * 'z' augmentation. |
732 | */ | 735 | */ |
733 | p = cie->initial_instructions; | 736 | p = cie->initial_instructions; |
734 | BUG_ON(!p); | 737 | UNWINDER_BUG_ON(!p); |
735 | break; | 738 | break; |
736 | } | 739 | } |
737 | } | 740 | } |
@@ -805,9 +808,11 @@ static int dwarf_parse_fde(void *entry, u32 entry_type, | |||
805 | return 0; | 808 | return 0; |
806 | } | 809 | } |
807 | 810 | ||
808 | static void dwarf_unwinder_dump(struct task_struct *task, struct pt_regs *regs, | 811 | static void dwarf_unwinder_dump(struct task_struct *task, |
812 | struct pt_regs *regs, | ||
809 | unsigned long *sp, | 813 | unsigned long *sp, |
810 | const struct stacktrace_ops *ops, void *data) | 814 | const struct stacktrace_ops *ops, |
815 | void *data) | ||
811 | { | 816 | { |
812 | struct dwarf_frame *frame, *_frame; | 817 | struct dwarf_frame *frame, *_frame; |
813 | unsigned long return_addr; | 818 | unsigned long return_addr; |
@@ -831,7 +836,6 @@ static void dwarf_unwinder_dump(struct task_struct *task, struct pt_regs *regs, | |||
831 | return_addr = frame->return_addr; | 836 | return_addr = frame->return_addr; |
832 | ops->address(data, return_addr, 1); | 837 | ops->address(data, return_addr, 1); |
833 | } | 838 | } |
834 | |||
835 | } | 839 | } |
836 | 840 | ||
837 | static struct unwinder dwarf_unwinder = { | 841 | static struct unwinder dwarf_unwinder = { |
diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index b3e0067db358..881b9a32b7de 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c | |||
@@ -8,7 +8,7 @@ | |||
8 | #include <asm/system.h> | 8 | #include <asm/system.h> |
9 | 9 | ||
10 | #ifdef CONFIG_BUG | 10 | #ifdef CONFIG_BUG |
11 | static void handle_BUG(struct pt_regs *regs) | 11 | void handle_BUG(struct pt_regs *regs) |
12 | { | 12 | { |
13 | enum bug_trap_type tt; | 13 | enum bug_trap_type tt; |
14 | tt = report_bug(regs->pc, regs); | 14 | tt = report_bug(regs->pc, regs); |
@@ -29,7 +29,10 @@ int is_valid_bugaddr(unsigned long addr) | |||
29 | if (probe_kernel_address((insn_size_t *)addr, opcode)) | 29 | if (probe_kernel_address((insn_size_t *)addr, opcode)) |
30 | return 0; | 30 | return 0; |
31 | 31 | ||
32 | return opcode == TRAPA_BUG_OPCODE; | 32 | if (opcode == TRAPA_BUG_OPCODE || opcode == TRAPA_UNWINDER_BUG_OPCODE) |
33 | return 1; | ||
34 | |||
35 | return 0; | ||
33 | } | 36 | } |
34 | #endif | 37 | #endif |
35 | 38 | ||
diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 563426487c6b..05a04b6df844 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c | |||
@@ -136,6 +136,7 @@ static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) | |||
136 | regs->pc = fixup->fixup; | 136 | regs->pc = fixup->fixup; |
137 | return; | 137 | return; |
138 | } | 138 | } |
139 | |||
139 | die(str, regs, err); | 140 | die(str, regs, err); |
140 | } | 141 | } |
141 | } | 142 | } |
diff --git a/arch/sh/kernel/unwinder.c b/arch/sh/kernel/unwinder.c index 2b30fa28b440..b9c122abe251 100644 --- a/arch/sh/kernel/unwinder.c +++ b/arch/sh/kernel/unwinder.c | |||
@@ -53,8 +53,6 @@ static struct list_head unwinder_list = { | |||
53 | 53 | ||
54 | static DEFINE_SPINLOCK(unwinder_lock); | 54 | static DEFINE_SPINLOCK(unwinder_lock); |
55 | 55 | ||
56 | static atomic_t unwinder_running = ATOMIC_INIT(0); | ||
57 | |||
58 | /** | 56 | /** |
59 | * select_unwinder - Select the best registered stack unwinder. | 57 | * select_unwinder - Select the best registered stack unwinder. |
60 | * | 58 | * |
@@ -122,6 +120,8 @@ int unwinder_register(struct unwinder *u) | |||
122 | return ret; | 120 | return ret; |
123 | } | 121 | } |
124 | 122 | ||
123 | int unwinder_faulted = 0; | ||
124 | |||
125 | /* | 125 | /* |
126 | * Unwind the call stack and pass information to the stacktrace_ops | 126 | * Unwind the call stack and pass information to the stacktrace_ops |
127 | * functions. Also handle the case where we need to switch to a new | 127 | * functions. Also handle the case where we need to switch to a new |
@@ -144,19 +144,40 @@ void unwind_stack(struct task_struct *task, struct pt_regs *regs, | |||
144 | * Hopefully this will give us a semi-reliable stacktrace so we | 144 | * Hopefully this will give us a semi-reliable stacktrace so we |
145 | * can diagnose why curr_unwinder->dump() faulted. | 145 | * can diagnose why curr_unwinder->dump() faulted. |
146 | */ | 146 | */ |
147 | if (atomic_inc_return(&unwinder_running) != 1) { | 147 | if (unwinder_faulted) { |
148 | spin_lock_irqsave(&unwinder_lock, flags); | 148 | spin_lock_irqsave(&unwinder_lock, flags); |
149 | 149 | ||
150 | if (!list_is_singular(&unwinder_list)) { | 150 | /* Make sure no one beat us to changing the unwinder */ |
151 | if (unwinder_faulted && !list_is_singular(&unwinder_list)) { | ||
151 | list_del(&curr_unwinder->list); | 152 | list_del(&curr_unwinder->list); |
152 | curr_unwinder = select_unwinder(); | 153 | curr_unwinder = select_unwinder(); |
154 | |||
155 | unwinder_faulted = 0; | ||
153 | } | 156 | } |
154 | 157 | ||
155 | spin_unlock_irqrestore(&unwinder_lock, flags); | 158 | spin_unlock_irqrestore(&unwinder_lock, flags); |
156 | atomic_dec(&unwinder_running); | ||
157 | } | 159 | } |
158 | 160 | ||
159 | curr_unwinder->dump(task, regs, sp, ops, data); | 161 | curr_unwinder->dump(task, regs, sp, ops, data); |
162 | } | ||
163 | |||
164 | /* | ||
165 | * Trap handler for UWINDER_BUG() statements. We must switch to the | ||
166 | * unwinder with the next highest rating. | ||
167 | */ | ||
168 | BUILD_TRAP_HANDLER(unwinder) | ||
169 | { | ||
170 | insn_size_t insn; | ||
171 | TRAP_HANDLER_DECL; | ||
172 | |||
173 | /* Rewind */ | ||
174 | regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); | ||
175 | insn = *(insn_size_t *)instruction_pointer(regs); | ||
176 | |||
177 | /* Switch unwinders when unwind_stack() is called */ | ||
178 | unwinder_faulted = 1; | ||
160 | 179 | ||
161 | atomic_dec(&unwinder_running); | 180 | #ifdef CONFIG_BUG |
181 | handle_BUG(regs); | ||
182 | #endif | ||
162 | } | 183 | } |