xref: /openbmc/linux/arch/openrisc/kernel/unwinder.c (revision 48bddb89)
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
29  * top of the next frame up.
30  */
or1k_frameinfo_valid(struct or1k_frameinfo * frameinfo)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  */
unwind_stack(void * data,unsigned long * stack,void (* trace)(void * data,unsigned long addr,int reliable))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  */
unwind_stack(void * data,unsigned long * stack,void (* trace)(void * data,unsigned long addr,int reliable))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 
106