aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStafford Horne <shorne@gmail.com>2017-07-24 08:44:35 -0400
committerStafford Horne <shorne@gmail.com>2017-11-03 01:01:15 -0400
commiteecac38b0423a69715073ecbde581dafd1abb28b (patch)
tree6c675e769ef8a5f0fb81b4ea2dd7596be9c5aebd
parent306e5e50a32140452849fff42fca104511fd6668 (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/Kconfig4
-rw-r--r--arch/openrisc/include/asm/unwinder.h20
-rw-r--r--arch/openrisc/kernel/Makefile3
-rw-r--r--arch/openrisc/kernel/stacktrace.c86
-rw-r--r--arch/openrisc/kernel/traps.c54
-rw-r--r--arch/openrisc/kernel/unwinder.c105
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
37config CPU_BIG_ENDIAN 38config CPU_BIG_ENDIAN
38 def_bool y 39 def_bool y
@@ -60,6 +61,9 @@ config TRACE_IRQFLAGS_SUPPORT
60config GENERIC_CSUM 61config GENERIC_CSUM
61 def_bool y 62 def_bool y
62 63
64config STACKTRACE_SUPPORT
65 def_bool y
66
63source "init/Kconfig" 67source "init/Kconfig"
64 68
65source "kernel/Kconfig.freezer" 69source "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
16void 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
7obj-y := setup.o or32_ksyms.o process.o dma.o \ 7obj-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
11obj-$(CONFIG_SMP) += smp.o 11obj-$(CONFIG_SMP) += smp.o
12obj-$(CONFIG_STACKTRACE) += stacktrace.o
12obj-$(CONFIG_MODULES) += module.o 13obj-$(CONFIG_MODULES) += module.o
13obj-$(CONFIG_OF) += prom.o 14obj-$(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 */
24static void
25save_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
41void save_stack_trace(struct stack_trace *trace)
42{
43 unwind_stack(trace, (unsigned long *) &trace, save_stack_address);
44}
45EXPORT_SYMBOL_GPL(save_stack_trace);
46
47static void
48save_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
67void 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}
78EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
79
80void
81save_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}
86EXPORT_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
42extern char _etext, _stext; 43extern char _etext, _stext;
43 44
@@ -45,61 +46,20 @@ int kstack_depth_to_print = 0x180;
45int lwa_flag; 46int lwa_flag;
46unsigned long __user *lwa_addr; 47unsigned long __user *lwa_addr;
47 48
48static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) 49void 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
53void 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 */
73void show_stack(struct task_struct *task, unsigned long *esp) 56void 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
105void show_trace_task(struct task_struct *tsk) 65void 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)(&regs->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
19struct 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 */
31static 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 */
60void 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 */
93void 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