xref: /openbmc/linux/arch/openrisc/kernel/unwinder.c (revision eecac38b0423a69715073ecbde581dafd1abb28b)
1*eecac38bSStafford Horne /*
2*eecac38bSStafford Horne  * OpenRISC unwinder.c
3*eecac38bSStafford Horne  *
4*eecac38bSStafford Horne  * Reusable arch specific api for unwinding stacks.
5*eecac38bSStafford Horne  *
6*eecac38bSStafford Horne  * Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
7*eecac38bSStafford Horne  *
8*eecac38bSStafford Horne  * This file is licensed under the terms of the GNU General Public License
9*eecac38bSStafford Horne  * version 2.  This program is licensed "as is" without any warranty of any
10*eecac38bSStafford Horne  * kind, whether express or implied.
11*eecac38bSStafford Horne  */
12*eecac38bSStafford Horne 
13*eecac38bSStafford Horne #include <linux/sched/task_stack.h>
14*eecac38bSStafford Horne #include <linux/kernel.h>
15*eecac38bSStafford Horne 
16*eecac38bSStafford Horne #include <asm/unwinder.h>
17*eecac38bSStafford Horne 
18*eecac38bSStafford Horne #ifdef CONFIG_FRAME_POINTER
19*eecac38bSStafford Horne struct or1k_frameinfo {
20*eecac38bSStafford Horne 	unsigned long *fp;
21*eecac38bSStafford Horne 	unsigned long ra;
22*eecac38bSStafford Horne 	unsigned long top;
23*eecac38bSStafford Horne };
24*eecac38bSStafford Horne 
25*eecac38bSStafford Horne /*
26*eecac38bSStafford Horne  * Verify a frameinfo structure.  The return address should be a valid text
27*eecac38bSStafford Horne  * address.  The frame pointer may be null if its the last frame, otherwise
28*eecac38bSStafford Horne  * the frame pointer should point to a location in the stack after the the
29*eecac38bSStafford Horne  * top of the next frame up.
30*eecac38bSStafford Horne  */
31*eecac38bSStafford Horne static inline int or1k_frameinfo_valid(struct or1k_frameinfo *frameinfo)
32*eecac38bSStafford Horne {
33*eecac38bSStafford Horne 	return (frameinfo->fp == NULL ||
34*eecac38bSStafford Horne 		(!kstack_end(frameinfo->fp) &&
35*eecac38bSStafford Horne 		 frameinfo->fp > &frameinfo->top)) &&
36*eecac38bSStafford Horne 	       __kernel_text_address(frameinfo->ra);
37*eecac38bSStafford Horne }
38*eecac38bSStafford Horne 
39*eecac38bSStafford Horne /*
40*eecac38bSStafford Horne  * Create a stack trace doing scanning which is frame pointer aware. We can
41*eecac38bSStafford Horne  * get reliable stack traces by matching the previously found frame
42*eecac38bSStafford Horne  * pointer with the top of the stack address every time we find a valid
43*eecac38bSStafford Horne  * or1k_frameinfo.
44*eecac38bSStafford Horne  *
45*eecac38bSStafford Horne  * Ideally the stack parameter will be passed as FP, but it can not be
46*eecac38bSStafford Horne  * guaranteed.  Therefore we scan each address looking for the first sign
47*eecac38bSStafford Horne  * of a return address.
48*eecac38bSStafford Horne  *
49*eecac38bSStafford Horne  * The OpenRISC stack frame looks something like the following.  The
50*eecac38bSStafford Horne  * location SP is held in r1 and location FP is held in r2 when frame pointers
51*eecac38bSStafford Horne  * enabled.
52*eecac38bSStafford Horne  *
53*eecac38bSStafford Horne  * SP   -> (top of stack)
54*eecac38bSStafford Horne  *      -  (callee saved registers)
55*eecac38bSStafford Horne  *      -  (local variables)
56*eecac38bSStafford Horne  * FP-8 -> previous FP             \
57*eecac38bSStafford Horne  * FP-4 -> return address          |- or1k_frameinfo
58*eecac38bSStafford Horne  * FP   -> (previous top of stack) /
59*eecac38bSStafford Horne  */
60*eecac38bSStafford Horne void unwind_stack(void *data, unsigned long *stack,
61*eecac38bSStafford Horne 		  void (*trace)(void *data, unsigned long addr, int reliable))
62*eecac38bSStafford Horne {
63*eecac38bSStafford Horne 	unsigned long *next_fp = NULL;
64*eecac38bSStafford Horne 	struct or1k_frameinfo *frameinfo = NULL;
65*eecac38bSStafford Horne 	int reliable = 0;
66*eecac38bSStafford Horne 
67*eecac38bSStafford Horne 	while (!kstack_end(stack)) {
68*eecac38bSStafford Horne 		frameinfo = container_of(stack,
69*eecac38bSStafford Horne 					 struct or1k_frameinfo,
70*eecac38bSStafford Horne 					 top);
71*eecac38bSStafford Horne 
72*eecac38bSStafford Horne 		if (__kernel_text_address(frameinfo->ra)) {
73*eecac38bSStafford Horne 			if (or1k_frameinfo_valid(frameinfo) &&
74*eecac38bSStafford Horne 			    (next_fp == NULL ||
75*eecac38bSStafford Horne 			     next_fp == &frameinfo->top)) {
76*eecac38bSStafford Horne 				reliable = 1;
77*eecac38bSStafford Horne 				next_fp = frameinfo->fp;
78*eecac38bSStafford Horne 			} else
79*eecac38bSStafford Horne 				reliable = 0;
80*eecac38bSStafford Horne 
81*eecac38bSStafford Horne 			trace(data, frameinfo->ra, reliable);
82*eecac38bSStafford Horne 		}
83*eecac38bSStafford Horne 		stack++;
84*eecac38bSStafford Horne 	}
85*eecac38bSStafford Horne }
86*eecac38bSStafford Horne 
87*eecac38bSStafford Horne #else /* CONFIG_FRAME_POINTER */
88*eecac38bSStafford Horne 
89*eecac38bSStafford Horne /*
90*eecac38bSStafford Horne  * Create a stack trace by doing a simple scan treating all text addresses
91*eecac38bSStafford Horne  * as return addresses.
92*eecac38bSStafford Horne  */
93*eecac38bSStafford Horne void unwind_stack(void *data, unsigned long *stack,
94*eecac38bSStafford Horne 		   void (*trace)(void *data, unsigned long addr, int reliable))
95*eecac38bSStafford Horne {
96*eecac38bSStafford Horne 	unsigned long addr;
97*eecac38bSStafford Horne 
98*eecac38bSStafford Horne 	while (!kstack_end(stack)) {
99*eecac38bSStafford Horne 		addr = *stack++;
100*eecac38bSStafford Horne 		if (__kernel_text_address(addr))
101*eecac38bSStafford Horne 			trace(data, addr, 0);
102*eecac38bSStafford Horne 	}
103*eecac38bSStafford Horne }
104*eecac38bSStafford Horne #endif /* CONFIG_FRAME_POINTER */
105*eecac38bSStafford Horne 
106