openrisc: support framepointers and STACKTRACE_SUPPORT
[sfrench/cifs-2.6.git] / arch / openrisc / kernel / unwinder.c
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