diff options
author | Stafford Horne <shorne@gmail.com> | 2017-07-24 08:44:35 -0400 |
---|---|---|
committer | Stafford Horne <shorne@gmail.com> | 2017-11-03 01:01:15 -0400 |
commit | eecac38b0423a69715073ecbde581dafd1abb28b (patch) | |
tree | 6c675e769ef8a5f0fb81b4ea2dd7596be9c5aebd | |
parent | 306e5e50a32140452849fff42fca104511fd6668 (diff) |
openrisc: support framepointers and STACKTRACE_SUPPORT
For lockdep support a reliable stack trace mechanism is needed. This
patch adds support in OpenRISC for the stacktrace framework, implemented
by a simple unwinder api. The unwinder api supports both framepointer
and basic stack tracing.
The unwinder is now used to replace the stack_dump() implementation as
well. The new traces are inline with other architectures trace format:
Call trace:
[<c0004448>] show_stack+0x3c/0x58
[<c031c940>] dump_stack+0xa8/0xe4
[<c0008104>] __cpu_up+0x64/0x130
[<c000d268>] bringup_cpu+0x3c/0x178
[<c000d038>] cpuhp_invoke_callback+0xa8/0x1fc
[<c000d680>] cpuhp_up_callbacks+0x44/0x14c
[<c000e400>] cpu_up+0x14c/0x1bc
[<c041da60>] smp_init+0x104/0x15c
[<c033843c>] ? kernel_init+0x0/0x140
[<c0415e04>] kernel_init_freeable+0xbc/0x25c
[<c033843c>] ? kernel_init+0x0/0x140
[<c0338458>] kernel_init+0x1c/0x140
[<c003a174>] ? schedule_tail+0x18/0xa0
[<c0006b80>] ret_from_fork+0x1c/0x9c
Signed-off-by: Stafford Horne <shorne@gmail.com>
-rw-r--r-- | arch/openrisc/Kconfig | 4 | ||||
-rw-r--r-- | arch/openrisc/include/asm/unwinder.h | 20 | ||||
-rw-r--r-- | arch/openrisc/kernel/Makefile | 3 | ||||
-rw-r--r-- | arch/openrisc/kernel/stacktrace.c | 86 | ||||
-rw-r--r-- | arch/openrisc/kernel/traps.c | 54 | ||||
-rw-r--r-- | arch/openrisc/kernel/unwinder.c | 105 |
6 files changed, 224 insertions, 48 deletions
diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index bfff04ae7f7d..399f55e82dcb 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig | |||
@@ -33,6 +33,7 @@ config OPENRISC | |||
33 | select ARCH_USE_QUEUED_SPINLOCKS | 33 | select ARCH_USE_QUEUED_SPINLOCKS |
34 | select ARCH_USE_QUEUED_RWLOCKS | 34 | select ARCH_USE_QUEUED_RWLOCKS |
35 | select OMPIC if SMP | 35 | select OMPIC if SMP |
36 | select ARCH_WANT_FRAME_POINTERS | ||
36 | 37 | ||
37 | config CPU_BIG_ENDIAN | 38 | config CPU_BIG_ENDIAN |
38 | def_bool y | 39 | def_bool y |
@@ -60,6 +61,9 @@ config TRACE_IRQFLAGS_SUPPORT | |||
60 | config GENERIC_CSUM | 61 | config GENERIC_CSUM |
61 | def_bool y | 62 | def_bool y |
62 | 63 | ||
64 | config STACKTRACE_SUPPORT | ||
65 | def_bool y | ||
66 | |||
63 | source "init/Kconfig" | 67 | source "init/Kconfig" |
64 | 68 | ||
65 | source "kernel/Kconfig.freezer" | 69 | source "kernel/Kconfig.freezer" |
diff --git a/arch/openrisc/include/asm/unwinder.h b/arch/openrisc/include/asm/unwinder.h new file mode 100644 index 000000000000..165ec6f02ab8 --- /dev/null +++ b/arch/openrisc/include/asm/unwinder.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * OpenRISC unwinder.h | ||
3 | * | ||
4 | * Architecture API for unwinding stacks. | ||
5 | * | ||
6 | * Copyright (C) 2017 Stafford Horne <shorne@gmail.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #ifndef __ASM_OPENRISC_UNWINDER_H | ||
14 | #define __ASM_OPENRISC_UNWINDER_H | ||
15 | |||
16 | void unwind_stack(void *data, unsigned long *stack, | ||
17 | void (*trace)(void *data, unsigned long addr, | ||
18 | int reliable)); | ||
19 | |||
20 | #endif /* __ASM_OPENRISC_UNWINDER_H */ | ||
diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile index 7d94643c878d..b4b51a07016a 100644 --- a/arch/openrisc/kernel/Makefile +++ b/arch/openrisc/kernel/Makefile | |||
@@ -6,9 +6,10 @@ extra-y := head.o vmlinux.lds | |||
6 | 6 | ||
7 | obj-y := setup.o or32_ksyms.o process.o dma.o \ | 7 | obj-y := setup.o or32_ksyms.o process.o dma.o \ |
8 | traps.o time.o irq.o entry.o ptrace.o signal.o \ | 8 | traps.o time.o irq.o entry.o ptrace.o signal.o \ |
9 | sys_call_table.o | 9 | sys_call_table.o unwinder.o |
10 | 10 | ||
11 | obj-$(CONFIG_SMP) += smp.o | 11 | obj-$(CONFIG_SMP) += smp.o |
12 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | ||
12 | obj-$(CONFIG_MODULES) += module.o | 13 | obj-$(CONFIG_MODULES) += module.o |
13 | obj-$(CONFIG_OF) += prom.o | 14 | obj-$(CONFIG_OF) += prom.o |
14 | 15 | ||
diff --git a/arch/openrisc/kernel/stacktrace.c b/arch/openrisc/kernel/stacktrace.c new file mode 100644 index 000000000000..43f140a28bc7 --- /dev/null +++ b/arch/openrisc/kernel/stacktrace.c | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | * Stack trace utility for OpenRISC | ||
3 | * | ||
4 | * Copyright (C) 2017 Stafford Horne <shorne@gmail.com> | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public License | ||
7 | * version 2. This program is licensed "as is" without any warranty of any | ||
8 | * kind, whether express or implied. | ||
9 | * | ||
10 | * Losely based on work from sh and powerpc. | ||
11 | */ | ||
12 | |||
13 | #include <linux/export.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/sched/debug.h> | ||
16 | #include <linux/stacktrace.h> | ||
17 | |||
18 | #include <asm/processor.h> | ||
19 | #include <asm/unwinder.h> | ||
20 | |||
21 | /* | ||
22 | * Save stack-backtrace addresses into a stack_trace buffer. | ||
23 | */ | ||
24 | static void | ||
25 | save_stack_address(void *data, unsigned long addr, int reliable) | ||
26 | { | ||
27 | struct stack_trace *trace = data; | ||
28 | |||
29 | if (!reliable) | ||
30 | return; | ||
31 | |||
32 | if (trace->skip > 0) { | ||
33 | trace->skip--; | ||
34 | return; | ||
35 | } | ||
36 | |||
37 | if (trace->nr_entries < trace->max_entries) | ||
38 | trace->entries[trace->nr_entries++] = addr; | ||
39 | } | ||
40 | |||
41 | void save_stack_trace(struct stack_trace *trace) | ||
42 | { | ||
43 | unwind_stack(trace, (unsigned long *) &trace, save_stack_address); | ||
44 | } | ||
45 | EXPORT_SYMBOL_GPL(save_stack_trace); | ||
46 | |||
47 | static void | ||
48 | save_stack_address_nosched(void *data, unsigned long addr, int reliable) | ||
49 | { | ||
50 | struct stack_trace *trace = (struct stack_trace *)data; | ||
51 | |||
52 | if (!reliable) | ||
53 | return; | ||
54 | |||
55 | if (in_sched_functions(addr)) | ||
56 | return; | ||
57 | |||
58 | if (trace->skip > 0) { | ||
59 | trace->skip--; | ||
60 | return; | ||
61 | } | ||
62 | |||
63 | if (trace->nr_entries < trace->max_entries) | ||
64 | trace->entries[trace->nr_entries++] = addr; | ||
65 | } | ||
66 | |||
67 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | ||
68 | { | ||
69 | unsigned long *sp = NULL; | ||
70 | |||
71 | if (tsk == current) | ||
72 | sp = (unsigned long *) &sp; | ||
73 | else | ||
74 | sp = (unsigned long *) KSTK_ESP(tsk); | ||
75 | |||
76 | unwind_stack(trace, sp, save_stack_address_nosched); | ||
77 | } | ||
78 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); | ||
79 | |||
80 | void | ||
81 | save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) | ||
82 | { | ||
83 | unwind_stack(trace, (unsigned long *) regs->sp, | ||
84 | save_stack_address_nosched); | ||
85 | } | ||
86 | EXPORT_SYMBOL_GPL(save_stack_trace_regs); | ||
diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c index 803e9e756f77..4085d72fa5ae 100644 --- a/arch/openrisc/kernel/traps.c +++ b/arch/openrisc/kernel/traps.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <asm/segment.h> | 38 | #include <asm/segment.h> |
39 | #include <asm/io.h> | 39 | #include <asm/io.h> |
40 | #include <asm/pgtable.h> | 40 | #include <asm/pgtable.h> |
41 | #include <asm/unwinder.h> | ||
41 | 42 | ||
42 | extern char _etext, _stext; | 43 | extern char _etext, _stext; |
43 | 44 | ||
@@ -45,61 +46,20 @@ int kstack_depth_to_print = 0x180; | |||
45 | int lwa_flag; | 46 | int lwa_flag; |
46 | unsigned long __user *lwa_addr; | 47 | unsigned long __user *lwa_addr; |
47 | 48 | ||
48 | static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) | 49 | void print_trace(void *data, unsigned long addr, int reliable) |
49 | { | 50 | { |
50 | return p > (void *)tinfo && p < (void *)tinfo + THREAD_SIZE - 3; | 51 | pr_emerg("[<%p>] %s%pS\n", (void *) addr, reliable ? "" : "? ", |
51 | } | 52 | (void *) addr); |
52 | |||
53 | void show_trace(struct task_struct *task, unsigned long *stack) | ||
54 | { | ||
55 | struct thread_info *context; | ||
56 | unsigned long addr; | ||
57 | |||
58 | context = (struct thread_info *) | ||
59 | ((unsigned long)stack & (~(THREAD_SIZE - 1))); | ||
60 | |||
61 | while (valid_stack_ptr(context, stack)) { | ||
62 | addr = *stack++; | ||
63 | if (__kernel_text_address(addr)) { | ||
64 | printk(" [<%08lx>]", addr); | ||
65 | print_symbol(" %s", addr); | ||
66 | printk("\n"); | ||
67 | } | ||
68 | } | ||
69 | printk(" =======================\n"); | ||
70 | } | 53 | } |
71 | 54 | ||
72 | /* displays a short stack trace */ | 55 | /* displays a short stack trace */ |
73 | void show_stack(struct task_struct *task, unsigned long *esp) | 56 | void show_stack(struct task_struct *task, unsigned long *esp) |
74 | { | 57 | { |
75 | unsigned long addr, *stack; | ||
76 | int i; | ||
77 | |||
78 | if (esp == NULL) | 58 | if (esp == NULL) |
79 | esp = (unsigned long *)&esp; | 59 | esp = (unsigned long *)&esp; |
80 | 60 | ||
81 | stack = esp; | 61 | pr_emerg("Call trace:\n"); |
82 | 62 | unwind_stack(NULL, esp, print_trace); | |
83 | printk("Stack dump [0x%08lx]:\n", (unsigned long)esp); | ||
84 | for (i = 0; i < kstack_depth_to_print; i++) { | ||
85 | if (kstack_end(stack)) | ||
86 | break; | ||
87 | if (__get_user(addr, stack)) { | ||
88 | /* This message matches "failing address" marked | ||
89 | s390 in ksymoops, so lines containing it will | ||
90 | not be filtered out by ksymoops. */ | ||
91 | printk("Failing address 0x%lx\n", (unsigned long)stack); | ||
92 | break; | ||
93 | } | ||
94 | stack++; | ||
95 | |||
96 | printk("sp + %02d: 0x%08lx\n", i * 4, addr); | ||
97 | } | ||
98 | printk("\n"); | ||
99 | |||
100 | show_trace(task, esp); | ||
101 | |||
102 | return; | ||
103 | } | 63 | } |
104 | 64 | ||
105 | void show_trace_task(struct task_struct *tsk) | 65 | void show_trace_task(struct task_struct *tsk) |
@@ -115,7 +75,7 @@ void show_registers(struct pt_regs *regs) | |||
115 | int in_kernel = 1; | 75 | int in_kernel = 1; |
116 | unsigned long esp; | 76 | unsigned long esp; |
117 | 77 | ||
118 | esp = (unsigned long)(®s->sp); | 78 | esp = (unsigned long)(regs->sp); |
119 | if (user_mode(regs)) | 79 | if (user_mode(regs)) |
120 | in_kernel = 0; | 80 | in_kernel = 0; |
121 | 81 | ||
diff --git a/arch/openrisc/kernel/unwinder.c b/arch/openrisc/kernel/unwinder.c new file mode 100644 index 000000000000..8ae15c2c1845 --- /dev/null +++ b/arch/openrisc/kernel/unwinder.c | |||
@@ -0,0 +1,105 @@ | |||
1 | /* | ||
2 | * OpenRISC unwinder.c | ||
3 | * | ||
4 | * Reusable arch specific api for unwinding stacks. | ||
5 | * | ||
6 | * Copyright (C) 2017 Stafford Horne <shorne@gmail.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/sched/task_stack.h> | ||
14 | #include <linux/kernel.h> | ||
15 | |||
16 | #include <asm/unwinder.h> | ||
17 | |||
18 | #ifdef CONFIG_FRAME_POINTER | ||
19 | struct or1k_frameinfo { | ||
20 | unsigned long *fp; | ||
21 | unsigned long ra; | ||
22 | unsigned long top; | ||
23 | }; | ||
24 | |||
25 | /* | ||
26 | * Verify a frameinfo structure. The return address should be a valid text | ||
27 | * address. The frame pointer may be null if its the last frame, otherwise | ||
28 | * the frame pointer should point to a location in the stack after the the | ||
29 | * top of the next frame up. | ||
30 | */ | ||
31 | static inline int or1k_frameinfo_valid(struct or1k_frameinfo *frameinfo) | ||
32 | { | ||
33 | return (frameinfo->fp == NULL || | ||
34 | (!kstack_end(frameinfo->fp) && | ||
35 | frameinfo->fp > &frameinfo->top)) && | ||
36 | __kernel_text_address(frameinfo->ra); | ||
37 | } | ||
38 | |||
39 | /* | ||
40 | * Create a stack trace doing scanning which is frame pointer aware. We can | ||
41 | * get reliable stack traces by matching the previously found frame | ||
42 | * pointer with the top of the stack address every time we find a valid | ||
43 | * or1k_frameinfo. | ||
44 | * | ||
45 | * Ideally the stack parameter will be passed as FP, but it can not be | ||
46 | * guaranteed. Therefore we scan each address looking for the first sign | ||
47 | * of a return address. | ||
48 | * | ||
49 | * The OpenRISC stack frame looks something like the following. The | ||
50 | * location SP is held in r1 and location FP is held in r2 when frame pointers | ||
51 | * enabled. | ||
52 | * | ||
53 | * SP -> (top of stack) | ||
54 | * - (callee saved registers) | ||
55 | * - (local variables) | ||
56 | * FP-8 -> previous FP \ | ||
57 | * FP-4 -> return address |- or1k_frameinfo | ||
58 | * FP -> (previous top of stack) / | ||
59 | */ | ||
60 | void unwind_stack(void *data, unsigned long *stack, | ||
61 | void (*trace)(void *data, unsigned long addr, int reliable)) | ||
62 | { | ||
63 | unsigned long *next_fp = NULL; | ||
64 | struct or1k_frameinfo *frameinfo = NULL; | ||
65 | int reliable = 0; | ||
66 | |||
67 | while (!kstack_end(stack)) { | ||
68 | frameinfo = container_of(stack, | ||
69 | struct or1k_frameinfo, | ||
70 | top); | ||
71 | |||
72 | if (__kernel_text_address(frameinfo->ra)) { | ||
73 | if (or1k_frameinfo_valid(frameinfo) && | ||
74 | (next_fp == NULL || | ||
75 | next_fp == &frameinfo->top)) { | ||
76 | reliable = 1; | ||
77 | next_fp = frameinfo->fp; | ||
78 | } else | ||
79 | reliable = 0; | ||
80 | |||
81 | trace(data, frameinfo->ra, reliable); | ||
82 | } | ||
83 | stack++; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | #else /* CONFIG_FRAME_POINTER */ | ||
88 | |||
89 | /* | ||
90 | * Create a stack trace by doing a simple scan treating all text addresses | ||
91 | * as return addresses. | ||
92 | */ | ||
93 | void unwind_stack(void *data, unsigned long *stack, | ||
94 | void (*trace)(void *data, unsigned long addr, int reliable)) | ||
95 | { | ||
96 | unsigned long addr; | ||
97 | |||
98 | while (!kstack_end(stack)) { | ||
99 | addr = *stack++; | ||
100 | if (__kernel_text_address(addr)) | ||
101 | trace(data, addr, 0); | ||
102 | } | ||
103 | } | ||
104 | #endif /* CONFIG_FRAME_POINTER */ | ||
105 | |||