aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/mips/Kconfig8
-rw-r--r--arch/mips/kernel/Makefile1
-rw-r--r--arch/mips/kernel/process.c1
-rw-r--r--arch/mips/kernel/stacktrace.c111
-rw-r--r--arch/mips/kernel/traps.c37
-rw-r--r--include/asm-mips/stacktrace.h44
6 files changed, 168 insertions, 34 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 30750c54bdf5..ba3a317ea42c 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1841,6 +1841,14 @@ config RWSEM_GENERIC_SPINLOCK
1841 bool 1841 bool
1842 default y 1842 default y
1843 1843
1844config LOCKDEP_SUPPORT
1845 bool
1846 default y
1847
1848config STACKTRACE_SUPPORT
1849 bool
1850 default y
1851
1844source "init/Kconfig" 1852source "init/Kconfig"
1845 1853
1846menu "Bus options (PCI, PCMCIA, EISA, ISA, TC)" 1854menu "Bus options (PCI, PCMCIA, EISA, ISA, TC)"
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 881c467c6982..cd9cec9e39e9 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -11,6 +11,7 @@ obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \
11binfmt_irix-objs := irixelf.o irixinv.o irixioctl.o irixsig.o \ 11binfmt_irix-objs := irixelf.o irixinv.o irixioctl.o irixsig.o \
12 irix5sys.o sysirix.o 12 irix5sys.o sysirix.o
13 13
14obj-$(CONFIG_STACKTRACE) += stacktrace.o
14obj-$(CONFIG_MODULES) += mips_ksyms.o module.o 15obj-$(CONFIG_MODULES) += mips_ksyms.o module.o
15 16
16obj-$(CONFIG_APM) += apm.o 17obj-$(CONFIG_APM) += apm.o
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 2613a0dd4b82..695538031c69 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -40,6 +40,7 @@
40#include <asm/elf.h> 40#include <asm/elf.h>
41#include <asm/isadep.h> 41#include <asm/isadep.h>
42#include <asm/inst.h> 42#include <asm/inst.h>
43#include <asm/stacktrace.h>
43#ifdef CONFIG_MIPS_MT_SMTC 44#ifdef CONFIG_MIPS_MT_SMTC
44#include <asm/mipsmtregs.h> 45#include <asm/mipsmtregs.h>
45extern void smtc_idle_loop_hook(void); 46extern void smtc_idle_loop_hook(void);
diff --git a/arch/mips/kernel/stacktrace.c b/arch/mips/kernel/stacktrace.c
new file mode 100644
index 000000000000..f851d0cc245f
--- /dev/null
+++ b/arch/mips/kernel/stacktrace.c
@@ -0,0 +1,111 @@
1/*
2 * arch/mips/kernel/stacktrace.c
3 *
4 * Stack trace management functions
5 *
6 * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
7 */
8#include <linux/sched.h>
9#include <linux/stacktrace.h>
10#include <asm/stacktrace.h>
11
12/*
13 * Save stack-backtrace addresses into a stack_trace buffer:
14 */
15static void save_raw_context_stack(struct stack_trace *trace,
16 unsigned int skip, unsigned long reg29)
17{
18 unsigned long *sp = (unsigned long *)reg29;
19 unsigned long addr;
20
21 while (!kstack_end(sp)) {
22 addr = *sp++;
23 if (__kernel_text_address(addr)) {
24 if (!skip)
25 trace->entries[trace->nr_entries++] = addr;
26 else
27 skip--;
28 if (trace->nr_entries >= trace->max_entries)
29 break;
30 }
31 }
32}
33
34static struct pt_regs * save_context_stack(struct stack_trace *trace,
35 unsigned int skip, struct task_struct *task, struct pt_regs *regs)
36{
37 unsigned long sp = regs->regs[29];
38#ifdef CONFIG_KALLSYMS
39 unsigned long ra = regs->regs[31];
40 unsigned long pc = regs->cp0_epc;
41 extern void ret_from_irq(void);
42
43 if (raw_show_trace || !__kernel_text_address(pc)) {
44 save_raw_context_stack(trace, skip, sp);
45 return NULL;
46 }
47 do {
48 if (!skip)
49 trace->entries[trace->nr_entries++] = pc;
50 else
51 skip--;
52 if (trace->nr_entries >= trace->max_entries)
53 break;
54 /*
55 * If we reached the bottom of interrupt context,
56 * return saved pt_regs.
57 */
58 if (pc == (unsigned long)ret_from_irq) {
59 unsigned long stack_page =
60 (unsigned long)task_stack_page(task);
61 if (!stack_page ||
62 sp < stack_page ||
63 sp > stack_page + THREAD_SIZE - 32)
64 break;
65 return (struct pt_regs *)sp;
66 }
67 pc = unwind_stack(task, &sp, pc, ra);
68 ra = 0;
69 } while (pc);
70#else
71 save_raw_context_stack(sp);
72#endif
73
74 return NULL;
75}
76
77/*
78 * Save stack-backtrace addresses into a stack_trace buffer.
79 * If all_contexts is set, all contexts (hardirq, softirq and process)
80 * are saved. If not set then only the current context is saved.
81 */
82void save_stack_trace(struct stack_trace *trace,
83 struct task_struct *task, int all_contexts,
84 unsigned int skip)
85{
86 struct pt_regs dummyregs;
87 struct pt_regs *regs = &dummyregs;
88
89 WARN_ON(trace->nr_entries || !trace->max_entries);
90
91 if (task && task != current) {
92 regs->regs[29] = task->thread.reg29;
93 regs->regs[31] = 0;
94 regs->cp0_epc = task->thread.reg31;
95 } else {
96 if (!task)
97 task = current;
98 prepare_frametrace(regs);
99 }
100
101 while (1) {
102 regs = save_context_stack(trace, skip, task, regs);
103 if (!all_contexts || !regs ||
104 trace->nr_entries >= trace->max_entries)
105 break;
106 trace->entries[trace->nr_entries++] = ULONG_MAX;
107 if (trace->nr_entries >= trace->max_entries)
108 break;
109 skip = 0;
110 }
111}
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index e51d8fd9a152..440b8651fd8b 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -41,6 +41,7 @@
41#include <asm/mmu_context.h> 41#include <asm/mmu_context.h>
42#include <asm/watch.h> 42#include <asm/watch.h>
43#include <asm/types.h> 43#include <asm/types.h>
44#include <asm/stacktrace.h>
44 45
45extern asmlinkage void handle_int(void); 46extern asmlinkage void handle_int(void);
46extern asmlinkage void handle_tlbm(void); 47extern asmlinkage void handle_tlbm(void);
@@ -92,16 +93,14 @@ static void show_raw_backtrace(unsigned long reg29)
92} 93}
93 94
94#ifdef CONFIG_KALLSYMS 95#ifdef CONFIG_KALLSYMS
95static int raw_show_trace; 96int raw_show_trace;
96static int __init set_raw_show_trace(char *str) 97static int __init set_raw_show_trace(char *str)
97{ 98{
98 raw_show_trace = 1; 99 raw_show_trace = 1;
99 return 1; 100 return 1;
100} 101}
101__setup("raw_show_trace", set_raw_show_trace); 102__setup("raw_show_trace", set_raw_show_trace);
102 103#endif
103extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
104 unsigned long pc, unsigned long ra);
105 104
106static void show_backtrace(struct task_struct *task, struct pt_regs *regs) 105static void show_backtrace(struct task_struct *task, struct pt_regs *regs)
107{ 106{
@@ -121,9 +120,6 @@ static void show_backtrace(struct task_struct *task, struct pt_regs *regs)
121 } while (pc); 120 } while (pc);
122 printk("\n"); 121 printk("\n");
123} 122}
124#else
125#define show_backtrace(task, r) show_raw_backtrace((r)->regs[29]);
126#endif
127 123
128/* 124/*
129 * This routine abuses get_user()/put_user() to reference pointers 125 * This routine abuses get_user()/put_user() to reference pointers
@@ -158,28 +154,6 @@ static void show_stacktrace(struct task_struct *task, struct pt_regs *regs)
158 show_backtrace(task, regs); 154 show_backtrace(task, regs);
159} 155}
160 156
161static __always_inline void prepare_frametrace(struct pt_regs *regs)
162{
163 __asm__ __volatile__(
164 ".set push\n\t"
165 ".set noat\n\t"
166#ifdef CONFIG_64BIT
167 "1: dla $1, 1b\n\t"
168 "sd $1, %0\n\t"
169 "sd $29, %1\n\t"
170 "sd $31, %2\n\t"
171#else
172 "1: la $1, 1b\n\t"
173 "sw $1, %0\n\t"
174 "sw $29, %1\n\t"
175 "sw $31, %2\n\t"
176#endif
177 ".set pop\n\t"
178 : "=m" (regs->cp0_epc),
179 "=m" (regs->regs[29]), "=m" (regs->regs[31])
180 : : "memory");
181}
182
183void show_stack(struct task_struct *task, unsigned long *sp) 157void show_stack(struct task_struct *task, unsigned long *sp)
184{ 158{
185 struct pt_regs regs; 159 struct pt_regs regs;
@@ -206,11 +180,6 @@ void dump_stack(void)
206{ 180{
207 struct pt_regs regs; 181 struct pt_regs regs;
208 182
209 /*
210 * Remove any garbage that may be in regs (specially func
211 * addresses) to avoid show_raw_backtrace() to report them
212 */
213 memset(&regs, 0, sizeof(regs));
214 prepare_frametrace(&regs); 183 prepare_frametrace(&regs);
215 show_backtrace(current, &regs); 184 show_backtrace(current, &regs);
216} 185}
diff --git a/include/asm-mips/stacktrace.h b/include/asm-mips/stacktrace.h
new file mode 100644
index 000000000000..231f6f897a61
--- /dev/null
+++ b/include/asm-mips/stacktrace.h
@@ -0,0 +1,44 @@
1#ifndef _ASM_STACKTRACE_H
2#define _ASM_STACKTRACE_H
3
4#include <asm/ptrace.h>
5
6#ifdef CONFIG_KALLSYMS
7extern int raw_show_trace;
8extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
9 unsigned long pc, unsigned long ra);
10#else
11#define raw_show_trace 1
12#define unwind_stack(task, sp, pc, ra) 0
13#endif
14
15static __always_inline void prepare_frametrace(struct pt_regs *regs)
16{
17#ifndef CONFIG_KALLSYMS
18 /*
19 * Remove any garbage that may be in regs (specially func
20 * addresses) to avoid show_raw_backtrace() to report them
21 */
22 memset(regs, 0, sizeof(*regs));
23#endif
24 __asm__ __volatile__(
25 ".set push\n\t"
26 ".set noat\n\t"
27#ifdef CONFIG_64BIT
28 "1: dla $1, 1b\n\t"
29 "sd $1, %0\n\t"
30 "sd $29, %1\n\t"
31 "sd $31, %2\n\t"
32#else
33 "1: la $1, 1b\n\t"
34 "sw $1, %0\n\t"
35 "sw $29, %1\n\t"
36 "sw $31, %2\n\t"
37#endif
38 ".set pop\n\t"
39 : "=m" (regs->cp0_epc),
40 "=m" (regs->regs[29]), "=m" (regs->regs[31])
41 : : "memory");
42}
43
44#endif /* _ASM_STACKTRACE_H */